Skip to content

Commit

Permalink
feat: optimization of "solid" polygon/polyline display (#1876)
Browse files Browse the repository at this point in the history
  • Loading branch information
monsieurtanuki authored May 23, 2024
1 parent d959a87 commit 7c99ae3
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 36 deletions.
56 changes: 39 additions & 17 deletions lib/src/layer/misc/line_patterns/pixel_hiker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class DottedPixelHiker extends _PixelHiker {
required super.closePath,
required super.canvasSize,
required super.patternFit,
required super.strokeWidth,
required double stepLength,
}) : super(segmentValues: [stepLength]);

Expand All @@ -25,7 +26,7 @@ class DottedPixelHiker extends _PixelHiker {
}

void addVisibleOffset(final Offset offset) {
if (VisibleSegment.isVisible(offset, canvasSize)) {
if (VisibleSegment.isVisible(offset, canvasSize, strokeWidth)) {
result.add(offset);
}
}
Expand Down Expand Up @@ -69,8 +70,8 @@ class DottedPixelHiker extends _PixelHiker {
///
/// Most important method of the class.
List<Offset>? _getVisibleDotList(Offset offset0, Offset offset1) {
final VisibleSegment? visibleSegment =
VisibleSegment.getVisibleSegment(offset0, offset1, canvasSize);
final VisibleSegment? visibleSegment = VisibleSegment.getVisibleSegment(
offset0, offset1, canvasSize, strokeWidth);
if (visibleSegment == null) {
addDistance(getDistance(offset0, offset1));
return null;
Expand Down Expand Up @@ -131,6 +132,7 @@ class DashedPixelHiker extends _PixelHiker {
required super.canvasSize,
required super.segmentValues,
required super.patternFit,
required super.strokeWidth,
});

/// Returns all visible segments.
Expand All @@ -155,7 +157,7 @@ class DashedPixelHiker extends _PixelHiker {
if (_segmentIndex.isOdd) {
if (patternFit == PatternFit.appendDot) {
if (!closePath) {
if (VisibleSegment.isVisible(offsets.last, canvasSize)) {
if (VisibleSegment.isVisible(offsets.last, canvasSize, strokeWidth)) {
result.add(VisibleSegment(offsets.last, offsets.last));
}
}
Expand Down Expand Up @@ -187,10 +189,7 @@ class DashedPixelHiker extends _PixelHiker {
final Offset offset1,
) {
final VisibleSegment? visibleSegment = VisibleSegment.getVisibleSegment(
offset0,
offset1,
canvasSize,
);
offset0, offset1, canvasSize, strokeWidth);
if (visibleSegment == null) {
addDistance(getDistance(offset0, offset1));
return null;
Expand Down Expand Up @@ -257,31 +256,52 @@ class SolidPixelHiker extends _PixelHiker {
required super.offsets,
required super.closePath,
required super.canvasSize,
required super.strokeWidth,
}) : super(
segmentValues: [],
patternFit: PatternFit.none,
);

/// Returns all visible segments.
List<VisibleSegment> getAllVisibleSegments() {
final List<VisibleSegment> result = [];

/// Adds all visible segments to [paths].
void addAllVisibleSegments(final List<Path> paths) {
if (offsets.length < 2) {
return result;
return;
}

double? latestX;
double? latestY;
List<Offset> polygons = [];

void addPolygons() {
if (polygons.isEmpty) {
return;
}
for (final path in paths) {
path.addPolygon(polygons, false);
}
polygons = [];
}

for (int i = 0; i < offsets.length - 1 + (closePath ? 1 : 0); i++) {
final VisibleSegment? visibleSegment = VisibleSegment.getVisibleSegment(
offsets[i],
offsets[(i + 1) % offsets.length],
canvasSize,
strokeWidth,
);
if (visibleSegment != null) {
result.add(visibleSegment);
if (visibleSegment == null) {
continue;
}
if (latestX != visibleSegment.begin.dx ||
latestY != visibleSegment.begin.dy) {
addPolygons();
polygons.add(visibleSegment.begin);
}
polygons.add(visibleSegment.end);
latestX = visibleSegment.end.dx;
latestY = visibleSegment.end.dy;
}

return result;
addPolygons();
}

@override
Expand All @@ -296,6 +316,7 @@ sealed class _PixelHiker {
required this.closePath,
required this.canvasSize,
required this.patternFit,
required this.strokeWidth,
}) {
_polylinePixelDistance = _getPolylinePixelDistance();
_init();
Expand All @@ -314,6 +335,7 @@ sealed class _PixelHiker {
final List<double> segmentValues;
final Size canvasSize;
final PatternFit patternFit;
final double strokeWidth;

/// Factor to be used on offset distances.
late final double _factor;
Expand Down
21 changes: 13 additions & 8 deletions lib/src/layer/misc/line_patterns/visible_segment.dart
Original file line number Diff line number Diff line change
Expand Up @@ -39,23 +39,28 @@ class VisibleSegment {
return code;
}

/// Returns true if the [offset] is inside the [canvasSize].
static bool isVisible(Offset offset, Size canvasSize) =>
/// Returns true if the [offset] is inside the [canvasSize] + [strokeWidth].
static bool isVisible(Offset offset, Size canvasSize, double strokeWidth) =>
_computeOutCode(
offset.dx, offset.dy, 0, 0, canvasSize.width, canvasSize.height) ==
offset.dx,
offset.dy,
-strokeWidth / 2,
-strokeWidth / 2,
canvasSize.width + strokeWidth / 2,
canvasSize.height + strokeWidth / 2) ==
_inside;

/// Clips a line segment to a rectangular area (canvas).
///
/// Returns null if the segment is invisible.
static VisibleSegment? getVisibleSegment(
Offset p0, Offset p1, Size canvasSize) {
Offset p0, Offset p1, Size canvasSize, double strokeWidth) {
// Function to compute the outCode for a point relative to the canvas

const double xMin = 0;
const double yMin = 0;
final double xMax = canvasSize.width;
final double yMax = canvasSize.height;
final double xMin = -strokeWidth / 2;
final double yMin = -strokeWidth / 2;
final double xMax = canvasSize.width + strokeWidth / 2;
final double yMax = canvasSize.height + strokeWidth / 2;

double x0 = p0.dx;
double y0 = p0.dy;
Expand Down
14 changes: 9 additions & 5 deletions lib/src/layer/polygon_layer/painter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ class _PolygonPainter<R extends Object> extends CustomPainter {
size,
canvas,
_getBorderPaint(polygon),
polygon.borderStrokeWidth,
);
}

Expand All @@ -238,7 +239,7 @@ class _PolygonPainter<R extends Object> extends CustomPainter {

if (!polygon.disableHolesBorder && polygon.borderStrokeWidth > 0.0) {
_addHoleBordersToPath(borderPath, polygon, holeOffsetsList, size,
canvas, _getBorderPaint(polygon));
canvas, _getBorderPaint(polygon), polygon.borderStrokeWidth);
}
}

Expand Down Expand Up @@ -314,6 +315,7 @@ class _PolygonPainter<R extends Object> extends CustomPainter {
Size canvasSize,
Canvas canvas,
Paint paint,
double strokeWidth,
) {
final isSolid = polygon.pattern == const StrokePattern.solid();
final isDashed = polygon.pattern.segments != null;
Expand All @@ -323,18 +325,17 @@ class _PolygonPainter<R extends Object> extends CustomPainter {
offsets: offsets,
closePath: true,
canvasSize: canvasSize,
strokeWidth: strokeWidth,
);
for (final visibleSegment in hiker.getAllVisibleSegments()) {
path.moveTo(visibleSegment.begin.dx, visibleSegment.begin.dy);
path.lineTo(visibleSegment.end.dx, visibleSegment.end.dy);
}
hiker.addAllVisibleSegments([path]);
} else if (isDotted) {
final DottedPixelHiker hiker = DottedPixelHiker(
offsets: offsets,
stepLength: polygon.borderStrokeWidth * polygon.pattern.spacingFactor!,
patternFit: polygon.pattern.patternFit!,
closePath: true,
canvasSize: canvasSize,
strokeWidth: strokeWidth,
);
for (final visibleDot in hiker.getAllVisibleDots()) {
canvas.drawCircle(visibleDot, polygon.borderStrokeWidth / 2, paint);
Expand All @@ -346,6 +347,7 @@ class _PolygonPainter<R extends Object> extends CustomPainter {
patternFit: polygon.pattern.patternFit!,
closePath: true,
canvasSize: canvasSize,
strokeWidth: strokeWidth,
);

for (final visibleSegment in hiker.getAllVisibleSegments()) {
Expand All @@ -362,6 +364,7 @@ class _PolygonPainter<R extends Object> extends CustomPainter {
Size canvasSize,
Canvas canvas,
Paint paint,
double strokeWidth,
) {
for (final offsets in holeOffsetsList) {
_addBorderToPath(
Expand All @@ -371,6 +374,7 @@ class _PolygonPainter<R extends Object> extends CustomPainter {
canvasSize,
canvas,
paint,
strokeWidth,
);
}
}
Expand Down
15 changes: 9 additions & 6 deletions lib/src/layer/polyline_layer/painter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,9 @@ class _PolylinePainter<R extends Object> extends CustomPainter {
needsLayerSaving = polyline.color.opacity < 1.0 ||
(polyline.gradientColors?.any((c) => c.opacity < 1.0) ?? false);

// strokeWidth, or strokeWidth + borderWidth if relevant.
late double largestStrokeWidth;

late final double strokeWidth;
if (polyline.useStrokeWidthInMeter) {
strokeWidth = _metersToStrokeWidth(
Expand All @@ -158,6 +161,7 @@ class _PolylinePainter<R extends Object> extends CustomPainter {
} else {
strokeWidth = polyline.strokeWidth;
}
largestStrokeWidth = strokeWidth;

final isSolid = polyline.pattern == const StrokePattern.solid();
final isDashed = polyline.pattern.segments != null;
Expand All @@ -182,6 +186,7 @@ class _PolylinePainter<R extends Object> extends CustomPainter {
// Outlined lines are drawn by drawing a thicker path underneath, then
// stenciling the middle (in case the line fill is transparent), and
// finally drawing the line fill.
largestStrokeWidth = strokeWidth + polyline.borderStrokeWidth;
borderPaint = Paint()
..color = polyline.borderColor
..strokeWidth = strokeWidth + polyline.borderStrokeWidth
Expand Down Expand Up @@ -213,20 +218,17 @@ class _PolylinePainter<R extends Object> extends CustomPainter {
offsets: offsets,
closePath: false,
canvasSize: size,
strokeWidth: largestStrokeWidth,
);
for (final visibleSegment in hiker.getAllVisibleSegments()) {
for (final path in paths) {
path.moveTo(visibleSegment.begin.dx, visibleSegment.begin.dy);
path.lineTo(visibleSegment.end.dx, visibleSegment.end.dy);
}
}
hiker.addAllVisibleSegments(paths);
} else if (isDotted) {
final DottedPixelHiker hiker = DottedPixelHiker(
offsets: offsets,
stepLength: strokeWidth * polyline.pattern.spacingFactor!,
patternFit: polyline.pattern.patternFit!,
closePath: false,
canvasSize: size,
strokeWidth: largestStrokeWidth,
);

final List<double> radii = [];
Expand All @@ -249,6 +251,7 @@ class _PolylinePainter<R extends Object> extends CustomPainter {
patternFit: polyline.pattern.patternFit!,
closePath: false,
canvasSize: size,
strokeWidth: largestStrokeWidth,
);

for (final visibleSegment in hiker.getAllVisibleSegments()) {
Expand Down

0 comments on commit 7c99ae3

Please sign in to comment.