标题:
介绍一个使用门槛很低的绘图平台 p5 js。
[打印本页]
作者:
wuyudi
时间:
2023-3-31 00:21
标题:
介绍一个使用门槛很低的绘图平台 p5 js。
此帖是给高中时的我一个交代。当时看到了向老师所著分形 50 例,心有余力不足。经历了几年后我力足了,故开此帖。
本帖代码都可以贴到
https://editor.p5js.org/
左边的编辑器里,点左上角播放。右边就能出图。
相关入门在
https://p5js.org/zh-Hans/get-started/
,不过直接对着下面的模板改也可以。
作者:
wuyudi
时间:
2023-3-31 00:24
上图,这图渲染仅 2s. 画板则需要很久。
下载
(56.3 KB)
2023-3-31 00:24
class Complex {
// By chatGPT
constructor(real, imaginary) {
this.real = real;
this.imaginary = imaginary || 0;
}
add(other) {
return new Complex(
this.real + other.real,
this.imaginary + other.imaginary
);
}
sub(other) {
return new Complex(
this.real - other.real,
this.imaginary - other.imaginary
);
}
mul(other) {
const real = this.real * other.real - this.imaginary * other.imaginary;
const imaginary = this.real * other.imaginary + this.imaginary * other.real;
return new Complex(real, imaginary);
}
div(other) {
const denominator = pow(other.real, 2) + pow(other.imaginary, 2);
const real =
(this.real * other.real + this.imaginary * other.imaginary) / denominator;
const imaginary =
(this.imaginary * other.real - this.real * other.imaginary) / denominator;
return new Complex(real, imaginary);
}
eq(other) {
return this.imaginary === other.imaginary && this.real === other.real;
}
norm() {
return sqrt(pow(this.real, 2) + pow(this.imaginary, 2));
}
}
function cpx(x, y) {
return new Complex(x, y);
}
let img;
const q0 = 1.45; // ,诱捕半径为 q0
function setup() {
createCanvas(windowHeight, windowHeight);
img = createImage(windowHeight, windowHeight);
img.loadPixels();
noLoop();
}
/**
* @param {p5.Vector} z
* s
*/
function getLastColor(z, s) {
// <-- c -> z
let c = cpx(0.5, -0.5); // <-- z -> c
for (let index = 0; index < 8; index++) {
const newz = c.add(z.mul(z));
if (newz.eq(z) || sq(newz.real) + sq(newz.imaginary) > 4) {
return s;
}
z = newz;
s = min(s, 3 * sqrt(abs(z.mul(z).real) + abs(z.mul(z).imaginary)));
}
return s;
}
function plot() {
for (let i = 0; i < img.width; i++) {
for (let j = 0; j < img.height; j++) {
const px = map(i, 0, img.width, -2, 2);
const py = map(j, 0, img.height, -2, 2);
const c = getLastColor(cpx(px, py), q0); // 令色参数 s1= q0
if (c < q0) {
img.set(i, j, color(map(c, 0, 1, 0, 255)));
}
}
}
}
function draw() {
background(0);
plot();
img.updatePixels();
image(img, 0, 0);
}
复制代码
图片附件:
下载.png
(2023-3-31 00:24, 56.3 KB) / 下载次数 656
http://inrm3d.cn/attachment.php?aid=29321&k=086a945c88dd753a916d37955507145a&t=1715996525&sid=HI7R1q
作者:
wuyudi
时间:
2023-3-31 00:29
基本的框架就在楼上了,为了操作方便,定义了一个 Complex 的 class, 但是这样每次都要 new Complex, 所以定义辅助函数 cpx(x,y)。
setup 函数用于初始化,只会运行一次。draw 会不停运行。但是设定 noLoop 后仅运行一次,适合分形渲染这类耗时巨大的工作。
作者:
wuyudi
时间:
2023-3-31 00:33
几个要点:
if (newz.eq(z) || sq(newz.real) + sq(newz.imaginary) > 4) {
复制代码
这一行就是出界 / 不动点的判断。可以对运算剪枝。
s = min(s, 3 * sqrt(abs(z.mul(z).real) + abs(z.mul(z).imaginary)));
复制代码
这一行是更新 s, 即陷阱参数。
for (let i = 0; i < img.width; i++) {
for (let j = 0; j < img.height; j++) {
const px = map(i, 0, img.width, -2, 2);
const py = map(j, 0, img.height, -2, 2);
复制代码
这些是对画布的每个点,计算 -2,2 的对应坐标,再计算对应着色参数。
作者:
wuyudi
时间:
2023-3-31 00:40
因为本帖主要是展示简单例子,所以就不设置复杂的上色函数了。
下载
(140.65 KB)
2023-3-31 00:40
class Complex {
// By chatGPT
constructor(real, imaginary) {
this.real = real;
this.imaginary = imaginary || 0;
}
add(other) {
return new Complex(
this.real + other.real,
this.imaginary + other.imaginary
);
}
sub(other) {
return new Complex(
this.real - other.real,
this.imaginary - other.imaginary
);
}
mul(other) {
const real = this.real * other.real - this.imaginary * other.imaginary;
const imaginary = this.real * other.imaginary + this.imaginary * other.real;
return new Complex(real, imaginary);
}
div(other) {
const denominator = pow(other.real, 2) + pow(other.imaginary, 2);
const real =
(this.real * other.real + this.imaginary * other.imaginary) / denominator;
const imaginary =
(this.imaginary * other.real - this.real * other.imaginary) / denominator;
return new Complex(real, imaginary);
}
eq(other) {
return this.imaginary === other.imaginary && this.real === other.real;
}
norm() {
return sqrt(pow(this.real, 2) + pow(this.imaginary, 2));
}
}
function cpx(x, y) {
return new Complex(x, y);
}
let img;
const q0 = 1; // ,诱捕半径为 q0
function setup() {
createCanvas(windowHeight, windowHeight);
img = createImage(windowHeight, windowHeight);
img.loadPixels();
noLoop();
}
/**
* @param {p5.Vector} z
* s
* @param {Vector} first 点#为诱捕点(或点陷阱中心),
*/
function getLastColor(c, s, first) {
// <-- c -> z
let z = cpx(0, 0);
for (let index = 0; index < 30; index++) {
const newz = c.add(z.mul(z));
if (newz.eq(z) || newz.norm() > 2) {
return s;
}
z = newz;
s = min(s, abs(1 - z.norm()));
}
return s;
}
function plot() {
for (let i = 0; i < img.width; i++) {
for (let j = 0; j < img.height; j++) {
const px = map(i, 0, img.width, -2, 2);
const py = map(j, 0, img.height, -2, 2);
const c = getLastColor(cpx(px, py), q0, cpx(0));
if(c < q0){
img.set(i, j, color(255 - map(c, 0, 1, 0, 255)));
}
}
}
}
function draw() {
background(0);
plot();
img.updatePixels();
image(img, 0, 0);
}
复制代码
图片附件:
下载 (1).png
(2023-3-31 00:40, 140.65 KB) / 下载次数 621
http://inrm3d.cn/attachment.php?aid=29322&k=d1e7be9583bb451cc6a9e2702132e73c&t=1715996525&sid=HI7R1q
欢迎光临 inRm3D: 画板论坛 (http://inrm3d.cn/)
Powered by Discuz! 7.0.0