@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
@@ -0,0 +1,127 @@
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 { ensureDefaultFont, getFont as getRegisteredFont } from '@faicad/faijs/brep/text/fontRegistry';
11
+ // ── Font loading ──
12
+ /**
13
+ * Get the default opentype.js font (OpenSans Regular).
14
+ *
15
+ * Fetched from fontRegistry — the same font used by the BREP path
16
+ * (text-to-solid). The first call triggers a lazy ensureDefaultFont() load.
17
+ *
18
+ * @returns a promise resolving to the default opentype.js Font.
19
+ */
20
+ export async function getOpentypeFont() {
21
+ await ensureDefaultFont();
22
+ const font = getRegisteredFont('default');
23
+ if (!font) {
24
+ throw new Error('[text-geometry] Default font not loaded. ' +
25
+ 'Call setFontLoader() before using text functions.');
26
+ }
27
+ return font;
28
+ }
29
+ // ── Text geometry ──
30
+ /**
31
+ * Create text geometry centered in X/Z, extruded along +Z.
32
+ *
33
+ * Uses opentype.js font.getPath() → THREE.Shape → ExtrudeGeometry.
34
+ * This is the same font source as the BREP path (text-to-solid),
35
+ * ensuring glyph consistency between mesh and BREP paths.
36
+ *
37
+ * @param text Text string to render
38
+ * @param size Font size in mm
39
+ * @param depth Extrusion depth in mm
40
+ * @param font opentype.js Font object (from fontRegistry)
41
+ * @returns a promise resolving to the extruded text geometry.
42
+ */
43
+ export async function createTextGeometry(text, size, depth, font) {
44
+ // Check for notdef glyphs (missing characters)
45
+ for (const ch of text) {
46
+ if (ch === ' ' || ch === '\t' || ch === '\n' || ch === '\r')
47
+ continue;
48
+ const glyph = font.charToGlyph(ch);
49
+ if (glyph.index === 0) {
50
+ throw new Error(`[text-geometry] Character "${ch}" (U+${ch.codePointAt(0).toString(16).toUpperCase()}) ` +
51
+ `not found in font. If it is a CJK character, a CJK font must be loaded.`);
52
+ }
53
+ }
54
+ const path = font.getPath(text, 0, 0, size);
55
+ const geo = opentypePathToGeometry(path, depth);
56
+ if (!geo) {
57
+ throw new Error(`[text-geometry] Failed to create geometry from text: "${text}"`);
58
+ }
59
+ return geo;
60
+ }
61
+ // ── opentype path → THREE geometry ──
62
+ /**
63
+ * Convert an opentype.js Path to THREE.BufferGeometry.
64
+ *
65
+ * Path commands → THREE.Shape → ExtrudeGeometry.
66
+ * The result is centered in X and Z (bounding box center → origin).
67
+ *
68
+ * Exported for reuse by the CJK text path and 3d_editor preview layer.
69
+ *
70
+ * @param path - the opentype.js path to convert.
71
+ * @param depth - extrusion depth in mm.
72
+ * @returns the resulting BufferGeometry, or null when the path is empty.
73
+ */
74
+ export function opentypePathToGeometry(path, depth) {
75
+ const commands = path.commands;
76
+ if (commands.length === 0)
77
+ return null;
78
+ const shapes = [];
79
+ let currentShape = null;
80
+ for (const cmd of commands) {
81
+ switch (cmd.type) {
82
+ case 'M':
83
+ // Implicit closure: opentype.js doesn't always emit 'Z' before a new 'M'.
84
+ // Push the current shape before starting a new subpath.
85
+ if (currentShape) {
86
+ shapes.push(currentShape);
87
+ }
88
+ currentShape = new THREE.Shape();
89
+ currentShape.moveTo(cmd.x, cmd.y);
90
+ break;
91
+ case 'L':
92
+ currentShape?.lineTo(cmd.x, cmd.y);
93
+ break;
94
+ case 'C':
95
+ currentShape?.bezierCurveTo(cmd.x1, cmd.y1, cmd.x2, cmd.y2, cmd.x, cmd.y);
96
+ break;
97
+ case 'Q':
98
+ currentShape?.quadraticCurveTo(cmd.x1, cmd.y1, cmd.x, cmd.y);
99
+ break;
100
+ case 'Z':
101
+ if (currentShape) {
102
+ shapes.push(currentShape);
103
+ currentShape = null;
104
+ }
105
+ break;
106
+ }
107
+ }
108
+ // Handle final unclosed subpath (opentype.js doesn't always emit 'Z')
109
+ if (currentShape) {
110
+ shapes.push(currentShape);
111
+ }
112
+ if (shapes.length === 0)
113
+ return null;
114
+ const geo = new THREE.ExtrudeGeometry(shapes, {
115
+ depth,
116
+ bevelEnabled: false,
117
+ curveSegments: 6,
118
+ });
119
+ geo.computeBoundingBox();
120
+ if (geo.boundingBox) {
121
+ const cx = (geo.boundingBox.max.x + geo.boundingBox.min.x) / 2;
122
+ const cz = (geo.boundingBox.max.z + geo.boundingBox.min.z) / 2;
123
+ geo.translate(-cx, -geo.boundingBox.min.y, -cz);
124
+ }
125
+ return geo;
126
+ }
127
+ //# sourceMappingURL=text-geometry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text-geometry.js","sourceRoot":"","sources":["../../src/extras/text-geometry.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * text-geometry — 文字几何创建(mesh 路径)\n *\n * 使用 opentype.js + OpenSans Regular 字体(与 BREP 路径统一)。\n * 字体来源:fontRegistry(通过依赖注入的 FontLoader 加载)。\n *\n * 过渡期仍用 THREE.Shape/ExtrudeGeometry,R6 时再替换为 manifold。\n */\n\nimport * as THREE from 'three'\nimport type { Font, Path, PathCommand } from 'opentype.js'\nimport { ensureDefaultFont, getFont as getRegisteredFont } from '@faicad/faijs/brep/text/fontRegistry'\n\n// ── Font loading ──\n\n/**\n * Get the default opentype.js font (OpenSans Regular).\n *\n * Fetched from fontRegistry — the same font used by the BREP path\n * (text-to-solid). The first call triggers a lazy ensureDefaultFont() load.\n *\n * @returns a promise resolving to the default opentype.js Font.\n */\nexport async function getOpentypeFont(): Promise<Font> {\n await ensureDefaultFont()\n const font = getRegisteredFont('default')\n if (!font) {\n throw new Error(\n '[text-geometry] Default font not loaded. ' +\n 'Call setFontLoader() before using text functions.',\n )\n }\n return font\n}\n\n// ── Text geometry ──\n\n/**\n * Create text geometry centered in X/Z, extruded along +Z.\n *\n * Uses opentype.js font.getPath() → THREE.Shape → ExtrudeGeometry.\n * This is the same font source as the BREP path (text-to-solid),\n * ensuring glyph consistency between mesh and BREP paths.\n *\n * @param text Text string to render\n * @param size Font size in mm\n * @param depth Extrusion depth in mm\n * @param font opentype.js Font object (from fontRegistry)\n * @returns a promise resolving to the extruded text geometry.\n */\nexport async function createTextGeometry(\n text: string,\n size: number,\n depth: number,\n font: Font,\n): Promise<THREE.BufferGeometry> {\n // Check for notdef glyphs (missing characters)\n for (const ch of text) {\n if (ch === ' ' || ch === '\\t' || ch === '\\n' || ch === '\\r') continue\n const glyph = font.charToGlyph(ch)\n if (glyph.index === 0) {\n throw new Error(\n `[text-geometry] Character \"${ch}\" (U+${ch.codePointAt(0)!.toString(16).toUpperCase()}) ` +\n `not found in font. If it is a CJK character, a CJK font must be loaded.`,\n )\n }\n }\n\n const path = font.getPath(text, 0, 0, size)\n const geo = opentypePathToGeometry(path, depth)\n if (!geo) {\n throw new Error(`[text-geometry] Failed to create geometry from text: \"${text}\"`)\n }\n return geo\n}\n\n// ── opentype path → THREE geometry ──\n\n/**\n * Convert an opentype.js Path to THREE.BufferGeometry.\n *\n * Path commands → THREE.Shape → ExtrudeGeometry.\n * The result is centered in X and Z (bounding box center → origin).\n *\n * Exported for reuse by the CJK text path and 3d_editor preview layer.\n *\n * @param path - the opentype.js path to convert.\n * @param depth - extrusion depth in mm.\n * @returns the resulting BufferGeometry, or null when the path is empty.\n */\nexport function opentypePathToGeometry(\n path: Path,\n depth: number,\n): THREE.BufferGeometry | null {\n const commands = path.commands as 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 // Push the current shape before starting a new subpath.\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 geo.computeBoundingBox()\n if (geo.boundingBox) {\n const cx = (geo.boundingBox.max.x + geo.boundingBox.min.x) / 2\n const cz = (geo.boundingBox.max.z + geo.boundingBox.min.z) / 2\n geo.translate(-cx, -geo.boundingBox.min.y, -cz)\n }\n return geo\n}\n"]}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * @faicad/faijs-extra — editor extension library for faijs.
3
+ *
4
+ * Carries the ops and preview helpers that serve the 3d_editor application but
5
+ * are not part of the faijs platform surface:
6
+ *
7
+ * - A group (editor ops): `fai_drill` / `fai_extrude` / `fai_split`,
8
+ * `group` / `assembly`, `copy`, `load`.
9
+ * - B group (svg / 3D text): `text`, `svgExtrude` plus the preview helpers
10
+ * `svgToExtrudedGeometry` / `parseSvgShapes` / `extrudeShapes` /
11
+ * `createTextGeometry` / `getOpentypeFont` / `opentypePathToGeometry` /
12
+ * `createMixedTextGeometry`.
13
+ *
14
+ * Assembly: merge `createEditorNamespace()` over core's `createApiNamespace()`,
15
+ * register the result as the host's `cad` library, then register the symbol
16
+ * names and install the mesh providers — see `./namespace`.
17
+ *
18
+ * Hosts that must not bundle three's non-basic chain (the weapp worker) use
19
+ * `@faicad/faijs-extra/editor-ops` instead: the A group only.
20
+ *
21
+ * This entry point is environment-agnostic (three + opentype.js only, no
22
+ * `node:*`); `./browser` is the same surface for browser/worker hosts.
23
+ */
24
+ export { createEditorNamespace, createEditorCadNamespace, mergeEditorNamespace, registerEditorSymbols, unregisterEditorSymbols, installEditorMeshProviders, uninstallEditorMeshProviders, } from './namespace.js';
25
+ export { ALL_EDITOR_OPS, CREATOR_OPS, EDITOR_OPS, isEditorOp, type CreatorOpName, type EditorOpName, } from './op-names.js';
26
+ export { fai_drill, assertDrillParams } from './ops/fai_drill.js';
27
+ export { fai_extrude, assertExtrudeParams } from './ops/fai_extrude.js';
28
+ export { fai_split } from './ops/fai_split.js';
29
+ export { group, assembly } from './ops/compound.js';
30
+ export type { GroupParams, AssemblyParams, AssemblyBehavior } from './ops/compound.js';
31
+ export { copy } from './ops/copy.js';
32
+ export { load } from './ops/load.js';
33
+ export type { AssemblyConstraint, FaceMateConstraint, FaceMateFace, EntityRef, FaceRef, EdgeRef, AssemblyVec3, MateConstraint, AlignConstraint, CoincidentConstraint, ConcentricConstraint, DistanceConstraint, AngleConstraint, ParallelConstraint, PerpendicularConstraint, FixedConstraint, StructuralConstraint, } from '@faicad/faijs/api/assembly/types';
34
+ export type { AssemblySolveResult } from '@faicad/faijs/api/assembly/solve';
35
+ export { text, assertTextParams } from './ops/text.js';
36
+ export { svgExtrude, assertSvgExtrudeParams } from './ops/svg-extrude.js';
37
+ export { svgToExtrudedGeometry, parseSvgShapes, extrudeShapes, type SvgExtrudeOptions, } from './extras/svg-extrude.js';
38
+ export { createTextGeometry, getOpentypeFont, opentypePathToGeometry, } from './extras/text-geometry.js';
39
+ export { createMixedTextGeometry } from './extras/text-cjk.js';
40
+ export { createEngraveDecorationProvider } from './mesh/engrave-decoration.js';
41
+ export { editorCad } from './mesh/index.js';
42
+ export { splitWithParams, dovetailSplit, dowelSplit, tenonSplit, computeBasisFromNormal, } from './mesh/fai_split.js';
43
+ export { drill } from './mesh/fai_drill.js';
44
+ export { extrude as meshExtrude } from './mesh/fai_extrude.js';
45
+ export { text as meshText, svgExtrude as meshSvgExtrude } from './mesh/primitives.js';
46
+ export type { Shape, Vec3, BoundingBox, FaceDescriptor, TextParams, SvgExtrudeParams, DrillParams, ExtrudeParams, SplitPlane, SplitResult, DovetailSplitParams, DowelSplitParams, TenonSplitParams, } from '@faicad/faijs/mesh/types';
47
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,OAAO,EACL,qBAAqB,EAAE,wBAAwB,EAAE,oBAAoB,EACrE,qBAAqB,EAAE,uBAAuB,EAC9C,0BAA0B,EAAE,4BAA4B,GACzD,MAAM,aAAa,CAAA;AACpB,OAAO,EACL,cAAc,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EACnD,KAAK,aAAa,EAAE,KAAK,YAAY,GACtC,MAAM,YAAY,CAAA;AAGnB,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAC9D,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AACpE,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;AACjC,YAAY,EACV,kBAAkB,EAAE,kBAAkB,EAAE,YAAY,EACpD,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EACzC,cAAc,EAAE,eAAe,EAAE,oBAAoB,EAAE,oBAAoB,EAC3E,kBAAkB,EAAE,eAAe,EAAE,kBAAkB,EAAE,uBAAuB,EAChF,eAAe,EAAE,oBAAoB,GACtC,MAAM,kCAAkC,CAAA;AACzC,YAAY,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAA;AAG3E,OAAO,EAAE,IAAI,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AACnD,OAAO,EAAE,UAAU,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAA;AAGtE,OAAO,EACL,qBAAqB,EAAE,cAAc,EAAE,aAAa,EACpD,KAAK,iBAAiB,GACvB,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EACL,kBAAkB,EAAE,eAAe,EAAE,sBAAsB,GAC5D,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAA;AAC3D,OAAO,EAAE,+BAA+B,EAAE,MAAM,2BAA2B,CAAA;AAG3E,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAA;AAClC,OAAO,EACL,eAAe,EAAE,aAAa,EAAE,UAAU,EAAE,UAAU,EAAE,sBAAsB,GAC/E,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAA;AACxC,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAC3D,OAAO,EAAE,IAAI,IAAI,QAAQ,EAAE,UAAU,IAAI,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAClF,YAAY,EACV,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,cAAc,EACxC,UAAU,EAAE,gBAAgB,EAC5B,WAAW,EAAE,aAAa,EAC1B,UAAU,EAAE,WAAW,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,gBAAgB,GACjF,MAAM,0BAA0B,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,47 @@
1
+ /**
2
+ * @faicad/faijs-extra — editor extension library for faijs.
3
+ *
4
+ * Carries the ops and preview helpers that serve the 3d_editor application but
5
+ * are not part of the faijs platform surface:
6
+ *
7
+ * - A group (editor ops): `fai_drill` / `fai_extrude` / `fai_split`,
8
+ * `group` / `assembly`, `copy`, `load`.
9
+ * - B group (svg / 3D text): `text`, `svgExtrude` plus the preview helpers
10
+ * `svgToExtrudedGeometry` / `parseSvgShapes` / `extrudeShapes` /
11
+ * `createTextGeometry` / `getOpentypeFont` / `opentypePathToGeometry` /
12
+ * `createMixedTextGeometry`.
13
+ *
14
+ * Assembly: merge `createEditorNamespace()` over core's `createApiNamespace()`,
15
+ * register the result as the host's `cad` library, then register the symbol
16
+ * names and install the mesh providers — see `./namespace`.
17
+ *
18
+ * Hosts that must not bundle three's non-basic chain (the weapp worker) use
19
+ * `@faicad/faijs-extra/editor-ops` instead: the A group only.
20
+ *
21
+ * This entry point is environment-agnostic (three + opentype.js only, no
22
+ * `node:*`); `./browser` is the same surface for browser/worker hosts.
23
+ */
24
+ export { createEditorNamespace, createEditorCadNamespace, mergeEditorNamespace, registerEditorSymbols, unregisterEditorSymbols, installEditorMeshProviders, uninstallEditorMeshProviders, } from './namespace.js';
25
+ export { ALL_EDITOR_OPS, CREATOR_OPS, EDITOR_OPS, isEditorOp, } from './op-names.js';
26
+ // ── A 组:编辑器 op 定义 ──
27
+ export { fai_drill, assertDrillParams } from './ops/fai_drill.js';
28
+ export { fai_extrude, assertExtrudeParams } from './ops/fai_extrude.js';
29
+ export { fai_split } from './ops/fai_split.js';
30
+ export { group, assembly } from './ops/compound.js';
31
+ export { copy } from './ops/copy.js';
32
+ export { load } from './ops/load.js';
33
+ // ── B 组:svg / 3D 文字 op ──
34
+ export { text, assertTextParams } from './ops/text.js';
35
+ export { svgExtrude, assertSvgExtrudeParams } from './ops/svg-extrude.js';
36
+ // ── B 组:预览辅助函数(宿主交互预览用,不提交几何)──
37
+ export { svgToExtrudedGeometry, parseSvgShapes, extrudeShapes, } from './extras/svg-extrude.js';
38
+ export { createTextGeometry, getOpentypeFont, opentypePathToGeometry, } from './extras/text-geometry.js';
39
+ export { createMixedTextGeometry } from './extras/text-cjk.js';
40
+ export { createEngraveDecorationProvider } from './mesh/engrave-decoration.js';
41
+ // ── mesh 实现聚合(编辑器侧脚本面/预览共用的实现入口)──
42
+ export { editorCad } from './mesh/index.js';
43
+ export { splitWithParams, dovetailSplit, dowelSplit, tenonSplit, computeBasisFromNormal, } from './mesh/fai_split.js';
44
+ export { drill } from './mesh/fai_drill.js';
45
+ export { extrude as meshExtrude } from './mesh/fai_extrude.js';
46
+ export { text as meshText, svgExtrude as meshSvgExtrude } from './mesh/primitives.js';
47
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,OAAO,EACL,qBAAqB,EAAE,wBAAwB,EAAE,oBAAoB,EACrE,qBAAqB,EAAE,uBAAuB,EAC9C,0BAA0B,EAAE,4BAA4B,GACzD,MAAM,aAAa,CAAA;AACpB,OAAO,EACL,cAAc,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,GAEpD,MAAM,YAAY,CAAA;AAEnB,sBAAsB;AACtB,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAC9D,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AACpE,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;AAUjC,2BAA2B;AAC3B,OAAO,EAAE,IAAI,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AACnD,OAAO,EAAE,UAAU,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAA;AAEtE,iCAAiC;AACjC,OAAO,EACL,qBAAqB,EAAE,cAAc,EAAE,aAAa,GAErD,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EACL,kBAAkB,EAAE,eAAe,EAAE,sBAAsB,GAC5D,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAA;AAC3D,OAAO,EAAE,+BAA+B,EAAE,MAAM,2BAA2B,CAAA;AAE3E,oCAAoC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAA;AAClC,OAAO,EACL,eAAe,EAAE,aAAa,EAAE,UAAU,EAAE,UAAU,EAAE,sBAAsB,GAC/E,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAA;AACxC,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAC3D,OAAO,EAAE,IAAI,IAAI,QAAQ,EAAE,UAAU,IAAI,cAAc,EAAE,MAAM,mBAAmB,CAAA","sourcesContent":["/**\n * @faicad/faijs-extra — editor extension library for faijs.\n *\n * Carries the ops and preview helpers that serve the 3d_editor application but\n * are not part of the faijs platform surface:\n *\n * - A group (editor ops): `fai_drill` / `fai_extrude` / `fai_split`,\n * `group` / `assembly`, `copy`, `load`.\n * - B group (svg / 3D text): `text`, `svgExtrude` plus the preview helpers\n * `svgToExtrudedGeometry` / `parseSvgShapes` / `extrudeShapes` /\n * `createTextGeometry` / `getOpentypeFont` / `opentypePathToGeometry` /\n * `createMixedTextGeometry`.\n *\n * Assembly: merge `createEditorNamespace()` over core's `createApiNamespace()`,\n * register the result as the host's `cad` library, then register the symbol\n * names and install the mesh providers — see `./namespace`.\n *\n * Hosts that must not bundle three's non-basic chain (the weapp worker) use\n * `@faicad/faijs-extra/editor-ops` instead: the A group only.\n *\n * This entry point is environment-agnostic (three + opentype.js only, no\n * `node:*`); `./browser` is the same surface for browser/worker hosts.\n */\nexport {\n createEditorNamespace, createEditorCadNamespace, mergeEditorNamespace,\n registerEditorSymbols, unregisterEditorSymbols,\n installEditorMeshProviders, uninstallEditorMeshProviders,\n} from './namespace'\nexport {\n ALL_EDITOR_OPS, CREATOR_OPS, EDITOR_OPS, isEditorOp,\n type CreatorOpName, type EditorOpName,\n} from './op-names'\n\n// ── A 组:编辑器 op 定义 ──\nexport { fai_drill, assertDrillParams } from './ops/fai_drill'\nexport { fai_extrude, assertExtrudeParams } 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'\nexport type {\n AssemblyConstraint, FaceMateConstraint, FaceMateFace,\n EntityRef, FaceRef, EdgeRef, AssemblyVec3,\n MateConstraint, AlignConstraint, CoincidentConstraint, ConcentricConstraint,\n DistanceConstraint, AngleConstraint, ParallelConstraint, PerpendicularConstraint,\n FixedConstraint, StructuralConstraint,\n} from '@faicad/faijs/api/assembly/types'\nexport type { AssemblySolveResult } from '@faicad/faijs/api/assembly/solve'\n\n// ── B 组:svg / 3D 文字 op ──\nexport { text, assertTextParams } from './ops/text'\nexport { svgExtrude, assertSvgExtrudeParams } from './ops/svg-extrude'\n\n// ── B 组:预览辅助函数(宿主交互预览用,不提交几何)──\nexport {\n svgToExtrudedGeometry, parseSvgShapes, extrudeShapes,\n type SvgExtrudeOptions,\n} from './extras/svg-extrude'\nexport {\n createTextGeometry, getOpentypeFont, opentypePathToGeometry,\n} from './extras/text-geometry'\nexport { createMixedTextGeometry } from './extras/text-cjk'\nexport { createEngraveDecorationProvider } from './mesh/engrave-decoration'\n\n// ── mesh 实现聚合(编辑器侧脚本面/预览共用的实现入口)──\nexport { editorCad } from './mesh'\nexport {\n splitWithParams, dovetailSplit, dowelSplit, tenonSplit, computeBasisFromNormal,\n} from './mesh/fai_split'\nexport { drill } from './mesh/fai_drill'\nexport { extrude as meshExtrude } from './mesh/fai_extrude'\nexport { text as meshText, svgExtrude as meshSvgExtrude } from './mesh/primitives'\nexport type {\n Shape, Vec3, BoundingBox, FaceDescriptor,\n TextParams, SvgExtrudeParams,\n DrillParams, ExtrudeParams,\n SplitPlane, SplitResult, DovetailSplitParams, DowelSplitParams, TenonSplitParams,\n} from '@faicad/faijs/mesh/types'\n"]}
@@ -0,0 +1,90 @@
1
+ import * as THREE from 'three';
2
+ import type { DrillDirection, DrillHoleParams } from './drill-types.js';
3
+ /**
4
+ * Build a Z-up hole geometry centered on the origin, with the axial direction
5
+ * along Z.
6
+ *
7
+ * - simple: CylinderGeometry rotated to Z-up
8
+ * - screw: makeScrew() already returns Z-up
9
+ *
10
+ * Tolerance is applied as a radial expansion (tolerance/2 on radius).
11
+ *
12
+ * @param params - drill-hole parameters (type, diameter, screw details, tolerance).
13
+ * @param height - axial length of the hole in millimeters.
14
+ * @returns the hole geometry as a Z-up BufferGeometry.
15
+ */
16
+ export declare function buildHoleGeometry(params: DrillHoleParams, height: number): THREE.BufferGeometry;
17
+ /**
18
+ * Compute the through-hole height by raycasting against the target mesh's
19
+ * bounding box. Returns { height, center } so the cylinder exactly spans the
20
+ * model thickness along the drill direction, without extending outside.
21
+ *
22
+ * @param position - world-space position of the hole entry point.
23
+ * @param direction - the drill direction (normalized internally).
24
+ * @param bbox - world-space bounding box of the target mesh.
25
+ * @returns the height and world-space center of the through hole.
26
+ */
27
+ export declare function computeThroughHoleDimensions(position: THREE.Vector3, direction: THREE.Vector3, bbox: THREE.Box3): {
28
+ height: number;
29
+ center: THREE.Vector3;
30
+ };
31
+ /**
32
+ * Compute the blind-hole center: the hole entry is at the click point,
33
+ * the hole extends inward by `depth`. The cylinder center is at
34
+ * position + direction * (depth / 2).
35
+ *
36
+ * @param position - world-space hole entry point.
37
+ * @param direction - the drill direction (normalized internally).
38
+ * @param depth - blind-hole depth in millimeters.
39
+ * @returns the world-space cylinder center.
40
+ */
41
+ export declare function computeBlindHoleCenter(position: THREE.Vector3, direction: THREE.Vector3, depth: number): THREE.Vector3;
42
+ /**
43
+ * Orient and position a Z-up hole geometry (axial along Z) to world space.
44
+ * Returns a new geometry with the transform baked in.
45
+ *
46
+ * @param geo - Z-up hole geometry centered on the origin.
47
+ * @param direction - world-space axis the hole should point along.
48
+ * @param center - world-space position of the hole center.
49
+ * @returns a transformed clone of the input geometry.
50
+ */
51
+ export declare function positionHoleGeometry(geo: THREE.BufferGeometry, direction: THREE.Vector3, center: THREE.Vector3): THREE.BufferGeometry;
52
+ /**
53
+ * Resolve the drill direction based on params.direction and the face normal.
54
+ *
55
+ * - 'normal': inward direction = negated face normal
56
+ * - 'x'/'y'/'z': axis-aligned, flipped to point INTO the material
57
+ * (i.e. opposite to the face normal)
58
+ *
59
+ * @param direction - requested drill-direction reference.
60
+ * @param faceNormal - face normal at the drill position.
61
+ * @returns the resolved unit drill direction.
62
+ */
63
+ export declare function resolveDrillDirection(direction: DrillDirection, faceNormal: THREE.Vector3): THREE.Vector3;
64
+ /**
65
+ * Drill a hole into a THREE.Mesh in world space, returning the resulting
66
+ * geometry in the mesh's local space.
67
+ *
68
+ * @param targetMesh - the mesh to drill into.
69
+ * @param params - drill-hole parameters.
70
+ * @param position - world-space hole entry point.
71
+ * @param faceNormal - world-space normal of the face at the entry point.
72
+ * @returns the drilled geometry in mesh-local space.
73
+ */
74
+ export declare function executeDrillHole(targetMesh: THREE.Mesh, params: DrillHoleParams, position: THREE.Vector3, faceNormal: THREE.Vector3): Promise<THREE.BufferGeometry>;
75
+ /**
76
+ * Core drill computation that accepts geometry + world matrix directly.
77
+ *
78
+ * This avoids the need to temporarily swap mesh.geometry when the mesh
79
+ * currently has a preview geometry on it. Callers that have the G0 geometry
80
+ * in a separate BufferGeometry can use this directly.
81
+ *
82
+ * @param sourceGeometry - the base geometry to drill (mesh-local space).
83
+ * @param worldMatrix - world transform of the source geometry.
84
+ * @param params - drill-hole parameters.
85
+ * @param position - world-space hole entry point.
86
+ * @param faceNormal - world-space face normal at the entry point.
87
+ * @returns the drilled geometry, transformed back into mesh-local space.
88
+ */
89
+ export declare function executeDrillHoleOnGeometry(sourceGeometry: THREE.BufferGeometry, worldMatrix: THREE.Matrix4, params: DrillHoleParams, position: THREE.Vector3, faceNormal: THREE.Vector3): Promise<THREE.BufferGeometry>;
90
+ //# sourceMappingURL=DrillHoleCore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DrillHoleCore.d.ts","sourceRoot":"","sources":["../../../src/mesh/drill-hole/DrillHoleCore.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAU9B,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAKpE;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,eAAe,EACvB,MAAM,EAAE,MAAM,GACb,KAAK,CAAC,cAAc,CAkEtB;AAED;;;;;;;;;GASG;AACH,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,KAAK,CAAC,OAAO,EACvB,SAAS,EAAE,KAAK,CAAC,OAAO,EACxB,IAAI,EAAE,KAAK,CAAC,IAAI,GACf;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,KAAK,CAAC,OAAO,CAAA;CAAE,CAqD3C;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,KAAK,CAAC,OAAO,EACvB,SAAS,EAAE,KAAK,CAAC,OAAO,EACxB,KAAK,EAAE,MAAM,GACZ,KAAK,CAAC,OAAO,CAEf;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,KAAK,CAAC,cAAc,EACzB,SAAS,EAAE,KAAK,CAAC,OAAO,EACxB,MAAM,EAAE,KAAK,CAAC,OAAO,GACpB,KAAK,CAAC,cAAc,CAWtB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,cAAc,EACzB,UAAU,EAAE,KAAK,CAAC,OAAO,GACxB,KAAK,CAAC,OAAO,CAiBf;AAED;;;;;;;;;GASG;AACH,wBAAsB,gBAAgB,CACpC,UAAU,EAAE,KAAK,CAAC,IAAI,EACtB,MAAM,EAAE,eAAe,EACvB,QAAQ,EAAE,KAAK,CAAC,OAAO,EACvB,UAAU,EAAE,KAAK,CAAC,OAAO,GACxB,OAAO,CAAC,KAAK,CAAC,cAAc,CAAC,CAS/B;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,0BAA0B,CAC9C,cAAc,EAAE,KAAK,CAAC,cAAc,EACpC,WAAW,EAAE,KAAK,CAAC,OAAO,EAC1B,MAAM,EAAE,eAAe,EACvB,QAAQ,EAAE,KAAK,CAAC,OAAO,EACvB,UAAU,EAAE,KAAK,CAAC,OAAO,GACxB,OAAO,CAAC,KAAK,CAAC,cAAc,CAAC,CA6E/B"}
@@ -0,0 +1,287 @@
1
+ import * as THREE from 'three';
2
+ import { makeScrew } from '@faicad/faijs/primitives/screw/screw';
3
+ import { getScrewSpec } from '@faicad/faijs/primitives/screw/screw-db';
4
+ import { computeBoolean, geoToManifoldMesh, manifoldMeshToGeo, } from '@faicad/faijs/boolean/csg-backend';
5
+ import { deriveNormals } from '@faicad/faijs/boolean/deriveNormals';
6
+ /** Rotation matrix: +90° around X — converts Y-up vertex data to Z-up. */
7
+ const ROT_Y_TO_Z = new THREE.Matrix4().makeRotationX(Math.PI / 2);
8
+ /**
9
+ * Build a Z-up hole geometry centered on the origin, with the axial direction
10
+ * along Z.
11
+ *
12
+ * - simple: CylinderGeometry rotated to Z-up
13
+ * - screw: makeScrew() already returns Z-up
14
+ *
15
+ * Tolerance is applied as a radial expansion (tolerance/2 on radius).
16
+ *
17
+ * @param params - drill-hole parameters (type, diameter, screw details, tolerance).
18
+ * @param height - axial length of the hole in millimeters.
19
+ * @returns the hole geometry as a Z-up BufferGeometry.
20
+ */
21
+ export function buildHoleGeometry(params, height) {
22
+ const toleranceRadius = params.tolerance / 2;
23
+ // ── simple: CylinderGeometry → rotate to Z-up ──
24
+ if (params.holeType === 'simple') {
25
+ const r = params.diameter / 2 + toleranceRadius;
26
+ const geo = new THREE.CylinderGeometry(r, r, height, 32);
27
+ geo.applyMatrix4(ROT_Y_TO_Z);
28
+ return geo;
29
+ }
30
+ // ── screw: makeScrew() already returns Z-up ──
31
+ if (params.holeType === 'screw') {
32
+ const geo = makeScrew({
33
+ system: params.screwSystem,
34
+ specIdx: params.screwSpecIdx,
35
+ thread: params.screwThread,
36
+ pitchCustom: 0,
37
+ length: height,
38
+ head: params.screwHead,
39
+ nRad: 32,
40
+ });
41
+ // Center the screw on origin. makeScrew extends from Z=0 to Z=height
42
+ // for the body, plus headHeight = spec.dia for the head (if present).
43
+ // The total length = height + headHeight, so center by half that.
44
+ const spec = getScrewSpec(params.screwSystem, params.screwSpecIdx);
45
+ const headHeight = params.screwHead !== 'none' ? spec.dia : 0;
46
+ const totalLength = height + headHeight;
47
+ geo.translate(0, 0, -totalLength / 2);
48
+ // For screw with head: makeScrew places the head at the +Z end (top of
49
+ // the screw), but for a drill hole the head must be at the -Z end — the
50
+ // entry point at the surface. Reflect Z to put the head at the entry side,
51
+ // and reverse triangle winding to maintain correct normals.
52
+ if (params.screwHead !== 'none') {
53
+ const pos = geo.getAttribute('position');
54
+ for (let i = 0; i < pos.count; i++) {
55
+ pos.setZ(i, -pos.getZ(i));
56
+ }
57
+ pos.needsUpdate = true;
58
+ // Fix winding order: reflection flips triangle orientation
59
+ const idx = geo.index;
60
+ const arr = idx.array;
61
+ for (let i = 0; i < arr.length; i += 3) {
62
+ const tmp = arr[i + 1];
63
+ arr[i + 1] = arr[i + 2];
64
+ arr[i + 2] = tmp;
65
+ }
66
+ idx.needsUpdate = true;
67
+ }
68
+ // Apply tolerance: scale radially (X and Y in Z-up space)
69
+ const baseRadius = spec.dia / 2;
70
+ if (toleranceRadius > 0 && baseRadius > 0) {
71
+ const scale = 1 + toleranceRadius / baseRadius;
72
+ geo.scale(scale, scale, 1);
73
+ }
74
+ return geo;
75
+ }
76
+ // Fallback (should not reach)
77
+ const geo = new THREE.CylinderGeometry(params.diameter / 2, params.diameter / 2, height, 32);
78
+ geo.applyMatrix4(ROT_Y_TO_Z);
79
+ return geo;
80
+ }
81
+ /**
82
+ * Compute the through-hole height by raycasting against the target mesh's
83
+ * bounding box. Returns { height, center } so the cylinder exactly spans the
84
+ * model thickness along the drill direction, without extending outside.
85
+ *
86
+ * @param position - world-space position of the hole entry point.
87
+ * @param direction - the drill direction (normalized internally).
88
+ * @param bbox - world-space bounding box of the target mesh.
89
+ * @returns the height and world-space center of the through hole.
90
+ */
91
+ export function computeThroughHoleDimensions(position, direction, bbox) {
92
+ const dir = direction.clone().normalize();
93
+ // Manual slab-method intersection: compute distances along dir to bbox entry/exit.
94
+ // This handles edge cases where position is exactly on the bbox boundary
95
+ // (Ray.intersectBox returns null in those cases).
96
+ const p = position;
97
+ const d = dir;
98
+ let tNear = -Infinity;
99
+ let tFar = Infinity;
100
+ // X slab
101
+ if (Math.abs(d.x) > 1e-12) {
102
+ const t1 = (bbox.min.x - p.x) / d.x;
103
+ const t2 = (bbox.max.x - p.x) / d.x;
104
+ tNear = Math.max(tNear, Math.min(t1, t2));
105
+ tFar = Math.min(tFar, Math.max(t1, t2));
106
+ }
107
+ else if (p.x < bbox.min.x || p.x > bbox.max.x) {
108
+ // Parallel to X slab and outside — no intersection
109
+ return { height: 0, center: position.clone() };
110
+ }
111
+ // Y slab
112
+ if (Math.abs(d.y) > 1e-12) {
113
+ const t1 = (bbox.min.y - p.y) / d.y;
114
+ const t2 = (bbox.max.y - p.y) / d.y;
115
+ tNear = Math.max(tNear, Math.min(t1, t2));
116
+ tFar = Math.min(tFar, Math.max(t1, t2));
117
+ }
118
+ else if (p.y < bbox.min.y || p.y > bbox.max.y) {
119
+ return { height: 0, center: position.clone() };
120
+ }
121
+ // Z slab
122
+ if (Math.abs(d.z) > 1e-12) {
123
+ const t1 = (bbox.min.z - p.z) / d.z;
124
+ const t2 = (bbox.max.z - p.z) / d.z;
125
+ tNear = Math.max(tNear, Math.min(t1, t2));
126
+ tFar = Math.min(tFar, Math.max(t1, t2));
127
+ }
128
+ else if (p.z < bbox.min.z || p.z > bbox.max.z) {
129
+ return { height: 0, center: position.clone() };
130
+ }
131
+ // Clamp: if position is inside or on boundary, tNear <= 0 <= tFar
132
+ // Forward distance = tFar (exit point along +dir)
133
+ // Backward distance = -tNear (exit point along -dir, i.e. behind us)
134
+ const forwardDist = tFar > 0 ? tFar : 0;
135
+ const backwardDist = tNear < 0 ? -tNear : 0;
136
+ const height = forwardDist + backwardDist;
137
+ const center = position.clone()
138
+ .add(dir.clone().multiplyScalar((forwardDist - backwardDist) / 2));
139
+ return { height, center };
140
+ }
141
+ /**
142
+ * Compute the blind-hole center: the hole entry is at the click point,
143
+ * the hole extends inward by `depth`. The cylinder center is at
144
+ * position + direction * (depth / 2).
145
+ *
146
+ * @param position - world-space hole entry point.
147
+ * @param direction - the drill direction (normalized internally).
148
+ * @param depth - blind-hole depth in millimeters.
149
+ * @returns the world-space cylinder center.
150
+ */
151
+ export function computeBlindHoleCenter(position, direction, depth) {
152
+ return position.clone().add(direction.clone().normalize().multiplyScalar(depth / 2));
153
+ }
154
+ /**
155
+ * Orient and position a Z-up hole geometry (axial along Z) to world space.
156
+ * Returns a new geometry with the transform baked in.
157
+ *
158
+ * @param geo - Z-up hole geometry centered on the origin.
159
+ * @param direction - world-space axis the hole should point along.
160
+ * @param center - world-space position of the hole center.
161
+ * @returns a transformed clone of the input geometry.
162
+ */
163
+ export function positionHoleGeometry(geo, direction, center) {
164
+ const result = geo.clone();
165
+ // Rotate Z axis to direction
166
+ const quat = new THREE.Quaternion().setFromUnitVectors(new THREE.Vector3(0, 0, 1), direction.clone().normalize());
167
+ result.applyQuaternion(quat);
168
+ // Translate to center
169
+ result.translate(center.x, center.y, center.z);
170
+ return result;
171
+ }
172
+ /**
173
+ * Resolve the drill direction based on params.direction and the face normal.
174
+ *
175
+ * - 'normal': inward direction = negated face normal
176
+ * - 'x'/'y'/'z': axis-aligned, flipped to point INTO the material
177
+ * (i.e. opposite to the face normal)
178
+ *
179
+ * @param direction - requested drill-direction reference.
180
+ * @param faceNormal - face normal at the drill position.
181
+ * @returns the resolved unit drill direction.
182
+ */
183
+ export function resolveDrillDirection(direction, faceNormal) {
184
+ if (direction === 'normal') {
185
+ return faceNormal.clone().negate().normalize();
186
+ }
187
+ // Axis-aligned direction
188
+ const axisDir = new THREE.Vector3(direction === 'x' ? 1 : 0, direction === 'y' ? 1 : 0, direction === 'z' ? 1 : 0);
189
+ // Flip to point INTO the material (opposite to face normal)
190
+ if (axisDir.dot(faceNormal) > 0) {
191
+ axisDir.negate();
192
+ }
193
+ return axisDir;
194
+ }
195
+ /**
196
+ * Drill a hole into a THREE.Mesh in world space, returning the resulting
197
+ * geometry in the mesh's local space.
198
+ *
199
+ * @param targetMesh - the mesh to drill into.
200
+ * @param params - drill-hole parameters.
201
+ * @param position - world-space hole entry point.
202
+ * @param faceNormal - world-space normal of the face at the entry point.
203
+ * @returns the drilled geometry in mesh-local space.
204
+ */
205
+ export async function executeDrillHole(targetMesh, params, position, faceNormal) {
206
+ targetMesh.updateMatrixWorld();
207
+ return executeDrillHoleOnGeometry(targetMesh.geometry, targetMesh.matrixWorld, params, position, faceNormal);
208
+ }
209
+ /**
210
+ * Core drill computation that accepts geometry + world matrix directly.
211
+ *
212
+ * This avoids the need to temporarily swap mesh.geometry when the mesh
213
+ * currently has a preview geometry on it. Callers that have the G0 geometry
214
+ * in a separate BufferGeometry can use this directly.
215
+ *
216
+ * @param sourceGeometry - the base geometry to drill (mesh-local space).
217
+ * @param worldMatrix - world transform of the source geometry.
218
+ * @param params - drill-hole parameters.
219
+ * @param position - world-space hole entry point.
220
+ * @param faceNormal - world-space face normal at the entry point.
221
+ * @returns the drilled geometry, transformed back into mesh-local space.
222
+ */
223
+ export async function executeDrillHoleOnGeometry(sourceGeometry, worldMatrix, params, position, faceNormal) {
224
+ // 1. Compute drill direction based on params.direction
225
+ const direction = resolveDrillDirection(params.direction, faceNormal);
226
+ // 2. Compute bbox from the source geometry (in world space)
227
+ sourceGeometry.computeBoundingBox();
228
+ const bbox = sourceGeometry.boundingBox.clone();
229
+ bbox.applyMatrix4(worldMatrix);
230
+ const bboxSize = bbox.getSize(new THREE.Vector3());
231
+ const bboxMax = Math.max(bboxSize.x, bboxSize.y, bboxSize.z);
232
+ // 3. Clamp depth to valid range: [0, bboxMax]
233
+ const safeDepth = Math.max(0, Math.min(params.depth, bboxMax));
234
+ // 4. Compute hole height and center
235
+ let holeHeight;
236
+ let holeCenter;
237
+ if (safeDepth === 0) {
238
+ // Through hole: clip to bbox
239
+ const dims = computeThroughHoleDimensions(position, direction, bbox);
240
+ // Add epsilon on both ends for clean CSG cut
241
+ holeHeight = dims.height + 0.2; // 0.1mm each side
242
+ holeCenter = dims.center;
243
+ }
244
+ else {
245
+ // Blind hole
246
+ holeHeight = safeDepth;
247
+ holeCenter = computeBlindHoleCenter(position, direction, safeDepth);
248
+ // For screw with head: the geometry is centered at (height + headHeight)/2,
249
+ // so the entry (-totalLength/2) must map to position. Adjust holeCenter
250
+ // by direction * headHeight/2 to compensate.
251
+ if (params.holeType === 'screw' && params.screwHead !== 'none') {
252
+ const spec = getScrewSpec(params.screwSystem, params.screwSpecIdx);
253
+ const headHeight = spec.dia;
254
+ holeCenter.add(direction.clone().normalize().multiplyScalar(headHeight / 2));
255
+ }
256
+ }
257
+ // 5. Build hole geometry (Z-up, centered on origin)
258
+ const holeGeo = buildHoleGeometry(params, holeHeight);
259
+ // 6. Position hole geometry to world space
260
+ const holeGeoWorld = positionHoleGeometry(holeGeo, direction, holeCenter);
261
+ // 7. Clone source geometry and transform to world space
262
+ const targetGeo = sourceGeometry.clone();
263
+ targetGeo.applyMatrix4(worldMatrix);
264
+ // 8. Convert to ManifoldMeshData
265
+ const targetManifold = geoToManifoldMesh(targetGeo);
266
+ const holeManifold = geoToManifoldMesh(holeGeoWorld);
267
+ // 9. Execute CSG subtract
268
+ const result = await computeBoolean([targetManifold, holeManifold], 'subtract');
269
+ // 10. Convert back to BufferGeometry
270
+ const resultGeo = manifoldMeshToGeo(result);
271
+ // 11. Transform back to mesh local space
272
+ const invMatrix = new THREE.Matrix4().copy(worldMatrix).invert();
273
+ resultGeo.applyMatrix4(invMatrix);
274
+ // Derive creased normals on the committed result so sharp edges stay sharp
275
+ // (the old computeVertexNormals() averaged on the welded shared-vertex
276
+ // topology and rounded them off). toCreasedNormals transforms correctly with
277
+ // applyMatrix4 above, but we re-derive to guarantee the final geometry uses
278
+ // the unified creased-normal logic regardless of the manifold output topology.
279
+ const finalGeo = deriveNormals(resultGeo);
280
+ resultGeo.dispose();
281
+ // 12. Cleanup
282
+ holeGeo.dispose();
283
+ holeGeoWorld.dispose();
284
+ targetGeo.dispose();
285
+ return finalGeo;
286
+ }
287
+ //# sourceMappingURL=DrillHoleCore.js.map