更多算法(语言为JavaScript) 持续更新...
戳我去GitHub看更多算法问题>>>>目录
戳我去GitHub看详细代码>>>>分形基础绘制
Vicsek fractal
基本正方形在3×3网格中分解为9个较小的正方形,绘制角落处的四个正方形和中间正方形。对于剩余的五个子方格中的每一个,递归地重复该过程。Vicsek分形是在此程序的极限下获得的集合。
// 绘制图形
draw(ctx, depth = this.depth, x = 0, y = 0, w = this.width, h = this.width) {
ctx.fillStyle = '#ccc';
if (depth <= 0 && depth != undefined || w <= 1 || h <= 1) {
// 绘制
ctx.fillRect(x, y, w, h)
return
}
this.depth = depth
// 要绘制的格子容器中的小格子的宽高 将格子容器划分为3 * 3 的矩阵 根据位置分别绘制小格子
let w_3 = w / 3
let h_3 = h / 3
// 递归绘制每个格子的5个方向
this.draw(ctx, depth - 1,x, y, w_3, h_3)
this.draw(ctx, depth - 1, x + 2 * w_3, y, w_3, h_3)
this.draw(ctx, depth - 1, x + 2 * w_3, y + 2 * w_3, w_3, h_3)
this.draw(ctx, depth - 1, x + w_3, y + w_3, w_3, h_3)
this.draw(ctx, depth - 1, x, y + 2 * w_3, w_3, h_3)
}
分形结果如下图:
Sierpinski carpet 分形
Sierpinski carpet 始于一个正方形"方形几何"。该正方形在3×3网格中被切割成9个一致的子方形,只绘制中心子方,然后,无限制地将相同的过程递归地应用于剩余的8个子方格。
// 绘制图形
draw(ctx, depth = this.depth, x = 0, y = 0, w = this.width, h = this.width) {
ctx.fillStyle = '#fff';
// 要绘制的格子容器中的小格子的宽高 将格子容器划分为3 * 3 的矩阵 根据位置分别绘制小格子
let w_3 = w / 3
let h_3 = h / 3
if (depth <= 0 && depth != undefined || w <= 1 || h <= 1) return
this.depth = depth
// 递归绘制9个格子
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
if (i === 1 && j === 1) {
// 中心的格子 直接绘制
ctx.fillRect( x + w_3, y + w_3, w_3, h_3)
} else {
// 其他八个角的格子 递归
this.draw(ctx, depth - 1, x + i * w_3, y + j * w_3, w_3, h_3)
}
}
}
}
分形结果如下图:
sierpinski triangle 分形
- 从等边三角形开始。
- 将其细分为四个较小的全等边三角形并移除中心三角形。
-
重复步骤2,永远保留每个剩余的较小三角形。
// 绘制图形
draw(ctx, depth = this.depth, Ax = 0, Ay = this.side, side = this.side, all = this.depth) {
ctx.fillStyle = '#ccc';
// 计算三角形其余两点坐标
let Bx = Ax + side
let By = Ay
let Cx = Ax + side / 2
let Cy = Ay - Math.sin( 2 * Math.PI / 360 * 60) * side
if (depth <= 0 && depth != undefined || side <= 1) {
// 传入三点坐标
this.triangle(ctx, Ax, Ay,Bx, By, Cx, Cy)
return
}
// 递归 在左下角坐标为 (Ax, Ay)且边长为 side 的正方形中绘制三个等边三角形
this.draw(ctx, depth - 1, Ax, Ay, side / 2, all)
this.draw(ctx, depth - 1, (Ax + Bx) / 2, (Ay + By) / 2, side / 2, all)
this.draw(ctx, depth - 1, (Ax + Cx) / 2, (Ay + Cy) / 2, side / 2, all)
}
// 绘制三角形 传入三点的坐标
triangle(ctx, x1, y1, x2, y2, x3, y3) {
ctx.beginPath()
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.lineTo(x3, y3);
ctx.closePath();
ctx.fill();
}
分形结果如下图:
koch snowflake 分形
概念:Koch雪花可以通过等边三角形开始构建,然后递归地改变每个线段,如下所示:
- 将线段划分为三个相等长度的段
- 绘制一个等边三角形,其中以第1步的中间部分作为基准,并指向外部。
- 从第2步中删除作为三角形底边的线段。
在该过程的一次迭代之后,得到的形状是六芒星的轮廓。
// 绘制图形
draw(ctx, depth = this.depth, x1 = 0, y1 = this.width / 2, side = this.width, deg = 0) {
ctx.strokeStyle = '#fff';
// 要绘制的格子容器中的小格子的宽高 将格子容器划分为3 * 3 的矩阵 根据位置分别绘制小格子
let w_3 = side / 3
if (depth < 0 && depth != undefined || w_3 <= 1) {
let x2 = x1 + Math.cos(2 * Math.PI / 360 * deg) * side
let y2 = y1 - Math.sin(2 * Math.PI / 360 * deg) * side
this.line(ctx, x1, y1, x2, y2)
return
}
let x2 = x1 + Math.cos(2 * Math.PI / 360 * deg) * w_3
let y2 = y1 - Math.sin(2 * Math.PI / 360 * deg) * w_3
this.draw(ctx, depth - 1, x1, y1, w_3, deg)
let x3 = x2 + w_3 * Math.cos(2 * Math.PI / 360 * (deg + 60))
let y3 = y2 - w_3 * Math.sin(2 * Math.PI / 360 * (deg + 60))
this.draw(ctx, depth - 1, x2, y2, w_3, deg + 60)
let x4 = x3 + w_3 * Math.cos(2 * Math.PI / 360 * (deg - 60))
let y4 = y3 - w_3 * Math.sin(2 * Math.PI / 360 * (deg - 60))
this.draw(ctx, depth - 1, x3, y3, w_3, deg - 60)
this.draw(ctx, depth - 1, x4, y4, w_3, deg)
}
// 绘制线条
line(ctx, x1, y1, x2, y2) {
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.closePath();
ctx.stroke();
}
分形结果如下图:
分形绘制树
// 绘制图形
draw(ctx, depth = this.depth, x1 = this.width / 2, y1 = this.width, side = this.width, deg = 0) {
ctx.strokeStyle = '#fff';
let w_2 = side / 2
if (depth < 0 && depth != undefined || w_2 <= 1) {
let x2 = x1 - Math.sin(2 * Math.PI / 360 * deg) * side
let y2 = y1 - Math.cos(2 * Math.PI / 360 * deg) * side
this.line(ctx, x1, y1, x2, y2)
return
}
// 获取中点坐标
let x2 = x1 - Math.sin(2 * Math.PI / 360 * deg) * w_2
let y2 = y1 - Math.cos(2 * Math.PI / 360 * deg) * w_2
// 下边的树杈
this.line(ctx, x1, y1, x2, y2)
// 左边的树杈
this.draw(ctx, depth - 1, x2, y2, w_2, deg + this.splitDeg / 2)
// 右边的树杈
this.draw(ctx, depth - 1, x2, y2, w_2, deg - this.splitDeg / 2)
}
// 绘制线条
line(ctx, x1, y1, x2, y2) {
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.closePath();
ctx.stroke();
}
分形结果如下图: