Blog der Heimetli Software AG

Wachstum eines Sechsecks

Wenn am Ende einer Liste von Kreisen immer wieder ein neuer Kreis hinzugefügt wird, entsteht nicht etwa ein Kreis sondern ein Sechseck!

Der Code für die Animation

class Circle
{
   constructor( x, y, r )
   {
      this.x = x ;
      this.y = y ;
      this.r = r ;
      this.color = "#0000CC" ;
   }

   draw( ctx )
   {
      ctx.fillStyle = this.color ;
      ctx.beginPath() ;
      ctx.arc( this.x, this.y, this.r, 0, 2*Math.PI ) ;
      ctx.fill() ;
      ctx.stroke() ;
   }
}

class Animation
{
   CENTERX = 250 ;
   CENTERY = 250 ;
   RADIUS  =   7 ;

   // Control points for the bezier curve
   points  = [
          { x: this.CENTERX, y: 0 },
          { x: 0, y: 0 },
          { x: 0, y: 0 },
          { x: 0, y: 0 } ] ;

   // Parameter for the bezier curve
   t       = 1 ;

   // List of the circles
   circles = [] ;

   // Moving circle
   circle  = new Circle( this.CENTERX, this.CENTERY, this.RADIUS ) ;

   ctx     = document.querySelector( "#canvas" ).getContext( "2d" ) ;

   bezier( a, b, c, d, t )
   {
      let ti = 1 - t ;

      return  a * ti * ti * ti + 3 * b * ti * ti * t + 3 * c * ti * t * t + d * t * t * t ;     
   } 

   moveCircle()
   {
      this.circle.x = this.bezier( this.points[0].x, this.points[1].x, this.points[2].x, this.points[3].x, this.t ) ;
      this.circle.y = this.bezier( this.points[0].y, this.points[1].y, this.points[2].y, this.points[3].y, this.t ) ;
   }

   addCircle( circles )
   {
      if( circles.length == 0 )
         circles.push( this.circle ) ;
      else if( circles.length == 1 )
      {
         this.circle.y = this.CENTERY - this.RADIUS ;
         this.circle   = new Circle( this.CENTERX, this.CENTERY+this.RADIUS, this.RADIUS ) ;
         circles.push( this.circle ) ;
      }
      else
      {
         const last = this.circle ;

         let cb = circles[0] ;

         let xmin = 250 ;
         let ymin = 250 ;

         for( let i = 0; i < circles.length - 2; i++ )
         {
            let c = circles[i] ;

            if( xmin > c.x )
               xmin = c.x ;

            if( ymin > c.y )
               ymin = c.y ;

            if( Math.hypot(last.x-c.x,last.y-c.y) < 2 * this.RADIUS + 1 )
            {
               cb = c ;
            }
         }

         const wx = this.CENTERX - xmin ;

         const pivot = { x: (last.x + cb.x) / 2, y: (last.y + cb.y) / 2 } ;
         const vect  = { dx: last.x - pivot.x, dy: last.y - pivot.y } ;

         this.points[3].x = pivot.x + vect.dy * Math.sqrt(3) ;
         this.points[3].y = pivot.y - vect.dx * Math.sqrt(3) ;

         this.circle = new Circle( this.CENTERX, 0, this.RADIUS ) ;

         if( Math.abs(last.x-this.points[3].x) < 3 )
         {
            if( this.points[3].y < last.y )
            {
               this.points[1].x = this.points[3].x + 50 ;
               this.points[1].y = ymin / 2 ;
               this.points[2].x = this.points[3].x + 50 ;
               this.points[2].y = this.points[3].y ;
            }
            else
            {
               this.points[1].x = this.points[3].x - 50 ;
               this.points[1].y = ymin / 2 ;
               this.points[2].x = this.points[3].x - 50 ;
               this.points[2].y = this.points[3].y ;
            }
         }
         else
         {
            if( this.points[3].y < last.y )
            {
               if( this.points[3].x < last.x )
               {
                  this.points[1].x = this.points[3].x ;
                  this.points[1].y = this.points[3].y / 2 ;
                  this.points[2].x = this.points[3].x + 20 ;
                  this.points[2].y = this.points[3].y - 20 ;
               }
               else
               {
                  this.points[1].x = this.CENTERX + wx + 100 ;
                  this.points[1].y = ymin / 3 ;
                  this.points[2].x = this.points[3].x + wx + 50 ;
                  this.points[2].y = this.points[3].y + 100 ;
               }
            }
            else
            {
               if( this.points[3].x < last.x )
               {
                  this.points[1].x = this.points[3].x ;
                  this.points[1].y = this.points[3].y / 2 ;
                  this.points[2].x = this.points[3].x - 20 ;
                  this.points[2].y = this.points[3].y - 20 ;
               }
               else
               {
                  this.points[1].x = this.CENTERX - wx - 100 ;
                  this.points[1].y = ymin / 3 ;
                  this.points[2].x = this.points[3].x - wx - 50 ;
                  this.points[2].y = this.points[3].y + 100 ;
               }
            }
         }

         circles.push( this.circle ) ;

         this.t = 0 ;
      }
   }

   update()
   {
      if( this.circles.length < 271 )
      {
         this.ctx.fillStyle = "#FFFFFF" ;
         this.ctx.fillRect( 0, 0, 500, 500 ) ;

         if( this.t >= 1 )
            this.addCircle( this.circles ) ;
         else
         {
            this.t += 0.01 ;
            this.moveCircle() ;
         }

         this.ctx.strokeStyle = "#FFFF00" ;
         this.circles.forEach( c => c.draw(this.ctx) ) ;

         requestAnimationFrame( update ) ;
      }
      else
      {
         if( this.t < 1 )
         {
            this.ctx.fillStyle = "#FFFFFF" ;
            this.ctx.fillRect( 0, 0, 500, 500 ) ;

            this.t += 0.01 ;
            this.moveCircle() ;

            this.ctx.strokeStyle = "#FFFF00" ;
            this.circles.forEach( c => c.draw(this.ctx) ) ;

            requestAnimationFrame( update ) ;
         }
      }
   }
}

function update()
{
   animation.update() ;
}

const animation = new Animation() ;
requestAnimationFrame( update ) ;