meshwriter-cudu 3.0.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 (81) hide show
  1. package/LICENSE.md +11 -0
  2. package/README.md +349 -0
  3. package/dist/fonts/comic-sans.d.ts +1105 -0
  4. package/dist/fonts/helvetica.d.ts +1208 -0
  5. package/dist/fonts/hiruko-pro.d.ts +658 -0
  6. package/dist/fonts/jura.d.ts +750 -0
  7. package/dist/fonts/webgl-dings.d.ts +109 -0
  8. package/dist/index.d.ts +295 -0
  9. package/dist/meshwriter.cjs.js +2645 -0
  10. package/dist/meshwriter.cjs.js.map +1 -0
  11. package/dist/meshwriter.esm.js +2606 -0
  12. package/dist/meshwriter.esm.js.map +1 -0
  13. package/dist/meshwriter.min.js +2 -0
  14. package/dist/meshwriter.min.js.map +1 -0
  15. package/dist/meshwriter.umd.js +7146 -0
  16. package/dist/meshwriter.umd.js.map +1 -0
  17. package/dist/src/babylonImports.d.ts +11 -0
  18. package/dist/src/bakedFontLoader.d.ts +43 -0
  19. package/dist/src/colorContrast.d.ts +117 -0
  20. package/dist/src/csg.d.ts +55 -0
  21. package/dist/src/curves.d.ts +20 -0
  22. package/dist/src/fogPlugin.d.ts +32 -0
  23. package/dist/src/fontCompression.d.ts +12 -0
  24. package/dist/src/fontRegistry.d.ts +54 -0
  25. package/dist/src/index.d.ts +47 -0
  26. package/dist/src/letterMesh.d.ts +46 -0
  27. package/dist/src/material.d.ts +34 -0
  28. package/dist/src/meshSplitter.d.ts +10 -0
  29. package/dist/src/meshwriter.d.ts +46 -0
  30. package/dist/src/sps.d.ts +27 -0
  31. package/dist/src/umd-entry.d.ts +3 -0
  32. package/dist/src/utils.d.ts +12 -0
  33. package/dist/src/variableFontCache.d.ts +56 -0
  34. package/dist/src/variableFontConverter.d.ts +21 -0
  35. package/dist/src/variableFontLoader.d.ts +99 -0
  36. package/fonts/Figure1.png +0 -0
  37. package/fonts/LICENSE-OFL.txt +93 -0
  38. package/fonts/README.md +174 -0
  39. package/fonts/atkinson-hyperlegible-next.d.ts +8 -0
  40. package/fonts/atkinson-hyperlegible-next.js +6576 -0
  41. package/fonts/atkinson-hyperlegible.js +3668 -0
  42. package/fonts/baked/atkinson-hyperlegible-next-200.json +1 -0
  43. package/fonts/baked/atkinson-hyperlegible-next-250.json +1 -0
  44. package/fonts/baked/atkinson-hyperlegible-next-300.json +1 -0
  45. package/fonts/baked/atkinson-hyperlegible-next-350.json +1 -0
  46. package/fonts/baked/atkinson-hyperlegible-next-400.json +1 -0
  47. package/fonts/baked/atkinson-hyperlegible-next-450.json +1 -0
  48. package/fonts/baked/atkinson-hyperlegible-next-500.json +1 -0
  49. package/fonts/baked/atkinson-hyperlegible-next-550.json +1 -0
  50. package/fonts/baked/atkinson-hyperlegible-next-600.json +1 -0
  51. package/fonts/baked/atkinson-hyperlegible-next-650.json +1 -0
  52. package/fonts/baked/atkinson-hyperlegible-next-700.json +1 -0
  53. package/fonts/baked/atkinson-hyperlegible-next-750.json +1 -0
  54. package/fonts/baked/atkinson-hyperlegible-next-800.json +1 -0
  55. package/fonts/baked/manifest.json +41 -0
  56. package/fonts/comic-sans.js +1532 -0
  57. package/fonts/helvetica.js +1695 -0
  58. package/fonts/hiruko-pro.js +838 -0
  59. package/fonts/index.js +16 -0
  60. package/fonts/jura.js +994 -0
  61. package/fonts/variable/atkinson-hyperlegible-next-variable.ttf +0 -0
  62. package/fonts/webgl-dings.js +113 -0
  63. package/package.json +76 -0
  64. package/src/babylonImports.js +29 -0
  65. package/src/bakedFontLoader.js +125 -0
  66. package/src/colorContrast.js +528 -0
  67. package/src/csg.js +220 -0
  68. package/src/curves.js +67 -0
  69. package/src/fogPlugin.js +98 -0
  70. package/src/fontCompression.js +141 -0
  71. package/src/fontRegistry.js +98 -0
  72. package/src/globals.d.ts +20 -0
  73. package/src/index.js +136 -0
  74. package/src/letterMesh.js +417 -0
  75. package/src/material.js +103 -0
  76. package/src/meshSplitter.js +337 -0
  77. package/src/meshwriter.js +303 -0
  78. package/src/sps.js +106 -0
  79. package/src/types.d.ts +551 -0
  80. package/src/umd-entry.js +130 -0
  81. package/src/utils.js +57 -0
package/src/csg.js ADDED
@@ -0,0 +1,220 @@
1
+ /**
2
+ * MeshWriter CSG Module
3
+ * Handles CSG version detection, async initialization, and ready state management
4
+ */
5
+
6
+ import { isObject, isPromiseLike } from './utils.js';
7
+ import {
8
+ CSG as BabylonCSG,
9
+ CSG2 as BabylonCSG2,
10
+ InitializeCSG2Async as BabylonInitializeCSG2Async,
11
+ IsCSG2Ready as BabylonIsCSG2Ready
12
+ } from './babylonImports.js';
13
+
14
+ // CSG libraries - initialized from Babylon imports or external source
15
+ let CSG = BabylonCSG;
16
+ let CSG2 = BabylonCSG2;
17
+ let InitializeCSG2Async = BabylonInitializeCSG2Async;
18
+ let IsCSG2Ready = BabylonIsCSG2Ready;
19
+
20
+ // State
21
+ let csgVersion = null; // 'CSG2', 'CSG', or null
22
+ let csgReady = false;
23
+ const csgReadyListeners = [];
24
+ let externalCSGInitializer = null;
25
+ let externalCSGReadyCheck = null;
26
+
27
+ /**
28
+ * Initialize CSG module with Babylon.js CSG classes
29
+ * Must be called before using any CSG functionality
30
+ * @param {Object} babylon - Object containing CSG, CSG2, InitializeCSG2Async, IsCSG2Ready
31
+ */
32
+ export function initCSGModule(babylon) {
33
+ if (isObject(babylon)) {
34
+ CSG = babylon.CSG || null;
35
+ CSG2 = babylon.CSG2 || null;
36
+ InitializeCSG2Async = babylon.InitializeCSG2Async || null;
37
+ IsCSG2Ready = babylon.IsCSG2Ready || null;
38
+ }
39
+ csgVersion = detectCSGVersion();
40
+
41
+ // Legacy CSG is immediately ready
42
+ if (csgVersion === 'CSG') {
43
+ markCSGInitialized();
44
+ } else if (csgVersion === 'CSG2') {
45
+ // Check if CSG2 is already initialized
46
+ csgReady = false;
47
+ if (runCSGReadyCheck()) {
48
+ markCSGInitialized();
49
+ }
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Detect which CSG implementation is available
55
+ * @returns {'CSG2'|'CSG'|null}
56
+ */
57
+ export function detectCSGVersion() {
58
+ // Prefer CSG2 (Babylon 7.31+)
59
+ if (isObject(CSG2) && typeof InitializeCSG2Async === 'function') {
60
+ return 'CSG2';
61
+ }
62
+ // Fall back to legacy CSG
63
+ if (isObject(CSG) && typeof CSG.FromMesh === 'function') {
64
+ return 'CSG';
65
+ }
66
+ return null;
67
+ }
68
+
69
+ /**
70
+ * Mark CSG as ready and notify all listeners
71
+ */
72
+ export function markCSGInitialized() {
73
+ if (csgReady) {
74
+ return;
75
+ }
76
+ csgReady = true;
77
+
78
+ // Notify all waiting listeners
79
+ if (csgReadyListeners.length) {
80
+ csgReadyListeners.splice(0).forEach(function(listener) {
81
+ try {
82
+ listener();
83
+ } catch (err) {
84
+ console.error("MeshWriter: onCSGReady listener failed", err);
85
+ }
86
+ });
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Check if CSG is ready for use
92
+ * @returns {boolean}
93
+ */
94
+ export function isCSGReady() {
95
+ if (csgVersion === 'CSG2') {
96
+ refreshCSGReadyState();
97
+ return csgReady;
98
+ }
99
+ return csgVersion === 'CSG';
100
+ }
101
+
102
+ /**
103
+ * Refresh CSG2 ready state from external checks
104
+ */
105
+ function refreshCSGReadyState() {
106
+ if (csgVersion !== 'CSG2' || csgReady) {
107
+ return;
108
+ }
109
+ if (runCSGReadyCheck()) {
110
+ markCSGInitialized();
111
+ }
112
+ }
113
+
114
+ /**
115
+ * Run CSG ready check (external or native)
116
+ */
117
+ function runCSGReadyCheck() {
118
+ // Try external ready check first
119
+ if (typeof externalCSGReadyCheck === "function") {
120
+ try {
121
+ if (externalCSGReadyCheck()) {
122
+ return true;
123
+ }
124
+ } catch (err) {
125
+ console.warn("MeshWriter: external CSG ready check failed", err);
126
+ }
127
+ }
128
+
129
+ // Check native IsCSG2Ready
130
+ if (typeof IsCSG2Ready === "function" && IsCSG2Ready()) {
131
+ return true;
132
+ }
133
+
134
+ return false;
135
+ }
136
+
137
+ /**
138
+ * Initialize CSG2 asynchronously
139
+ * @returns {Promise<void>}
140
+ */
141
+ export async function initializeCSG2() {
142
+ if (csgVersion !== 'CSG2') {
143
+ return;
144
+ }
145
+ if (csgReady) {
146
+ return;
147
+ }
148
+
149
+ const initializer = externalCSGInitializer || InitializeCSG2Async;
150
+ if (typeof initializer !== "function") {
151
+ throw new Error(
152
+ "MeshWriter: No CSG2 initializer available. " +
153
+ "Use MeshWriter.setCSGInitializer() or ensure BABYLON.InitializeCSG2Async is available."
154
+ );
155
+ }
156
+
157
+ const result = initializer();
158
+ if (isPromiseLike(result)) {
159
+ await result;
160
+ }
161
+ markCSGInitialized();
162
+ }
163
+
164
+ // Public API
165
+
166
+ /**
167
+ * Get the current CSG version being used
168
+ * @returns {'CSG2'|'CSG'|null}
169
+ */
170
+ export function getCSGVersion() {
171
+ return csgVersion;
172
+ }
173
+
174
+ /**
175
+ * Set an external CSG2 initializer function
176
+ * @param {Function} fn - Async function that initializes CSG2
177
+ */
178
+ export function setCSGInitializer(fn) {
179
+ if (typeof fn === "function") {
180
+ externalCSGInitializer = fn;
181
+ }
182
+ }
183
+
184
+ /**
185
+ * Set an external CSG2 ready check function
186
+ * @param {Function} fn - Function that returns true when CSG2 is ready
187
+ */
188
+ export function setCSGReadyCheck(fn) {
189
+ if (typeof fn === "function") {
190
+ externalCSGReadyCheck = fn;
191
+ refreshCSGReadyState();
192
+ }
193
+ }
194
+
195
+ /**
196
+ * Register a callback to be called when CSG is ready
197
+ * If CSG is already ready, callback is called immediately
198
+ * @param {Function} listener
199
+ */
200
+ export function onCSGReady(listener) {
201
+ if (typeof listener !== "function") {
202
+ return;
203
+ }
204
+ if (isCSGReady()) {
205
+ listener();
206
+ } else {
207
+ csgReadyListeners.push(listener);
208
+ }
209
+ }
210
+
211
+ /**
212
+ * Get the CSG library to use for operations
213
+ * @returns {Object} - CSG or CSG2 class
214
+ */
215
+ export function getCSGLib() {
216
+ return csgVersion === 'CSG2' ? CSG2 : CSG;
217
+ }
218
+
219
+ // Export for direct access when needed
220
+ export { CSG, CSG2 };
package/src/curves.js ADDED
@@ -0,0 +1,67 @@
1
+ /**
2
+ * MeshWriter Curve Extensions
3
+ * Optimized Path2 curve methods for better performance
4
+ */
5
+
6
+ import { Path2, Curve3, Vector3 } from './babylonImports.js';
7
+
8
+ /**
9
+ * Extended Path2 interface with MeshWriter curve methods
10
+ * @typedef {Object} Path2Extensions
11
+ * @property {(redX: number, redY: number, blueX: number, blueY: number) => Path2} addQuadraticCurveTo
12
+ * @property {(redX: number, redY: number, greenX: number, greenY: number, blueX: number, blueY: number) => Path2} addCubicCurveTo
13
+ */
14
+
15
+ // Optimized segment count for curves
16
+ // Native Babylon 6+ uses 36 segments which causes slowdown
17
+ // MeshWriter uses 6 for better performance
18
+ export const curveSampleSize = 6;
19
+
20
+ /**
21
+ * Install optimized curve methods on Path2 prototype
22
+ * Must be called after Babylon.js is loaded
23
+ */
24
+ export function installCurveExtensions() {
25
+ if (!Path2 || !Path2.prototype) {
26
+ return;
27
+ }
28
+
29
+ /** @type {any} */
30
+ const proto = Path2.prototype;
31
+
32
+ // Quadratic Bezier with optimized segment count
33
+ proto.addQuadraticCurveTo = function(redX, redY, blueX, blueY) {
34
+ const points = this.getPoints();
35
+ const lastPoint = points[points.length - 1];
36
+ const origin = new Vector3(lastPoint.x, lastPoint.y, 0);
37
+ const control = new Vector3(redX, redY, 0);
38
+ const destination = new Vector3(blueX, blueY, 0);
39
+
40
+ const curve = Curve3.CreateQuadraticBezier(origin, control, destination, curveSampleSize);
41
+ const curvePoints = curve.getPoints();
42
+
43
+ for (let i = 1; i < curvePoints.length; i++) {
44
+ this.addLineTo(curvePoints[i].x, curvePoints[i].y);
45
+ }
46
+ return this; // Return this for method chaining
47
+ };
48
+
49
+ // Cubic Bezier with optimized segment count
50
+ proto.addCubicCurveTo = function(redX, redY, greenX, greenY, blueX, blueY) {
51
+ const points = this.getPoints();
52
+ const lastPoint = points[points.length - 1];
53
+ const origin = new Vector3(lastPoint.x, lastPoint.y, 0);
54
+ const control1 = new Vector3(redX, redY, 0);
55
+ const control2 = new Vector3(greenX, greenY, 0);
56
+ const destination = new Vector3(blueX, blueY, 0);
57
+
58
+ const nbPoints = Math.floor(0.3 + curveSampleSize * 1.5);
59
+ const curve = Curve3.CreateCubicBezier(origin, control1, control2, destination, nbPoints);
60
+ const curvePoints = curve.getPoints();
61
+
62
+ for (let i = 1; i < curvePoints.length; i++) {
63
+ this.addLineTo(curvePoints[i].x, curvePoints[i].y);
64
+ }
65
+ return this; // Return this for method chaining
66
+ };
67
+ }
@@ -0,0 +1,98 @@
1
+ /**
2
+ * TextFogPlugin - MaterialPluginBase that applies fog to emissive color
3
+ *
4
+ * Babylon's standard fog only affects diffuse/ambient channels.
5
+ * This plugin recalculates fog blending for the final color output,
6
+ * ensuring emissive text fades properly with distance fog.
7
+ */
8
+
9
+ import { MaterialPluginBase } from './babylonImports.js';
10
+
11
+ /**
12
+ * Plugin that applies scene fog to text materials by modifying
13
+ * the final fragment color before output.
14
+ */
15
+ export class TextFogPlugin extends MaterialPluginBase {
16
+ /**
17
+ * @param {import('@babylonjs/core/Materials/material').Material} material
18
+ */
19
+ constructor(material) {
20
+ var priority = 300; // Run after standard material processing
21
+ var defines = { 'MESHWRITER_TEXT_FOG': false };
22
+ super(material, 'TextFogPlugin', priority, defines);
23
+ this._enable(true);
24
+ }
25
+
26
+ /**
27
+ * Set the define based on whether scene fog is enabled
28
+ * @param {object} defines
29
+ * @param {import('@babylonjs/core/scene').Scene} scene
30
+ * @param {import('@babylonjs/core/Meshes/mesh').Mesh} mesh
31
+ */
32
+ prepareDefines(defines, scene, mesh) {
33
+ // Enable when scene has any fog mode set (1=LINEAR, 2=EXP, 3=EXP2)
34
+ defines['MESHWRITER_TEXT_FOG'] = scene.fogMode !== 0;
35
+ }
36
+
37
+ getClassName() {
38
+ return 'TextFogPlugin';
39
+ }
40
+
41
+ getUniforms() {
42
+ // We use Babylon's built-in fog uniforms (vFogInfos, vFogColor)
43
+ // which are already available in the standard material
44
+ return { ubo: [] };
45
+ }
46
+
47
+ /**
48
+ * Clean up the plugin
49
+ */
50
+ dispose() {
51
+ super.dispose();
52
+ }
53
+
54
+ /**
55
+ * Inject shader code to apply fog to emissive color
56
+ * @param {string} shaderType - 'vertex' or 'fragment'
57
+ */
58
+ getCustomCode(shaderType) {
59
+ if (shaderType === 'fragment') {
60
+ return {
61
+ // This injection point runs just before gl_FragColor is finalized
62
+ // At this point, standard fog has been applied to diffuse/ambient
63
+ // but emissive contribution bypasses fog, so we re-apply fog
64
+ // to the entire output to properly fade text into fog
65
+ 'CUSTOM_FRAGMENT_BEFORE_FRAGCOLOR': `
66
+ #ifdef MESHWRITER_TEXT_FOG
67
+ #ifdef FOG
68
+ // Recalculate fog for the full fragment color including emissive
69
+ // vFogInfos: x=fogMode, y=fogStart, z=fogEnd, w=fogDensity
70
+ // vFogColor: fog RGB color
71
+ // vFogDistance: vec3 distance from camera (set by vertex shader)
72
+
73
+ float textFogFactor = 1.0;
74
+ float textFogDist = length(vFogDistance);
75
+
76
+ if (FOGMODE_LINEAR == vFogInfos.x) {
77
+ // Linear fog: factor = (end - dist) / (end - start)
78
+ textFogFactor = clamp((vFogInfos.z - textFogDist) / (vFogInfos.z - vFogInfos.y), 0.0, 1.0);
79
+ } else if (FOGMODE_EXP == vFogInfos.x) {
80
+ // Exponential fog: factor = exp(-dist * density)
81
+ textFogFactor = clamp(exp(-textFogDist * vFogInfos.w), 0.0, 1.0);
82
+ } else if (FOGMODE_EXP2 == vFogInfos.x) {
83
+ // Exponential squared fog: factor = exp(-(dist * density)^2)
84
+ float fogDistDensity = textFogDist * vFogInfos.w;
85
+ textFogFactor = clamp(exp(-fogDistDensity * fogDistDensity), 0.0, 1.0);
86
+ }
87
+
88
+ // Blend the entire fragment (including emissive) toward fog color
89
+ // textFogFactor: 1.0 = no fog (full color), 0.0 = full fog
90
+ color.rgb = mix(vFogColor, color.rgb, textFogFactor);
91
+ #endif
92
+ #endif
93
+ `,
94
+ };
95
+ }
96
+ return null;
97
+ }
98
+ }
@@ -0,0 +1,141 @@
1
+ /**
2
+ * MeshWriter Font Compression/Decompression
3
+ * Base-128 encoding for font data compression (~50% size reduction)
4
+ */
5
+
6
+ import { isArray } from './utils.js';
7
+
8
+ const floor = Math.floor;
9
+
10
+ // Encoding arrays (initialized once at module load)
11
+ let b128back;
12
+ let b128digits;
13
+
14
+ /**
15
+ * Initialize encoding arrays for base-128 conversion
16
+ * Called once when module loads
17
+ */
18
+ function prepArray() {
19
+ let pntr = -1;
20
+ let n;
21
+ b128back = new Uint8Array(256);
22
+ b128digits = new Array(128);
23
+
24
+ while (160 > pntr++) {
25
+ if (pntr < 128) {
26
+ n = fr128to256(pntr);
27
+ b128digits[pntr] = String.fromCharCode(n);
28
+ b128back[n] = pntr;
29
+ } else {
30
+ if (pntr === 128) {
31
+ b128back[32] = pntr;
32
+ } else {
33
+ b128back[pntr + 71] = pntr;
34
+ }
35
+ }
36
+ }
37
+
38
+ function fr128to256(n) {
39
+ if (n < 92) {
40
+ return n < 58 ? n < 6 ? n + 33 : n + 34 : n + 35;
41
+ } else {
42
+ return n + 69;
43
+ }
44
+ }
45
+ }
46
+
47
+ // Initialize on module load
48
+ prepArray();
49
+
50
+ /**
51
+ * Convert base-128 encoded string to number
52
+ */
53
+ function frB128(s) {
54
+ let result = 0;
55
+ let i = -1;
56
+ const l = s.length - 1;
57
+ while (i++ < l) {
58
+ result = result * 128 + b128back[s.charCodeAt(i)];
59
+ }
60
+ return result;
61
+ }
62
+
63
+ /**
64
+ * Convert number to base-128 encoded string
65
+ */
66
+ function toB128(i) {
67
+ let s = b128digits[(i % 128)];
68
+ i = floor(i / 128);
69
+ while (i > 0) {
70
+ s = b128digits[(i % 128)] + s;
71
+ i = floor(i / 128);
72
+ }
73
+ return s;
74
+ }
75
+
76
+ /**
77
+ * Decode a compressed command list string
78
+ * @param {string} str - Compressed string (space-separated encoded commands)
79
+ * @returns {Array} - Array of decoded command arrays
80
+ */
81
+ export function decodeList(str) {
82
+ const split = str.split(" ");
83
+ const list = [];
84
+
85
+ split.forEach(function(cmds) {
86
+ if (cmds.length === 12) { list.push(decode6(cmds)); }
87
+ if (cmds.length === 8) { list.push(decode4(cmds)); }
88
+ if (cmds.length === 4) { list.push(decode2(cmds)); }
89
+ });
90
+
91
+ return list;
92
+
93
+ function decode6(s) {
94
+ return [
95
+ decode1(s, 0, 2), decode1(s, 2, 4), decode1(s, 4, 6),
96
+ decode1(s, 6, 8), decode1(s, 8, 10), decode1(s, 10, 12)
97
+ ];
98
+ }
99
+ function decode4(s) {
100
+ return [decode1(s, 0, 2), decode1(s, 2, 4), decode1(s, 4, 6), decode1(s, 6, 8)];
101
+ }
102
+ function decode2(s) {
103
+ return [decode1(s, 0, 2), decode1(s, 2, 4)];
104
+ }
105
+ function decode1(s, start, end) {
106
+ return (frB128(s.substring(start, end)) - 4000) / 2;
107
+ }
108
+ }
109
+
110
+ /**
111
+ * Encode a command list to compressed string
112
+ * @param {Array} list - Array of command arrays
113
+ * @returns {string} - Compressed string
114
+ */
115
+ export function codeList(list) {
116
+ let str = "";
117
+ let xtra = "";
118
+
119
+ if (isArray(list)) {
120
+ list.forEach(function(cmds) {
121
+ if (cmds.length === 6) { str += xtra + code6(cmds); xtra = " "; }
122
+ if (cmds.length === 4) { str += xtra + code4(cmds); xtra = " "; }
123
+ if (cmds.length === 2) { str += xtra + code2(cmds); xtra = " "; }
124
+ });
125
+ }
126
+
127
+ return str;
128
+
129
+ function code6(a) {
130
+ return code1(a[0]) + code1(a[1]) + code1(a[2]) + code1(a[3]) + code1(a[4]) + code1(a[5]);
131
+ }
132
+ function code4(a) {
133
+ return code1(a[0]) + code1(a[1]) + code1(a[2]) + code1(a[3]);
134
+ }
135
+ function code2(a) {
136
+ return code1(a[0]) + code1(a[1]);
137
+ }
138
+ function code1(n) {
139
+ return toB128((n + n) + 4000);
140
+ }
141
+ }
@@ -0,0 +1,98 @@
1
+ /**
2
+ * MeshWriter Font Registry
3
+ * Manages font registration and lookup
4
+ */
5
+
6
+ import { isObject } from './utils.js';
7
+ import { codeList, decodeList } from './fontCompression.js';
8
+
9
+ // Private font storage
10
+ const FONTS = {};
11
+
12
+ /**
13
+ * Register a font for use with MeshWriter
14
+ * @param {string} name - Font name (case-sensitive, used in "font-family" option)
15
+ * @param {Function|Object} fontData - Font factory function or pre-initialized font object
16
+ *
17
+ * @example
18
+ * // Register a font factory (receives codeList for encoding)
19
+ * import helvetica from 'meshwriter/fonts/helvetica';
20
+ * registerFont('Helvetica', helvetica);
21
+ *
22
+ * // Register with aliases
23
+ * registerFont('Arial', helvetica);
24
+ * registerFont('sans-serif', helvetica);
25
+ */
26
+ export function registerFont(name, fontData) {
27
+ if (typeof fontData === 'function') {
28
+ // Font is a factory function expecting codeList
29
+ FONTS[name] = fontData(codeList);
30
+ } else if (isObject(fontData)) {
31
+ // Font is already initialized
32
+ FONTS[name] = fontData;
33
+ } else {
34
+ throw new Error(`MeshWriter: Invalid font data for "${name}"`);
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Register multiple font aliases pointing to the same font
40
+ * @param {string} targetName - Name of already-registered font
41
+ * @param {...string} aliases - Alias names to register
42
+ *
43
+ * @example
44
+ * registerFont('Helvetica', helveticaData);
45
+ * registerFontAliases('Helvetica', 'Arial', 'sans-serif');
46
+ */
47
+ export function registerFontAliases(targetName, ...aliases) {
48
+ if (!FONTS[targetName]) {
49
+ throw new Error(`MeshWriter: Cannot create aliases: font "${targetName}" not registered`);
50
+ }
51
+ aliases.forEach(alias => {
52
+ FONTS[alias] = FONTS[targetName];
53
+ });
54
+ }
55
+
56
+ /**
57
+ * Get a registered font by name
58
+ * @param {string} name - Font name
59
+ * @returns {Object|undefined} - Font object or undefined if not found
60
+ */
61
+ export function getFont(name) {
62
+ return FONTS[name];
63
+ }
64
+
65
+ /**
66
+ * Check if a font is registered
67
+ * @param {string} name - Font name
68
+ * @returns {boolean}
69
+ */
70
+ export function isFontRegistered(name) {
71
+ return isObject(FONTS[name]);
72
+ }
73
+
74
+ /**
75
+ * Get list of all registered font names
76
+ * @returns {string[]}
77
+ */
78
+ export function getRegisteredFonts() {
79
+ return Object.keys(FONTS);
80
+ }
81
+
82
+ /**
83
+ * Unregister a font (mainly for testing)
84
+ * @param {string} name - Font name to remove
85
+ */
86
+ export function unregisterFont(name) {
87
+ delete FONTS[name];
88
+ }
89
+
90
+ /**
91
+ * Clear all registered fonts (mainly for testing)
92
+ */
93
+ export function clearFonts() {
94
+ Object.keys(FONTS).forEach(key => delete FONTS[key]);
95
+ }
96
+
97
+ // Re-export compression utilities for font authors
98
+ export { codeList, decodeList };
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Global type declarations for MeshWriter UMD builds
3
+ * Include this file if using MeshWriter via script tag
4
+ */
5
+
6
+ import type { MeshWriterStatic } from './types';
7
+
8
+ declare global {
9
+ /** Global MeshWriter (UMD bundle) */
10
+ var MeshWriter: MeshWriterStatic | undefined;
11
+ /** Legacy name for MeshWriter (UMD bundle) */
12
+ var TYPE: MeshWriterStatic | undefined;
13
+
14
+ namespace BABYLON {
15
+ /** MeshWriter attached to BABYLON namespace (UMD bundle) */
16
+ var MeshWriter: MeshWriterStatic | undefined;
17
+ }
18
+ }
19
+
20
+ export {};