collide method

void collide(
  1. Manifold manifold,
  2. EdgeShape edgeA,
  3. Transform xfA,
  4. PolygonShape polygonB2,
  5. Transform xfB,
)

Implementation

void collide(
  Manifold manifold,
  EdgeShape edgeA,
  Transform xfA,
  PolygonShape polygonB2,
  Transform xfB,
) {
  xf.setFrom(Transform.mulTrans(xfA, xfB));
  centroidB.setFrom(Transform.mulVec2(xf, polygonB2.centroid));

  v0 = edgeA.vertex0;
  v1 = edgeA.vertex1;
  v2 = edgeA.vertex2;
  v3 = edgeA.vertex3;

  final hasVertex0 = edgeA.hasVertex0;
  final hasVertex3 = edgeA.hasVertex3;

  _edge1
    ..setFrom(v2)
    ..sub(v1);
  _edge1.normalize();
  normal1.setValues(_edge1.y, -_edge1.x);
  final offset1 = normal1.dot(
    _temp
      ..setFrom(centroidB)
      ..sub(v1),
  );
  var offset0 = 0.0;
  var offset2 = 0.0;
  var convex1 = false;
  var convex2 = false;

  // Is there a preceding edge?
  if (hasVertex0) {
    _edge0
      ..setFrom(v1)
      ..sub(v0);
    _edge0.normalize();
    normal0.setValues(_edge0.y, -_edge0.x);
    convex1 = _edge0.cross(_edge1) >= 0.0;
    offset0 = normal0.dot(
      _temp
        ..setFrom(centroidB)
        ..sub(v0),
    );
  }

  // Is there a following edge?
  if (hasVertex3) {
    _edge2
      ..setFrom(v3)
      ..sub(v2);
    _edge2.normalize();
    normal2.setValues(_edge2.y, -_edge2.x);
    convex2 = _edge1.cross(_edge2) > 0.0;
    offset2 = normal2.dot(
      _temp
        ..setFrom(centroidB)
        ..sub(v2),
    );
  }

  // Determine front or back collision. Determine collision normal limits.
  if (hasVertex0 && hasVertex3) {
    if (convex1 && convex2) {
      front = offset0 >= 0.0 || offset1 >= 0.0 || offset2 >= 0.0;
      if (front) {
        normal.x = normal1.x;
        normal.y = normal1.y;
        lowerLimit.x = normal0.x;
        lowerLimit.y = normal0.y;
        upperLimit.x = normal2.x;
        upperLimit.y = normal2.y;
      } else {
        normal.x = -normal1.x;
        normal.y = -normal1.y;
        lowerLimit.x = -normal1.x;
        lowerLimit.y = -normal1.y;
        upperLimit.x = -normal1.x;
        upperLimit.y = -normal1.y;
      }
    } else if (convex1) {
      front = offset0 >= 0.0 || (offset1 >= 0.0 && offset2 >= 0.0);
      if (front) {
        normal.x = normal1.x;
        normal.y = normal1.y;
        lowerLimit.x = normal0.x;
        lowerLimit.y = normal0.y;
        upperLimit.x = normal1.x;
        upperLimit.y = normal1.y;
      } else {
        normal.x = -normal1.x;
        normal.y = -normal1.y;
        lowerLimit.x = -normal2.x;
        lowerLimit.y = -normal2.y;
        upperLimit.x = -normal1.x;
        upperLimit.y = -normal1.y;
      }
    } else if (convex2) {
      front = offset2 >= 0.0 || (offset0 >= 0.0 && offset1 >= 0.0);
      if (front) {
        normal.x = normal1.x;
        normal.y = normal1.y;
        lowerLimit.x = normal1.x;
        lowerLimit.y = normal1.y;
        upperLimit.x = normal2.x;
        upperLimit.y = normal2.y;
      } else {
        normal.x = -normal1.x;
        normal.y = -normal1.y;
        lowerLimit.x = -normal1.x;
        lowerLimit.y = -normal1.y;
        upperLimit.x = -normal0.x;
        upperLimit.y = -normal0.y;
      }
    } else {
      front = offset0 >= 0.0 && offset1 >= 0.0 && offset2 >= 0.0;
      if (front) {
        normal.x = normal1.x;
        normal.y = normal1.y;
        lowerLimit.x = normal1.x;
        lowerLimit.y = normal1.y;
        upperLimit.x = normal1.x;
        upperLimit.y = normal1.y;
      } else {
        normal.x = -normal1.x;
        normal.y = -normal1.y;
        lowerLimit.x = -normal2.x;
        lowerLimit.y = -normal2.y;
        upperLimit.x = -normal0.x;
        upperLimit.y = -normal0.y;
      }
    }
  } else if (hasVertex0) {
    if (convex1) {
      front = offset0 >= 0.0 || offset1 >= 0.0;
      if (front) {
        normal.x = normal1.x;
        normal.y = normal1.y;
        lowerLimit.x = normal0.x;
        lowerLimit.y = normal0.y;
        upperLimit.x = -normal1.x;
        upperLimit.y = -normal1.y;
      } else {
        normal.x = -normal1.x;
        normal.y = -normal1.y;
        lowerLimit.x = normal1.x;
        lowerLimit.y = normal1.y;
        upperLimit.x = -normal1.x;
        upperLimit.y = -normal1.y;
      }
    } else {
      front = offset0 >= 0.0 && offset1 >= 0.0;
      if (front) {
        normal.x = normal1.x;
        normal.y = normal1.y;
        lowerLimit.x = normal1.x;
        lowerLimit.y = normal1.y;
        upperLimit.x = -normal1.x;
        upperLimit.y = -normal1.y;
      } else {
        normal.x = -normal1.x;
        normal.y = -normal1.y;
        lowerLimit.x = normal1.x;
        lowerLimit.y = normal1.y;
        upperLimit.x = -normal0.x;
        upperLimit.y = -normal0.y;
      }
    }
  } else if (hasVertex3) {
    if (convex2) {
      front = offset1 >= 0.0 || offset2 >= 0.0;
      if (front) {
        normal.x = normal1.x;
        normal.y = normal1.y;
        lowerLimit.x = -normal1.x;
        lowerLimit.y = -normal1.y;
        upperLimit.x = normal2.x;
        upperLimit.y = normal2.y;
      } else {
        normal.x = -normal1.x;
        normal.y = -normal1.y;
        lowerLimit.x = -normal1.x;
        lowerLimit.y = -normal1.y;
        upperLimit.x = normal1.x;
        upperLimit.y = normal1.y;
      }
    } else {
      front = offset1 >= 0.0 && offset2 >= 0.0;
      if (front) {
        normal.x = normal1.x;
        normal.y = normal1.y;
        lowerLimit.x = -normal1.x;
        lowerLimit.y = -normal1.y;
        upperLimit.x = normal1.x;
        upperLimit.y = normal1.y;
      } else {
        normal.x = -normal1.x;
        normal.y = -normal1.y;
        lowerLimit.x = -normal2.x;
        lowerLimit.y = -normal2.y;
        upperLimit.x = normal1.x;
        upperLimit.y = normal1.y;
      }
    }
  } else {
    front = offset1 >= 0.0;
    if (front) {
      normal.x = normal1.x;
      normal.y = normal1.y;
      lowerLimit.x = -normal1.x;
      lowerLimit.y = -normal1.y;
      upperLimit.x = -normal1.x;
      upperLimit.y = -normal1.y;
    } else {
      normal.x = -normal1.x;
      normal.y = -normal1.y;
      lowerLimit.x = normal1.x;
      lowerLimit.y = normal1.y;
      upperLimit.x = normal1.x;
      upperLimit.y = normal1.y;
    }
  }

  // Get polygonB in frameA
  polygonB.count = polygonB2.vertices.length;
  for (var i = 0; i < polygonB2.vertices.length; ++i) {
    polygonB.vertices[i]
        .setFrom(Transform.mulVec2(xf, polygonB2.vertices[i]));
    polygonB.normals[i].setFrom(Rot.mulVec2(xf.q, polygonB2.normals[i]));
  }

  radius = 2.0 * settings.polygonRadius;

  manifold.pointCount = 0;

  computeEdgeSeparation(_edgeAxis);

  // If no valid normal can be found than this edge should not collide.
  if (_edgeAxis.type == EPAxisType.unknown) {
    return;
  }

  if (_edgeAxis.separation > radius) {
    return;
  }

  computePolygonSeparation(_polygonAxis);
  if (_polygonAxis.type != EPAxisType.unknown &&
      _polygonAxis.separation > radius) {
    return;
  }

  // Use hysteresis for jitter reduction.
  const relativeTol = 0.98;
  const kAbsoluteTol = 0.001;

  EPAxis primaryAxis;
  if (_polygonAxis.type == EPAxisType.unknown) {
    primaryAxis = _edgeAxis;
  } else if (_polygonAxis.separation >
      relativeTol * _edgeAxis.separation + kAbsoluteTol) {
    primaryAxis = _polygonAxis;
  } else {
    primaryAxis = _edgeAxis;
  }

  final ie0 = _incidentEdge[0];
  final ie1 = _incidentEdge[1];

  if (primaryAxis.type == EPAxisType.edgeA) {
    manifold.type = ManifoldType.faceA;

    // Search for the polygon normal that is most anti-parallel to the edge
    // normal.
    var bestIndex = 0;
    var bestValue = normal.dot(polygonB.normals[0]);
    for (var i = 1; i < polygonB.count; ++i) {
      final value = normal.dot(polygonB.normals[i]);
      if (value < bestValue) {
        bestValue = value;
        bestIndex = i;
      }
    }

    final i1 = bestIndex;
    final i2 = i1 + 1 < polygonB.count ? i1 + 1 : 0;

    ie0.v.setFrom(polygonB.vertices[i1]);
    ie0.id.indexA = 0;
    ie0.id.indexB = i1 & 0xFF;
    ie0.id.typeA = ContactIDType.face.index & 0xFF;
    ie0.id.typeB = ContactIDType.vertex.index & 0xFF;

    ie1.v.setFrom(polygonB.vertices[i2]);
    ie1.id.indexA = 0;
    ie1.id.indexB = i2 & 0xFF;
    ie1.id.typeA = ContactIDType.face.index & 0xFF;
    ie1.id.typeB = ContactIDType.vertex.index & 0xFF;

    if (front) {
      _rf.i1 = 0;
      _rf.i2 = 1;
      _rf.v1.setFrom(v1);
      _rf.v2.setFrom(v2);
      _rf.normal.setFrom(normal1);
    } else {
      _rf.i1 = 1;
      _rf.i2 = 0;
      _rf.v1.setFrom(v2);
      _rf.v2.setFrom(v1);
      _rf.normal
        ..setFrom(normal1)
        ..negate();
    }
  } else {
    manifold.type = ManifoldType.faceB;

    ie0.v.setFrom(v1);
    ie0.id.indexA = 0;
    ie0.id.indexB = primaryAxis.index & 0xFF;
    ie0.id.typeA = ContactIDType.vertex.index & 0xFF;
    ie0.id.typeB = ContactIDType.face.index & 0xFF;

    ie1.v.setFrom(v2);
    ie1.id.indexA = 0;
    ie1.id.indexB = primaryAxis.index & 0xFF;
    ie1.id.typeA = ContactIDType.vertex.index & 0xFF;
    ie1.id.typeB = ContactIDType.face.index & 0xFF;

    _rf.i1 = primaryAxis.index;
    _rf.i2 = _rf.i1 + 1 < polygonB.count ? _rf.i1 + 1 : 0;
    _rf.v1.setFrom(polygonB.vertices[_rf.i1]);
    _rf.v2.setFrom(polygonB.vertices[_rf.i2]);
    _rf.normal.setFrom(polygonB.normals[_rf.i1]);
  }

  _rf.sideNormal1.setValues(_rf.normal.y, -_rf.normal.x);
  _rf.sideNormal2
    ..setFrom(_rf.sideNormal1)
    ..negate();
  _rf.sideOffset1 = _rf.sideNormal1.dot(_rf.v1);
  _rf.sideOffset2 = _rf.sideNormal2.dot(_rf.v2);

  // Clip to box side 1
  if (Collision.clipSegmentToLine(
        _clipPoints1,
        _incidentEdge,
        _rf.sideNormal1,
        _rf.sideOffset1,
        _rf.i1,
      ) <
      settings.maxManifoldPoints) {
    return;
  }

  // Clip to negative box side 1
  if (Collision.clipSegmentToLine(
        _clipPoints2,
        _clipPoints1,
        _rf.sideNormal2,
        _rf.sideOffset2,
        _rf.i2,
      ) <
      settings.maxManifoldPoints) {
    return;
  }

  // Now _clipPoints2 contains the clipped points.
  if (primaryAxis.type == EPAxisType.edgeA) {
    manifold.localNormal.setFrom(_rf.normal);
    manifold.localPoint.setFrom(_rf.v1);
  } else {
    manifold.localNormal.setFrom(polygonB2.normals[_rf.i1]);
    manifold.localPoint.setFrom(polygonB2.vertices[_rf.i1]);
  }

  var pointCount = 0;
  for (var i = 0; i < settings.maxManifoldPoints; ++i) {
    double separation;

    separation = _rf.normal.dot(
      _temp
        ..setFrom(_clipPoints2[i].v)
        ..sub(_rf.v1),
    );

    if (separation <= radius) {
      final cp = manifold.points[pointCount];

      if (primaryAxis.type == EPAxisType.edgeA) {
        cp.localPoint.setFrom(Transform.mulTransVec2(xf, _clipPoints2[i].v));
        cp.id.set(_clipPoints2[i].id);
      } else {
        cp.localPoint.setFrom(_clipPoints2[i].v);
        cp.id.typeA = _clipPoints2[i].id.typeB;
        cp.id.typeB = _clipPoints2[i].id.typeA;
        cp.id.indexA = _clipPoints2[i].id.indexB;
        cp.id.indexB = _clipPoints2[i].id.indexA;
      }

      ++pointCount;
    }
  }

  manifold.pointCount = pointCount;
}