create-definedmotion 0.1.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 (98) hide show
  1. package/bin/index.js +3 -0
  2. package/package.json +37 -0
  3. package/src/cli.js +100 -0
  4. package/template/.editorconfig +9 -0
  5. package/template/.prettierignore +6 -0
  6. package/template/.prettierrc.yaml +10 -0
  7. package/template/_gitignore +10 -0
  8. package/template/build/entitlements.mac.plist +12 -0
  9. package/template/build/icon.icns +0 -0
  10. package/template/build/icon.ico +0 -0
  11. package/template/build/icon.png +0 -0
  12. package/template/electron-builder.yml +43 -0
  13. package/template/electron.vite.config.ts +50 -0
  14. package/template/eslint.config.mjs +24 -0
  15. package/template/package-lock.json +10299 -0
  16. package/template/package.json +64 -0
  17. package/template/resources/icon.png +0 -0
  18. package/template/src/assets/audio/fadeSound.mp3 +0 -0
  19. package/template/src/assets/audio/fadeSound2.mp3 +0 -0
  20. package/template/src/assets/audio/interstellar.mp3 +0 -0
  21. package/template/src/assets/audio/keyboard1.mp3 +0 -0
  22. package/template/src/assets/audio/keyboard2.mp3 +0 -0
  23. package/template/src/assets/audio/keyboard3.mp3 +0 -0
  24. package/template/src/assets/audio/tick_sound.mp3 +0 -0
  25. package/template/src/assets/base.css +67 -0
  26. package/template/src/assets/electron.svg +10 -0
  27. package/template/src/assets/fonts/Geo-Regular.woff +0 -0
  28. package/template/src/assets/fonts/Montserrat-Italic-VariableFont_wght.woff2 +0 -0
  29. package/template/src/assets/fonts/Montserrat-Medium.ttf +0 -0
  30. package/template/src/assets/fonts/Montserrat-Medium.woff +0 -0
  31. package/template/src/assets/fonts/Montserrat-VariableFont_wght.woff2 +0 -0
  32. package/template/src/assets/fonts/glitch.ttf +0 -0
  33. package/template/src/assets/hdri/indoor1.hdr +0 -0
  34. package/template/src/assets/hdri/metro1.hdr +0 -0
  35. package/template/src/assets/hdri/outdoor1.hdr +0 -0
  36. package/template/src/assets/hdri/photo-studio1.hdr +0 -0
  37. package/template/src/assets/hdri/photo-studio2.hdr +0 -0
  38. package/template/src/assets/hdri/photo-studio3.hdr +0 -0
  39. package/template/src/assets/objects/keyboardScene/ibm-keyboard.glb +0 -0
  40. package/template/src/assets/wavy-lines.svg +25 -0
  41. package/template/src/entry.ts +20 -0
  42. package/template/src/example_scenes/alternativesScene.ts +88 -0
  43. package/template/src/example_scenes/dependencyScene.ts +116 -0
  44. package/template/src/example_scenes/fourierMachineScene.ts +108 -0
  45. package/template/src/example_scenes/fourierSeriesScene.ts +678 -0
  46. package/template/src/example_scenes/keyboardScene.ts +447 -0
  47. package/template/src/example_scenes/surfaceScene.ts +88 -0
  48. package/template/src/example_scenes/tutorials/easy1.ts +59 -0
  49. package/template/src/example_scenes/tutorials/easy2.ts +141 -0
  50. package/template/src/example_scenes/tutorials/easy3.ts +133 -0
  51. package/template/src/example_scenes/tutorials/medium1.ts +154 -0
  52. package/template/src/example_scenes/vectorField.ts +209 -0
  53. package/template/src/example_scenes/visulizingFunctions.ts +246 -0
  54. package/template/src/main/index.ts +101 -0
  55. package/template/src/main/rendering.ts +219 -0
  56. package/template/src/main/storage.ts +35 -0
  57. package/template/src/preload/index.d.ts +8 -0
  58. package/template/src/preload/index.ts +36 -0
  59. package/template/src/renderer/index.html +17 -0
  60. package/template/src/renderer/src/App.svelte +130 -0
  61. package/template/src/renderer/src/app.css +24 -0
  62. package/template/src/renderer/src/env.d.ts +2 -0
  63. package/template/src/renderer/src/lib/animation/animations.ts +214 -0
  64. package/template/src/renderer/src/lib/animation/captureCanvas.ts +85 -0
  65. package/template/src/renderer/src/lib/animation/helpers.ts +7 -0
  66. package/template/src/renderer/src/lib/animation/interpolations.ts +155 -0
  67. package/template/src/renderer/src/lib/animation/protocols.ts +79 -0
  68. package/template/src/renderer/src/lib/audio/loader.ts +104 -0
  69. package/template/src/renderer/src/lib/fonts/Roboto_Regular.json +1 -0
  70. package/template/src/renderer/src/lib/fonts/montserrat-medium.json +1 -0
  71. package/template/src/renderer/src/lib/fonts/montserrat.json +1 -0
  72. package/template/src/renderer/src/lib/general/helpers.ts +77 -0
  73. package/template/src/renderer/src/lib/general/onDestory.ts +10 -0
  74. package/template/src/renderer/src/lib/mathHelpers/vectors.ts +18 -0
  75. package/template/src/renderer/src/lib/rendering/bumpMaps/noise.ts +84 -0
  76. package/template/src/renderer/src/lib/rendering/helpers.ts +35 -0
  77. package/template/src/renderer/src/lib/rendering/lighting3d.ts +387 -0
  78. package/template/src/renderer/src/lib/rendering/materials.ts +6 -0
  79. package/template/src/renderer/src/lib/rendering/objects/import.ts +148 -0
  80. package/template/src/renderer/src/lib/rendering/objects2d.ts +489 -0
  81. package/template/src/renderer/src/lib/rendering/objects3d.ts +89 -0
  82. package/template/src/renderer/src/lib/rendering/protocols.ts +21 -0
  83. package/template/src/renderer/src/lib/rendering/setup.ts +71 -0
  84. package/template/src/renderer/src/lib/rendering/svg/drawing.ts +213 -0
  85. package/template/src/renderer/src/lib/rendering/svg/parsing.ts +717 -0
  86. package/template/src/renderer/src/lib/rendering/svg/rastered.ts +42 -0
  87. package/template/src/renderer/src/lib/rendering/svgObjects.ts +1137 -0
  88. package/template/src/renderer/src/lib/scene/helpers.ts +89 -0
  89. package/template/src/renderer/src/lib/scene/sceneClass.ts +648 -0
  90. package/template/src/renderer/src/lib/shaders/background_gradient/frag.glsl +12 -0
  91. package/template/src/renderer/src/lib/shaders/background_gradient/vert.glsl +6 -0
  92. package/template/src/renderer/src/lib/shaders/hdri_blur/frag.glsl +45 -0
  93. package/template/src/renderer/src/lib/shaders/hdri_blur/vert.glsl +5 -0
  94. package/template/src/renderer/src/main.ts +9 -0
  95. package/template/svelte.config.mjs +7 -0
  96. package/template/tsconfig.json +4 -0
  97. package/template/tsconfig.node.json +10 -0
  98. package/template/tsconfig.web.json +32 -0
@@ -0,0 +1,148 @@
1
+ import * as THREE from 'three'
2
+ import { MTLLoader } from 'three/examples/jsm/loaders/MTLLoader.js'
3
+ import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js'
4
+ import { GLTFLoader, GLTF } from 'three/examples/jsm/loaders/GLTFLoader.js'
5
+
6
+ // Helper function to load an MTL file using a Promise
7
+ function loadMTL(mtlPath, mtlFile) {
8
+ return new Promise((resolve, reject) => {
9
+ const mtlLoader = new MTLLoader()
10
+ mtlLoader.setPath(mtlPath)
11
+ mtlLoader.load(
12
+ mtlFile,
13
+ (materials) => {
14
+ materials.preload()
15
+ resolve(materials)
16
+ },
17
+ undefined,
18
+ (err) => {
19
+ reject(new Error(`Error loading MTL: ${err}`))
20
+ }
21
+ )
22
+ })
23
+ }
24
+
25
+ // Helper function to load an OBJ file using a Promise
26
+ function loadOBJ(objPath, objFile, materials) {
27
+ return new Promise((resolve, reject) => {
28
+ const objLoader = new OBJLoader()
29
+ if (materials) {
30
+ objLoader.setMaterials(materials)
31
+ }
32
+ objLoader.setPath(objPath)
33
+ objLoader.load(
34
+ objFile,
35
+ (object) => {
36
+ resolve(object)
37
+ },
38
+ undefined,
39
+ (err) => {
40
+ reject(new Error(`Error loading OBJ: ${err}`))
41
+ }
42
+ )
43
+ })
44
+ }
45
+
46
+ // Define an interface for the parameters
47
+ interface LoadParams {
48
+ mtlPath: string
49
+ mtlFile: string
50
+ objPath: string
51
+ objFile: string
52
+ }
53
+
54
+ // Generic async function that loads an OBJ with its MTL
55
+ export async function loadOBJWithMTL({
56
+ mtlPath,
57
+ mtlFile,
58
+ objPath,
59
+ objFile
60
+ }: LoadParams): Promise<THREE.Object3D> {
61
+ try {
62
+ // Load the MTL file and get the materials
63
+ const materials = await loadMTL(mtlPath, mtlFile)
64
+ // Load the OBJ file with the preloaded materials
65
+ const object = await loadOBJ(objPath, objFile, materials)
66
+ return object as any
67
+ } catch (error) {
68
+ console.error(error)
69
+ throw error
70
+ }
71
+ }
72
+
73
+ // Generic async function that loads an OBJ with its MTL given full file paths
74
+ export async function loadOBJWithMTLPaths(
75
+ objFullPath: string,
76
+ mtlFullPath: string
77
+ ): Promise<THREE.Object3D> {
78
+ try {
79
+ // Load the MTL file and get the materials
80
+ const materials = await new Promise<any>((resolve, reject) => {
81
+ const mtlLoader = new MTLLoader()
82
+ mtlLoader.load(
83
+ mtlFullPath,
84
+ (materials) => {
85
+ materials.preload()
86
+ resolve(materials)
87
+ },
88
+ undefined,
89
+ (error) => reject(new Error(`Error loading MTL file: ${error}`))
90
+ )
91
+ })
92
+
93
+ // Load the OBJ file with the preloaded materials
94
+ const object = await new Promise<THREE.Object3D>((resolve, reject) => {
95
+ const objLoader = new OBJLoader()
96
+ objLoader.setMaterials(materials)
97
+ objLoader.load(
98
+ objFullPath,
99
+ (object) => resolve(object),
100
+ undefined,
101
+ (error) => reject(new Error(`Error loading OBJ file: ${error}`))
102
+ )
103
+ })
104
+
105
+ return object
106
+ } catch (error) {
107
+ console.error(error)
108
+ throw error
109
+ }
110
+ }
111
+
112
+ // Generic async function that loads a GLB file given its full path/URL
113
+ export async function loadGLB(
114
+ glbPath: string,
115
+ overrideMaterial?: THREE.Material
116
+ ): Promise<THREE.Object3D> {
117
+ try {
118
+ const gltf: GLTF = await new Promise((resolve, reject) => {
119
+ const loader = new GLTFLoader()
120
+ loader.load(
121
+ glbPath,
122
+ (gltfData) => resolve(gltfData),
123
+ undefined,
124
+ (error) => reject(new Error(`Error loading GLB file: ${error}`))
125
+ )
126
+ })
127
+
128
+ gltf.scene.traverse((child) => {
129
+ if ((child as THREE.Mesh).isMesh) {
130
+ child.castShadow = true
131
+ child.receiveShadow = true
132
+
133
+ if (overrideMaterial) {
134
+ child.material = overrideMaterial
135
+ // if the material instance has textures or other maps,
136
+ // make sure to mark it for update
137
+ child.material.needsUpdate = true
138
+ }
139
+ }
140
+ })
141
+
142
+ // Return the scene (or top-level object) from the GLB file
143
+ return gltf.scene
144
+ } catch (error) {
145
+ console.error(error)
146
+ throw error
147
+ }
148
+ }
@@ -0,0 +1,489 @@
1
+ import * as THREE from 'three'
2
+ import { COLORS } from './helpers'
3
+ import { TextGeometry } from 'three/examples/jsm/geometries/TextGeometry.js'
4
+ import { FontLoader, LineGeometry, LineMaterial, ThreeMFLoader } from 'three/examples/jsm/Addons.js'
5
+ import fontJSON from '../fonts/montserrat.json'
6
+ import fontTroika from '$assets/fonts/Montserrat-Medium.woff'
7
+
8
+ import { Text } from 'troika-three-text'
9
+ import { preloadFont, configureTextBuilder } from 'troika-three-text'
10
+ import { Line2 } from 'three/examples/jsm/lines/webgpu/Line2.js'
11
+ import { Vector3 } from 'three'
12
+
13
+ let hasLoadedFonts = false
14
+
15
+ export const loadFonts = (): Promise<void> => {
16
+ if (hasLoadedFonts) new Promise((resolve) => resolve)
17
+ configureTextBuilder({
18
+ useWorker: false
19
+ })
20
+
21
+ return new Promise((resolve) => {
22
+ preloadFont(
23
+ {
24
+ font: fontTroika,
25
+ characters: 'abcdefghijklmnopqrstuvwxyz'
26
+ },
27
+ () => {
28
+ console.log('preload font complete')
29
+ hasLoadedFonts = true
30
+ resolve()
31
+ }
32
+ )
33
+ })
34
+ }
35
+
36
+ export interface ObjectOptions {
37
+ color?: THREE.ColorRepresentation
38
+ material?: THREE.Material
39
+ }
40
+
41
+ let loadedFont: any = null
42
+
43
+ const loader = new FontLoader()
44
+ const loadFont = () => {
45
+ loadedFont = loader.parse(fontJSON as any)
46
+ }
47
+
48
+ interface MeshWithColorMaterial extends THREE.Mesh {
49
+ material: {
50
+ color: THREE.Color
51
+ } & THREE.Material
52
+ }
53
+
54
+ const createMesh = (
55
+ geometry: THREE.BufferGeometry,
56
+ options?: ObjectOptions
57
+ ): MeshWithColorMaterial => {
58
+ const meshMaterial = options?.material
59
+ ? options.material
60
+ : new THREE.MeshBasicMaterial({
61
+ color: options?.color ? options.color : new THREE.Color(1, 1, 1),
62
+ transparent: true
63
+ })
64
+ const mesh = new THREE.Mesh(geometry, meshMaterial)
65
+ return mesh as any
66
+ }
67
+
68
+ interface RectangleOptions extends ObjectOptions {
69
+ stroke?: {
70
+ color?: THREE.ColorRepresentation
71
+ width?: number
72
+ placement?: 'inside' | 'outside' | 'center'
73
+ visible?: boolean
74
+ resolution?: THREE.Vector2
75
+ }
76
+ }
77
+
78
+ export const createRectangle = (
79
+ width: number = 10,
80
+ height: number = 10,
81
+ options?: RectangleOptions
82
+ ) => {
83
+ const geometry = new THREE.PlaneGeometry(width, height)
84
+ const mesh = createMesh(geometry, options)
85
+
86
+ if (options?.stroke) {
87
+ const strokeParams = options.stroke
88
+ const placement = strokeParams.placement ?? 'outside'
89
+ const strokeWidth = strokeParams.width ?? 0.1
90
+
91
+ // Calculate adjusted dimensions
92
+ let strokeWidthAdj = width
93
+ let strokeHeightAdj = height
94
+ switch (placement) {
95
+ case 'inside':
96
+ strokeWidthAdj = width - strokeWidth / 2
97
+ strokeHeightAdj = height - strokeWidth / 2
98
+ break
99
+ case 'outside':
100
+ strokeWidthAdj = width + strokeWidth / 2
101
+ strokeHeightAdj = height + strokeWidth / 2
102
+ break
103
+ case 'center':
104
+ strokeWidthAdj = width
105
+ strokeHeightAdj = height
106
+ break
107
+ }
108
+
109
+ // Create rectangle path with adjusted corners
110
+ const halfW = strokeWidthAdj / 2
111
+ const halfH = strokeHeightAdj / 2
112
+ const cornerCut = strokeWidth * 0.2 // Adjust this to control corner shape
113
+ const zValue = 0.001
114
+
115
+ const points = [
116
+ // Bottom-left to bottom-right with corner cut
117
+ new THREE.Vector3(-halfW + cornerCut, -halfH, zValue),
118
+ new THREE.Vector3(halfW - cornerCut, -halfH, zValue),
119
+
120
+ // Bottom-right to top-right with corner cut
121
+ new THREE.Vector3(halfW, -halfH + cornerCut, zValue),
122
+ new THREE.Vector3(halfW, halfH - cornerCut, zValue),
123
+
124
+ // Top-right to top-left with corner cut
125
+ new THREE.Vector3(halfW - cornerCut, halfH, zValue),
126
+ new THREE.Vector3(-halfW + cornerCut, halfH, zValue),
127
+
128
+ // Top-left to bottom-left with corner cut
129
+ new THREE.Vector3(-halfW, halfH - cornerCut, zValue),
130
+ new THREE.Vector3(-halfW, -halfH + cornerCut, zValue),
131
+
132
+ // Close the loop
133
+ new THREE.Vector3(-halfW + cornerCut, -halfH, zValue)
134
+ ]
135
+
136
+ const lineGeometry = new LineGeometry()
137
+ lineGeometry.setPositions(points.flatMap((p) => [p.x, p.y, p.z]))
138
+
139
+ const lineMaterial = new LineMaterial({
140
+ color: strokeParams.color ?? 0x000000,
141
+ linewidth: strokeWidth,
142
+ worldUnits: true,
143
+ resolution:
144
+ strokeParams.resolution || new THREE.Vector2(window.innerWidth, window.innerHeight),
145
+ depthTest: false,
146
+ depthWrite: false,
147
+ transparent: true
148
+ } as THREE.LineBasicMaterialParameters) // Type assertion here
149
+
150
+ const stroke = new Line2(lineGeometry, lineMaterial as any)
151
+ stroke.renderOrder = 1
152
+
153
+ if (mesh.material instanceof THREE.Material) {
154
+ mesh.material.depthWrite = true
155
+ }
156
+
157
+ mesh.add(stroke)
158
+ ;(mesh as any).stroke = stroke
159
+ }
160
+
161
+ return mesh
162
+ }
163
+
164
+ // Define interface extending Line with your custom method
165
+ export interface PaddedLine extends THREE.Line {
166
+ updatePositions: (
167
+ newPoint1?: THREE.Vector3,
168
+ newPoint2?: THREE.Vector3,
169
+ newPadding?: number
170
+ ) => void
171
+ userData: {
172
+ point1: THREE.Vector3
173
+ point2: THREE.Vector3
174
+ padding: number
175
+ }
176
+ }
177
+
178
+ export const createLine = ({
179
+ point1 = new THREE.Vector3(0, 0, 0),
180
+ point2 = new THREE.Vector3(0, 0, 0),
181
+ color = new THREE.Color(1, 1, 1),
182
+ width = 1,
183
+ padding = 0
184
+ }: {
185
+ point1?: THREE.Vector3
186
+ point2?: THREE.Vector3
187
+ color?: THREE.ColorRepresentation
188
+ width?: number
189
+ padding?: number
190
+ } = {}): PaddedLine => {
191
+ // Create the line geometry
192
+ const geometry = new THREE.BufferGeometry()
193
+
194
+ // Set initial positions (will be updated immediately)
195
+ const positions = new Float32Array(6)
196
+ const posAttribute = new THREE.BufferAttribute(positions, 3)
197
+ geometry.setAttribute('position', posAttribute)
198
+
199
+ // Create the line material
200
+ const material = new THREE.LineBasicMaterial({
201
+ color: color,
202
+ linewidth: width,
203
+ transparent: true // Note: line width only works in WebGLRenderer with GL_LINES (limited browser support)
204
+ })
205
+
206
+ // Create the line
207
+ const line = new THREE.Line(geometry, material as any) as PaddedLine
208
+
209
+ // Store the original points and padding as properties
210
+ line.userData = {
211
+ point1: point1.clone(),
212
+ point2: point2.clone(),
213
+ padding: padding
214
+ }
215
+
216
+ // Initial position update
217
+ updateLinePositions(line)
218
+
219
+ // Add update method to the line
220
+ line.updatePositions = function (
221
+ newPoint1?: THREE.Vector3,
222
+ newPoint2?: THREE.Vector3,
223
+ newPadding?: number
224
+ ) {
225
+ // Update stored values if provided
226
+ if (newPoint1) this.userData.point1.copy(newPoint1)
227
+ if (newPoint2) this.userData.point2.copy(newPoint2)
228
+ if (newPadding !== undefined) this.userData.padding = newPadding
229
+
230
+ // Update the line geometry
231
+ updateLinePositions(this)
232
+ }
233
+
234
+ return line
235
+ }
236
+
237
+ // Reusable vectors to avoid creating objects each time
238
+ const tempDir = new THREE.Vector3()
239
+ const tempAdjusted1 = new THREE.Vector3()
240
+ const tempAdjusted2 = new THREE.Vector3()
241
+ const tempMidPoint = new THREE.Vector3()
242
+
243
+ function updateLinePositions(line: PaddedLine) {
244
+ const { point1, point2, padding } = line.userData
245
+ const positions = line.geometry.attributes.position.array as Float32Array
246
+
247
+ // Don't apply padding if points are too close
248
+ const distance = point1.distanceTo(point2)
249
+
250
+ if (distance <= padding * 2) {
251
+ // Points are too close for padding, use midpoint
252
+ tempMidPoint.addVectors(point1, point2).multiplyScalar(0.5)
253
+
254
+ positions[0] = positions[3] = tempMidPoint.x
255
+ positions[1] = positions[4] = tempMidPoint.y
256
+ positions[2] = positions[5] = tempMidPoint.z
257
+ } else {
258
+ // Calculate direction vector
259
+ tempDir.subVectors(point2, point1).normalize()
260
+
261
+ // Create adjusted points with padding
262
+ tempAdjusted1.copy(point1).addScaledVector(tempDir, padding)
263
+ tempAdjusted2.copy(point2).addScaledVector(tempDir, -padding)
264
+
265
+ // Update the positions array
266
+ positions[0] = tempAdjusted1.x
267
+ positions[1] = tempAdjusted1.y
268
+ positions[2] = tempAdjusted1.z
269
+ positions[3] = tempAdjusted2.x
270
+ positions[4] = tempAdjusted2.y
271
+ positions[5] = tempAdjusted2.z
272
+ }
273
+
274
+ // Mark the attribute as needing update
275
+ line.geometry.attributes.position.needsUpdate = true
276
+ }
277
+
278
+ type StrokePlacement = 'inside' | 'outside' | 'center'
279
+
280
+ interface CircleOptions extends ObjectOptions {
281
+ stroke?: {
282
+ color?: THREE.ColorRepresentation
283
+ width?: number
284
+ placement?: StrokePlacement
285
+ visible?: boolean
286
+ resolution?: THREE.Vector2
287
+ }
288
+ }
289
+
290
+ export const createCircle = (radius: number = 10, options?: CircleOptions) => {
291
+ const geometry = new THREE.CircleGeometry(radius, 100)
292
+ const mesh = createMesh(geometry, options)
293
+
294
+ if (options?.stroke) {
295
+ const strokeParams = options.stroke
296
+ const placement = strokeParams.placement ?? 'center'
297
+ const strokeWidth = strokeParams.width ?? 0.1
298
+
299
+ let strokeRadius = radius
300
+ switch (placement) {
301
+ case 'inside':
302
+ strokeRadius = radius - strokeWidth / 2
303
+ break
304
+ case 'outside':
305
+ strokeRadius = radius + strokeWidth / 2
306
+ break
307
+ case 'center':
308
+ strokeRadius = radius
309
+ break
310
+ }
311
+
312
+ const path = new THREE.Path()
313
+ path.absarc(0, 0, strokeRadius, 0, Math.PI * 2)
314
+ const points = path.getPoints(100).map((p) => new THREE.Vector3(p.x, p.y, 0.001))
315
+
316
+ const lineGeometry = new LineGeometry()
317
+ lineGeometry.setPositions(points.flatMap((p) => [p.x, p.y, p.z]))
318
+
319
+ const lineMaterial = new LineMaterial({
320
+ color: strokeParams.color ?? 0x000000,
321
+ linewidth: strokeWidth,
322
+ worldUnits: true,
323
+ resolution:
324
+ strokeParams.resolution || new THREE.Vector2(window.innerWidth, window.innerHeight),
325
+ depthTest: false, // Disable depth checking
326
+ depthWrite: false, // Don't affect depth buffer
327
+ transparent: true // Allow overdraw
328
+ })
329
+
330
+ const stroke = new Line2(lineGeometry, lineMaterial as any)
331
+ stroke.renderOrder = 1 // Higher than default 0
332
+
333
+ // Make parent mesh's material depthWrite: true
334
+ if (mesh.material instanceof THREE.Material) {
335
+ mesh.material.depthWrite = true
336
+ }
337
+
338
+ mesh.add(stroke)
339
+ ;(mesh as any).stroke = stroke
340
+ }
341
+
342
+ return mesh
343
+ }
344
+
345
+ export const updateText = async (text: any, newText: string) => {
346
+ if (text.text === newText) return
347
+ text.text = newText
348
+
349
+ await new Promise<void>((resolve) => {
350
+ text.sync(() => {
351
+ resolve()
352
+ })
353
+ })
354
+
355
+ return
356
+ }
357
+
358
+ export const createFastText = async (text: string, size: number = 10, color: number = 0xffffff) => {
359
+ // Create a Troika Text instance
360
+ const textMesh = new Text()
361
+
362
+ // Set basic properties
363
+ textMesh.text = text
364
+ textMesh.fontSize = size
365
+ textMesh.color = color
366
+ textMesh.font = fontTroika
367
+
368
+ // Center the text
369
+ textMesh.anchorX = 'center'
370
+ textMesh.anchorY = 'middle'
371
+
372
+ // Create a wrapper object to maintain compatibility with your existing code
373
+
374
+ // Sync any changes and make text visible
375
+ await new Promise<void>((resolve) => {
376
+ textMesh.sync(() => {
377
+ resolve()
378
+ })
379
+ })
380
+
381
+ return textMesh
382
+ }
383
+
384
+ export const createMeshText = (text: string, size: number = 10, options?: ObjectOptions) => {
385
+ if (!loadedFont) {
386
+ loadFont()
387
+ }
388
+
389
+ const textOptions = {
390
+ font: loadedFont,
391
+ size: size,
392
+ depth: size / 10,
393
+ curveSegments: 12,
394
+ bevelEnabled: true,
395
+ bevelThickness: 0,
396
+ bevelSize: 1,
397
+ bevelOffset: 0,
398
+ bevelSegments: 5
399
+ }
400
+
401
+ const geometry = new TextGeometry(text, textOptions)
402
+
403
+ // Compute the bounding box
404
+ geometry.computeBoundingBox()
405
+
406
+ // Calculate the center offset based on the bounding box
407
+ const centerX = -0.5 * (geometry.boundingBox!.max.x + geometry.boundingBox!.min.x)
408
+ const centerY = -0.5 * (geometry.boundingBox!.max.y + geometry.boundingBox!.min.y)
409
+
410
+ // Apply the translation to center the text
411
+ geometry.translate(centerX, centerY, 0)
412
+
413
+ return createMesh(geometry, options)
414
+ }
415
+
416
+ export const createChars = (text: string, size: number = 10, options?: ObjectOptions) => {
417
+ if (!loadedFont) {
418
+ loadFont()
419
+ }
420
+
421
+ console.log('Font loaded:', loadedFont)
422
+
423
+ const letterSpacing = 0.1 * size // Default spacing is 5% of character size
424
+ const centerText = true
425
+
426
+ // Create a group to hold all character meshes
427
+ const textGroup = new THREE.Group()
428
+
429
+ // Track the total width to position characters correctly
430
+ let currentPosition = 0
431
+
432
+ // Create individual characters
433
+ for (let i = 0; i < text.length; i++) {
434
+ const char = text[i]
435
+
436
+ // Skip if it's a space, but add spacing
437
+ if (char === ' ') {
438
+ currentPosition += size * 0.5 // Space width is half the character size
439
+ continue
440
+ }
441
+
442
+ const textOptions = {
443
+ font: loadedFont,
444
+ size: size,
445
+ depth: 3,
446
+ curveSegments: 12,
447
+ bevelEnabled: true,
448
+ bevelThickness: 10,
449
+ bevelSize: 1,
450
+ bevelOffset: 0,
451
+ bevelSegments: 5
452
+ }
453
+
454
+ // Create geometry for this character
455
+ const geometry = new TextGeometry(char, textOptions)
456
+
457
+ // Compute bounding box for positioning
458
+ geometry.computeBoundingBox()
459
+
460
+ // Center the character vertically
461
+ const centerY = -0.5 * (geometry.boundingBox!.max.y + geometry.boundingBox!.min.y)
462
+
463
+ // We don't center horizontally - we'll position each character sequentially
464
+ geometry.translate(0, centerY, 0)
465
+
466
+ // Create mesh for this character
467
+ const charMesh = createMesh(geometry, options)
468
+
469
+ // Position character at the current offset
470
+ charMesh.position.x = currentPosition
471
+
472
+ // Add to group
473
+ textGroup.add(charMesh)
474
+
475
+ // Update position for next character
476
+ const charWidth = geometry.boundingBox!.max.x - geometry.boundingBox!.min.x
477
+ currentPosition += charWidth + letterSpacing
478
+ }
479
+
480
+ // Calculate total width (minus the last letter spacing)
481
+ const totalWidth = currentPosition - letterSpacing
482
+
483
+ // Center the entire text group if requested
484
+ if (centerText) {
485
+ textGroup.position.x = -totalWidth / 2
486
+ }
487
+
488
+ return textGroup
489
+ }