/// <summary> /// Verifica si este poligono esta colisionando o va a colisionar /// con otro poligono dado. /// Esta verificación se realiza utilizando SAT (Separating Axis Theorem) la cual explica /// que, si existe una linea en el plano que pueda separar dos distintas figuras en diferentes /// lados por completo, entonces estas figuras no están colisionando. /// Nota: Este teorema solo debería aplicarse ante poligonos concavos, de otra forma el algoritmo /// no daria una respuesta correcta ante poligonos convexos. /// </summary> /// <param name="polygon2">Segundo poligono con el cual se hará la verificacion</param> /// <param name="distance">Distancia por la cual se movera el poligono principal (this)</param> /// <param name="result">Referencia del objeto que contendrá la información necesaria sobre /// si se esta intersectando, si se intersectará y la distancia necesaria /// para evitar una intersección</param> protected override CollisionResult IntersectWithPolygon(Polygon polygon2, ref Vector2 distance) { CollisionResult result = new CollisionResult(); result.triggeringBody = this; result.affectedBody = polygon2; // Se asume que ambos poligonos intersectan hasta que // se pueda demostrar lo contrario result.intersect = true; result.willIntersect = true; // Se obtiene la cantidad de lados del poligono principal // y del segundo poligono int sideCountA = this.sides.Count; int sideCountB = polygon2.Sides.Count; float minIntervalDistance = float.PositiveInfinity; Vector2 translationAxis = new Vector2(); // Lado actual a analizar Line currentSide; // Se realiza una verificacion por cada uno de los lados de ambos poligonos // hasta que pueda encontrarse alguna separación entre ambos o hasta que se hayan // verificado todos los lados for (int sideIndex = 0; sideIndex < sideCountA + sideCountB; sideIndex++) { if (sideIndex < sideCountA) { currentSide = this.sides[sideIndex]; } else { currentSide = polygon2.Sides[sideIndex - sideCountA]; } // Se obtiene un Vector perpendicular al lado actual con valores unitarios, // esto servira para obtener el eje sobre el cual deben hacerse las proyecciones Vector2 axis = new Vector2(-currentSide.Edge.Y, currentSide.Edge.X); axis.Normalize(); // Se proyectan ambos poligonos sobre el mismo eje float minA = 0; float minB = 0; float maxA = 0; float maxB = 0; this.Project(ref axis, out minA, out maxA); polygon2.Project(ref axis, out minB, out maxB); // Se obtiene el intervalo entre ambos poligonos sobre el eje, // si el intervalo (separación) es mayor a 0 entonces los poligonos // no están intersectando float intervalDistance = IntervalDistance(minA, maxA, minB, maxB); if (intervalDistance > 0) result.intersect = false; // Luego se realizan los mismos calculos pero sumando el vector // de velocidad al poligono en (posible) movimiento float velocityProjection = Vector2.Dot(axis, distance); //if (velocityProjection < 0) //{ minA += velocityProjection; //} //else //{ maxA += velocityProjection; //} // Si el intervalo de distancia es menor a 0 con el poligono en movimiento, // entonces los poligonos tambien intersectaran al moverse intervalDistance = IntervalDistance(minA, maxA, minB, maxB); if (intervalDistance > 0) result.willIntersect = false; // Si ya sabemos que los poligonos estan intersectando y van a intersectar, // no hay mucho mas que hacer aquí asi que terminamos las verificaciones if (result.intersect == false && result.willIntersect == false) { break; } // Si el intervalo de distancia es el minimo, se guarda // junto con el eje donde fue encontrado para asi poder separar // a los poligonos en esa dirección intervalDistance = Math.Abs(intervalDistance); if (intervalDistance < minIntervalDistance) { minIntervalDistance = intervalDistance; translationAxis = axis; Vector2 d = this.Center - polygon2.Center; if (Vector2.Dot(d, translationAxis) < 0) { translationAxis = -translationAxis; } } } // El vector minimo de transición // servira para separar ambos poligonos en caso de que colisionen. if (result.willIntersect) { result.minimumTranslationVector = translationAxis * minIntervalDistance; } result.translationAxis = translationAxis; return result; }
/// <summary> /// Verifica si este rectangulo esta colisionando o va a colisionar /// con un poligono dado. /// Esta verificación se realiza utilizando SAT (Separating Axis Theorem) la cual explica /// que, si existe una linea en el plano que pueda separar dos distintas figuras en diferentes /// lados por completo, entonces estas figuras no están colisionando. /// Nota: Este teorema solo debería aplicarse ante poligonos concavos, de otra forma el algoritmo /// no daria una respuesta correcta ante poligonos convexos. /// </summary> /// <param name="polygon2">Poligono con el cual se hará la verificacion</param> /// <param name="distance">Distancia por la cual se movera este rectangulo</param> /// <param name="result">Referencia del objeto que contendrá la información necesaria sobre /// si se esta intersectando, si se intersectará y la distancia necesaria /// para evitar una intersección</param> protected override CollisionResult IntersectWithPolygon(Polygon polygon, ref Vector2 distance) { CollisionResult result = new CollisionResult(); result.triggeringBody = this; result.affectedBody = polygon; // Se asume que ambas figuras intersectan hasta que // se pueda demostrar lo contrario result.intersect = true; result.willIntersect = true; // Se obtiene la cantidad de lados del rectangulo // y del poligono int edgeCountRectangle = 2; int edgeCountPolygon = polygon.Sides.Count; float minIntervalDistance = float.PositiveInfinity; Vector2 translationAxis = new Vector2(); Vector2 axis = new Vector2(); Line currentSide; // Se realiza una verificacion por cada uno de los lados del poligono y por dos lados // perpendiculares del rectangulo hasta que pueda encontrarse alguna separación entre // ambas o hasta que se hayan verificado todos los lados for (int edgeIndex = 0; edgeIndex < edgeCountPolygon + edgeCountRectangle; edgeIndex++) { if (edgeIndex < edgeCountRectangle) { if (edgeIndex == 0) { currentSide = this.sides[0]; } else { currentSide = this.sides[1]; } } else { currentSide = polygon.Sides[edgeIndex - edgeCountRectangle]; } // Se obtiene un Vector perpendicular al lado actual con valores unitarios, // esto servira para obtener el eje sobre el cual deben hacerse las proyecciones axis.X = -currentSide.Edge.Y; axis.Y = currentSide.Edge.X; axis.Normalize(); // Se proyectan ambas figuras sobre el mismo eje float minA = 0; float minB = 0; float maxA = 0; float maxB = 0; this.Project(ref axis, out minA, out maxA); polygon.Project(ref axis, out minB, out maxB); // Se obtiene el intervalo entre ambas figuras sobre el eje, // si el intervalo (separación) es mayor a 0 entonces las figuras // no están intersectando float intervalDistance = IntervalDistance(minA, maxA, minB, maxB); if (intervalDistance > 0) result.intersect = false; float velocityProjection; Vector2.Dot(ref axis, ref distance, out velocityProjection); // Luego se realizan los mismos calculos pero sumando el vector // de velocidad al rectangulo en (posible) movimiento //if (velocityProjection < 0) //{ minA += velocityProjection; //} //else //{ maxA += velocityProjection; //} // Si el intervalo de distancia es menor a 0 con el rectangulo en movimiento, // entonces las figuras tambien intersectaran al moverse intervalDistance = IntervalDistance(minA, maxA, minB, maxB); if (intervalDistance > 0) result.willIntersect = false; // Si ya sabemos que los poligonos estan intersectando y van a intersectar, // no hay mucho mas que hacer aquí asi que terminamos las verificaciones if (result.intersect == false && result.willIntersect == false) { break; } // Si el intervalo de distancia es el minimo, se guarda // junto con el eje donde fue encontrado para asi poder separar // a las figuras en esa dirección intervalDistance = Math.Abs(intervalDistance); if (intervalDistance < minIntervalDistance) { minIntervalDistance = intervalDistance; translationAxis = axis; Vector2 d; Vector2 secondBodyCenter = polygon.Center; Vector2.Subtract(ref this.center, ref secondBodyCenter, out d); float dot; Vector2.Dot(ref d, ref translationAxis, out dot); if (dot < 0) { Vector2.Multiply(ref translationAxis, -1, out translationAxis); } } } // El vector minimo de transición // servira para separar ambas figuras en caso de que colisionen. if (result.willIntersect) { Vector2.Multiply(ref translationAxis, minIntervalDistance, out result.minimumTranslationVector); } result.translationAxis = translationAxis; return result; }