fluidcad 0.0.35 → 0.0.37
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.
- package/LICENSE.txt +21 -504
- package/README.md +1 -1
- package/bin/commands/login.js +33 -5
- package/bin/commands/mcp.js +3 -2
- package/bin/commands/publish.js +103 -8
- package/bin/lib/api-client.js +8 -0
- package/bin/lib/model-config.js +27 -4
- package/bin/lib/prompt.js +97 -0
- package/lib/dist/common/edge.d.ts +1 -1
- package/lib/dist/common/face.d.ts +1 -1
- package/lib/dist/common/scene-object.d.ts +6 -0
- package/lib/dist/common/scene-object.js +8 -0
- package/lib/dist/common/shape-factory.d.ts +1 -1
- package/lib/dist/common/shape-history-tracker.d.ts +1 -1
- package/lib/dist/common/shape.d.ts +1 -1
- package/lib/dist/common/solid.d.ts +1 -1
- package/lib/dist/common/transformable-primitive.d.ts +12 -1
- package/lib/dist/common/transformable-primitive.js +27 -0
- package/lib/dist/common/vertex.d.ts +1 -1
- package/lib/dist/common/wire.d.ts +1 -1
- package/lib/dist/core/2d/index.d.ts +1 -0
- package/lib/dist/core/2d/index.js +1 -0
- package/lib/dist/core/2d/text.d.ts +30 -0
- package/lib/dist/core/2d/text.js +37 -0
- package/lib/dist/core/helix.d.ts +20 -0
- package/lib/dist/core/helix.js +36 -0
- package/lib/dist/core/index.d.ts +3 -1
- package/lib/dist/core/index.js +2 -0
- package/lib/dist/core/interfaces.d.ts +180 -0
- package/lib/dist/core/wrap.d.ts +17 -0
- package/lib/dist/core/wrap.js +39 -0
- package/lib/dist/features/2d/text.d.ts +67 -0
- package/lib/dist/features/2d/text.js +320 -0
- package/lib/dist/features/cylinder.d.ts +3 -1
- package/lib/dist/features/cylinder.js +5 -2
- package/lib/dist/features/extrude-base.d.ts +1 -0
- package/lib/dist/features/extrude-to-face.d.ts +1 -0
- package/lib/dist/features/extrude-to-face.js +6 -0
- package/lib/dist/features/fillet.d.ts +1 -1
- package/lib/dist/features/helix.d.ts +41 -0
- package/lib/dist/features/helix.js +337 -0
- package/lib/dist/features/select.js +32 -8
- package/lib/dist/features/simple-extruder.d.ts +1 -1
- package/lib/dist/features/simple-extruder.js +7 -2
- package/lib/dist/features/sphere.d.ts +3 -1
- package/lib/dist/features/sphere.js +5 -2
- package/lib/dist/features/sweep.js +7 -2
- package/lib/dist/features/wrap.d.ts +39 -0
- package/lib/dist/features/wrap.js +116 -0
- package/lib/dist/filters/edge/belongs-to-face.d.ts +3 -1
- package/lib/dist/filters/edge/belongs-to-face.js +14 -10
- package/lib/dist/filters/filter.d.ts +1 -1
- package/lib/dist/filters/from-object.d.ts +1 -1
- package/lib/dist/filters/tangent-expander.d.ts +1 -1
- package/lib/dist/filters/tangent-expander.js +57 -40
- package/lib/dist/helpers/scene-helpers.d.ts +2 -0
- package/lib/dist/helpers/scene-helpers.js +1 -1
- package/lib/dist/index.d.ts +2 -0
- package/lib/dist/index.js +3 -1
- package/lib/dist/io/file-import.d.ts +7 -0
- package/lib/dist/io/file-import.js +28 -1
- package/lib/dist/io/font-registry.d.ts +45 -0
- package/lib/dist/io/font-registry.js +272 -0
- package/lib/dist/math/bspline-interpolation.d.ts +29 -0
- package/lib/dist/math/bspline-interpolation.js +194 -0
- package/lib/dist/oc/boolean-ops.d.ts +3 -1
- package/lib/dist/oc/boolean-ops.js +15 -1
- package/lib/dist/oc/color-transfer.d.ts +1 -1
- package/lib/dist/oc/constraints/constraint-helpers.d.ts +4 -4
- package/lib/dist/oc/constraints/curve/tangent-circle-solver.js +10 -9
- package/lib/dist/oc/constraints/curve/tangent-line-solver.js +5 -6
- package/lib/dist/oc/convert.d.ts +1 -1
- package/lib/dist/oc/draft-ops.d.ts +1 -1
- package/lib/dist/oc/edge-ops.d.ts +2 -2
- package/lib/dist/oc/edge-ops.js +13 -14
- package/lib/dist/oc/edge-props.d.ts +1 -1
- package/lib/dist/oc/edge-query.d.ts +1 -1
- package/lib/dist/oc/edge-query.js +3 -8
- package/lib/dist/oc/errors.d.ts +8 -0
- package/lib/dist/oc/errors.js +27 -0
- package/lib/dist/oc/explorer.d.ts +2 -2
- package/lib/dist/oc/extrude-ops.d.ts +28 -2
- package/lib/dist/oc/extrude-ops.js +56 -7
- package/lib/dist/oc/face-ops.d.ts +2 -1
- package/lib/dist/oc/face-ops.js +11 -0
- package/lib/dist/oc/face-props.d.ts +1 -1
- package/lib/dist/oc/face-query.d.ts +12 -1
- package/lib/dist/oc/face-query.js +39 -0
- package/lib/dist/oc/fillet-ops.d.ts +1 -1
- package/lib/dist/oc/fillet-ops.js +4 -4
- package/lib/dist/oc/geometry.d.ts +1 -1
- package/lib/dist/oc/geometry.js +12 -14
- package/lib/dist/oc/helix-ops.d.ts +37 -0
- package/lib/dist/oc/helix-ops.js +88 -0
- package/lib/dist/oc/hit-test.d.ts +1 -1
- package/lib/dist/oc/index.d.ts +4 -0
- package/lib/dist/oc/index.js +2 -0
- package/lib/dist/oc/init.d.ts +1 -1
- package/lib/dist/oc/init.js +1 -1
- package/lib/dist/oc/intersection.js +1 -1
- package/lib/dist/oc/io.d.ts +6 -6
- package/lib/dist/oc/io.js +31 -24
- package/lib/dist/oc/measure/classify.d.ts +34 -0
- package/lib/dist/oc/measure/classify.js +246 -0
- package/lib/dist/oc/measure/measure-ops.d.ts +9 -0
- package/lib/dist/oc/measure/measure-ops.js +210 -0
- package/lib/dist/oc/measure/measure-types.d.ts +39 -0
- package/lib/dist/oc/measure/measure-types.js +1 -0
- package/lib/dist/oc/measure/sampling.d.ts +9 -0
- package/lib/dist/oc/measure/sampling.js +77 -0
- package/lib/dist/oc/measure/vec.d.ts +13 -0
- package/lib/dist/oc/measure/vec.js +23 -0
- package/lib/dist/oc/mesh.d.ts +1 -1
- package/lib/dist/oc/mesh.js +40 -28
- package/lib/dist/oc/path-sampler.d.ts +29 -0
- package/lib/dist/oc/path-sampler.js +63 -0
- package/lib/dist/oc/props.d.ts +1 -1
- package/lib/dist/oc/props.js +4 -1
- package/lib/dist/oc/shape-hash.d.ts +26 -0
- package/lib/dist/oc/shape-hash.js +32 -0
- package/lib/dist/oc/shape-ops.d.ts +5 -3
- package/lib/dist/oc/shape-ops.js +6 -5
- package/lib/dist/oc/sweep-ops.d.ts +22 -1
- package/lib/dist/oc/sweep-ops.js +206 -18
- package/lib/dist/oc/text-outline.d.ts +62 -0
- package/lib/dist/oc/text-outline.js +212 -0
- package/lib/dist/oc/topology-index.d.ts +1 -1
- package/lib/dist/oc/vertex-ops.d.ts +1 -1
- package/lib/dist/oc/wire-ops.d.ts +1 -1
- package/lib/dist/oc/wire-ops.js +1 -1
- package/lib/dist/oc/wrap-development.d.ts +105 -0
- package/lib/dist/oc/wrap-development.js +179 -0
- package/lib/dist/oc/wrap-ops.d.ts +100 -0
- package/lib/dist/oc/wrap-ops.js +406 -0
- package/lib/dist/rendering/render-solid.js +10 -2
- package/lib/dist/scene-manager.d.ts +2 -0
- package/lib/dist/scene-manager.js +29 -0
- package/lib/dist/tests/features/cylinder-curve-filter.test.js +3 -3
- package/lib/dist/tests/features/extrude-to-face.test.js +38 -1
- package/lib/dist/tests/features/helix.test.d.ts +1 -0
- package/lib/dist/tests/features/helix.test.js +295 -0
- package/lib/dist/tests/features/repeat-primitive.test.d.ts +1 -0
- package/lib/dist/tests/features/repeat-primitive.test.js +60 -0
- package/lib/dist/tests/features/rib.test.js +6 -1
- package/lib/dist/tests/features/sweep.test.js +125 -1
- package/lib/dist/tests/features/text.test.d.ts +1 -0
- package/lib/dist/tests/features/text.test.js +347 -0
- package/lib/dist/tests/features/wrap-development.test.d.ts +1 -0
- package/lib/dist/tests/features/wrap-development.test.js +130 -0
- package/lib/dist/tests/features/wrap-extruded-target.test.d.ts +1 -0
- package/lib/dist/tests/features/wrap-extruded-target.test.js +106 -0
- package/lib/dist/tests/features/wrap-repeat.test.d.ts +1 -0
- package/lib/dist/tests/features/wrap-repeat.test.js +93 -0
- package/lib/dist/tests/features/wrap.test.d.ts +1 -0
- package/lib/dist/tests/features/wrap.test.js +331 -0
- package/lib/dist/tests/math/bspline-interpolation.test.d.ts +1 -0
- package/lib/dist/tests/math/bspline-interpolation.test.js +119 -0
- package/lib/dist/tests/measure.test.d.ts +1 -0
- package/lib/dist/tests/measure.test.js +288 -0
- package/lib/dist/tsconfig.tsbuildinfo +1 -1
- package/llm-docs/api/helix.md +64 -0
- package/llm-docs/api/index.json +11 -2
- package/llm-docs/api/text.md +52 -0
- package/llm-docs/api/types/helix.md +105 -0
- package/llm-docs/api/types/text.md +138 -0
- package/llm-docs/api/types/wrap.md +131 -0
- package/llm-docs/api/wrap.md +62 -0
- package/llm-docs/index.json +121 -1
- package/mcp/dist/server.js +20 -1
- package/mcp/dist/tools/inspection.d.ts +17 -0
- package/mcp/dist/tools/inspection.js +14 -0
- package/package.json +7 -3
- package/server/dist/fluidcad-server.d.ts +29 -0
- package/server/dist/fluidcad-server.js +40 -0
- package/server/dist/index.js +4 -2
- package/server/dist/model-package/pack.js +7 -6
- package/server/dist/model-package/types.d.ts +4 -3
- package/server/dist/preferences.d.ts +4 -0
- package/server/dist/preferences.js +2 -0
- package/server/dist/routes/measure.d.ts +3 -0
- package/server/dist/routes/measure.js +32 -0
- package/server/dist/routes/preferences.js +6 -0
- package/server/dist/routes/sketch-edits.js +2 -1
- package/ui/dist/assets/{index-CDJmUpFI.css → index-dAFdg2Un.css} +1 -1
- package/ui/dist/assets/{index-MRqwG9Vh.js → index-no7mtr5s.js} +149 -102
- package/ui/dist/index.html +2 -2
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import getSystemFontsDefault from "get-system-fonts";
|
|
2
|
+
import { openSync, create } from "fontkit";
|
|
3
|
+
import { readWorkspaceAssetBytes } from "./file-import.js";
|
|
4
|
+
// get-system-fonts ships a CJS default export; nodenext types the default import
|
|
5
|
+
// as the module namespace, so cast it to its callable signature (the runtime
|
|
6
|
+
// default import is the function).
|
|
7
|
+
const listSystemFonts = getSystemFontsDefault;
|
|
8
|
+
/**
|
|
9
|
+
* A `.font(...)` argument is treated as a local workspace file when it ends in a
|
|
10
|
+
* font extension; otherwise it is a system family name (e.g. "Arial").
|
|
11
|
+
*/
|
|
12
|
+
const FONT_FILE_RE = /\.(ttf|otf|ttc|woff2?|dfont)$/i;
|
|
13
|
+
/**
|
|
14
|
+
* Fallback families for common names that may be absent on a given OS. The OS
|
|
15
|
+
* matcher (fontconfig/CoreText/DirectWrite) would normally substitute these;
|
|
16
|
+
* since `get-system-fonts` only lists files, we substitute ourselves.
|
|
17
|
+
*/
|
|
18
|
+
const FAMILY_ALIASES = {
|
|
19
|
+
"arial": ["helvetica", "helvetica neue", "liberation sans", "arimo", "dejavu sans", "noto sans"],
|
|
20
|
+
"helvetica": ["helvetica neue", "arial", "liberation sans", "arimo", "dejavu sans", "noto sans"],
|
|
21
|
+
"times new roman": ["times", "liberation serif", "tinos", "dejavu serif", "noto serif"],
|
|
22
|
+
"times": ["times new roman", "liberation serif", "tinos", "dejavu serif"],
|
|
23
|
+
"courier new": ["courier", "liberation mono", "cousine", "dejavu sans mono", "noto sans mono"],
|
|
24
|
+
"courier": ["courier new", "liberation mono", "dejavu sans mono"],
|
|
25
|
+
"comic sans ms": ["comic neue", "dejavu sans"],
|
|
26
|
+
"segoe ui": ["liberation sans", "dejavu sans", "noto sans", "arial"],
|
|
27
|
+
"calibri": ["carlito", "liberation sans", "dejavu sans"],
|
|
28
|
+
"cambria": ["caladea", "liberation serif", "dejavu serif"],
|
|
29
|
+
"verdana": ["dejavu sans", "liberation sans", "noto sans"],
|
|
30
|
+
"tahoma": ["dejavu sans", "liberation sans"],
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Preferred default families (in order) when no font is specified.
|
|
34
|
+
*/
|
|
35
|
+
const DEFAULT_FAMILIES = [
|
|
36
|
+
"helvetica", "arial", "liberation sans", "dejavu sans", "noto sans",
|
|
37
|
+
"segoe ui", "san francisco", "roboto",
|
|
38
|
+
];
|
|
39
|
+
function normName(name) {
|
|
40
|
+
return name.trim().toLowerCase().replace(/\s+/g, " ");
|
|
41
|
+
}
|
|
42
|
+
function faces(opened) {
|
|
43
|
+
return opened.fonts ?? [opened];
|
|
44
|
+
}
|
|
45
|
+
function isItalic(font) {
|
|
46
|
+
if (font.italicAngle && Math.abs(font.italicAngle) > 0.01) {
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
return /italic|oblique/i.test(font.subfamilyName || "");
|
|
50
|
+
}
|
|
51
|
+
function weightOf(font) {
|
|
52
|
+
const os2 = font["OS/2"];
|
|
53
|
+
if (os2 && typeof os2.usWeightClass === "number") {
|
|
54
|
+
return os2.usWeightClass;
|
|
55
|
+
}
|
|
56
|
+
return /bold/i.test(font.subfamilyName || "") ? 700 : 400;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Resolves font names/paths to ready-to-use fontkit `Font` instances.
|
|
60
|
+
*
|
|
61
|
+
* `init()` enumerates system fonts (via `get-system-fonts`) once per process and
|
|
62
|
+
* builds a family -> variants index (via `fontkit`). `resolve()` is synchronous
|
|
63
|
+
* so it can be called from `SceneObject.build()`; it requires `init()` to have
|
|
64
|
+
* completed first, mirroring `getOC()` / `loadOC()`.
|
|
65
|
+
*/
|
|
66
|
+
export class FontRegistry {
|
|
67
|
+
static index = null;
|
|
68
|
+
static allFiles = [];
|
|
69
|
+
static fontCache = new Map();
|
|
70
|
+
static async init() {
|
|
71
|
+
if (this.index) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const index = new Map();
|
|
75
|
+
let files = [];
|
|
76
|
+
try {
|
|
77
|
+
files = await listSystemFonts();
|
|
78
|
+
}
|
|
79
|
+
catch (e) {
|
|
80
|
+
console.warn("FontRegistry: failed to list system fonts:", e.message);
|
|
81
|
+
}
|
|
82
|
+
this.allFiles = files;
|
|
83
|
+
for (const file of files) {
|
|
84
|
+
try {
|
|
85
|
+
for (const face of faces(openSync(file))) {
|
|
86
|
+
if (!face || !face.familyName) {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
const key = normName(face.familyName);
|
|
90
|
+
const variant = {
|
|
91
|
+
path: file,
|
|
92
|
+
psName: face.postscriptName,
|
|
93
|
+
weight: weightOf(face),
|
|
94
|
+
italic: isItalic(face),
|
|
95
|
+
};
|
|
96
|
+
const list = index.get(key);
|
|
97
|
+
if (list) {
|
|
98
|
+
list.push(variant);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
index.set(key, [variant]);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
// Skip unreadable / unsupported font files.
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
this.index = index;
|
|
110
|
+
console.debug(`FontRegistry: indexed ${index.size} families from ${files.length} files`);
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Resolves a font request into a ready fontkit `Font`. Never returns null:
|
|
114
|
+
* falls back through aliases -> default families -> any available font, and
|
|
115
|
+
* only throws if the machine has no usable fonts at all.
|
|
116
|
+
*/
|
|
117
|
+
static resolve(opts) {
|
|
118
|
+
const weight = opts.weight ?? 400;
|
|
119
|
+
const italic = opts.italic ?? false;
|
|
120
|
+
const name = opts.font?.trim();
|
|
121
|
+
// 1. Local workspace file, detected by extension.
|
|
122
|
+
if (name && FONT_FILE_RE.test(name)) {
|
|
123
|
+
return this.loadLocalFile(name);
|
|
124
|
+
}
|
|
125
|
+
const index = this.ensureInit();
|
|
126
|
+
// 2. Requested system family (+ aliases).
|
|
127
|
+
if (name) {
|
|
128
|
+
const font = this.openFirstRenderable(this.collectVariants(normName(name), true), weight, italic);
|
|
129
|
+
if (font) {
|
|
130
|
+
return font;
|
|
131
|
+
}
|
|
132
|
+
console.warn(`FontRegistry: font "${name}" not usable; falling back to a default font.`);
|
|
133
|
+
}
|
|
134
|
+
// 3. Default family priority list.
|
|
135
|
+
for (const fam of DEFAULT_FAMILIES) {
|
|
136
|
+
const font = this.openFirstRenderable(this.collectVariants(fam, false), weight, italic);
|
|
137
|
+
if (font) {
|
|
138
|
+
return font;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
// 4. Any indexed family.
|
|
142
|
+
const anyFamily = this.openFirstRenderable([...index.values()].flat(), weight, italic);
|
|
143
|
+
if (anyFamily) {
|
|
144
|
+
return anyFamily;
|
|
145
|
+
}
|
|
146
|
+
// 5. Any font file at all (index may be empty if name parsing failed).
|
|
147
|
+
for (const file of this.allFiles) {
|
|
148
|
+
const font = this.tryOpenFile(file);
|
|
149
|
+
if (font) {
|
|
150
|
+
return font;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
throw new Error('No fonts available on this system. Install a font package, or specify a ' +
|
|
154
|
+
'local font file, e.g. text(...).font("fonts/MyFont.ttf").');
|
|
155
|
+
}
|
|
156
|
+
static ensureInit() {
|
|
157
|
+
if (!this.index) {
|
|
158
|
+
throw new Error("Fonts not initialized. Call FontRegistry.init() first.");
|
|
159
|
+
}
|
|
160
|
+
return this.index;
|
|
161
|
+
}
|
|
162
|
+
/** Collects a family's variants plus its alias families' variants, unsorted. */
|
|
163
|
+
static collectVariants(key, useAliases) {
|
|
164
|
+
const index = this.ensureInit();
|
|
165
|
+
const out = [];
|
|
166
|
+
const direct = index.get(key);
|
|
167
|
+
if (direct) {
|
|
168
|
+
out.push(...direct);
|
|
169
|
+
}
|
|
170
|
+
if (useAliases) {
|
|
171
|
+
for (const alias of FAMILY_ALIASES[key] ?? []) {
|
|
172
|
+
const list = index.get(normName(alias));
|
|
173
|
+
if (list) {
|
|
174
|
+
out.push(...list);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return out;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Sorts variants by italic match then nearest weight, and opens the first that
|
|
182
|
+
* actually renders (skipping fonts that throw on outline access).
|
|
183
|
+
*/
|
|
184
|
+
static openFirstRenderable(list, weight, italic) {
|
|
185
|
+
const sorted = [...list].sort((a, b) => {
|
|
186
|
+
const ai = a.italic === italic ? 0 : 1;
|
|
187
|
+
const bi = b.italic === italic ? 0 : 1;
|
|
188
|
+
if (ai !== bi) {
|
|
189
|
+
return ai - bi;
|
|
190
|
+
}
|
|
191
|
+
return Math.abs(a.weight - weight) - Math.abs(b.weight - weight);
|
|
192
|
+
});
|
|
193
|
+
for (const variant of sorted) {
|
|
194
|
+
const font = this.openVariant(variant);
|
|
195
|
+
if (font) {
|
|
196
|
+
return font;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
/** Opens and validates a variant; caches the result, including known-bad (null). */
|
|
202
|
+
static openVariant(variant) {
|
|
203
|
+
const cacheKey = `${variant.path}|${variant.psName}`;
|
|
204
|
+
const cached = this.fontCache.get(cacheKey);
|
|
205
|
+
if (cached !== undefined) {
|
|
206
|
+
return cached;
|
|
207
|
+
}
|
|
208
|
+
let font = null;
|
|
209
|
+
try {
|
|
210
|
+
// Do NOT pass the postscriptName as openSync's 2nd argument: for a single
|
|
211
|
+
// (non-collection) font that arg is treated as a *variation instance*
|
|
212
|
+
// name and throws on non-variable fonts. Open the file, then pick the
|
|
213
|
+
// matching face out of a collection (.ttc) ourselves.
|
|
214
|
+
const list = faces(openSync(variant.path));
|
|
215
|
+
const picked = list.find(f => f.postscriptName === variant.psName) ?? list[0];
|
|
216
|
+
// Weight/italic come from face selection above; we never call fontkit
|
|
217
|
+
// getVariation() — some fonts declare an fvar wght axis without the
|
|
218
|
+
// variable outline tables and throw lazily at layout() time.
|
|
219
|
+
font = picked && this.canRender(picked) ? picked : null;
|
|
220
|
+
}
|
|
221
|
+
catch {
|
|
222
|
+
font = null;
|
|
223
|
+
}
|
|
224
|
+
this.fontCache.set(cacheKey, font);
|
|
225
|
+
return font;
|
|
226
|
+
}
|
|
227
|
+
/** All indexed system font family names (lowercased). Useful for tooling/tests. */
|
|
228
|
+
static availableFamilies() {
|
|
229
|
+
return [...this.ensureInit().keys()];
|
|
230
|
+
}
|
|
231
|
+
static tryOpenFile(file) {
|
|
232
|
+
try {
|
|
233
|
+
const face = faces(openSync(file))[0];
|
|
234
|
+
if (face?.familyName && this.canRender(face)) {
|
|
235
|
+
return face;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
catch {
|
|
239
|
+
// skip unreadable / unusable font file
|
|
240
|
+
}
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* A font is usable only if its outlines compute without throwing. Fonts that
|
|
245
|
+
* declare an fvar variation axis but lack the variable outline tables
|
|
246
|
+
* (gvar/glyf or CFF2) throw lazily when glyph paths are accessed.
|
|
247
|
+
*/
|
|
248
|
+
static canRender(font) {
|
|
249
|
+
try {
|
|
250
|
+
const glyph = font.glyphForCodePoint(65); // 'A'
|
|
251
|
+
void glyph.path.commands;
|
|
252
|
+
return true;
|
|
253
|
+
}
|
|
254
|
+
catch {
|
|
255
|
+
return false;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
static loadLocalFile(relPath) {
|
|
259
|
+
const cacheKey = `file:${relPath}`;
|
|
260
|
+
const cached = this.fontCache.get(cacheKey);
|
|
261
|
+
if (cached) {
|
|
262
|
+
return cached;
|
|
263
|
+
}
|
|
264
|
+
const bytes = readWorkspaceAssetBytes(relPath);
|
|
265
|
+
if (!bytes) {
|
|
266
|
+
throw new Error(`Font file not found in workspace: ${relPath}`);
|
|
267
|
+
}
|
|
268
|
+
const font = faces(create(Buffer.from(bytes)))[0];
|
|
269
|
+
this.fontCache.set(cacheKey, font);
|
|
270
|
+
return font;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global B-spline interpolation of planar points (The NURBS Book, §9.2.1):
|
|
3
|
+
* chord-length parameters, knots by averaging, and a banded linear system
|
|
4
|
+
* solved densely. The resulting curve passes through every input point and
|
|
5
|
+
* is C2 (for the cubic case).
|
|
6
|
+
*
|
|
7
|
+
* Exists because fluidcad-ocjs currently miscompiles the `Geom2dAPI` fitting
|
|
8
|
+
* classes (both their constructors and `Init` produce corrupted curves);
|
|
9
|
+
* `Geom2d_BSplineCurve`'s array constructor is unaffected, so we compute the
|
|
10
|
+
* poles/knots ourselves and hand them over.
|
|
11
|
+
*/
|
|
12
|
+
export interface XY {
|
|
13
|
+
x: number;
|
|
14
|
+
y: number;
|
|
15
|
+
}
|
|
16
|
+
export interface BSpline2dData {
|
|
17
|
+
poles: XY[];
|
|
18
|
+
/** Distinct knot values (OCC convention). */
|
|
19
|
+
knots: number[];
|
|
20
|
+
/** Multiplicity per distinct knot. */
|
|
21
|
+
multiplicities: number[];
|
|
22
|
+
degree: number;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Interpolates the given planar points with a B-spline of degree up to 3
|
|
26
|
+
* (lower for 2 or 3 points). The curve passes exactly through every point,
|
|
27
|
+
* in order, parameterized by chord length.
|
|
28
|
+
*/
|
|
29
|
+
export declare function interpolateBSpline2d(rawPoints: ReadonlyArray<XY>): BSpline2dData;
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global B-spline interpolation of planar points (The NURBS Book, §9.2.1):
|
|
3
|
+
* chord-length parameters, knots by averaging, and a banded linear system
|
|
4
|
+
* solved densely. The resulting curve passes through every input point and
|
|
5
|
+
* is C2 (for the cubic case).
|
|
6
|
+
*
|
|
7
|
+
* Exists because fluidcad-ocjs currently miscompiles the `Geom2dAPI` fitting
|
|
8
|
+
* classes (both their constructors and `Init` produce corrupted curves);
|
|
9
|
+
* `Geom2d_BSplineCurve`'s array constructor is unaffected, so we compute the
|
|
10
|
+
* poles/knots ourselves and hand them over.
|
|
11
|
+
*/
|
|
12
|
+
/** Largest knot span index whose half-open interval contains t. */
|
|
13
|
+
function findSpan(poleCount, degree, t, flatKnots) {
|
|
14
|
+
const n = poleCount - 1;
|
|
15
|
+
if (t >= flatKnots[n + 1]) {
|
|
16
|
+
return n;
|
|
17
|
+
}
|
|
18
|
+
let low = degree;
|
|
19
|
+
let high = n + 1;
|
|
20
|
+
let mid = (low + high) >> 1;
|
|
21
|
+
while (t < flatKnots[mid] || t >= flatKnots[mid + 1]) {
|
|
22
|
+
if (t < flatKnots[mid]) {
|
|
23
|
+
high = mid;
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
low = mid;
|
|
27
|
+
}
|
|
28
|
+
mid = (low + high) >> 1;
|
|
29
|
+
}
|
|
30
|
+
return mid;
|
|
31
|
+
}
|
|
32
|
+
/** Nonzero basis function values N[span-degree .. span] at t (Cox–de Boor). */
|
|
33
|
+
function basisFunctions(span, t, degree, flatKnots) {
|
|
34
|
+
const values = [1];
|
|
35
|
+
const left = [];
|
|
36
|
+
const right = [];
|
|
37
|
+
for (let j = 1; j <= degree; j++) {
|
|
38
|
+
left[j] = t - flatKnots[span + 1 - j];
|
|
39
|
+
right[j] = flatKnots[span + j] - t;
|
|
40
|
+
let saved = 0;
|
|
41
|
+
for (let r = 0; r < j; r++) {
|
|
42
|
+
const temp = values[r] / (right[r + 1] + left[j - r]);
|
|
43
|
+
values[r] = saved + right[r + 1] * temp;
|
|
44
|
+
saved = left[j - r] * temp;
|
|
45
|
+
}
|
|
46
|
+
values[j] = saved;
|
|
47
|
+
}
|
|
48
|
+
return values;
|
|
49
|
+
}
|
|
50
|
+
/** Gaussian elimination with partial pivoting; solves A·x = b for both RHS columns. */
|
|
51
|
+
function solveDense(matrix, rhsX, rhsY) {
|
|
52
|
+
const n = matrix.length;
|
|
53
|
+
for (let col = 0; col < n; col++) {
|
|
54
|
+
let pivot = col;
|
|
55
|
+
for (let row = col + 1; row < n; row++) {
|
|
56
|
+
if (Math.abs(matrix[row][col]) > Math.abs(matrix[pivot][col])) {
|
|
57
|
+
pivot = row;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (Math.abs(matrix[pivot][col]) < 1e-14) {
|
|
61
|
+
throw new Error("B-spline interpolation: singular system (degenerate or duplicate points)");
|
|
62
|
+
}
|
|
63
|
+
if (pivot !== col) {
|
|
64
|
+
[matrix[pivot], matrix[col]] = [matrix[col], matrix[pivot]];
|
|
65
|
+
[rhsX[pivot], rhsX[col]] = [rhsX[col], rhsX[pivot]];
|
|
66
|
+
[rhsY[pivot], rhsY[col]] = [rhsY[col], rhsY[pivot]];
|
|
67
|
+
}
|
|
68
|
+
for (let row = col + 1; row < n; row++) {
|
|
69
|
+
const factor = matrix[row][col] / matrix[col][col];
|
|
70
|
+
if (factor === 0) {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
for (let k = col; k < n; k++) {
|
|
74
|
+
matrix[row][k] -= factor * matrix[col][k];
|
|
75
|
+
}
|
|
76
|
+
rhsX[row] -= factor * rhsX[col];
|
|
77
|
+
rhsY[row] -= factor * rhsY[col];
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const x = new Array(n);
|
|
81
|
+
const y = new Array(n);
|
|
82
|
+
for (let row = n - 1; row >= 0; row--) {
|
|
83
|
+
let sumX = rhsX[row];
|
|
84
|
+
let sumY = rhsY[row];
|
|
85
|
+
for (let k = row + 1; k < n; k++) {
|
|
86
|
+
sumX -= matrix[row][k] * x[k];
|
|
87
|
+
sumY -= matrix[row][k] * y[k];
|
|
88
|
+
}
|
|
89
|
+
x[row] = sumX / matrix[row][row];
|
|
90
|
+
y[row] = sumY / matrix[row][row];
|
|
91
|
+
}
|
|
92
|
+
return { x, y };
|
|
93
|
+
}
|
|
94
|
+
/** Drops consecutive points closer than a millionth of the total chord. */
|
|
95
|
+
function dedupe(points) {
|
|
96
|
+
let total = 0;
|
|
97
|
+
for (let i = 1; i < points.length; i++) {
|
|
98
|
+
total += Math.hypot(points[i].x - points[i - 1].x, points[i].y - points[i - 1].y);
|
|
99
|
+
}
|
|
100
|
+
const minChord = Math.max(1e-12, total * 1e-6);
|
|
101
|
+
const result = [points[0]];
|
|
102
|
+
for (let i = 1; i < points.length; i++) {
|
|
103
|
+
const prev = result[result.length - 1];
|
|
104
|
+
if (Math.hypot(points[i].x - prev.x, points[i].y - prev.y) >= minChord) {
|
|
105
|
+
result.push(points[i]);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// Never drop the true endpoint: replace the last kept point if needed.
|
|
109
|
+
const last = points[points.length - 1];
|
|
110
|
+
const kept = result[result.length - 1];
|
|
111
|
+
if (kept.x !== last.x || kept.y !== last.y) {
|
|
112
|
+
if (result.length > 1) {
|
|
113
|
+
result[result.length - 1] = last;
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
result.push(last);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return result;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Interpolates the given planar points with a B-spline of degree up to 3
|
|
123
|
+
* (lower for 2 or 3 points). The curve passes exactly through every point,
|
|
124
|
+
* in order, parameterized by chord length.
|
|
125
|
+
*/
|
|
126
|
+
export function interpolateBSpline2d(rawPoints) {
|
|
127
|
+
const points = dedupe(rawPoints);
|
|
128
|
+
if (points.length < 2) {
|
|
129
|
+
throw new Error("B-spline interpolation needs at least two distinct points");
|
|
130
|
+
}
|
|
131
|
+
const n = points.length - 1;
|
|
132
|
+
const degree = Math.min(3, n);
|
|
133
|
+
// Chord-length parameters, normalized to [0, 1].
|
|
134
|
+
const params = new Array(points.length);
|
|
135
|
+
params[0] = 0;
|
|
136
|
+
for (let i = 1; i <= n; i++) {
|
|
137
|
+
params[i] = params[i - 1] + Math.hypot(points[i].x - points[i - 1].x, points[i].y - points[i - 1].y);
|
|
138
|
+
}
|
|
139
|
+
const total = params[n];
|
|
140
|
+
for (let i = 1; i <= n; i++) {
|
|
141
|
+
params[i] /= total;
|
|
142
|
+
}
|
|
143
|
+
params[n] = 1;
|
|
144
|
+
// Knots by averaging (NURBS book eq. 9.8): clamped ends, one internal knot
|
|
145
|
+
// per unconstrained pole.
|
|
146
|
+
const flatKnots = [];
|
|
147
|
+
for (let i = 0; i <= degree; i++) {
|
|
148
|
+
flatKnots.push(0);
|
|
149
|
+
}
|
|
150
|
+
for (let j = 1; j <= n - degree; j++) {
|
|
151
|
+
let sum = 0;
|
|
152
|
+
for (let i = j; i <= j + degree - 1; i++) {
|
|
153
|
+
sum += params[i];
|
|
154
|
+
}
|
|
155
|
+
flatKnots.push(sum / degree);
|
|
156
|
+
}
|
|
157
|
+
for (let i = 0; i <= degree; i++) {
|
|
158
|
+
flatKnots.push(1);
|
|
159
|
+
}
|
|
160
|
+
// Interpolation system: one basis row per parameter.
|
|
161
|
+
const matrix = [];
|
|
162
|
+
const rhsX = [];
|
|
163
|
+
const rhsY = [];
|
|
164
|
+
for (let k = 0; k <= n; k++) {
|
|
165
|
+
const row = new Array(n + 1).fill(0);
|
|
166
|
+
const span = findSpan(n + 1, degree, params[k], flatKnots);
|
|
167
|
+
const values = basisFunctions(span, params[k], degree, flatKnots);
|
|
168
|
+
for (let j = 0; j <= degree; j++) {
|
|
169
|
+
row[span - degree + j] = values[j];
|
|
170
|
+
}
|
|
171
|
+
matrix.push(row);
|
|
172
|
+
rhsX.push(points[k].x);
|
|
173
|
+
rhsY.push(points[k].y);
|
|
174
|
+
}
|
|
175
|
+
const solved = solveDense(matrix, rhsX, rhsY);
|
|
176
|
+
// Convert the flat knot vector to OCC's distinct-knots + multiplicities.
|
|
177
|
+
const knots = [];
|
|
178
|
+
const multiplicities = [];
|
|
179
|
+
for (const knot of flatKnots) {
|
|
180
|
+
if (knots.length > 0 && knot === knots[knots.length - 1]) {
|
|
181
|
+
multiplicities[multiplicities.length - 1]++;
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
knots.push(knot);
|
|
185
|
+
multiplicities.push(1);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return {
|
|
189
|
+
poles: solved.x.map((x, i) => ({ x, y: solved.y[i] })),
|
|
190
|
+
knots,
|
|
191
|
+
multiplicities,
|
|
192
|
+
degree,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import type { BRepAlgoAPI_Cut, TopoDS_Shape } from "
|
|
1
|
+
import type { BRepAlgoAPI_Cut, TopoDS_Shape } from "ocjs-fluidcad";
|
|
2
2
|
import { Shape } from "../common/shape.js";
|
|
3
3
|
import { Solid } from "../common/solid.js";
|
|
4
4
|
import { Edge } from "../common/edge.js";
|
|
5
5
|
import { Face } from "../common/face.js";
|
|
6
6
|
import { Plane } from "../math/plane.js";
|
|
7
7
|
export declare class BooleanOps {
|
|
8
|
+
private static readonly FEATURE_BOOLEAN_FUZZY;
|
|
8
9
|
static cutShapes(shape: Shape, tool: Shape): Shape;
|
|
9
10
|
static cutShapesRaw(shape: TopoDS_Shape, tool: TopoDS_Shape): TopoDS_Shape;
|
|
10
11
|
static cutMultiShape(stocks: Shape[], tools: Shape[], plane?: Plane, cutDistance?: number): {
|
|
@@ -33,6 +34,7 @@ export declare class BooleanOps {
|
|
|
33
34
|
*/
|
|
34
35
|
static fuseStockAndTools(stock: Shape[], tools: Shape[], opts?: {
|
|
35
36
|
glue?: 'full' | 'shift';
|
|
37
|
+
skipSimplify?: boolean;
|
|
36
38
|
}): {
|
|
37
39
|
result: Shape[];
|
|
38
40
|
modifiedShapes: Shape[];
|
|
@@ -7,6 +7,16 @@ import { Edge } from "../common/edge.js";
|
|
|
7
7
|
import { Face } from "../common/face.js";
|
|
8
8
|
import { EdgeOps } from "./edge-ops.js";
|
|
9
9
|
export class BooleanOps {
|
|
10
|
+
// Fuzzy tolerance (mm) for the feature cut/fuse builders. A swept tube whose
|
|
11
|
+
// path lies on a face it's cut from / fused to (e.g. a helical thread at the
|
|
12
|
+
// cylinder's own radius) touches that face tangentially along the contact
|
|
13
|
+
// curves; at zero fuzz OCCT's BOPAlgo silently no-ops (cut removes nothing,
|
|
14
|
+
// fuse returns an empty compound → "Unknown shape type"). A small fuzzy value
|
|
15
|
+
// resolves the near-coincident contact into clean intersections. 1e-4 is the
|
|
16
|
+
// smallest that works reliably here (1e-5 lands in a worse-than-zero regime);
|
|
17
|
+
// at 1e-4 mm it's far below any real feature size, so well-separated geometry
|
|
18
|
+
// is unaffected.
|
|
19
|
+
static FEATURE_BOOLEAN_FUZZY = 1e-4;
|
|
10
20
|
static cutShapes(shape, tool) {
|
|
11
21
|
const result = BooleanOps.cutShapesRaw(shape.getShape(), tool.getShape());
|
|
12
22
|
return ShapeFactory.fromShape(result);
|
|
@@ -49,6 +59,7 @@ export class BooleanOps {
|
|
|
49
59
|
cutMaker.SetTools(toolList);
|
|
50
60
|
cutMaker.SetNonDestructive(true);
|
|
51
61
|
cutMaker.SetRunParallel(true);
|
|
62
|
+
cutMaker.SetFuzzyValue(BooleanOps.FEATURE_BOOLEAN_FUZZY);
|
|
52
63
|
cutMaker.Build(progress);
|
|
53
64
|
const result = cutMaker.Shape();
|
|
54
65
|
const resultSolids = Explorer.findShapes(result, Explorer.getOcShapeType("solid"));
|
|
@@ -162,6 +173,7 @@ export class BooleanOps {
|
|
|
162
173
|
builder.SetNonDestructive(true);
|
|
163
174
|
builder.SetCheckInverted(true);
|
|
164
175
|
builder.SetRunParallel(true);
|
|
176
|
+
builder.SetFuzzyValue(BooleanOps.FEATURE_BOOLEAN_FUZZY);
|
|
165
177
|
if (opts?.glue === 'full') {
|
|
166
178
|
builder.SetGlue(oc.BOPAlgo_GlueEnum.BOPAlgo_GlueFull);
|
|
167
179
|
}
|
|
@@ -185,7 +197,9 @@ export class BooleanOps {
|
|
|
185
197
|
builder.SetTools(toolList);
|
|
186
198
|
const progress = new oc.Message_ProgressRange();
|
|
187
199
|
builder.Build(progress);
|
|
188
|
-
|
|
200
|
+
if (!opts?.skipSimplify) {
|
|
201
|
+
builder.SimplifyResult(false, true, oc.Precision.Angular());
|
|
202
|
+
}
|
|
189
203
|
const resultShape = builder.Shape();
|
|
190
204
|
const rawShapes = Explorer.findAllShapes(resultShape);
|
|
191
205
|
const result = rawShapes.map(s => ShapeFactory.fromShape(s));
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import type { GccEnt_Position, gp_Circ, gp_Circ2d, gp_Lin, gp_Lin2d, gp_Pln, gp_Pnt, gp_Pnt2d
|
|
1
|
+
import type { GccEnt_Position, Geom_Curve, gp_Circ, gp_Circ2d, gp_Lin, gp_Lin2d, gp_Pln, gp_Pnt, gp_Pnt2d } from "ocjs-fluidcad";
|
|
2
2
|
import { ConstraintQualifier } from "../../features/2d/constraints/qualified-geometry.js";
|
|
3
3
|
import { Point2D } from "../../math/point.js";
|
|
4
4
|
import { Edge } from "../../common/edge.js";
|
|
5
5
|
import { Shape } from "../../common/shape.js";
|
|
6
6
|
import { Plane } from "../../math/plane.js";
|
|
7
7
|
export declare function get2dGeometry<T extends gp_Circ | gp_Lin | gp_Pnt>(plane: gp_Pln, geometry: T): gp_Lin2d | gp_Circ2d | gp_Pnt2d;
|
|
8
|
-
export declare function get2dCurve(plane: gp_Pln, curve:
|
|
8
|
+
export declare function get2dCurve(plane: gp_Pln, curve: Geom_Curve): import("ocjs-fluidcad").Geom2d_Curve;
|
|
9
9
|
export declare function getQualifier(qualifier: ConstraintQualifier): GccEnt_Position;
|
|
10
|
-
export declare function getQualifiedCurve(plane: gp_Pln, curve:
|
|
11
|
-
export declare function getQualifiedGeometry(plane: gp_Pln, geometry: gp_Circ | gp_Lin, qualifier: ConstraintQualifier): import("
|
|
10
|
+
export declare function getQualifiedCurve(plane: gp_Pln, curve: Geom_Curve, qualifier: ConstraintQualifier): import("ocjs-fluidcad").Geom2dGcc_QualifiedCurve;
|
|
11
|
+
export declare function getQualifiedGeometry(plane: gp_Pln, geometry: gp_Circ | gp_Lin, qualifier: ConstraintQualifier): import("ocjs-fluidcad").GccEnt_QualifiedCirc | import("ocjs-fluidcad").GccEnt_QualifiedLin;
|
|
12
12
|
export declare function calculateTangent(solutions: {
|
|
13
13
|
center: gp_Pnt2d;
|
|
14
14
|
radius: number;
|
|
@@ -67,15 +67,16 @@ export class CurveTangentCircleSolver {
|
|
|
67
67
|
const tolerance = oc.Precision.Angular();
|
|
68
68
|
const [pln, disposePln] = Convert.toGpPln(plane);
|
|
69
69
|
const [pnt, disposePnt] = Convert.toGpPnt2d(vertex.toPoint2D());
|
|
70
|
+
// Handles are unwrapped in V8: pass the Geom2d_CartesianPoint straight to the
|
|
71
|
+
// solver (constructing the abstract oc.Geom2d_Point base throws at runtime).
|
|
70
72
|
const geom2dPnt = new oc.Geom2d_CartesianPoint(pnt);
|
|
71
|
-
const handle = new oc.Handle_Geom2d_Point(geom2dPnt);
|
|
72
73
|
disposePnt();
|
|
73
74
|
const curve = this.getCurve(lineShape.shape);
|
|
74
75
|
const qualifiedCurve = getQualifiedCurve(pln, curve, lineShape.qualifier);
|
|
75
|
-
const solver = new oc.Geom2dGcc_Circ2d2TanRad(qualifiedCurve,
|
|
76
|
+
const solver = new oc.Geom2dGcc_Circ2d2TanRad(qualifiedCurve, geom2dPnt, radius, tolerance);
|
|
76
77
|
const solutions = this.getSolutions(solver, plane);
|
|
77
78
|
disposePln();
|
|
78
|
-
|
|
79
|
+
geom2dPnt.delete();
|
|
79
80
|
return solutions;
|
|
80
81
|
}
|
|
81
82
|
getSolutions(solver, plane) {
|
|
@@ -102,11 +103,11 @@ export class CurveTangentCircleSolver {
|
|
|
102
103
|
}
|
|
103
104
|
getCurve(shape) {
|
|
104
105
|
const oc = getOC();
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
return
|
|
106
|
+
// BRepAdaptor_Curve no longer exposes Curve() in OCCT 8.0. BRep_Tool.Curve
|
|
107
|
+
// returns the underlying Geom_Curve with the edge's location applied (world
|
|
108
|
+
// space) — the same geometry the adaptor chain used to yield — which is what
|
|
109
|
+
// GeomAPI.To2d / the Gcc qualifier expect.
|
|
110
|
+
const edge = oc.TopoDS.Edge(shape.getShape());
|
|
111
|
+
return oc.BRep_Tool.Curve(edge, 0, 1).returnValue;
|
|
111
112
|
}
|
|
112
113
|
}
|
|
@@ -73,11 +73,10 @@ export class CurveTangentLineSolver {
|
|
|
73
73
|
}
|
|
74
74
|
getCurve(shape) {
|
|
75
75
|
const oc = getOC();
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
return handle;
|
|
76
|
+
// BRepAdaptor_Curve no longer exposes Curve() in OCCT 8.0. BRep_Tool.Curve
|
|
77
|
+
// returns the underlying Geom_Curve with the edge's location applied (world
|
|
78
|
+
// space) — the same geometry the adaptor chain used to yield.
|
|
79
|
+
const edge = oc.TopoDS.Edge(shape.getShape());
|
|
80
|
+
return oc.BRep_Tool.Curve(edge, 0, 1).returnValue;
|
|
82
81
|
}
|
|
83
82
|
}
|
package/lib/dist/oc/convert.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { gp_Pnt, gp_Pnt2d, gp_Vec, gp_Dir, gp_Ax1, gp_Ax2, gp_Ax3, gp_Pln, gp_Trsf, gp_Quaternion } from "
|
|
1
|
+
import type { gp_Pnt, gp_Pnt2d, gp_Vec, gp_Dir, gp_Ax1, gp_Ax2, gp_Ax3, gp_Pln, gp_Trsf, gp_Quaternion } from "ocjs-fluidcad";
|
|
2
2
|
import { Point, Point2D } from "../math/point.js";
|
|
3
3
|
import { Vector3d } from "../math/vector3d.js";
|
|
4
4
|
import { Axis } from "../math/axis.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { TopoDS_Edge, TopoDS_Vertex } from "
|
|
1
|
+
import type { TopoDS_Edge, TopoDS_Vertex } from "ocjs-fluidcad";
|
|
2
2
|
import { Axis } from "../math/axis.js";
|
|
3
3
|
import { Point } from "../math/point.js";
|
|
4
4
|
import { Vector3d } from "../math/vector3d.js";
|
|
@@ -20,7 +20,7 @@ export declare class EdgeOps {
|
|
|
20
20
|
static getLastVertexRaw(edge: TopoDS_Edge): TopoDS_Vertex;
|
|
21
21
|
static edgeToAxisRaw(edge: TopoDS_Edge): Axis;
|
|
22
22
|
static axisToEdgeRaw(axis: Axis): TopoDS_Edge;
|
|
23
|
-
static edgeMiddlePoint(edge: TopoDS_Edge): import("
|
|
23
|
+
static edgeMiddlePoint(edge: TopoDS_Edge): import("ocjs-fluidcad").gp_Pnt;
|
|
24
24
|
static getVertexPointRaw(vertex: TopoDS_Vertex): Point;
|
|
25
25
|
static getEdgeMidPointRaw(edge: TopoDS_Edge): Point;
|
|
26
26
|
static reverseEdgeRaw(edge: TopoDS_Edge): TopoDS_Edge;
|