Bernhard Häussner
Tags: Artikel mit dem Tag «Codesnippets» durchstöbern

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

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

MySQL-Queries sparen mit GROUP_CONCAT

22.06.2009, 20:10
Ein MySQL-Query mit GROUP_CONCAT

Ein MySQL-Query mit GROUP_CONCAT

Fast jede (Web-)Anwendung, die eine Datenbank wie MySQL benutzt, muss irgendwie das Problem lösen, zu einem Artikel mehrere Tags, zu einem Eintrag mehrere Kommentare, zu einem Wort mehrere Synonyme zu finden, die sich in einer weiteren Tabelle befinden. Jedes Mal, wenn mehrere Zeilen Abgefragt werden sollten, die wieder mehrere untergeordnete Zeilen haben, bedeutet das meistens für jedes Element einen weiteren Query zu starten, oder eine zweite Abfrage mit einer weiteren Liste zu starten, oder eine riesige Reihe Datensätze abzufragen und der Anwendung das Gruppieren zu überlassen. Mit GROUP_CONCAT (Dokumentation) lässt sich in MySQL (fast) alles in ein SQL-Query zusammenfassen.

Einführung

Nehmen wir eine einfache Datenbankstuktur an, wie die eines Blogs. Es gibt blogeintraege mit einem Titel, blogtags mit einem Namen. Dann gibt es noch eine Tabelle, die beides verbindet, z.B. blogtaglinks. Jeder Datensatz hat natürlich eine eindeutige ID. Wenn man jetzt alle Blogeinträge auflisten will, könnte man das wie folgt machen:

SELECT Titel FROM blogeintraege

Um die Anzahl der Verbundenen Tags herauszufinden, wäre ein solcher Join üblich:

SELECT Titel, COUNT(distinct(t.ID))
FROM blogeintraege b, blogtags t, blogtaglinks l
WHERE b.ID = l.ID_entry and t.ID = l.ID_tag
GROUP BY b.ID
+-------------------------+-----------------------+
| Titel                   | COUNT(distinct(t.ID)) |
+-------------------------+-----------------------+
| Hello World!            |                     2 |
| The CMS                 |                     2 |
| To be Released Later... |                     1 |
+-------------------------+-----------------------+
3 rows in set (0.01 sec)

Hier werden jeweils alle Spalten, die den selben Blogeintrag repräsentieren gruppiert und dann aus diesen Gruppen die Anzahl der verschiedenen Tags abgefragt. Dabei ist COUNT die Gruppenfunktion, die die nun versteckten, „weg-gruppierten“, Datensätze verarbeitet - hier zählt.

Die GROUP_CONCAT-Funktion

MySQL und andere RDBMSs kennen für Zahlen einige Funktionen, die Summe, Durchschnitt etc. gruppierter Zahlen berechnen können. MySQL kennt darüber hinaus die GROUP_CONCAT()-Funktion, welche die „versteckten“ Werte (also auch CHAR) in einer Spalte zusammen zu hängen. Mit dieser Funktion wäre es also möglich, die Tags direkt aufzulisten:

SELECT Titel, GROUP_CONCAT(distinct(t.name))
FROM blogeintraege b, blogtags t, blogtaglinks l
WHERE b.ID = l.ID_entry and t.ID = l.ID_tag
GROUP BY b.ID
+-------------------------+---------------------------------+
| Titel                   | GROUP_CONCAT(distinct(t.name))  |
+-------------------------+---------------------------------+
| Hello World!            | Projekt: Mein Blog,Persönliches |
| The CMS                 | PHP,Projekt: Mein Blog          |
| To be Released Later... | Projekt: Mein Blog              |
+-------------------------+---------------------------------+
3 rows in set (0.00 sec)

Jetzt wird anstatt der Anzahl der Tags eine Komma-getrennte Liste der Tags angezeigt. Das ist insofern ein recht großer Fortschritt, da wir nicht nochmal eine zweite Abfrage machen müssen, die uns jeweils die Namen der Tags heraus sucht.

Weiter Ausbauen

Jetzt wird es Zeit ein bisschen zu Optimieren. In diesem Beispiel macht es wenig Sinn, aber theoretisch könnten zwei Tags den gleichen Namen haben. In diesem Fall würde uns MySQL aber den Namen nur einmal anzeigen, da distinct() doppelte Werte aussortiert. Wir würden also weniger Tags bekommen, als ein COUNT(distinct(t.ID)) anzeigt. Das können wir lösen, indem wir die distinct()-Funktion auf die IDs der Tags anwenden:

SELECT Titel, GROUP_CONCAT(distinct(t.ID),'-', t.name)
FROM blogeintraege b, blogtags t, blogtaglinks l
WHERE b.ID = l.ID_entry and t.ID = l.ID_tag
GROUP BY b.ID

Das würde uns für jeden Blogeintrag eine Liste erstellen, wie 42-Webbrowser,28-Productivity,27-Design. Die eigentlich überflüssigen IDs werden später noch entfernt, z.B. mit PHP.

Wenn wir zusätzlich noch die Erstellungsdaten z.B. der letzten drei Kommentare abfragen wollen, zeigt sich, warum wir distinct() verwenden müssen: Sonst wird jeder Tag so oft angezeigt, je nach dem, wie viele Kommentare ein Eintrag hat. Wenn wir also noch eine Tabelle benutzen, wie blogkommentare mit creation_time, und uns jeweils die letzten Erstellungszeiten anzeigen lassen wollen, benutzen wir:

SELECT Titel, GROUP_CONCAT(distinct(t.ID),'-', t.name), 
GROUP_CONCAT(distinct(c.ID),'-',c.creation_time ORDER BY creation_time DESC)
FROM blogeintraege b, blogtags t, blogtaglinks l, blogkommentare c
WHERE b.ID = l.ID_entry and t.ID = l.ID_tag and c.entry_id = b.id
GROUP BY b.ID

Jetzt haben wir noch eine weitere Spalte mit einem Wert, wie 34-2008-10-20 18:28:41,33-2008-10-19 23:22:37. Doch je mehr Spalten wir abfragen, desto unübersichtlicher werden die Listen. Außerdem hängt ja immer noch die ungewollte, aber doch notwendige id davor.

Verarbeitung mit PHP

Diese minderen kosmetischen Makel lassen sich dann in der Anwendung noch beheben. Hier zeige ich, wie ich mit PHP die Listen in Arrays lade und sortiere:

function arrayifyGrpConcats($q,$resultset) {
  $regexp='/\\bgroup_concat\\((.*?) separator \'(?<sep>.*?)\'\\) +as +(?<name>\w+)\\b/i';
  if ( preg_match_all($regexp,$q,$matches,PREG_SET_ORDER) ) {
    foreach ($matches as $grpCoClause) {
      if (isset($grpCoClause["name"],$grpCoClause["sep"])) {
        $name=$grpCoClause["name"];
        if (strpos($name,'_',1)===false) {
          $foldername=$name;
          $subname=false;
        } else {
          $foldername=substr($name,0,strpos($name,'_',1));
          $subname=substr($name,strpos($name,'_',1)+1);
        }
        $s=$grpCoClause["sep"];
        foreach ($resultset as $id=>$row) {
          $val=$row[$name];
          unset($resultset[$id][$name]);
          if ($subname) {
            if (!isset($resultset[$id][$foldername])) {
              $resultset[$id][$foldername]=array();
            }
            $vals=explode($s,$val);
            for ($i=0, $len=count($vals);$i<$len;$i++) {
              if (!isset($resultset[$id][$foldername][$i])) {
                $resultset[$id][$foldername][$i]=array();
              }
              $resultset[$id][$foldername][$i][$subname]=preg_replace('/\\d+-/','',$vals[$i],1);
            }
          } else {
             $resultset[$id][$foldername]=explode($s,$val);
          }
        }
      }
    }
  }
  return $resultset;
}

Diese Funktion kann direkt nach der Abfrage auf die Ergebnisse angewendet werden. Dazu übergibt man ihr das Result als durchnummeriertes Array mit je einem assoziativen Unterarray (Dictionary) pro Datensatz, der als Schlüssel die Spaltennamen und als Werte den jeweiligen Spaltenwert enthält. Die Funktion sucht dann im Query nach GROUP_CONCAT-Aufrufen der From group_concat(... separator ',') as iwas. Das Trennsymbol wird explizit angegeben, damit es hier keine Missverständnisse gibt.

Man sollte bedenken, dass MySQL mit dem Separator im Wert nichts macht. Wie man im SQL sieht habe ich das Problem mit dem Trennzeichen im Wert „gelöst“, indem ich es aus dem Kommentartext einfach gelöscht habe. Eine ausgefeiltere Methode habe ich bei stackoverflow gefunden.

Als Alias nach as kann man fast alles angeben. Die Funktion wird diesen Namen als Spaltennamen im Result-Array verwenden.

Als kleines Extra habe ich noch eingebaut, dass die Funktion optional mehrere Spalten zusammen nimmt, wenn sie ein Alias der Form gruppe_name erkennt: Sie wird dann im Resultset-Array unter gruppe ein nummeriertes Array anlegen, das ein assoziatives Arrays enthält mit den verschieden name. Wenn wir also zu einem Kommentar nicht nur das Datum, sondern auch den Autor wissen wollen, könnten wir group_by... as comm_time, group_by... as comm_name verwenden, um die Daten für jeden Kommentar zusammen zu halten.

Im Endeffekt kann das dann so aussehen:

SELECT b.id, b.Titel AS Titel,
group_concat(DISTINCT t.ID,'-', t.name separator ', ') as tags_Name, 
group_concat(DISTINCT t.id separator ', ') as tags_id, 
group_concat(DISTINCT c.ID,'-',c.creation_time separator ', ') as comm_time, 
group_concat(DISTINCT c.ID,'-',REPLACE(LEFT(c.Text,20),',','') 
     separator ', ') as comm_text 
FROM blogeintraege b, blogtaglinks l, blogtags t, blogkommentare c
WHERE b.ID = l.ID_entry and t.ID = l.ID_tag and c.entry_id=b.id
GROUP BY b.ID LIMIT 1,2

Was uns dann dieses Ergebnis liefert: (print_r)

Array
(
    [0] => Array
        (
            [id] => 2
            [Titel] => The CMS
            [tags] => Array
                (
                    [0] => Array
                        (
                            [Name] => PHP
                            [id] => 3
                        )

                    [1] => Array
                        (
                            [Name] => Projekt: Mein Blog
                            [id] => 2
                        )

                )

            [comm] => Array
                (
                    [0] => Array
                        (
                            [time] => 2008-10-19 23:22:37
                            [name] => Bernhard H.
                            [text] => Also das Kommentarsy
                        )

                    [1] => Array
                        (
                            [time] => 2008-10-20 18:28:41
                            [name] => Bernhard H.
                            [text] => So ich hab es jetz 
                        )

                )

        )

    [1] => Array
        (
            [id] => 10
            [Titel] => To be Released Later...
            [tags] => Array
                (
                    [0] => Array
                        (
                            [Name] => Projekt: Mein Blog
                            [id] => 2
                        )

                )

            [comm] => Array
                (
                    [0] => Array
                        (
                            [time] => 2008-10-20 19:48:50
                            [name] => Bernhard H.
                            [text] => Cool das klappt auc
                        )

                )

        )

)

Ich hoffe mit dieser Methode in Zukunft Datenbankabfragen zu vermindern und damit die Geschwindigkeit der Anwendungen weiter zu erhöhen. Zwei kleine Sachen noch:

SELECT @@group_concat_max_len

Dieser Wert ist die maximale Länger der mit GROUP_CONCAT gebildeten Spaltenwerte und lieget meist bei 1024. Außerdem kann es nötig sein den Spaltenwerte von BLOB zu konvertieren: CAST(... as CHAR).

Kommentare: keine

Processing und Blender

17.06.2009, 20:25

Processing ist gut geeignet um Daten und Berechnungen zu visualisieren, Blender übernimmt dafür mehr Material- und Rendering-Aufgaben. Dennoch gibt es Bereiche, in denen sich die Verwendungszwecke überschneiden, zum Beispiel die hier gezeigten Formen.

Das Bild oben ist eine runde zweifache Spirale - Auf der innersten Ebene jeweils zwei gestreckte und zu Spiralen verdrehte Kreise und Quadrate, deren Dehnprodukt sich dann spiralförmig schraubend um einen Kreis windet. Zwar kann Blender auch mit Python gescriptet werden, das habe ich mir aber bisher noch nicht angeschaut, weshalb alles eher in „Handarbeit“ entstanden ist. Die Farben habe ich nachträglich noch ein bisschen mit GIMP verändert. Das ist ein Rendering in Original-Farben und mit einer etwas angehobenen Ebene:

Wenn sich diese Doppelspiralkreise dann wiederum um einen Kreis legen, entsteht das:

Mit Processing geht dieses verdoppeln und drehen alles viel Einfacher. Hier mal ein Bild aus einem nicht wirklich fertigem Projekt:

Es ist ein Versuch eine Art Haarbüschel nach gewissen Expansionsregeln zu erstellen. Die „Haare“ können in der Sketch interaktiv nach oben und unten gebogen werden, was ein recht lustiges Bild ergibt. Vielleicht hier nochmal ein Bild mit besserem Überblick und anderer rendering-Methode:

Ich habe mich auch schon in Processing etwas mit Spiralen auseinandergesetzt, ist aber leider nicht in 3D, aber dank farblicher Überarbeitung doch noch recht interessant geworden:

spiral
spiral

Die Bildungsvorschrift ist übrigens eine Linie von einem Punkt aus im rechten Winkel zum Ursprung bis zu einem bestimmten Winkel und das sehr oft wiederholt. Der Sourcecode beschränkt sich auf wenige Zeilen:

// Initialisieren
size (1800,1100);
smooth();
background(255);
// Setzt den Ursprung des Koordinatensystems auf die Mitte
translate(width/2,height/2);

// 20 Spiralen
for (int k=0; k<20; k++) {
  // Weiterdrehung pro Spiralsegment
  float rotateStep=PI/11.465;
  // Anzahl der Spiralsegmente
  int rotateAnz=200;
  // Startposition
  float len=k; // wird ei jeder Spirale leicht vaiiert
  // Startposition 2
  float prelen=cos(rotateStep)*len;
  
  // Die Spiralensegmente erstellen
  for (int i=0; i<rotateAnz; i++) {
    // für jedes Segment ein Stück weiter drehen
    rotate(rotateStep);
    // Entfernung des Endes des letzten Segments zum Ursrung
    // Verwendet als Entfernung des Anfangs des aktuellen Segments zum U.
    len=prelen/cos(rotateStep);
    // eine Linie vom Ursprung zum Anfang des Segments
    stroke(0,40);
    line(0,0,len,0);
    /* Damit das letzte, nicht abgeschlossene Segment 
     * nicht so hässlich aus der Spirale hängt */
    if (i!=rotateAnz-1) { 
      // Eine Linie vom Anfang zum Ende des Segments
      // Das wird die Spirale
      stroke(0,200);
      line(len,0,len,len*tan(rotateStep));
    }
    prelen=len;
  }
}

Mit den Kommentaren ist es doch ganz ordentlich länger geworden. Vom rechten Winkel sind übrigens nurnoch die Trigonometrischen Funktionen cos und tan übrig: Die Strecke Ursprung-Segmentbeginn ist die Hypotenuse des Segments und wird die (zu unserem Schritt-Rotationswinkel) Ankathede des nächsten.

Kommentare: 2 Einträge
 
Χρόνογραφ
© 2008-2017 by Bernhard Häussner - Impressum - Login
Kurz-Link zur Homepage: http://1.co.de/