Su richiesta di un amico, c'era la necessita' di estrarre solo le righe pari (o dispari) da un file testo puro usando Bash
il primo tentativo e' stato fatto con un ciclo for
--------------------------------------------
for i in `seq 1 2 10`;
do
comando="sed -n "$i"p file1.txt >> file2.txt"
eval $comando
done
---------------------------------------------
in realta' il comando sed ha gia' al suo interno questa possibilita', infatti lo switch p permette di indicare a quale riga iniziare a quante righe saltare. per esempio nell'esempio sottostante vengono salvate una riga si' ed una no partendo da 1
---------------------------------------------
sed -n 1~2p file1.txt >> file2.txt
---------------------------------------------
lunedì 31 marzo 2014
Geocompass per FirefoxOS
Questa applicazione e' un porting della precedente Geocompass per Android per FirefoxOS
A parte il linguaggio differente, il codice condivide larghe parte di righe che sono state semplicemente copiate complice una sintassi uguale tra Java e Javascript
Il programma e' stato scritto con l'aiuto di Cordova/Phonegap.
Per istruzioni si puo' consultare Wikipedia o questa pagina
L'applicazione puo' essere scaricata a questo link
index.html
----------------------------------------------------
<!DOCTYPE html>
<html>
<head>
<title>Geocompass</title>
<style>
#content {
width: 100px;
height:100px;
margin: 0px auto;
}
</style>
<script type="text/javascript" charset="utf-8" src="./js/cordova.js"></script>
<script type="text/javascript" charset="utf-8" src="./js/misura.js"></script>
</head>
<body onload="drawCanvas();">
<div id="content">
<canvas id="myCanvas" width="256" height="256">Your browser doesn't support canvas tag</canvas>
</div>
<p id="ang" style="text-align:center;font-family:arial;color:red;font-size:30px;"></p>
<p style="text-align:center;font-family:arial;color:red;font-size:30px;">Strike/Dip</p><center>
<p><a style='text-align:center;font-family:arial;color:red;font-size:20px;' href='http://en.wikipedia.org/wiki/Strike_and_dip'>Help</a></p></center>
<p style="text-align:center;font-family:arial;color:red;font-size:15px;">Contact : lucainnoc@gmail.com</p></center>
</body>
</html>
----------------------------------------------------
misura.js
----------------------------------------------------
var surface;
var misura;
var angle = 0;
var ang_maxpendenza = 0;
function drawCanvas() {
screen.mozLockOrientation("portrait-primary");
surface = document.getElementById("myCanvas");
if (surface.getContext) {
misura = new Image();
misura.onload = loadingComplete;
misura.src = "./img/misura.png";
if (ang_maxpendenza < 10)
{
misura.src = "./img/orizzontale.png";
}
if (ang_maxpendenza > 80)
{
misura.src = "./img/verticale.png";
}
}
}
function loadingComplete(e) {
var surfaceContext = surface.getContext('2d');
surfaceContext.fillStyle = "rgb(255,255,255)";
surfaceContext.fillRect(0, 0, surface.width, surface.height);
surfaceContext.save();
surfaceContext.translate(misura.width * 0.5, misura.height * 0.5);
if ((ang_maxpendenza > 10) && (ang_maxpendenza < 80))
{
surfaceContext.rotate(angle* 0.0174532925199432957);
}
surfaceContext.translate(-misura.width * 0.5, -misura.height * 0.5);
surfaceContext.drawImage(misura, 0, 0);
surfaceContext.restore();
}
function converti2deg(angolo) {
return angolo*(180/Math.PI);
}
function converti2rad(angolo) {
return angolo*(Math.PI/180);
}
window.addEventListener('deviceorientation', function(event) {
var azimuth = event.alpha;
var pitch = event.beta;
var roll = event.gamma;
pitch = converti2rad(pitch);
roll = converti2rad(roll);
var cosalfa = Math.cos(roll-(Math.PI/2));
var cosbeta = Math.cos(pitch-(Math.PI/2));
var dir_maxpendenza = -converti2deg(Math.atan(cosalfa/cosbeta));
if ((pitch < 0) && (roll<=0)) dir_maxpendenza = dir_maxpendenza + 180;
if ((pitch <= 0) && (roll>0)) dir_maxpendenza = dir_maxpendenza + 180;
if ((pitch > 0) && (roll>0)) dir_maxpendenza = dir_maxpendenza + 360;
if ((pitch == 0) && (roll>0)) dir_maxpendenza = 270;
if ((pitch > 0) && (roll==0)) dir_maxpendenza = 0;
ang_maxpendenza = 90-converti2deg(Math.acos(Math.sqrt((cosalfa*cosalfa)+(cosbeta*cosbeta))));
var dir_maxpendenza_reale = (azimuth+dir_maxpendenza)%360;
document.getElementById("ang").innerHTML = dir_maxpendenza_reale.toFixed(0)+"/"+ang_maxpendenza.toFixed(0);
if (ang_maxpendenza < 10) document.getElementById("ang").innerHTML = "Horizontal";
if (ang_maxpendenza > 80) document.getElementById("ang").innerHTML = "Vertical " + azimuth.toFixed(0) + "-" + (azimuth.toFixed(0)+180)%360;
angle = dir_maxpendenza;
drawCanvas()
}, false);
----------------------------------------------------
manifest.webapp
----------------------------------------------------
{
"name": "Geocompass",
"description": "Compass for Geologists",
"launch_path":"/index.html",
"installs_allowed_from":["*"],
"version":"0.9.0","name":"Geocompass",
"orientation": "portrait-primary",
"icons": {
"256": "/img/icon-256.png",
"128": "/img/icon-128.png",
"120": "/img/icon-120.png",
"90": "/img/icon-90.png",
"60": "/img/icon-60.png",
"32": "/img/icon-32.png"
},
"developer": {
"name": "Luca Innocenti",
"url": "http://debiaonoldcomputers.blogspot.com/"
},
"default_locale": "en"
}
----------------------------------------------------
venerdì 28 marzo 2014
Pubblicato MandExplorer su Firefox MarketPlace
Finalmente e' stato pubblicata sul MarketPlace la mia prima applicazione per FirefoxOs
Puo' essere scaricata da https://marketplace.firefox.com/app/mandexplorer/
Puo' essere scaricata da https://marketplace.firefox.com/app/mandexplorer/
giovedì 27 marzo 2014
Sviluppo e pubblicazione su Firefox OS
Dopo aver scritto la mia prima applicazione per Firefox Os, ho provato a pubblicarla sul Market di Firefox Os.
Di seguito viene descritto come sviluppare e pubblicare su questa piattaforma
Di fatto non esiste un SDK e tutte le operazioni di sviluppo si possono svolgere all'interno dell'emulatore di Firefox OS che si installa aprendo l'indirizzo
about:app-manager
nell'url di Firefox (versione 26 e successive)
Al primo avvio viene richiesto di installare l'emulatore e l'adb helper (per sviluppare direttamente sul telefono)
Nella finestra App si indica la posizione (directory) delle varie applicazioni in fase di sviluppo mentre nella finestra Dispositivo si puo' gestire il lancio e la chiusura dell'applicazione sia nell'emulatore che nel telefono fisico
In basso alla finestra Dispositivo si ha la modalita' di connessione (nel caso di utilizzo di telefono fisico per ottenere la connessione e' necessario confermare anche sul telefono)
Per ogni modifica si deve effettuare un Aggiorna sulla finestra App e poi lanciare l'applicazione da dispositivi
Una volta terminato il lavoro si crea un file .zip che contiene l'applicazione (compresa di immagini, javascript e file manifest secondo le specifiche di Firefox e si va sul Firefox MarketPlace (usando Firefox), ci si registra (utilizzando Mozilla Persona) e poi si sottomette l'applicazione come file .zip
Di seguito viene descritto come sviluppare e pubblicare su questa piattaforma
Di fatto non esiste un SDK e tutte le operazioni di sviluppo si possono svolgere all'interno dell'emulatore di Firefox OS che si installa aprendo l'indirizzo
about:app-manager
nell'url di Firefox (versione 26 e successive)
Al primo avvio viene richiesto di installare l'emulatore e l'adb helper (per sviluppare direttamente sul telefono)
Nella finestra App si indica la posizione (directory) delle varie applicazioni in fase di sviluppo mentre nella finestra Dispositivo si puo' gestire il lancio e la chiusura dell'applicazione sia nell'emulatore che nel telefono fisico
In basso alla finestra Dispositivo si ha la modalita' di connessione (nel caso di utilizzo di telefono fisico per ottenere la connessione e' necessario confermare anche sul telefono)
Per ogni modifica si deve effettuare un Aggiorna sulla finestra App e poi lanciare l'applicazione da dispositivi
Una volta terminato il lavoro si crea un file .zip che contiene l'applicazione (compresa di immagini, javascript e file manifest secondo le specifiche di Firefox e si va sul Firefox MarketPlace (usando Firefox), ci si registra (utilizzando Mozilla Persona) e poi si sottomette l'applicazione come file .zip
A questo punto il sito effettua dei controlli e si rieffettua la procedura fino a quando non ci sono errori
(Non e' richiesto nessun pagamento, a differenza di Google dove e' forfettario o Apple dove e' annuale)
Si risponde ad una serie di domande (alcune anche curiose del tipo se l'applicazione fa propaganda nazista???)
E si attende pazientemente che l'applicazione sia approvata (e' un procedimento piuttosto lento al confronto di Play Store)
mercoledì 26 marzo 2014
Android Wear
Dopo aver modificato il telefono per avere un dispositivo ad Android 4.4, ho provato Android Wear, la versione per i futuri Smart Watch di Google.
Al momento e' disponibile solo una developer preview (per cui bisogna essere registrati) all'interno dell'emulatore
Sul telefono si installa l'applicazione Android Wear Preview App mentre dall'Android SDK Manager si deve scaricare Android Wear ARM EABI v7a System Image.
e si crea una nuova macchina virtuale AVD basata sull'immagine di Android Wear
A questo punto si esegue l'emulatore, si connette il telefono via USB e si attiva l'applicazione sul telefono.
Al computer si digita quindi il comando
adb -d forward tcp:5601 tcp:5601
Al momento e' disponibile solo una developer preview (per cui bisogna essere registrati) all'interno dell'emulatore
Sul telefono si installa l'applicazione Android Wear Preview App mentre dall'Android SDK Manager si deve scaricare Android Wear ARM EABI v7a System Image.
e si crea una nuova macchina virtuale AVD basata sull'immagine di Android Wear
Al computer si digita quindi il comando
adb -d forward tcp:5601 tcp:5601
l'applicazione sul telefono mostra lo stato di connessione e l'icona sull'emulatore mostra lo stato di connessione.
Per generare quindi una notifica ho effettuato una telefonata al cellulare e sul display dello Smart Watch e' comparsa la notizia
Di fatto il sistema reagisce sostanzialmente alle notifiche che gli derivano dal telefono a cui e' accoppiato.
Non e' necessario che le notifiche siano di sistema. Si puo' creare una applicazione che generi notifiche e queste vengono instradate verso lo smartwatch
Per esempio il codice sottostante genera una piccola applicazione che lancia una notifica all'avvio (codice modificato dal post originale, da notare che il codice originale non era stato concepito per Android Wear a dimostrazione di come si possa importare semplicemente codice relativo ad altre API)
Per compilare il codice e' necessario inserire tra le librerie wereable-preview-support.jar tra le Referenced Libraries
-----------------------------------------------------------------------------
package com.wear.orologio;
import android.app.Activity;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
public class MainActivity extends Activity {
NotificationManager notificationManager;
Notification myNotification;
private static final int MY_NOTIFICATION_ID=1;
private final String myBlog = "http://www.google.com/";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Context context = getApplicationContext();
Intent myIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(myBlog));
PendingIntent pendingIntent = PendingIntent.getActivity(
MainActivity.this,
0,
myIntent,
Intent.FLAG_ACTIVITY_NEW_TASK);
myNotification = new Notification.Builder(context)
.setContentTitle("Notifica")
.setContentText("Dal Luca Innocenti")
.setTicker("Notifica")
.setWhen(System.currentTimeMillis())
.setContentIntent(pendingIntent)
.setDefaults(Notification.DEFAULT_SOUND)
.setAutoCancel(true)
.setSmallIcon(R.drawable.ic_launcher)
.build();
notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(MY_NOTIFICATION_ID, myNotification);
}
}
Non e' necessario che le notifiche siano di sistema. Si puo' creare una applicazione che generi notifiche e queste vengono instradate verso lo smartwatch
Per esempio il codice sottostante genera una piccola applicazione che lancia una notifica all'avvio (codice modificato dal post originale, da notare che il codice originale non era stato concepito per Android Wear a dimostrazione di come si possa importare semplicemente codice relativo ad altre API)
Per compilare il codice e' necessario inserire tra le librerie wereable-preview-support.jar tra le Referenced Libraries
-----------------------------------------------------------------------------
package com.wear.orologio;
import android.app.Activity;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
public class MainActivity extends Activity {
NotificationManager notificationManager;
Notification myNotification;
private static final int MY_NOTIFICATION_ID=1;
private final String myBlog = "http://www.google.com/";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Context context = getApplicationContext();
Intent myIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(myBlog));
PendingIntent pendingIntent = PendingIntent.getActivity(
MainActivity.this,
0,
myIntent,
Intent.FLAG_ACTIVITY_NEW_TASK);
myNotification = new Notification.Builder(context)
.setContentTitle("Notifica")
.setContentText("Dal Luca Innocenti")
.setTicker("Notifica")
.setWhen(System.currentTimeMillis())
.setContentIntent(pendingIntent)
.setDefaults(Notification.DEFAULT_SOUND)
.setAutoCancel(true)
.setSmallIcon(R.drawable.ic_launcher)
.build();
notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(MY_NOTIFICATION_ID, myNotification);
}
}
-----------------------------------------------------------------------------
Ma Android Wear sara' solo notifiche???
Non sembra in quanto sull'emulatore dello Smart Watch funziona tranquillamente Hello World per cui il dispositivo sembra avere una propria autonomia dal telefono
Cyanogenmod 11 KitKat su Nexus S
Volevo provare ad usare Android Wear ma e' necessario avere un telefono con almeno Android 4.3 (non e' contemplato l'uso di tablet come il Nexus 7 2012) per cui ho deciso di modificare il Nexus S (rimasto fermo ad Android 4.1) per installare la Cyanogenmod 11 Android KitKat 4.4
La procedura prevede di scarica l'immagine Cyanogenmod da questo link e si carica il file cm-11-20131122-UNOFFICIAL-crespo.zip sulla memoria del telefono
Per poter installare il file e' necessario utilizzare la CWM Recovery da questo link ed in particolare il file recovery-clockwork-6.0.4.3-crespo.img (ho provato con la recovery gia' installata ma non ha funzionato)
per installare la CWM si collega il telefono, lo si accende con la combinazione Power + Vol Su e da shell si digita
fastboot flash recovery recovery-clockwork-6.0.4.3-crespo.img
La procedura prevede di scarica l'immagine Cyanogenmod da questo link e si carica il file cm-11-20131122-UNOFFICIAL-crespo.zip sulla memoria del telefono
Per poter installare il file e' necessario utilizzare la CWM Recovery da questo link ed in particolare il file recovery-clockwork-6.0.4.3-crespo.img (ho provato con la recovery gia' installata ma non ha funzionato)
per installare la CWM si collega il telefono, lo si accende con la combinazione Power + Vol Su e da shell si digita
fastboot flash recovery recovery-clockwork-6.0.4.3-crespo.img
a questo punto si rispenge e si riaccende il telefono sempre con la combinazione Power + Vol Su e si seleziona l'opzione Recovery (Vol Su e Vol Giu'per spostarsi, Power per selezionare), si effettua il wipe dei dati e si installa da zip file cm-11-20131122-UNOFFICIAL-crespo.zip
Le applicazioni Gooogle si scaricano da questo link e si installano con procedura simile (install from zip file)
Alla fine il telefono si riavvia con Android 4.4. E' forse un filino piu' lento della versione 4.1 ma e' assolutamente usabile (unico appunto e' che la tastiera di Unlock del pin della Sim card e' schiacciata ed i tasti non sono cosi' comodi da selezionare)
martedì 25 marzo 2014
Fractal Explorer su Firefox Os
Ed ecco il programmino per esplorare Mandelbrot in versione Firefox OS
Rispetto alla versione HTML5 pura ci sono alcune differenze
Per prima cosa e' stato necessario aggiungere un pulsante di Reset perche' premendo il pulsante Home del telefono l'applicazione non viene chiusa ma solo nascosta ed al successivo riavvio si presenta con l'ultimo livello di zoom impostato
Inoltre poter passare la validazione del Market di Firefox e' stato necessario dividere la parte HTML dalla sezione Javascript
Da questo link di puo' scaricare il file zip dell'applicazione
manifest.webapp
------------------------------------------------------------------------
{
"version": "0.5",
"name": "MandExplorer",
"description": "A simple Mandelbrot Explorer with zoom function",
"launch_path": "/index.html",
"icons": {
"16": "/app-icons/icon-16.png",
"32": "/app-icons/icon-32.png",
"48": "/app-icons/icon-48.png",
"60": "/app-icons/icon-60.png",
"90": "/app-icons/icon-90.png",
"120": "/app-icons/icon-120.png",
"128": "/app-icons/icon-128.png",
"256": "/app-icons/icon-256.png"
},
"developer": {
"name": "Luca Innocenti",
"url": "http://debiaonoldcomputers.blogspot.com/"
},
"locales": {
"en": {
"description": "A simple Mandelbrot Explorer with zoom function",
"developer": {
"url": "http://debiaonoldcomputers.blogspot.com/"
}
},
"it": {
"description": "Un semplice visualizzatore con zoomabile dell'insieme di Mandelbrot",
"developer": {
"url": "http://debiaonoldcomputers.blogspot.com/"
}
}
},
"default_locale": "en"
}
index.html
------------------------------------------------------------------------
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<script src="./js/calcola.js"></script>
<canvas id="mandelbrot" height="400" width="320">
Il Browser non supporta HTML5.
<!-- Utile per inserire informazioni di default -->
</canvas><br>
<center><input type="button" id="reset" value="Reset"></center>
</body>
</html>
calcola.js
------------------------------------------------------------------------
//inizializza le variabili
//dimensioni dello schermo
var SCREEN_HEIGHT = 400;
var SCREEN_WIDTH = 320;
//finestra di visualizzazione in coordinate Mandelbrot
var a1 = -2.0;
var b1 = -1.2;
var a2 = 1.0;
var b2 = 1.2;
//numero di cicli
var it = 25;
var delta_x ;
var delta_y;
// palette di 12 colori
var colori = ["#FFFFFF","#FF0000","#00FF00","#0000FF","#FF00FF","#FFFF00","#00FFFF","#FFaa00","#abcedf","#fedc0a","#ab16dd","#d00fba","#edabcc","#ddacff"];
//function punto(x1,y1,
function disegna(r_mi,i_mi,r_ma,i_ma,ite)
{
//effettua il calcolo dell'insieme date le dimensioni della finestra
var canvas = document.getElementById("mandelbrot");
if (canvas.getContext) {
var context = canvas.getContext("2d");
//rende il fondo nero
context.fillStyle="rgb(0,0,0)";
context.fillRect(0, 0, canvas.width, canvas.height);
var re_min = r_mi;
var im_min = i_mi;
var re_max = r_ma;
var im_max = i_ma;
var iterazioni = ite;
var r;
var a,b;
var x,y,x_new,y_new;
var test;
var k,j,i;
var re_factor = (re_max-re_min);
var im_factor = (im_max-im_min);
for (var i=0;i<SCREEN_HEIGHT;i++)
{
for (var j=0;j<SCREEN_WIDTH;j++)
{
a = re_min+(j*re_factor/SCREEN_WIDTH);
b = im_min+(i*im_factor/SCREEN_HEIGHT);
x = 0;
y = 0;
test = 0;
for (var k=0;k<iterazioni;k++)
{
x_new = (x*x)-(y*y)+a;
y_new = (2*x*y)+b;
if (((x_new*x_new)+(y_new*y_new))>4)
{
// colora il punto
r = k%12;
context.beginPath();
context.fillRect(j-1,i-1,1,1);
context.fillStyle=colori[r];
context.stroke();
break;
}
x = x_new;
y = y_new;
}
}
}
}
}
function doMouseDown(event)
{
canvas_x = event.pageX;
canvas_y = event.pageY;
//calcola il punto in coordinate Mandelbrot
var p1 = (((a2-a1)/SCREEN_WIDTH)*canvas_x) + a1;
var p2 = (((b2-b1)/SCREEN_HEIGHT)*canvas_y) + b1;
//calcola la dimensione della finestra (riduzione di 1/4)
var delta_x = (a2-a1)/4;
var delta_y = delta_x*(SCREEN_WIDTH/SCREEN_WIDTH);
//aumenta il numero di iterazioni per ogni ciclo
it = it + 75;
//definisce la finestra intorno al punto cliccato
a1 = p1 - delta_y;
b1 = p2 - delta_x;
a2 = p1 + delta_y;
b2 = p2 + delta_x;
disegna(a1,b1,a2,b2,it);
}
function res(event)
{
a1 = -2.0;
b1 = -1.2;
a2 = 1.0;
b2 = 1.2;
it = 25;
disegna(a1,b1,a2,b2,it);
}
window.onload = function()
{
//riscala alla dimensione della finestra
var canvas = document.getElementById("mandelbrot");
var button = document.getElementById("reset");
//aggiunge l'evento click del mouse
canvas.addEventListener("mousedown",doMouseDown,false);
button.addEventListener("click", res,false);
disegna(a1,b1,a2,b2,it);
}
------------------------------------------------------------------------
Rispetto alla versione HTML5 pura ci sono alcune differenze
Per prima cosa e' stato necessario aggiungere un pulsante di Reset perche' premendo il pulsante Home del telefono l'applicazione non viene chiusa ma solo nascosta ed al successivo riavvio si presenta con l'ultimo livello di zoom impostato
In esecuzione sull'emulatore
In esecuzione su Alcatel One Touch Fire
Inoltre poter passare la validazione del Market di Firefox e' stato necessario dividere la parte HTML dalla sezione Javascript
Da questo link di puo' scaricare il file zip dell'applicazione
manifest.webapp
------------------------------------------------------------------------
{
"version": "0.5",
"name": "MandExplorer",
"description": "A simple Mandelbrot Explorer with zoom function",
"launch_path": "/index.html",
"icons": {
"16": "/app-icons/icon-16.png",
"32": "/app-icons/icon-32.png",
"48": "/app-icons/icon-48.png",
"60": "/app-icons/icon-60.png",
"90": "/app-icons/icon-90.png",
"120": "/app-icons/icon-120.png",
"128": "/app-icons/icon-128.png",
"256": "/app-icons/icon-256.png"
},
"developer": {
"name": "Luca Innocenti",
"url": "http://debiaonoldcomputers.blogspot.com/"
},
"locales": {
"en": {
"description": "A simple Mandelbrot Explorer with zoom function",
"developer": {
"url": "http://debiaonoldcomputers.blogspot.com/"
}
},
"it": {
"description": "Un semplice visualizzatore con zoomabile dell'insieme di Mandelbrot",
"developer": {
"url": "http://debiaonoldcomputers.blogspot.com/"
}
}
},
"default_locale": "en"
}
------------------------------------------------------------------------
index.html
------------------------------------------------------------------------
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<script src="./js/calcola.js"></script>
<canvas id="mandelbrot" height="400" width="320">
Il Browser non supporta HTML5.
<!-- Utile per inserire informazioni di default -->
</canvas><br>
<center><input type="button" id="reset" value="Reset"></center>
</body>
</html>
------------------------------------------------------------------------
calcola.js
------------------------------------------------------------------------
//inizializza le variabili
//dimensioni dello schermo
var SCREEN_HEIGHT = 400;
var SCREEN_WIDTH = 320;
//finestra di visualizzazione in coordinate Mandelbrot
var a1 = -2.0;
var b1 = -1.2;
var a2 = 1.0;
var b2 = 1.2;
//numero di cicli
var it = 25;
var delta_x ;
var delta_y;
// palette di 12 colori
var colori = ["#FFFFFF","#FF0000","#00FF00","#0000FF","#FF00FF","#FFFF00","#00FFFF","#FFaa00","#abcedf","#fedc0a","#ab16dd","#d00fba","#edabcc","#ddacff"];
//function punto(x1,y1,
function disegna(r_mi,i_mi,r_ma,i_ma,ite)
{
//effettua il calcolo dell'insieme date le dimensioni della finestra
var canvas = document.getElementById("mandelbrot");
if (canvas.getContext) {
var context = canvas.getContext("2d");
//rende il fondo nero
context.fillStyle="rgb(0,0,0)";
context.fillRect(0, 0, canvas.width, canvas.height);
var re_min = r_mi;
var im_min = i_mi;
var re_max = r_ma;
var im_max = i_ma;
var iterazioni = ite;
var r;
var a,b;
var x,y,x_new,y_new;
var test;
var k,j,i;
var re_factor = (re_max-re_min);
var im_factor = (im_max-im_min);
for (var i=0;i<SCREEN_HEIGHT;i++)
{
for (var j=0;j<SCREEN_WIDTH;j++)
{
a = re_min+(j*re_factor/SCREEN_WIDTH);
b = im_min+(i*im_factor/SCREEN_HEIGHT);
x = 0;
y = 0;
test = 0;
for (var k=0;k<iterazioni;k++)
{
x_new = (x*x)-(y*y)+a;
y_new = (2*x*y)+b;
if (((x_new*x_new)+(y_new*y_new))>4)
{
// colora il punto
r = k%12;
context.beginPath();
context.fillRect(j-1,i-1,1,1);
context.fillStyle=colori[r];
context.stroke();
break;
}
x = x_new;
y = y_new;
}
}
}
}
}
function doMouseDown(event)
{
canvas_x = event.pageX;
canvas_y = event.pageY;
//calcola il punto in coordinate Mandelbrot
var p1 = (((a2-a1)/SCREEN_WIDTH)*canvas_x) + a1;
var p2 = (((b2-b1)/SCREEN_HEIGHT)*canvas_y) + b1;
//calcola la dimensione della finestra (riduzione di 1/4)
var delta_x = (a2-a1)/4;
var delta_y = delta_x*(SCREEN_WIDTH/SCREEN_WIDTH);
//aumenta il numero di iterazioni per ogni ciclo
it = it + 75;
//definisce la finestra intorno al punto cliccato
a1 = p1 - delta_y;
b1 = p2 - delta_x;
a2 = p1 + delta_y;
b2 = p2 + delta_x;
disegna(a1,b1,a2,b2,it);
}
function res(event)
{
a1 = -2.0;
b1 = -1.2;
a2 = 1.0;
b2 = 1.2;
it = 25;
disegna(a1,b1,a2,b2,it);
}
window.onload = function()
{
//riscala alla dimensione della finestra
var canvas = document.getElementById("mandelbrot");
var button = document.getElementById("reset");
//aggiunge l'evento click del mouse
canvas.addEventListener("mousedown",doMouseDown,false);
button.addEventListener("click", res,false);
disegna(a1,b1,a2,b2,it);
}
Fractal Explorer in HTML5
Per provare a creare una applicazione per Firefox OS ho modificato il programmino in HTML5 per generare l'insieme di Mandelbrot per renderlo interattivo
In pratica cliccando sullo schermo con il mouse viene effettuato uno zoom centrato sul punto cliccato diminuendo la finestra di visualizzazione di 1/4
Il programma in esecuzione
Rispetto alla versione precedente e' stata creata anche una semplice palette da 12 colori
-----------------------------------------------------------
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</style>
</head>
<body>
<canvas id="mandelbrot" height="400" width="800">
Il Browser non supporta HTML5.
<!-- Utile per inserire informazioni di default -->
</canvas>
<script type="text/javascript">
//inizializza le variabili
//dimensioni dello schermo
var SCREEN_HEIGHT = 0;
var SCREEN_WIDTH = 0;
//finestra di visualizzazione in coordinate Mandelbrot
var a1 = -2.0;
var b1 = -1.2;
var a2 = 1.0;
var b2 = 1.2;
//numero di cicli
var it = 100;
var delta_x ;
var delta_y;
// palette di 12 colori
var colori = ["#FFFFFF","#FF0000","#00FF00","#0000FF","#FF00FF","#FFFF00","#00FFFF","#FFaa00","#abcedf","#fedc0a","#ab16dd","#d00fba","#edabcc","#ddacff"];
//function punto(x1,y1,
function disegna(r_mi,i_mi,r_ma,i_ma,ite)
{
//effettua il calcolo dell'insieme date le dimensioni della finestra
var canvas = document.getElementById("mandelbrot");
if (canvas.getContext) {
var context = canvas.getContext("2d");
//rende il fondo nero
context.fillStyle="rgb(0,0,0)";
context.fillRect(0, 0, canvas.width, canvas.height);
var re_min = r_mi;
var im_min = i_mi;
var re_max = r_ma;
var im_max = i_ma;
var iterazioni = ite;
var r;
var a,b;
var x,y,x_new,y_new;
var test;
var k,j,i;
var re_factor = (re_max-re_min);
var im_factor = (im_max-im_min);
for (var i=0;i<SCREEN_HEIGHT;i++)
{
for (var j=0;j<SCREEN_WIDTH;j++)
{
a = re_min+(j*re_factor/SCREEN_WIDTH);
b = im_min+(i*im_factor/SCREEN_HEIGHT);
x = 0;
y = 0;
test = 0;
for (var k=0;k<iterazioni;k++)
{
x_new = (x*x)-(y*y)+a;
y_new = (2*x*y)+b;
if (((x_new*x_new)+(y_new*y_new))>4)
{
// colora il punto
r = k%12;
context.beginPath();
context.fillRect(j,i,1,1);
context.fillStyle=colori[r];
context.stroke();
break;
}
x = x_new;
y = y_new;
}
}
}
}
}
function doMouseDown(event)
{
canvas_x = event.pageX;
canvas_y = event.pageY;
//calcola il punto in coordinate Mandelbrot
var p1 = (((a2-a1)/SCREEN_WIDTH)*canvas_x) + a1;
var p2 = (((b2-b1)/SCREEN_HEIGHT)*canvas_y) + b1;
//calcola la dimensione della finestra (riduzione di 1/4)
var delta_x = (a2-a1)/4;
var delta_y = delta_x*(SCREEN_WIDTH/SCREEN_WIDTH);
//aumenta il numero di iterazioni per ogni ciclo
it = it + 75;
//definisce la finestra intorno al punto cliccato
a1 = p1 - delta_y;
b1 = p2 - delta_x;
a2 = p1 + delta_y;
b2 = p2 + delta_x;
disegna(a1,b1,a2,b2,it);
}
window.onload = function()
{
//riscala alla dimensione della finestra
var canvas = document.getElementById("mandelbrot");
if (canvas.width < window.innerWidth)
{
canvas.width = window.innerWidth;
SCREEN_WIDTH = canvas.width;
}
if (canvas.height < window.innerHeight)
{
canvas.height = window.innerHeight;
SCREEN_HEIGHT = canvas.height;
}
//aggiunge l'evento click del mouse
canvas.addEventListener("mousedown",doMouseDown,false);
disegna(a1,b1,a2,b2,it);
}
</script>
</body>
</html>
In pratica cliccando sullo schermo con il mouse viene effettuato uno zoom centrato sul punto cliccato diminuendo la finestra di visualizzazione di 1/4
Il programma in esecuzione
Rispetto alla versione precedente e' stata creata anche una semplice palette da 12 colori
-----------------------------------------------------------
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</style>
</head>
<body>
<canvas id="mandelbrot" height="400" width="800">
Il Browser non supporta HTML5.
<!-- Utile per inserire informazioni di default -->
</canvas>
<script type="text/javascript">
//inizializza le variabili
//dimensioni dello schermo
var SCREEN_HEIGHT = 0;
var SCREEN_WIDTH = 0;
//finestra di visualizzazione in coordinate Mandelbrot
var a1 = -2.0;
var b1 = -1.2;
var a2 = 1.0;
var b2 = 1.2;
//numero di cicli
var it = 100;
var delta_x ;
var delta_y;
// palette di 12 colori
var colori = ["#FFFFFF","#FF0000","#00FF00","#0000FF","#FF00FF","#FFFF00","#00FFFF","#FFaa00","#abcedf","#fedc0a","#ab16dd","#d00fba","#edabcc","#ddacff"];
//function punto(x1,y1,
function disegna(r_mi,i_mi,r_ma,i_ma,ite)
{
//effettua il calcolo dell'insieme date le dimensioni della finestra
var canvas = document.getElementById("mandelbrot");
if (canvas.getContext) {
var context = canvas.getContext("2d");
//rende il fondo nero
context.fillStyle="rgb(0,0,0)";
context.fillRect(0, 0, canvas.width, canvas.height);
var re_min = r_mi;
var im_min = i_mi;
var re_max = r_ma;
var im_max = i_ma;
var iterazioni = ite;
var r;
var a,b;
var x,y,x_new,y_new;
var test;
var k,j,i;
var re_factor = (re_max-re_min);
var im_factor = (im_max-im_min);
for (var i=0;i<SCREEN_HEIGHT;i++)
{
for (var j=0;j<SCREEN_WIDTH;j++)
{
a = re_min+(j*re_factor/SCREEN_WIDTH);
b = im_min+(i*im_factor/SCREEN_HEIGHT);
x = 0;
y = 0;
test = 0;
for (var k=0;k<iterazioni;k++)
{
x_new = (x*x)-(y*y)+a;
y_new = (2*x*y)+b;
if (((x_new*x_new)+(y_new*y_new))>4)
{
// colora il punto
r = k%12;
context.beginPath();
context.fillRect(j,i,1,1);
context.fillStyle=colori[r];
context.stroke();
break;
}
x = x_new;
y = y_new;
}
}
}
}
}
function doMouseDown(event)
{
canvas_x = event.pageX;
canvas_y = event.pageY;
//calcola il punto in coordinate Mandelbrot
var p1 = (((a2-a1)/SCREEN_WIDTH)*canvas_x) + a1;
var p2 = (((b2-b1)/SCREEN_HEIGHT)*canvas_y) + b1;
//calcola la dimensione della finestra (riduzione di 1/4)
var delta_x = (a2-a1)/4;
var delta_y = delta_x*(SCREEN_WIDTH/SCREEN_WIDTH);
//aumenta il numero di iterazioni per ogni ciclo
it = it + 75;
//definisce la finestra intorno al punto cliccato
a1 = p1 - delta_y;
b1 = p2 - delta_x;
a2 = p1 + delta_y;
b2 = p2 + delta_x;
disegna(a1,b1,a2,b2,it);
}
window.onload = function()
{
//riscala alla dimensione della finestra
var canvas = document.getElementById("mandelbrot");
if (canvas.width < window.innerWidth)
{
canvas.width = window.innerWidth;
SCREEN_WIDTH = canvas.width;
}
if (canvas.height < window.innerHeight)
{
canvas.height = window.innerHeight;
SCREEN_HEIGHT = canvas.height;
}
//aggiunge l'evento click del mouse
canvas.addEventListener("mousedown",doMouseDown,false);
disegna(a1,b1,a2,b2,it);
}
</script>
</body>
</html>
venerdì 21 marzo 2014
Interazione tra Kinect ed XWindow con PyOpenNi
Francamente non amo Processing (a meno che non si declini come IDE di Arduino) ed dopo un po' di ricerche ho trovato il modo di pilotare Kinect mediante Python con la libreria PyOpenNi
L'installazione e' sostanzialmente manuale dai sorgenti con la procedura
git clone https://github.com/jmendeth/PyOpenNI.git
L'installazione e' sostanzialmente manuale dai sorgenti con la procedura
mkdir PyOpenNI-build
cd PyOpenNI-build
cmake ../PyOpenNI
(per soddisfare le dipendenze sudo apt-get install cmake build-essential git-core \ python-dev libboost-python-dev)
Al termine della compilazione non c'e' la possibilita' di fare make install in quanto e' lasciato all'utente la fase finale. Per i miei scopi la cosa piu' semplice e' stata quella di spostare il file openni.so dalla PyOpenNI-build/lib nella cartella degli esempi
Un esempio veramente interessate e' dato da hand-tracker.py che, brevemente modificato, puo' essere utilizzato per creare un mouse virtuale per X (nel brevissimo esempio se la mano si trova tra 750 e 1000 mm dal Kinect viene agganciato il Kinect, se si trova a meno di 750 mm si fa clic sul punto del mouse....lo script e' ampiamente migliorabile)
------------------------------------------------
#! /usr/bin/python
from openni import *
import os
context = Context()
context.init()
depth_generator = DepthGenerator()
depth_generator.create(context)
depth_generator.set_resolution_preset(RES_VGA)
depth_generator.fps = 30
gesture_generator = GestureGenerator()
gesture_generator.create(context)
gesture_generator.add_gesture('Wave')
hands_generator = HandsGenerator()
hands_generator.create(context)
# Declare the callbacks
# gesture
def gesture_detected(src, gesture, id, end_point):
print "Detected gesture:", gesture
hands_generator.start_tracking(end_point)
# gesture_detected
def gesture_progress(src, gesture, point, progress): pass
# gesture_progress
def create(src, id, pos, time):
print 'Create ', id, pos
# create
def update(src, id, pos, time):
print 'Update ', id, pos
x = pos[0]+300
y = pos[1]+300
if (x < 0):
x = 0
if (y < 0):
y = 0
if ((pos[2] >750) and (pos[2] < 1000)):
os.system("xdotool mousemove "+ str(x)+" "+str(y))
if (pos[2] <750):
os.system("xdotool mousemove "+ str(x)+" "+str(y) + " click 1")
# update
def destroy(src, id, time):
print 'Destroy ', id
# destroy
# Register the callbacks
gesture_generator.register_gesture_cb(gesture_detected, gesture_progress)
hands_generator.register_hand_cb(create, update, destroy)
# Start generating
context.start_generating_all()
print 'Make a Wave to start tracking...'
while True:
context.wait_any_update_all()
# while
(per soddisfare le dipendenze sudo apt-get install cmake build-essential git-core \ python-dev libboost-python-dev)
Al termine della compilazione non c'e' la possibilita' di fare make install in quanto e' lasciato all'utente la fase finale. Per i miei scopi la cosa piu' semplice e' stata quella di spostare il file openni.so dalla PyOpenNI-build/lib nella cartella degli esempi
Un esempio veramente interessate e' dato da hand-tracker.py che, brevemente modificato, puo' essere utilizzato per creare un mouse virtuale per X (nel brevissimo esempio se la mano si trova tra 750 e 1000 mm dal Kinect viene agganciato il Kinect, se si trova a meno di 750 mm si fa clic sul punto del mouse....lo script e' ampiamente migliorabile)
------------------------------------------------
#! /usr/bin/python
from openni import *
import os
context = Context()
context.init()
depth_generator = DepthGenerator()
depth_generator.create(context)
depth_generator.set_resolution_preset(RES_VGA)
depth_generator.fps = 30
gesture_generator = GestureGenerator()
gesture_generator.create(context)
gesture_generator.add_gesture('Wave')
hands_generator = HandsGenerator()
hands_generator.create(context)
# Declare the callbacks
# gesture
def gesture_detected(src, gesture, id, end_point):
print "Detected gesture:", gesture
hands_generator.start_tracking(end_point)
# gesture_detected
def gesture_progress(src, gesture, point, progress): pass
# gesture_progress
def create(src, id, pos, time):
print 'Create ', id, pos
# create
def update(src, id, pos, time):
print 'Update ', id, pos
x = pos[0]+300
y = pos[1]+300
if (x < 0):
x = 0
if (y < 0):
y = 0
if ((pos[2] >750) and (pos[2] < 1000)):
os.system("xdotool mousemove "+ str(x)+" "+str(y))
if (pos[2] <750):
os.system("xdotool mousemove "+ str(x)+" "+str(y) + " click 1")
# update
def destroy(src, id, time):
print 'Destroy ', id
# destroy
# Register the callbacks
gesture_generator.register_gesture_cb(gesture_detected, gesture_progress)
hands_generator.register_hand_cb(create, update, destroy)
# Start generating
context.start_generating_all()
print 'Make a Wave to start tracking...'
while True:
context.wait_any_update_all()
# while
Realta' aumentata con Aruco JS e WebGL
Una piccola introduzione: le librerie di realta' aumentata non gestiscono la fase di creazione degli oggetti 3D per cui si deve avere come integrazione una libreria grafica 3D
Detto cio' viene mostrato un breve esempio di come gestire un marker per la realta' aumentata catturato da una WebCam e creare un oggetto 3D che interagisca con il marker, il tutto gestito all'interno di un browser (in questo caso Firefox...Chrome non ha funzionato ma credo che sia dovuto ai permessi della webcam)
La libreria di AR utilizzata e' stata ArucoJS, la versione javascript di una libreria disponibile anche in C
Per prima cosa si deve stampare il marker (attenzione, una volta stampato il marker non deve essere tagliato a filo con il bordo nero ma deve essere lasciata una striscia bianca all'esterno del quadrato nero, altrimenti il marker non viene riconosciuto)
successivamente ho leggermente modificato l'esempio debug-posit presente nella cartella samples per generare un cubo al di sopra del marker con i comandi WebGL. Le modifiche, veramente minime, sono indicate dal segno giallo nel sorgente
Da notare che quando e' in esecuzione la pagina su Firefox, il processore entra in funzione in modo pesante con accensione delle ventole (su MacBook) e la pagina diventa un po' scattosa
il progetto puo' essere scaricato da questo link
---------------------------------------------------------------------------
<html>
<head>
<title>Augmented Reality</title>
<script type="text/javascript" src="libs/Three.js"></script>
<script type="text/javascript" src="svd.js"></script>
<script type="text/javascript" src="posit1.js"></script>
<script type="text/javascript" src="cv.js"></script>
<script type="text/javascript" src="aruco.js"></script>
<script>
var video, canvas, context, imageData, detector, posit;
var renderer1, renderer2, renderer3;
var scene1, scene2, scene3, scene4;
var camera1, camera2, camera3, camera4;
var plane1, plane2, model, texture;
var step = 0.0;
var modelSize = 35.0; //millimeters
function onLoad(){
video = document.getElementById("video");
canvas = document.getElementById("canvas");
context = canvas.getContext("2d");
canvas.width = parseInt(canvas.style.width);
canvas.height = parseInt(canvas.style.height);
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
if (navigator.getUserMedia){
init();
}
};
function init(){
navigator.getUserMedia({video:true},
function (stream){
if (window.webkitURL) {
video.src = window.webkitURL.createObjectURL(stream);
} else if (video.mozSrcObject !== undefined) {
video.mozSrcObject = stream;
} else {
video.src = stream;
}
},
function(error){
}
);
detector = new AR.Detector();
posit = new POS.Posit(modelSize, canvas.width);
createRenderers();
createScenes();
requestAnimationFrame(tick);
};
function tick(){
requestAnimationFrame(tick);
if (video.readyState === video.HAVE_ENOUGH_DATA){
snapshot();
var markers = detector.detect(imageData);
drawCorners(markers);
updateScenes(markers);
render();
}
};
function snapshot(){
context.drawImage(video, 0, 0, canvas.width, canvas.height);
imageData = context.getImageData(0, 0, canvas.width, canvas.height);
};
function drawCorners(markers){
var corners, corner, i, j;
context.lineWidth = 3;
for (i = 0; i < markers.length; ++ i){
corners = markers[i].corners;
context.strokeStyle = "red";
context.beginPath();
for (j = 0; j < corners.length; ++ j){
corner = corners[j];
context.moveTo(corner.x, corner.y);
corner = corners[(j + 1) % corners.length];
context.lineTo(corner.x, corner.y);
}
context.stroke();
context.closePath();
context.strokeStyle = "green";
context.strokeRect(corners[0].x - 2, corners[0].y - 2, 4, 4);
}
};
function createRenderers(){
renderer1 = new THREE.WebGLRenderer();
renderer1.setClearColorHex(0xffff00, 1);
renderer1.setSize(canvas.width, canvas.height);
document.getElementById("container1").appendChild(renderer1.domElement);
scene1 = new THREE.Scene();
camera1 = new THREE.PerspectiveCamera(40, canvas.width / canvas.height, 1, 1000);
scene1.add(camera1);
renderer2 = new THREE.WebGLRenderer();
renderer2.setClearColorHex(0xffff00, 1);
renderer2.setSize(canvas.width, canvas.height);
document.getElementById("container2").appendChild(renderer2.domElement);
scene2 = new THREE.Scene();
camera2 = new THREE.PerspectiveCamera(40, canvas.width / canvas.height, 1, 1000);
scene2.add(camera2);
renderer3 = new THREE.WebGLRenderer();
renderer3.setClearColorHex(0xffffff, 1);
renderer3.setSize(canvas.width, canvas.height);
document.getElementById("container").appendChild(renderer3.domElement);
scene3 = new THREE.Scene();
camera3 = new THREE.OrthographicCamera(-0.5, 0.5, 0.5, -0.5);
scene3.add(camera3);
scene4 = new THREE.Scene();
camera4 = new THREE.PerspectiveCamera(40, canvas.width / canvas.height, 1, 1000);
scene4.add(camera4);
};
function render(){
renderer1.clear();
renderer1.render(scene1, camera1);
renderer2.clear();
renderer2.render(scene2, camera2);
renderer3.autoClear = false;
renderer3.clear();
renderer3.render(scene3, camera3);
renderer3.render(scene4, camera4);
};
function createScenes(){
plane1 = createPlane();
scene1.add(plane1);
plane2 = createPlane();
scene2.add(plane2);
texture = createTexture();
scene3.add(texture);
model = createModel();
scene4.add(model);
};
function createPlane(){
var object = new THREE.Object3D(),
geometry = new THREE.PlaneGeometry(1.0, 1.0, 0.0),
material = new THREE.MeshNormalMaterial(),
mesh = new THREE.Mesh(geometry, material);
object.add(mesh);
return object;
};
function createTexture(){
var texture = new THREE.Texture(video),
object = new THREE.Object3D(),
geometry = new THREE.PlaneGeometry(1.0, 1.0, 0.0),
material = new THREE.MeshBasicMaterial( {map: texture, depthTest: false, depthWrite: false} ),
mesh = new THREE.Mesh(geometry, material);
object.position.z = -1;
object.add(mesh);
return object;
};
function createModel(){
var object = new THREE.Object3D(),
cubo_geometry = new THREE.CubeGeometry(1,1,1),
cubo_material = new THREE.MeshNormalMaterial(),
mesh = new THREE.Mesh(cubo_geometry, cubo_material);
object.add(mesh);
return object;
};
function updateScenes(markers){
var corners, corner, pose, i;
if (markers.length > 0){
corners = markers[0].corners;
for (i = 0; i < corners.length; ++ i){
corner = corners[i];
corner.x = corner.x - (canvas.width / 2);
corner.y = (canvas.height / 2) - corner.y;
}
pose = posit.pose(corners);
updateObject(plane1, pose.bestRotation, pose.bestTranslation);
updateObject(plane2, pose.alternativeRotation, pose.alternativeTranslation);
updateObject(model, pose.bestRotation, pose.bestTranslation);
updatePose("pose1", pose.bestError, pose.bestRotation, pose.bestTranslation);
updatePose("pose2", pose.alternativeError, pose.alternativeRotation, pose.alternativeTranslation);
step += 0.025;
model.rotation.z -= step;
}
texture.children[0].material.map.needsUpdate = true;
};
function updateObject(object, rotation, translation){
object.scale.x = modelSize;
object.scale.y = modelSize;
object.scale.z = modelSize;
object.rotation.x = -Math.asin(-rotation[1][2]);
object.rotation.y = -Math.atan2(rotation[0][2], rotation[2][2]);
object.rotation.z = Math.atan2(rotation[1][0], rotation[1][1]);
object.position.x = translation[0];
object.position.y = translation[1];
object.position.z = -translation[2];
};
function updatePose(id, error, rotation, translation){
var yaw = -Math.atan2(rotation[0][2], rotation[2][2]);
var pitch = -Math.asin(-rotation[1][2]);
var roll = Math.atan2(rotation[1][0], rotation[1][1]);
var d = document.getElementById(id);
d.innerHTML = " error: " + error
+ "<br/>"
+ " x: " + (translation[0] | 0)
+ " y: " + (translation[1] | 0)
+ " z: " + (translation[2] | 0)
+ "<br/>"
+ " yaw: " + Math.round(-yaw * 180.0/Math.PI)
+ " pitch: " + Math.round(-pitch * 180.0/Math.PI)
+ " roll: " + Math.round(roll * 180.0/Math.PI);
};
window.onload = onLoad;
</script>
</head>
<body style="text-align: center; font-family: monospace;">
<video id="video" width=320 height=240 autoplay="true" style="display:none;"></video>
<div style="margin: 10px;"><strong>-= Augmented Reality =-</strong></div>
<div style="width: 100%;">
<div style="width: 650px; margin-left:auto; margin-right:auto;">
<canvas id="canvas" style="width: 320px; height: 240px; float: left; border: solid 1px black;"></canvas>
<div id="container" style="width: 320px; height: 240px; float: left; border: solid 1px black; background: green;"></div>
<div style="clear: both;"></div>
<div style="float: left; border: solid 1px black;">
<div id="container1" style="width: 320px; height: 240px; background: red;"></div>
<div id="pose1"></div>
</div>
<div style="float: left; border: solid 1px black;">
<div id="container2" style="width: 320px; height: 240px; background: blue;"></div>
<div id="pose2"></div>
</div>
</div>
</div>
<div style="clear: both;"></div>
<div style="margin: 15px;"><strong>Powered by <a href="http://code.google.com/p/js-aruco/">js-aruco</a> and <a href="https://github.com/mrdoob/three.js">Three.js</a></strong></div>
</body>
</html>
Detto cio' viene mostrato un breve esempio di come gestire un marker per la realta' aumentata catturato da una WebCam e creare un oggetto 3D che interagisca con il marker, il tutto gestito all'interno di un browser (in questo caso Firefox...Chrome non ha funzionato ma credo che sia dovuto ai permessi della webcam)
La libreria di AR utilizzata e' stata ArucoJS, la versione javascript di una libreria disponibile anche in C
successivamente ho leggermente modificato l'esempio debug-posit presente nella cartella samples per generare un cubo al di sopra del marker con i comandi WebGL. Le modifiche, veramente minime, sono indicate dal segno giallo nel sorgente
Da notare che quando e' in esecuzione la pagina su Firefox, il processore entra in funzione in modo pesante con accensione delle ventole (su MacBook) e la pagina diventa un po' scattosa
il progetto puo' essere scaricato da questo link
---------------------------------------------------------------------------
<html>
<head>
<title>Augmented Reality</title>
<script type="text/javascript" src="libs/Three.js"></script>
<script type="text/javascript" src="svd.js"></script>
<script type="text/javascript" src="posit1.js"></script>
<script type="text/javascript" src="cv.js"></script>
<script type="text/javascript" src="aruco.js"></script>
<script>
var video, canvas, context, imageData, detector, posit;
var renderer1, renderer2, renderer3;
var scene1, scene2, scene3, scene4;
var camera1, camera2, camera3, camera4;
var plane1, plane2, model, texture;
var step = 0.0;
var modelSize = 35.0; //millimeters
function onLoad(){
video = document.getElementById("video");
canvas = document.getElementById("canvas");
context = canvas.getContext("2d");
canvas.width = parseInt(canvas.style.width);
canvas.height = parseInt(canvas.style.height);
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
if (navigator.getUserMedia){
init();
}
};
function init(){
navigator.getUserMedia({video:true},
function (stream){
if (window.webkitURL) {
video.src = window.webkitURL.createObjectURL(stream);
} else if (video.mozSrcObject !== undefined) {
video.mozSrcObject = stream;
} else {
video.src = stream;
}
},
function(error){
}
);
detector = new AR.Detector();
posit = new POS.Posit(modelSize, canvas.width);
createRenderers();
createScenes();
requestAnimationFrame(tick);
};
function tick(){
requestAnimationFrame(tick);
if (video.readyState === video.HAVE_ENOUGH_DATA){
snapshot();
var markers = detector.detect(imageData);
drawCorners(markers);
updateScenes(markers);
render();
}
};
function snapshot(){
context.drawImage(video, 0, 0, canvas.width, canvas.height);
imageData = context.getImageData(0, 0, canvas.width, canvas.height);
};
function drawCorners(markers){
var corners, corner, i, j;
context.lineWidth = 3;
for (i = 0; i < markers.length; ++ i){
corners = markers[i].corners;
context.strokeStyle = "red";
context.beginPath();
for (j = 0; j < corners.length; ++ j){
corner = corners[j];
context.moveTo(corner.x, corner.y);
corner = corners[(j + 1) % corners.length];
context.lineTo(corner.x, corner.y);
}
context.stroke();
context.closePath();
context.strokeStyle = "green";
context.strokeRect(corners[0].x - 2, corners[0].y - 2, 4, 4);
}
};
function createRenderers(){
renderer1 = new THREE.WebGLRenderer();
renderer1.setClearColorHex(0xffff00, 1);
renderer1.setSize(canvas.width, canvas.height);
document.getElementById("container1").appendChild(renderer1.domElement);
scene1 = new THREE.Scene();
camera1 = new THREE.PerspectiveCamera(40, canvas.width / canvas.height, 1, 1000);
scene1.add(camera1);
renderer2 = new THREE.WebGLRenderer();
renderer2.setClearColorHex(0xffff00, 1);
renderer2.setSize(canvas.width, canvas.height);
document.getElementById("container2").appendChild(renderer2.domElement);
scene2 = new THREE.Scene();
camera2 = new THREE.PerspectiveCamera(40, canvas.width / canvas.height, 1, 1000);
scene2.add(camera2);
renderer3 = new THREE.WebGLRenderer();
renderer3.setClearColorHex(0xffffff, 1);
renderer3.setSize(canvas.width, canvas.height);
document.getElementById("container").appendChild(renderer3.domElement);
scene3 = new THREE.Scene();
camera3 = new THREE.OrthographicCamera(-0.5, 0.5, 0.5, -0.5);
scene3.add(camera3);
scene4 = new THREE.Scene();
camera4 = new THREE.PerspectiveCamera(40, canvas.width / canvas.height, 1, 1000);
scene4.add(camera4);
};
function render(){
renderer1.clear();
renderer1.render(scene1, camera1);
renderer2.clear();
renderer2.render(scene2, camera2);
renderer3.autoClear = false;
renderer3.clear();
renderer3.render(scene3, camera3);
renderer3.render(scene4, camera4);
};
function createScenes(){
plane1 = createPlane();
scene1.add(plane1);
plane2 = createPlane();
scene2.add(plane2);
texture = createTexture();
scene3.add(texture);
model = createModel();
scene4.add(model);
};
function createPlane(){
var object = new THREE.Object3D(),
geometry = new THREE.PlaneGeometry(1.0, 1.0, 0.0),
material = new THREE.MeshNormalMaterial(),
mesh = new THREE.Mesh(geometry, material);
object.add(mesh);
return object;
};
function createTexture(){
var texture = new THREE.Texture(video),
object = new THREE.Object3D(),
geometry = new THREE.PlaneGeometry(1.0, 1.0, 0.0),
material = new THREE.MeshBasicMaterial( {map: texture, depthTest: false, depthWrite: false} ),
mesh = new THREE.Mesh(geometry, material);
object.position.z = -1;
object.add(mesh);
return object;
};
function createModel(){
var object = new THREE.Object3D(),
cubo_geometry = new THREE.CubeGeometry(1,1,1),
cubo_material = new THREE.MeshNormalMaterial(),
mesh = new THREE.Mesh(cubo_geometry, cubo_material);
object.add(mesh);
return object;
};
function updateScenes(markers){
var corners, corner, pose, i;
if (markers.length > 0){
corners = markers[0].corners;
for (i = 0; i < corners.length; ++ i){
corner = corners[i];
corner.x = corner.x - (canvas.width / 2);
corner.y = (canvas.height / 2) - corner.y;
}
pose = posit.pose(corners);
updateObject(plane1, pose.bestRotation, pose.bestTranslation);
updateObject(plane2, pose.alternativeRotation, pose.alternativeTranslation);
updateObject(model, pose.bestRotation, pose.bestTranslation);
updatePose("pose1", pose.bestError, pose.bestRotation, pose.bestTranslation);
updatePose("pose2", pose.alternativeError, pose.alternativeRotation, pose.alternativeTranslation);
step += 0.025;
model.rotation.z -= step;
}
texture.children[0].material.map.needsUpdate = true;
};
function updateObject(object, rotation, translation){
object.scale.x = modelSize;
object.scale.y = modelSize;
object.scale.z = modelSize;
object.rotation.x = -Math.asin(-rotation[1][2]);
object.rotation.y = -Math.atan2(rotation[0][2], rotation[2][2]);
object.rotation.z = Math.atan2(rotation[1][0], rotation[1][1]);
object.position.x = translation[0];
object.position.y = translation[1];
object.position.z = -translation[2];
};
function updatePose(id, error, rotation, translation){
var yaw = -Math.atan2(rotation[0][2], rotation[2][2]);
var pitch = -Math.asin(-rotation[1][2]);
var roll = Math.atan2(rotation[1][0], rotation[1][1]);
var d = document.getElementById(id);
d.innerHTML = " error: " + error
+ "<br/>"
+ " x: " + (translation[0] | 0)
+ " y: " + (translation[1] | 0)
+ " z: " + (translation[2] | 0)
+ "<br/>"
+ " yaw: " + Math.round(-yaw * 180.0/Math.PI)
+ " pitch: " + Math.round(-pitch * 180.0/Math.PI)
+ " roll: " + Math.round(roll * 180.0/Math.PI);
};
window.onload = onLoad;
</script>
</head>
<body style="text-align: center; font-family: monospace;">
<video id="video" width=320 height=240 autoplay="true" style="display:none;"></video>
<div style="margin: 10px;"><strong>-= Augmented Reality =-</strong></div>
<div style="width: 100%;">
<div style="width: 650px; margin-left:auto; margin-right:auto;">
<canvas id="canvas" style="width: 320px; height: 240px; float: left; border: solid 1px black;"></canvas>
<div id="container" style="width: 320px; height: 240px; float: left; border: solid 1px black; background: green;"></div>
<div style="clear: both;"></div>
<div style="float: left; border: solid 1px black;">
<div id="container1" style="width: 320px; height: 240px; background: red;"></div>
<div id="pose1"></div>
</div>
<div style="float: left; border: solid 1px black;">
<div id="container2" style="width: 320px; height: 240px; background: blue;"></div>
<div id="pose2"></div>
</div>
</div>
</div>
<div style="clear: both;"></div>
<div style="margin: 15px;"><strong>Powered by <a href="http://code.google.com/p/js-aruco/">js-aruco</a> and <a href="https://github.com/mrdoob/three.js">Three.js</a></strong></div>
</body>
</html>
Iscriviti a:
Post (Atom)
Debugger integrato ESP32S3
Aggiornamento In realta' il Jtag USB funziona anche sui moduli cinesi Il problema risiede nell'ID USB della porta Jtag. Nel modulo...
-
In questo post viene indicato come creare uno scatterplot dinamico basato da dati ripresi da un file csv (nel dettaglio il file csv e' c...
-
La scheda ESP32-2432S028R monta un Esp Dev Module con uno schermo TFT a driver ILI9341 di 320x240 pixels 16 bit colore.Il sito di riferiment...
-
Questo post e' a seguito di quanto gia' visto nella precedente prova Lo scopo e' sempre il solito: creare un sistema che permet...