set method

void set(
  1. List<Vector2> updatedVertices
)

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);
}