返回列表 回复 发帖

介绍一个使用门槛很低的绘图平台 p5 js。

此帖是给高中时的我一个交代。当时看到了向老师所著分形 50 例,心有余力不足。经历了几年后我力足了,故开此帖。

本帖代码都可以贴到 https://editor.p5js.org/ 左边的编辑器里,点左上角播放。右边就能出图。
相关入门在 https://p5js.org/zh-Hans/get-started/ ,不过直接对着下面的模板改也可以。
上图,这图渲染仅 2s. 画板则需要很久。
下载.png
  1. class Complex {
  2.   // By chatGPT
  3.   constructor(real, imaginary) {
  4.     this.real = real;
  5.     this.imaginary = imaginary || 0;
  6.   }

  7.   add(other) {
  8.     return new Complex(
  9.       this.real + other.real,
  10.       this.imaginary + other.imaginary
  11.     );
  12.   }

  13.   sub(other) {
  14.     return new Complex(
  15.       this.real - other.real,
  16.       this.imaginary - other.imaginary
  17.     );
  18.   }

  19.   mul(other) {
  20.     const real = this.real * other.real - this.imaginary * other.imaginary;
  21.     const imaginary = this.real * other.imaginary + this.imaginary * other.real;
  22.     return new Complex(real, imaginary);
  23.   }

  24.   div(other) {
  25.     const denominator = pow(other.real, 2) + pow(other.imaginary, 2);
  26.     const real =
  27.       (this.real * other.real + this.imaginary * other.imaginary) / denominator;
  28.     const imaginary =
  29.       (this.imaginary * other.real - this.real * other.imaginary) / denominator;
  30.     return new Complex(real, imaginary);
  31.   }
  32.   eq(other) {
  33.     return this.imaginary === other.imaginary && this.real === other.real;
  34.   }
  35.   norm() {
  36.     return sqrt(pow(this.real, 2) + pow(this.imaginary, 2));
  37.   }
  38. }
  39. function cpx(x, y) {
  40.   return new Complex(x, y);
  41. }
  42. let img;
  43. const q0 = 1.45; // ,诱捕半径为 q0
  44. function setup() {
  45.   createCanvas(windowHeight, windowHeight);
  46.   img = createImage(windowHeight, windowHeight);
  47.   img.loadPixels();
  48.   noLoop();
  49. }

  50. /**
  51. * @param {p5.Vector} z
  52. * s
  53. */
  54. function getLastColor(z, s) {
  55.   // <-- c -> z
  56.   let c = cpx(0.5, -0.5); // <-- z -> c
  57.   for (let index = 0; index < 8; index++) {
  58.     const newz = c.add(z.mul(z));
  59.     if (newz.eq(z) || sq(newz.real) + sq(newz.imaginary) > 4) {
  60.       return s;
  61.     }
  62.     z = newz;
  63.     s = min(s, 3 * sqrt(abs(z.mul(z).real) + abs(z.mul(z).imaginary)));
  64.   }
  65.   return s;
  66. }

  67. function plot() {
  68.   for (let i = 0; i < img.width; i++) {
  69.     for (let j = 0; j < img.height; j++) {
  70.       const px = map(i, 0, img.width, -2, 2);
  71.       const py = map(j, 0, img.height, -2, 2);
  72.       const c = getLastColor(cpx(px, py), q0); // 令色参数 s1= q0
  73.       if (c < q0) {
  74.         img.set(i, j, color(map(c, 0, 1, 0, 255)));
  75.       }
  76.     }
  77.   }
  78. }

  79. function draw() {
  80.   background(0);
  81.   plot();
  82.   img.updatePixels();
  83.   image(img, 0, 0);
  84. }
复制代码
基本的框架就在楼上了,为了操作方便,定义了一个 Complex 的 class, 但是这样每次都要 new Complex, 所以定义辅助函数 cpx(x,y)。
setup 函数用于初始化,只会运行一次。draw 会不停运行。但是设定 noLoop 后仅运行一次,适合分形渲染这类耗时巨大的工作。
几个要点:
  1. if (newz.eq(z) || sq(newz.real) + sq(newz.imaginary) > 4) {
复制代码
这一行就是出界 / 不动点的判断。可以对运算剪枝。
  1. s = min(s, 3 * sqrt(abs(z.mul(z).real) + abs(z.mul(z).imaginary)));
复制代码
这一行是更新 s, 即陷阱参数。
  1.   for (let i = 0; i < img.width; i++) {
  2.     for (let j = 0; j < img.height; j++) {
  3.       const px = map(i, 0, img.width, -2, 2);
  4.       const py = map(j, 0, img.height, -2, 2);
复制代码
这些是对画布的每个点,计算 -2,2 的对应坐标,再计算对应着色参数。
因为本帖主要是展示简单例子,所以就不设置复杂的上色函数了。

下载 (1).png
  1. class Complex {
  2.   // By chatGPT
  3.   constructor(real, imaginary) {
  4.     this.real = real;
  5.     this.imaginary = imaginary || 0;
  6.   }

  7.   add(other) {
  8.     return new Complex(
  9.       this.real + other.real,
  10.       this.imaginary + other.imaginary
  11.     );
  12.   }

  13.   sub(other) {
  14.     return new Complex(
  15.       this.real - other.real,
  16.       this.imaginary - other.imaginary
  17.     );
  18.   }

  19.   mul(other) {
  20.     const real = this.real * other.real - this.imaginary * other.imaginary;
  21.     const imaginary = this.real * other.imaginary + this.imaginary * other.real;
  22.     return new Complex(real, imaginary);
  23.   }

  24.   div(other) {
  25.     const denominator = pow(other.real, 2) + pow(other.imaginary, 2);
  26.     const real =
  27.       (this.real * other.real + this.imaginary * other.imaginary) / denominator;
  28.     const imaginary =
  29.       (this.imaginary * other.real - this.real * other.imaginary) / denominator;
  30.     return new Complex(real, imaginary);
  31.   }
  32.   eq(other) {
  33.     return this.imaginary === other.imaginary && this.real === other.real;
  34.   }
  35.   norm() {
  36.     return sqrt(pow(this.real, 2) + pow(this.imaginary, 2));
  37.   }
  38. }
  39. function cpx(x, y) {
  40.   return new Complex(x, y);
  41. }
  42. let img;
  43. const q0 = 1; // ,诱捕半径为 q0
  44. function setup() {
  45.   createCanvas(windowHeight, windowHeight);
  46.   img = createImage(windowHeight, windowHeight);
  47.   img.loadPixels();
  48.   noLoop();
  49. }

  50. /**
  51. * @param {p5.Vector} z
  52. * s
  53. * @param {Vector} first 点#为诱捕点(或点陷阱中心),
  54. */
  55. function getLastColor(c, s, first) {
  56.   // <-- c -> z
  57.   let z = cpx(0, 0);
  58.   for (let index = 0; index < 30; index++) {
  59.     const newz = c.add(z.mul(z));
  60.     if (newz.eq(z) || newz.norm() > 2) {
  61.       return s;
  62.     }
  63.     z = newz;
  64.     s = min(s, abs(1 - z.norm()));
  65.   }
  66.   return s;
  67. }

  68. function plot() {
  69.   for (let i = 0; i < img.width; i++) {
  70.     for (let j = 0; j < img.height; j++) {
  71.       const px = map(i, 0, img.width, -2, 2);
  72.       const py = map(j, 0, img.height, -2, 2);
  73.       const c = getLastColor(cpx(px, py), q0, cpx(0));
  74.       if(c < q0){
  75.       img.set(i, j, color(255 - map(c, 0, 1, 0, 255)));
  76.       }
  77.     }
  78.   }
  79. }

  80. function draw() {
  81.   background(0);
  82.   plot();
  83.   img.updatePixels();
  84.   image(img, 0, 0);
  85. }
复制代码
返回列表