@faicad/faijs-extra 0.16.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 (99) hide show
  1. package/LICENSE +29 -0
  2. package/NOTICE +29 -0
  3. package/dist/browser.d.ts +12 -0
  4. package/dist/browser.d.ts.map +1 -0
  5. package/dist/browser.js +12 -0
  6. package/dist/browser.js.map +1 -0
  7. package/dist/editor-ops.d.ts +50 -0
  8. package/dist/editor-ops.d.ts.map +1 -0
  9. package/dist/editor-ops.js +77 -0
  10. package/dist/editor-ops.js.map +1 -0
  11. package/dist/extras/svg-extrude.d.ts +43 -0
  12. package/dist/extras/svg-extrude.d.ts.map +1 -0
  13. package/dist/extras/svg-extrude.js +78 -0
  14. package/dist/extras/svg-extrude.js.map +1 -0
  15. package/dist/extras/text-cjk.d.ts +36 -0
  16. package/dist/extras/text-cjk.d.ts.map +1 -0
  17. package/dist/extras/text-cjk.js +146 -0
  18. package/dist/extras/text-cjk.js.map +1 -0
  19. package/dist/extras/text-geometry.d.ts +47 -0
  20. package/dist/extras/text-geometry.d.ts.map +1 -0
  21. package/dist/extras/text-geometry.js +127 -0
  22. package/dist/extras/text-geometry.js.map +1 -0
  23. package/dist/index.d.ts +47 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +47 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/mesh/drill-hole/DrillHoleCore.d.ts +90 -0
  28. package/dist/mesh/drill-hole/DrillHoleCore.d.ts.map +1 -0
  29. package/dist/mesh/drill-hole/DrillHoleCore.js +287 -0
  30. package/dist/mesh/drill-hole/DrillHoleCore.js.map +1 -0
  31. package/dist/mesh/drill-hole/drill-types.d.ts +34 -0
  32. package/dist/mesh/drill-hole/drill-types.d.ts.map +1 -0
  33. package/dist/mesh/drill-hole/drill-types.js +7 -0
  34. package/dist/mesh/drill-hole/drill-types.js.map +1 -0
  35. package/dist/mesh/engrave-decoration.d.ts +13 -0
  36. package/dist/mesh/engrave-decoration.d.ts.map +1 -0
  37. package/dist/mesh/engrave-decoration.js +39 -0
  38. package/dist/mesh/engrave-decoration.js.map +1 -0
  39. package/dist/mesh/fai_drill.d.ts +21 -0
  40. package/dist/mesh/fai_drill.d.ts.map +1 -0
  41. package/dist/mesh/fai_drill.js +92 -0
  42. package/dist/mesh/fai_drill.js.map +1 -0
  43. package/dist/mesh/fai_extrude.d.ts +24 -0
  44. package/dist/mesh/fai_extrude.d.ts.map +1 -0
  45. package/dist/mesh/fai_extrude.js +42 -0
  46. package/dist/mesh/fai_extrude.js.map +1 -0
  47. package/dist/mesh/fai_split.d.ts +164 -0
  48. package/dist/mesh/fai_split.d.ts.map +1 -0
  49. package/dist/mesh/fai_split.js +269 -0
  50. package/dist/mesh/fai_split.js.map +1 -0
  51. package/dist/mesh/index.d.ts +57 -0
  52. package/dist/mesh/index.d.ts.map +1 -0
  53. package/dist/mesh/index.js +34 -0
  54. package/dist/mesh/index.js.map +1 -0
  55. package/dist/mesh/primitives.d.ts +17 -0
  56. package/dist/mesh/primitives.d.ts.map +1 -0
  57. package/dist/mesh/primitives.js +53 -0
  58. package/dist/mesh/primitives.js.map +1 -0
  59. package/dist/namespace.d.ts +82 -0
  60. package/dist/namespace.d.ts.map +1 -0
  61. package/dist/namespace.js +109 -0
  62. package/dist/namespace.js.map +1 -0
  63. package/dist/op-names.d.ts +36 -0
  64. package/dist/op-names.d.ts.map +1 -0
  65. package/dist/op-names.js +45 -0
  66. package/dist/op-names.js.map +1 -0
  67. package/dist/ops/compound.d.ts +110 -0
  68. package/dist/ops/compound.d.ts.map +1 -0
  69. package/dist/ops/compound.js +164 -0
  70. package/dist/ops/compound.js.map +1 -0
  71. package/dist/ops/copy.d.ts +40 -0
  72. package/dist/ops/copy.d.ts.map +1 -0
  73. package/dist/ops/copy.js +83 -0
  74. package/dist/ops/copy.js.map +1 -0
  75. package/dist/ops/fai_drill.d.ts +46 -0
  76. package/dist/ops/fai_drill.d.ts.map +1 -0
  77. package/dist/ops/fai_drill.js +243 -0
  78. package/dist/ops/fai_drill.js.map +1 -0
  79. package/dist/ops/fai_extrude.d.ts +36 -0
  80. package/dist/ops/fai_extrude.d.ts.map +1 -0
  81. package/dist/ops/fai_extrude.js +79 -0
  82. package/dist/ops/fai_extrude.js.map +1 -0
  83. package/dist/ops/fai_split.d.ts +46 -0
  84. package/dist/ops/fai_split.d.ts.map +1 -0
  85. package/dist/ops/fai_split.js +255 -0
  86. package/dist/ops/fai_split.js.map +1 -0
  87. package/dist/ops/load.d.ts +41 -0
  88. package/dist/ops/load.d.ts.map +1 -0
  89. package/dist/ops/load.js +84 -0
  90. package/dist/ops/load.js.map +1 -0
  91. package/dist/ops/svg-extrude.d.ts +32 -0
  92. package/dist/ops/svg-extrude.d.ts.map +1 -0
  93. package/dist/ops/svg-extrude.js +74 -0
  94. package/dist/ops/svg-extrude.js.map +1 -0
  95. package/dist/ops/text.d.ts +34 -0
  96. package/dist/ops/text.d.ts.map +1 -0
  97. package/dist/ops/text.js +101 -0
  98. package/dist/ops/text.js.map +1 -0
  99. package/package.json +60 -0
package/LICENSE ADDED
@@ -0,0 +1,29 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Faicad contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
23
+ ---
24
+
25
+ Third-party components bundled in this distribution retain their own licenses:
26
+
27
+ - `src/vendored/brepjs` (compiled into `dist/vendored/**`): ported from the
28
+ brepjs project, licensed under the Apache License 2.0. See NOTICE for the
29
+ full attribution. Apache-2.0 text: https://www.apache.org/licenses/LICENSE-2.0
package/NOTICE ADDED
@@ -0,0 +1,29 @@
1
+ faijs — @faicad/faijs NOTICE
2
+ ============================
3
+
4
+ This product includes code ported from the `brepjs` project
5
+ (https://github.com/OpenCascade/brepjs), licensed under the Apache
6
+ License 2.0. Ported as of brepjs commit 8685273a (locked upstream
7
+ reference; no two-way sync). The ported files retain their original
8
+ Apache-2.0 attribution; see the vendored NOTICE in the source
9
+ repository (packages/core/src/vendored/brepjs/NOTICE).
10
+
11
+ Full Apache-2.0 text: https://www.apache.org/licenses/LICENSE-2.0
12
+
13
+ Runtime dependencies (not distributed inside this package)
14
+ ----------------------------------------------------------
15
+
16
+ The following optional/peer dependencies are loaded by the host at
17
+ runtime and are NOT distributed inside this tarball. They carry their
18
+ own licenses:
19
+
20
+ - `occt-wasm` — JS glue: MIT OR Apache-2.0; the compiled WASM output is
21
+ LGPL-2.1-only (inherited from OCCT, https://dev.opencascade.org).
22
+ The LGPL replacement obligation is satisfied because the host loads
23
+ the `.wasm` from a URL that can be overridden
24
+ (`OcctKernel.init({ wasm: '...' })`).
25
+ - `manifold-3d` — Apache-2.0.
26
+
27
+ `@salusoft89/planegcs` (LGPL-2.0-or-later) is used only by the
28
+ non-shipped `src/fcstd` module and is intentionally not a dependency of
29
+ the published package.
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @faicad/faijs-extra/browser — browser/worker entry.
3
+ *
4
+ * The extension library is environment-agnostic: its dependency graph is three
5
+ * (+ `three/examples` addons) and opentype.js, with no `node:*` module and no
6
+ * wasm loader. This entry therefore re-exports the main entry verbatim; it
7
+ * exists so hosts can import the extension library the same way they import
8
+ * core's `@faicad/faijs/browser`, and so the entry-boundary guard has an
9
+ * explicit symbol to pin.
10
+ */
11
+ export * from './index.js';
12
+ //# sourceMappingURL=browser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,cAAc,SAAS,CAAA"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @faicad/faijs-extra/browser — browser/worker entry.
3
+ *
4
+ * The extension library is environment-agnostic: its dependency graph is three
5
+ * (+ `three/examples` addons) and opentype.js, with no `node:*` module and no
6
+ * wasm loader. This entry therefore re-exports the main entry verbatim; it
7
+ * exists so hosts can import the extension library the same way they import
8
+ * core's `@faicad/faijs/browser`, and so the entry-boundary guard has an
9
+ * explicit symbol to pin.
10
+ */
11
+ export * from './index.js';
12
+ //# sourceMappingURL=browser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.js","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,cAAc,SAAS,CAAA","sourcesContent":["/**\n * @faicad/faijs-extra/browser — browser/worker entry.\n *\n * The extension library is environment-agnostic: its dependency graph is three\n * (+ `three/examples` addons) and opentype.js, with no `node:*` module and no\n * wasm loader. This entry therefore re-exports the main entry verbatim; it\n * exists so hosts can import the extension library the same way they import\n * core's `@faicad/faijs/browser`, and so the entry-boundary guard has an\n * explicit symbol to pin.\n */\nexport * from './index'\n"]}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * editor-ops — the editor op subgroup entry (`@faicad/faijs-extra/editor-ops`).
3
+ *
4
+ * Carries the plan §4.2 A group: `fai_drill` / `fai_extrude` / `fai_split`,
5
+ * `group` / `assembly`, `copy`, `load`. Unlike the full entry this module never
6
+ * reaches three's non-basic chain (`three/examples`' `SVGLoader`,
7
+ * `Shape`/`ExtrudeGeometry`) nor opentype.js — its dependency graph is three's
8
+ * math/container/basic-generator API only.
9
+ *
10
+ * That property is what lets the weapp worker mount these ops: the end side
11
+ * needs `cad.group` / `cad.copy` / `cad.load` to replay web-authored project
12
+ * scripts, but must not bundle the svg/3D-text chain (plan §3.1 G3). The guard
13
+ * `entry-boundary.test.ts` pins it.
14
+ */
15
+ import { type StdlibNamespace } from '@faicad/faijs/runtime-state';
16
+ /**
17
+ * Assemble the editor-owned ops into a `cad`-shaped namespace.
18
+ *
19
+ * @returns the editor op namespace, ready to be spread over the platform surface.
20
+ */
21
+ export declare function createEditorOpsNamespace(): StdlibNamespace;
22
+ /**
23
+ * Merge the editor op namespace over a platform namespace (pure).
24
+ * @param platform - the platform namespace (`createApiNamespace()`).
25
+ * @returns the merged namespace to register as the host's `cad` library.
26
+ */
27
+ export declare function mergeEditorOpsNamespace(platform: StdlibNamespace): StdlibNamespace;
28
+ /**
29
+ * The `cad` namespace a three-basic-only host registers: core's platform
30
+ * surface plus the editor-owned ops (no svg/3D-text creators).
31
+ *
32
+ * This is the entry the weapp worker uses — it must not bundle
33
+ * `three/examples`' `SVGLoader` nor the `Shape`/`ExtrudeGeometry` chain.
34
+ *
35
+ * @returns the namespace to hand to `registerLib('cad', …)`.
36
+ */
37
+ export declare function createEditorOwnedCadNamespace(): StdlibNamespace;
38
+ /** Register the A-group op names in the core symbol table (P5). */
39
+ export declare function registerEditorOpsSymbols(): void;
40
+ /** Undo `registerEditorOpsSymbols()`. */
41
+ export declare function unregisterEditorOpsSymbols(): void;
42
+ export { EDITOR_OPS } from './op-names.js';
43
+ export { fai_drill } from './ops/fai_drill.js';
44
+ export { fai_extrude } from './ops/fai_extrude.js';
45
+ export { fai_split } from './ops/fai_split.js';
46
+ export { group, assembly } from './ops/compound.js';
47
+ export type { GroupParams, AssemblyParams, AssemblyBehavior } from './ops/compound.js';
48
+ export { copy } from './ops/copy.js';
49
+ export { load } from './ops/load.js';
50
+ //# sourceMappingURL=editor-ops.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"editor-ops.d.ts","sourceRoot":"","sources":["../src/editor-ops.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAoB,KAAK,eAAe,EAAE,MAAM,6BAA6B,CAAA;AAWpF;;;;GAIG;AACH,wBAAgB,wBAAwB,IAAI,eAAe,CAW1D;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,eAAe,GAAG,eAAe,CAElF;AAED;;;;;;;;GAQG;AACH,wBAAgB,6BAA6B,IAAI,eAAe,CAE/D;AAED,mEAAmE;AACnE,wBAAgB,wBAAwB,IAAI,IAAI,CAE/C;AAED,yCAAyC;AACzC,wBAAgB,0BAA0B,IAAI,IAAI,CAEjD;AAED,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AACvC,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAChD,YAAY,EAAE,WAAW,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AACnF,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA"}
@@ -0,0 +1,77 @@
1
+ /**
2
+ * editor-ops — the editor op subgroup entry (`@faicad/faijs-extra/editor-ops`).
3
+ *
4
+ * Carries the plan §4.2 A group: `fai_drill` / `fai_extrude` / `fai_split`,
5
+ * `group` / `assembly`, `copy`, `load`. Unlike the full entry this module never
6
+ * reaches three's non-basic chain (`three/examples`' `SVGLoader`,
7
+ * `Shape`/`ExtrudeGeometry`) nor opentype.js — its dependency graph is three's
8
+ * math/container/basic-generator API only.
9
+ *
10
+ * That property is what lets the weapp worker mount these ops: the end side
11
+ * needs `cad.group` / `cad.copy` / `cad.load` to replay web-authored project
12
+ * scripts, but must not bundle the svg/3D-text chain (plan §3.1 G3). The guard
13
+ * `entry-boundary.test.ts` pins it.
14
+ */
15
+ import { CONTRACT_VERSION } from '@faicad/faijs/runtime-state';
16
+ import { registerSymbolTableEntries, unregisterSymbolTableEntries } from '@faicad/faijs/symbol-table';
17
+ import { createApiNamespace } from '@faicad/faijs/api/api-namespace';
18
+ import { EDITOR_OPS } from './op-names.js';
19
+ import { fai_drill } from './ops/fai_drill.js';
20
+ import { fai_extrude } from './ops/fai_extrude.js';
21
+ import { fai_split } from './ops/fai_split.js';
22
+ import { group, assembly } from './ops/compound.js';
23
+ import { copy } from './ops/copy.js';
24
+ import { load } from './ops/load.js';
25
+ /**
26
+ * Assemble the editor-owned ops into a `cad`-shaped namespace.
27
+ *
28
+ * @returns the editor op namespace, ready to be spread over the platform surface.
29
+ */
30
+ export function createEditorOpsNamespace() {
31
+ return {
32
+ contractVersion: CONTRACT_VERSION,
33
+ fai_drill,
34
+ fai_extrude,
35
+ fai_split,
36
+ group,
37
+ assembly,
38
+ copy,
39
+ load,
40
+ };
41
+ }
42
+ /**
43
+ * Merge the editor op namespace over a platform namespace (pure).
44
+ * @param platform - the platform namespace (`createApiNamespace()`).
45
+ * @returns the merged namespace to register as the host's `cad` library.
46
+ */
47
+ export function mergeEditorOpsNamespace(platform) {
48
+ return { ...platform, ...createEditorOpsNamespace() };
49
+ }
50
+ /**
51
+ * The `cad` namespace a three-basic-only host registers: core's platform
52
+ * surface plus the editor-owned ops (no svg/3D-text creators).
53
+ *
54
+ * This is the entry the weapp worker uses — it must not bundle
55
+ * `three/examples`' `SVGLoader` nor the `Shape`/`ExtrudeGeometry` chain.
56
+ *
57
+ * @returns the namespace to hand to `registerLib('cad', …)`.
58
+ */
59
+ export function createEditorOwnedCadNamespace() {
60
+ return mergeEditorOpsNamespace(createApiNamespace());
61
+ }
62
+ /** Register the A-group op names in the core symbol table (P5). */
63
+ export function registerEditorOpsSymbols() {
64
+ registerSymbolTableEntries(EDITOR_OPS);
65
+ }
66
+ /** Undo `registerEditorOpsSymbols()`. */
67
+ export function unregisterEditorOpsSymbols() {
68
+ unregisterSymbolTableEntries(EDITOR_OPS);
69
+ }
70
+ export { EDITOR_OPS } from './op-names.js';
71
+ export { fai_drill } from './ops/fai_drill.js';
72
+ export { fai_extrude } from './ops/fai_extrude.js';
73
+ export { fai_split } from './ops/fai_split.js';
74
+ export { group, assembly } from './ops/compound.js';
75
+ export { copy } from './ops/copy.js';
76
+ export { load } from './ops/load.js';
77
+ //# sourceMappingURL=editor-ops.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"editor-ops.js","sourceRoot":"","sources":["../src/editor-ops.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,gBAAgB,EAAwB,MAAM,6BAA6B,CAAA;AACpF,OAAO,EAAE,0BAA0B,EAAE,4BAA4B,EAAE,MAAM,4BAA4B,CAAA;AACrG,OAAO,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAA;AACpE,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AACvC,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAChD,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AAEjC;;;;GAIG;AACH,MAAM,UAAU,wBAAwB;IACtC,OAAO;QACL,eAAe,EAAE,gBAAgB;QACjC,SAAS;QACT,WAAW;QACX,SAAS;QACT,KAAK;QACL,QAAQ;QACR,IAAI;QACJ,IAAI;KACyB,CAAA;AACjC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,uBAAuB,CAAC,QAAyB;IAC/D,OAAO,EAAE,GAAG,QAAQ,EAAE,GAAG,wBAAwB,EAAE,EAAE,CAAA;AACvD,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,6BAA6B;IAC3C,OAAO,uBAAuB,CAAC,kBAAkB,EAAE,CAAC,CAAA;AACtD,CAAC;AAED,mEAAmE;AACnE,MAAM,UAAU,wBAAwB;IACtC,0BAA0B,CAAC,UAAU,CAAC,CAAA;AACxC,CAAC;AAED,yCAAyC;AACzC,MAAM,UAAU,0BAA0B;IACxC,4BAA4B,CAAC,UAAU,CAAC,CAAA;AAC1C,CAAC;AAED,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AACvC,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAEhD,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA","sourcesContent":["/**\n * editor-ops — the editor op subgroup entry (`@faicad/faijs-extra/editor-ops`).\n *\n * Carries the plan §4.2 A group: `fai_drill` / `fai_extrude` / `fai_split`,\n * `group` / `assembly`, `copy`, `load`. Unlike the full entry this module never\n * reaches three's non-basic chain (`three/examples`' `SVGLoader`,\n * `Shape`/`ExtrudeGeometry`) nor opentype.js — its dependency graph is three's\n * math/container/basic-generator API only.\n *\n * That property is what lets the weapp worker mount these ops: the end side\n * needs `cad.group` / `cad.copy` / `cad.load` to replay web-authored project\n * scripts, but must not bundle the svg/3D-text chain (plan §3.1 G3). The guard\n * `entry-boundary.test.ts` pins it.\n */\nimport { CONTRACT_VERSION, type StdlibNamespace } from '@faicad/faijs/runtime-state'\nimport { registerSymbolTableEntries, unregisterSymbolTableEntries } from '@faicad/faijs/symbol-table'\nimport { createApiNamespace } from '@faicad/faijs/api/api-namespace'\nimport { EDITOR_OPS } from './op-names'\nimport { fai_drill } from './ops/fai_drill'\nimport { fai_extrude } from './ops/fai_extrude'\nimport { fai_split } from './ops/fai_split'\nimport { group, assembly } from './ops/compound'\nimport { copy } from './ops/copy'\nimport { load } from './ops/load'\n\n/**\n * Assemble the editor-owned ops into a `cad`-shaped namespace.\n *\n * @returns the editor op namespace, ready to be spread over the platform surface.\n */\nexport function createEditorOpsNamespace(): StdlibNamespace {\n return {\n contractVersion: CONTRACT_VERSION,\n fai_drill,\n fai_extrude,\n fai_split,\n group,\n assembly,\n copy,\n load,\n } as unknown as StdlibNamespace\n}\n\n/**\n * Merge the editor op namespace over a platform namespace (pure).\n * @param platform - the platform namespace (`createApiNamespace()`).\n * @returns the merged namespace to register as the host's `cad` library.\n */\nexport function mergeEditorOpsNamespace(platform: StdlibNamespace): StdlibNamespace {\n return { ...platform, ...createEditorOpsNamespace() }\n}\n\n/**\n * The `cad` namespace a three-basic-only host registers: core's platform\n * surface plus the editor-owned ops (no svg/3D-text creators).\n *\n * This is the entry the weapp worker uses — it must not bundle\n * `three/examples`' `SVGLoader` nor the `Shape`/`ExtrudeGeometry` chain.\n *\n * @returns the namespace to hand to `registerLib('cad', …)`.\n */\nexport function createEditorOwnedCadNamespace(): StdlibNamespace {\n return mergeEditorOpsNamespace(createApiNamespace())\n}\n\n/** Register the A-group op names in the core symbol table (P5). */\nexport function registerEditorOpsSymbols(): void {\n registerSymbolTableEntries(EDITOR_OPS)\n}\n\n/** Undo `registerEditorOpsSymbols()`. */\nexport function unregisterEditorOpsSymbols(): void {\n unregisterSymbolTableEntries(EDITOR_OPS)\n}\n\nexport { EDITOR_OPS } from './op-names'\nexport { fai_drill } from './ops/fai_drill'\nexport { fai_extrude } from './ops/fai_extrude'\nexport { fai_split } from './ops/fai_split'\nexport { group, assembly } from './ops/compound'\nexport type { GroupParams, AssemblyParams, AssemblyBehavior } from './ops/compound'\nexport { copy } from './ops/copy'\nexport { load } from './ops/load'\n"]}
@@ -0,0 +1,43 @@
1
+ import * as THREE from 'three';
2
+ /**
3
+ * Options controlling an SVG extrusion.
4
+ *
5
+ * targetLongSide maps the longer logical side of the SVG to this length in
6
+ * mm (the short side is scaled proportionally); naturalWidth/naturalHeight
7
+ * are the SVG's logical dimensions used to compute the scale factor.
8
+ */
9
+ export interface SvgExtrudeOptions {
10
+ depth: number;
11
+ /** 长边目标长度(mm),短边按比例计算。默认 20 */
12
+ targetLongSide?: number;
13
+ /** SVG 原始逻辑尺寸,用于计算实际缩放比例 */
14
+ naturalWidth: number;
15
+ naturalHeight: number;
16
+ }
17
+ /**
18
+ * Parse SVG text into a list of THREE.Shape contours.
19
+ * Uses SVGLoader to convert the SVG paths into shapes usable by ExtrudeGeometry.
20
+ *
21
+ * @param svgText - the SVG source text.
22
+ * @returns the list of closed shapes found in the SVG.
23
+ * @throws when the SVG contains no closed contour.
24
+ */
25
+ export declare function parseSvgShapes(svgText: string): THREE.Shape[];
26
+ /**
27
+ * Extrude a list of shapes into a single merged BufferGeometry.
28
+ * The scale factor is computed from naturalWidth/naturalHeight and targetLongSide.
29
+ *
30
+ * @param shapes - the shapes to extrude.
31
+ * @param options - the extrusion options (depth and scale inputs).
32
+ * @returns the merged, scaled, centred BufferGeometry.
33
+ */
34
+ export declare function extrudeShapes(shapes: THREE.Shape[], options: SvgExtrudeOptions): THREE.BufferGeometry;
35
+ /**
36
+ * One-stop helper: SVG text → extruded BufferGeometry.
37
+ *
38
+ * @param svgText - the SVG source text.
39
+ * @param options - the extrusion options.
40
+ * @returns the extruded BufferGeometry.
41
+ */
42
+ export declare function svgToExtrudedGeometry(svgText: string, options: SvgExtrudeOptions): THREE.BufferGeometry;
43
+ //# sourceMappingURL=svg-extrude.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"svg-extrude.d.ts","sourceRoot":"","sources":["../../src/extras/svg-extrude.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAI9B;;;;;;GAMG;AACH,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAA;IACb,+BAA+B;IAC/B,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,4BAA4B;IAC5B,YAAY,EAAE,MAAM,CAAA;IACpB,aAAa,EAAE,MAAM,CAAA;CACtB;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,KAAK,CAAC,KAAK,EAAE,CAiB7D;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,KAAK,CAAC,KAAK,EAAE,EACrB,OAAO,EAAE,iBAAiB,GACzB,KAAK,CAAC,cAAc,CAqCtB;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,iBAAiB,GACzB,KAAK,CAAC,cAAc,CAGtB"}
@@ -0,0 +1,78 @@
1
+ import * as THREE from 'three';
2
+ import { SVGLoader } from 'three/examples/jsm/loaders/SVGLoader.js';
3
+ import { mergeBufferGeometries } from '@faicad/faijs/primitives/mesh-primitives';
4
+ /**
5
+ * Parse SVG text into a list of THREE.Shape contours.
6
+ * Uses SVGLoader to convert the SVG paths into shapes usable by ExtrudeGeometry.
7
+ *
8
+ * @param svgText - the SVG source text.
9
+ * @returns the list of closed shapes found in the SVG.
10
+ * @throws when the SVG contains no closed contour.
11
+ */
12
+ export function parseSvgShapes(svgText) {
13
+ const loader = new SVGLoader();
14
+ const svgData = loader.parse(svgText);
15
+ const allShapes = [];
16
+ for (const path of svgData.paths) {
17
+ const shapes = SVGLoader.createShapes(path);
18
+ for (const shape of shapes) {
19
+ allShapes.push(shape);
20
+ }
21
+ }
22
+ if (allShapes.length === 0) {
23
+ throw new Error('No closed contour found in SVG. Please check the file content.');
24
+ }
25
+ return allShapes;
26
+ }
27
+ /**
28
+ * Extrude a list of shapes into a single merged BufferGeometry.
29
+ * The scale factor is computed from naturalWidth/naturalHeight and targetLongSide.
30
+ *
31
+ * @param shapes - the shapes to extrude.
32
+ * @param options - the extrusion options (depth and scale inputs).
33
+ * @returns the merged, scaled, centred BufferGeometry.
34
+ */
35
+ export function extrudeShapes(shapes, options) {
36
+ const { depth, naturalWidth, naturalHeight, targetLongSide = 20 } = options;
37
+ // 计算缩放比例:长边映射到 targetLongSide
38
+ const longSide = Math.max(naturalWidth, naturalHeight);
39
+ const scale = longSide > 0 ? targetLongSide / longSide : 1;
40
+ // 先挤出,再整体缩放几何体
41
+ const geometries = shapes.map((shape) => {
42
+ const geo = new THREE.ExtrudeGeometry(shape, {
43
+ depth,
44
+ bevelEnabled: false,
45
+ });
46
+ // 应用缩放
47
+ geo.scale(scale, scale, 1);
48
+ return geo;
49
+ });
50
+ // 合并所有几何体为一个
51
+ const merged = mergeBufferGeometries(geometries);
52
+ merged.computeVertexNormals();
53
+ // 居中处理:XY 居中,Z 底部对齐到 0(3D 打印放在热床上)
54
+ merged.computeBoundingBox();
55
+ if (merged.boundingBox) {
56
+ const cx = (merged.boundingBox.max.x + merged.boundingBox.min.x) / 2;
57
+ const cy = (merged.boundingBox.max.y + merged.boundingBox.min.y) / 2;
58
+ const cz = merged.boundingBox.min.z;
59
+ merged.translate(-cx, -cy, -cz);
60
+ }
61
+ // 清理临时 geometry
62
+ for (const g of geometries) {
63
+ g.dispose();
64
+ }
65
+ return merged;
66
+ }
67
+ /**
68
+ * One-stop helper: SVG text → extruded BufferGeometry.
69
+ *
70
+ * @param svgText - the SVG source text.
71
+ * @param options - the extrusion options.
72
+ * @returns the extruded BufferGeometry.
73
+ */
74
+ export function svgToExtrudedGeometry(svgText, options) {
75
+ const shapes = parseSvgShapes(svgText);
76
+ return extrudeShapes(shapes, options);
77
+ }
78
+ //# sourceMappingURL=svg-extrude.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"svg-extrude.js","sourceRoot":"","sources":["../../src/extras/svg-extrude.ts"],"names":[],"mappings":"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","sourcesContent":["import * as THREE from 'three'\nimport { SVGLoader, type SVGResult } from 'three/examples/jsm/loaders/SVGLoader.js'\nimport { mergeBufferGeometries } from '@faicad/faijs/primitives/mesh-primitives'\n\n/**\n * Options controlling an SVG extrusion.\n *\n * targetLongSide maps the longer logical side of the SVG to this length in\n * mm (the short side is scaled proportionally); naturalWidth/naturalHeight\n * are the SVG's logical dimensions used to compute the scale factor.\n */\nexport interface SvgExtrudeOptions {\n depth: number\n /** 长边目标长度(mm),短边按比例计算。默认 20 */\n targetLongSide?: number\n /** SVG 原始逻辑尺寸,用于计算实际缩放比例 */\n naturalWidth: number\n naturalHeight: number\n}\n\n/**\n * Parse SVG text into a list of THREE.Shape contours.\n * Uses SVGLoader to convert the SVG paths into shapes usable by ExtrudeGeometry.\n *\n * @param svgText - the SVG source text.\n * @returns the list of closed shapes found in the SVG.\n * @throws when the SVG contains no closed contour.\n */\nexport function parseSvgShapes(svgText: string): THREE.Shape[] {\n const loader = new SVGLoader()\n const svgData: SVGResult = loader.parse(svgText)\n\n const allShapes: THREE.Shape[] = []\n for (const path of svgData.paths) {\n const shapes = SVGLoader.createShapes(path)\n for (const shape of shapes) {\n allShapes.push(shape)\n }\n }\n\n if (allShapes.length === 0) {\n throw new Error('No closed contour found in SVG. Please check the file content.')\n }\n\n return allShapes\n}\n\n/**\n * Extrude a list of shapes into a single merged BufferGeometry.\n * The scale factor is computed from naturalWidth/naturalHeight and targetLongSide.\n *\n * @param shapes - the shapes to extrude.\n * @param options - the extrusion options (depth and scale inputs).\n * @returns the merged, scaled, centred BufferGeometry.\n */\nexport function extrudeShapes(\n shapes: THREE.Shape[],\n options: SvgExtrudeOptions,\n): THREE.BufferGeometry {\n const { depth, naturalWidth, naturalHeight, targetLongSide = 20 } = options\n\n // 计算缩放比例:长边映射到 targetLongSide\n const longSide = Math.max(naturalWidth, naturalHeight)\n const scale = longSide > 0 ? targetLongSide / longSide : 1\n\n // 先挤出,再整体缩放几何体\n const geometries = shapes.map((shape) => {\n const geo = new THREE.ExtrudeGeometry(shape, {\n depth,\n bevelEnabled: false,\n })\n // 应用缩放\n geo.scale(scale, scale, 1)\n return geo\n })\n\n // 合并所有几何体为一个\n const merged = mergeBufferGeometries(geometries)\n merged.computeVertexNormals()\n\n // 居中处理:XY 居中,Z 底部对齐到 0(3D 打印放在热床上)\n merged.computeBoundingBox()\n if (merged.boundingBox) {\n const cx = (merged.boundingBox.max.x + merged.boundingBox.min.x) / 2\n const cy = (merged.boundingBox.max.y + merged.boundingBox.min.y) / 2\n const cz = merged.boundingBox.min.z\n merged.translate(-cx, -cy, -cz)\n }\n\n // 清理临时 geometry\n for (const g of geometries) {\n g.dispose()\n }\n\n return merged\n}\n\n/**\n * One-stop helper: SVG text → extruded BufferGeometry.\n *\n * @param svgText - the SVG source text.\n * @param options - the extrusion options.\n * @returns the extruded BufferGeometry.\n */\nexport function svgToExtrudedGeometry(\n svgText: string,\n options: SvgExtrudeOptions,\n): THREE.BufferGeometry {\n const shapes = parseSvgShapes(svgText)\n return extrudeShapes(shapes, options)\n}"]}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * text-cjk — mixed CJK + Latin text geometry (editor extension library).
3
+ *
4
+ * Uses the Local Font Access API (window.queryLocalFonts) to load system CJK
5
+ * fonts, then parses glyph paths with opentype.js to create THREE.Shape →
6
+ * ExtrudeGeometry.
7
+ *
8
+ * When a CJK font is available, it is used for ALL characters (including Latin).
9
+ * When no CJK font is available, the default OpenSans Regular font (from
10
+ * fontRegistry) is used for all characters via opentype.js.
11
+ *
12
+ * The character classification / system-font loading half lives in core
13
+ * (`@faicad/faijs/primitives/text/cjk-font`) and is deliberately three-free.
14
+ */
15
+ import * as THREE from 'three';
16
+ import type { Font } from 'opentype.js';
17
+ /**
18
+ * Generate geometry for mixed text (CJK + Latin).
19
+ *
20
+ * When cjkFont is available, it is used for ALL characters (including Latin),
21
+ * since CJK fonts typically contain Latin glyphs.
22
+ *
23
+ * When cjkFont is null, the default OpenSans Regular font (from fontRegistry)
24
+ * is used for all characters via opentype.js — this is the same font used by
25
+ * the BREP path, ensuring glyph consistency.
26
+ *
27
+ * @param text Text string to render
28
+ * @param size Font size in mm
29
+ * @param depth Extrusion depth in mm
30
+ * @param cjkFont CJK font from system (null = use default font only)
31
+ * @param defaultFont Default opentype.js Font (OpenSans Regular from fontRegistry).
32
+ * If not provided, will be loaded via getOpentypeFont().
33
+ * @returns a promise resolving to the merged, centred geometry for the whole text.
34
+ */
35
+ export declare function createMixedTextGeometry(text: string, size: number, depth: number, cjkFont: Font | null, defaultFont?: Font): Promise<THREE.BufferGeometry>;
36
+ //# sourceMappingURL=text-cjk.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text-cjk.d.ts","sourceRoot":"","sources":["../../src/extras/text-cjk.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAI9B,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAA;AAwEvC;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,IAAI,GAAG,IAAI,EACpB,WAAW,CAAC,EAAE,IAAI,GACjB,OAAO,CAAC,KAAK,CAAC,cAAc,CAAC,CA4D/B"}
@@ -0,0 +1,146 @@
1
+ /**
2
+ * text-cjk — mixed CJK + Latin text geometry (editor extension library).
3
+ *
4
+ * Uses the Local Font Access API (window.queryLocalFonts) to load system CJK
5
+ * fonts, then parses glyph paths with opentype.js to create THREE.Shape →
6
+ * ExtrudeGeometry.
7
+ *
8
+ * When a CJK font is available, it is used for ALL characters (including Latin).
9
+ * When no CJK font is available, the default OpenSans Regular font (from
10
+ * fontRegistry) is used for all characters via opentype.js.
11
+ *
12
+ * The character classification / system-font loading half lives in core
13
+ * (`@faicad/faijs/primitives/text/cjk-font`) and is deliberately three-free.
14
+ */
15
+ import * as THREE from 'three';
16
+ import { mergeBufferGeometries } from '@faicad/faijs/primitives/mesh-primitives';
17
+ import { getOpentypeFont } from './text-geometry.js';
18
+ /**
19
+ * Generate geometry for a single character using opentype.js.
20
+ * Uses the same shape/extrude chain as opentypePathToGeometry().
21
+ */
22
+ function charGeometry(char, size, depth, font, xOffset) {
23
+ const glyph = font.charToGlyph(char);
24
+ if (!glyph || !glyph.path)
25
+ return null;
26
+ const path = glyph.getPath(xOffset, 0, size);
27
+ if (!path || !path.commands || path.commands.length === 0)
28
+ return null;
29
+ // Build shapes without centering (the per-character offset is handled here).
30
+ const commands = path.commands;
31
+ if (commands.length === 0)
32
+ return null;
33
+ const shapes = [];
34
+ let currentShape = null;
35
+ for (const cmd of commands) {
36
+ switch (cmd.type) {
37
+ case 'M':
38
+ // Implicit closure: opentype.js doesn't always emit 'Z' before a new 'M'.
39
+ if (currentShape) {
40
+ shapes.push(currentShape);
41
+ }
42
+ currentShape = new THREE.Shape();
43
+ currentShape.moveTo(cmd.x, cmd.y);
44
+ break;
45
+ case 'L':
46
+ currentShape?.lineTo(cmd.x, cmd.y);
47
+ break;
48
+ case 'C':
49
+ currentShape?.bezierCurveTo(cmd.x1, cmd.y1, cmd.x2, cmd.y2, cmd.x, cmd.y);
50
+ break;
51
+ case 'Q':
52
+ currentShape?.quadraticCurveTo(cmd.x1, cmd.y1, cmd.x, cmd.y);
53
+ break;
54
+ case 'Z':
55
+ if (currentShape) {
56
+ shapes.push(currentShape);
57
+ currentShape = null;
58
+ }
59
+ break;
60
+ }
61
+ }
62
+ // Handle final unclosed subpath (opentype.js doesn't always emit 'Z')
63
+ if (currentShape) {
64
+ shapes.push(currentShape);
65
+ }
66
+ if (shapes.length === 0)
67
+ return null;
68
+ const geo = new THREE.ExtrudeGeometry(shapes, {
69
+ depth,
70
+ bevelEnabled: false,
71
+ curveSegments: 6,
72
+ });
73
+ return geo;
74
+ }
75
+ /**
76
+ * Generate geometry for mixed text (CJK + Latin).
77
+ *
78
+ * When cjkFont is available, it is used for ALL characters (including Latin),
79
+ * since CJK fonts typically contain Latin glyphs.
80
+ *
81
+ * When cjkFont is null, the default OpenSans Regular font (from fontRegistry)
82
+ * is used for all characters via opentype.js — this is the same font used by
83
+ * the BREP path, ensuring glyph consistency.
84
+ *
85
+ * @param text Text string to render
86
+ * @param size Font size in mm
87
+ * @param depth Extrusion depth in mm
88
+ * @param cjkFont CJK font from system (null = use default font only)
89
+ * @param defaultFont Default opentype.js Font (OpenSans Regular from fontRegistry).
90
+ * If not provided, will be loaded via getOpentypeFont().
91
+ * @returns a promise resolving to the merged, centred geometry for the whole text.
92
+ */
93
+ export async function createMixedTextGeometry(text, size, depth, cjkFont, defaultFont) {
94
+ // Use CJK font for all chars if available, otherwise use default font
95
+ const font = cjkFont ?? defaultFont ?? (await getOpentypeFont());
96
+ // All characters individually
97
+ const charGeos = [];
98
+ let cursorX = 0;
99
+ for (const char of text) {
100
+ if (char === ' ' || char === '\t' || char === '\n' || char === '\r') {
101
+ // Space: advance by approximate width
102
+ const spaceAdv = font.charToGlyph(' ').advanceWidth ?? size / 3;
103
+ cursorX += spaceAdv * (size / font.unitsPerEm);
104
+ continue;
105
+ }
106
+ const glyph = font.charToGlyph(char);
107
+ if (glyph.index === 0) {
108
+ // Skip notdef glyphs (should not happen if font check was done upstream)
109
+ continue;
110
+ }
111
+ const geo = charGeometry(char, size, depth, font, cursorX);
112
+ if (geo) {
113
+ geo.computeBoundingBox();
114
+ const bb = geo.boundingBox;
115
+ const charWidth = bb.max.x - bb.min.x;
116
+ const adv = (glyph.advanceWidth ?? 0) * (size / font.unitsPerEm);
117
+ // Use advance width for cursor, but ensure minimum advance
118
+ const effectiveAdv = adv > 0 ? adv : charWidth + size * 0.1;
119
+ charGeos.push({ geo, advance: effectiveAdv });
120
+ cursorX += effectiveAdv;
121
+ }
122
+ }
123
+ // Merge all character geometries into one
124
+ if (charGeos.length === 0) {
125
+ // Fallback: create a minimal geometry from a question mark
126
+ const geo = charGeometry('?', size, depth, font, 0);
127
+ if (geo)
128
+ return geo;
129
+ return new THREE.BufferGeometry();
130
+ }
131
+ // Merge via BufferGeometryUtils or manual merge
132
+ const merged = mergeBufferGeometries(charGeos.map((cg) => cg.geo));
133
+ // Center the whole text in X/Z
134
+ merged.computeBoundingBox();
135
+ if (merged.boundingBox) {
136
+ const cx = (merged.boundingBox.max.x + merged.boundingBox.min.x) / 2;
137
+ const cz = (merged.boundingBox.max.z + merged.boundingBox.min.z) / 2;
138
+ merged.translate(-cx, -merged.boundingBox.min.y, -cz);
139
+ }
140
+ // Dispose individual geometries
141
+ for (const cg of charGeos) {
142
+ cg.geo.dispose();
143
+ }
144
+ return merged;
145
+ }
146
+ //# sourceMappingURL=text-cjk.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text-cjk.js","sourceRoot":"","sources":["../../src/extras/text-cjk.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * text-cjk — mixed CJK + Latin text geometry (editor extension library).\n *\n * Uses the Local Font Access API (window.queryLocalFonts) to load system CJK\n * fonts, then parses glyph paths with opentype.js to create THREE.Shape →\n * ExtrudeGeometry.\n *\n * When a CJK font is available, it is used for ALL characters (including Latin).\n * When no CJK font is available, the default OpenSans Regular font (from\n * fontRegistry) is used for all characters via opentype.js.\n *\n * The character classification / system-font loading half lives in core\n * (`@faicad/faijs/primitives/text/cjk-font`) and is deliberately three-free.\n */\nimport * as THREE from 'three'\n// Type-only: glyph parsing itself lives in core's fontRegistry; the extension\n// library never calls opentype at runtime (keeps it out of the A-group closure).\nimport type * as opentype from 'opentype.js'\nimport type { Font } from 'opentype.js'\nimport { mergeBufferGeometries } from '@faicad/faijs/primitives/mesh-primitives'\nimport { getOpentypeFont } from './text-geometry'\n\n/**\n * Generate geometry for a single character using opentype.js.\n * Uses the same shape/extrude chain as opentypePathToGeometry().\n */\nfunction charGeometry(\n char: string,\n size: number,\n depth: number,\n font: Font,\n xOffset: number,\n): THREE.BufferGeometry | null {\n const glyph = font.charToGlyph(char)\n if (!glyph || !glyph.path) return null\n\n const path = glyph.getPath(xOffset, 0, size)\n if (!path || !path.commands || path.commands.length === 0) return null\n\n // Build shapes without centering (the per-character offset is handled here).\n const commands = path.commands as opentype.PathCommand[]\n if (commands.length === 0) return null\n\n const shapes: THREE.Shape[] = []\n let currentShape: THREE.Shape | null = null\n\n for (const cmd of commands) {\n switch (cmd.type) {\n case 'M':\n // Implicit closure: opentype.js doesn't always emit 'Z' before a new 'M'.\n if (currentShape) {\n shapes.push(currentShape)\n }\n currentShape = new THREE.Shape()\n currentShape.moveTo(cmd.x!, cmd.y!)\n break\n case 'L':\n currentShape?.lineTo(cmd.x!, cmd.y!)\n break\n case 'C':\n currentShape?.bezierCurveTo(cmd.x1!, cmd.y1!, cmd.x2!, cmd.y2!, cmd.x!, cmd.y!)\n break\n case 'Q':\n currentShape?.quadraticCurveTo(cmd.x1!, cmd.y1!, cmd.x!, cmd.y!)\n break\n case 'Z':\n if (currentShape) {\n shapes.push(currentShape)\n currentShape = null\n }\n break\n }\n }\n\n // Handle final unclosed subpath (opentype.js doesn't always emit 'Z')\n if (currentShape) {\n shapes.push(currentShape)\n }\n\n if (shapes.length === 0) return null\n\n const geo = new THREE.ExtrudeGeometry(shapes, {\n depth,\n bevelEnabled: false,\n curveSegments: 6,\n })\n return geo\n}\n\n\n/**\n * Generate geometry for mixed text (CJK + Latin).\n *\n * When cjkFont is available, it is used for ALL characters (including Latin),\n * since CJK fonts typically contain Latin glyphs.\n *\n * When cjkFont is null, the default OpenSans Regular font (from fontRegistry)\n * is used for all characters via opentype.js — this is the same font used by\n * the BREP path, ensuring glyph consistency.\n *\n * @param text Text string to render\n * @param size Font size in mm\n * @param depth Extrusion depth in mm\n * @param cjkFont CJK font from system (null = use default font only)\n * @param defaultFont Default opentype.js Font (OpenSans Regular from fontRegistry).\n * If not provided, will be loaded via getOpentypeFont().\n * @returns a promise resolving to the merged, centred geometry for the whole text.\n */\nexport async function createMixedTextGeometry(\n text: string,\n size: number,\n depth: number,\n cjkFont: Font | null,\n defaultFont?: Font,\n): Promise<THREE.BufferGeometry> {\n // Use CJK font for all chars if available, otherwise use default font\n const font = cjkFont ?? defaultFont ?? (await getOpentypeFont())\n\n // All characters individually\n const charGeos: { geo: THREE.BufferGeometry; advance: number }[] = []\n let cursorX = 0\n\n for (const char of text) {\n if (char === ' ' || char === '\\t' || char === '\\n' || char === '\\r') {\n // Space: advance by approximate width\n const spaceAdv = font.charToGlyph(' ').advanceWidth ?? size / 3\n cursorX += spaceAdv * (size / font.unitsPerEm)\n continue\n }\n\n const glyph = font.charToGlyph(char)\n if (glyph.index === 0) {\n // Skip notdef glyphs (should not happen if font check was done upstream)\n continue\n }\n\n const geo = charGeometry(char, size, depth, font, cursorX)\n if (geo) {\n geo.computeBoundingBox()\n const bb = geo.boundingBox!\n const charWidth = bb.max.x - bb.min.x\n const adv = (glyph.advanceWidth ?? 0) * (size / font.unitsPerEm)\n // Use advance width for cursor, but ensure minimum advance\n const effectiveAdv = adv > 0 ? adv : charWidth + size * 0.1\n charGeos.push({ geo, advance: effectiveAdv })\n cursorX += effectiveAdv\n }\n }\n\n // Merge all character geometries into one\n if (charGeos.length === 0) {\n // Fallback: create a minimal geometry from a question mark\n const geo = charGeometry('?', size, depth, font, 0)\n if (geo) return geo\n return new THREE.BufferGeometry()\n }\n\n // Merge via BufferGeometryUtils or manual merge\n const merged = mergeBufferGeometries(charGeos.map((cg) => cg.geo))\n\n // Center the whole text in X/Z\n merged.computeBoundingBox()\n if (merged.boundingBox) {\n const cx = (merged.boundingBox.max.x + merged.boundingBox.min.x) / 2\n const cz = (merged.boundingBox.max.z + merged.boundingBox.min.z) / 2\n merged.translate(-cx, -merged.boundingBox.min.y, -cz)\n }\n\n // Dispose individual geometries\n for (const cg of charGeos) {\n cg.geo.dispose()\n }\n\n return merged\n}\n"]}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * text-geometry — 文字几何创建(mesh 路径)
3
+ *
4
+ * 使用 opentype.js + OpenSans Regular 字体(与 BREP 路径统一)。
5
+ * 字体来源:fontRegistry(通过依赖注入的 FontLoader 加载)。
6
+ *
7
+ * 过渡期仍用 THREE.Shape/ExtrudeGeometry,R6 时再替换为 manifold。
8
+ */
9
+ import * as THREE from 'three';
10
+ import type { Font, Path } from 'opentype.js';
11
+ /**
12
+ * Get the default opentype.js font (OpenSans Regular).
13
+ *
14
+ * Fetched from fontRegistry — the same font used by the BREP path
15
+ * (text-to-solid). The first call triggers a lazy ensureDefaultFont() load.
16
+ *
17
+ * @returns a promise resolving to the default opentype.js Font.
18
+ */
19
+ export declare function getOpentypeFont(): Promise<Font>;
20
+ /**
21
+ * Create text geometry centered in X/Z, extruded along +Z.
22
+ *
23
+ * Uses opentype.js font.getPath() → THREE.Shape → ExtrudeGeometry.
24
+ * This is the same font source as the BREP path (text-to-solid),
25
+ * ensuring glyph consistency between mesh and BREP paths.
26
+ *
27
+ * @param text Text string to render
28
+ * @param size Font size in mm
29
+ * @param depth Extrusion depth in mm
30
+ * @param font opentype.js Font object (from fontRegistry)
31
+ * @returns a promise resolving to the extruded text geometry.
32
+ */
33
+ export declare function createTextGeometry(text: string, size: number, depth: number, font: Font): Promise<THREE.BufferGeometry>;
34
+ /**
35
+ * Convert an opentype.js Path to THREE.BufferGeometry.
36
+ *
37
+ * Path commands → THREE.Shape → ExtrudeGeometry.
38
+ * The result is centered in X and Z (bounding box center → origin).
39
+ *
40
+ * Exported for reuse by the CJK text path and 3d_editor preview layer.
41
+ *
42
+ * @param path - the opentype.js path to convert.
43
+ * @param depth - extrusion depth in mm.
44
+ * @returns the resulting BufferGeometry, or null when the path is empty.
45
+ */
46
+ export declare function opentypePathToGeometry(path: Path, depth: number): THREE.BufferGeometry | null;
47
+ //# sourceMappingURL=text-geometry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text-geometry.d.ts","sourceRoot":"","sources":["../../src/extras/text-geometry.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,KAAK,EAAE,IAAI,EAAE,IAAI,EAAe,MAAM,aAAa,CAAA;AAK1D;;;;;;;GAOG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CAUrD;AAID;;;;;;;;;;;;GAYG;AACH,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,IAAI,GACT,OAAO,CAAC,KAAK,CAAC,cAAc,CAAC,CAmB/B;AAID;;;;;;;;;;;GAWG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,IAAI,EACV,KAAK,EAAE,MAAM,GACZ,KAAK,CAAC,cAAc,GAAG,IAAI,CAuD7B"}