Shadowart: Unterschied zwischen den Versionen

Zeile 18: Zeile 18:
 
== Erstellung des Schattenobjekts ==
 
== Erstellung des Schattenobjekts ==
  
 +
=== Erste Versuche ===
 
Wir haben uns der Verzerrung mit dem Versuch und Irrtumprinzip genähert.
 
Wir haben uns der Verzerrung mit dem Versuch und Irrtumprinzip genähert.
  
Zeile 25: Zeile 26:
  
 
Die fertigen Vektordaten wurden dann mit der [[CNC|CNC Fräse]] aus 4mm Pappelsperrholz ausgefräst.
 
Die fertigen Vektordaten wurden dann mit der [[CNC|CNC Fräse]] aus 4mm Pappelsperrholz ausgefräst.
 +
 +
=== Verbesserung der Genauigkeit ===
 +
Das beschriebene Vorgehen hat zwar zu brauchbaren Ergebnissen geführt, ist jedoch umständlich zu handhaben und es ist nicht möglich genau zu arbeiten. Z.B. den Abstand der Lichtquelle genau einzustellen.
 +
 +
[[Benutzer: ptflea|ptflea]] hat sich der Sache angenommen und ein Programm in [http://www.processing.org Processing] geschrieben das SVG-Dateien aus [http://www.inkscape.org Inkscape] entgegen nimmt und die Verzerrung in Abhängigkeit der Entfernung der Lichtquelle berechnet.
 +
 +
Das Programm zerlegt das Zielobjekt in einzelne Punkte und verbindet jeden einzelnen Punkt im dreidimensionalen Raum mit der Lichtquelle. Die Schnittpunkte mit der Zielebene ( entspricht dem verzerrten Objekt) werden miteinander verbunden und als SVG-Datei gespeichert.
 +
 +
=== Programm in Processing ===
 +
Die Ausgangsdaten werden in Inkscape in der gewünschten 'Schattenwurfgrösse' in mm erstellt und als SVG-Datei im Ordner 'data' im Sketchordner abgelegt werden.
 +
 +
Die Ausgabedatei liegt im Sketchordner mit der Namensergänzung '_Shadow'.
 +
 +
 +
Die fertige Datei muss noch in Inkscape bearbeitet werden, da die Linien nicht geschlossen sind:
 +
 +
:Objekt markieren und mit 'F2' in den Knotenbearbeitungsmodus wechseln.
 +
:Mit 'STRG'+'A' alles markieren und in der Symbolleiste 'Gewählte Endknoten durch ein neues Segment verbinden' klicken.
 +
 +
 +
Die Verarbeitung der SVG-Dateien übernimmt die [http://www.ricardmarxer.com/geomerative/ Geomerative-Libary]
 +
 +
Hier ist der Code:
 +
<syntaxhighlight lang="c" line enclose="div">
 +
import geomerative.*; // http://www.ricardmarxer.com/geomerative/
 +
 +
 +
//SVG muss auf dem Kopf stehen und links oben angelegt sein, in mm der gewünschten Grösse
 +
//Input-SVG muss im Data-Ordner liegen
 +
//Output-SVG liegt im Sketch-Ordner -> Originalname+_'Shadow'
 +
String Dateiname = "gluehbirne_ptflea";
 +
 +
//Koordianten der Lichtquelle in mm
 +
int lichtY = 150;  //Entfernung
 +
int lichtZ = 80; //Höhe
 +
// x wird immer in die Mitte des Objektes gelegt -> siehe Variable 'mitte'
 +
 +
float mm2px_faktor = 3.54331; // Umrechung der px von Inkscape zu mm 1mm entspricht 3.54331px
 +
int trennwert = 10;  //Inseln erkennen und nicht verbinden -> kleine Zahl wenn Insel sehr nahe beieinander
 +
float scale = 1 ;    //Grösse der Darstellung auf dem Bildschirm
 +
 +
String[] SVGdatei = new String[2]; //String Array zur koordinatenausgabe
 +
RPoint[] points;  //Array für Koordinaten
 +
 +
int i = 0; // Zähler
 +
float mitte = 0; // Mitte der X-Achse des Objektes
 +
float PXlast = 0; // vorheriger x-Punkt um Linie zeichnen zu können
 +
float PYlast = 0; // vorheriger y-Punkt um Linie zeichnen zu können
 +
 +
//3D-Raum mit Schnittebene
 +
PVector[] face = new PVector[3];
 +
PVector n;//normal
 +
Ray r1;
 +
 +
 +
void setup(){
 +
 +
 
 +
  size(1000,600,P3D); // Grösse der Anzeigefläche
 +
    //Fläche der 'Verzerrungsebene' definieren
 +
    face[0] = new PVector(-100,0,0);
 +
    face[1] = new PVector(0,0,100);
 +
    face[2] = new PVector(100,0,0);
 +
   
 +
   
 +
  r1 = new Ray(new PVector(-100,160,50),new PVector(0,200,300));
 +
 
 +
  // Load given svg file in sketch/data
 +
  RG.init(this);
 +
  RShape objShape = RG.loadShape(Dateiname + ".svg");
 +
  // Linien in Punkte zerlegen
 +
  points = objShape.getPoints();
 +
 
 +
  background(255);
 +
  translate(width/2, height/2,-500);
 +
  rotateX(map(90,0,height,-PI,PI));
 +
  rotateY(map(90,0,width,-PI,PI));
 +
 +
  PVector c = new PVector();//centroid
 +
  for(PVector p : face) c.add(p);
 +
  c.div(3.0);
 +
  PVector cb = PVector.sub(face[2],face[1]);
 +
  PVector ab = PVector.sub(face[0],face[1]);
 +
  n = cb.cross(ab);//compute normal
 +
 
 +
 
 +
  //Mittelpunkt der x-Achse des Objektes festelegen -> X der Lichtquelle
 +
      for (int x=0; x<points.length-1; x++) {
 +
              if (points[x].x > mitte) {
 +
                mitte = points[x].x;
 +
              }
 +
      }
 +
      mitte = mitte/2;
 +
   
 +
  noLoop(); // damit draw() nur ein mal ausgeführt wird
 +
 
 +
  //SVG Datei Basics
 +
  SVGdatei[0] = "<svg\n  version=\"1.1\"\n  width=\"210mm\"\n  height=\"297mm\"\n  id=\"svg2\">\n  <g id=\"layer1\">\n    <path\n      d=\"";
 +
  SVGdatei[1] = "      id=\"path5917\"\n      style=\"fill:none;stroke:#000000;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none\" />\n </g>\n</svg>";
 +
  }
 +
 
 +
 
 +
void draw(){
 +
    //umrechung der Lichtposition von mm zu px, da Inkspace im SVG px verwendet
 +
    lichtY = (int)(-lichtY * mm2px_faktor); //Entfernung
 +
    lichtZ = (int)(lichtZ * mm2px_faktor);
 +
 +
 +
    // Solange noch Punkte im Array sind Linie von Punkt zu Punkt zeichnen
 +
    for (int i=0; i < points.length - 1; i++) { 
 +
     
 +
        //Vector von der Lichtposition zu einem Punkt des Objektes anlegen
 +
        r1 = new Ray(new PVector(mitte,lichtY,lichtZ),new PVector(points[i].x, points[i].y,0));
 +
 +
        //http://paulbourke.net/geometry/planeline/
 +
        //line to plane intersection u = N dot ( P3 - P1 ) / N dot (P2 - P1), P = P1 + u (P2-P1), where P1,P2 are on the line and P3 is a point on the plane
 +
        //Schnittpunkt mit der 'Verzerrungsebene' berechnen
 +
        PVector P2SubP1 = PVector.sub(r1.end,r1.start);
 +
        PVector P3SubP1 = PVector.sub(face[0],r1.start);
 +
        float u = n.dot(P3SubP1) / n.dot(P2SubP1);
 +
        PVector P = PVector.add(r1.start,PVector.mult(P2SubP1,u));
 +
        strokeWeight(1);
 +
        n.normalize();
 +
   
 +
        //original zeichnen
 +
        point(points[i].x, points[i].y);
 +
             
 +
        // Inseln erkennen und nicht verbinden
 +
        if ((PXlast < 0.001) || (abs(PXlast-P.x) > trennwert) || (abs(P.x-PXlast) > trennwert) || (abs(PYlast-P.z) > trennwert) || (abs(P.z-PYlast) > trennwert)) {
 +
                      PXlast = P.x;
 +
                      PYlast = P.z;
 +
                      SVGdatei[0] = SVGdatei[0] + "M "; // Trennung der Linie einfügen
 +
                  }
 +
                 
 +
        //verzerrtes Objekt zeichnen         
 +
        line(PXlast*scale,PYlast*scale,0,P.x*scale,P.z*scale,0);
 +
       
 +
        //Punkte in array speichern zur Ausgabe
 +
        SVGdatei[0] = SVGdatei[0] + PXlast + "," + PYlast + " ";
 +
       
 +
        //letzten Punkt merken um die Punkte zu verbinden
 +
        PXlast = P.x;
 +
        PYlast = P.z;
 +
    }
 +
   
 +
    //Punkte in die SVG-Datein schreiben
 +
    SVGdatei[0] = SVGdatei[0] + "\""; // Anführungsstriche einfügen
 +
    saveStrings(Dateiname + "_Shadow.svg", SVGdatei);
 +
  }
 +
 +
 +
class Ray{
 +
  PVector start = new PVector(),end = new PVector();
 +
  Ray(PVector s,PVector e){  start = s ; end = e;  }
 +
}
 +
</syntaxhighlight>
  
  
Zeile 36: Zeile 193:
  
 
<gallery>
 
<gallery>
PB168082.jpeg|Ampelmänchen
+
PB168082.jpeg|Ampelmännchen
 
PB168079.jpeg|Hände
 
PB168079.jpeg|Hände
 
PB168081.jpeg|sitzendes Mädchen
 
PB168081.jpeg|sitzendes Mädchen
PB168078.jpeg|blick in den Gang
+
PB168078.jpeg|Blick in den Gang
 
kind.jpg|Kind
 
kind.jpg|Kind
affro kirsche.jpg|Afro Kirsche
 
 
cadillac.jpg|Cadillac
 
cadillac.jpg|Cadillac
 
</gallery>
 
</gallery>

Version vom 17. Januar 2014, 16:31 Uhr

Crystal Clear action run.png
Shadowart

Status: stable

PB168148.jpeg
Beschreibung Silhouetten Schablonen für Schattenbild
Autor: spezi ptflea
Version 1
PayPal Spenden für Shadowart

Beschreibung

Als Idee für die Bamberger Lichthöfe 2013 haben wir, inspiriert durch die Arbeiten von Kumo Yamashita, versucht diverse schattenwerfende Objekte zu basteln. Dabei wurde eine Silhouette entsprechend der Lichtquelle angepasst, damit im Schatten das Ausgangssbild rekonstruiert wird. Die verzerrten Konturen wurden für die Lichthöfe aus Sperrholz mit der CNC Fräse ausgefräst.


Erstellung des Schattenobjekts

Erste Versuche

Wir haben uns der Verzerrung mit dem Versuch und Irrtumprinzip genähert.

Zuerst mussten wir feststellen, dass eine Stauchung nicht ausreicht, sondern zusätzlich eine Trapezverzerrung notwendig ist. Weiterhin ist die Stauchung nicht linear, sondern wird nach oben stärker.

Mit Gimp (gimp-tool-perspective) lässt sich eine Trapezverzerrung mit nichtlinearer Stauchung erstellen. Danach wurde das Bild mit Inkscape (tutorial-tracing) in eine Vektorgrafik umgewandelt und noch stärker gestaucht.

Die fertigen Vektordaten wurden dann mit der CNC Fräse aus 4mm Pappelsperrholz ausgefräst.

Verbesserung der Genauigkeit

Das beschriebene Vorgehen hat zwar zu brauchbaren Ergebnissen geführt, ist jedoch umständlich zu handhaben und es ist nicht möglich genau zu arbeiten. Z.B. den Abstand der Lichtquelle genau einzustellen.

ptflea hat sich der Sache angenommen und ein Programm in Processing geschrieben das SVG-Dateien aus Inkscape entgegen nimmt und die Verzerrung in Abhängigkeit der Entfernung der Lichtquelle berechnet.

Das Programm zerlegt das Zielobjekt in einzelne Punkte und verbindet jeden einzelnen Punkt im dreidimensionalen Raum mit der Lichtquelle. Die Schnittpunkte mit der Zielebene ( entspricht dem verzerrten Objekt) werden miteinander verbunden und als SVG-Datei gespeichert.

Programm in Processing

Die Ausgangsdaten werden in Inkscape in der gewünschten 'Schattenwurfgrösse' in mm erstellt und als SVG-Datei im Ordner 'data' im Sketchordner abgelegt werden.

Die Ausgabedatei liegt im Sketchordner mit der Namensergänzung '_Shadow'.


Die fertige Datei muss noch in Inkscape bearbeitet werden, da die Linien nicht geschlossen sind:

Objekt markieren und mit 'F2' in den Knotenbearbeitungsmodus wechseln.
Mit 'STRG'+'A' alles markieren und in der Symbolleiste 'Gewählte Endknoten durch ein neues Segment verbinden' klicken.


Die Verarbeitung der SVG-Dateien übernimmt die Geomerative-Libary

Hier ist der Code:

  1import geomerative.*; // http://www.ricardmarxer.com/geomerative/
  2
  3
  4//SVG muss auf dem Kopf stehen und links oben angelegt sein, in mm der gewünschten Grösse
  5//Input-SVG muss im Data-Ordner liegen
  6//Output-SVG liegt im Sketch-Ordner -> Originalname+_'Shadow'
  7String Dateiname = "gluehbirne_ptflea";
  8
  9//Koordianten der Lichtquelle in mm
 10int lichtY = 150;  //Entfernung
 11int lichtZ = 80; //Höhe
 12// x wird immer in die Mitte des Objektes gelegt -> siehe Variable 'mitte'
 13
 14float mm2px_faktor = 3.54331; // Umrechung der px von Inkscape zu mm 1mm entspricht 3.54331px
 15int trennwert = 10;   //Inseln erkennen und nicht verbinden -> kleine Zahl wenn Insel sehr nahe beieinander
 16float scale = 1 ;    //Grösse der Darstellung auf dem Bildschirm
 17
 18String[] SVGdatei = new String[2]; //String Array zur koordinatenausgabe
 19RPoint[] points;  //Array für Koordinaten
 20
 21int i = 0; // Zähler
 22float mitte = 0; // Mitte der X-Achse des Objektes
 23float PXlast = 0; // vorheriger x-Punkt um Linie zeichnen zu können 
 24float PYlast = 0; // vorheriger y-Punkt um Linie zeichnen zu können 
 25
 26//3D-Raum mit Schnittebene 
 27PVector[] face = new PVector[3];
 28PVector n;//normal
 29Ray r1;
 30
 31
 32void setup(){
 33
 34  
 35  size(1000,600,P3D); // Grösse der Anzeigefläche
 36    //Fläche der 'Verzerrungsebene' definieren
 37    face[0] = new PVector(-100,0,0);
 38    face[1] = new PVector(0,0,100);
 39    face[2] = new PVector(100,0,0);
 40    
 41    
 42  r1 = new Ray(new PVector(-100,160,50),new PVector(0,200,300));
 43  
 44  // Load given svg file in sketch/data
 45  RG.init(this);
 46  RShape objShape = RG.loadShape(Dateiname + ".svg");
 47  // Linien in Punkte zerlegen
 48  points = objShape.getPoints();
 49  
 50  background(255);
 51  translate(width/2, height/2,-500);
 52  rotateX(map(90,0,height,-PI,PI));
 53  rotateY(map(90,0,width,-PI,PI));
 54
 55  PVector c = new PVector();//centroid
 56  for(PVector p : face) c.add(p);
 57  c.div(3.0);
 58  PVector cb = PVector.sub(face[2],face[1]);
 59  PVector ab = PVector.sub(face[0],face[1]);
 60  n = cb.cross(ab);//compute normal
 61  
 62   
 63  //Mittelpunkt der x-Achse des Objektes festelegen -> X der Lichtquelle
 64      for (int x=0; x<points.length-1; x++) {
 65              if (points[x].x > mitte) {
 66                mitte = points[x].x;
 67              }
 68      }
 69      mitte = mitte/2;
 70     
 71  noLoop(); // damit draw() nur ein mal ausgeführt wird
 72  
 73  //SVG Datei Basics
 74  SVGdatei[0] = "<svg\n   version=\"1.1\"\n   width=\"210mm\"\n   height=\"297mm\"\n   id=\"svg2\">\n  <g id=\"layer1\">\n    <path\n       d=\"";
 75  SVGdatei[1] = "       id=\"path5917\"\n       style=\"fill:none;stroke:#000000;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none\" />\n </g>\n</svg>";
 76   }
 77  
 78  
 79void draw(){
 80     //umrechung der Lichtposition von mm zu px, da Inkspace im SVG px verwendet
 81    lichtY = (int)(-lichtY * mm2px_faktor); //Entfernung
 82    lichtZ = (int)(lichtZ * mm2px_faktor);
 83
 84 
 85    // Solange noch Punkte im Array sind Linie von Punkt zu Punkt zeichnen
 86    for (int i=0; i < points.length - 1; i++) {  
 87      
 88        //Vector von der Lichtposition zu einem Punkt des Objektes anlegen
 89        r1 = new Ray(new PVector(mitte,lichtY,lichtZ),new PVector(points[i].x, points[i].y,0));
 90
 91        //http://paulbourke.net/geometry/planeline/
 92        //line to plane intersection u = N dot ( P3 - P1 ) / N dot (P2 - P1), P = P1 + u (P2-P1), where P1,P2 are on the line and P3 is a point on the plane
 93        //Schnittpunkt mit der 'Verzerrungsebene' berechnen
 94        PVector P2SubP1 = PVector.sub(r1.end,r1.start);
 95        PVector P3SubP1 = PVector.sub(face[0],r1.start);
 96        float u = n.dot(P3SubP1) / n.dot(P2SubP1);
 97        PVector P = PVector.add(r1.start,PVector.mult(P2SubP1,u));
 98        strokeWeight(1);
 99        n.normalize();
100    
101        //original zeichnen
102        point(points[i].x, points[i].y);
103              
104        // Inseln erkennen und nicht verbinden
105        if ((PXlast < 0.001) || (abs(PXlast-P.x) > trennwert) || (abs(P.x-PXlast) > trennwert) || (abs(PYlast-P.z) > trennwert) || (abs(P.z-PYlast) > trennwert)) {
106                      PXlast = P.x;
107                      PYlast = P.z;
108                      SVGdatei[0] = SVGdatei[0] + "M "; // Trennung der Linie einfügen
109                  }
110                  
111        //verzerrtes Objekt zeichnen          
112        line(PXlast*scale,PYlast*scale,0,P.x*scale,P.z*scale,0);
113        
114        //Punkte in array speichern zur Ausgabe
115        SVGdatei[0] = SVGdatei[0] + PXlast + "," + PYlast + " ";
116        
117        //letzten Punkt merken um die Punkte zu verbinden
118        PXlast = P.x;
119        PYlast = P.z;
120    } 
121    
122    //Punkte in die SVG-Datein schreiben
123    SVGdatei[0] = SVGdatei[0] + "\""; // Anführungsstriche einfügen
124    saveStrings(Dateiname + "_Shadow.svg", SVGdatei);
125  }
126
127
128class Ray{
129  PVector start = new PVector(),end = new PVector();
130  Ray(PVector s,PVector e){   start = s ; end = e;  }
131}


Installation

Die gefrästen Objekte wurden mit Kreppband an der Wand befästigt ..

Als Lichtquelle diente jeweils eine PowerLED mit Abstrahlwinkel: 120°

Bilder