最近经常能看到一些很漂亮的绘图,通过软件绘制各种静态或动态的曲线,例如本文封面的蝴蝶(这只是其中比较简单的一种)。除了感叹数学之美,本篇将介绍用什么工具,以及怎样使用,让你快速实现一个自己的函数可视化。
Processing 由 Casey Reas 与 Ben Fry 于 2001 年在 MIT Media Lab 的 “Design by Numbers” 项目组中创建,旨在让艺术家、设计师和学生能够用简洁的代码快速实现交互式图形、动画和可视化。Processing的核心理念是把编程当作“电子速写本”,提供统一的 API,让创意项目的原型开发变得像绘画一样直观。
与Processing功能相同或类似的工具有:openFrameworks、Cinder、p5.js (Web 版)等,以下对这几个工具在学习曲线、运行效率、跨平台部署等方面做个简单比较。

总结
这里以macOS M4芯片为例。Processing对于搭载M4芯片的Mac,提供了完整的ARM原生支持。除此之外,对windows、Linux操作系统也作为完美支持。
步骤1:下载安装包
# 访问Processing官网下载最新版
# 网址:https://processing.org/download
# 选择macOS ARM 64-bit版本步骤2:安装与配置
步骤3:验证安装
void setup() {
size(400, 400);
background(255, 0, 0);
println("Processing在M4 Mac上运行成功!");
}点击运行按钮,确认程序正常执行。这段代码是设置红色背景,并且设置窗口大小为400*400。Processing IDE包含以下主要区域:

让我们创建一个简单的动态图形程序:
// 全局变量
float circleSize = 50;
float speed = 2;
void setup() {
// 初始化画布 - 800x600像素
size(800, 600);
// 设置平滑抗锯齿
smooth();
// 设置帧率
frameRate(60);
}
void draw() {
// 半透明背景(创建拖尾效果)
fill(0, 25);
rect(0, 0, width, height);
// 更新圆圈大小
circleSize = 100 + 50 * sin(millis() * 0.005);
// 设置填充颜色(RGB)
fill(255, 100, 100, 150);
// 设置边框颜色
stroke(255, 200);
// 设置边框粗细
strokeWeight(3);
// 在鼠标位置绘制圆圈
ellipse(mouseX, mouseY, circleSize, circleSize);
// 在控制台输出信息
println("帧率: " + frameRate + " | 圆圈大小: " + circleSize);
}
// 鼠标点击交互
void mousePressed() {
// 随机改变背景色
background(random(255), random(255), random(255));
}
// 键盘交互
void keyPressed() {
if (key == ' ') {
// 空格键保存截图
saveFrame("screenshot-####.png");
}
}
在代码编辑器中输入上述代码后,点击上面的运行图标,会弹出一个窗口展示可视化效果:

Processing 的程序执行基于一个简单而强大的事件驱动模型,其中 setup() 和 draw() 是两个最核心的函数,构成了绝大多数 Processing 程序的基础骨架。
setup() 函数在程序启动时仅执行一次,主要用于初始化设置:
void setup() {
// 设置画布尺寸(必须)
size(800, 600);
// 设置背景色
background(255);
// 设置绘制模式
smooth(); // 抗锯齿
noCursor(); // 隐藏光标
frameRate(30); // 设置帧率
// 加载资源
PImage img = loadImage("texture.jpg");
PFont font = createFont("Arial", 16);
// 初始化变量
circleX = width/2;
circleY = height/2;
println("程序初始化完成!");
}
执行特点:
draw() 之前)必须包含的操作:size(400, 400); // 必须设置画布大小
坐标系统:
原点(0,0)在左上角
X轴向右递增,Y轴向下递增
draw() 函数在 setup() 执行完毕后持续重复执行,形成程序的主循环:
void draw() {
// 1. 清空画布(可选)
background(0);
// 2. 更新状态
updatePhysics();
// 3. 绘制图形
drawShapes();
// 4. 处理交互
handleInteraction();
// 5. 显示信息
displayStats();
}执行流程详解frameRate() 调整)常用绘图函数:
point(x, y): 绘制点
line(x1, y1, x2, y2): 绘制线
rect(x, y, width, height): 绘制矩形
ellipse(x, y, width, height): 绘制椭圆
与其他成熟的软件一样,processing提供了丰富的示例(https://processing.org/examples)。从基础的数组、颜色设置、图片,到复杂一些的函数展示、变形(Transform)、3D动画等都包含其中。你可以从浅到深,逐个尝试运行这些示例并学习代码。


4 进阶数学可视化示例
以下是一个结合多种数学函数的艺术可视化示例,实现效果:

代码如下:
/**
* 数学艺术:参数曲线场可视化
* 结合了李萨如曲线、正弦波和噪声函数
*/
float time = 0;
int resolution = 200;
float noiseScale = 0.02;
void setup() {
size(1000, 800, P2D);
colorMode(HSB, 360, 100, 100, 100);
background(0);
strokeWeight(1.5);
}
void draw() {
// 创建渐变背景
drawGradientBackground();
// 绘制参数曲线网络
drawParametricNetwork();
// 更新时间
time += 0.02;
// 显示信息
displayInfo();
}
void drawGradientBackground() {
// 禁用描边
noStroke();
// 创建径向渐变
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
// 计算距离中心的距离
float dist = dist(x, y, width/2, height/2);
float hue = map(sin(time + dist * 0.01), -1, 1, 200, 300);
float brightness = map(dist, 0, width/2, 80, 10);
// 设置像素颜色
stroke(hue, 70, brightness, 10);
point(x, y);
}
}
}
void drawParametricNetwork() {
// 绘制主参数曲线
for (int i = 0; i <= resolution; i++) {
float t = map(i, 0, resolution, 0, TWO_PI);
// 计算多条曲线的参数
for (int layer = 1; layer <= 3; layer++) {
float layerScale = 0.5 + layer * 0.3;
// 参数曲线方程
float x1 = width/2 + 150 * layerScale * sin(2 * t + time) * cos(3 * t);
float y1 = height/2 + 150 * layerScale * cos(4 * t + time) * sin(2 * t);
float x2 = width/2 + 150 * layerScale * sin(3 * t - time) * cos(2 * t);
float y2 = height/2 + 150 * layerScale * cos(5 * t - time) * sin(3 * t);
// 添加Perlin噪声
float noiseVal = noise(x1 * noiseScale, y1 * noiseScale, time);
x1 += 30 * (noiseVal - 0.5);
y1 += 30 * (noiseVal - 0.5);
// 设置颜色(基于极坐标)
float hue = map(t, 0, TWO_PI, 0, 360);
float alpha = map(layer, 1, 3, 40, 80);
stroke(hue, 80, 90, alpha);
// 绘制曲线段
if (i > 0) {
float prevT = map(i-1, 0, resolution, 0, TWO_PI);
float prevX1 = width/2 + 150 * layerScale * sin(2 * prevT + time) * cos(3 * prevT);
float prevY1 = height/2 + 150 * layerScale * cos(4 * prevT + time) * sin(2 * prevT);
line(prevX1, prevY1, x1, y1);
}
// 绘制连接线
stroke(hue, 60, 80, 20);
line(x1, y1, x2, y2);
}
}
// 绘制动态粒子
drawParticles();
}
void drawParticles() {
int particleCount = 50;
for (int i = 0; i < particleCount; i++) {
float angle = time + i * TWO_PI / particleCount;
float radius = 100 + 50 * sin(time * 2 + i * 0.5);
// 粒子位置(极坐标转笛卡尔坐标)
float x = width/2 + radius * cos(angle);
float y = height/2 + radius * sin(angle);
// 粒子大小和颜色
float size = 3 + 2 * sin(time * 3 + i);
float hue = (angle * 180/PI + time * 50) % 360;
// 绘制粒子
noStroke();
fill(hue, 90, 100, 80);
ellipse(x, y, size, size);
// 粒子轨迹
stroke(hue, 80, 100, 30);
strokeWeight(1);
if (frameCount > 1) {
float prevX = width/2 + radius * cos(angle - 0.1);
float prevY = height/2 + radius * sin(angle - 0.1);
line(prevX, prevY, x, y);
}
}
}
void displayInfo() {
// 显示帧率和时间信息
fill(0, 0, 100, 60);
rect(10, 10, 180, 60);
fill(0, 0, 100);
textSize(12);
text("帧率: " + nf(frameRate, 0, 1), 20, 30);
text("时间: " + nf(time, 0, 2), 20, 50);
text("分辨率: " + resolution + "x" + resolution, 20, 70);
}
// 交互功能
void mousePressed() {
// 鼠标点击时重置背景并添加效果
background(0);
time = 0;
}
void keyPressed() {
if (key == ' ') {
// 空格键保存高分辨率截图
saveFrame("math_art_####.png");
} else if (key == 'r' || key == 'R') {
// R键重置
background(0);
time = 0;
} else if (key == CODED) {
if (keyCode == UP) {
resolution = min(500, resolution + 10);
} else if (keyCode == DOWN) {
resolution = max(50, resolution - 10);
}
}
}4.3 其他官方学习资源这些曲线不仅美丽,还能帮助你理解数学与艺术的深刻联系。你可以通过调整参数、添加颜色渐变和动画效果来创造属于自己的数学艺术作品!