10 PRINT 生成艺术,基于 p5.js 的实现

最后更新于 2023-04-29

本教程假设你已经有了基础的编程经验,了解并熟悉变量定义、函数定义、循环、逻辑判断等基础知识

当提到计算机生成艺术(generative art)的时候,“10 PRINT” 是一个非常经典的程序,它可以生成无限变化的有趣的图案。这个程序最初是在1980年代创建的,被广泛用于早期的计算机和游戏机上。如今,在使用各种现代的编程语言和工具来生成艺术作品的时候,“10 PRINT” 仍然是一个受欢迎的选择。

我们先看看 10 PRINT 有什么样的魅力,能够经久不衰。下面是我们最终实现的效果。

## 开始第一步

万事开头难,但是对于 p5.js 来说,开头是最简单不过了。我们先从 p5.js 最基础的骨架代码开始,在画板上绘制一个白色的背景,并且绘制一条直线。

我们先来认识一下这几行简单的代码:

1
2
3
4
5
6
7
8
9
function setup() {
  createCanvas(300, 300)
}

function draw() {
  background(255)

  line(0, 0,  100, 100)
}
  1. setupdraw 都属于 p5.js 的生命周期函数,其中 setup 只会执行一次,而 draw 则相当于渲染循环,会一遍又一遍地运行。
  2. createCanvas 也是一个 p5.js 的内置函数,即创建一个给定尺寸的画板。
  3. background 是一个设置背景颜色的内置函数,当我们调用了 background 后,画板之前的内容就会被清空掉。
  4. line 是一个 p5.js 的内置函数,用来画直线的,line 接收四个参数,第一个第二个参数分别是起点的横坐标和纵坐标,第三个第四个参数分别是终点的横坐标和纵坐标。所以 line(0, 0, 100, 100) 的意思是画一条直线,起点坐标是 (0, 0) ,终点坐标是 (100, 100)

接下来,我们来看看如何基于这个简单的程序来实现 10 PRINT 的效果了。

## 10 PRINT 分析

10 PRINT 画作

仔细观察上面这幅图,我们可以看出有几个关键的点:

  1. 整幅图是由直线线条组成
  2. 直线线条的方向有两个,分别是向左、向右倾斜 45 度

加上红色的辅助线,我们可以更清晰地看到这一规律。

10 PRINT pattern

重新审视这个名为 10 PRINT 的绘画作品,你会发现它的表现更加清晰易懂了。实现这样一个有趣的效果只需要两步即可:

首先,将绘画板分割为一个个正方形的网格。 其次,通过在每个小正方形中绘制线条来表现图案,具体有两种方式:

1. 从左上角绘制直线到右下角
2. 从右上角绘制直线到左下角

这个其实是 Grid Tile Pattern 网格法,而 10 PRINT 是网格法里最基础的一个表现形式。

## 编码实现

### 1. 对画板空间进行网格分割

根据我们上面的分析,首先需要通过编码实现对画板空间的分割,将其划分为一个一个的小正方形格子。

#### 画第一排的正方形

p5.js 正方形

从上图中我们可以看出,要想画出一排正方形,我们从左边依次往右边画即可。

我们定义一个变量 space, 用它来表示正方形的边长,默认是 20。定义一个 squarePoint 的变量,用它来表示正方形左上角的坐标。要实现从左往右依次画正方形,我们用一个 for 循环就可以实现了,让正方形的左上角的 x 坐标依次递增即可。

其中 square 是 p5.js 内置的函数,可以绘制一个正方形。

#### 画多排的正方形

有了绘制第一排正方形的经验,我们很容易绘制出多排正方形。我们只需要再外面再多加一层循环,将正方形的的左上角坐标从第一排的 y = 0 移动到最后一排 y = height 就可以了。

好了,最艰难的一步已经完成了,通过几行代码,我们实现了对画板进行网格的划分。

### 2. 在网格里画斜线

接下来,我们需要在已经划分好的网格中绘制向左倾斜和向右倾斜两条直线。那如何来画出这两条斜线呢,我们来看看下面这个示意图。

p5.js 斜线
  1. 画右斜线 从图中我们可以看出,右斜线的起点是正方形的左上角坐标,终点是正方形的右下角坐标。起点坐标我们已经清楚,我们需要计算出终点坐标。终点的 x 坐标可以用左上角坐标的 x 坐标 + 正方形边长来表示,终点的 y 坐标可以用 左上角坐标的 y 坐标 + 正方形边长 来表示。

    1
    2
    3
    4
    5
    6
    7
    8
    
    // 起点坐标
    const startPoint = squarePoint
    
    // 终点坐标
    const endPoint = {
      x: squarePoint.x + space,
      y: squarePoint.y + space
    }
  2. 画左斜线
    同理,左斜线的起点是正方形的右上角坐标,终点是正方形的左下角坐标,起点和终点的坐标计算代码如下。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
    // 起点坐标
    const startPoint = {
      x: squarePoint.x + space,
      y: squarePoint.y
    }
    
    // 终点坐标
    const endPoint = {
      x: squarePoint.x,
      y: squarePoint.y + space
    }

明确了左右斜线的画法,我们就来画几条斜线看看具体的效果吧。

有了网格,有了向左向右两条斜线,我们还缺少重要的一点,那就是确定什么时候画左斜线,什么时候画右斜线。 最简单的,我们可以采用随机的策略。我们使用 p5.js 的 random 函数生成一个随机数,如果这个随机数大于 0.5,那么我们就画左斜线,否则就画右斜线。

1
2
3
4
5
6
const directionVal = random()
if (directionVal > 0.5) {
  // 画左斜线
} else {
  // 画左斜线
}

解决了这个问题,我们就可以完成整个画作了。

### 组合在一起,最终代码效果

## 更进一步

看完了本教程,相信你已经可以自己独立做出一个 10 Print 艺术画作了,你还可以探索一下其他的变化,让你的画作如众不同。下面是一些可能的点:

  1. 修改线条的宽度,看看会发生什么。你可以使用 strokeWeight 方法来实现这一点。
  2. 对线条进行着色。现在的线条是黑色的,你可以用 stroke 方法来修改线条的颜色。
  3. 改变线条的长度。现在的线条都是统一长度的,你可以试着修改一下,动态改变线条的长度。提示,修改线条的起点和终点位置就可以实现,你可以在原来的位置上加一个随机偏移量。
  4. 在每个网格里动态添加一些其他的图形,或者多画几个线条试试。

快来动手试试吧。