Blog der Heimetli Software AG

Simuliertes 3D-Objekt, mit Canvas und JavaScript realisiert

Immer weniger Surfer können Java-Applets anzeigen, dafür wurde der Support der Browser für Canvas immer besser. So als Versuch habe ich mein erstes Applet mit Canvas und JavaScript neu implementiert.

Wenn Sie mit der Maus über das Objekt fahren, dreht es sich um seinen Mittelpunkt. Auf dem Tablet funktioniert das nicht, und deshalb gibt es jetzt eine bessere Version.

Das Objekt wird nicht etwa durch WebGL dargestellt, sondern verwendet den "Painters Algorithm". Dabei werden weiter entfernte Flächen zuerst gezeichnet, und dann alle anderen auf der Z-Achse bis zum Betrachter darüber.

Der Code sieht vereinfacht so aus:

// Ecken definieren
var points =   [ { x: 1, y: 1, z: 1 }, { x: 1, y: 1, z:-1 },
                 { x:-1, y: 1, z:-1 }, { x:-1, y: 1, z: 1 }, 
                 { x: 1, y:-1, z: 1 }, { x: 1, y:-1, z:-1 },
                 { x:-1, y:-1, z:-1 }, { x:-1, y:-1, z: 1 } ] ;

// Flächen definieren
var surfaces = [ { p0: 3, p1: 2, p2: 6, p3: 7, color: "#FF0000" },
                 { p0: 0, p1: 6, p2: 5, p3: 3, color: "#0000FF" },
                 { p0: 0, p1: 1, p2: 5, p3: 4, color: "#FF0000" },
                 { p0: 0, p1: 4, p2: 7, p3: 3, color: "#FFFFFF" } ] ;

// Hilfsvariablen
var canvas, ctx, sinalpha, cosalpha, sinbeta, cosbeta ;

// X-Koordinate der Projektion berechnen
function px( point )
{
   return (cosalpha * point.x - sinalpha * point.y) * 100 + 200 ;
}

// Y-Koordinate der Projektion berechnen
function py( point )
{
   return (cosbeta * point.z - sinbeta * (cosalpha * point.y + sinalpha * point.x)) * 100 + 200 ;
}

// Eine Fläche zeichnen
function drawsurface( s )
{
   ctx.beginPath() ;
   ctx.moveTo( px(points[s.p0]), py(points[s.p0]) ) ;
   ctx.lineTo( px(points[s.p1]), py(points[s.p1]) ) ;
   ctx.lineTo( px(points[s.p2]), py(points[s.p2]) ) ;
   ctx.lineTo( px(points[s.p3]), py(points[s.p3]) ) ;
   ctx.closePath() ;
   ctx.fillStyle = s.color ;
   ctx.fill() ;
}

// Körper zeichnen. alpha und beta sind die Drehwinkel
function draw( alpha, beta )
{
   // Hilfsvariablen berechnen
   sinalpha = Math.sin( alpha ) ;
   cosalpha = Math.cos( alpha ) ;
   sinbeta  = Math.sin( beta  ) ;
   cosbeta  = Math.cos( beta  ) ;

   // Hintergrund löschen
   ctx.clearRect( 0, 0, canvas.width, canvas.height ) ;

   // Bodenfläche zuerst zeichnen wenn der Körper nach vorne gekippt ist
   if( beta <= 0 )
      drawsurface( surfaces[3] ) ;
   
   // Reihenfolge der Flächen wenn der Körper nach rechts gedreht ist
   if( alpha >= 0 )
   {
      drawsurface( surfaces[0] ) ;
      drawsurface( surfaces[1] ) ;
      drawsurface( surfaces[2] ) ;
   }
   else
   {
      drawsurface( surfaces[2] ) ;
      drawsurface( surfaces[1] ) ;
      drawsurface( surfaces[0] ) ;
   }

   // Bodenfläche zuletzt zeichnen wenn der Körper nach hinten gekippt ist
   if( beta > 0 )
      drawsurface( surfaces[3] ) ;
}

Die Mittelfläche war übrigens nicht so geplant, sondern entstand durch einen Zählfehler bei der Applet-Version. Was dabei herauskam, gefiehl mir aber besser als meine ursprüngliche Idee ;-)