set method
Create a convex hull from the given array of points. The length of the
list must be in the range 3, Settings.maxPolygonVertices
.
Warning: the points may be re-ordered, even if they form a convex polygon.
Warning: collinear points are removed.
Implementation
void set(List<Vector2> updatedVertices) {
final updatedCount = updatedVertices.length;
assert(updatedCount >= 3, 'Too few vertices to form polygon');
assert(updatedCount <= settings.maxPolygonVertices, 'Too many vertices');
if (updatedCount < 3) {
setAsBoxXY(1.0, 1.0);
return;
}
// Perform welding and copy vertices into local buffer.
final points = <Vector2>[];
for (final v in updatedVertices) {
var unique = true;
for (var j = 0; j < points.length; ++j) {
if (v.distanceToSquared(points[j]) < 0.5 * settings.linearSlop) {
unique = false;
break;
}
}
if (unique) {
points.add(v.clone());
if (points.length == settings.maxPolygonVertices) {
break;
}
}
}
if (points.length < 3) {
assert(false, 'Too few vertices to be a polygon');
setAsBoxXY(1.0, 1.0);
return;
}
// Create the convex hull using the Gift wrapping algorithm
// http://en.wikipedia.org/wiki/Gift_wrapping_algorithm
// Find the right most point on the hull
var rightMostPoint = points.first;
for (final point in points) {
final x = point.x;
final y = point.y;
final x0 = rightMostPoint.x;
final y0 = rightMostPoint.y;
if (x > x0 || (x == x0 && y < y0)) {
rightMostPoint = point;
}
}
final hull = <Vector2>[rightMostPoint];
var pointOnHull = rightMostPoint;
do {
// Set first point in the set as the initial candidate for the
// next point on the convex hull.
var endPoint = points[0];
// Test the candidate point against all points in the set to find
// the next convex hull point.
for (final point in points) {
// If the candidate point is the current last point on the convex
// hull, update the candidate point to the current point and continue
// checking against the remaining points.
if (endPoint == pointOnHull) {
endPoint = point;
continue;
}
// Use the cross product of the vectors from the current convex hull
// point to the candidate point and the test point to see if the winding
// changes from CCW to CW. This indicates the current point is a better
// candidate for a hull point. Update the candidate point.
final r = endPoint.clone()..sub(pointOnHull);
final v = point.clone()..sub(pointOnHull);
final c = r.cross(v);
if (c < 0.0) {
endPoint = point;
}
// Collinearity check
if (c == 0.0 && v.length2 > r.length2) {
endPoint = point;
}
}
// Set the end point candidate as the new current convex hull point.
pointOnHull = endPoint;
if (!hull.contains(pointOnHull)) {
hull.add(pointOnHull);
}
} while (pointOnHull != hull.first);
// Copy vertices.
vertices.clear();
vertices.addAll(hull);
vertices.forEach((_) => normals.add(Vector2.zero()));
final edge = Vector2.zero();
// Compute normals. Ensure the edges have non-zero length.
for (var i = 0; i < vertices.length; ++i) {
final i1 = i;
final i2 = (i + 1) % vertices.length;
edge
..setFrom(vertices[i2])
..sub(vertices[i1]);
assert(edge.length2 > settings.epsilon * settings.epsilon);
edge.scaleOrthogonalInto(-1.0, normals[i]);
normals[i].normalize();
}
// Compute the polygon centroid.
computeCentroid(vertices, vertices.length);
}