变换动画

这里分享一个变换算法。原理简单,效果有趣。你可以绘制任何内容,然后对图形进行形态转换。

实现原理

包含两个部分 1.运动算法 2.关联坐标点

运动算法

有尝试用程序写过运动效果的想必非常熟悉这个写法。

(Processing 代码)

PVector A,B;

void setup(){
  size(500,500);
  A = new PVector(width/2,height/2);
}

void draw(){
  background(0);
  B = new PVector(mouseX,mouseY);
  A.x = A.x + (B.x - A.x) * 0.05;
  A.y = A.y + (B.y - A.y) * 0.05;
  fill(255);
  ellipse(A.x,A.y,10,10);
}

点A往点B的方向不断累加,并按比例地不断逼近点 B,则可实现由快至慢的运动效果。你也可以用其他的运动算法来决定点 A 的运动形式。匀速,变速,直线运动,曲线运动。

关联坐标点

一般用数组或是vector来记录点坐标,所以可以将两个不同的绘画轨迹分成两组来保存。外面再加一个 for 循环做遍历,就可以将上面的运动效果,应用到每一个点上。但问题在于,每组点的数据量,往往是不一致的。画面的内容越多,点的坐标数就越多。因此无法完全一一对应。

这里有一个巧妙的解决方法。

(关键代码)

  if(pic1.length < pic2.length){
            int addNum = pic2.length - pic1.length;
            for(int i = 0;i < addNum;i++){
                pic1 = (PVector [])append(pic1,pic1[int(random(pic1.length-1))]);
               
            }
        }
        if(pic1.length > pic2.length){
            int addNum = pic1.length - pic2.length;
            for(int i = 0;i < addNum;i++){
               pic2 = (PVector [])append(pic2,pic2[int(random(pic2.length-1))]);
            }
        }

只要在点的数量相对少的一边,通过随机的方式,补充点坐标,就可以使两边的点一一对应起来。这种做法不会破坏点数较少一边的造型,相当于某些点有了重叠的分身。它会在运动的过程中,分裂成若干个点。又或是若干个点,汇聚成一个点。

Processing完整代码:

PVector []pic1;
PVector []pic2;
int curPic;
boolean running;

void setup(){
  size(500,500);
  pic1 = new PVector[0];
  pic2 = new PVector[0];
  curPic = 1;
}

void draw(){
  background(0);
  
  if(curPic == 1){
    for(int i = 0;i < pic1.length;i++){
      noStroke();
      fill(255);
      ellipse(pic1[i].x,pic1[i].y,5,5);
    }
  }
  if(curPic == 2){
    for(int i = 0;i < pic2.length;i++){
      noStroke();
      fill(255);
      ellipse(pic2[i].x,pic2[i].y,5,5);
    }
  }
  
    if(running && pic1.length!= 0 && pic2.length!= 0){
        for(int i = 0;i < pic2.length;i++){
            pic2[i].x += (pic1[i].x - pic2[i].x) * 0.05;
            pic2[i].y += (pic1[i].y - pic2[i].y) * 0.05;
        }
    }
}

void keyPressed(){
  if(key == '1'){
    curPic = 1;
  }
  if(key == '2'){
    curPic = 2; 
  }
  if(key == 'r'){
        if(pic1.length < pic2.length){
            int addNum = pic2.length - pic1.length;
            for(int i = 0;i < addNum;i++){
                pic1 = (PVector [])append(pic1,pic1[int(random(pic1.length-1))]);
               
            }
        }
        if(pic1.length > pic2.length){
            int addNum = pic1.length - pic2.length;
            for(int i = 0;i < addNum;i++){
               pic2 = (PVector [])append(pic2,pic2[int(random(pic2.length-1))]);
            }
        }
        running = true;
    }
}

void mouseDragged(){
  if(curPic == 1){
    pic1 = (PVector [])append(pic1,new PVector(mouseX,mouseY));
  }
  if(curPic == 2){
    pic2 = (PVector [])append(pic2,new PVector(mouseX,mouseY));
  }
  if(curPic == 'r'){
    running = true; 
  }
}

上面的代码可以完整地体现变化的过程。

  • 数字键 1 ,2 切换画布
  • 数字键 1 对应绘制图形1
  • 数字键 2 对应绘制图形2
  • ‘r’键:开始变换动画,将图形 2 变成图形 1.

为了让代码结构更清晰,做了一定程度的简化。以上的转换过程是不可逆的,如果你希望可以自由来回切换,则需要创建一个数组 temp 来作为中间变量,以保存变换之前的点坐标。

其它形式

这种变换可以有多种变式。

你只要导入模型,获取模型的顶点数据。 图片的转换也是同理,由于图片尺寸本身是规整的,前后像素点的数量亦一致,所以只需建立点与点之间的对应关系。

这里将前后两张图片进行了明度排序,图片A最亮的像素点,对应用图片B最亮的像素点,由亮到暗往下排序。由于这个变换过程并不改变原始图片像素的颜色,所以前后图片灰度像素的层次分布越相似,最终还原的效果偏差会越少。

坐标点也可以替换成图片素材,可以依此制作一个围棋棋形变化的动画。

因为是用棋子去表现,如果某些棋子是凭空出现,会显得比较奇怪。所以在排列设计棋形的时候,就刻意用数量相同的棋子(数量都是51)。

OF 源码: https://github.com/Wenzy--/OpenFrameWorksTest/tree/