创意自画像——我对对称美的追求

1.对称之美——我的灵感来源

  在科学中,对称性是指某种操作下的不变性或者守恒性,对称性常与守恒定律相联系。与空间平移不变性对应的是动量守恒定律;与时间平移不变性对应的是能量守恒定律;与转动变换不变性对应的是角动量守恒;与空间反射(镜像)操作不变性对应的是宇称守恒。在弱相互作用中,“宇称”不守恒,自然界在C或P下不是对称的,在CP下也不是对称的,但却是CPT对称的。这里C表示电荷变号操作,相当于反转变换,如由底片洗出照片,电子变正电子,物质变反物质;P表示镜像反射操作,如人照镜子;T表示时间反演操作,如微观可逆过程。也就是说,当同时把粒子与反粒子互变(C)、左与右互变(P)、过去与未来互变(T),自然界又是对称的。——百度百科

  没错,我们生活中处处存在对称,对称也是我非常喜欢的一个性质,甚至对于可以那些明明可以对称但偏偏没有对称的东西有一种执念,就想它对称起来。所以我的创意编程灵感就来源于此。

2.作品展示

*鼠标未进入时*

鼠标未进入时

*鼠标进入时*

鼠标进入

*鼠标在中运动*

在这里插入图片描述

*鼠标移出时*

在这里插入图片描述

2.1表达思想

  图片想表现的正是对称的概念,正负相生,阴阳相容,用鼠标作过画的人都知道,别说画根直线或者画得像什么东西了,就写个字都奇丑无比,因为其难以控制。所以,这里鼠标体现的就是杂乱无章的运动,而通过对该运动的复制和对称演绎,实现了将无形无律之运动转变为有形有律之图案。看单个图案或许并不规则,但通过简单的旋转重复对称,整幅图案就变得有规律甚至让人觉得美妙动人。

2.2图片内容和程序功能的介绍:

  ①在图案中间有两个同心圆环,旋转方向相反;②周围有小球绕圆心做半径不断周期性变化的圆周运动,并有拖影效果;③当鼠标进入时,小球会离开当前位置并去追逐鼠标,该动作在50帧内完成;④鼠标在画布中运动时,小球跟随鼠标运动;⑤当鼠标离开画布时,小球离开当前位置前往自动旋转的位置,该动作也在50帧内完成;⑥通过上下方向键实现画布中小球数目的变化。

3.程序编写及代码

3.1 同心圆环的实现

  在P5中并没有找到直接画圆弧的函数,所以这里的实现很简单,就是利用先后绘画图案的遮盖关系,画一大一小两个扇形,一个亮色一个和背景色相同,即可实现圆弧的绘制。然后就是圆弧的简单堆砌。
  圆弧旋转的实现用的是上一篇文章的方法,millis()函数记录了程序运行的时长,通过它来控制旋转,不再赘述。

  var t = millis()/20;
//draw the circle at the center
  for (var n=1; n<=numSlices; n++)
  {
    fill(0, 255, 255);
    for (var i=0; i<4; i++)
    {
      arc(0, 0, 120, 120, t+10+i*90, t+90-20+i*90);
    }
    fill(0);
    for (i=0; i<4; i++)
    {
      arc(0, 0, 110, 110, t+10+i*90-5, t+90-20+i*90+5);
    }

    fill(0, 255, 255);
    for (i=0; i<4; i++)
    {
      arc(0, 0, 80, 80, -t+10+i*90, -t+90-20+i*90);
    }
    fill(0);
    for (i=0; i<4; i++)
    {
      arc(0, 0, 70, 70, -t+10+i*90-5, -t+90-20+i*90+5);
    }
  }

3.2 小球拖尾效果的实现

  通过全局position数组记录小球的过往位置信息,并将其同步绘画在当前画布上,并按时间的先后顺序调整其相对大小(新点大旧点小)即可。
  这段代码参考了CSDN,但优化了其代码,更正了逻辑错误也减少了没用的冗余。

function drawPoints(xs) 
{
  for (var i = xs.length - 1; i >= 0; i--)
  {
    var x = xs[i].x;
    var y = xs[i].y;
    var dia = diameter + diameterStep * (numPositions - i);//基础大小(即最小的)+变大步长
    fill(0, 255, 255, 255 * (1 - i / numPositions));
    ellipse(x - centerX, y - centerY, dia, dia);
  }
}

3.3 拖尾小球周期性运动的实现

  这里用了旋转画布的思想来实现其对称,在画布以规定的次数旋转一周后即可得到规定数目的小球,这里用numSlices变量来控制小球数量。
  小球的圆周运动同样是借助millis()得到时间来实现,实现半径的变大变小是用了direction变量来控制其半径变化方向,到达规定边界后更改direction的值即可。

//小球自动运动时位置的寻找
if (direction_control == 0)
  {
    au_positions.unshift( { 
      x: 
      sin(-t*1.5) * range_control+400, 
      y: 
      cos(-t*1.5) * range_control+400,
    }
    );
    range_control = range_control + 1;
    if (range_control>=300)
    {
      direction_control = 1;
    }
  } else
  {
    au_positions.unshift( { 
      x: 
      sin(-t*1.5) * range_control+400, 
      y: 
      cos(-t*1.5) * range_control+400,
    }
    );
    range_control = range_control - 1;
    if (range_control<=100)
    {
      direction_control = 0;
    }
  }
  au_positions.splice(numPositions);//用来去掉过于陈旧的点,只保留规定数目的点

  然后调用画拖尾小球的函数,并将画布旋转规定次数即可。

      for (var m = 1; m <= numSlices; m++) 
      {

        rotate(360 / numSlices);
        drawPoints(au_positions);
      }

3.4 鼠标在画布中时小球运动的实现

  和上文实现类似,只不过小球位置的记录不同。

  positions.unshift( { 
  x: 
  mouseX, 
  y: 
  mouseY,
  }
  );
  positions.splice(numPositions);

  同样旋转画布实现对称。

	  for (var i = 0; i < numSlices; i++) 
      {
        drawPoints(positions);
        rotate(360 / numSlices);
      }

3.5 鼠标进入时小球追逐动画的实现

  首先判断鼠标是否在画布中(我的画布大小是800*800在左上角)。
  然后判断鼠标是由画布外进入的还是一直都在画布中的。若由画布外进入则进行追逐动画。
  实现: ①通过两个变量来控制,一个用来判断鼠标是否为由外部移入,一个用来判断追逐的开始(用来对小球追逐位置初始化),这里借鉴了操作系统中原子操作的思想,即做一个标记来标明我这步工作是否做完,我不不做完就不做下一步才能干的事情。
      ②小球开始追逐的位置即为小球当前自动运动到的位置,然后将▲d设置为二者之间的差距/(50 - 已经运动帧数)的步长(小球会随鼠标运动越追越快),然后不断绘制小球每步的位置即可。需要注意的是:为了旋转方便我将绘图原点放在了画布中央,但是鼠标位置是以浏览器用户控制区左上角为原点的,所以这其中涉及到一个坐标转化问题(一个初中都应该会的坐标转化结果搞了一晚上……)。

  if (mouseX<800 && mouseY<800)
  {
    
    if (mouse_first_2_out == 0)
    {
      mouse_first_2_out = 1;
      mouse_first_1_out = 1;
    }
    
    if (mouse_first_2_in == 1)//chase the mouse
    {
      if (mouse_first_1_in == 1)
      {
        x0 = int(400-au_positions[0].x);
        y0 = int(400-au_positions[0].y);
        mouse_first_1_in = 0;
      }
      if (chase_num_in < 50)
      {
        ddx = (0-(mouseX-400)-x0) / (50 - chase_num_in);
        ddy = (0-(mouseY-400)-y0) / (50 - chase_num_in);
        x0 = x0 + int(ddx);
        y0 = y0 + int(ddy);

        var dia = diameter + diameterStep * numPositions;
        fill(0, 255, 255);
        for (var m = 1; m <= numSlices; m++) 
        {
          rotate(360 / numSlices);
          ellipse(-x0, -y0, dia, dia);
        }
        chase_num_in++;
      } else
      {
        mouse_first_2_in = 0;
        chase_num_in = 0;
        //print('have chase the mouse');
      }
    } else
    {
      // For each slice
      for (var i = 0; i < numSlices; i++) 
      {
        drawPoints(positions);
        rotate(360 / numSlices);
      }
    }
  }

3.5 鼠标离开时小球归位动画的实现

  这里和鼠标进入时极为相似,将其反向即可。

  else
  {

    if (mouse_first_2_in == 0)
    {
      mouse_first_2_in = 1;
      mouse_first_1_in = 1;
    }


    if (mouse_first_2_out == 1)//chase the mouse
    {
      if (mouse_first_1_out == 1)
      {
        x0 = -int(mouseX-400);
        y0 = -int(mouseY-400);
        mouse_first_1_out = 0;
      }
      
      if (chase_num_out < 50)
      {
        ddx = (0-(au_positions[1].x-400)-x0) / (50 - chase_num_out);
        ddy = (0-(au_positions[1].y-400)-y0) / (50 - chase_num_out);
        x0 = x0 + int(ddx);
        y0 = y0 + int(ddy);

        var dia = diameter + diameterStep * numPositions;
        fill(0, 255, 255);
        for (var m = 1; m <= numSlices; m++) 
        {

          rotate(360 / numSlices);
          ellipse(-x0, -y0, dia, dia);
        }
        chase_num_out++;
      }else
      {
        mouse_first_2_out = 0;
        chase_num_out = 0;
        //print('have chase the mouse');
      }
    }
    else
    {
      for (var m = 1; m <= numSlices; m++) 
      {

        rotate(360 / numSlices);
        drawPoints(au_positions);
      }
    }
  } 

3.6 键盘响应函数

  这里用键盘响应函数实现小球数目的增减。需要注意的是keyIsDown()函数会对你的按键操作响应一次,完成规定操作,而keyIsPressed()函数则只会在按下相应按键的时段内进行响应,在放开按键后会恢复成按键前的样子。

    if(keyIsDown(UP_ARROW))
    {
      numSlices++;
      time_control_rull = time_control;
    }
    else if(keyIsDown(DOWN_ARROW))
    {
      numSlices--;
      time_control_rull = time_control;
    }

  这里还有个问题,即更新速度太快了,往往按以下后会一下子响应好多次。所以加入了按键时间间隔判断,如果这次按下的时间在上次按下的0.5秒之后了才行。优化代码如下:

  time_control = millis();
  //前面还有一个定义了初始值的time_control_rull。
  //print(time_control - time_control_rull);
  if(time_control - time_control_rull >=500)
  {
    
    if(keyIsDown(UP_ARROW))
    {
      numSlices++;
      time_control_rull = time_control;
    }
    else if(keyIsDown(DOWN_ARROW))
    {
      numSlices--;
      time_control_rull = time_control;
    }
  }

4.实验体会

  这是我第一个P5小三百行的程序,能实现自己心里所想的功能还是很有成就感的,但是创作时长达十个小时,这是让我没有想到的。其实总结写起来很简单,但做的时候真是大费周章。我认为有两点值得反思学习,首先,头脑中蓝图要清晰,我的蓝图真正清晰起来是在编写了三个小时左右的时候。其次,代码实际上就是逻辑和数学的体现,公式一定要推导清楚,不要急于敲代码尝试!因为很浪费时间。我发现当我敲代码时总是想先敲出来试试,导致思考总是仓促的、马虎的、想当然的、不周到的,导致一直在出错,这无疑是浪费时间的,是应该避免的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值