fluidcad 0.0.37 → 0.0.39
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/lib/dist/core/2d/aline.d.ts +0 -8
- package/lib/dist/core/2d/aline.js +3 -22
- package/lib/dist/core/2d/arc.d.ts +1 -23
- package/lib/dist/core/2d/arc.js +11 -28
- package/lib/dist/core/2d/circle.d.ts +1 -8
- package/lib/dist/core/2d/circle.js +7 -24
- package/lib/dist/core/2d/ellipse.d.ts +1 -17
- package/lib/dist/core/2d/ellipse.js +7 -45
- package/lib/dist/core/2d/hline.d.ts +0 -7
- package/lib/dist/core/2d/hline.js +5 -27
- package/lib/dist/core/2d/intersect.d.ts +0 -7
- package/lib/dist/core/2d/intersect.js +0 -13
- package/lib/dist/core/2d/line.d.ts +1 -8
- package/lib/dist/core/2d/line.js +6 -24
- package/lib/dist/core/2d/offset.d.ts +1 -11
- package/lib/dist/core/2d/offset.js +0 -16
- package/lib/dist/core/2d/polygon.d.ts +1 -17
- package/lib/dist/core/2d/polygon.js +18 -37
- package/lib/dist/core/2d/project.d.ts +0 -7
- package/lib/dist/core/2d/project.js +0 -13
- package/lib/dist/core/2d/rect.d.ts +1 -9
- package/lib/dist/core/2d/rect.js +0 -19
- package/lib/dist/core/2d/slot.d.ts +0 -23
- package/lib/dist/core/2d/slot.js +18 -37
- package/lib/dist/core/2d/text.d.ts +0 -7
- package/lib/dist/core/2d/text.js +0 -14
- package/lib/dist/core/2d/vline.d.ts +0 -7
- package/lib/dist/core/2d/vline.js +5 -23
- package/lib/dist/core/fillet.js +7 -0
- package/lib/dist/core/index.d.ts +1 -1
- package/lib/dist/core/interfaces.d.ts +43 -0
- package/lib/dist/core/plane.d.ts +26 -6
- package/lib/dist/core/plane.js +21 -44
- package/lib/dist/features/2d/connect.d.ts +2 -0
- package/lib/dist/features/2d/connect.js +3 -0
- package/lib/dist/features/2d/intersect.js +4 -1
- package/lib/dist/features/2d/offset.js +2 -2
- package/lib/dist/features/frozen-geometry.d.ts +29 -0
- package/lib/dist/features/frozen-geometry.js +62 -0
- package/lib/dist/features/loft.d.ts +38 -1
- package/lib/dist/features/loft.js +133 -8
- package/lib/dist/features/plane-from-object.d.ts +16 -4
- package/lib/dist/features/plane-from-object.js +101 -8
- package/lib/dist/features/rib.js +11 -1
- package/lib/dist/features/shell.d.ts +1 -1
- package/lib/dist/features/shell.js +9 -0
- package/lib/dist/features/sweep.d.ts +11 -1
- package/lib/dist/features/sweep.js +43 -7
- package/lib/dist/helpers/clone-transform.js +62 -5
- package/lib/dist/helpers/scene-helpers.d.ts +12 -0
- package/lib/dist/helpers/scene-helpers.js +26 -0
- package/lib/dist/math/bspline-interpolation.d.ts +33 -6
- package/lib/dist/math/bspline-interpolation.js +187 -84
- package/lib/dist/oc/loft/constrained-loft.d.ts +32 -0
- package/lib/dist/oc/loft/constrained-loft.js +61 -0
- package/lib/dist/oc/loft/curve-data.d.ts +18 -0
- package/lib/dist/oc/loft/curve-data.js +54 -0
- package/lib/dist/oc/loft/curve-eval.d.ts +23 -0
- package/lib/dist/oc/loft/curve-eval.js +84 -0
- package/lib/dist/oc/loft/guided-loft.d.ts +89 -0
- package/lib/dist/oc/loft/guided-loft.js +368 -0
- package/lib/dist/oc/loft/section-compatibility.d.ts +131 -0
- package/lib/dist/oc/loft/section-compatibility.js +481 -0
- package/lib/dist/oc/loft/section-curve.d.ts +53 -0
- package/lib/dist/oc/loft/section-curve.js +264 -0
- package/lib/dist/oc/loft/skinning.d.ts +104 -0
- package/lib/dist/oc/loft/skinning.js +299 -0
- package/lib/dist/oc/loft-ops.d.ts +28 -1
- package/lib/dist/oc/loft-ops.js +31 -1
- package/lib/dist/oc/ncollection.d.ts +19 -0
- package/lib/dist/oc/ncollection.js +69 -0
- package/lib/dist/oc/rib-ops.js +5 -17
- package/lib/dist/oc/sweep-ops.d.ts +7 -16
- package/lib/dist/oc/sweep-ops.js +67 -99
- package/lib/dist/oc/thin-face-maker.d.ts +0 -19
- package/lib/dist/oc/thin-face-maker.js +3 -68
- package/lib/dist/oc/wire-extend-ops.d.ts +12 -0
- package/lib/dist/oc/wire-extend-ops.js +42 -0
- package/lib/dist/oc/wire-ops.d.ts +28 -2
- package/lib/dist/oc/wire-ops.js +88 -4
- package/lib/dist/oc/wrap-ops.js +8 -15
- package/lib/dist/tests/features/2d/circle.test.js +0 -10
- package/lib/dist/tests/features/2d/ellipse.test.js +0 -30
- package/lib/dist/tests/features/2d/line.test.js +0 -5
- package/lib/dist/tests/features/2d/offset.test.js +74 -11
- package/lib/dist/tests/features/2d/polygon.test.js +0 -16
- package/lib/dist/tests/features/2d/rect.test.js +0 -11
- package/lib/dist/tests/features/2d/slot.test.js +0 -8
- package/lib/dist/tests/features/fillet2d.test.js +37 -1
- package/lib/dist/tests/features/loft-conditions.test.d.ts +1 -0
- package/lib/dist/tests/features/loft-conditions.test.js +246 -0
- package/lib/dist/tests/features/loft-guides.test.d.ts +1 -0
- package/lib/dist/tests/features/loft-guides.test.js +276 -0
- package/lib/dist/tests/features/plane.test.js +95 -0
- package/lib/dist/tests/features/repeat-sweep-borrowed-path.test.d.ts +1 -0
- package/lib/dist/tests/features/repeat-sweep-borrowed-path.test.js +35 -0
- package/lib/dist/tests/features/rib.test.js +50 -0
- package/lib/dist/tests/features/shell.test.js +35 -1
- package/lib/dist/tests/features/sweep.test.js +127 -1
- package/lib/dist/tests/features/text.test.js +33 -14
- package/lib/dist/tests/math/bspline-interpolation.test.js +97 -1
- package/lib/dist/tsconfig.tsbuildinfo +1 -1
- package/llm-docs/api/index.json +1 -1
- package/llm-docs/api/types/loft.md +36 -1
- package/llm-docs/api/types/sweep.md +13 -1
- package/llm-docs/api/types/text.md +2 -2
- package/llm-docs/index.json +5 -5
- package/package.json +1 -1
- package/server/dist/fluidcad-server.d.ts +1 -1
- package/server/dist/fluidcad-server.js +11 -1
- package/server/dist/routes/params.js +1 -1
- package/ui/dist/assets/{index-no7mtr5s.js → index-D8zV21wB.js} +83 -83
- package/ui/dist/index.html +1 -1
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Wire } from "../../common/wire.js";
|
|
2
|
+
import { Solid } from "../../common/solid.js";
|
|
3
|
+
import { LoftEndCondition } from "./skinning.js";
|
|
4
|
+
export type { LoftConditionKind, LoftEndCondition } from "./skinning.js";
|
|
5
|
+
/**
|
|
6
|
+
* Loft with start/end conditions. OCC's `BRepOffsetAPI_ThruSections` cannot
|
|
7
|
+
* constrain end tangency, so this path skins the surface itself: profiles
|
|
8
|
+
* become compatible B-spline sections (`SectionCompatibility`), matching pole
|
|
9
|
+
* columns are interpolated along the loft with the end derivatives pinned,
|
|
10
|
+
* and the resulting surface is capped and sewn into a solid (`Skinning`).
|
|
11
|
+
*
|
|
12
|
+
* Conditions:
|
|
13
|
+
* - `normal`: the surface leaves the profile along the profile's plane
|
|
14
|
+
* normal — a perpendicular takeoff.
|
|
15
|
+
* - `tangent`: the surface leaves the profile inside the profile's plane,
|
|
16
|
+
* directed outward — profiles become tangency planes (e.g. a barrel from
|
|
17
|
+
* two stacked circles). Negative magnitudes direct it inward.
|
|
18
|
+
*/
|
|
19
|
+
export declare class ConstrainedLoft {
|
|
20
|
+
static build(wires: Wire[], startCondition: LoftEndCondition | undefined, endCondition: LoftEndCondition | undefined): Solid[];
|
|
21
|
+
/**
|
|
22
|
+
* Thin-walled conditioned loft, assembled directly: outer wall, inner
|
|
23
|
+
* wall, and two planar ring caps sewn into one solid. Cutting the inner
|
|
24
|
+
* loft out of the outer with a boolean instead takes OCC seconds — two
|
|
25
|
+
* nearly-parallel B-spline shells are the pave-filler's worst case — and
|
|
26
|
+
* the walls are already exact offsets, so no boolean is needed.
|
|
27
|
+
*/
|
|
28
|
+
static buildThin(outerWires: Wire[], innerWires: Wire[], startCondition: LoftEndCondition | undefined, endCondition: LoftEndCondition | undefined): Solid[];
|
|
29
|
+
private static skinWires;
|
|
30
|
+
/** Planar ring between the outer and inner wall boundaries at one end. */
|
|
31
|
+
private static ringCap;
|
|
32
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Wire } from "../../common/wire.js";
|
|
2
|
+
import { FaceOps } from "../face-ops.js";
|
|
3
|
+
import { SectionCompatibility } from "./section-compatibility.js";
|
|
4
|
+
import { Skinning } from "./skinning.js";
|
|
5
|
+
/**
|
|
6
|
+
* Loft with start/end conditions. OCC's `BRepOffsetAPI_ThruSections` cannot
|
|
7
|
+
* constrain end tangency, so this path skins the surface itself: profiles
|
|
8
|
+
* become compatible B-spline sections (`SectionCompatibility`), matching pole
|
|
9
|
+
* columns are interpolated along the loft with the end derivatives pinned,
|
|
10
|
+
* and the resulting surface is capped and sewn into a solid (`Skinning`).
|
|
11
|
+
*
|
|
12
|
+
* Conditions:
|
|
13
|
+
* - `normal`: the surface leaves the profile along the profile's plane
|
|
14
|
+
* normal — a perpendicular takeoff.
|
|
15
|
+
* - `tangent`: the surface leaves the profile inside the profile's plane,
|
|
16
|
+
* directed outward — profiles become tangency planes (e.g. a barrel from
|
|
17
|
+
* two stacked circles). Negative magnitudes direct it inward.
|
|
18
|
+
*/
|
|
19
|
+
export class ConstrainedLoft {
|
|
20
|
+
static build(wires, startCondition, endCondition) {
|
|
21
|
+
const compatible = ConstrainedLoft.skinWires(wires);
|
|
22
|
+
const skinned = Skinning.skinSections(compatible, startCondition, endCondition);
|
|
23
|
+
return [Skinning.buildLoftSolid(compatible, skinned.grid, skinned.vBasis)];
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Thin-walled conditioned loft, assembled directly: outer wall, inner
|
|
27
|
+
* wall, and two planar ring caps sewn into one solid. Cutting the inner
|
|
28
|
+
* loft out of the outer with a boolean instead takes OCC seconds — two
|
|
29
|
+
* nearly-parallel B-spline shells are the pave-filler's worst case — and
|
|
30
|
+
* the walls are already exact offsets, so no boolean is needed.
|
|
31
|
+
*/
|
|
32
|
+
static buildThin(outerWires, innerWires, startCondition, endCondition) {
|
|
33
|
+
const outer = ConstrainedLoft.skinWires(outerWires);
|
|
34
|
+
const inner = ConstrainedLoft.skinWires(innerWires);
|
|
35
|
+
const outerSkin = Skinning.skinSections(outer, startCondition, endCondition);
|
|
36
|
+
const innerSkin = Skinning.skinSections(inner, startCondition, endCondition);
|
|
37
|
+
const faces = [
|
|
38
|
+
...Skinning.sideFaces(outer, outerSkin.grid, outerSkin.vBasis),
|
|
39
|
+
...Skinning.sideFaces(inner, innerSkin.grid, innerSkin.vBasis),
|
|
40
|
+
ConstrainedLoft.ringCap(outer, outerSkin, inner, innerSkin, false),
|
|
41
|
+
ConstrainedLoft.ringCap(outer, outerSkin, inner, innerSkin, true),
|
|
42
|
+
];
|
|
43
|
+
return [Skinning.sewSolid(faces)];
|
|
44
|
+
}
|
|
45
|
+
static skinWires(wires) {
|
|
46
|
+
for (const wire of wires) {
|
|
47
|
+
if (!wire.isClosed()) {
|
|
48
|
+
throw new Error("Loft with start/end conditions requires closed profiles.");
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return SectionCompatibility.build(wires.map(w => w.getShape()));
|
|
52
|
+
}
|
|
53
|
+
/** Planar ring between the outer and inner wall boundaries at one end. */
|
|
54
|
+
static ringCap(outer, outerSkin, inner, innerSkin, isEnd) {
|
|
55
|
+
const column = (grid) => grid.map(row => row[isEnd ? row.length - 1 : 0]);
|
|
56
|
+
const outerWire = new Wire(Skinning.capWire(outer, column(outerSkin.grid)));
|
|
57
|
+
const innerWire = new Wire(Skinning.capWire(inner, column(innerSkin.grid)));
|
|
58
|
+
const ring = FaceOps.makeFaceWithHoles(outerWire, [innerWire]);
|
|
59
|
+
return FaceOps.fixFaceOrientation(ring).getShape();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Geom_BSplineCurve } from "ocjs-fluidcad";
|
|
2
|
+
import type { BSplineCurveData } from "../../math/bspline-interpolation.js";
|
|
3
|
+
/**
|
|
4
|
+
* Plain-JS mirror of a `Geom_BSplineCurve`. `weights` is null for polynomial
|
|
5
|
+
* curves; when present it holds one weight per pole (rational curve).
|
|
6
|
+
*/
|
|
7
|
+
export interface RationalBSplineData extends BSplineCurveData {
|
|
8
|
+
weights: number[] | null;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Moves B-spline curves between OCC handles and plain data, so the knot/pole
|
|
12
|
+
* bookkeeping of the loft pipeline (concatenation, unification, skinning) can
|
|
13
|
+
* run on ordinary arrays instead of chains of native calls.
|
|
14
|
+
*/
|
|
15
|
+
export declare class CurveData {
|
|
16
|
+
static read(curve: Geom_BSplineCurve): RationalBSplineData;
|
|
17
|
+
static build(data: RationalBSplineData): Geom_BSplineCurve;
|
|
18
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { getOC } from "../init.js";
|
|
2
|
+
import { NCollections } from "../ncollection.js";
|
|
3
|
+
/**
|
|
4
|
+
* Moves B-spline curves between OCC handles and plain data, so the knot/pole
|
|
5
|
+
* bookkeeping of the loft pipeline (concatenation, unification, skinning) can
|
|
6
|
+
* run on ordinary arrays instead of chains of native calls.
|
|
7
|
+
*/
|
|
8
|
+
export class CurveData {
|
|
9
|
+
static read(curve) {
|
|
10
|
+
const poles = [];
|
|
11
|
+
for (let i = 1; i <= curve.NbPoles(); i++) {
|
|
12
|
+
const pole = curve.Pole(i);
|
|
13
|
+
poles.push([pole.X(), pole.Y(), pole.Z()]);
|
|
14
|
+
pole.delete();
|
|
15
|
+
}
|
|
16
|
+
const knots = [];
|
|
17
|
+
const multiplicities = [];
|
|
18
|
+
for (let i = 1; i <= curve.NbKnots(); i++) {
|
|
19
|
+
knots.push(curve.Knot(i));
|
|
20
|
+
multiplicities.push(curve.Multiplicity(i));
|
|
21
|
+
}
|
|
22
|
+
let weights = null;
|
|
23
|
+
if (curve.IsRational()) {
|
|
24
|
+
weights = [];
|
|
25
|
+
for (let i = 1; i <= curve.NbPoles(); i++) {
|
|
26
|
+
weights.push(curve.Weight(i));
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return { poles, weights, knots, multiplicities, degree: curve.Degree() };
|
|
30
|
+
}
|
|
31
|
+
static build(data) {
|
|
32
|
+
const oc = getOC();
|
|
33
|
+
const [poles, disposePoles] = NCollections.toArray1Pnt(data.poles);
|
|
34
|
+
const [knots, disposeKnots] = NCollections.toArray1Double(data.knots);
|
|
35
|
+
const [multiplicities, disposeMultiplicities] = NCollections.toArray1Int(data.multiplicities);
|
|
36
|
+
try {
|
|
37
|
+
if (data.weights) {
|
|
38
|
+
const [weights, disposeWeights] = NCollections.toArray1Double(data.weights);
|
|
39
|
+
try {
|
|
40
|
+
return new oc.Geom_BSplineCurve(poles, weights, knots, multiplicities, data.degree, false, false);
|
|
41
|
+
}
|
|
42
|
+
finally {
|
|
43
|
+
disposeWeights();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return new oc.Geom_BSplineCurve(poles, knots, multiplicities, data.degree, false);
|
|
47
|
+
}
|
|
48
|
+
finally {
|
|
49
|
+
disposePoles();
|
|
50
|
+
disposeKnots();
|
|
51
|
+
disposeMultiplicities();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plain-data B-spline evaluation for the loft pipeline. Sections and guides
|
|
3
|
+
* live as pole/knot arrays between pipeline stages (see `CurveData`), and
|
|
4
|
+
* evaluating them directly in JS avoids rebuilding native curves just to
|
|
5
|
+
* sample a few points.
|
|
6
|
+
*/
|
|
7
|
+
/** Expands distinct knots + multiplicities into the flat knot sequence. */
|
|
8
|
+
export declare function flattenKnots(knots: ReadonlyArray<number>, multiplicities: ReadonlyArray<number>): number[];
|
|
9
|
+
export interface EvaluableBSpline {
|
|
10
|
+
degree: number;
|
|
11
|
+
knots: number[];
|
|
12
|
+
multiplicities: number[];
|
|
13
|
+
poles: number[][];
|
|
14
|
+
weights?: number[] | null;
|
|
15
|
+
}
|
|
16
|
+
/** Point on a (possibly rational) B-spline at parameter t. */
|
|
17
|
+
export declare function evaluateBSplinePoint(curve: EvaluableBSpline, t: number): number[];
|
|
18
|
+
/**
|
|
19
|
+
* Parameter in [low, high] whose evaluated point lies closest to `target`:
|
|
20
|
+
* dense sampling to bracket the minimum, then golden-section refinement.
|
|
21
|
+
* Works for any point-valued curve evaluation (plain-data or native).
|
|
22
|
+
*/
|
|
23
|
+
export declare function closestCurveParameter(evaluate: (t: number) => number[], target: ReadonlyArray<number>, low?: number, high?: number, samples?: number): number;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { findSpan, basisFunctions } from "../../math/bspline-interpolation.js";
|
|
2
|
+
/**
|
|
3
|
+
* Plain-data B-spline evaluation for the loft pipeline. Sections and guides
|
|
4
|
+
* live as pole/knot arrays between pipeline stages (see `CurveData`), and
|
|
5
|
+
* evaluating them directly in JS avoids rebuilding native curves just to
|
|
6
|
+
* sample a few points.
|
|
7
|
+
*/
|
|
8
|
+
/** Expands distinct knots + multiplicities into the flat knot sequence. */
|
|
9
|
+
export function flattenKnots(knots, multiplicities) {
|
|
10
|
+
const flat = [];
|
|
11
|
+
for (let i = 0; i < knots.length; i++) {
|
|
12
|
+
for (let m = 0; m < multiplicities[i]; m++) {
|
|
13
|
+
flat.push(knots[i]);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return flat;
|
|
17
|
+
}
|
|
18
|
+
/** Point on a (possibly rational) B-spline at parameter t. */
|
|
19
|
+
export function evaluateBSplinePoint(curve, t) {
|
|
20
|
+
const flat = flattenKnots(curve.knots, curve.multiplicities);
|
|
21
|
+
const span = findSpan(curve.poles.length, curve.degree, t, flat);
|
|
22
|
+
const values = basisFunctions(span, t, curve.degree, flat);
|
|
23
|
+
const dimension = curve.poles[0].length;
|
|
24
|
+
const numerator = new Array(dimension).fill(0);
|
|
25
|
+
let denominator = 0;
|
|
26
|
+
for (let j = 0; j <= curve.degree; j++) {
|
|
27
|
+
const poleIndex = span - curve.degree + j;
|
|
28
|
+
const weight = curve.weights ? curve.weights[poleIndex] : 1;
|
|
29
|
+
const blend = values[j] * weight;
|
|
30
|
+
denominator += blend;
|
|
31
|
+
for (let d = 0; d < dimension; d++) {
|
|
32
|
+
numerator[d] += blend * curve.poles[poleIndex][d];
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return numerator.map(v => v / denominator);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Parameter in [low, high] whose evaluated point lies closest to `target`:
|
|
39
|
+
* dense sampling to bracket the minimum, then golden-section refinement.
|
|
40
|
+
* Works for any point-valued curve evaluation (plain-data or native).
|
|
41
|
+
*/
|
|
42
|
+
export function closestCurveParameter(evaluate, target, low = 0, high = 1, samples = 512) {
|
|
43
|
+
const distanceAt = (t) => {
|
|
44
|
+
const p = evaluate(t);
|
|
45
|
+
let sum = 0;
|
|
46
|
+
for (let d = 0; d < target.length; d++) {
|
|
47
|
+
sum += (p[d] - target[d]) * (p[d] - target[d]);
|
|
48
|
+
}
|
|
49
|
+
return sum;
|
|
50
|
+
};
|
|
51
|
+
let bestIndex = 0;
|
|
52
|
+
let bestDistance = Infinity;
|
|
53
|
+
for (let i = 0; i <= samples; i++) {
|
|
54
|
+
const d = distanceAt(low + ((high - low) * i) / samples);
|
|
55
|
+
if (d < bestDistance) {
|
|
56
|
+
bestDistance = d;
|
|
57
|
+
bestIndex = i;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
let a = low + ((high - low) * Math.max(0, bestIndex - 1)) / samples;
|
|
61
|
+
let b = low + ((high - low) * Math.min(samples, bestIndex + 1)) / samples;
|
|
62
|
+
const phi = (Math.sqrt(5) - 1) / 2;
|
|
63
|
+
let mid1 = b - phi * (b - a);
|
|
64
|
+
let mid2 = a + phi * (b - a);
|
|
65
|
+
let d1 = distanceAt(mid1);
|
|
66
|
+
let d2 = distanceAt(mid2);
|
|
67
|
+
for (let iteration = 0; iteration < 40; iteration++) {
|
|
68
|
+
if (d1 <= d2) {
|
|
69
|
+
b = mid2;
|
|
70
|
+
mid2 = mid1;
|
|
71
|
+
d2 = d1;
|
|
72
|
+
mid1 = b - phi * (b - a);
|
|
73
|
+
d1 = distanceAt(mid1);
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
a = mid1;
|
|
77
|
+
mid1 = mid2;
|
|
78
|
+
d1 = d2;
|
|
79
|
+
mid2 = a + phi * (b - a);
|
|
80
|
+
d2 = distanceAt(mid2);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return (a + b) / 2;
|
|
84
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { Wire } from "../../common/wire.js";
|
|
2
|
+
import { Solid } from "../../common/solid.js";
|
|
3
|
+
import { LoftEndCondition } from "./skinning.js";
|
|
4
|
+
/**
|
|
5
|
+
* Loft steered by one or two side rails. OCC's pipe-shell machinery distorts
|
|
6
|
+
* pinned sections along curved rails (they tilt with the sweep frame, and
|
|
7
|
+
* the auxiliary-spine contact mode rotates and smears non-circular sections),
|
|
8
|
+
* so this path builds the surface itself:
|
|
9
|
+
*
|
|
10
|
+
* 1. skin the profiles alone (`Skinning`) — the unguided base surface,
|
|
11
|
+
* 2. slice the base into virtual sections between the profiles,
|
|
12
|
+
* 3. move each virtual section rigidly so its rail-contact point rides the
|
|
13
|
+
* guide (one rail), or map its two contact points onto both rails with a
|
|
14
|
+
* rotation + axial stretch (two rails) — profiles themselves stay exact,
|
|
15
|
+
* 4. re-skin through the full stack and cap/sew into a solid.
|
|
16
|
+
*
|
|
17
|
+
* Guides must pass through every profile: sections are pinned where they
|
|
18
|
+
* were sketched, so a rail that misses a profile is unsatisfiable.
|
|
19
|
+
*
|
|
20
|
+
* Start/end conditions compose with guides: the condition's takeoff field
|
|
21
|
+
* fades smoothly to the natural (unconstrained) takeoff around each guide's
|
|
22
|
+
* contact point — the rails own their sides of the surface, the condition
|
|
23
|
+
* shapes the rest. The faded fields drive both the base skin (so the
|
|
24
|
+
* virtual sections carry the conditioned shape) and the final re-skin (so
|
|
25
|
+
* the takeoff is pinned exactly where the condition is in force).
|
|
26
|
+
*/
|
|
27
|
+
export declare class GuidedLoft {
|
|
28
|
+
/** Virtual sections inserted between consecutive profiles. */
|
|
29
|
+
private static readonly SECTIONS_PER_SPAN;
|
|
30
|
+
/** How far (mm) a guide may sit from a profile boundary and still count as touching. */
|
|
31
|
+
private static readonly CONTACT_TOLERANCE;
|
|
32
|
+
/** Fallback condition-falloff radius (in section parameter) for a single guide. */
|
|
33
|
+
private static readonly SINGLE_GUIDE_FALLOFF;
|
|
34
|
+
static build(profileWires: Wire[], guideWires: Wire[], startCondition?: LoftEndCondition, endCondition?: LoftEndCondition): Solid[];
|
|
35
|
+
/**
|
|
36
|
+
* The unguided base skin, with any end conditions already faded around the
|
|
37
|
+
* guide contacts (blended towards the natural chord takeoff, per column).
|
|
38
|
+
*/
|
|
39
|
+
private static skinBase;
|
|
40
|
+
/**
|
|
41
|
+
* The condition's takeoff field for one end of a section stack, faded to
|
|
42
|
+
* the stack's own natural takeoff (the chord to the neighbouring section)
|
|
43
|
+
* around each guide contact. Null when no condition applies to that end.
|
|
44
|
+
*/
|
|
45
|
+
private static stackField;
|
|
46
|
+
/**
|
|
47
|
+
* Per-column condition weight: 0 at each guide contact, smoothstepping to
|
|
48
|
+
* 1 beyond the falloff radius (half the closest contact separation for two
|
|
49
|
+
* guides). Columns are located by their Greville parameters.
|
|
50
|
+
*/
|
|
51
|
+
private static falloffWeights;
|
|
52
|
+
/**
|
|
53
|
+
* Locates the guide's contact with every profile: the guide parameter
|
|
54
|
+
* where it crosses the profile plane, and the profile parameter of the
|
|
55
|
+
* nearest boundary point. Off-boundary rails are reported with their gap —
|
|
56
|
+
* sections stay where they were sketched, so such rails are unsatisfiable.
|
|
57
|
+
*/
|
|
58
|
+
private static anchorGuide;
|
|
59
|
+
/**
|
|
60
|
+
* Guide parameter where the guide crosses the plane (centroid, normal):
|
|
61
|
+
* the sign-change bracket (or on-plane endpoint) whose crossing point lies
|
|
62
|
+
* closest to the centroid, refined by bisection. Null when the guide never
|
|
63
|
+
* reaches the plane.
|
|
64
|
+
*/
|
|
65
|
+
private static planeCrossing;
|
|
66
|
+
/**
|
|
67
|
+
* The full section stack: the exact profiles at their own parameters, and
|
|
68
|
+
* between each pair the base surface's iso-sections carried onto the rails.
|
|
69
|
+
*/
|
|
70
|
+
private static buildSectionStack;
|
|
71
|
+
/** Poles of the base surface's iso-section at loft parameter v (a v-basis row blend). */
|
|
72
|
+
private static isoSectionPoles;
|
|
73
|
+
/**
|
|
74
|
+
* Carries a virtual section onto the rails. One rail translates the
|
|
75
|
+
* section rigidly; two rails additionally rotate and stretch it along the
|
|
76
|
+
* contact axis so both contact points land on their rails. Affine maps
|
|
77
|
+
* leave NURBS weights untouched, so transforming poles transforms the
|
|
78
|
+
* section exactly.
|
|
79
|
+
*/
|
|
80
|
+
private static applyRails;
|
|
81
|
+
/**
|
|
82
|
+
* Affine map taking segment (a1, a2) onto (A1, A2): stretch along the
|
|
83
|
+
* segment axis, minimal rotation aligning the axes, rigid perpendicular
|
|
84
|
+
* components. Degenerate segments fall back to the average translation.
|
|
85
|
+
*/
|
|
86
|
+
private static mapSegment;
|
|
87
|
+
/** Rodrigues rotation taking unit vector `from` to unit vector `to`. */
|
|
88
|
+
private static minimalRotation;
|
|
89
|
+
}
|