2018年11月29日 星期四

「智泉拾叁」創作


2018 年 11 月 22 日,「智泉 13」正式開始。距離上一次的體驗式課程,已經相隔有 17 年左右。如同 19 年前參與「IN117」一樣,我也一起設計團隊標誌及製服;同時也發揮想像力,設計一些美術作品。其中一樣作品,是希望用元祖太極圖案,加上中文字「拾叁」來創作。如果一個一個地畫出太極實在很耗時,作為程式員,這個情景可以幫得上忙。


要製作出這樣的效果,首先要準備一張遮罩圖。黑色代表繪畫太極的空間,白色代表留白的地方。把遮罩圖黑色地方記下,然後在這些地方隨機生成太極的圖案。我希望把整個過程以動畫方式呈現,所以加入 Circle 類別用來處理太極由小變大的過程;並且把每一步驟的幀記錄下來。最後,以 ffmpeg 指令「ffmpeg -framerate 60 -i out_%04d.png -s:v 1024x550 -c:v libx264 -profile:v high -crf 20 -pix_fmt yuv420p w13.mp4」生成影片。
<?php
//----------------------------------------------------------------------------------------
//  Packing Circle with Mask Image
//----------------------------------------------------------------------------------------
//  Platform: macOS Mojave + PHP5
//  Written by Pacess HO
//  Copyrights Pacess Studio, 2018.  All rights reserved.
//----------------------------------------------------------------------------------------

class Circle  {
   private $isGrowing = true;

   public $x = 0;
   public $y = 0;
   public $r = 3.0;

   //----------------------------------------------------------------------------------------
   function setup($x, $y)  {
      $this->x = $x;
      $this->y = $y;
      $this->r = 3.0;
   }

   //----------------------------------------------------------------------------------------
   function draw($image)  {
      $foreground = imagecolorallocate($image, 0, 0, 0);
      imageellipse($image, $this->x, $this->y, $this->r*2, $this->r*2, $foreground);
   }

   //----------------------------------------------------------------------------------------
   function grow()  {
      if ($this->isGrowing == false)  {return;}
      $this->r++;

      if ($this->r > 30)  {$this->isGrowing = false;}
   }

   //----------------------------------------------------------------------------------------
   function stopGrow()  {$this->isGrowing = false;}

   //----------------------------------------------------------------------------------------
   function isEdge($width, $height)  {
      if (($this->x-$this->r) < 0)  {return true;}
      if (($this->y-$this->r) < 0)  {return true;}
      if (($this->x+$this->r) > $width)  {return true;}
      if (($this->y+$this->r) > $height)  {return true;}
      return false;
   }
}

//========================================================================================
//  Main program
$_width = 1024;
$_height = 768;

//  Loading mask image
list($_width, $_height) = getimagesize("w13.png");
$backgroundImage = imagecreatefrompng("w13.png");

//  Convert mask into spot array
$spotArray = array();
for ($y=0; $y<$_height; $y++)  {
   for ($x=0; $x<$_width; $x++)  {
      $color = imagecolorat($backgroundImage, $x, $y);
      $blue = $color&255;
      if ($blue >= 80)  {continue;}

      $spotArray[] = array($x, $y);
   }
}

//----------------------------------------------------------------------------------------
//  Create logo animation
$max = 99999;
$_array = array();
for ($i=0; $i<1000; $i++)  {

   //  New circle
   for ($j=0; $j<5; $j++)  {

      $valid = true;
      $value = rand(0, count($spotArray));
      $spot = $spotArray[$value];
      $x = $spot[0];
      $y = $spot[1];

      $newCircle = new Circle();
      $newCircle->setup($x, $y);
      foreach ($_array as $circle)  {

         $distance = sqrt(pow($circle->x-$newCircle->x, 2)+pow($circle->y-$newCircle->y, 2));
         if ($distance < ($circle->r+$newCircle->r+2))  {$valid = false;}
      }

      if ($valid == true && $max > 0)  {
         $_array[] = $newCircle;
         $max--;
      }
   }

   //  Create image
   $image = imagecreatetruecolor($_width, $_height);
   $foreground = imagecolorallocate($image, 0, 0, 0);
   $background = imagecolorallocate($image, 255, 255, 255);
   imagefilledrectangle($image, 0, 0, $_width, $_height, $background);
   foreach ($_array as $circle)  {

      $boolean = $circle->isEdge($_width, $_height);
      if ($boolean == true)  {$circle->stopGrow();}

      $x = $circle->x;
      $y = $circle->y;
      $r = $circle->r;
      imageellipse($image, $x, $y, $r*2, $r*2, $foreground);

      //  Overlapping
      $overlapping = false;
      foreach ($_array as $circle2)  {

         if ($circle == $circle2)  {continue;}
         $distance = sqrt(pow($circle->x-$circle2->x, 2)+pow($circle->y-$circle2->y, 2));
         if ($distance < ($circle->r+$circle2->r+2))  {$circle->stopGrow();}
      }

      $circle->grow();
   }

   $filename = sprintf("out_%04d.png", $i);
   imagepng($image, $filename);
   imagedestroy($image);
}
?>

沒有留言: