@opsydyn/effect-three 0.3.0 → 0.4.0

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.
Files changed (125) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +12 -0
  3. package/dist/{index-8cf8t6by.js → index-h3vf50fn.js} +2113 -83
  4. package/dist/index-h3vf50fn.js.map +53 -0
  5. package/dist/index-ttwy0znv.js +145 -0
  6. package/dist/index-ttwy0znv.js.map +12 -0
  7. package/dist/index.js +488 -147
  8. package/dist/index.js.map +8 -6
  9. package/dist/kernel/boxSelect.d.ts +22 -0
  10. package/dist/kernel/boxSelect.d.ts.map +1 -0
  11. package/dist/kernel/build.d.ts +142 -6
  12. package/dist/kernel/build.d.ts.map +1 -1
  13. package/dist/kernel/cameraShake.d.ts.map +1 -1
  14. package/dist/kernel/describeScene.d.ts +50 -0
  15. package/dist/kernel/describeScene.d.ts.map +1 -0
  16. package/dist/kernel/description.d.ts +155 -1
  17. package/dist/kernel/description.d.ts.map +1 -1
  18. package/dist/kernel/diff.d.ts +15 -0
  19. package/dist/kernel/diff.d.ts.map +1 -1
  20. package/dist/kernel/equality.d.ts +9 -1
  21. package/dist/kernel/equality.d.ts.map +1 -1
  22. package/dist/kernel/gizmo.d.ts +69 -0
  23. package/dist/kernel/gizmo.d.ts.map +1 -0
  24. package/dist/kernel/history.d.ts +61 -0
  25. package/dist/kernel/history.d.ts.map +1 -0
  26. package/dist/kernel/index.d.ts +13 -2
  27. package/dist/kernel/index.d.ts.map +1 -1
  28. package/dist/kernel/index.js +106 -3
  29. package/dist/kernel/index.js.map +1 -1
  30. package/dist/kernel/inspect.d.ts +85 -0
  31. package/dist/kernel/inspect.d.ts.map +1 -0
  32. package/dist/kernel/lineGeometry.d.ts +25 -0
  33. package/dist/kernel/lineGeometry.d.ts.map +1 -0
  34. package/dist/kernel/lockstep.d.ts +114 -0
  35. package/dist/kernel/lockstep.d.ts.map +1 -0
  36. package/dist/kernel/loop.d.ts.map +1 -1
  37. package/dist/kernel/materials.d.ts.map +1 -1
  38. package/dist/kernel/pivot.d.ts +54 -0
  39. package/dist/kernel/pivot.d.ts.map +1 -0
  40. package/dist/kernel/pointBuffers.d.ts +17 -0
  41. package/dist/kernel/pointBuffers.d.ts.map +1 -0
  42. package/dist/kernel/pointer.d.ts +12 -0
  43. package/dist/kernel/pointer.d.ts.map +1 -1
  44. package/dist/kernel/renderKit.d.ts +21 -1
  45. package/dist/kernel/renderKit.d.ts.map +1 -1
  46. package/dist/kernel/renderer.d.ts +6 -0
  47. package/dist/kernel/renderer.d.ts.map +1 -1
  48. package/dist/kernel/sceneChanges.d.ts +87 -0
  49. package/dist/kernel/sceneChanges.d.ts.map +1 -0
  50. package/dist/kernel/sceneGraph.d.ts +6 -0
  51. package/dist/kernel/sceneGraph.d.ts.map +1 -1
  52. package/dist/kernel/seeded.d.ts +9 -0
  53. package/dist/kernel/seeded.d.ts.map +1 -0
  54. package/dist/kernel/shots.d.ts +26 -0
  55. package/dist/kernel/shots.d.ts.map +1 -0
  56. package/dist/kernel/tape.d.ts +38 -0
  57. package/dist/kernel/tape.d.ts.map +1 -0
  58. package/dist/kernel/trail.d.ts +31 -0
  59. package/dist/kernel/trail.d.ts.map +1 -0
  60. package/dist/kernel/viewportGizmo.d.ts +25 -0
  61. package/dist/kernel/viewportGizmo.d.ts.map +1 -0
  62. package/dist/runtime/Inspector.d.ts +20 -0
  63. package/dist/runtime/Inspector.d.ts.map +1 -0
  64. package/dist/runtime/SceneFile.d.ts +448 -0
  65. package/dist/runtime/SceneFile.d.ts.map +1 -0
  66. package/dist/runtime/SceneSchema.d.ts +318 -0
  67. package/dist/runtime/SceneSchema.d.ts.map +1 -1
  68. package/dist/runtime/errors.d.ts +9 -0
  69. package/dist/runtime/errors.d.ts.map +1 -1
  70. package/dist/runtime/index.d.ts +3 -1
  71. package/dist/runtime/index.d.ts.map +1 -1
  72. package/dist/webgl/index.d.ts.map +1 -1
  73. package/dist/webgl/index.js +228 -3
  74. package/dist/webgl/index.js.map +6 -4
  75. package/dist/webgl/line.d.ts +4 -0
  76. package/dist/webgl/line.d.ts.map +1 -0
  77. package/dist/webgl/points.d.ts +4 -0
  78. package/dist/webgl/points.d.ts.map +1 -0
  79. package/dist/webgpu/index.d.ts.map +1 -1
  80. package/dist/webgpu/index.js +206 -26
  81. package/dist/webgpu/index.js.map +6 -4
  82. package/dist/webgpu/line.d.ts +4 -0
  83. package/dist/webgpu/line.d.ts.map +1 -0
  84. package/dist/webgpu/points.d.ts +4 -0
  85. package/dist/webgpu/points.d.ts.map +1 -0
  86. package/package.json +2 -1
  87. package/src/kernel/boxSelect.ts +73 -0
  88. package/src/kernel/build.ts +332 -0
  89. package/src/kernel/cameraShake.ts +3 -13
  90. package/src/kernel/describeScene.ts +224 -0
  91. package/src/kernel/description.ts +175 -0
  92. package/src/kernel/diff.ts +39 -0
  93. package/src/kernel/equality.ts +101 -0
  94. package/src/kernel/gizmo.ts +478 -0
  95. package/src/kernel/history.ts +118 -0
  96. package/src/kernel/index.ts +97 -0
  97. package/src/kernel/inspect.ts +214 -0
  98. package/src/kernel/lineGeometry.ts +75 -0
  99. package/src/kernel/lockstep.ts +237 -0
  100. package/src/kernel/loop.ts +5 -0
  101. package/src/kernel/materials.ts +3 -0
  102. package/src/kernel/pivot.ts +418 -0
  103. package/src/kernel/pointBuffers.ts +115 -0
  104. package/src/kernel/pointer.ts +55 -12
  105. package/src/kernel/renderKit.ts +23 -1
  106. package/src/kernel/renderer.ts +6 -0
  107. package/src/kernel/sceneChanges.ts +265 -0
  108. package/src/kernel/sceneGraph.ts +319 -8
  109. package/src/kernel/seeded.ts +18 -0
  110. package/src/kernel/shots.ts +167 -0
  111. package/src/kernel/tape.ts +124 -0
  112. package/src/kernel/trail.ts +170 -0
  113. package/src/kernel/viewportGizmo.ts +194 -0
  114. package/src/runtime/Inspector.ts +43 -0
  115. package/src/runtime/SceneFile.ts +46 -0
  116. package/src/runtime/SceneSchema.ts +247 -0
  117. package/src/runtime/errors.ts +6 -0
  118. package/src/runtime/index.ts +3 -1
  119. package/src/webgl/index.ts +4 -0
  120. package/src/webgl/line.ts +66 -0
  121. package/src/webgl/points.ts +205 -0
  122. package/src/webgpu/index.ts +4 -0
  123. package/src/webgpu/line.ts +67 -0
  124. package/src/webgpu/points.ts +193 -0
  125. package/dist/index-8cf8t6by.js.map +0 -41
@@ -208,6 +208,11 @@ export type BasicMaterial = {
208
208
  readonly transparent: boolean;
209
209
  readonly side: MaterialSide;
210
210
  readonly wireframe: boolean;
211
+ /**
212
+ * Whether it is hidden behind what is nearer, as usual; `false` draws it over
213
+ * everything (and writes no depth), as three's gizmos and helpers are drawn.
214
+ */
215
+ readonly depthTest: boolean;
211
216
  };
212
217
 
213
218
  /** A matte (Lambertian) material: cheap per-vertex lighting, no highlights. */
@@ -390,6 +395,8 @@ export type GroupNode = {
390
395
  * distance (Drei's `Detailed`, three's `LOD`), or `null` to show them all.
391
396
  */
392
397
  readonly detail: Detail | null;
398
+ /** A ribbon behind the group as it moves (Drei's `Trail`), or `null` for none. */
399
+ readonly trail: Trail | null;
393
400
  };
394
401
 
395
402
  /**
@@ -470,6 +477,47 @@ export type MeshNode = {
470
477
  readonly receiveShadow: boolean;
471
478
  /** A rigid body simulating the mesh, when the scene has `physics`; `null` for none. */
472
479
  readonly body: Body | null;
480
+ /** Lines along the mesh's hard edges (Drei's `Edges`), or `null` for none. */
481
+ readonly edges: Edges | null;
482
+ /** A ribbon behind the mesh as it moves (Drei's `Trail`), or `null` for none. */
483
+ readonly trail: Trail | null;
484
+ };
485
+
486
+ /**
487
+ * A trail behind a moving node, as Drei's `Trail`: `length × 10` points of where it has
488
+ * been, a new one every `interval` steps (60 a second of scene time) when it has moved at
489
+ * least `stride`, `decay` of them at a time; drawn as a ribbon facing the camera, a tenth
490
+ * of `width` wide in scene units, narrowing (`taper`) and fading (`fade`) to the tail.
491
+ * `local` follows the node's own position rather than its world position, as Drei's
492
+ * does; the ribbon is drawn in the scene's space either way.
493
+ */
494
+ export type Trail = {
495
+ readonly width: number;
496
+ readonly length: number;
497
+ readonly decay: number;
498
+ readonly local: boolean;
499
+ readonly stride: number;
500
+ readonly interval: number;
501
+ readonly color: Rgb;
502
+ readonly taper: 'none' | 'linear' | 'quadratic';
503
+ readonly fade: boolean;
504
+ };
505
+
506
+ /**
507
+ * Lines along a mesh's edges where its faces meet at more than `threshold` degrees
508
+ * (three's `EdgesGeometry`, as Drei's `Edges`), drawn as a line (`width` pixels, or scene
509
+ * units with `worldUnits`) by the render kit. Never picked: hits go to the mesh.
510
+ */
511
+ export type Edges = {
512
+ readonly threshold: number;
513
+ readonly color: Rgb;
514
+ readonly width: number;
515
+ readonly worldUnits: boolean;
516
+ readonly dashed: boolean;
517
+ readonly dashSize: number;
518
+ readonly gapSize: number;
519
+ readonly opacity: number;
520
+ readonly transparent: boolean;
473
521
  };
474
522
 
475
523
  /**
@@ -928,8 +976,106 @@ export type ShaderGridNode = {
928
976
  readonly side: MaterialSide;
929
977
  };
930
978
 
979
+ /**
980
+ * A line through points, as Drei's `Line` (three's fat lines, `Line2`): `width` pixels
981
+ * wide on screen, or scene units with `worldUnits`; one colour, or one per point; solid
982
+ * or dashed. With `segments` each pair of points is its own segment (Drei's `Segments`).
983
+ * A render kit draws it; without one it is an empty group.
984
+ */
985
+ export type LineNode = {
986
+ readonly _tag: 'Line';
987
+ readonly key: string;
988
+ readonly position: Vec3;
989
+ readonly rotation: Rotation;
990
+ readonly scale: Vec3;
991
+ readonly transition: Transition;
992
+ readonly behaviour: Behave | null;
993
+ /** Makes the line pickable, within its width. */
994
+ readonly interactive: boolean;
995
+ readonly points: ReadonlyArray<Vec3>;
996
+ readonly segments: boolean;
997
+ readonly color: Rgb;
998
+ /** A colour for each point, blended along the line, or `null` for `color` throughout. */
999
+ readonly vertexColors: ReadonlyArray<Rgb> | null;
1000
+ readonly width: number;
1001
+ readonly worldUnits: boolean;
1002
+ readonly dashed: boolean;
1003
+ readonly dashSize: number;
1004
+ readonly gapSize: number;
1005
+ readonly dashScale: number;
1006
+ readonly dashOffset: number;
1007
+ readonly opacity: number;
1008
+ readonly transparent: boolean;
1009
+ };
1010
+
1011
+ /**
1012
+ * Points, drawn by the render kit as round or square dots, Drei's sparkles or its
1013
+ * stars. The look says which, and carries everything needed to draw them: given
1014
+ * points, or a seed that scatters them the same way every time.
1015
+ */
1016
+ export type PointsNode = {
1017
+ readonly _tag: 'Points';
1018
+ readonly key: string;
1019
+ readonly position: Vec3;
1020
+ readonly rotation: Rotation;
1021
+ readonly scale: Vec3;
1022
+ readonly transition: Transition;
1023
+ readonly behaviour: Behave | null;
1024
+ readonly look: PointLook;
1025
+ };
1026
+
1027
+ /**
1028
+ * What points look like:
1029
+ *
1030
+ * - `Dots`: Drei's `Points` with its `PointMaterial` (three's `PointsMaterial`, `round`
1031
+ * to circles): `size` pixels across, smaller with distance under `sizeAttenuation`;
1032
+ * one colour or one a point, one size or a factor a point.
1033
+ * - `Sparkles`: Drei's `Sparkles`: `count` glowing motes scattered through a box of
1034
+ * `scale`, drifting by `noise` at `speed`.
1035
+ * - `Stars`: Drei's `Stars`: `count` stars in a shell from `radius` to `radius + depth`,
1036
+ * `factor` sizing them, twinkling at `speed`, `fade` softening their edges.
1037
+ */
1038
+ export type PointLook =
1039
+ | {
1040
+ readonly _tag: 'Dots';
1041
+ readonly positions: ReadonlyArray<Vec3>;
1042
+ readonly colors: ReadonlyArray<Rgb> | null;
1043
+ readonly sizes: ReadonlyArray<number> | null;
1044
+ readonly color: Rgb;
1045
+ readonly size: number;
1046
+ readonly sizeAttenuation: boolean;
1047
+ readonly round: boolean;
1048
+ readonly opacity: number;
1049
+ readonly transparent: boolean;
1050
+ }
1051
+ | {
1052
+ readonly _tag: 'Sparkles';
1053
+ readonly count: number;
1054
+ readonly speed: number;
1055
+ readonly opacity: number;
1056
+ readonly scale: Vec3;
1057
+ /** Each mote's size, or `null` for Drei's: random, up to 1. */
1058
+ readonly size: number | null;
1059
+ readonly color: Rgb;
1060
+ readonly noise: Vec3;
1061
+ readonly seed: number;
1062
+ }
1063
+ | {
1064
+ readonly _tag: 'Stars';
1065
+ readonly radius: number;
1066
+ readonly depth: number;
1067
+ readonly count: number;
1068
+ readonly factor: number;
1069
+ readonly saturation: number;
1070
+ readonly fade: boolean;
1071
+ readonly speed: number;
1072
+ readonly seed: number;
1073
+ };
1074
+
931
1075
  /** Any node in a scene. Keys are unique across the whole scene. */
932
1076
  export type SceneNode =
1077
+ | PointsNode
1078
+ | LineNode
933
1079
  | GridNode
934
1080
  | ShaderGridNode
935
1081
  | GroupNode
@@ -1080,6 +1226,33 @@ export type ViewRect = {
1080
1226
  */
1081
1227
  export type View = { readonly key: string; readonly camera: Camera; readonly rect: ViewRect };
1082
1228
 
1229
+ /** Where a HUD's origin sits on the canvas: a corner, an edge's middle, or the centre. */
1230
+ export type HudAlignment =
1231
+ | 'top-left'
1232
+ | 'top-center'
1233
+ | 'top-right'
1234
+ | 'center-left'
1235
+ | 'center-center'
1236
+ | 'center-right'
1237
+ | 'bottom-left'
1238
+ | 'bottom-center'
1239
+ | 'bottom-right';
1240
+
1241
+ /**
1242
+ * Nodes drawn over the scene, Drei's `Hud`, as data: a scene of their own (keys apart
1243
+ * from the scene's), drawn after it with only its depth cleared, through an orthographic
1244
+ * camera measured in CSS pixels, y up, whose origin sits `margin` pixels in from the
1245
+ * `alignment`'s edges (Drei's `GizmoHelper` places its gizmo so). Pointer events reach
1246
+ * its interactive meshes before anything behind them; their points and rays are in the
1247
+ * HUD's own space. Scene lighting and fog do not reach it.
1248
+ */
1249
+ export type Hud = {
1250
+ readonly alignment: HudAlignment;
1251
+ /** CSS pixels in from the aligned edges, `[x, y]`: unused for a centred direction. */
1252
+ readonly margin: readonly [number, number];
1253
+ readonly children: ReadonlyArray<SceneNode>;
1254
+ };
1255
+
1083
1256
  /**
1084
1257
  * A small scene rendered into a texture that materials use as `map: id`: Drei's
1085
1258
  * `RenderTexture`, as data. Its nodes have their own key namespace and may use the
@@ -1264,6 +1437,8 @@ export type SceneDescription = {
1264
1437
  readonly views: ReadonlyArray<View>;
1265
1438
  /** Scenes rendered into textures for materials to use. */
1266
1439
  readonly renderTextures: ReadonlyArray<RenderTexture>;
1440
+ /** Nodes drawn over the scene, or `null` for none. */
1441
+ readonly hud: Hud | null;
1267
1442
  /** Points to show HTML at, placed every frame the scene draws. */
1268
1443
  readonly overlays: ReadonlyArray<Overlay>;
1269
1444
  /** Image-based lighting and background, or `null` for none. */
@@ -31,6 +31,10 @@ import {
31
31
  labelMaterialEquals,
32
32
  labelTypeEquals,
33
33
  xrSpaceEquals,
34
+ lineLookEquals,
35
+ edgesEquals,
36
+ trailEquals,
37
+ pointLookEquals,
34
38
  detailEquals,
35
39
  shaderGridLookEquals,
36
40
  instancesEqual,
@@ -82,6 +86,14 @@ export type Field =
82
86
  | 'text'
83
87
  /** A shader grid's look: anything but where it is. */
84
88
  | 'grid'
89
+ /** A line's points or look: anything but where it is. */
90
+ | 'line'
91
+ /** A mesh's edge lines. */
92
+ | 'edges'
93
+ /** A group's or mesh's trail. */
94
+ | 'trail'
95
+ /** Points' look: what they are and where, within the node. */
96
+ | 'look'
85
97
  /** The tracked space a group follows. */
86
98
  | 'follow'
87
99
  /** A group's levels of detail. */
@@ -94,6 +106,9 @@ export type Op =
94
106
  /** A render texture was created, resized or its scene changed (its own operations). */
95
107
  | { readonly _tag: 'UpdateRenderTexture'; readonly id: string; readonly ops: ReadonlyArray<Op> }
96
108
  | { readonly _tag: 'RemoveRenderTexture'; readonly id: string }
109
+ /** The HUD was created or its scene changed (its own operations). */
110
+ | { readonly _tag: 'UpdateHud'; readonly ops: ReadonlyArray<Op> }
111
+ | { readonly _tag: 'RemoveHud' }
97
112
  | { readonly _tag: 'SetRenderSettings'; readonly settings: RenderSettings }
98
113
  | { readonly _tag: 'DefineResources'; readonly resources: ResourceDefinitions }
99
114
  | { readonly _tag: 'Create'; readonly node: SceneNode; readonly parent: ParentKey }
@@ -135,6 +150,7 @@ export const changedFields = (
135
150
  if (!bodyEquals(prev.body, next.body)) fields.push('body');
136
151
  if (!xrSpaceEquals(prev.follow, next.follow)) fields.push('follow');
137
152
  if (!detailEquals(prev.detail, next.detail)) fields.push('detail');
153
+ if (!trailEquals(prev.trail, next.trail)) fields.push('trail');
138
154
  return fields;
139
155
  }
140
156
  case 'Mesh': {
@@ -150,6 +166,8 @@ export const changedFields = (
150
166
  if (prev.castShadow !== next.castShadow) fields.push('castShadow');
151
167
  if (prev.receiveShadow !== next.receiveShadow) fields.push('receiveShadow');
152
168
  if (!bodyEquals(prev.body, next.body)) fields.push('body');
169
+ if (!edgesEquals(prev.edges, next.edges)) fields.push('edges');
170
+ if (!trailEquals(prev.trail, next.trail)) fields.push('trail');
153
171
  return fields;
154
172
  }
155
173
  case 'Instances': {
@@ -239,6 +257,27 @@ export const changedFields = (
239
257
  if (!Object.is(prev.shadowBias, next.shadowBias)) fields.push('shadowBias');
240
258
  return fields;
241
259
  }
260
+ case 'Points': {
261
+ if (prev._tag !== 'Points') return undefined;
262
+ if (!behaviourEquals(prev.behaviour, next.behaviour)) fields.push('behaviour');
263
+ if (!vec3Equals(prev.position, next.position)) fields.push('position');
264
+ if (!rotationEquals(prev.rotation, next.rotation)) fields.push('rotation');
265
+ if (!vec3Equals(prev.scale, next.scale)) fields.push('scale');
266
+ if (!transitionEquals(prev.transition, next.transition)) fields.push('transition');
267
+ if (!pointLookEquals(prev.look, next.look)) fields.push('look');
268
+ return fields;
269
+ }
270
+ case 'Line': {
271
+ if (prev._tag !== 'Line') return undefined;
272
+ if (!behaviourEquals(prev.behaviour, next.behaviour)) fields.push('behaviour');
273
+ if (!vec3Equals(prev.position, next.position)) fields.push('position');
274
+ if (!rotationEquals(prev.rotation, next.rotation)) fields.push('rotation');
275
+ if (!vec3Equals(prev.scale, next.scale)) fields.push('scale');
276
+ if (!transitionEquals(prev.transition, next.transition)) fields.push('transition');
277
+ if (prev.interactive !== next.interactive) fields.push('interactive');
278
+ if (!lineLookEquals(prev, next)) fields.push('line');
279
+ return fields;
280
+ }
242
281
  case 'ShaderGrid': {
243
282
  if (prev._tag !== 'ShaderGrid') return undefined;
244
283
  if (!vec3Equals(prev.position, next.position)) fields.push('position');
@@ -9,6 +9,10 @@ import type {
9
9
  Behave,
10
10
  CameraShake,
11
11
  Detail,
12
+ LineNode,
13
+ Edges,
14
+ Trail,
15
+ PointLook,
12
16
  Body,
13
17
  BodyRef,
14
18
  GeometryRef,
@@ -573,6 +577,103 @@ export const labelMaterialEquals = (a: MaterialRef | null, b: MaterialRef | null
573
577
  a === b || (a !== null && b !== null && materialRefEquals(a, b));
574
578
 
575
579
  /** Shader grid equality, apart from its position and rotation. */
580
+ /** Points' look equality: the same kind with the same settings and points. */
581
+ export const pointLookEquals = (a: PointLook, b: PointLook): boolean => {
582
+ if (a === b) return true;
583
+ switch (a._tag) {
584
+ case 'Dots':
585
+ return (
586
+ b._tag === 'Dots' &&
587
+ a.positions.length === b.positions.length &&
588
+ a.positions.every((point, index) => vec3Equals(point, b.positions[index] ?? point)) &&
589
+ numbersEqual(a.colors, b.colors) &&
590
+ numbersEqual(a.sizes, b.sizes) &&
591
+ Object.is(a.color, b.color) &&
592
+ Object.is(a.size, b.size) &&
593
+ a.sizeAttenuation === b.sizeAttenuation &&
594
+ a.round === b.round &&
595
+ Object.is(a.opacity, b.opacity) &&
596
+ a.transparent === b.transparent
597
+ );
598
+ case 'Sparkles':
599
+ return (
600
+ b._tag === 'Sparkles' &&
601
+ a.count === b.count &&
602
+ Object.is(a.speed, b.speed) &&
603
+ Object.is(a.opacity, b.opacity) &&
604
+ vec3Equals(a.scale, b.scale) &&
605
+ Object.is(a.size, b.size) &&
606
+ Object.is(a.color, b.color) &&
607
+ vec3Equals(a.noise, b.noise) &&
608
+ Object.is(a.seed, b.seed)
609
+ );
610
+ case 'Stars':
611
+ return (
612
+ b._tag === 'Stars' &&
613
+ Object.is(a.radius, b.radius) &&
614
+ Object.is(a.depth, b.depth) &&
615
+ a.count === b.count &&
616
+ Object.is(a.factor, b.factor) &&
617
+ Object.is(a.saturation, b.saturation) &&
618
+ a.fade === b.fade &&
619
+ Object.is(a.speed, b.speed) &&
620
+ Object.is(a.seed, b.seed)
621
+ );
622
+ }
623
+ };
624
+
625
+ /** Trail equality: every setting. */
626
+ export const trailEquals = (a: Trail | null, b: Trail | null): boolean =>
627
+ a === b ||
628
+ (a !== null &&
629
+ b !== null &&
630
+ Object.is(a.width, b.width) &&
631
+ Object.is(a.length, b.length) &&
632
+ Object.is(a.decay, b.decay) &&
633
+ a.local === b.local &&
634
+ Object.is(a.stride, b.stride) &&
635
+ Object.is(a.interval, b.interval) &&
636
+ Object.is(a.color, b.color) &&
637
+ a.taper === b.taper &&
638
+ a.fade === b.fade);
639
+
640
+ /** Edge lines' equality: every setting. */
641
+ export const edgesEquals = (a: Edges | null, b: Edges | null): boolean =>
642
+ a === b ||
643
+ (a !== null &&
644
+ b !== null &&
645
+ Object.is(a.threshold, b.threshold) &&
646
+ Object.is(a.color, b.color) &&
647
+ Object.is(a.width, b.width) &&
648
+ a.worldUnits === b.worldUnits &&
649
+ a.dashed === b.dashed &&
650
+ Object.is(a.dashSize, b.dashSize) &&
651
+ Object.is(a.gapSize, b.gapSize) &&
652
+ Object.is(a.opacity, b.opacity) &&
653
+ a.transparent === b.transparent);
654
+
655
+ /** A line's points and look: everything but where it is. */
656
+ export const lineLookEquals = (a: LineNode, b: LineNode): boolean =>
657
+ a.segments === b.segments &&
658
+ Object.is(a.color, b.color) &&
659
+ Object.is(a.width, b.width) &&
660
+ a.worldUnits === b.worldUnits &&
661
+ a.dashed === b.dashed &&
662
+ Object.is(a.dashSize, b.dashSize) &&
663
+ Object.is(a.gapSize, b.gapSize) &&
664
+ Object.is(a.dashScale, b.dashScale) &&
665
+ Object.is(a.dashOffset, b.dashOffset) &&
666
+ Object.is(a.opacity, b.opacity) &&
667
+ a.transparent === b.transparent &&
668
+ (a.points === b.points ||
669
+ (a.points.length === b.points.length &&
670
+ a.points.every((point, index) => vec3Equals(point, b.points[index] ?? point)))) &&
671
+ (a.vertexColors === b.vertexColors ||
672
+ (a.vertexColors !== null &&
673
+ b.vertexColors !== null &&
674
+ a.vertexColors.length === b.vertexColors.length &&
675
+ a.vertexColors.every((color, index) => Object.is(color, b.vertexColors?.[index]))));
676
+
576
677
  export const shaderGridLookEquals = (a: ShaderGridNode, b: ShaderGridNode): boolean =>
577
678
  Object.is(a.size[0], b.size[0]) &&
578
679
  Object.is(a.size[1], b.size[1]) &&