Implementation
int convert() {
final numPoints = points.length;
if (numPoints < 2) return 0;
final isClosed = points[0].equals(points[numPoints - 1]);
Vector2 previousPoint = points[0];
Vector2? nextPoint;
final strokeWidth2 = style["strokeWidth"] / 2;
final deltaU = 1 / (numPoints - 1);
bool innerSideModified = false;
bool joinIsOnLeftSide = false;
bool isMiter = false;
bool initialJoinIsOnLeftSide = false;
num u1 = 0;
// Get initial left and right stroke points
getNormal(points[0], points[1], tempV2_1).scale(strokeWidth2);
lastPointL.setFrom(points[0]).sub(tempV2_1);
lastPointR.setFrom(points[0]).add(tempV2_1);
point0L.setFrom(lastPointL);
point0R.setFrom(lastPointR);
for (int iPoint = 1; iPoint < numPoints; iPoint++) {
currentPoint = points[iPoint];
// Get next point
if (iPoint == numPoints - 1) {
if (isClosed) {
// Skip duplicated initial point
nextPoint = points[1];
} else {
nextPoint = null;
}
} else {
nextPoint = points[iPoint + 1];
}
// Normal of previous segment in tempV2_1
final normal1 = tempV2_1;
getNormal(previousPoint, currentPoint, normal1);
tempV2_3.setFrom(normal1).scale(strokeWidth2);
currentPointL.setFrom(currentPoint).sub(tempV2_3);
currentPointR.setFrom(currentPoint).add(tempV2_3);
u1 = u0 + deltaU;
innerSideModified = false;
if (nextPoint != null) {
// Normal of next segment in tempV2_2
getNormal(currentPoint, nextPoint, tempV2_2);
tempV2_3.setFrom(tempV2_2).scale(strokeWidth2);
nextPointL.setFrom(currentPoint).sub(tempV2_3);
nextPointR.setFrom(currentPoint).add(tempV2_3);
joinIsOnLeftSide = true;
tempV2_3.sub2(nextPoint, previousPoint);
if (normal1.dot(tempV2_3) < 0) {
joinIsOnLeftSide = false;
}
if (iPoint == 1) initialJoinIsOnLeftSide = joinIsOnLeftSide;
tempV2_3.sub2(nextPoint, currentPoint);
tempV2_3.normalize();
final dot = normal1.dot(tempV2_3).abs();
// If path is straight, don't create join
if (dot != 0) {
// Compute inner and outer segment intersections
final miterSide = strokeWidth2 / dot;
tempV2_3.scale(-miterSide);
tempV2_4.sub2(currentPoint, previousPoint);
tempV2_5.setFrom(tempV2_4).setLength(miterSide).add(tempV2_3);
innerPoint.setFrom(tempV2_5).negate();
final miterLength2 = tempV2_5.length;
final segmentLengthPrev = tempV2_4.length;
tempV2_4.divideScalar(segmentLengthPrev);
tempV2_6.sub2(nextPoint, currentPoint);
final segmentLengthNext = tempV2_6.length;
tempV2_6.divideScalar(segmentLengthNext);
// Check that previous and next segments doesn't overlap with the innerPoint of intersection
if (tempV2_4.dot(innerPoint) < segmentLengthPrev &&
tempV2_6.dot(innerPoint) < segmentLengthNext) {
innerSideModified = true;
}
outerPoint.setFrom(tempV2_5).add(currentPoint);
innerPoint.add(currentPoint);
isMiter = false;
if (innerSideModified) {
if (joinIsOnLeftSide) {
nextPointR.setFrom(innerPoint);
currentPointR.setFrom(innerPoint);
} else {
nextPointL.setFrom(innerPoint);
currentPointL.setFrom(innerPoint);
}
} else {
// The segment triangles are generated here if there was overlapping
makeSegmentTriangles(u1);
}
switch (style["strokeLineJoin"]) {
case 'bevel':
makeSegmentWithBevelJoin(
joinIsOnLeftSide, innerSideModified, u1, u1);
break;
case 'round':
// Segment triangles
createSegmentTrianglesWithMiddleSection(
joinIsOnLeftSide, innerSideModified, u1);
// Join triangles
if (joinIsOnLeftSide) {
makeCircularSector(
currentPoint, currentPointL, nextPointL, u1, 0);
} else {
makeCircularSector(
currentPoint, nextPointR, currentPointR, u1, 1);
}
break;
case 'miter':
case 'miter-clip':
default:
final miterFraction =
(strokeWidth2 * style["strokeMiterLimit"]) / miterLength2;
if (miterFraction < 1) {
// The join miter length exceeds the miter limit
if (style["strokeLineJoin"] != 'miter-clip') {
makeSegmentWithBevelJoin(
joinIsOnLeftSide, innerSideModified, u1, u1);
break;
} else {
// Segment triangles
createSegmentTrianglesWithMiddleSection(
joinIsOnLeftSide, innerSideModified, u1);
// Miter-clip join triangles
if (joinIsOnLeftSide) {
tempV2_6.sub2(outerPoint, currentPointL)
.scale(miterFraction)
.add(currentPointL);
tempV2_7.sub2(outerPoint, nextPointL)
.scale(miterFraction)
.add(nextPointL);
addVertex(currentPointL, u1, 0);
addVertex(tempV2_6, u1, 0);
addVertex(currentPoint, u1, 0.5);
addVertex(currentPoint, u1, 0.5);
addVertex(tempV2_6, u1, 0);
addVertex(tempV2_7, u1, 0);
addVertex(currentPoint, u1, 0.5);
addVertex(tempV2_7, u1, 0);
addVertex(nextPointL, u1, 0);
} else {
tempV2_6.sub2(outerPoint, currentPointR)
.scale(miterFraction)
.add(currentPointR);
tempV2_7.sub2(outerPoint, nextPointR)
.scale(miterFraction)
.add(nextPointR);
addVertex(currentPointR, u1, 1);
addVertex(tempV2_6, u1, 1);
addVertex(currentPoint, u1, 0.5);
addVertex(currentPoint, u1, 0.5);
addVertex(tempV2_6, u1, 1);
addVertex(tempV2_7, u1, 1);
addVertex(currentPoint, u1, 0.5);
addVertex(tempV2_7, u1, 1);
addVertex(nextPointR, u1, 1);
}
}
} else {
// Miter join segment triangles
if (innerSideModified) {
// Optimized segment + join triangles
if (joinIsOnLeftSide) {
addVertex(lastPointR, u0, 1);
addVertex(lastPointL, u0, 0);
addVertex(outerPoint, u1, 0);
addVertex(lastPointR, u0, 1);
addVertex(outerPoint, u1, 0);
addVertex(innerPoint, u1, 1);
} else {
addVertex(lastPointR, u0, 1);
addVertex(lastPointL, u0, 0);
addVertex(outerPoint, u1, 1);
addVertex(lastPointL, u0, 0);
addVertex(innerPoint, u1, 0);
addVertex(outerPoint, u1, 1);
}
if (joinIsOnLeftSide) {
nextPointL.setFrom(outerPoint);
} else {
nextPointR.setFrom(outerPoint);
}
} else {
// Add extra miter join triangles
if (joinIsOnLeftSide) {
addVertex(currentPointL, u1, 0);
addVertex(outerPoint, u1, 0);
addVertex(currentPoint, u1, 0.5);
addVertex(currentPoint, u1, 0.5);
addVertex(outerPoint, u1, 0);
addVertex(nextPointL, u1, 0);
} else {
addVertex(currentPointR, u1, 1);
addVertex(outerPoint, u1, 1);
addVertex(currentPoint, u1, 0.5);
addVertex(currentPoint, u1, 0.5);
addVertex(outerPoint, u1, 1);
addVertex(nextPointR, u1, 1);
}
}
isMiter = true;
}
break;
}
} else {
// The segment triangles are generated here when two consecutive points are collinear
makeSegmentTriangles(u1);
}
} else {
// The segment triangles are generated here if it is the ending segment
makeSegmentTriangles(u1);
}
if (!isClosed && iPoint == numPoints - 1) {
// Start line endcap
addCapGeometry(points[0], point0L, point0R, joinIsOnLeftSide, true, u0);
}
// Increment loop variables
u0 = u1;
previousPoint = currentPoint;
lastPointL.setFrom(nextPointL);
lastPointR.setFrom(nextPointR);
}
if (!isClosed) {
// Ending line endcap
addCapGeometry(currentPoint, currentPointL, currentPointR,
joinIsOnLeftSide, false, u1);
} else if (innerSideModified) {
// Modify path first segment vertices to adjust to the segments inner and outer intersections
Vector2 lastOuter = outerPoint;
Vector2 lastInner = innerPoint;
if (initialJoinIsOnLeftSide != joinIsOnLeftSide) {
lastOuter = innerPoint;
lastInner = outerPoint;
}
if (joinIsOnLeftSide) {
if (isMiter || initialJoinIsOnLeftSide) {
lastInner.copyIntoArray(vertices, 0 * 3);
lastInner.copyIntoArray(vertices, 3 * 3);
if (isMiter) {
lastOuter.copyIntoArray(vertices, 1 * 3);
}
}
} else {
if (isMiter || !initialJoinIsOnLeftSide) {
lastInner.copyIntoArray(vertices, 1 * 3);
lastInner.copyIntoArray(vertices, 3 * 3);
if (isMiter) {
lastOuter.copyIntoArray(vertices, 0 * 3);
}
}
}
}
return numVertices;
// -- End of algorithm
}