static Vector2L GetAxisNormal(List <Vector2L> vertexArray, int pointIndex) { Vector2L pt1 = vertexArray[pointIndex]; Vector2L pt2 = pointIndex >= vertexArray.Count - 1 ? vertexArray[0] : vertexArray[pointIndex + 1]; Vector2L p = new Vector2L(-(pt2.y - pt1.y), pt2.x - pt1.x); p.Normalize(); return(p); }
/// <summary> /// 根据Separating Axis Theorem理论实现 /// https://www.sevenson.com.au/actionscript/sat/ /// https://github.com/sevdanski/SAT_AS3 /// </summary> /// <param name="convex1"></param> /// <param name="convex2"></param> /// <returns></returns> public static CollisionInfo Circle2dWithConvex2d(Circle2d circleA, Convex2d polygonA, bool flip, bool docalc) { CollisionInfo result = new CollisionInfo(); if (flip) { result.shapeA = polygonA; result.shapeB = circleA; } else { result.shapeA = circleA; result.shapeB = polygonA; } result.shapeAContained = true; result.shapeBContained = true; // get the offset Vector2L vOffset = new Vector2L(polygonA.m_pos.x - circleA.m_pos.x, polygonA.m_pos.y - circleA.m_pos.y); // get the vertices List <Vector2L> p1 = polygonA.GetRotateList(); // find the closest point Vector2L closestPoint = new Vector2L(); FloatL minDist = FloatL.MaxValue; foreach (var iter in p1) { FloatL currentDist = (circleA.m_pos - (polygonA.m_pos + iter)).sqrMagnitude; if (currentDist < minDist) { minDist = currentDist; closestPoint = polygonA.m_pos + iter; } } // make a normal of this vector Vector2L vAxis = closestPoint - circleA.m_pos; vAxis.Normalize(); // project polygon A FloatL min0 = Vector2L.Dot(vAxis, p1[0]); FloatL max0 = min0; for (int j = 1; j < p1.Count; ++j) { FloatL t = Vector2L.Dot(vAxis, p1[j]); if (t < min0) { min0 = t; } if (t > max0) { max0 = t; } } // project circle A FloatL min1 = Vector2L.Dot(vAxis, Vector2L.zero); FloatL max1 = min1 + circleA.m_radius; min1 -= circleA.m_radius; // shift polygonA's projected points FloatL sOffset = Vector2L.Dot(vAxis, vOffset); min0 += sOffset; max0 += sOffset; // test for intersections FloatL d0 = min0 - max1; FloatL d1 = min1 - max0; if (d0 > 0 || d1 > 0) { // gap found return(null); } FloatL shortestDist = FloatL.MaxValue; if (docalc) { FloatL distmin = (max1 - min0) * -1; //Math.min(dist0, dist1); if (flip) { distmin *= -1; } FloatL distminAbs = (distmin < 0) ? distmin * -1 : distmin; // check for containment if (!flip) { if (max0 > max1 || min0 < min1) { result.shapeAContained = false; } if (max1 > max0 || min1 < min0) { result.shapeBContained = false; } } else { if (max0 < max1 || min0 > min1) { result.shapeAContained = false; } if (max1 < max0 || min1 > min0) { result.shapeBContained = false; } } // this distance is shorter so use it... result.distance = distmin; result.vector = vAxis; // shortestDist = distminAbs; } // loop through all of the axis on the first polygon for (int i = 0; i < p1.Count; i++) { // find the axis that we will project onto vAxis = GetAxisNormal(p1, i); // project polygon A min0 = Vector2L.Dot(vAxis, p1[0]); max0 = min0; // for (int j = 1; j < p1.Count; j++) { FloatL t = Vector2L.Dot(vAxis, p1[j]); if (t < min0) { min0 = t; } if (t > max0) { max0 = t; } } // project circle A min1 = Vector2L.Dot(vAxis, new Vector2L(0, 0)); max1 = min1 + circleA.m_radius; min1 -= circleA.m_radius; // shift polygonA's projected points sOffset = Vector2L.Dot(vAxis, vOffset); min0 += sOffset; max0 += sOffset; // test for intersections d0 = min0 - max1; d1 = min1 - max0; if (d0 > 0 || d1 > 0) { // gap found return(null); } if (docalc) { // check for containment if (!flip) { if (max0 > max1 || min0 < min1) { result.shapeAContained = false; } if (max1 > max0 || min1 < min0) { result.shapeBContained = false; } } else { if (max0 < max1 || min0 > min1) { result.shapeAContained = false; } if (max1 < max0 || min1 > min0) { result.shapeBContained = false; } } FloatL distmin = (max1 - min0) * -1; if (flip) { distmin *= -1; } FloatL distminAbs = (distmin < 0) ? distmin * -1 : distmin; if (distminAbs < shortestDist) { // this distance is shorter so use it... result.distance = distmin; result.vector = vAxis; // shortestDist = distminAbs; } } } // if you are here then no gap was found return(result); }