RoundedPolygon.fromVertices constructor
RoundedPolygon.fromVertices({
- required List<
double> vertices, - CornerRounding rounding = .unrounded,
- List<
CornerRounding> ? perVertexRounding, - double centerX = .minPositive,
- double centerY = .minPositive,
Implementation
factory RoundedPolygon.fromVertices({
required List<double> vertices,
CornerRounding rounding = .unrounded,
List<CornerRounding>? perVertexRounding,
double centerX = .minPositive,
double centerY = .minPositive,
}) {
if (vertices.length < 6) {
throw ArgumentError("Polygons must have at least 3 vertices.");
}
if (!vertices.length.isEven) {
throw ArgumentError("The vertices array should have even size.");
}
if (perVertexRounding != null &&
perVertexRounding.length * 2 != vertices.length) {
throw ArgumentError(
"perVertexRounding list should be either null or "
"the same size as the number of vertices (vertices.size / 2)",
);
}
final corners = <List<Cubic>>[];
final n = vertices.length ~/ 2;
final roundedCorners = <_RoundedCorner>[];
for (var i = 0; i < n; i++) {
final vtxRounding = perVertexRounding?[i] ?? rounding;
final prevIndex = ((i + n - 1) % n) * 2;
final nextIndex = ((i + 1) % n) * 2;
roundedCorners.add(
_RoundedCorner(
Point(vertices[prevIndex], vertices[prevIndex + 1]),
Point(vertices[i * 2], vertices[i * 2 + 1]),
Point(vertices[nextIndex], vertices[nextIndex + 1]),
vtxRounding,
),
);
}
// For each side, check if we have enough space to do the cuts needed, and if not split
// the available space, first for round cuts, then for smoothing if there is space left.
// Each element in this list is a pair, that represent how much we can do of the cut for
// the given side (side i goes from corner i to corner i+1), the elements of the pair are:
// first is how much we can use of expectedRoundCut, second how much of expectedCut
final List<(double, double)> cutAdjusts = List.generate(n, (ix) {
final expectedRoundCut =
roundedCorners[ix].expectedRoundCut +
roundedCorners[(ix + 1) % n].expectedRoundCut;
final expectedCut =
roundedCorners[ix].expectedCut +
roundedCorners[(ix + 1) % n].expectedCut;
final vtxX = vertices[ix * 2];
final vtxY = vertices[ix * 2 + 1];
final nextVtxX = vertices[((ix + 1) % n) * 2];
final nextVtxY = vertices[((ix + 1) % n) * 2 + 1];
final sideSize = distance(vtxX - nextVtxX, vtxY - nextVtxY);
// Check expectedRoundCut first, and ensure we fulfill rounding needs first for
// both corners before using space for smoothing
if (expectedRoundCut > sideSize) {
// Not enough room for fully rounding, see how much we can actually do.
return (sideSize / expectedRoundCut, 0.0);
} else if (expectedCut > sideSize) {
// We can do full rounding, but not full smoothing.
return (
1.0,
(sideSize - expectedRoundCut) / (expectedCut - expectedRoundCut),
);
} else {
// There is enough room for rounding & smoothing.
return (1.0, 1.0);
}
});
// Create and store list of beziers for each [potentially] rounded corner
for (var i = 0; i < n; i++) {
// allowedCuts[0] is for the side from the previous corner to this one,
// allowedCuts[1] is for the side from this corner to the next one.
final allowedCuts = List<double>.filled(2, 0.0);
for (var delta = 0; delta <= 1; delta++) {
final (roundCutRatio, cutRatio) = cutAdjusts[(i + n - 1 + delta) % n];
allowedCuts[delta] =
roundedCorners[i].expectedRoundCut * roundCutRatio +
(roundedCorners[i].expectedCut -
roundedCorners[i].expectedRoundCut) *
cutRatio;
}
corners.add(roundedCorners[i].getCubics(allowedCuts[0], allowedCuts[1]));
}
// Finally, store the calculated cubics. This includes all of the rounded corners
// from above, along with new cubics representing the edges between those corners.
final tempFeatures = <Feature>[];
for (var i = 0; i < n; i++) {
// Note that these indices are for pairs of values (points), they need to be
// doubled to access the xy values in the vertices float array
final prevVtxIndex = (i + n - 1) % n;
final nextVtxIndex = (i + 1) % n;
final currVertex = Point(vertices[i * 2], vertices[i * 2 + 1]);
final prevVertex = Point(
vertices[prevVtxIndex * 2],
vertices[prevVtxIndex * 2 + 1],
);
final nextVertex = Point(
vertices[nextVtxIndex * 2],
vertices[nextVtxIndex * 2 + 1],
);
tempFeatures
..add(Corner(corners[i], convex(prevVertex, currVertex, nextVertex)))
..add(
Edge([
.straightLine(
corners[i].last.anchor1X,
corners[i].last.anchor1Y,
corners[(i + 1) % n].first.anchor0X,
corners[(i + 1) % n].first.anchor0Y,
),
]),
);
}
final c = centerX == .minPositive || centerY == .minPositive
? calculateCenter(vertices)
: Point(centerX, centerY);
return RoundedPolygon(tempFeatures, c);
}