simulationjsv2 0.2.3 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/graphics.js CHANGED
@@ -1,30 +1,37 @@
1
- import { vec3, quat, mat4, vec2, vec4 } from 'wgpu-matrix';
2
- import { Vertex, VertexCache, cloneBuf, color, colorFromVector4, lossyTriangulate, vec3ToPixelRatio, vector3FromVector2, vector2, vector3, vertex, vertexBuffer2d, vertexBuffer3d, Color, transitionValues, logger, vector2FromVector3, interpolateColors } from './utils.js';
1
+ import { vec3, mat4, vec2, vec4 } from 'wgpu-matrix';
2
+ import { Vertex, VertexCache, cloneBuf, color, colorFromVector4, vector3ToPixelRatio, vector2, vector3, vertex, Color, transitionValues, logger, vector2FromVector3, matrix4, rotateMat4, vector3FromVector2 } from './utils.js';
3
+ import { CircleGeometry, CubeGeometry, Line2dGeometry, Line3dGeometry, PlaneGeometry, PolygonGeometry, SplineGeometry, SquareGeometry } from './geometry.js';
3
4
  export class SimulationElement {
4
- pos;
5
5
  color;
6
- camera;
6
+ wireframe;
7
7
  vertexCache;
8
- constructor(pos, color = new Color()) {
9
- this.pos = pos;
10
- const temp = vector3(...this.pos);
11
- vec3ToPixelRatio(temp);
12
- for (let i = 0; i < this.pos.length; i++) {
13
- this.pos[i] = temp[i];
14
- }
8
+ rotation;
9
+ is3d;
10
+ /**
11
+ * @param pos - Expected to be adjusted to devicePixelRatio before reaching constructor
12
+ */
13
+ constructor(color = new Color(), rotation, is3d = true) {
15
14
  this.color = color;
16
15
  this.vertexCache = new VertexCache();
17
- this.camera = null;
16
+ this.is3d = is3d;
17
+ this.wireframe = false;
18
+ this.rotation = rotation;
18
19
  }
19
- setPos(pos) {
20
- this.pos = pos;
20
+ getGeometryType() {
21
+ return this.geometry.getType();
22
+ }
23
+ setWireframe(wireframe) {
24
+ this.wireframe = wireframe;
25
+ }
26
+ isWireframe() {
27
+ return this.wireframe;
28
+ }
29
+ getColor() {
30
+ return this.color;
21
31
  }
22
32
  getPos() {
23
33
  return this.pos;
24
34
  }
25
- setCamera(camera) {
26
- this.camera = camera;
27
- }
28
35
  fill(newColor, t = 0, f) {
29
36
  const diff = newColor.diff(this.color);
30
37
  const finalColor = newColor.clone();
@@ -39,16 +46,79 @@ export class SimulationElement {
39
46
  this.vertexCache.updated();
40
47
  }, t, f);
41
48
  }
42
- getColor() {
43
- return this.color;
49
+ defaultUpdateMatrix(camera) {
50
+ const matrix = matrix4();
51
+ if (typeof this.rotation === 'number') {
52
+ const pos = vector3FromVector2(this.pos);
53
+ pos[1] = camera.getScreenSize()[1] - pos[1];
54
+ mat4.translate(matrix, pos, matrix);
55
+ mat4.rotateZ(matrix, this.rotation, matrix);
56
+ }
57
+ else {
58
+ mat4.translate(matrix, this.pos, matrix);
59
+ rotateMat4(matrix, this.rotation);
60
+ }
61
+ this.geometry.updateMatrix(matrix);
62
+ }
63
+ getBuffer(camera) {
64
+ if (this.vertexCache.shouldUpdate() || camera.hasUpdated()) {
65
+ this.updateMatrix(camera);
66
+ this.geometry.recompute();
67
+ let resBuffer = [];
68
+ if (this.isWireframe()) {
69
+ resBuffer = this.geometry.getWireframeBuffer(this.color);
70
+ }
71
+ else {
72
+ resBuffer = this.geometry.getTriangleBuffer(this.color);
73
+ }
74
+ this.vertexCache.setCache(resBuffer);
75
+ return resBuffer;
76
+ }
77
+ return this.vertexCache.getCache();
78
+ }
79
+ }
80
+ export class SimulationElement3d extends SimulationElement {
81
+ pos;
82
+ rotation;
83
+ constructor(pos, rotation = vector3(), color) {
84
+ super(color, rotation);
85
+ this.pos = pos;
86
+ vector3ToPixelRatio(this.pos);
87
+ this.rotation = rotation;
88
+ }
89
+ rotate(amount, t = 0, f) {
90
+ const finalRotation = cloneBuf(this.rotation);
91
+ vec3.add(finalRotation, amount, finalRotation);
92
+ return transitionValues((p) => {
93
+ this.rotation[0] += amount[0] * p;
94
+ this.rotation[1] += amount[1] * p;
95
+ this.rotation[2] += amount[2] * p;
96
+ this.vertexCache.updated();
97
+ }, () => {
98
+ this.rotation = finalRotation;
99
+ this.vertexCache.updated();
100
+ }, t, f);
101
+ }
102
+ rotateTo(rot, t = 0, f) {
103
+ const diff = vector3();
104
+ vec3.sub(rot, this.rotation, diff);
105
+ return transitionValues((p) => {
106
+ this.rotation[0] += diff[0] * p;
107
+ this.rotation[1] += diff[1] * p;
108
+ this.rotation[2] += diff[2] * p;
109
+ this.vertexCache.updated();
110
+ }, () => {
111
+ this.rotation = rot;
112
+ this.vertexCache.updated();
113
+ }, t, f);
44
114
  }
45
115
  move(amount, t = 0, f) {
46
- const finalPos = amount.length === 3 ? vector3() : vector2();
47
- vec3.add(finalPos, this.pos, amount);
116
+ const finalPos = cloneBuf(this.pos);
117
+ vec3.add(finalPos, amount, finalPos);
48
118
  return transitionValues((p) => {
49
- for (let i = 0; i < this.pos.length; i++) {
50
- this.pos[i] += amount[i] * p;
51
- }
119
+ this.pos[0] += amount[0] * p;
120
+ this.pos[1] += amount[1] * p;
121
+ this.pos[2] += amount[2] * p;
52
122
  this.vertexCache.updated();
53
123
  }, () => {
54
124
  this.pos = finalPos;
@@ -56,110 +126,157 @@ export class SimulationElement {
56
126
  }, t, f);
57
127
  }
58
128
  moveTo(pos, t = 0, f) {
59
- const diff = pos.length === 3 ? vector3() : vector2();
60
- vec3.sub(diff, pos, this.pos);
129
+ const diff = vector3();
130
+ vec3.sub(pos, this.pos, diff);
61
131
  return transitionValues((p) => {
62
- for (let i = 0; i < this.pos.length; i++) {
63
- this.pos[i] += diff[i] * p;
64
- }
132
+ this.pos[0] += diff[0] * p;
133
+ this.pos[1] += diff[1] * p;
134
+ this.pos[2] += diff[2] * p;
65
135
  this.vertexCache.updated();
66
136
  }, () => {
67
- for (let i = 0; i < this.pos.length; i++) {
68
- this.pos[i] = pos[i];
69
- }
137
+ this.pos = pos;
70
138
  this.vertexCache.updated();
71
139
  }, t, f);
72
140
  }
73
141
  }
74
- export class Plane extends SimulationElement {
75
- points;
142
+ export class SimulationElement2d extends SimulationElement {
143
+ pos;
76
144
  rotation;
77
- constructor(pos, points, color, rotation = vector3()) {
78
- super(pos, color);
79
- this.points = points;
145
+ constructor(pos, rotation = 0, color) {
146
+ super(color, rotation, false);
147
+ this.pos = pos;
80
148
  this.rotation = rotation;
81
149
  }
82
- setPoints(newPoints) {
83
- this.points = newPoints;
150
+ rotate(rotation, t = 0, f) {
151
+ const finalRotation = this.rotation + rotation;
152
+ return transitionValues((p) => {
153
+ this.rotation += rotation * p;
154
+ this.vertexCache.updated();
155
+ }, () => {
156
+ this.rotation = finalRotation;
157
+ this.vertexCache.updated();
158
+ }, t, f);
84
159
  }
85
- rotate(amount, t = 0, f) {
86
- const initial = vec3.clone(this.rotation);
160
+ rotateTo(newRotation, t = 0, f) {
161
+ const diff = newRotation - this.rotation;
87
162
  return transitionValues((p) => {
88
- const step = vector3();
89
- vec3.scale(amount, p, step);
90
- vec3.add(this.rotation, step, this.rotation);
163
+ this.rotation += diff * p;
91
164
  this.vertexCache.updated();
92
165
  }, () => {
93
- vec3.add(initial, amount, initial);
94
- this.rotation = initial;
166
+ this.rotation = newRotation;
95
167
  this.vertexCache.updated();
96
168
  }, t, f);
97
169
  }
98
- rotateTo(angle, t = 0, f) {
99
- const diff = vector3();
100
- vec3.sub(angle, this.rotation, diff);
170
+ move(amount, t = 0, f) {
171
+ const finalPos = vector2();
172
+ vec3.add(amount, this.pos, finalPos);
101
173
  return transitionValues((p) => {
102
- const toRotate = vector3();
103
- vec3.scale(diff, p, toRotate);
104
- vec3.add(this.rotation, toRotate, this.rotation);
174
+ this.pos[0] += amount[0] * p;
175
+ this.pos[1] += amount[1] * p;
105
176
  this.vertexCache.updated();
106
177
  }, () => {
107
- this.rotation = angle;
178
+ this.pos = finalPos;
108
179
  this.vertexCache.updated();
109
180
  }, t, f);
110
181
  }
111
- getBuffer(_, force) {
112
- if (this.vertexCache.shouldUpdate() || force) {
113
- let resBuffer = [];
114
- const triangles = lossyTriangulate(this.points).flat();
115
- triangles.forEach((verticy) => {
116
- const rot = quat.create();
117
- quat.fromEuler(...this.rotation, 'xyz', rot);
118
- const mat = mat4.create();
119
- mat4.fromQuat(rot, mat);
120
- const out = vector3();
121
- vec3.transformMat4(verticy.getPos(), mat, out);
122
- vec3.add(out, this.getPos(), out);
123
- let vertexColor = verticy.getColor();
124
- vertexColor = vertexColor ? vertexColor : this.getColor();
125
- resBuffer = resBuffer.concat(vertexBuffer3d(out[0], out[1], out[2], vertexColor));
126
- });
127
- this.vertexCache.setCache(resBuffer);
128
- return resBuffer;
129
- }
130
- return this.vertexCache.getCache();
182
+ moveTo(pos, t = 0, f) {
183
+ const diff = vector2();
184
+ vec2.sub(pos, this.pos, diff);
185
+ return transitionValues((p) => {
186
+ this.pos[0] += diff[0] * p;
187
+ this.pos[1] += diff[1] * p;
188
+ this.vertexCache.updated();
189
+ }, () => {
190
+ this.pos = pos;
191
+ this.vertexCache.updated();
192
+ }, t, f);
193
+ }
194
+ }
195
+ export class Plane extends SimulationElement3d {
196
+ geometry;
197
+ points;
198
+ constructor(pos, points, color, rotation = vector3()) {
199
+ super(pos, rotation, color);
200
+ this.rotation = rotation;
201
+ this.points = points;
202
+ this.geometry = new PlaneGeometry(points);
203
+ }
204
+ setPoints(newPoints) {
205
+ this.points = newPoints;
206
+ this.vertexCache.updated();
207
+ }
208
+ updateMatrix(camera) {
209
+ this.defaultUpdateMatrix(camera);
131
210
  }
132
211
  }
133
- export class Square extends SimulationElement {
212
+ export class Square extends SimulationElement2d {
213
+ geometry;
134
214
  width;
135
215
  height;
136
- rotation;
137
216
  vertexColors;
138
- points;
139
217
  /**
140
218
  * @param vertexColors{Record<number, Color>} - 0 is top left vertex, numbers increase clockwise
141
219
  */
142
220
  constructor(pos, width, height, color, rotation, vertexColors) {
143
- super(pos, color);
221
+ super(pos, rotation, color);
144
222
  this.width = width * devicePixelRatio;
145
223
  this.height = height * devicePixelRatio;
146
- this.rotation = rotation || 0;
147
- this.vertexColors = vertexColors || {};
148
- this.points = [
149
- vector2(this.width / 2, this.height / 2),
150
- vector2(-this.width / 2, this.height / 2),
151
- vector2(-this.width / 2, -this.height / 2),
152
- vector2(this.width / 2, -this.height / 2)
153
- ];
224
+ this.vertexColors = this.cloneColorMap(vertexColors || {});
225
+ this.geometry = new SquareGeometry(this.width, this.height);
226
+ this.geometry.setVertexColorMap(this.vertexColors);
227
+ }
228
+ cloneColorMap(colorMap) {
229
+ const newColorMap = {};
230
+ Object.entries(colorMap).forEach(([key, value]) => {
231
+ newColorMap[+key] = value.clone();
232
+ });
233
+ return newColorMap;
234
+ }
235
+ setVertexColors(newColorMap, t = 0, f) {
236
+ const colorMap = this.cloneColorMap(newColorMap);
237
+ const diffMap = {};
238
+ Object.entries(colorMap).forEach(([key, value]) => {
239
+ if (!this.vertexColors[+key]) {
240
+ this.vertexColors[+key] = color();
241
+ }
242
+ diffMap[+key] = value.diff(this.vertexColors[+key] || color());
243
+ });
244
+ Object.entries(this.vertexColors).forEach(([key, value]) => {
245
+ if (!diffMap[+key]) {
246
+ const clone = value.clone();
247
+ clone.r *= -1;
248
+ clone.g *= -1;
249
+ clone.b *= -1;
250
+ diffMap[+key] = clone;
251
+ }
252
+ });
253
+ return transitionValues((p) => {
254
+ Object.entries(diffMap).forEach(([key, value]) => {
255
+ const color = this.vertexColors[+key];
256
+ color.r += value.r * p;
257
+ color.g += value.g * p;
258
+ color.b += value.b * p;
259
+ color.a += value.a * p;
260
+ this.vertexColors[+key] = color;
261
+ });
262
+ this.geometry.setVertexColorMap(this.vertexColors);
263
+ this.vertexCache.updated();
264
+ }, () => {
265
+ this.vertexColors = colorMap;
266
+ this.geometry.setVertexColorMap(this.vertexColors);
267
+ this.vertexCache.updated();
268
+ }, t, f);
154
269
  }
155
270
  scaleWidth(amount, t = 0, f) {
156
271
  const finalWidth = this.width * amount;
157
272
  const diffWidth = finalWidth - this.width;
158
273
  return transitionValues((p) => {
159
274
  this.width += diffWidth * p;
275
+ this.geometry.setWidth(this.width);
160
276
  this.vertexCache.updated();
161
277
  }, () => {
162
278
  this.width = finalWidth;
279
+ this.geometry.setWidth(this.width);
163
280
  this.vertexCache.updated();
164
281
  }, t, f);
165
282
  }
@@ -168,9 +285,11 @@ export class Square extends SimulationElement {
168
285
  const diffHeight = finalHeight - this.height;
169
286
  return transitionValues((p) => {
170
287
  this.height += diffHeight * p;
288
+ this.geometry.setHeight(this.height);
171
289
  this.vertexCache.updated();
172
290
  }, () => {
173
291
  this.height = finalHeight;
292
+ this.geometry.setHeight(this.height);
174
293
  this.vertexCache.updated();
175
294
  }, t, f);
176
295
  }
@@ -182,10 +301,14 @@ export class Square extends SimulationElement {
182
301
  return transitionValues((p) => {
183
302
  this.width += diffWidth * p;
184
303
  this.height += diffHeight * p;
304
+ this.geometry.setWidth(this.width);
305
+ this.geometry.setHeight(this.height);
185
306
  this.vertexCache.updated();
186
307
  }, () => {
187
308
  this.width = finalWidth;
188
309
  this.height = finalHeight;
310
+ this.geometry.setWidth(this.width);
311
+ this.geometry.setHeight(this.height);
189
312
  this.vertexCache.updated();
190
313
  }, t, f);
191
314
  }
@@ -194,9 +317,11 @@ export class Square extends SimulationElement {
194
317
  const diffWidth = num - this.width;
195
318
  return transitionValues((p) => {
196
319
  this.width += diffWidth * p;
320
+ this.geometry.setWidth(this.width);
197
321
  this.vertexCache.updated();
198
322
  }, () => {
199
323
  this.width = num;
324
+ this.geometry.setWidth(this.width);
200
325
  this.vertexCache.updated();
201
326
  }, t, f);
202
327
  }
@@ -205,75 +330,45 @@ export class Square extends SimulationElement {
205
330
  const diffHeight = num - this.height;
206
331
  return transitionValues((p) => {
207
332
  this.height += diffHeight * p;
333
+ this.geometry.setHeight(this.height);
208
334
  this.vertexCache.updated();
209
335
  }, () => {
210
336
  this.height = num;
337
+ this.geometry.setHeight(this.height);
211
338
  this.vertexCache.updated();
212
339
  }, t, f);
213
340
  }
214
- rotate(rotation, t = 0, f) {
215
- const finalRotation = this.rotation + rotation;
216
- return transitionValues((p) => {
217
- this.rotation += rotation * p;
218
- this.vertexCache.updated();
219
- }, () => {
220
- this.rotation = finalRotation;
221
- this.vertexCache.updated();
222
- }, t, f);
223
- }
224
- setRotation(newRotation, t = 0, f) {
225
- const diff = newRotation - this.rotation;
226
- return transitionValues((p) => {
227
- this.rotation += diff * p;
228
- this.vertexCache.updated();
229
- }, () => {
230
- this.rotation = newRotation;
231
- this.vertexCache.updated();
232
- }, t, f);
233
- }
234
- getBuffer(camera, force) {
235
- if (this.vertexCache.shouldUpdate() || force) {
236
- let resBuffer = [];
237
- const vertexOrder = [0, 1, 2, 0, 2, 3];
238
- const rotationMat = mat4.identity();
239
- mat4.rotateZ(rotationMat, this.rotation, rotationMat);
240
- const points = this.points.map((vec) => {
241
- const pos = vector2();
242
- vec2.add(vec, pos, pos);
243
- vec2.transformMat4(vec, rotationMat, pos);
244
- vec2.add(vec, this.getPos(), pos);
245
- pos[1] = camera.getScreenSize()[1] - pos[1];
246
- pos[0] += this.width / 2;
247
- pos[1] -= this.height / 2;
248
- return pos;
249
- });
250
- vertexOrder.forEach((vertex) => {
251
- let vertexColor = this.vertexColors[vertex];
252
- vertexColor = vertexColor ? vertexColor : this.getColor();
253
- resBuffer = resBuffer.concat(vertexBuffer2d(points[vertex][0], points[vertex][1], vertexColor));
254
- });
255
- this.vertexCache.setCache(resBuffer);
256
- return resBuffer;
257
- }
258
- return this.vertexCache.getCache();
341
+ updateMatrix(camera) {
342
+ const pos = cloneBuf(this.pos);
343
+ pos[1] = camera.getScreenSize()[1] - pos[1];
344
+ pos[0] += this.width / 2;
345
+ pos[1] -= this.height / 2;
346
+ const matrix = matrix4();
347
+ mat4.translate(matrix, vector3FromVector2(pos), matrix);
348
+ mat4.rotateZ(matrix, this.rotation, matrix);
349
+ this.geometry.updateMatrix(matrix);
259
350
  }
260
351
  }
261
- export class Circle extends SimulationElement {
352
+ export class Circle extends SimulationElement2d {
353
+ geometry;
262
354
  radius;
263
- detail = 100;
355
+ detail;
264
356
  constructor(pos, radius, color, detail = 50) {
265
- super(pos, color);
266
- this.radius = radius * devicePixelRatio;
357
+ super(pos, 0, color);
358
+ this.radius = radius;
267
359
  this.detail = detail;
360
+ this.geometry = new CircleGeometry(this.radius, this.detail);
268
361
  }
269
362
  setRadius(num, t = 0, f) {
270
363
  num *= devicePixelRatio;
271
364
  const diff = num - this.radius;
272
365
  return transitionValues((p) => {
273
366
  this.radius += diff * p;
367
+ this.geometry.setRadius(this.radius);
274
368
  this.vertexCache.updated();
275
369
  }, () => {
276
370
  this.radius = num;
371
+ this.geometry.setRadius(this.radius);
277
372
  this.vertexCache.updated();
278
373
  }, t, f);
279
374
  }
@@ -282,64 +377,26 @@ export class Circle extends SimulationElement {
282
377
  const diff = finalRadius - this.radius;
283
378
  return transitionValues((p) => {
284
379
  this.radius += diff * p;
380
+ this.geometry.setRadius(this.radius);
285
381
  this.vertexCache.updated();
286
382
  }, () => {
287
383
  this.radius = finalRadius;
384
+ this.geometry.setRadius(this.radius);
288
385
  this.vertexCache.updated();
289
386
  }, t, f);
290
387
  }
291
- getBuffer(camera, force) {
292
- if (this.vertexCache.shouldUpdate() || force) {
293
- const points = [];
294
- const rotationInc = (Math.PI * 2) / this.detail;
295
- for (let i = 0; i < this.detail; i++) {
296
- const mat = mat4.identity();
297
- mat4.rotateZ(mat, rotationInc * i, mat);
298
- const vec = vector3(this.radius);
299
- vec3.transformMat4(vec, mat, vec);
300
- vec3.add(vec, this.getPos(), vec);
301
- const screenSize = camera.getScreenSize();
302
- points.push(new Vertex(vec[0], screenSize[1] - vec[1], vec[2], this.getColor(), false));
303
- }
304
- const vertices = lossyTriangulate(points).reduce((acc, curr) => {
305
- curr.forEach((vertex) => acc.push(...vertex.toBuffer(this.getColor())));
306
- return acc;
307
- }, []);
308
- this.vertexCache.setCache(vertices);
309
- return vertices;
310
- }
311
- return this.vertexCache.getCache();
388
+ updateMatrix(camera) {
389
+ this.defaultUpdateMatrix(camera);
312
390
  }
313
391
  }
314
- export class Polygon extends SimulationElement {
392
+ // TODO: litterally this whole thing
393
+ export class Polygon extends SimulationElement2d {
394
+ geometry;
315
395
  vertices;
316
- rotation = 0;
317
- constructor(pos, vertices, color) {
318
- super(pos, color);
319
- this.vertices = vertices.map((vertex) => {
320
- const newVertex = vertex.clone();
321
- newVertex.setZ(0);
322
- newVertex.setIs3d(false);
323
- return vertex;
324
- });
325
- }
326
- rotate(amount, t = 0, f) {
327
- const finalRotation = this.rotation + amount;
328
- return transitionValues((p) => {
329
- this.rotation += amount * p;
330
- this.vertexCache.updated();
331
- }, () => {
332
- this.rotation = finalRotation;
333
- }, t, f);
334
- }
335
- rotateTo(num, t = 0, f) {
336
- const diff = num - this.rotation;
337
- return transitionValues((p) => {
338
- this.rotation += diff * p;
339
- this.vertexCache.updated();
340
- }, () => {
341
- this.rotation = num;
342
- }, t, f);
396
+ constructor(pos, points, color, rotation) {
397
+ super(pos, rotation, color);
398
+ this.vertices = points;
399
+ this.geometry = new PolygonGeometry(this.vertices);
343
400
  }
344
401
  getVertices() {
345
402
  return this.vertices;
@@ -355,7 +412,7 @@ export class Polygon extends SimulationElement {
355
412
  if (vertices.length > this.vertices.length) {
356
413
  while (vertices.length > this.vertices.length) {
357
414
  const lastPos = lastVert.getPos();
358
- this.vertices.push(new Vertex(lastPos[0], lastPos[1], 0, lastVert.getColor() || this.getColor(), false));
415
+ this.vertices.push(new Vertex(lastPos[0], lastPos[1], 0, lastVert.getColor() || this.color, false));
359
416
  }
360
417
  }
361
418
  const initialPositions = this.vertices.map((p) => cloneBuf(p.getPos()));
@@ -373,16 +430,16 @@ export class Polygon extends SimulationElement {
373
430
  })
374
431
  : [])
375
432
  ];
376
- const initialColors = this.vertices.map((vert) => (vert.getColor() || this.getColor()).toVec4());
433
+ const initialColors = this.vertices.map((vert) => (vert.getColor() || this.color).toVec4());
377
434
  const colorChanges = [
378
435
  ...vertices.map((vert, i) => {
379
- const diff = (vert.getColor() || this.getColor()).diff(this.vertices[i].getColor() || this.getColor());
436
+ const diff = (vert.getColor() || this.color).diff(this.vertices[i].getColor() || this.color);
380
437
  return diff.toVec4();
381
438
  }),
382
439
  ...(this.vertices.length > vertices.length
383
440
  ? this.vertices.slice(vertices.length, this.vertices.length).map((vert) => {
384
441
  const toColor = vertices[vertices.length - 1].getColor();
385
- return (toColor || this.getColor()).diff(vert.getColor() || this.getColor()).toVec4();
442
+ return (toColor || this.color).diff(vert.getColor() || this.color).toVec4();
386
443
  })
387
444
  : [])
388
445
  ];
@@ -393,7 +450,7 @@ export class Polygon extends SimulationElement {
393
450
  vec3.scale(posChange, p, posChange);
394
451
  vec3.add(vert.getPos(), posChange, posChange);
395
452
  vec4.scale(colorChange, p, colorChange);
396
- vec4.add((vert.getColor() || this.getColor()).toVec4(), colorChange, colorChange);
453
+ vec4.add((vert.getColor() || this.color).toVec4(), colorChange, colorChange);
397
454
  vert.setPos(posChange);
398
455
  vert.setColor(colorFromVector4(colorChange));
399
456
  });
@@ -411,38 +468,21 @@ export class Polygon extends SimulationElement {
411
468
  this.vertexCache.updated();
412
469
  }, t, f);
413
470
  }
414
- getBuffer(camera, force) {
415
- if (this.vertexCache.shouldUpdate() || force) {
416
- let resBuffer = [];
417
- const rotationMat = mat4.identity();
418
- mat4.rotateZ(rotationMat, this.rotation, rotationMat);
419
- lossyTriangulate(this.vertices)
420
- .flat()
421
- .forEach((vert) => {
422
- const pos = vector3();
423
- vec3.add(vert.getPos(), pos, pos);
424
- vec3.transformMat4(pos, rotationMat, pos);
425
- vec3.add(this.getPos(), pos, pos);
426
- pos[1] = camera.getScreenSize()[1] - pos[1];
427
- resBuffer = resBuffer.concat(vertexBuffer2d(pos[0], pos[1], vert.getColor() || this.getColor()));
428
- });
429
- this.vertexCache.setCache(resBuffer);
430
- return resBuffer;
431
- }
432
- return this.vertexCache.getCache();
471
+ updateMatrix(camera) {
472
+ this.defaultUpdateMatrix(camera);
433
473
  }
434
474
  }
435
- export class Line3d extends SimulationElement {
475
+ export class Line3d extends SimulationElement3d {
476
+ geometry;
436
477
  to;
437
- toColor;
438
478
  thickness;
439
479
  constructor(pos, to, thickness) {
440
- super(pos.getPos(), to.getColor() || undefined);
480
+ super(pos.getPos(), vector3(), to.getColor() || undefined);
441
481
  this.thickness = thickness;
442
- this.toColor = to.getColor() || this.getColor();
443
482
  this.to = to.getPos();
444
483
  vec3.scale(this.to, devicePixelRatio, this.to);
445
- vec3.sub(this.to, this.getPos(), this.to);
484
+ vec3.sub(this.to, this.pos, this.to);
485
+ this.geometry = new Line3dGeometry(this.pos, this.to, this.thickness);
446
486
  }
447
487
  setStart(pos, t = 0, f) {
448
488
  return this.moveTo(pos, t, f);
@@ -462,56 +502,24 @@ export class Line3d extends SimulationElement {
462
502
  this.vertexCache.updated();
463
503
  }, t, f);
464
504
  }
465
- getBuffer(_, force) {
466
- if (this.vertexCache.shouldUpdate() || force) {
467
- const normal = vector2(-this.to[1], this.to[0]);
468
- vec2.normalize(normal, normal);
469
- vec2.scale(normal, this.thickness / 2, normal);
470
- const pos = this.getPos();
471
- const resBuffer = [
472
- ...vertexBuffer3d(pos[0] + normal[0], pos[1] + normal[1], pos[2], this.getColor()),
473
- ...vertexBuffer3d(pos[0] - normal[0], pos[1] - normal[1], pos[2], this.getColor()),
474
- ...vertexBuffer3d(pos[0] + this.to[0] + normal[0], pos[1] + this.to[1] + normal[1], pos[2] + this.to[2], this.toColor || this.getColor()),
475
- ...vertexBuffer3d(pos[0] - normal[0], pos[1] - normal[1], pos[2], this.getColor()),
476
- ...vertexBuffer3d(pos[0] + this.to[0] + normal[0], pos[1] + this.to[1] + normal[1], pos[2] + this.to[2], this.toColor || this.getColor()),
477
- ...vertexBuffer3d(pos[0] + this.to[0] - normal[0], pos[1] + this.to[1] - normal[1], pos[2] + this.to[2], this.toColor || this.getColor())
478
- ];
479
- this.vertexCache.setCache(resBuffer);
480
- return resBuffer;
481
- }
482
- return this.vertexCache.getCache();
505
+ updateMatrix(camera) {
506
+ return this.defaultUpdateMatrix(camera);
483
507
  }
484
508
  }
485
- export class Line2d extends SimulationElement {
509
+ export class Line2d extends SimulationElement2d {
510
+ geometry;
486
511
  to;
487
- toColor;
488
512
  thickness;
489
513
  constructor(from, to, thickness = 1) {
490
- super(from.getPos(), from.getColor() || undefined);
514
+ super(vector2FromVector3(from.getPos()), 0, from.getColor() || undefined);
491
515
  this.thickness = thickness * devicePixelRatio;
492
- this.toColor = to.getColor();
493
516
  this.to = vector2FromVector3(to.getPos());
494
517
  vec2.scale(this.to, devicePixelRatio, this.to);
495
- vec2.sub(this.to, this.getPos(), this.to);
496
- }
497
- setEndColor(newColor, t = 0, f) {
498
- if (!this.toColor)
499
- this.toColor = this.getColor();
500
- const diff = newColor.diff(this.toColor);
501
- const finalColor = newColor.clone();
502
- return transitionValues((p) => {
503
- this.toColor.r += diff.r * p;
504
- this.toColor.g += diff.g * p;
505
- this.toColor.b += diff.b * p;
506
- this.toColor.a += diff.a * p;
507
- this.vertexCache.updated();
508
- }, () => {
509
- this.toColor = finalColor;
510
- this.vertexCache.updated();
511
- }, t, f);
518
+ vec2.sub(this.to, this.pos, this.to);
519
+ this.geometry = new Line2dGeometry(this.pos, this.to, this.thickness);
512
520
  }
513
521
  setStart(pos, t = 0, f) {
514
- return this.moveTo(vector3FromVector2(pos), t, f);
522
+ return this.moveTo(pos, t, f);
515
523
  }
516
524
  setEnd(pos, t = 0, f) {
517
525
  const diff = vector3();
@@ -526,136 +534,33 @@ export class Line2d extends SimulationElement {
526
534
  this.vertexCache.updated();
527
535
  }, t, f);
528
536
  }
529
- getBuffer(camera, force) {
530
- if (this.vertexCache.shouldUpdate() || force) {
531
- const normal = vector2(-this.to[1], this.to[0]);
532
- vec2.normalize(normal, normal);
533
- vec2.scale(normal, this.thickness / 2, normal);
534
- const screenSize = camera.getScreenSize();
535
- const pos = this.getPos();
536
- const resBuffer = [
537
- ...vertexBuffer2d(pos[0] + normal[0], screenSize[1] - pos[1] + normal[1], this.getColor()),
538
- ...vertexBuffer2d(pos[0] - normal[0], screenSize[1] - pos[1] - normal[1], this.getColor()),
539
- ...vertexBuffer2d(pos[0] + this.to[0] + normal[0], screenSize[1] - pos[1] + this.to[1] + normal[1], this.toColor || this.getColor()),
540
- ...vertexBuffer2d(pos[0] - normal[0], screenSize[1] - pos[1] - normal[1], this.getColor()),
541
- ...vertexBuffer2d(pos[0] + this.to[0] + normal[0], screenSize[1] - pos[1] + this.to[1] + normal[1], this.toColor || this.getColor()),
542
- ...vertexBuffer2d(pos[0] + this.to[0] - normal[0], screenSize[1] - pos[1] + this.to[1] - normal[1], this.toColor || this.getColor())
543
- ];
544
- this.vertexCache.setCache(resBuffer);
545
- return resBuffer;
546
- }
547
- return this.vertexCache.getCache();
537
+ updateMatrix(camera) {
538
+ return this.defaultUpdateMatrix(camera);
548
539
  }
549
540
  }
550
- export class Cube extends SimulationElement {
551
- vertices;
552
- rotation;
541
+ export class Cube extends SimulationElement3d {
542
+ geometry;
553
543
  width;
554
544
  height;
555
545
  depth;
556
- wireframe;
557
- wireframeLines;
558
- static wireframeOrder = [
559
- [0, 1],
560
- [1, 2],
561
- [2, 3],
562
- [3, 0],
563
- [4, 5],
564
- [5, 6],
565
- [6, 7],
566
- [7, 4],
567
- [0, 4],
568
- [3, 7],
569
- [1, 5],
570
- [2, 6]
571
- ];
572
546
  constructor(pos, width, height, depth, color, rotation) {
573
- super(pos, color);
547
+ super(pos, rotation, color);
574
548
  this.width = width * devicePixelRatio;
575
549
  this.height = height * devicePixelRatio;
576
550
  this.depth = depth * devicePixelRatio;
577
551
  this.rotation = rotation || vector3();
578
- this.wireframe = false;
579
- this.wireframeLines = [];
580
- const numWireframeLines = 12;
581
- const lineThickness = 0.025;
582
- for (let i = 0; i < numWireframeLines; i++) {
583
- this.wireframeLines.push(new Line3d(vertex(), vertex(), lineThickness));
584
- }
585
- this.vertices = [];
586
- this.computeVertices();
587
- this.shiftWireframeLines();
588
- }
589
- computeVertices() {
590
- console.log(this.width, this.height);
591
- this.vertices = [
592
- // front face
593
- vector3(-this.width / 2, -this.height / 2, this.depth / 2),
594
- vector3(this.width / 2, -this.height / 2, this.depth / 2),
595
- vector3(this.width / 2, this.height / 2, this.depth / 2),
596
- vector3(-this.width / 2, this.height / 2, this.depth / 2),
597
- // back face
598
- vector3(-this.width / 2, -this.height / 2, -this.depth / 2),
599
- vector3(this.width / 2, -this.height / 2, -this.depth / 2),
600
- vector3(this.width / 2, this.height / 2, -this.depth / 2),
601
- vector3(-this.width / 2, this.height / 2, -this.depth / 2)
602
- ];
603
- }
604
- shiftWireframeLines() {
605
- let rotMatrix = mat4.identity();
606
- mat4.rotateZ(rotMatrix, this.rotation[2], rotMatrix);
607
- mat4.rotateY(rotMatrix, this.rotation[1], rotMatrix);
608
- mat4.rotateX(rotMatrix, this.rotation[0], rotMatrix);
609
- const pos = this.getPos();
610
- Cube.wireframeOrder.forEach((lineVertices, index) => {
611
- const line = this.wireframeLines[index];
612
- const start = cloneBuf(this.vertices[lineVertices[0]]);
613
- const endPoint = cloneBuf(this.vertices[lineVertices[1]]);
614
- vec3.sub(endPoint, start, endPoint);
615
- vec3.transformMat4(endPoint, rotMatrix, endPoint);
616
- vec3.transformMat4(start, rotMatrix, start);
617
- vec3.add(start, pos, start);
618
- line.setStart(start);
619
- line.setEnd(endPoint);
620
- });
621
- }
622
- setWireframe(wireframe) {
623
- this.wireframe = wireframe;
624
- }
625
- rotate(amount, t = 0, f) {
626
- const finalRotation = cloneBuf(this.rotation);
627
- vec3.add(finalRotation, amount, finalRotation);
628
- return transitionValues((p) => {
629
- this.rotation[0] += amount[0] * p;
630
- this.rotation[1] += amount[1] * p;
631
- this.rotation[2] += amount[2] * p;
632
- this.vertexCache.updated();
633
- }, () => {
634
- this.rotation = finalRotation;
635
- this.vertexCache.updated();
636
- }, t, f);
637
- }
638
- setRotation(rot, t = 0, f) {
639
- const diff = vector3();
640
- vec3.sub(rot, this.rotation, diff);
641
- return transitionValues((p) => {
642
- this.rotation[0] += diff[0] * p;
643
- this.rotation[1] += diff[1] * p;
644
- this.rotation[2] += diff[2] * p;
645
- this.vertexCache.updated();
646
- }, () => {
647
- this.rotation = rot;
648
- this.vertexCache.updated();
649
- }, t, f);
552
+ this.geometry = new CubeGeometry(this.width, this.height, this.depth);
650
553
  }
651
554
  setWidth(width, t = 0, f) {
652
555
  width *= devicePixelRatio;
653
556
  const diff = width - this.width;
654
557
  return transitionValues((p) => {
655
558
  this.width += diff * p;
559
+ this.geometry.setWidth(this.width);
656
560
  this.vertexCache.updated();
657
561
  }, () => {
658
562
  this.width = width;
563
+ this.geometry.setWidth(this.width);
659
564
  this.vertexCache.updated();
660
565
  }, t, f);
661
566
  }
@@ -664,9 +569,11 @@ export class Cube extends SimulationElement {
664
569
  const diff = height - this.width;
665
570
  return transitionValues((p) => {
666
571
  this.height += diff * p;
572
+ this.geometry.setHeight(this.height);
667
573
  this.vertexCache.updated();
668
574
  }, () => {
669
575
  this.height = height;
576
+ this.geometry.setHeight(this.height);
670
577
  this.vertexCache.updated();
671
578
  }, t, f);
672
579
  }
@@ -675,9 +582,11 @@ export class Cube extends SimulationElement {
675
582
  const diff = depth - this.width;
676
583
  return transitionValues((p) => {
677
584
  this.depth += diff * p;
585
+ this.geometry.setDepth(this.depth);
678
586
  this.vertexCache.updated();
679
587
  }, () => {
680
588
  this.depth = depth;
589
+ this.geometry.setDepth(this.depth);
681
590
  this.vertexCache.updated();
682
591
  }, t, f);
683
592
  }
@@ -692,46 +601,22 @@ export class Cube extends SimulationElement {
692
601
  this.width += widthDiff * p;
693
602
  this.height += heightDiff * p;
694
603
  this.depth += depthDiff * p;
604
+ this.geometry.setWidth(this.width);
605
+ this.geometry.setHeight(this.height);
606
+ this.geometry.setDepth(this.depth);
695
607
  this.vertexCache.updated();
696
608
  }, () => {
697
609
  this.width = finalWidth;
698
610
  this.height = finalHeight;
699
611
  this.depth = finalDepth;
612
+ this.geometry.setWidth(this.width);
613
+ this.geometry.setHeight(this.height);
614
+ this.geometry.setDepth(this.depth);
700
615
  this.vertexCache.updated();
701
616
  }, t, f);
702
617
  }
703
- getBuffer(camera, force) {
704
- if (this.vertexCache.shouldUpdate() || force) {
705
- this.computeVertices();
706
- this.shiftWireframeLines();
707
- const triangleOrder = [
708
- 0, 1, 2, 0, 2, 3,
709
- 4, 5, 6, 4, 6, 7,
710
- 0, 3, 7, 0, 7, 4,
711
- 0, 4, 5, 0, 5, 1,
712
- 1, 2, 6, 1, 5, 6,
713
- 2, 3, 7, 2, 6, 7
714
- ];
715
- let rotMatrix = mat4.identity();
716
- mat4.rotateZ(rotMatrix, this.rotation[2], rotMatrix);
717
- mat4.rotateY(rotMatrix, this.rotation[1], rotMatrix);
718
- mat4.rotateX(rotMatrix, this.rotation[0], rotMatrix);
719
- const pos = this.getPos();
720
- let resBuffer = [];
721
- triangleOrder.forEach((index) => {
722
- const vertex = cloneBuf(this.vertices[index]);
723
- vec3.transformMat4(vertex, rotMatrix, vertex);
724
- resBuffer = resBuffer.concat(vertexBuffer3d(vertex[0] + pos[0], vertex[1] + pos[1], vertex[2] + pos[2], this.getColor()));
725
- });
726
- if (this.wireframe) {
727
- this.wireframeLines.forEach((line) => {
728
- resBuffer = resBuffer.concat(line.getBuffer(camera, force));
729
- });
730
- }
731
- this.vertexCache.setCache(resBuffer);
732
- return resBuffer;
733
- }
734
- return this.vertexCache.getCache();
618
+ updateMatrix(camera) {
619
+ this.defaultUpdateMatrix(camera);
735
620
  }
736
621
  }
737
622
  export class BezierCurve2d {
@@ -859,45 +744,31 @@ export class SplinePoint2d {
859
744
  ];
860
745
  }
861
746
  }
862
- export class Spline2d extends SimulationElement {
747
+ export class Spline2d extends SimulationElement2d {
748
+ geometry;
863
749
  curves;
864
750
  thickness;
865
751
  detail;
866
752
  interpolateStart;
867
753
  interpolateLimit;
868
- distance;
869
754
  constructor(pos, points, thickness = devicePixelRatio, detail = 40) {
870
- super(pos.getPos(), pos.getColor() || undefined);
755
+ super(vector2FromVector3(pos.getPos()), 0, pos.getColor() || undefined);
871
756
  this.curves = [];
872
757
  this.thickness = thickness * devicePixelRatio;
873
758
  this.detail = detail;
874
759
  this.interpolateStart = 0;
875
760
  this.interpolateLimit = 1;
876
- this.distance = 0;
877
- for (let i = 0; i < points.length; i++) {
878
- let prevControl = null;
879
- let prevColor = null;
880
- if (i > 0) {
881
- prevControl = cloneBuf(points[i - 1].getRawControls()[1]);
882
- vec2.negate(prevControl, prevControl);
883
- const prevColors = points[i - 1].getColors();
884
- if (prevColors.at(-1)) {
885
- prevColor = prevColors.at(-1) || null;
886
- }
887
- }
888
- const bezierPoints = points[i].getVectorArray(i > 0 ? vector2FromVector3(points[i - 1].getEnd().getPos()) : null, prevControl);
889
- const curve = new CubicBezierCurve2d(bezierPoints, points[i].getDetail(), points[i].getColors(prevColor));
890
- this.distance += curve.getLength();
891
- this.curves.push(curve);
892
- }
761
+ this.geometry = new SplineGeometry(points, this.getColor(), this.thickness, this.detail);
893
762
  }
894
763
  setInterpolateStart(start, t = 0, f) {
895
764
  const diff = start - this.interpolateStart;
896
765
  return transitionValues((p) => {
897
766
  this.interpolateStart += diff * p;
767
+ this.geometry.updateInterpolationStart(this.interpolateStart);
898
768
  this.vertexCache.updated();
899
769
  }, () => {
900
770
  this.interpolateStart = start;
771
+ this.geometry.updateInterpolationStart(this.interpolateStart);
901
772
  this.vertexCache.updated();
902
773
  }, t, f);
903
774
  }
@@ -905,9 +776,11 @@ export class Spline2d extends SimulationElement {
905
776
  const diff = limit - this.interpolateLimit;
906
777
  return transitionValues((p) => {
907
778
  this.interpolateLimit += diff * p;
779
+ this.geometry.updateInterpolationLimit(this.interpolateLimit);
908
780
  this.vertexCache.updated();
909
781
  }, () => {
910
782
  this.interpolateLimit = limit;
783
+ this.geometry.updateInterpolationLimit(this.interpolateLimit);
911
784
  this.vertexCache.updated();
912
785
  }, t, f);
913
786
  }
@@ -923,66 +796,7 @@ export class Spline2d extends SimulationElement {
923
796
  const [vec] = this.interpolateSlope(t);
924
797
  return vec;
925
798
  }
926
- getBuffer(camera, force) {
927
- if (this.vertexCache.shouldUpdate() || force) {
928
- const screenSize = camera.getScreenSize();
929
- let verticesTop = [];
930
- const verticesBottom = [];
931
- let currentDistance = 0;
932
- let interpolationStarted = false;
933
- outer: for (let i = 0; i < this.curves.length; i++) {
934
- const detail = this.curves[i].getDetail() || this.detail;
935
- const step = 1 / detail;
936
- const distanceRatio = currentDistance / this.distance;
937
- if (distanceRatio > this.interpolateLimit)
938
- break;
939
- const curveLength = this.curves[i].getLength();
940
- currentDistance += curveLength;
941
- const sectionRatio = curveLength / this.distance;
942
- for (let j = 0; j < detail + 1; j++) {
943
- let currentInterpolation = step * j;
944
- let atLimit = false;
945
- if (step * j * sectionRatio + distanceRatio > this.interpolateLimit) {
946
- atLimit = true;
947
- currentInterpolation = (this.interpolateLimit - distanceRatio) / sectionRatio;
948
- }
949
- if (distanceRatio + currentInterpolation / this.curves.length < this.interpolateStart) {
950
- continue;
951
- }
952
- if (!interpolationStarted) {
953
- interpolationStarted = true;
954
- currentInterpolation = (this.interpolateStart - distanceRatio) / sectionRatio;
955
- j--;
956
- }
957
- const [point, slope] = this.curves[i].interpolateSlope(currentInterpolation);
958
- const pos = this.getPos();
959
- point[0] += pos[0];
960
- point[1] += screenSize[1] - pos[1];
961
- const normal = vector2(-slope[1], slope[0]);
962
- vec2.normalize(normal, normal);
963
- vec2.scale(normal, this.thickness / 2, normal);
964
- const colors = this.curves[i].getColors().map((c) => (c ? c : this.getColor()));
965
- const vertexColor = interpolateColors(colors, currentInterpolation);
966
- const vertTop = vertex(point[0] + normal[0], point[1] + normal[1], 0, vertexColor);
967
- verticesTop.push(vertTop);
968
- const vertBottom = vertex(point[0] - normal[0], point[1] - normal[1], 0, vertexColor);
969
- verticesBottom.unshift(vertBottom);
970
- if (atLimit) {
971
- break outer;
972
- }
973
- }
974
- }
975
- verticesTop = verticesTop.concat(verticesBottom);
976
- let resBuffer = [];
977
- lossyTriangulate(verticesTop)
978
- .flat()
979
- .forEach((vert) => {
980
- const pos = vert.getPos();
981
- resBuffer = resBuffer.concat(vertexBuffer2d(pos[0], pos[1], vert.getColor() || this.getColor()));
982
- });
983
- this.vertexCache.setCache(resBuffer);
984
- return resBuffer;
985
- }
986
- return this.vertexCache.getCache();
799
+ updateMatrix(camera) {
800
+ this.defaultUpdateMatrix(camera);
987
801
  }
988
802
  }