Bernhard Häussner
Journal: Neueste Artikel erscheinen hier (Seite 10 von 23)

3D-Perspektive mit einer kurzen Formel

04.09.2009, 19:07
3D-Effekt mit Javascript

3D-Effekt mit Javascript

Für die Gestaltung von 3D-Effekten, sind zwar duzende Hardware-beschleunigte Toolkits parat, doch im Fall von HTML-Canvas bzw. auf Webseiten generell sieht das Angebot nicht so reich aus. Das hat mich dazu bewegt, selbst einen kleinen Blick in die Mathematik hinter 3D-Projektionen/3D-Perspektive zu werfen. Meine Erkenntnisse konnte ich in einer kurzen Formel zusammenfassen.

Wer sich nicht für die Mathematik interessiert, kann natürlich flash oder eine der vielen Canvas-3D-Librarys verwenden.

Das Prinzip hinter der 3D-Projektion ist meist das einer Lochkamera. Lichtstrahlen fallen vom abzubildenden Punkt im Raum durch ein Loch und auf einen Schirm. Diese Art der Projektion war eine der ersten bekannten und mit ihrer alten Bezeichnung „camera obscura“ (lat. für dunkle Kammer, die in der der Schirm angebracht war) namensgebend für die heute auf Linsenoptik basierenden Kameras.

Die Lochkamera ist leider nicht so lichtstark wie die Linsenoptik und ihre Schärfe ändert sich nicht mit der Entfernung, sondern mit der Größe des Lochs.

Im Gegensatz zur Linsenoptik ist die Lochkamera aber durch den einfachen Strahlensatz zu berechnen. Mit dem Strahlensatz kann man ganz einfach die Koordinaten eines Punktes im Raum umrechnen in Koordinaten auf dem Schirm. Dazu genügt folgende Funktion:

F:ℝ³→ℝ², F(x,y,z)= P( x*d / z | y*d / z ) ; d: distance to screen

Dass diese Funktion klappt, zeigt dieses Beispiel. Hier eine Graphische Erläuterung:

Strahlensatz für Perspektive. Schwarz: Abzubildender Punkt/Lichtstrahl, Blau: Kamera, Grün: Kameraparameter Bildweite, Rot: Koordinate auf dem Schirm, Lila: Ursprungskoordinaten

Strahlensatz für Perspektive. Schwarz: Abzubildender Punkt/Lichtstrahl, Blau: Kamera, Grün: Kameraparameter Bildweite, Rot: Koordinate auf dem Schirm, Lila: Ursprungskoordinaten

Wegen dem Strahlensatz gilt x' / x = d / z, aufgelöst x' = d*x / z, analog für die y-Koordinate.

Etwas logischer wäre vielleicht der Schirm hinter dem Loch, vor allem da Objekte näher an die Kamera heran kommen können, doch so kann man sich den Schirm wie den Computerbildschirm vorstellen, durch den man in die Raumillusion hineinschaut.

Diese Formel erledigt die Abbildung in der Kamera. Jedoch muss der Punkt bereits in Koordinaten relativ zur Kamera gegeben sein. Da man normalerweise Punkte zunächst durch ein Weltkoordinatensystem definiert, muss man sie erst transformieren.

Bei meinem einfachen Beispiel beschränkt sich die Transformation zunächst auf eine Verschiebung entlang der z-Achse und später habe ich noch eine Rotation um die y-Achse hinzugefügt. Diese Koordinatenumrechnungen lassen sich in Transformationsmatrizen beschreiben.

Dann fehlt eigentlich nur noch die Darstellung. Im Beispiel wird der Text durch einfache CSS-Manipulationen an die richtige Position gebracht, und die Kreise werden mit dem canvas-Element gerendert.

Mit einem solchen Modell lassen sich schon einfache Drahtgitter problemlos darstellen (Demo), da eine Kante zwischen zwei Punkten im Raum auch eine Strecke zwischen zwei Punkten auf dem Schirm darstellt, und uns somit die Berechnung dieser Bildpunkte erspart. Für eine ausgefeiltere Darstellung kann man Dreiecke oder Polygone verwenden (Demo), für die ungefähr das selbe gilt, nur dass z.B. ihr Winkel zu einer Lichtquelle für die Kolorierung verwendet werden kann

Für nahezu realistische Lichteffekte bedarf es allerdings eines anderen Modells, genannt Raytracing, bei dem man den Weg rückwärts geht und bei der Kamera anfängt. Das wurde übrigens auch schon in Javascript umgesetzt, ist aber für Echtzeit-Anwendungen eher ungeeignet (vielleicht kommen bald die ersten Computerspiele mit der - in Realtime - noch neuen Technik).

Kommentare: 3 Einträge

jQuery Plugin intoViewport

06.08.2009, 20:08

Das Plugin intoViewport erledigt als Erweiterung zu der populären Javascript-Bibliothek jQuery das scrollen bestimmter HTML-Elemente einer Webseite in den Sichtbereich des Browsers, aber nur wenn nötig und nur so weit wie nötig. Es hat nur eine Größe von 480 Byte (minified).

Demo

Dieses minimale jQuery-Beispiel würde den 4. Link auf der Seite in das Darstellungsfeld des Betrachters scollen:

$('a').eq(4).intoViewport();

Scrollen - so viel wie nötig und so wenig wie möglich

Das Script vergleicht jeweils Höhe und Y-Position des übergebenen DOM-Elements und des aktuell sichtbaren Seitenausschnitt des Browsers (Viewport).

Sollte das Element über den Viewport herausragen, oder gar komplett über diesem sein, scrollt es so weit nach oben, bis das gesamte Element sichtbar ist, also wird die Oberkante des Ausschnitts an die Oberkante des Elements verschoben.

Sollte das Element hingen weiter unten liegen, scrollt es nach unten, und wiederum nur so weit, dass es Element gerade sichtbar ist.

Ist das Element schon komplett im Viewport, so tut es überhaupt nichts. Die Scroll-Maßnahmen sind möglichst restriktiv gehalten, da scrollen ungefragt, oder gar sinnlos, den Benutzer der Seite für gewöhnlich verwirrt.

Im Fall, dass das Element größer als das Browserfenster ist, versucht das Plugin möglichst viel des Elements anzuzeigen und scrollt die Oberkante des Elements an den Oberen Bildschirmrand, sodass man immer noch oben beginnen kann zu Lesen.

Wieso, Weshalb, Warum?

Das Plugin soll verhindern, dass, beispielsweise mit jQuery, später eingeblendete Elemente vom Surfer unbemerkt erscheinen, wie Warnmeldungen.

Benötigt habe ich das Plugin auch für meine Suchfunktion, wo nach Möglichkeit die Ergebnisliste komplett angezeigt werden soll. Denn hier sind versteckte Teile fatal: man tippt (meist) mit beiden Händen und kann daher nicht scrollen. Außerdem wird bei der Tastaturnavigation durch die Suchergebnisse der Bildschirmausschnitt dem gewählten Ergebnis folgen.

Die Einsatzgebiete sind sicher noch vielfältiger und der Kreativität sind, wie immer, keine Grenzen gesetzt.

Es grenzt sich von der Javascript-Funktion scrollIntoView() dadurch ab, dass es sanft (animiert) scrollen kann, dadurch den User nicht total orientierungslos lässt. Zwar gab es schon Lösungen zum sanften Scrollen mit jQuery, aber diese beinhalteten leider nicht den Test, ob und wie weit gescrollt werden muss.

Der Javascript-Quelltext

Für Interessierte, hier der Sourcecode des Plugins:

(function($) {
  jQuery.fn.intoViewport = function(options) {
    options = $.extend({
      // Configuration
      // Add whatever options animate schould get by default
      duration:  200,
      easing: "swing"
    }, options || {});
    return this.each(function(){
      // scroll to certain destination:
      function scrTo(dest) {
        $("html,body").stop().animate({ scrollTop: dest}, options );
      }
      var
        //current viewport Y-position
        scr=$(document).scrollTop()||$(window).scrollTop(),
        // viewport Y-size
        wheight=$(window).height(),
        // element Y-position
        top=$(this).offset().top,
        // element Y-size
        eheight=$(this).outerHeight();
        // case element before viewport:
        if (scr>top) {
          scrTo(top); // scroll up to element
        // case viewport before element (bottom part of e. outside):
        } else if (scr!=top && top+eheight>scr+wheight) { 
          // scroll down till everything is inside
          scrTo(top+Math.min(eheight-wheight,0));
          //              ^ but don't hide top part again
        }
      });
    };
})(jQuery); //compatibility

Ich habe das Plugin allerdings nicht nicht in zu vielen Browsern und Situationen getestet. Daher könnte es sein, dass es in einigen Browsern nicht funktioniert. Auch könnte es Probleme geben, wenn die Elemente innerhalb von scrollbaren Elementen liegen, wie <div>s mit der CSS-Eigenschaft overflow:auto;.

Kommentare: 2 Einträge

Linux Backup Server

02.08.2009, 13:04
Backup Server

Backup Server

Da die Anzahl „wichtiger“ elektronischer Dokumente, hauptsächlich Rechnungen, wächst, und ich auch sonst nichts vermissen will, sichere ich meine Daten hin und wieder auf einem Backup-Server. Wie ich das mache und was man noch mit einem solchen Server anfangen kann:

Volldatensicherung

Für die komplette Sicherung des /home-Ordners erstelle ich tar-Archive.

Um einfach auf die Backuplaufwerke zugreifen zu können, kann man SMB-Freigaben mounten:

mount -t cifs -o username=bernhard //server/backup /mnt/backup/
#Beispielhafter-Aufruf:
tar -czvf /mnt/backup/pc1/`date +%Y-%m-%d`-amoebes.tar.gz\
 -X /home/amoebe/tarexclude\
 /home/amoebe >> /mnt/backup/pc1/last.log 2>&1

Es geht aber natürlich auch über eine Pipe zu SSH:

tar -czf - -X /home/amoebe/tarexclude /home/amoebe\
 | ssh server "cd /home/bernhard/backup/pc1/\
 && cat - > `date +%Y-%m-%d`-amoebes.tar.gz"

Weil ich der eigenen Samba-(nicht)-Konfiguration kaum vertraue, und ich auch nicht immer 100%ig weiß, was CIFS anrichtet, ist mir SSH lieber. Die Verschlüsselung von SSH kostet zumindest nicht zu viel Zeit - das Backup dauert ohnehin nur rund 7 Minuten bei den gut 2 GB. Und SSH ist fast immer verfügbar, während Samba nicht überall installiert ist.

Da man temporäre Dateien, wie den Mülleimer, nicht zu sichern braucht, kann man sie vom Backup ausschließen. Dazu hat tar eine eingebaute Filterfunktion, die aus einer Datei eine Liste von Mustern auslesen kann, welche Dateien nicht in das Archiv kommen. Für mein OpenSUSE/KDE habe ich mir folgende Excludes für tar zusammengestellt:

/home/amoebe/tarexclude:
.local/share/Trash/files/*
.thumbnails/*
.beagle
.gvfs
*~
*.swp

Inkrementelles Backup

Für größere Datenmengen ist das Vollbackup nicht mehr zu praktikabel, weshalb man nur noch die Änderungen seit dem letzten Backup überträgt. Dazu ist rsync das passende Tool und bedient sich so:

rsync -vaP --exclude=Backups/ -e ssh amoebe@192.168.1.36:/mnt/lib\
 /home/bernhard/backup/lib/
#Allgemein: rsync [Quelle] [Ziel]

rsync kann SSH gleich mit benutzen. Dieser Befehl läuft auf dem Backup-Server z.B. in einem Cron-Job. Er wird die Dateien vergleichen und neue Dateien bzw. veränderte Dateien übertragen. Es wird allerdings kein Archiv erstellt. Mit diesem Befehl halte ich eine Kopie meiner Datensammlung synchronisiert, was bei jedem Durchlauf ungefähr 2 Minuten braucht (bei 57 GB).

Sharing

Ein Vorteil von einem solchen NAS-ähnlichem Backup-Server ist, dass man auch gleich gut Dateien zwischen Computern im Netzwerk teilen kann. So habe ich auf den Server ein Software-Repository, in dem sich duzende von Windows-Installern, jQuery-Plugins und ähnlichem tummeln. Dazu bastelt man zunächst ein Ordner mit Setuid-Bit, welches beim Erstellen von Dateien die Gruppenzugehörigkeit auf die des Ordners setzt. Oder man ändert einen Ordner nachträglich:

chgrp -R sharer /srv/share
find /srv/share/* -type d -exec chmod g+srw {} \;

So kann dann eine ganze Benutzergruppe den Ordner teilen. Eigentlich bräuchte man dazu noch ein nettes Webinterface mit einer Datenbank im Hintergrund, sodass man die Dateien mit Meta-Infos versehen kann und schnell durchsuchen kann.

Freizeit

Leider kein echtes Wärmebild von meinem Computer

Leider kein echtes Wärmebild von meinem Computer

Da der Server die meiste Zeit mehr oder weniger nutzlos herumsteht, lasse ich ihn nebenbei potentielle Primzahlen faktorisieren. Dazu gibt es das Projekt GIMPS, wo auf Distributed Computing gesetzt wird, um riesige Primzahlen zu finden. Man läd sich das Programm mprime herunter und startet es, dann beantwortet man ein paar Fragen und das rechnen kann beginnen. Ungefähr so:

mkdir primes
cd primes/
wget http://mersenneforum.org/gimps/mprime259-linux64.tar.gz
tar xzvf mprime259-linux64.tar.gz
less readme.txt
#Wenn man screen nicht hat, debian:
sudo apt-get install screen
screen
./mprime
# strg+a strg+d lässt das Programm im Hintergrund weiterlaufen („detach“)
screen -r #holt es zurück. 

Etwas skeptisch bin ich damit allerdings noch, da es meine CPUs recht warm (50° C) hält (und damit die Lüfter laut) und auch etwas mehr Strom verbraucht.

http://www.mersenne.org/ - Website von GIMPS

Mehrere SSH-Verbindungen

Wenn man ein paar Computer gleichzeitig benutzt, und ein paar Terminals offen hat, kann es schnell passieren, dass man einen Befehl auf dem falschen Computer ausführt. „Ein Paar“ beginnt hier erfahrungsgemäß wirklich schon bei 2. Daher mache ich meine Remote-Prompts gerne türkis, mit dieser Zeile in der .bashrc:

PS1="\@ \[\033[0;36m\]\u@\\h\[\033[0m\]:\w> "

Da das immer noch nicht eindeutig genug ist, habe ich mir die Terminals auch noch mit unterschiedlichen Farben hinterlegt:

Das geht mit urxvt oder indem man in Konsole ein neues Farbschema anlegt und dann ein neues Profil, für das man bei Befehl den SSH-Befehl eingibt.

SCP und SHH Dateiübertragungsgeschwindigkeit

Irgendwie würde mich interessieren, wieviel SCP und SSH durch die Verschlüsselung langsamer werden. Darum habe ich paar Komponententests des Vollbackups gemacht.

  • Reines Übertragen der Backup-Datei mit scp: 1787MB in 00:50 bei 35.7MB/s
  • Erstellen des Archivs: 1787 MB in 4:51 also 6 MB/s
  • Lesegeschwindigkeit Quelle: 58,5 MB/s (arm)
  • Schreibgeschwindigkeit Ziel: 79,3 MB/s
  • Lesegeschwindigkeit Ziel: 371 MB/s (für Übertragungs-Tests verwendet)
  • Lesegeschwindigkeit über SMB: 48,9 MB/s
  • Netzwerkgeschwindigkeit rund 100MB/s

Es ist also kein Problem die Daten zu verschlüsseln, da das erstellen des Archivs nicht sonderlich schnell ist. Bei einer geringeren Netzwerkgeschwindigkeit, z.B. bei fast Ethernet oder über das Internet würde es sogar ohne archivieren nicht mehr auffallen.

Das sind also meine Tricks, was ich mit dem Server so anstelle.

Kommentare: keine

Javascript Library Builder

31.07.2009, 13:07
JS-Build

JS-Build

Es gibt einige Methoden Javascript auf HTML-Seiten einzubinden. Ein Block im <body>, in onclick-Attributen und ähnlichem, oder als Verknüpfungen im <head> aber am besten in einer externen Datei, die nach dem HTML-Dokument geladen wird. Nur will man beim Entwickeln die einzelnen Javascript-Teile meist in getrennte Dateien legen und auf der Seite dann möglichst viel in eine (am besten möglichst kleine) Datei packen, um Requests zu sparen. Dieses Problem kann der Javascript Library Builder lösen, ein recht kurzes Shell-Script, welches eine Sammlung von .js-Dateien in wenige Dateien minimiert.

Man nehme zum Beispiel eine solche Dateistruktur:

lib/
  php/...
  js_frontend/
    00_jQuery.js
    01_jQ_plugin.js
    02_effekte.js
    licence.txt
  js_backend/
    03_ajaxCalls.js

Der Scriptaufruf wäre so einfach wie:

jslibbuild.sh -o build lib/js* 

Ich habe auf dieser Seite 27 einzelne Dateien, die ich in zwei Javascript Dateien packe, nämlich für die Seite und für das CMS. Diese Beispiel-Dateistruktur würde jetzt in den zwei Dateien js_frontend.min.js und js_backend.min.js im Ordner build resultieren. Der Datei js_frontend.min.js würde zusätzlich nach dem minimieren mit jsmin die Datei licence.txt vorgehängt, sodass z.B. in Kommentaren (die von jsmin geschluckt werden) vermerkte Autorenhinweise erhalten bleiben, was unter anderem bei den CC-Lizenzen nötig ist.

Dazu akzeptiert das Script die optionale Option -l nach der der Name der Lizenz-Dateien angegeben wird. Eine weitere optionale Option ist -c, um das build-Verzeichnis vor dem Anlegen der neuen Dateien zu löschen. Das ist nötig, wenn ein ganzer source-Ordner gelöscht wird, aber dringend zu vermeiden, wenn weitere Dateien im build-Ordner liegen. Hier noch ein Beispiel:

jslibbuild.sh -c -l gpl.txt -o /srv/www/htdocs/js ~/web/lib/js/* 

Das Script benötigt jsmin, steht unter GPLv2+ und kann hier heruntergeladen werden:

Zum „installieren“ einfach in den Ordner ~/bin kopieren. Das Script ist eigentlich ziemlich einfach, kann aber doch einiges an Arbeit ersparen.

Kommentare: keine

My MySQL API onion for PHP

26.07.2009, 17:42
MySQL Onion

MySQL Onion

A huge part of web applications is usually the interaction with the SQL database. This is why I want as little work as possible connecting, escaping values, getting the right tables an so on in PHP. But it should stay simple and allow modular approaches. Therefor I'm using some nested APIs for doing queries easily:

PDO

The very fist thing I am using is PDO. It can handle many RDBMS, but I am most of the times using MySQL or SQLite. By using PDO as an API for the following layers I can make sure most of the code will work for many RDBMSs. PDO even simplifies transactions and prepared statements. Here's some sample PHP code using PDO:

$pdo=new PDO('mysql:host='.$host.';dbname='.$db, $user, $passwort);

$pdo->exec('UPDATE test SET foo="bar" WHERE id=4');

$satement=$pdo->prepare('SELECT * FROM blogeintraege WHERE id=:id');
$satement->bindValue(':id',3,PDO::PARAM_INT);
$satement->execute();
$data=$satement->fetchAll();

PDO Simplifier

The next layer is a class that will hold a MySQL database Connection (a PDO Object) and offer some simple functions for doing e.g. a simple prepared statement. Instead of binding each values manually, you can throw an array in.

It also includes a cache, if you want to run statements more than once. It can append a prefix to all queried tables and checks dynamically inserted tables for validity to avoid SQL-injections and MySQL errors. It is used like that:

$res=$db->sql("SELECT * FROM blogeintraege");
$res=$db->sql(
  "SELECT * FROM #test WHERE id=:id",
  array('id'=>$id),array('id'=>PDO::PARAM_INT),
  array('test'=>'blogeintraege'),
  array('limits'=>array(0,$l),'buffered'=>false)
);

For one array element this does not look too tiny, but the more values are bound, the more useful it gets. And it is very useful if you already have your values in an array, like $_GET.

Note that nearly everything is optional. The table array can contain more tables, for example you can have an array of tables for different languages, if they are in different tables. The bind-types don't need to be specified too. You can even leave out everything except the query as shown in the fist line of code. The Result will by default be returned as a nice array (the GROUP_CONCAT fields are array'ed too) but you can use all other PDO fetch types.

This layer follows a rather functional approve, so I needed another layer for accessing the central sql()-Function in an OOP manner. This should avoid some runtime errors and you can modify the SQL in a modular system.

Statement builder

So I created a wrapper object, that holds a pointer to the database and will construct the parameters for sql(). This comes in handy as more and more optional parameters are added.

The PDO Simplifier has a method to build such statement-objects called sqlO(). This is how the wrapper is used:

$db->sqlO('INSERT INTO blogtaglinks SET ##,type=3')
   ->setSet(array('ID_tag'=>$lasttagid,'ID_entry'=>$id))
   ->exec();
$res=$db->sqlO("SELECT * FROM #test WHERE id=:id")
        ->setData(array('id'=>$id))
        ->setDataTypes(array('id'=>PDO::PARAM_INT))
        ->setTables('test'=>'blogeintraege'),
        ->setLimits(0,$l)
        ->setBuffered(false)
        ->exec();
);

As you can see, it is a little more code, but the code is pretty self-explanatory and now one can build the sets and the other parameters as arrays and then include them easily in the statements.

A bit different: Zend Framework's $db->select() approach

A next step would be to build queries with a single API. This is a feature implemented by the Zend Framework, where you can build your SQL with some API functions and it will even work across various databases:

select = $db->select()
  ->from('blogeintraege',array('id','Titel'))
  ->where('id < ?', $id)
  ->order('id DESC')
  ->limit(0,10);

Well doesn't that look nice?

Kommentare: keine
 
Χρόνογραφ
© 2008-2018 by Bernhard Häussner - Impressum - Login
Kurz-Link zu dieser Seite: http://1-co.de/bj