@@ -212,12 +212,85 @@ function drawSegment(
212212 return k ;
213213}
214214
215+ /**
216+ * Extend points array for smooth closed curve by prepending last two points
217+ * and appending first two points
218+ */
219+ function extendPointsForClosedCurve (
220+ points : ArrayLike < number > ,
221+ startIndex : number ,
222+ len : number ,
223+ connectNulls : boolean
224+ ) : ArrayLike < number > {
225+ const firstX = points [ startIndex * 2 ] ;
226+ const firstY = points [ startIndex * 2 + 1 ] ;
227+ const lastX = points [ ( len - 1 ) * 2 ] ;
228+ const lastY = points [ ( len - 1 ) * 2 + 1 ] ;
229+
230+ // Find the second-to-last valid point
231+ let prevIdx = len - 2 ;
232+ if ( connectNulls ) {
233+ while ( prevIdx >= startIndex && isPointNull ( points [ prevIdx * 2 ] , points [ prevIdx * 2 + 1 ] ) ) {
234+ prevIdx -- ;
235+ }
236+ }
237+
238+ // Find the second valid point
239+ let nextIdx = startIndex + 1 ;
240+ if ( connectNulls ) {
241+ while ( nextIdx < len && isPointNull ( points [ nextIdx * 2 ] , points [ nextIdx * 2 + 1 ] ) ) {
242+ nextIdx ++ ;
243+ }
244+ }
245+
246+ // Build prepend part (2 points = 4 values)
247+ const prependPart = new ( points . constructor as any ) ( 4 ) ;
248+ if ( prevIdx >= startIndex ) {
249+ prependPart [ 0 ] = points [ prevIdx * 2 ] ;
250+ prependPart [ 1 ] = points [ prevIdx * 2 + 1 ] ;
251+ }
252+ else {
253+ prependPart [ 0 ] = lastX ;
254+ prependPart [ 1 ] = lastY ;
255+ }
256+ prependPart [ 2 ] = lastX ;
257+ prependPart [ 3 ] = lastY ;
258+
259+ // Build append part (2 points = 4 values)
260+ const appendPart = new ( points . constructor as any ) ( 4 ) ;
261+ appendPart [ 0 ] = firstX ;
262+ appendPart [ 1 ] = firstY ;
263+ if ( nextIdx < len ) {
264+ appendPart [ 2 ] = points [ nextIdx * 2 ] ;
265+ appendPart [ 3 ] = points [ nextIdx * 2 + 1 ] ;
266+ }
267+ else {
268+ appendPart [ 2 ] = firstX ;
269+ appendPart [ 3 ] = firstY ;
270+ }
271+
272+ // Create extended points array and merge all parts
273+ const pointsLen = ( len - startIndex ) * 2 ;
274+ const extendedLength = 4 + pointsLen + 4 ;
275+ const extendedPoints = new ( points . constructor as any ) ( extendedLength ) ;
276+ extendedPoints . set ( prependPart , 0 ) ;
277+ // Copy original points slice
278+ const pointsSlice = ( points as any ) . subarray
279+ ? ( points as any ) . subarray ( startIndex * 2 , len * 2 )
280+ : Array . prototype . slice . call ( points , startIndex * 2 , len * 2 ) ;
281+ extendedPoints . set ( pointsSlice , 4 ) ;
282+ extendedPoints . set ( appendPart , 4 + pointsLen ) ;
283+
284+ return extendedPoints ;
285+ }
286+
215287class ECPolylineShape {
216288 points : ArrayLike < number > ;
217289 smooth = 0 ;
218290 smoothConstraint = true ;
219291 smoothMonotone : 'x' | 'y' | 'none' ;
220292 connectNulls : boolean ;
293+ connectEnds : boolean ;
221294}
222295
223296interface ECPolylineProps extends PathProps {
@@ -246,7 +319,7 @@ export class ECPolyline extends Path<ECPolylineProps> {
246319 }
247320
248321 buildPath ( ctx : PathProxy , shape : ECPolylineShape ) {
249- const points = shape . points ;
322+ let points = shape . points ;
250323
251324 let i = 0 ;
252325 let len = points . length / 2 ;
@@ -266,6 +339,24 @@ export class ECPolyline extends Path<ECPolylineProps> {
266339 }
267340 }
268341 }
342+
343+ // If connectEnds is enabled, extend points array for smooth closed curve
344+ if ( shape . connectEnds && len > 2 ) {
345+ const firstX = points [ i * 2 ] ;
346+ const firstY = points [ i * 2 + 1 ] ;
347+ const lastX = points [ ( len - 1 ) * 2 ] ;
348+ const lastY = points [ ( len - 1 ) * 2 + 1 ] ;
349+
350+ // Only connect if first and last points are not null and not the same
351+ if ( ! isPointNull ( firstX , firstY ) && ! isPointNull ( lastX , lastY )
352+ && ( firstX !== lastX || firstY !== lastY ) ) {
353+
354+ points = extendPointsForClosedCurve ( points , i , len , shape . connectNulls ) ;
355+ i = 2 ;
356+ len = points . length / 2 - 1 ;
357+ }
358+ }
359+
269360 while ( i < len ) {
270361 i += drawSegment (
271362 ctx , points , i , len , len ,
@@ -370,8 +461,8 @@ export class ECPolygon extends Path {
370461 }
371462
372463 buildPath ( ctx : PathProxy , shape : ECPolygonShape ) {
373- const points = shape . points ;
374- const stackedOnPoints = shape . stackedOnPoints ;
464+ let points = shape . points ;
465+ let stackedOnPoints = shape . stackedOnPoints ;
375466
376467 let i = 0 ;
377468 let len = points . length / 2 ;
@@ -390,6 +481,27 @@ export class ECPolygon extends Path {
390481 }
391482 }
392483 }
484+
485+ // If connectEnds is enabled, extend both points and stackedOnPoints arrays
486+ if ( shape . connectEnds && len > 2 ) {
487+ const firstX = points [ i * 2 ] ;
488+ const firstY = points [ i * 2 + 1 ] ;
489+ const lastX = points [ ( len - 1 ) * 2 ] ;
490+ const lastY = points [ ( len - 1 ) * 2 + 1 ] ;
491+
492+ // Only connect if first and last points are not null and not the same
493+ if ( ! isPointNull ( firstX , firstY ) && ! isPointNull ( lastX , lastY )
494+ && ( firstX !== lastX || firstY !== lastY ) ) {
495+
496+ points = extendPointsForClosedCurve ( points , i , len , shape . connectNulls ) ;
497+ if ( stackedOnPoints ) {
498+ stackedOnPoints = extendPointsForClosedCurve ( stackedOnPoints , i , len , shape . connectNulls ) ;
499+ }
500+ i = 2 ;
501+ len = points . length / 2 - 1 ;
502+ }
503+ }
504+
393505 while ( i < len ) {
394506 const k = drawSegment (
395507 ctx , points , i , len , len ,
0 commit comments