@3driseai/3drise-viewer 0.1.29 → 0.1.30

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 (164) hide show
  1. package/dist/animationsV2/ClipsRuntime.d.ts +4 -0
  2. package/dist/animationsV2/ClipsRuntime.d.ts.map +1 -1
  3. package/dist/animationsV2/ClipsRuntime.js +66 -2
  4. package/dist/animationsV2/clipUtils.js +1 -1
  5. package/dist/animationsV2/sequenceUtils.d.ts +5 -0
  6. package/dist/animationsV2/sequenceUtils.d.ts.map +1 -1
  7. package/dist/animationsV2/sequenceUtils.js +10 -0
  8. package/dist/animationsV2/types.d.ts +6 -0
  9. package/dist/animationsV2/types.d.ts.map +1 -1
  10. package/dist/components/EffectsGenerator.d.ts.map +1 -1
  11. package/dist/components/EffectsGenerator.js +43 -38
  12. package/dist/components/GeometryShape.d.ts.map +1 -1
  13. package/dist/components/GeometryShape.js +22 -2
  14. package/dist/components/ObjectNode.d.ts.map +1 -1
  15. package/dist/components/ObjectNode.js +6 -17
  16. package/dist/components/ParticlesGenerator.d.ts.map +1 -1
  17. package/dist/components/ParticlesGenerator.js +44 -24
  18. package/dist/components/PickVolume.d.ts +17 -0
  19. package/dist/components/PickVolume.d.ts.map +1 -0
  20. package/dist/components/PickVolume.js +20 -0
  21. package/dist/components/SkyStars.d.ts.map +1 -1
  22. package/dist/components/SkyStars.js +83 -2
  23. package/dist/components/TextGenerator.d.ts.map +1 -1
  24. package/dist/components/TextGenerator.js +3 -2
  25. package/dist/components/index.d.ts +1 -8
  26. package/dist/components/index.d.ts.map +1 -1
  27. package/dist/components/index.js +1 -4
  28. package/dist/components/particles/BioluminescentWakeParticles.d.ts +5 -0
  29. package/dist/components/particles/BioluminescentWakeParticles.d.ts.map +1 -1
  30. package/dist/components/particles/BioluminescentWakeParticles.js +2 -1
  31. package/dist/components/particles/BrownianDustParticles.d.ts +5 -0
  32. package/dist/components/particles/BrownianDustParticles.d.ts.map +1 -1
  33. package/dist/components/particles/BrownianDustParticles.js +2 -0
  34. package/dist/components/particles/ChladniSandParticles.d.ts +5 -0
  35. package/dist/components/particles/ChladniSandParticles.d.ts.map +1 -1
  36. package/dist/components/particles/ChladniSandParticles.js +2 -1
  37. package/dist/components/particles/ElectronOrbitalsParticles.d.ts +5 -0
  38. package/dist/components/particles/ElectronOrbitalsParticles.d.ts.map +1 -1
  39. package/dist/components/particles/ElectronOrbitalsParticles.js +2 -1
  40. package/dist/components/particles/FaradayPowderParticles.d.ts +5 -0
  41. package/dist/components/particles/FaradayPowderParticles.d.ts.map +1 -1
  42. package/dist/components/particles/FaradayPowderParticles.js +2 -1
  43. package/dist/components/particles/GrinderSparksParticles.d.ts +5 -0
  44. package/dist/components/particles/GrinderSparksParticles.d.ts.map +1 -1
  45. package/dist/components/particles/GrinderSparksParticles.js +2 -1
  46. package/dist/components/particles/MagneticBottleParticles.d.ts +5 -0
  47. package/dist/components/particles/MagneticBottleParticles.d.ts.map +1 -1
  48. package/dist/components/particles/MagneticBottleParticles.js +2 -1
  49. package/dist/components/particles/NeonRainParticles.d.ts +5 -0
  50. package/dist/components/particles/NeonRainParticles.d.ts.map +1 -1
  51. package/dist/components/particles/NeonRainParticles.js +2 -1
  52. package/dist/components/particles/ShapeBurstParticles.d.ts +5 -0
  53. package/dist/components/particles/ShapeBurstParticles.d.ts.map +1 -1
  54. package/dist/components/particles/ShapeBurstParticles.js +2 -1
  55. package/dist/components/particles/ShapeFlowParticles.d.ts +5 -0
  56. package/dist/components/particles/ShapeFlowParticles.d.ts.map +1 -1
  57. package/dist/components/particles/ShapeFlowParticles.js +2 -1
  58. package/dist/components/particles/TidalStreamParticles.d.ts +5 -0
  59. package/dist/components/particles/TidalStreamParticles.d.ts.map +1 -1
  60. package/dist/components/particles/TidalStreamParticles.js +2 -0
  61. package/dist/components/text/PlainText.d.ts +15 -0
  62. package/dist/components/text/PlainText.d.ts.map +1 -0
  63. package/dist/components/text/PlainText.js +4 -0
  64. package/dist/components/text/TextObjectView.d.ts +3 -1
  65. package/dist/components/text/TextObjectView.d.ts.map +1 -1
  66. package/dist/components/text/TextObjectView.js +9 -3
  67. package/dist/components/text/engine/looks/index.d.ts.map +1 -1
  68. package/dist/components/text/engine/looks/index.js +2 -1
  69. package/dist/components/text/engine/looks/plain.d.ts +146 -0
  70. package/dist/components/text/engine/looks/plain.d.ts.map +1 -0
  71. package/dist/components/text/engine/looks/plain.js +104 -0
  72. package/dist/components/text/fonts/bebas-neue.outline.d.ts +3 -0
  73. package/dist/components/text/fonts/bebas-neue.outline.d.ts.map +1 -0
  74. package/dist/components/text/fonts/bebas-neue.outline.js +4 -0
  75. package/dist/components/text/fonts/bebas-neue.strokes.d.ts +3 -0
  76. package/dist/components/text/fonts/bebas-neue.strokes.d.ts.map +1 -0
  77. package/dist/components/text/fonts/bebas-neue.strokes.js +4 -0
  78. package/dist/components/text/fonts/index.d.ts +1 -1
  79. package/dist/components/text/fonts/index.d.ts.map +1 -1
  80. package/dist/components/text/fonts/index.js +15 -1
  81. package/dist/components/text/fonts/inter.outline.d.ts +3 -0
  82. package/dist/components/text/fonts/inter.outline.d.ts.map +1 -0
  83. package/dist/components/text/fonts/inter.outline.js +4 -0
  84. package/dist/components/text/fonts/inter.strokes.d.ts +3 -0
  85. package/dist/components/text/fonts/inter.strokes.d.ts.map +1 -0
  86. package/dist/components/text/fonts/inter.strokes.js +4 -0
  87. package/dist/components/text/fonts/lora.outline.d.ts +3 -0
  88. package/dist/components/text/fonts/lora.outline.d.ts.map +1 -0
  89. package/dist/components/text/fonts/lora.outline.js +4 -0
  90. package/dist/components/text/fonts/lora.strokes.d.ts +3 -0
  91. package/dist/components/text/fonts/lora.strokes.d.ts.map +1 -0
  92. package/dist/components/text/fonts/lora.strokes.js +4 -0
  93. package/dist/components/text/fonts/montserrat.outline.d.ts +3 -0
  94. package/dist/components/text/fonts/montserrat.outline.d.ts.map +1 -0
  95. package/dist/components/text/fonts/montserrat.outline.js +4 -0
  96. package/dist/components/text/fonts/montserrat.strokes.d.ts +3 -0
  97. package/dist/components/text/fonts/montserrat.strokes.d.ts.map +1 -0
  98. package/dist/components/text/fonts/montserrat.strokes.js +4 -0
  99. package/dist/components/text/fonts/oswald.outline.d.ts +3 -0
  100. package/dist/components/text/fonts/oswald.outline.d.ts.map +1 -0
  101. package/dist/components/text/fonts/oswald.outline.js +4 -0
  102. package/dist/components/text/fonts/oswald.strokes.d.ts +3 -0
  103. package/dist/components/text/fonts/oswald.strokes.d.ts.map +1 -0
  104. package/dist/components/text/fonts/oswald.strokes.js +4 -0
  105. package/dist/components/text/fonts/playfair-display.outline.d.ts +3 -0
  106. package/dist/components/text/fonts/playfair-display.outline.d.ts.map +1 -0
  107. package/dist/components/text/fonts/playfair-display.outline.js +4 -0
  108. package/dist/components/text/fonts/playfair-display.strokes.d.ts +3 -0
  109. package/dist/components/text/fonts/playfair-display.strokes.d.ts.map +1 -0
  110. package/dist/components/text/fonts/playfair-display.strokes.js +4 -0
  111. package/dist/components/text/fonts/roboto.outline.d.ts +3 -0
  112. package/dist/components/text/fonts/roboto.outline.d.ts.map +1 -0
  113. package/dist/components/text/fonts/roboto.outline.js +4 -0
  114. package/dist/components/text/fonts/roboto.strokes.d.ts +3 -0
  115. package/dist/components/text/fonts/roboto.strokes.d.ts.map +1 -0
  116. package/dist/components/text/fonts/roboto.strokes.js +4 -0
  117. package/dist/components/text/index.d.ts +2 -0
  118. package/dist/components/text/index.d.ts.map +1 -1
  119. package/dist/components/text/index.js +1 -0
  120. package/dist/components/text/useTextObject.d.ts +1 -0
  121. package/dist/components/text/useTextObject.d.ts.map +1 -1
  122. package/dist/components/text/useTextObject.js +23 -11
  123. package/dist/constants.d.ts.map +1 -1
  124. package/dist/constants.js +2 -0
  125. package/dist/index.d.ts +1 -1
  126. package/dist/index.d.ts.map +1 -1
  127. package/dist/index.js +1 -1
  128. package/dist/operations/catalogue/flock.d.ts.map +1 -1
  129. package/dist/operations/catalogue/flock.js +44 -9
  130. package/dist/operations/catalogue/inkBloom.d.ts.map +1 -1
  131. package/dist/operations/catalogue/inkBloom.js +46 -9
  132. package/dist/operations/catalogue/liquidMetal.d.ts.map +1 -1
  133. package/dist/operations/catalogue/liquidMetal.js +41 -12
  134. package/dist/operations/catalogue/particleSwarm.d.ts.map +1 -1
  135. package/dist/operations/catalogue/particleSwarm.js +29 -9
  136. package/dist/operations/catalogue/petalPeel.d.ts.map +1 -1
  137. package/dist/operations/catalogue/petalPeel.js +33 -9
  138. package/dist/operations/catalogue/portal.d.ts.map +1 -1
  139. package/dist/operations/catalogue/portal.js +33 -11
  140. package/dist/operations/catalogue/sandErosion.d.ts.map +1 -1
  141. package/dist/operations/catalogue/sandErosion.js +36 -14
  142. package/dist/operations/catalogue/teleport.d.ts.map +1 -1
  143. package/dist/operations/catalogue/teleport.js +43 -12
  144. package/dist/operations/catalogue/unweave.d.ts.map +1 -1
  145. package/dist/operations/catalogue/unweave.js +34 -15
  146. package/dist/operations/catalogue/voxelize.d.ts.map +1 -1
  147. package/dist/operations/catalogue/voxelize.js +37 -10
  148. package/dist/triggers/engine.d.ts +1 -1
  149. package/dist/triggers/engine.d.ts.map +1 -1
  150. package/dist/triggers/engine.js +3 -0
  151. package/dist/triggers/hosts/actions.d.ts.map +1 -1
  152. package/dist/triggers/hosts/actions.js +1 -1
  153. package/dist/triggers/hosts/interactions.d.ts.map +1 -1
  154. package/dist/triggers/hosts/interactions.js +1 -1
  155. package/dist/utils/pickProxy.d.ts +11 -0
  156. package/dist/utils/pickProxy.d.ts.map +1 -0
  157. package/dist/utils/pickProxy.js +36 -0
  158. package/dist/utils/shapes.d.ts +4 -0
  159. package/dist/utils/shapes.d.ts.map +1 -1
  160. package/dist/utils/shapes.js +4 -1
  161. package/dist/utils/textShape.d.ts +24 -0
  162. package/dist/utils/textShape.d.ts.map +1 -0
  163. package/dist/utils/textShape.js +102 -0
  164. package/package.json +2 -2
@@ -6,7 +6,8 @@
6
6
  // of light beyond the object on that side (left/right: screen, at trigger)
7
7
  // random every thread segment flies off on its own heading and fades
8
8
  // scale the whole thread sculpture expands outward, keeping its shape, and fades
9
- // With dispersing off, the object simply stays as a woven thread sculpture.
9
+ // With Disappear off (an object effect's default) the object stays as a woven thread
10
+ // sculpture and holds: light pulses run along the rings, threads shimmer.
10
11
  // Reverting weaves the object back together.
11
12
  import { AdditiveBlending, BufferAttribute, BufferGeometry, Color, LineSegments, Matrix4, ShaderMaterial, Vector3 } from 'three';
12
13
  import { defineOperation } from '../types.js';
@@ -19,6 +20,8 @@ const KEY = 'unweave';
19
20
  const STATE = '__opUnweave';
20
21
  const MAX_TRIS = 300_000;
21
22
  const MAX_SEGMENTS = 200_000;
23
+ /** The threads run on past the surface (0 → 1) to 1.6 while dispersing. */
24
+ const END = 1.6;
22
25
  const MODES = ['up', 'down', 'left', 'right', 'random', 'scale'];
23
26
  /** Slice every mesh of the target with `levels` horizontal planes (root space). */
24
27
  function contourSegments(target, levels) {
@@ -76,17 +79,19 @@ function contourSegments(target, levels) {
76
79
  export default defineOperation({
77
80
  value: 'unweave',
78
81
  label: 'Unweave',
79
- description: 'Re-draw the targets as glowing threads that unravel into a point of light — reverting weaves them back',
82
+ description: 'Re-draw the targets as glowing threads that unravel into a point of light (or, Disappear off, stay as a thread sculpture) — reverting weaves them back',
80
83
  channel: 'visual',
81
84
  multi: true,
82
85
  toggles: true,
83
- usableIn: ['interaction', 'action'],
86
+ usableIn: ['interaction', 'action', 'object'],
87
+ objectParams: { disappear: false },
88
+ // no presence: with Disappear off the look decorates the object (it appears, then is re-drawn); on, it is the entrance / exit
84
89
  params: {
85
90
  color: { type: 'color', label: 'Thread', default: '#ffcf7a' },
86
91
  threads: { type: 'number', label: 'Threads', default: 56, min: 8, max: 200, step: 1, hint: 'Contour lines from bottom to top' },
87
- disperse: { type: 'boolean', label: 'Disperse', default: true, hint: 'Off: the object stays as a thread sculpture' },
92
+ disappear: { type: 'boolean', label: 'Disappear', default: true, hint: 'Off: the object stays as a glowing thread sculpture' },
88
93
  mode: {
89
- type: 'select', label: 'Dispersal', default: 'up',
94
+ type: 'select', label: 'Dispersal', default: 'up', showWhen: { disappear: ['true'] },
90
95
  options: [
91
96
  { value: 'up', label: 'Unravel upward' }, { value: 'down', label: 'Unravel downward' },
92
97
  { value: 'left', label: 'Unravel left → right' }, { value: 'right', label: 'Unravel right → left' },
@@ -114,7 +119,7 @@ export default defineOperation({
114
119
  uniforms: uniforms,
115
120
  vertexShader: `${decl}
116
121
  attribute float aUnwLevel; attribute float aUnwAng; attribute float aUnwRand;
117
- varying float vUnwA; varying float vUnwU;
122
+ varying float vUnwA; varying float vUnwU; varying float vUnwL; varying float vUnwR;
118
123
  ${fieldGlsl(P)}
119
124
  ${GLSL_ROT}
120
125
  void main() {
@@ -136,6 +141,7 @@ void main() {
136
141
  float u = clamp((prog - 0.55 - delay * 0.9) / 0.4, 0.0, 1.0) * uUnwDisperse;
137
142
  u = u * u * (3.0 - 2.0 * u);
138
143
  vUnwU = u;
144
+ vUnwL = s; vUnwR = aUnwRand;
139
145
  vec3 p;
140
146
  if (uUnwMode < 3.5) {
141
147
  vec3 rel = fxRot(ax, u * (2.5 + aUnwRand)) * rel0;
@@ -152,10 +158,16 @@ void main() {
152
158
  gl_Position = projectionMatrix * modelViewMatrix * vec4(p, 1.0);
153
159
  }`,
154
160
  fragmentShader: `
155
- uniform vec3 uUnwColor; varying float vUnwA; varying float vUnwU;
161
+ uniform vec3 uUnwColor; uniform float uUnwTime; uniform float uUnwDisperse; uniform float uUnwProgress;
162
+ varying float vUnwA; varying float vUnwU; varying float vUnwL; varying float vUnwR;
156
163
  void main() {
157
164
  float a = vUnwA * (1.0 - smoothstep(0.85, 1.0, vUnwU));
158
- vec3 c = uUnwColor * (1.2 + 2.5 * vUnwU);
165
+ // holding as a sculpture: a pulse of light climbs the rings, threads shimmer
166
+ float held = (1.0 - uUnwDisperse) * step(0.9999, uUnwProgress);
167
+ float pulse = 0.5 + 0.5 * sin((vUnwL * 7.0 - uUnwTime * 1.3) * 6.2832);
168
+ float shimmer = 0.6 + 0.4 * sin(uUnwTime * (2.0 + 3.0 * vUnwR) + vUnwR * 40.0);
169
+ float glow = mix(1.0, 0.55 + 0.9 * pulse * pulse + 0.35 * shimmer, held);
170
+ vec3 c = uUnwColor * (1.2 + 2.5 * vUnwU) * glow;
159
171
  gl_FragColor = vec4(c * a, a);
160
172
  #include <tonemapping_fragment>
161
173
  #include <colorspace_fragment>
@@ -186,7 +198,7 @@ void main() {
186
198
  s.color.value.set(String(p.color));
187
199
  const mode = MODES.includes(String(p.mode)) ? String(p.mode) : 'up';
188
200
  s.mode.value = MODES.indexOf(mode);
189
- s.disperse.value = p.disperse === false ? 0 : 1;
201
+ s.disperse.value = p.disappear === false ? 0 : 1;
190
202
  if (mode === 'up')
191
203
  s.axis.value.set(0, 1, 0);
192
204
  else if (mode === 'down')
@@ -205,12 +217,18 @@ void main() {
205
217
  s.field.sweep.value.copy(s.axis.value).negate();
206
218
  }
207
219
  s.lines.visible = true;
208
- const keep = s.disperse.value === 0;
209
- // 0 → 1: surface goes (field clamps at 1); the threads run on to 1.6
210
- return runProgress(target, s.field, 1.6, {
220
+ if (s.disperse.value === 0) {
221
+ // keep: the surface goes (0 → 1), the sculpture stays and holds
222
+ return runProgress(target, s.field, 1, {
223
+ duration: Number(p.duration) * (1 / END), ease: 'none', hold: true,
224
+ onComplete: () => hideMeshes(target, KEY, true),
225
+ });
226
+ }
227
+ hideMeshes(target, KEY, false);
228
+ // 0 → 1: surface goes (field clamps at 1); the threads run on to END
229
+ return runProgress(target, s.field, END, {
211
230
  duration: Number(p.duration), ease: 'none',
212
- onComplete: () => { hideMeshes(target, KEY, true); if (!keep)
213
- s.lines.visible = false; },
231
+ onComplete: () => { hideMeshes(target, KEY, true); s.lines.visible = false; },
214
232
  });
215
233
  },
216
234
  revert(target, p) {
@@ -219,8 +237,9 @@ void main() {
219
237
  return;
220
238
  hideMeshes(target, KEY, false);
221
239
  s.lines.visible = true;
240
+ // from a hold it only has part of the way to go: keep the same speed
222
241
  return runProgress(target, s.field, 0, {
223
- duration: Number(p.duration), ease: 'none',
242
+ duration: Number(p.duration) * Math.min(1, Math.max(0.1, s.field.progress.value / END)), ease: 'none',
224
243
  onComplete: () => {
225
244
  s.mats.forEach(m => removeLayer(m, KEY));
226
245
  s.lines.removeFromParent();
@@ -1 +1 @@
1
- {"version":3,"file":"voxelize.d.ts","sourceRoot":"","sources":["../../../src/operations/catalogue/voxelize.ts"],"names":[],"mappings":";AAsDA,wBAoFG"}
1
+ {"version":3,"file":"voxelize.d.ts","sourceRoot":"","sources":["../../../src/operations/catalogue/voxelize.ts"],"names":[],"mappings":";AA0DA,wBA2GG"}
@@ -4,6 +4,8 @@
4
4
  // passes, the surface goes and its voxel pops in with a brief flash; a moment
5
5
  // later the voxel lifts off along the surface normal and upward, tumbling and
6
6
  // shrinking. Reverting flies every cube back and the surface re-forms.
7
+ // With Disappear off (an object effect's default) the whole object stays digitised:
8
+ // every cube in place, and holds — a scan line climbs the rows, a few cubes flicker.
7
9
  import { BoxGeometry } from 'three';
8
10
  import { defineOperation } from '../types.js';
9
11
  import { GLSL_ROT, addLayer, forEachMeshMaterial, removeLayer } from './shaderLayers.js';
@@ -14,6 +16,8 @@ const P = 'Vox';
14
16
  const KEY = 'voxelize';
15
17
  const STATE = '__opVoxelize';
16
18
  const LIFE = 0.75;
19
+ /** Keep: the voxel age is clamped here — popped in, flash gone, not yet lifting off. */
20
+ const KEEP_AGE = 0.28;
17
21
  /** Quantise surface samples to a grid: one voxel per occupied cell, averaged. */
18
22
  function buildVoxels(target, resolution) {
19
23
  const sample = sampleSurfaceCached(target, 40000, 37);
@@ -61,15 +65,18 @@ function buildVoxels(target, resolution) {
61
65
  export default defineOperation({
62
66
  value: 'voxelize',
63
67
  label: 'Voxelise',
64
- description: 'Digitise the targets into cubes that peel off and float away — reverting clicks them back',
68
+ description: 'Digitise the targets into cubes that peel off and float away (or, Disappear off, stay as a voxel model) — reverting clicks them back',
65
69
  channel: 'visual',
66
70
  multi: true,
67
71
  toggles: true,
68
- usableIn: ['interaction', 'action'],
72
+ usableIn: ['interaction', 'action', 'object'],
73
+ objectParams: { disappear: false },
74
+ // no presence: with Disappear off the look decorates the object (it appears, then is re-drawn); on, it is the entrance / exit
69
75
  params: {
70
76
  direction: { type: 'select', label: 'Direction', default: 'up', options: DIRECTION_OPTIONS },
71
77
  resolution: { type: 'number', label: 'Resolution', default: 26, min: 6, max: 70, step: 1, hint: 'Cubes across the object’s largest side' },
72
- lift: { type: 'number', label: 'Float', default: 1, min: 0, max: 4, step: 0.05, unit: '×' },
78
+ lift: { type: 'number', label: 'Float', default: 1, min: 0, max: 4, step: 0.05, unit: '×', showWhen: { disappear: ['true'] } },
79
+ disappear: { type: 'boolean', label: 'Disappear', default: true, hint: 'Off: the object stays as a model of cubes' },
73
80
  edgeColor: { type: 'color', label: 'Scan colour', default: '#7fe3ff' },
74
81
  scale: { type: 'number', label: 'Pattern', default: 2, min: 0.5, max: 12, step: 0.1 },
75
82
  duration: { type: 'number', label: 'Duration', default: 3, min: 0.3, max: 12, step: 0.1, unit: 's' },
@@ -79,7 +86,7 @@ export default defineOperation({
79
86
  if (!st) {
80
87
  const field = createFieldState(target);
81
88
  field.edgeWidth.value = 0.04;
82
- const life = { value: LIFE }, lift = { value: 1 };
89
+ const life = { value: LIFE }, lift = { value: 1 }, keep = { value: 0 };
83
90
  const vox = buildVoxels(target, Math.round(Number(p.resolution)));
84
91
  const s = vox.cell * 0.94;
85
92
  const mesh = createFxInstances(target, {
@@ -88,11 +95,11 @@ export default defineOperation({
88
95
  attributes: { aVoxP: [vox.positions, 3], aVoxN: [vox.normals, 3], aVoxR: [vox.rand, 4] },
89
96
  layerKey: 'voxelInstances', roughness: 0.55, metalness: 0.05,
90
97
  layer: {
91
- uniforms: { ...fieldUniforms(P, field), uVoxLife: life, uVoxLift: lift },
92
- common: `varying float vVoxFlash;\n${fieldGlsl(P)}\n${GLSL_ROT}`,
98
+ uniforms: { ...fieldUniforms(P, field), uVoxLife: life, uVoxLift: lift, uVoxKeep: keep },
99
+ common: `varying float vVoxFlash; varying float vVoxHold; varying vec2 vVoxYR;\n${fieldGlsl(P)}\n${GLSL_ROT}`,
93
100
  vertexPars: /* glsl */ `
94
101
  attribute vec3 aVoxP; attribute vec3 aVoxN; attribute vec4 aVoxR;
95
- float voxAge() { return (uVoxProgress - VoxBirthOf(VoxField(VoxNorm(aVoxP)))) / uVoxLife; }
102
+ float voxAge() { const float KEEP_AGE = ${KEEP_AGE.toFixed(2)}; float a = (uVoxProgress - VoxBirthOf(VoxField(VoxNorm(aVoxP)))) / uVoxLife; return mix(a, min(a, KEEP_AGE), uVoxKeep); }
96
103
  mat3 voxSpin(float a) { float fly = smoothstep(0.3, 1.0, a); return fxRot(normalize(aVoxR.xyz - 0.5 + vec3(1e-3)), fly * fly * (2.0 + 3.0 * aVoxR.w)); }`,
97
104
  normal: `{ objectNormal = voxSpin(voxAge()) * objectNormal; }`,
98
105
  vertex: /* glsl */ `
@@ -106,11 +113,21 @@ export default defineOperation({
106
113
  + (aVoxR.xyz - 0.5) * E * 0.25 * fly;
107
114
  transformed = voxSpin(a) * (transformed * sc) + off;
108
115
  vVoxFlash = pop * (1.0 - smoothstep(0.1, 0.24, a));
116
+ // holding as a voxel model
117
+ vVoxHold = uVoxKeep * step(0.9999, uVoxProgress);
118
+ vVoxYR = vec2(VoxNorm(aVoxP).y, aVoxR.w);
119
+ }`,
120
+ emissive: /* glsl */ `
121
+ {
122
+ // a scan line climbs the rows now and then; a few cubes flicker
123
+ float scan = fract(uVoxTime * 0.22);
124
+ float line = exp(-pow((vVoxYR.x - scan * 1.2 + 0.1) * 18.0, 2.0));
125
+ float flick = step(0.955, vVoxYR.y) * (0.5 + 0.5 * sin(uVoxTime * (7.0 + 9.0 * vVoxYR.y) + vVoxYR.y * 50.0));
126
+ totalEmissiveRadiance += uVoxEdgeColor * (vVoxFlash * 0.7 + vVoxHold * (0.35 * line + 0.3 * flick * flick));
109
127
  }`,
110
- emissive: `totalEmissiveRadiance += uVoxEdgeColor * vVoxFlash * 0.7;`,
111
128
  },
112
129
  });
113
- st = { field, life, lift, mesh, mats: new Set() };
130
+ st = { field, life, lift, keep, mesh, mats: new Set() };
114
131
  const own = st;
115
132
  const layer = surfaceLayer(P, field, 'tint');
116
133
  forEachMeshMaterial(target, (mat, m) => {
@@ -125,6 +142,15 @@ export default defineOperation({
125
142
  setFieldParams(s.field, p, target, ctx);
126
143
  s.lift.value = Number(p.lift);
127
144
  s.mesh.visible = true;
145
+ s.keep.value = p.disappear === false ? 1 : 0;
146
+ if (s.keep.value) {
147
+ // keep: the surface goes (0 → 1), every cube stays in place and holds
148
+ return runProgress(target, s.field, 1, {
149
+ duration: Number(p.duration) / (1 + LIFE), ease: 'none', hold: true,
150
+ onComplete: () => hideMeshes(target, KEY, true),
151
+ });
152
+ }
153
+ hideMeshes(target, KEY, false);
128
154
  return runProgress(target, s.field, 1 + LIFE, {
129
155
  duration: Number(p.duration), ease: 'none',
130
156
  onComplete: () => { hideMeshes(target, KEY, true); s.mesh.visible = false; },
@@ -136,8 +162,9 @@ export default defineOperation({
136
162
  return;
137
163
  hideMeshes(target, KEY, false);
138
164
  s.mesh.visible = true;
165
+ // from a hold it only has part of the way to go: keep the same speed
139
166
  return runProgress(target, s.field, 0, {
140
- duration: Number(p.duration), ease: 'none',
167
+ duration: Number(p.duration) * Math.min(1, Math.max(0.1, s.field.progress.value / (1 + LIFE))), ease: 'none',
141
168
  onComplete: () => {
142
169
  s.mats.forEach(m => removeLayer(m, KEY));
143
170
  disposeFxInstances(s.mesh);
@@ -17,7 +17,7 @@ export interface RunOptions {
17
17
  catalog?: TriggerCatalog;
18
18
  effects?: boolean;
19
19
  }
20
- export type RunResult = 'ran' | 'queued' | 'waiting' | 'cycle' | 'refused' | 'unknown' | 'invalid';
20
+ export type RunResult = 'ran' | 'queued' | 'waiting' | 'cycle' | 'refused' | 'unknown' | 'invalid' | 'off';
21
21
  export interface EngineContext extends TriggerContext {
22
22
  catalog: TriggerCatalog;
23
23
  }
@@ -1 +1 @@
1
- {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../src/triggers/engine.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACzF,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC9E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAGhE,OAAO,EAAkB,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;AAGjE,2EAA2E;AAC3E,MAAM,WAAW,cAAc;IAC3B,MAAM,CAAC,CAAC,EAAE,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAAC;IAChD,IAAI,CAAC,CAAC,EAAE,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAAC;IAC7C,QAAQ,CAAC,CAAC,EAAE,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS,CAAC;IACrD,KAAK,CAAC,IAAI,UAAU,CAAC;CACxB;AAED,MAAM,WAAW,UAAU;IACvB,MAAM,EAAE,aAAa,CAAC;IACtB,IAAI,CAAC,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IAC9B,sFAAsF;IACtF,KAAK,CAAC,EAAE,aAAa,EAAE,CAAC;IACxB,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;AAEnG,MAAM,WAAW,aAAc,SAAQ,cAAc;IACjD,OAAO,EAAE,cAAc,CAAC;CAC3B;AAmBD,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,cAAc,GAAG,MAAM,IAAI,CAG1E;AAYD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,OAAO,CAI7E;AAED,8FAA8F;AAC9F,wBAAgB,WAAW,CAAC,GAAG,EAAE,YAAY,EAAE,IAAI,CAAC,EAAE,cAAc,CAAC,MAAM,CAAC,GAAG,OAAO,CAIrF;AAED,uEAAuE;AACvE,eAAO,MAAM,WAAW,GAAI,KAAK,MAAM,KAAG,aAAa,GAAG,SAA4B,CAAC;AAEvF,oEAAoE;AACpE,wBAAgB,YAAY,IAAI,aAAa,EAAE,CAG9C;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,UAAU,GAAG,SAAS,CAiCzE;AA6CD;;;GAGG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,YAAY,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAGrI;AAED,oDAAoD;AACpD,wBAAgB,kBAAkB,IAAI,IAAI,CAAoB"}
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../src/triggers/engine.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACzF,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC9E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAGhE,OAAO,EAAkB,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;AAGjE,2EAA2E;AAC3E,MAAM,WAAW,cAAc;IAC3B,MAAM,CAAC,CAAC,EAAE,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAAC;IAChD,IAAI,CAAC,CAAC,EAAE,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAAC;IAC7C,QAAQ,CAAC,CAAC,EAAE,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS,CAAC;IACrD,KAAK,CAAC,IAAI,UAAU,CAAC;CACxB;AAED,MAAM,WAAW,UAAU;IACvB,MAAM,EAAE,aAAa,CAAC;IACtB,IAAI,CAAC,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IAC9B,sFAAsF;IACtF,KAAK,CAAC,EAAE,aAAa,EAAE,CAAC;IACxB,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,KAAK,CAAC;AAE3G,MAAM,WAAW,aAAc,SAAQ,cAAc;IACjD,OAAO,EAAE,cAAc,CAAC;CAC3B;AAmBD,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,cAAc,GAAG,MAAM,IAAI,CAG1E;AAYD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,OAAO,CAI7E;AAED,8FAA8F;AAC9F,wBAAgB,WAAW,CAAC,GAAG,EAAE,YAAY,EAAE,IAAI,CAAC,EAAE,cAAc,CAAC,MAAM,CAAC,GAAG,OAAO,CAIrF;AAED,uEAAuE;AACvE,eAAO,MAAM,WAAW,GAAI,KAAK,MAAM,KAAG,aAAa,GAAG,SAA4B,CAAC;AAEvF,oEAAoE;AACpE,wBAAgB,YAAY,IAAI,aAAa,EAAE,CAG9C;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,UAAU,GAAG,SAAS,CAmCzE;AA6CD;;;GAGG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,YAAY,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAGrI;AAED,oDAAoD;AACpD,wBAAgB,kBAAkB,IAAI,IAAI,CAAoB"}
@@ -62,6 +62,9 @@ export function currentChain() {
62
62
  export function runCommand(cmd, opts) {
63
63
  if (!cmd || typeof cmd !== 'object' || !cmd.kind)
64
64
  return 'invalid';
65
+ // an effect switched off in its list is kept but never runs (a revert still goes through: it may have been on)
66
+ if (cmd.kind === 'effect' && cmd.effect?.enabled === false && cmd.command !== 'revert')
67
+ return 'off';
65
68
  const top = stack[stack.length - 1];
66
69
  const ctx = {
67
70
  source: opts.source,
@@ -1 +1 @@
1
- {"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../../../src/triggers/hosts/actions.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAG3F,mDAAmD;AACnD,eAAO,MAAM,gBAAgB,GAAI,QAAQ,OAAO,CAAC,cAAc,CAAC,GAAG,IAAI,GAAG,SAAS,KAAG,YAAY,EACrC,CAAC;AAE9D,0EAA0E;AAC1E,eAAO,MAAM,eAAe,GAAI,QAAQ,OAAO,CAAC,cAAc,CAAC,GAAG,IAAI,GAAG,SAAS,KAAG,eAAe,EAClB,CAAC;AAEnF,8DAA8D;AAC9D,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,cAAc,EAAE,IAAI,EAAE,OAAO,GAAG,QAAQ,GAAG,IAAI,CAUxF"}
1
+ {"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../../../src/triggers/hosts/actions.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAG3F,mDAAmD;AACnD,eAAO,MAAM,gBAAgB,GAAI,QAAQ,OAAO,CAAC,cAAc,CAAC,GAAG,IAAI,GAAG,SAAS,KAAG,YAAY,EACrC,CAAC;AAE9D,0EAA0E;AAC1E,eAAO,MAAM,eAAe,GAAI,QAAQ,OAAO,CAAC,cAAc,CAAC,GAAG,IAAI,GAAG,SAAS,KAAG,eAAe,EACY,CAAC;AAEjH,8DAA8D;AAC9D,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,cAAc,EAAE,IAAI,EAAE,OAAO,GAAG,QAAQ,GAAG,IAAI,CAUxF"}
@@ -2,7 +2,7 @@ import { runCommand } from '../engine.js';
2
2
  /** An action's commands, in the order they run. */
3
3
  export const actionCommandsOf = (action) => (Array.isArray(action?.commands) ? action.commands : []);
4
4
  /** The effects among an action's commands (to pre-warm their shaders). */
5
- export const actionEffectsOf = (action) => actionCommandsOf(action).flatMap(c => (c.kind === 'effect' ? [c.effect] : []));
5
+ export const actionEffectsOf = (action) => actionCommandsOf(action).flatMap(c => (c.kind === 'effect' && c.effect.enabled !== false ? [c.effect] : []));
6
6
  /** Switch an action's commands on (apply) or off (revert). */
7
7
  export function runActionCommands(action, mode) {
8
8
  const source = { host: 'action', id: action.id };
@@ -1 +1 @@
1
- {"version":3,"file":"interactions.d.ts","sourceRoot":"","sources":["../../../src/triggers/hosts/interactions.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAEvE,wDAAwD;AACxD,eAAO,MAAM,UAAU,GAAI,aAAa,OAAO,CAAC,WAAW,CAAC,GAAG,IAAI,GAAG,SAAS,KAAG,YAAY,EACvB,CAAC;AAExE,+EAA+E;AAC/E,eAAO,MAAM,SAAS,GAAI,aAAa,OAAO,CAAC,WAAW,CAAC,GAAG,IAAI,GAAG,SAAS,sDACG,CAAC"}
1
+ {"version":3,"file":"interactions.d.ts","sourceRoot":"","sources":["../../../src/triggers/hosts/interactions.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAEvE,wDAAwD;AACxD,eAAO,MAAM,UAAU,GAAI,aAAa,OAAO,CAAC,WAAW,CAAC,GAAG,IAAI,GAAG,SAAS,KAAG,YAAY,EACvB,CAAC;AAExE,+EAA+E;AAC/E,eAAO,MAAM,SAAS,GAAI,aAAa,OAAO,CAAC,WAAW,CAAC,GAAG,IAAI,GAAG,SAAS,sDACiC,CAAC"}
@@ -1,4 +1,4 @@
1
1
  /** An interaction's commands, in the order they run. */
2
2
  export const commandsOf = (interaction) => (Array.isArray(interaction?.commands) ? interaction.commands : []);
3
3
  /** The effects among an interaction's commands (to pre-warm their shaders). */
4
- export const effectsOf = (interaction) => commandsOf(interaction).flatMap(c => (c.kind === 'effect' ? [c.effect] : []));
4
+ export const effectsOf = (interaction) => commandsOf(interaction).flatMap(c => (c.kind === 'effect' && c.effect.enabled !== false ? [c.effect] : []));
@@ -0,0 +1,11 @@
1
+ import * as THREE from 'three';
2
+ export declare const PICK_PROXY_NAME = "pickProxy";
3
+ export declare function pickMaterial(side?: THREE.Side): THREE.MeshBasicMaterial;
4
+ /** A unit plane proxy (scale it to the object's extent). */
5
+ export declare function pickPlane(): THREE.Mesh;
6
+ /** A unit sphere proxy (scale it to the object's radius). Front faces only: one hit per ray from outside. */
7
+ export declare function pickSphere(): THREE.Mesh;
8
+ export declare const NO_RAYCAST: () => any;
9
+ /** Takes every object under `root` out of raycasting (the proxy is the hit target). Idempotent and cheap: safe per frame. */
10
+ export declare function stopRaycast(root: THREE.Object3D): void;
11
+ //# sourceMappingURL=pickProxy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pickProxy.d.ts","sourceRoot":"","sources":["../../src/utils/pickProxy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAY/B,eAAO,MAAM,eAAe,cAAc,CAAC;AAE3C,wBAAgB,YAAY,CAAC,IAAI,GAAE,KAAK,CAAC,IAAuB,GAAG,KAAK,CAAC,iBAAiB,CAEzF;AAED,4DAA4D;AAC5D,wBAAgB,SAAS,IAAI,KAAK,CAAC,IAAI,CAItC;AAED,6GAA6G;AAC7G,wBAAgB,UAAU,IAAI,KAAK,CAAC,IAAI,CAIvC;AAED,eAAO,MAAM,UAAU,WAAa,CAAC;AACrC,6HAA6H;AAC7H,wBAAgB,WAAW,CAAC,IAAI,EAAE,KAAK,CAAC,QAAQ,GAAG,IAAI,CAEtD"}
@@ -0,0 +1,36 @@
1
+ import * as THREE from 'three';
2
+ // =============================================================================
3
+ // Pick proxy — one invisible hit shape per object, so hover / click (the Actions tab's triggers, selection)
4
+ // react to the object as a whole instead of to whatever its shaders happen to draw: a text hits on its box,
5
+ // not letter by letter; a particle cloud on its volume, not on one sprite.
6
+ //
7
+ // The proxy is a real mesh that draws nothing (colorWrite / depthWrite off) — not `visible: false`, which the
8
+ // event layer skips. The object's own drawing meshes opt out of raycasting (`stopRaycast`) so the proxy is the
9
+ // only thing the pointer can land on. Its geometry also gives the selection gizmo a true bounding box.
10
+ // =============================================================================
11
+ export const PICK_PROXY_NAME = 'pickProxy';
12
+ export function pickMaterial(side = THREE.DoubleSide) {
13
+ return new THREE.MeshBasicMaterial({ colorWrite: false, depthWrite: false, side });
14
+ }
15
+ /** A unit plane proxy (scale it to the object's extent). */
16
+ export function pickPlane() {
17
+ const m = new THREE.Mesh(new THREE.PlaneGeometry(1, 1), pickMaterial());
18
+ m.name = PICK_PROXY_NAME;
19
+ m.frustumCulled = false;
20
+ m.renderOrder = -1000;
21
+ return m;
22
+ }
23
+ /** A unit sphere proxy (scale it to the object's radius). Front faces only: one hit per ray from outside. */
24
+ export function pickSphere() {
25
+ const m = new THREE.Mesh(new THREE.SphereGeometry(1, 12, 8), pickMaterial(THREE.FrontSide));
26
+ m.name = PICK_PROXY_NAME;
27
+ m.frustumCulled = false;
28
+ m.renderOrder = -1000;
29
+ return m;
30
+ }
31
+ export const NO_RAYCAST = () => null;
32
+ /** Takes every object under `root` out of raycasting (the proxy is the hit target). Idempotent and cheap: safe per frame. */
33
+ export function stopRaycast(root) {
34
+ root.traverse((o) => { if (o.raycast !== NO_RAYCAST)
35
+ o.raycast = NO_RAYCAST; });
36
+ }
@@ -39,6 +39,10 @@ export interface ShapeOptions {
39
39
  tube?: number;
40
40
  size?: number;
41
41
  text?: string;
42
+ /** text: packed outline font id, extra letter advance (em), bevel on/off */
43
+ font?: string;
44
+ letterSpacing?: number;
45
+ bevelEnabled?: boolean;
42
46
  vertices?: number[];
43
47
  indices?: number[];
44
48
  normals?: number[];
@@ -1 +1 @@
1
- {"version":3,"file":"shapes.d.ts","sourceRoot":"","sources":["../../src/utils/shapes.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"shapes.d.ts","sourceRoot":"","sources":["../../src/utils/shapes.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import { BoxGeometry, BufferGeometry, CapsuleGeometry, ConeGeometry, CylinderGeometry, DodecahedronGeometry, ExtrudeGeometry, Float32BufferAttribute, IcosahedronGeometry, OctahedronGeometry, Path, PlaneGeometry, RingGeometry, Shape, ShapeGeometry, SphereGeometry, TetrahedronGeometry, TorusGeometry, } from 'three';
2
2
  import { mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js';
3
+ import { buildTextGeometry, outlineFontSync } from './textShape.js';
3
4
  const safeNumber = (value, fallback, min) => {
4
5
  const num = typeof value === 'number' && !Number.isNaN(value) ? value : fallback;
5
6
  return min !== undefined ? Math.max(num, min) : num;
@@ -175,7 +176,9 @@ export const shapeGenerators = {
175
176
  octahedron: ({ radius, detail }) => new OctahedronGeometry(safeNumber(radius, 0.5, 0.0001), safeNumber(detail, 0, 0)),
176
177
  tetrahedron: ({ radius, detail }) => new TetrahedronGeometry(safeNumber(radius, 0.5, 0.0001), safeNumber(detail, 0, 0)),
177
178
  dodecahedron: ({ radius, detail }) => new DodecahedronGeometry(safeNumber(radius, 0.5, 0.0001), safeNumber(detail, 0, 0)),
178
- text: ({ text = 'Hello', size, height }) => new BoxGeometry(text.length * safeNumber(size, 1, 0.0001) * 0.6, safeNumber(size, 1, 0.0001), safeNumber(height, 0.2, 0.0001)),
179
+ // the string extruded from a packed outline font (utils/textShape); empty until the font has loaded,
180
+ // GeometryShape re-renders when it lands
181
+ text: (o) => { const f = outlineFontSync(o.font); return f ? buildTextGeometry(f, o) : new BufferGeometry(); },
179
182
  point: ({ size }) => new SphereGeometry(safeNumber(size, 0.1, 0.0001), 16, 16),
180
183
  custom: ({ vertices = [], indices = [], normals, uvs }) => {
181
184
  const geometry = new BufferGeometry();
@@ -0,0 +1,24 @@
1
+ import { BufferGeometry } from 'three';
2
+ export declare const DEFAULT_TEXT_SHAPE_FONT = "inter";
3
+ export declare const isOutlineFontLoaded: (id: string | undefined) => boolean;
4
+ /** The packed outline font when it is already loaded, else null (and the load is started). */
5
+ export declare function outlineFontSync(id: string | undefined): any | null;
6
+ /** Loads (once) and caches a packed outline font. */
7
+ export declare function loadOutlineFont(id: string | undefined): Promise<any>;
8
+ export interface TextShapeOptions {
9
+ text?: string;
10
+ font?: string;
11
+ /** Em size in world units. */
12
+ size?: number;
13
+ /** Extrusion depth in world units. */
14
+ height?: number;
15
+ /** Extra advance between letters, in em. */
16
+ letterSpacing?: number;
17
+ bevelEnabled?: boolean;
18
+ bevelThickness?: number;
19
+ bevelSize?: number;
20
+ bevelSegments?: number;
21
+ }
22
+ /** The string as an extruded, centred geometry. `font` is a loaded packed outline font (outlineFontSync / loadOutlineFont). */
23
+ export declare function buildTextGeometry(font: any, o: TextShapeOptions): BufferGeometry;
24
+ //# sourceMappingURL=textShape.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"textShape.d.ts","sourceRoot":"","sources":["../../src/utils/textShape.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAgC,MAAM,OAAO,CAAC;AAgBrE,eAAO,MAAM,uBAAuB,UAAU,CAAC;AAG/C,eAAO,MAAM,mBAAmB,GAAI,IAAI,MAAM,GAAG,SAAS,KAAG,OAAgC,CAAC;AAE9F,8FAA8F;AAC9F,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,GAAG,GAAG,IAAI,CAMlE;AAED,qDAAqD;AACrD,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,CAUpE;AAED,MAAM,WAAW,gBAAgB;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,8BAA8B;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sCAAsC;IACtC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4CAA4C;IAC5C,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;CAC1B;AAqBD,+HAA+H;AAC/H,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,EAAE,gBAAgB,GAAG,cAAc,CAgChF"}
@@ -0,0 +1,102 @@
1
+ import { BufferGeometry, ExtrudeGeometry, Path, Shape } from 'three';
2
+ import { layoutOutline } from '../components/text/engine/glyphs.js';
3
+ import { loadOutline, TEXT_FONT_IDS } from '../components/text/fonts/index.js';
4
+ // =============================================================================
5
+ // Mesh text — the `text` shape of the mesh family: the string extruded into a solid, from the same packed
6
+ // outline fonts the text looks use (components/text/fonts), so one font list serves every text in 3drise.
7
+ //
8
+ // The outline fonts load lazily (one import() per font, a few hundred KB each). Shape generators are
9
+ // synchronous, so a font is read from this cache; the first build of a font yields an empty geometry and
10
+ // starts the load, and GeometryShape re-renders once it lands (useTextShapeFont).
11
+ // =============================================================================
12
+ const fonts = new Map();
13
+ const pending = new Map();
14
+ export const DEFAULT_TEXT_SHAPE_FONT = 'inter';
15
+ const fontId = (id) => (id && TEXT_FONT_IDS.includes(id) ? id : DEFAULT_TEXT_SHAPE_FONT);
16
+ export const isOutlineFontLoaded = (id) => fonts.has(fontId(id));
17
+ /** The packed outline font when it is already loaded, else null (and the load is started). */
18
+ export function outlineFontSync(id) {
19
+ const k = fontId(id);
20
+ const f = fonts.get(k);
21
+ if (f)
22
+ return f;
23
+ void loadOutlineFont(k);
24
+ return null;
25
+ }
26
+ /** Loads (once) and caches a packed outline font. */
27
+ export function loadOutlineFont(id) {
28
+ const k = fontId(id);
29
+ const f = fonts.get(k);
30
+ if (f)
31
+ return Promise.resolve(f);
32
+ let p = pending.get(k);
33
+ if (!p) {
34
+ p = loadOutline(k).then((font) => { fonts.set(k, font); pending.delete(k); return font; });
35
+ pending.set(k, p);
36
+ }
37
+ return p;
38
+ }
39
+ // even-odd point in polygon (flat [x, y, ...] array)
40
+ function inside(px, py, p) {
41
+ let c = false;
42
+ const n = p.length / 2;
43
+ for (let i = 0, j = n - 1; i < n; j = i++) {
44
+ const xi = p[2 * i], yi = p[2 * i + 1], xj = p[2 * j], yj = p[2 * j + 1];
45
+ if (((yi > py) !== (yj > py)) && (px < ((xj - xi) * (py - yi)) / (yj - yi) + xi))
46
+ c = !c;
47
+ }
48
+ return c;
49
+ }
50
+ const pathOf = (pts, P, s) => {
51
+ // layoutOutline closes every contour by repeating its first point: drop it, Shape closes itself
52
+ const n = pts.length / 2 - 1;
53
+ P.moveTo(pts[0] * s, pts[1] * s);
54
+ for (let i = 1; i < n; i++)
55
+ P.lineTo(pts[2 * i] * s, pts[2 * i + 1] * s);
56
+ P.closePath();
57
+ return P;
58
+ };
59
+ /** The string as an extruded, centred geometry. `font` is a loaded packed outline font (outlineFontSync / loadOutlineFont). */
60
+ export function buildTextGeometry(font, o) {
61
+ const size = Math.max(1e-4, o.size ?? 1), depth = Math.max(1e-4, o.height ?? 0.2);
62
+ const text = String(o.text ?? 'Hello');
63
+ const layout = layoutOutline(font, text.length ? text : ' ', { align: 'center', letterSpacing: o.letterSpacing ?? 0 });
64
+ const shapes = [];
65
+ // contours come per glyph, outer first then counters (layoutOutline sorts them so); a counter belongs to the
66
+ // outer contour of its glyph that contains it
67
+ const byGlyph = new Map();
68
+ for (const s of layout.strokes) {
69
+ let g = byGlyph.get(s.glyph);
70
+ if (!g) {
71
+ g = { outers: [], holes: [] };
72
+ byGlyph.set(s.glyph, g);
73
+ }
74
+ if (s.hole)
75
+ g.holes.push(s.pts);
76
+ else {
77
+ const shape = pathOf(s.pts, new Shape(), size);
78
+ g.outers.push({ pts: s.pts, shape });
79
+ shapes.push(shape);
80
+ }
81
+ }
82
+ for (const g of byGlyph.values()) {
83
+ if (!g.outers.length)
84
+ continue;
85
+ for (const h of g.holes) {
86
+ const owner = g.outers.find((oc) => inside(h[0], h[1], oc.pts.subarray(0, oc.pts.length - 2))) ?? g.outers[0];
87
+ owner.shape.holes.push(pathOf(h, new Path(), size));
88
+ }
89
+ }
90
+ if (!shapes.length)
91
+ return new BufferGeometry();
92
+ const bevel = !!o.bevelEnabled;
93
+ const geo = new ExtrudeGeometry(shapes, {
94
+ depth, curveSegments: 1, bevelEnabled: bevel,
95
+ bevelThickness: bevel ? Math.max(0, o.bevelThickness ?? 0.03) : 0,
96
+ bevelSize: bevel ? Math.max(0, o.bevelSize ?? 0.02) : 0,
97
+ bevelOffset: 0,
98
+ bevelSegments: bevel ? Math.max(1, Math.round(o.bevelSegments ?? 3)) : 1,
99
+ });
100
+ geo.center();
101
+ return geo;
102
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@3driseai/3drise-viewer",
3
- "version": "0.1.29",
3
+ "version": "0.1.30",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -39,6 +39,6 @@
39
39
  },
40
40
  "dependencies": {
41
41
  "@3driseai/3drise-common": "^1.0.1",
42
- "@3driseai/3drise-core": "^1.0.22"
42
+ "@3driseai/3drise-core": "^1.0.23"
43
43
  }
44
44
  }