Skip to content
Permalink
Newer
Older
100644 249 lines (226 sloc) 6.94 KB
Oct 4, 2019
1
import {
2
random,
3
randomSelect,
4
translate,
5
rotate,
6
dist,
Oct 9, 2019
7
mult,
8
normalize,
9
equiTriangleHeight,
10
chordToRad
Oct 4, 2019
11
} from "../helpers/math";
Oct 5, 2019
12
import { grid2d } from "../helpers/grids";
Oct 13, 2019
14
const debug = false;
Oct 14, 2019
15
Aug 8, 2020
16
const DRAW_BG = true;
Oct 11, 2019
17
const PI = Math.PI;
18
const TWO_PI = 2 * PI;
19
const THIRD_TWO_PI = TWO_PI / 3;
Oct 14, 2019
20
const ROOT_2 = Math.sqrt(2);
21
22
export default class Drawing {
Aug 9, 2020
23
constructor({ styles, ctx, width, height, iters, snowflakeIters, strokeColor, strokeWidth=2, bgColor, canOverlap=false }) {
Oct 13, 2018
24
// add defaults
25
this.styles = Object.assign(
26
{
Aug 9, 2020
27
stroke: strokeColor || "hsla(340, 100%, 50%, 0.4)",
28
strokeWidth: strokeWidth
29
},
Oct 13, 2018
30
styles
31
);
32
Oct 14, 2019
33
this.cache = new Set();
Aug 9, 2020
34
35
Object.assign(this, {
36
ctx,
37
width,
38
height,
39
iters,
40
bgColor,
41
snowflakeIters,
42
canOverlap
43
})
Oct 13, 2018
44
}
45
46
draw() {
Oct 6, 2019
47
const { ctx, styles, width, height } = this;
Nov 24, 2018
48
ctx.draw(() => {
Aug 8, 2020
49
if (DRAW_BG) {
50
ctx.setStyles({
51
strokeWidth: 0,
Aug 9, 2020
52
fill: this.bgColor
Aug 8, 2020
53
});
Oct 14, 2019
54
ctx.rect(width, height, 0, 0);
55
}
Oct 9, 2019
56
ctx.setStyles(styles);
Oct 14, 2019
57
this.kochTessel2(
Oct 11, 2019
58
{ x: width / 2, y: height / 2 },
Aug 8, 2020
59
chordToRad(width / 3, THIRD_TWO_PI),
Aug 9, 2020
60
this.iters,
61
this.snowflakeIters,
Oct 11, 2019
62
);
Oct 11, 2019
63
});
WIP
Oct 13, 2019
64
}
65
Oct 14, 2019
66
kochTessel3(center, radius, depth = 1, iters = 1, i = 2) {
67
const childRad = radius / 2;
68
if (depth == 0) {
69
this.kochSnowflake({
70
center,
71
radius,
72
offsetRot: Math.PI / 6,
73
iters
74
});
75
} else {
76
for (let i = 0; i < 6; i++) {
Oct 14, 2019
77
const theta = Math.PI / 6 + (i / 6) * TWO_PI;
Oct 14, 2019
78
const spoke = radius;
79
const pos = translate(
80
{
81
x: spoke * Math.sin(theta),
82
y: spoke * Math.cos(theta)
83
},
84
center
85
);
86
this.kochTessel3(pos, childRad, depth - 1, iters, i);
87
}
88
}
89
}
90
Aug 8, 2020
91
kochTessel2(center, radius, depth = 1, iters = 1, offsetRot = Math.PI / 6) {
92
if (debug) {
Oct 13, 2019
93
this.ctx.ellipse(2, 2, center.x, center.y);
94
this.ctx.ellipse(radius * 2, radius * 2, center.x, center.y);
Oct 13, 2019
95
}
Oct 11, 2019
96
wip
Oct 11, 2019
97
if (depth == 0) {
98
if (debug) {
99
return;
100
}
Oct 11, 2019
101
this.kochSnowflake({
wip
Oct 11, 2019
102
center,
103
radius: equiTriangleHeight(radius),
Aug 9, 2020
104
offsetRot: offsetRot + Math.PI / 6,
wip
Oct 11, 2019
105
iters
Oct 11, 2019
106
});
wip
Oct 11, 2019
107
} else {
108
const childRad = radius / Math.sqrt(3);
wip
Oct 11, 2019
109
for (let i = 0; i < 6; i++) {
110
const theta = offsetRot + (i * Math.PI) / 3;
Aug 8, 2020
111
const spoke = radius;
Oct 11, 2019
112
const pos = translate(
113
{
wip
Oct 11, 2019
114
x: spoke * Math.sin(theta),
115
y: spoke * Math.cos(theta)
Oct 11, 2019
116
},
117
center
wip
Oct 11, 2019
118
);
Oct 13, 2019
119
Oct 14, 2019
120
this.kochTessel2(
121
pos,
122
childRad,
123
depth - 1,
124
iters,
125
offsetRot + Math.PI / 6
126
);
wip
Oct 11, 2019
127
}
128
}
129
}
130
Aug 8, 2020
131
getCacheId(center) {
Oct 14, 2019
132
return `${Math.round(center.x)},${Math.round(center.y)}`;
133
}
Aug 8, 2020
134
135
inCache(center, err = 1) {
136
// skip caching for overlap effect
Aug 9, 2020
137
if (this.canOverlap) return false;
Aug 8, 2020
138
139
const id = this.getCacheId(center);
Oct 14, 2019
140
const possNeibs = [
141
center,
Aug 8, 2020
142
translate(center, { x: 1, y: 0 }),
143
translate(center, { x: -1, y: 0 }),
144
translate(center, { x: 0, y: 1 }),
145
translate(center, { x: 0, y: -1 }),
146
translate(center, { x: 1, y: 1 }),
147
translate(center, { x: 1, y: -1 }),
148
translate(center, { x: -1, y: -1 }),
149
translate(center, { x: -1, y: 1 })
150
];
151
return (
152
possNeibs.filter(neib => this.cache.has(this.getCacheId(neib)))
153
.length > 0
154
);
Oct 14, 2019
155
}
156
157
kochSnowflake({ center, radius, offsetRot, iters, lineWidth = 1 }) {
Aug 8, 2020
158
if (this.inCache(center)) {
159
return;
Oct 14, 2019
160
}
Aug 8, 2020
161
this.cache.add(this.getCacheId(center));
162
Oct 11, 2019
163
const points = [];
164
const num = 3;
165
for (let i = 0; i < num; i++) {
Oct 14, 2019
166
const theta = offsetRot + (-i / num) * TWO_PI;
wip
Oct 11, 2019
167
points.push(
168
translate(
169
{
170
x: radius * Math.sin(theta),
171
y: radius * Math.cos(theta)
172
},
173
center
174
)
175
);
Oct 11, 2019
176
}
Oct 11, 2019
177
Oct 11, 2019
178
points.forEach((start, i) => {
179
const end = points[i + 1 > points.length - 1 ? 0 : i + 1];
Oct 11, 2019
180
Oct 13, 2019
181
this.kochCurve({ start, end, iters, lineWidth });
Oct 9, 2019
182
});
183
}
Oct 5, 2019
184
Oct 13, 2019
185
kochCurve({ start, end, iters = 3, lineWidth = 1 }) {
Oct 11, 2019
186
const len = {
187
x: (end.x - start.x) / 3,
188
y: (end.y - start.y) / 3
189
};
190
191
const { ctx } = this;
192
if (iters == 0) {
193
this.thiccLine(start.x, start.y, end.x, end.y, lineWidth);
194
} else {
Oct 13, 2019
195
this.kochCurve({
196
start,
197
end: translate(start, len),
198
iters: iters - 1,
wip
Oct 11, 2019
199
lineWidth
Oct 13, 2019
200
});
201
this.kochCurve({
202
start: translate(start, len),
Oct 14, 2019
203
end: translate(
204
rotate(len, -Math.PI / 3),
205
translate(start, len)
206
),
Oct 13, 2019
207
iters: iters - 1,
208
lineWidth
209
});
Oct 11, 2019
210
Oct 13, 2019
211
this.kochCurve({
Oct 14, 2019
212
start: translate(
213
rotate(len, -Math.PI / 3),
214
translate(start, len)
215
),
Oct 13, 2019
216
end: translate(start, mult(len, 2)),
217
iters: iters - 1,
wip
Oct 11, 2019
218
lineWidth
Oct 13, 2019
219
});
220
this.kochCurve({
221
start: translate(start, mult(len, 2)),
Oct 14, 2019
222
end,
Oct 13, 2019
223
iters: iters - 1,
wip
Oct 11, 2019
224
lineWidth
Oct 13, 2019
225
});
Oct 11, 2019
226
}
227
}
228
Oct 10, 2019
229
thiccLine(sx, sy, ex, ey, lineWidth = 1) {
Oct 11, 2019
230
lineWidth = Math.trunc(lineWidth);
Oct 10, 2019
231
const vec = normalize(rotate({ x: sx - ex, y: sy - ey }, Math.PI / 2));
232
for (let i = 0; i < lineWidth; i++) {
Oct 11, 2019
233
const offset = mult(vec, i - Math.floor(lineWidth / 2));
234
const st = translate({ x: sx, y: sy }, offset);
235
const en = translate({ x: ex, y: ey }, offset);
236
this.ctx.line(st.x, st.y, en.x, en.y);
Oct 9, 2019
237
}
238
}
Oct 6, 2019
239
Oct 10, 2019
240
thiccDot(x, y, size) {
241
for (let i = 0; i < size; i++) {
242
this.ctx.ellipse(i, i, x, y);
Oct 9, 2019
243
}
Oct 13, 2018
244
}
245
Oct 13, 2018
246
save() {
247
this.ctx.save();
248
}
249
}