Skip to content

Commit 3c3793f

Browse files
committed
Enhance TrimmedSurface component with adaptive sampling and world space projection features. Introduce new props for trim curve resolution, adaptive angle, and depth. Update Storybook examples to demonstrate new functionality and improve documentation for props.
1 parent e30def5 commit 3c3793f

File tree

3 files changed

+448
-34
lines changed

3 files changed

+448
-34
lines changed

src/components/TrimmedSurface.tsx

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,27 @@ import earcut from "earcut";
1010
import {
1111
projectPointToSurface,
1212
computeNormal,
13-
sampleNurbsCurve2D,
13+
adaptiveSampleNurbsCurve2D,
14+
projectPointToSurfaceUV,
1415
} from "../utils/nurbs";
1516

1617
interface Props extends Omit<MeshProps, "geometry"> {
1718
color?: string;
19+
trimCurveResolution?: number;
20+
adaptiveMaxAngleDeg?: number;
21+
adaptiveMaxDepth?: number;
1822
wireframe?: boolean;
23+
world?: boolean;
1924
children?: ReactElement | ReactElement[];
2025
}
2126

2227
export function TrimmedSurface({
2328
color = "#ffffff",
29+
trimCurveResolution = 200,
30+
adaptiveMaxAngleDeg = 5,
31+
adaptiveMaxDepth = 10,
2432
wireframe = false,
33+
world = false,
2534
children,
2635
...meshProps
2736
}: Props) {
@@ -93,25 +102,62 @@ export function TrimmedSurface({
93102
.map((child) => {
94103
if (!isValidElement(child)) return null;
95104
const curveProps = child.props;
96-
return verb.geom.NurbsCurve.byKnotsControlPointsWeights(
105+
const curve = verb.geom.NurbsCurve.byKnotsControlPointsWeights(
97106
curveProps.degree,
98107
curveProps.knots,
99108
curveProps.points,
100109
curveProps.weights
101110
);
111+
112+
if (world) {
113+
// If in world space, project curve points onto surface to get UV coordinates
114+
const numPoints = trimCurveResolution;
115+
const projectedPoints: [number, number][] = [];
116+
for (let i = 0; i <= numPoints; i++) {
117+
const t = i / numPoints;
118+
const point = curve.point(t);
119+
// Project 3D point onto surface to get UV coordinates
120+
const uv = projectPointToSurfaceUV(verbSurface, point);
121+
if (uv) {
122+
projectedPoints.push(uv);
123+
}
124+
}
125+
// Create new curve in UV space using projected points
126+
if (projectedPoints.length > 0) {
127+
return verb.geom.NurbsCurve.byKnotsControlPointsWeights(
128+
curveProps.degree,
129+
Array(projectedPoints.length + curveProps.degree + 1)
130+
.fill(0)
131+
.map((_, i) => {
132+
if (i < curveProps.degree + 1) return 0;
133+
if (i >= projectedPoints.length) return 1;
134+
return (
135+
(i - curveProps.degree) /
136+
(projectedPoints.length - curveProps.degree)
137+
);
138+
}),
139+
projectedPoints.map(([u, v]) => [u, v, 0]),
140+
Array(projectedPoints.length).fill(1)
141+
);
142+
}
143+
return null;
144+
}
145+
146+
return curve;
102147
})
103148
.filter((curve): curve is verb.geom.NurbsCurve => curve !== null);
104149

105150
// Sample all curves
106151
const uvLoops: [number, number][][] = trimmingCurves.map((curve) =>
107-
sampleNurbsCurve2D(curve, 200)
152+
adaptiveSampleNurbsCurve2D(curve, adaptiveMaxAngleDeg, adaptiveMaxDepth)
108153
);
109154

110155
// Flatten UVs for earcut
111156
const earcutVertices: number[] = [];
112157
const holeIndices: number[] = [];
113158
let vertexCount = 0;
114159

160+
// Add curve points first to maintain boundary
115161
uvLoops.forEach((loop, i) => {
116162
if (i > 0) holeIndices.push(vertexCount);
117163
loop.forEach(([u, v]) => {
@@ -149,7 +195,16 @@ export function TrimmedSurface({
149195
setGeometry(geometry);
150196

151197
return () => geometry.dispose();
152-
}, [children, color, wireframe, scene]);
198+
}, [
199+
children,
200+
color,
201+
wireframe,
202+
scene,
203+
trimCurveResolution,
204+
adaptiveMaxAngleDeg,
205+
adaptiveMaxDepth,
206+
world,
207+
]);
153208

154209
if (!geometry) return null;
155210

0 commit comments

Comments
 (0)