文章目录

  • 引言
  • 原作品:Circular Hello Curves
    • 主流程
    • 构造函数
    • update
    • display
  • 拓展:Broken Heart
    • 变成心形
    • 添加眼睛
  • 更多拓展
  • 小结

引言

最近听见一种说法:“雷神”分为两种,一种存在于漫威宇宙,一种存在于宝可梦宇宙。前者身形高大,能抵挡一切病毒、辐射和毒气攻击,手持“雷神之锤”还可操纵雷电风暴天气。后者是一只黄皮电耗子,体重超标,容易感冒,疑似靠脸吃饭,放电方式成迷。

最近大家肯定都被下面这个靠脸吃饭的黄皮耗子萌到了吧!

是滴,这就是最近大火的电影《大侦探皮卡丘》里面的那一只,让人不禁感叹:原来你是这样的皮卡丘。

但今天我们的主角不是皮卡丘。大家都知道这部电影是改编自游戏《精灵宝可梦》,里面有各种各样的精灵,而皮卡丘只是其中一只。今天我们的主角是一只比较特殊的精灵——百变怪。

这次它站c位的原因不是它有变成任何精灵这种无与伦比的能力,而是它碰巧出现在了下面的这个作品中。

观察作品,可以发现一个个字母在看上去好像在这只黑黑的百变怪身上有规律的跳舞,很有律动感。

这件作品Circular Hello Curves是大佬Vamoss用P5.js写的,我用Processing实现了相同的效果。

在讲解完这个作品后,我会给大家看看我在次基础上进行的拓展——Broken Heart,大家可以想一想到底是看见可什么伤心的消息,才让一颗漂亮的心扭曲成如此模样?

心急的同学可以看看在线效果Broken Heart。
  

原作品:Circular Hello Curves

现在我们就来介绍如何实现Circular Hello Curves。
  

主流程

第一步我们来看一下代码的主要流程。首先我们声明一个类型为Shape的全局变量,这个就是百变怪和字母的混合体。然后在setup函数对其进行声明,传入的参数分别是这个混合体中心的坐标和需要绘制的文字内容。接下在draw函数中对这个混合体不断得进行刷新和绘制。最后监听鼠标移动事件,在鼠标进行移动的时候对字体进行旋转。

Shape s;
void setup() {size(600, 600);s = new Shape(width / 2, height /2, 100, "Circular Hello Curves");
}  void draw() {background(255);s.update();s.display();
}void mouseMoved() {s.rotateText();
}class Shape{}

  

构造函数

第二步我们来看看Shape对象的构造函数,看一看这个混合体由什么构成,需要初始化什么东西。

分析下面的构造函数我们可以得到如下的发现。

百变怪是由一系列点围成的,每一个点由它的极坐标表示,而它所在的极坐标系是以Shape的x,y为坐标原点的。用一个PVector存储每一个点,其中x维度代表的是该点的方向,y维度代表的是该点的长度。初始化的时候在 [ 0 , 2 π ) [0, 2\pi) [0,2π)的范围内给每一个点设置一个方向。

然后给每一个字符分配一个PVector,用来存储它的极坐标以及它旋转的角度,其中x维度代表的是方向,y维度代表的是长度,z维度是旋转的角度。初始化的时候只用初始化每一个字符的方向。假设该字符所占用的弧长近似等于它的宽度,根据弧度制角度的计算公式,可以计算出它对应的夹角,从而依次计算每个字符的方向。

  Shape(float _x, float _y, float _r, String _text) {x = _x;y = _y;r = _r; text = _text;//初始化构成百变怪的点。points = new ArrayList();for (int i = 0; i < SEGMENT; i++) {float theta = map(i, 0, SEGMENT - 1, 0, TAU);//初始化每一个的方向points.add(new PVector(theta, 0));}//初始化需要用到的字体,并进行设置。f = createFont("Helvetica", _r / 2 * 1.5);textFont(f);textAlign(CENTER);//初始化需要展现的字母。chars = new ArrayList(); float theta = 0;for (int i = 0; i < text.length(); i++) {char currentChar = text.charAt(i);float thetaStep = textWidth(currentChar) / (_r * 1.5);theta += thetaStep / 2;//初始化每一个的方向chars.add(new PVector(theta, 0, 0));theta += thetaStep / 2;}}

然后我们再看看Shape的成员变量的声明。

class Shape{//构成百变怪点的数量final int SEGMENT = 200;//百变怪的扭曲程度final float NOISE_SCALE = 1;ArrayList<PVector> points;ArrayList<PVector> chars;float x, y, r;//绘制字体的启始角度float startAngle;String text;PFont f;//...
}

  

update

在每一次update的时候,我们会更新每一个点的位置,这个主要是更新它到原点的距离来实现的。同时也会更新每一个字符的位置和旋转角度,每个字符的位置也是通过更新它到原点的距离实现的。

  void update() {for (PVector p : points) {p.y = rByTheta(p.x);}for (PVector c : chars) {c.y = rByTheta(c.x + startAngle);c.z = angleByTheta(c.x + startAngle);}}

下面我们首先来看一下每一个点如何根据它的方向来确定它到原点的距离。我们会对r乘上一个scale来达到扭曲效果,而scale就是我们前面提到的周期性随机数。不同的是,我们传入了第三个参数,使得每一次刷新的时候,每一个点的到原点的距离发生变化,这样才会有膨胀和收缩的效果。

  float rByTheta(float theta) {float time = float(frameCount) / 100;float scale = noise(cos(theta) + 1, sin(theta) + 1, time) * NOISE_SCALE + 1;return  r * scale;}

然后我们来分析如何通过该点的方向来确定每个字符的旋转角度。我们首先获得该字符附近左右的两个点,然后根据这两个点到原点的距离获得该字符的旋转角度。具体方法如代码所示。

  float angleByTheta(float theta) {final float offset = 0.06;float theta1 = theta - offset, theta2 = theta + offset;float a = rByTheta(theta1);float b = rByTheta(theta2);return atan2(a * sin(theta1) - b * sin(theta2), a * cos(theta1) - b * cos(theta2)) + PI;}

在该部分的最后我们再来看如何实现文字随着鼠标旋转的效果。我们是通过更新文字的起始角度来实现的,而该角度是鼠标与坐标原点的连线和起始边的夹角的角度,我们通过如下的方式计算而得。

  void rotateText() {startAngle = atan2(mouseY - y, mouseX - x);}

  

display

最后来我们来看一下Shape函数,看它是如何绘制这个混合体的。

首先将points中所有点绘制并连接起来,然后填充为黑色。这样就将混合体中的百变怪部分绘制完成了。

接下来就是根据chars里面存储的数据对每一个字符进行绘制。值得注意的是每个字符保存的方向是相对于起始角度的,所以在绘制的时候应该加上startAngle。

  void display() {//绘制百变怪translate(x, y);beginShape();fill(0);for (PVector p : points) {vertex(p.y * cos(p.x), p.y * sin(p.x));}endShape();//绘制文字for (int i = 0; i < text.length(); i++) {PVector c = chars.get(i);pushMatrix();translate(c.y * cos(c.x + startAngle), c.y * sin(c.x + startAngle));rotate(c.z);text(text.charAt(i), 0, 0);popMatrix();}}

  

拓展:Broken Heart

接下来给大家简单讲解一下我在Circular Hello Curves基础上实现的Broken Heart。主要分为两步,第一步将扭曲前的图形从圆形变成心形,第二步添加一双随着鼠标转动的眼睛。
  

变成心形

从圆形变成心形很简单,用我们之前提到的心型线就ok,修改rByTheta如下。这里改变余弦函数初相的目的是将心型线顺势针旋转九十度,使得心尖向下。

  float rByTheta(float theta) {float time = float(frameCount) / 100;float scale = noise(cos(theta) + 1, sin(theta) + 1, time) * NOISE_SCALE + 1;return  r * (1 + cos(theta - PI / 2)) * scale ;}

  

添加眼睛

添加眼睛我们需要对Shape这个类做如下的修改。最主要的是增加了一个rotateEye的函数,该函数的作用是让眼睛跟着鼠标转动。

class Shape{//...Eye left, right;Shape(float _x, float _y, float _r, String _text){//...left = new Eye(-r / 1.8, r, r );right = new Eye(r / 1.8, r, r);}void rotateEye() {left.update(mouseX - x, mouseY - y);right.update(mouseX - x, mouseY - y);}void display(){//draw Eyesleft.display();right.display();}//...
}

接下来我们将监听鼠标移动事件函数中修改如下。

void mouseMoved() {s.rotateText();s.rotateEye();
}

同时也修改传入Shape的位置和需要绘制的文字。

s = new Shape(width / 2, height / 3, 100, "Sad news!!!");

最后我们来看一下Eye这个类的定义,绘制一个眼睛的时候会绘制两个圆,分别代表眼白和眼珠,其中眼珠的方向是朝着鼠标的方向。

class Eye {float x, y, size, angle;Eye(float _x, float _y, float _size) {x = _x;y = _y;size = _size;}void update(float mx, float my) {angle = atan2(my - y, mx - x);}void display() {pushMatrix();translate(x, y);fill(255);ellipse(0, 0, size, size);rotate(angle);fill(0);ellipse(size/4, 0, size/2, size/2);popMatrix();}
}

  

更多拓展

代码部分就讲解完了,下面再给大家说两个我能想到的继续拓展的方向。

  • 泪汪汪的眼睛:Broken Heart的眼睛只是简单的圆形,说句实话,看上去不太像伤心的样子。如果我们能用上面的方法把眼睛扭曲一下,增加一种泪汪汪的感觉,是不是能看上去更悲痛欲绝?
  • 更多的形状:我们目前只是在圆形和心形基础上进行扭曲,大家可以去找更多的方程,将它们的图像进行扭曲,可能会得到一些更加炫酷的效果!
      

小结

那么又到了和大家说再见的时候了。

今天我们一起看了字母在百变怪上翩翩起舞,也看了一颗扭曲到变形的伤心的心。

最后再告诉大家一个小秘密,大家知道皮卡丘的脸颊两边的腮红是用来干什么的吗?你们一定猜不到,那是皮卡丘的电器袋,是用来放电的!

Processing 案例 | 字母在宠物小精灵上翩翩起舞相关推荐

  1. 《Linux高性能服务器编程》学习总结(四)——TCP/IP通信案例:访问Internet上的Web服务器...

    第四章      TCP/IP通信案例:访问Internet上的Web服务器 HTTP协议是工作在应用层上的协议,其应用十分广泛,而在进行通信的过程中,经常使用HTTP代理服务器.HTTP代理服务器主 ...

  2. 第五章:iptables应用案例分析(代理服务器上设置iptables)

    一.代理服务器架设的位置如上图 实验环境如下: 1)局域网网段:192.168.1.0/24,该网段内有2台服务器和1台客户端 (1)WEB服务器:192.168.1.3/24 (2)FTP服务器:1 ...

  3. 精彩案例-悬浮在桌面上的照相机

    一.简介 这个案例就是在桌面上开启一个悬浮窗,悬浮窗里实时显示照相机的内容,可以自由拖动,当在非桌面状态下自动隐藏.如下图所示(): PS:gif都失真了,凑合看,实际中这个窗口是不会闪烁的 我做这个 ...

  4. 数据分析案例学习---关于“线上教育提升毛利额问题

    数据分析案例学习-关于"线上教育提升毛利额问题"思路总结如下 一.背景分析拆解: 1.某教育公司成立于1999年,早年发展线下职业教育,已做到一定规模,有一定的群众基础.近年,互联 ...

  5. Processing 案例 | 扑面而来的满天繁星

    文章目录 引言 效果展现 原理分析 代码实现 确定代码执行流程 星星绘制在屏幕上 让星星动起来 点击鼠标,星星的速度发生改变 视角改变 完整代码 结语    引言 清明时节雨纷纷,路上行人欲断魂&qu ...

  6. jvm性能调优 - 17案例实战_每日上亿请求量的电商系统 老轻代垃圾回收参数如何优化

    文章目录 Pre 在案例背景下什么时候对象会进入老年代? 大促期间多久会触发一次Full GC? 老年代GC的时候会发生"Concurrent Mode Failure"吗? CM ...

  7. Processing 案例 | 去“富士山”看樱花从树上纷纷而落

    文章目录 引言 代码 大体结构 generateNewTree class Branch class Leaf 交互 鼠标 键盘 拓展 小结 参考资料 引言 大家都知道,樱花是日本的国花,他们对于樱花 ...

  8. Processing 案例 | 郭锐文先生的 worms

    文章目录 引言 代码 大体框架 Class StageManager Class Interference Class Worm Class Vertex 小结 参考资料 引言 最近在各大美术学院做讲 ...

  9. Processing 案例 | 诡异的八爪鱼

    文章目录 引言 代码 主要结构 class Squid class Tentacle class Limb 拓展 小结 引言 冬天到了,春天还会远吗?七月到了,八月还会远吗? 我们迎来了七月的烈日炎炎 ...

最新文章

  1. 零基础入门--中文命名实体识别(BiLSTM+CRF模型,含代码)
  2. SystemML大规模机器学习,优化算子融合方案的研究
  3. 利用外部知识增加QA答案自然程度,这是阿里的问答模型新思路丨EMNLP
  4. C#学习之三层架构实例
  5. android app内嵌h5页面,app内嵌h5页面:前端与原生语言的配合
  6. Excel工作表密码保护的破解
  7. 解决朋友圈压缩_朋友中最有趣的朋友[已解决]
  8. icmp端口_pingtunnel搭建icmp隧道
  9. java判断斐波那契数列_Java 实例 - 斐波那契数列
  10. MATLAB学习笔记(十六)
  11. Asp.Net中global.asax文件的描述
  12. python 程序块 挂掉的服务_写一个python的服务监控程序
  13. 微商分销代理商城源码带代理等级和升级条件 thinkphp框架
  14. 使用CSS美化shiny app效果
  15. OpenWrt mesh组网设置
  16. jquery获取span标签下的第一个span子标签内容
  17. Windows8 安装Vs2008 报错的解决方法
  18. 日语基础语法(完整篇)
  19. 格式: echo -e \033[字背景颜色;字体颜色m字符串\033[0m
  20. RFID智能档案管理系统 设计方案

热门文章

  1. 透过ISICDM,看医学图像分析的未来趋势与挑战
  2. 基于自研分布式内存及流数据库技术的柏睿数据宣布获2亿元C轮融资
  3. arp miss攻击_如何查看是否被arp攻击
  4. 什么是 ARP 攻击?
  5. 管理和理解 suspect_pages 表
  6. 为什么选择SpringCloud Gateway(SCG)
  7. 信息熵、信息增益、信息增益比
  8. 文库发表评论奖励规则说明
  9. MPchart的piechart一些方法
  10. python locals()用法_Python locals()用法及代码示例