@@ -28,6 +28,13 @@ class _PolygonPainter<R extends Object> extends CustomPainter {
2828
2929 final _hits = < R > []; // Avoids repetitive memory reallocation
3030
31+ // OutCodes for the Cohen-Sutherland algorithm
32+ static const _csInside = 0 ; // 0000
33+ static const _csLeft = 1 ; // 0001
34+ static const _csRight = 2 ; // 0010
35+ static const _csBottom = 4 ; // 0100
36+ static const _csTop = 8 ; // 1000
37+
3138 /// Create a new [_PolygonPainter] instance.
3239 _PolygonPainter ({
3340 required this .polygons,
@@ -212,6 +219,9 @@ class _PolygonPainter<R extends Object> extends CustomPainter {
212219 origin: origin,
213220 points: projectedPolygon.points,
214221 ),
222+ size,
223+ _getBorderPaint (polygon),
224+ canvas,
215225 );
216226 }
217227
@@ -234,7 +244,8 @@ class _PolygonPainter<R extends Object> extends CustomPainter {
234244 }
235245
236246 if (! polygon.disableHolesBorder && polygon.borderStrokeWidth > 0.0 ) {
237- _addHoleBordersToPath (borderPath, polygon, holeOffsetsList);
247+ _addHoleBordersToPath (borderPath, polygon, holeOffsetsList, size,
248+ canvas, _getBorderPaint (polygon));
238249 }
239250 }
240251
@@ -246,7 +257,7 @@ class _PolygonPainter<R extends Object> extends CustomPainter {
246257 // ensure polygons and labels are stacked correctly, i.e.:
247258 // p1, p1_label, p2, p2_label, ... .
248259
249- // The painter will be null if the layouting algorithm determined that
260+ // The painter will be null if the layOuting algorithm determined that
250261 // there isn't enough space.
251262 final painter = _buildLabelTextPainter (
252263 mapSize: camera.size,
@@ -307,11 +318,15 @@ class _PolygonPainter<R extends Object> extends CustomPainter {
307318 Path path,
308319 Polygon polygon,
309320 List <Offset > offsets,
321+ Size canvasSize,
322+ Paint paint,
323+ Canvas canvas,
310324 ) {
311325 if (polygon.isDotted) {
312326 final borderRadius = polygon.borderStrokeWidth / 2 ;
313327 final spacing = polygon.borderStrokeWidth * 1.5 ;
314- _addDottedLineToPath (path, offsets, borderRadius, spacing);
328+ _addDottedLineToPath (
329+ canvas, paint, offsets, borderRadius, spacing, canvasSize);
315330 } else {
316331 _addLineToPath (path, offsets);
317332 }
@@ -321,12 +336,16 @@ class _PolygonPainter<R extends Object> extends CustomPainter {
321336 Path path,
322337 Polygon polygon,
323338 List <List <Offset >> holeOffsetsList,
339+ Size canvasSize,
340+ Canvas canvas,
341+ Paint paint,
324342 ) {
325343 if (polygon.isDotted) {
326344 final borderRadius = polygon.borderStrokeWidth / 2 ;
327345 final spacing = polygon.borderStrokeWidth * 1.5 ;
328346 for (final offsets in holeOffsetsList) {
329- _addDottedLineToPath (path, offsets, borderRadius, spacing);
347+ _addDottedLineToPath (
348+ canvas, paint, offsets, borderRadius, spacing, canvasSize);
330349 }
331350 } else {
332351 for (final offsets in holeOffsetsList) {
@@ -335,52 +354,152 @@ class _PolygonPainter<R extends Object> extends CustomPainter {
335354 }
336355 }
337356
357+ // Function to clip a line segment to a rectangular area (canvas)
358+ List <Offset >? _getVisibleSegment (Offset p0, Offset p1, Size canvasSize) {
359+ // Function to compute the outCode for a point relative to the canvas
360+ int computeOutCode (
361+ double x,
362+ double y,
363+ double xMin,
364+ double yMin,
365+ double xMax,
366+ double yMax,
367+ ) {
368+ int code = _csInside;
369+
370+ if (x < xMin) {
371+ code | = _csLeft;
372+ } else if (x > xMax) {
373+ code | = _csRight;
374+ }
375+ if (y < yMin) {
376+ code | = _csBottom;
377+ } else if (y > yMax) {
378+ code | = _csTop;
379+ }
380+
381+ return code;
382+ }
383+
384+ const double xMin = 0 ;
385+ const double yMin = 0 ;
386+ final double xMax = canvasSize.width;
387+ final double yMax = canvasSize.height;
388+
389+ double x0 = p0.dx;
390+ double y0 = p0.dy;
391+ double x1 = p1.dx;
392+ double y1 = p1.dy;
393+
394+ int outCode0 = computeOutCode (x0, y0, xMin, yMin, xMax, yMax);
395+ int outCode1 = computeOutCode (x1, y1, xMin, yMin, xMax, yMax);
396+ bool accept = false ;
397+
398+ while (true ) {
399+ if ((outCode0 | outCode1) == 0 ) {
400+ // Both points inside; trivially accept
401+ accept = true ;
402+ break ;
403+ } else if ((outCode0 & outCode1) != 0 ) {
404+ // Both points share an outside zone; trivially reject
405+ break ;
406+ } else {
407+ // Could be partially inside; calculate intersection
408+ double x;
409+ double y;
410+ final int outCodeOut = outCode0 != 0 ? outCode0 : outCode1;
411+
412+ if ((outCodeOut & _csTop) != 0 ) {
413+ x = x0 + (x1 - x0) * (yMax - y0) / (y1 - y0);
414+ y = yMax;
415+ } else if ((outCodeOut & _csBottom) != 0 ) {
416+ x = x0 + (x1 - x0) * (yMin - y0) / (y1 - y0);
417+ y = yMin;
418+ } else if ((outCodeOut & _csRight) != 0 ) {
419+ y = y0 + (y1 - y0) * (xMax - x0) / (x1 - x0);
420+ x = xMax;
421+ } else if ((outCodeOut & _csLeft) != 0 ) {
422+ y = y0 + (y1 - y0) * (xMin - x0) / (x1 - x0);
423+ x = xMin;
424+ } else {
425+ // This else block should never be reached.
426+ break ;
427+ }
428+
429+ // Update the point and outCode
430+ if (outCodeOut == outCode0) {
431+ x0 = x;
432+ y0 = y;
433+ outCode0 = computeOutCode (x0, y0, xMin, yMin, xMax, yMax);
434+ } else {
435+ x1 = x;
436+ y1 = y;
437+ outCode1 = computeOutCode (x1, y1, xMin, yMin, xMax, yMax);
438+ }
439+ }
440+ }
441+
442+ if (accept) {
443+ // Make sure we return the points within the canvas
444+ return [Offset (x0, y0), Offset (x1, y1)];
445+ }
446+ return null ;
447+ }
448+
338449 void _addDottedLineToPath (
339- Path path,
450+ Canvas canvas,
451+ Paint paint,
340452 List <Offset > offsets,
341453 double radius,
342454 double stepLength,
455+ Size canvasSize,
343456 ) {
344457 if (offsets.isEmpty) {
345458 return ;
346459 }
347460
348- double startDistance = 0 ;
349- for (int i = 0 ; i < offsets.length; i++ ) {
350- final o0 = offsets[i % offsets.length];
351- final o1 = offsets[(i + 1 ) % offsets.length];
352- final totalDistance = (o0 - o1).distance;
353-
354- double distance = startDistance;
355- while (distance < totalDistance) {
356- final done = distance / totalDistance;
357- final remain = 1.0 - done;
358- final offset = Offset (
359- o0.dx * remain + o1.dx * done,
360- o0.dy * remain + o1.dy * done,
361- );
362- path.addOval (Rect .fromCircle (center: offset, radius: radius));
363-
364- distance += stepLength;
461+ // Calculate for all segments, including closing the loop from the last to the first point
462+ final int totalOffsets = offsets.length;
463+ for (int i = 0 ; i < totalOffsets; i++ ) {
464+ final Offset start = offsets[i % totalOffsets];
465+ final Offset end =
466+ offsets[(i + 1 ) % totalOffsets]; // Wrap around to the first point
467+
468+ // Attempt to adjust the segment to the visible part of the canvas
469+ final List <Offset >? visibleSegment =
470+ _getVisibleSegment (start, end, canvasSize);
471+ if (visibleSegment == null ) {
472+ continue ; // Skip if the segment is completely outside
365473 }
366474
367- startDistance = distance < totalDistance
368- ? stepLength - (totalDistance - distance)
369- : distance - totalDistance;
475+ final Offset adjustedStart = visibleSegment[0 ];
476+ final Offset adjustedEnd = visibleSegment[1 ];
477+ final double lineLength = (adjustedStart - adjustedEnd).distance;
478+ final Offset stepVector =
479+ (adjustedEnd - adjustedStart) / lineLength * stepLength;
480+ double traveledDistance = 0 ;
481+
482+ Offset currentPoint = adjustedStart;
483+ while (traveledDistance < lineLength) {
484+ // Draw the circle if within the canvas bounds (additional check now redundant)
485+ canvas.drawCircle (currentPoint, radius, paint);
486+
487+ // Move to the next point
488+ currentPoint = currentPoint + stepVector;
489+ traveledDistance += stepLength;
490+ }
370491 }
371-
372- path.addOval (Rect .fromCircle (center: offsets.last, radius: radius));
373492 }
374493
375494 void _addLineToPath (Path path, List <Offset > offsets) {
376495 path.addPolygon (offsets, true );
377496 }
378497
379498 ({Offset min, Offset max}) _getBounds (Offset origin, Polygon polygon) {
380- final bbox = polygon.boundingBox;
499+ final bBox = polygon.boundingBox;
381500 return (
382- min: getOffset (camera, origin, bbox .southWest),
383- max: getOffset (camera, origin, bbox .northEast),
501+ min: getOffset (camera, origin, bBox .southWest),
502+ max: getOffset (camera, origin, bBox .northEast),
384503 );
385504 }
386505
0 commit comments