@@ -10,18 +10,27 @@ import earcut from "earcut";
1010import {
1111 projectPointToSurface ,
1212 computeNormal ,
13- sampleNurbsCurve2D ,
13+ adaptiveSampleNurbsCurve2D ,
14+ projectPointToSurfaceUV ,
1415} from "../utils/nurbs" ;
1516
1617interface 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
2227export 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