Shadowart

Crystal Clear action run.png
Shadowart

Status: stable

Shadowart space invader kl.jpg
Beschreibung Silhouetten Schablonen für Schattenbild
Autor: spezi ptflea
Version 1
Flattr Flattr Shadowart
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.

Wichtig: Das Objekt muss auf dem Kopf stehen und links oben angelegt sein (siehe Beispiele)

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:

  1 import 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'
  7 String Dateiname = "space_invader";
  8 
  9 //Koordinaten der Lichtquelle in mm
 10 int lichtY = 150;  //Entfernung
 11 int lichtZ = 80; //Höhe
 12 // x wird immer in die Mitte des Objektes gelegt -> siehe Variable 'mitte'
 13 
 14 float mm2px_faktor = 3.54331; // Umrechung der px von Inkscape zu mm 1mm entspricht 3.54331px
 15 int trennwert = 10;   //Inseln erkennen und nicht verbinden -> kleine Zahl wenn Insel sehr nahe beieinander
 16 float scale = 1 ;    //Grösse der Darstellung auf dem Bildschirm
 17 
 18 String[] SVGdatei = new String[2]; //String Array zur koordinatenausgabe
 19 RPoint[] points;  //Array für Koordinaten
 20 
 21 int i = 0; // Zähler
 22 float mitte = 0; // Mitte der X-Achse des Objektes
 23 float PXlast = 0; // vorheriger x-Punkt um Linie zeichnen zu können 
 24 float PYlast = 0; // vorheriger y-Punkt um Linie zeichnen zu können 
 25 
 26 //3D-Raum mit Schnittebene 
 27 PVector[] face = new PVector[3];
 28 PVector n;//normal
 29 Ray r1;
 30 
 31 
 32 void 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   
 79 void 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 
128 class Ray{
129   PVector start = new PVector(),end = new PVector();
130   Ray(PVector s,PVector e){   start = s ; end = e;  }
131 }

Beispiel: Space Invader

Ziel ist ein ca. 42 x 30 cm großer Space Invader der mit einer LED beleucht wird die 15 cm entfernt ist und einen Abstand von 8 cm zur Wand hat.

Space Inavder (SVG)
Space Inavder Installation
Space Inavder verzerrt (SVG) -> runterladen und laserschneiden!!!

Beispiel: ccc-r0ket

ccc roket (SVG)
ccc roket verzerrt (SVG) -> runterladen und laserschneiden!!!

Installation

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

Als Lichtquelle diente jeweils eine PowerLED mit Abstrahlwinkel: 120°

Bilder