simulationjsv2 0.2.4 → 0.2.6

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