@@ -28,6 +28,13 @@ class _PolygonPainter<R extends Object> extends CustomPainter {
28
28
29
29
final _hits = < R > []; // Avoids repetitive memory reallocation
30
30
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
+
31
38
/// Create a new [_PolygonPainter] instance.
32
39
_PolygonPainter ({
33
40
required this .polygons,
@@ -212,6 +219,9 @@ class _PolygonPainter<R extends Object> extends CustomPainter {
212
219
origin: origin,
213
220
points: projectedPolygon.points,
214
221
),
222
+ size,
223
+ _getBorderPaint (polygon),
224
+ canvas,
215
225
);
216
226
}
217
227
@@ -234,7 +244,8 @@ class _PolygonPainter<R extends Object> extends CustomPainter {
234
244
}
235
245
236
246
if (! polygon.disableHolesBorder && polygon.borderStrokeWidth > 0.0 ) {
237
- _addHoleBordersToPath (borderPath, polygon, holeOffsetsList);
247
+ _addHoleBordersToPath (borderPath, polygon, holeOffsetsList, size,
248
+ canvas, _getBorderPaint (polygon));
238
249
}
239
250
}
240
251
@@ -246,7 +257,7 @@ class _PolygonPainter<R extends Object> extends CustomPainter {
246
257
// ensure polygons and labels are stacked correctly, i.e.:
247
258
// p1, p1_label, p2, p2_label, ... .
248
259
249
- // The painter will be null if the layouting algorithm determined that
260
+ // The painter will be null if the layOuting algorithm determined that
250
261
// there isn't enough space.
251
262
final painter = _buildLabelTextPainter (
252
263
mapSize: camera.size,
@@ -307,11 +318,15 @@ class _PolygonPainter<R extends Object> extends CustomPainter {
307
318
Path path,
308
319
Polygon polygon,
309
320
List <Offset > offsets,
321
+ Size canvasSize,
322
+ Paint paint,
323
+ Canvas canvas,
310
324
) {
311
325
if (polygon.isDotted) {
312
326
final borderRadius = polygon.borderStrokeWidth / 2 ;
313
327
final spacing = polygon.borderStrokeWidth * 1.5 ;
314
- _addDottedLineToPath (path, offsets, borderRadius, spacing);
328
+ _addDottedLineToPath (
329
+ canvas, paint, offsets, borderRadius, spacing, canvasSize);
315
330
} else {
316
331
_addLineToPath (path, offsets);
317
332
}
@@ -321,12 +336,16 @@ class _PolygonPainter<R extends Object> extends CustomPainter {
321
336
Path path,
322
337
Polygon polygon,
323
338
List <List <Offset >> holeOffsetsList,
339
+ Size canvasSize,
340
+ Canvas canvas,
341
+ Paint paint,
324
342
) {
325
343
if (polygon.isDotted) {
326
344
final borderRadius = polygon.borderStrokeWidth / 2 ;
327
345
final spacing = polygon.borderStrokeWidth * 1.5 ;
328
346
for (final offsets in holeOffsetsList) {
329
- _addDottedLineToPath (path, offsets, borderRadius, spacing);
347
+ _addDottedLineToPath (
348
+ canvas, paint, offsets, borderRadius, spacing, canvasSize);
330
349
}
331
350
} else {
332
351
for (final offsets in holeOffsetsList) {
@@ -335,52 +354,152 @@ class _PolygonPainter<R extends Object> extends CustomPainter {
335
354
}
336
355
}
337
356
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
+
338
449
void _addDottedLineToPath (
339
- Path path,
450
+ Canvas canvas,
451
+ Paint paint,
340
452
List <Offset > offsets,
341
453
double radius,
342
454
double stepLength,
455
+ Size canvasSize,
343
456
) {
344
457
if (offsets.isEmpty) {
345
458
return ;
346
459
}
347
460
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
365
473
}
366
474
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
+ }
370
491
}
371
-
372
- path.addOval (Rect .fromCircle (center: offsets.last, radius: radius));
373
492
}
374
493
375
494
void _addLineToPath (Path path, List <Offset > offsets) {
376
495
path.addPolygon (offsets, true );
377
496
}
378
497
379
498
({Offset min, Offset max}) _getBounds (Offset origin, Polygon polygon) {
380
- final bbox = polygon.boundingBox;
499
+ final bBox = polygon.boundingBox;
381
500
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),
384
503
);
385
504
}
386
505
0 commit comments