All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.dyn4j.collision.narrowphase.CircleDetector Maven / Gradle / Ivy

/*
 * Copyright (c) 2010-2020 William Bittle  http://www.dyn4j.org/
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, are permitted 
 * provided that the following conditions are met:
 * 
 *   * Redistributions of source code must retain the above copyright notice, this list of conditions 
 *     and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above copyright notice, this list of conditions 
 *     and the following disclaimer in the documentation and/or other materials provided with the 
 *     distribution.
 *   * Neither the name of the copyright holder nor the names of its contributors may be used to endorse or 
 *     promote products derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR 
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package org.dyn4j.collision.narrowphase;

import org.dyn4j.geometry.Circle;
import org.dyn4j.geometry.Ray;
import org.dyn4j.geometry.Transform;
import org.dyn4j.geometry.Vector2;

/**
 * Class devoted to {@link Circle} detection queries.
 * @author William Bittle
 * @version 3.2.0
 * @since 2.0.0
 */
public final class CircleDetector {
	/**
	 * Hidden constructor.
	 */
	private CircleDetector() {}
	
	/**
	 * Fast method for determining a collision between two {@link Circle}s.
	 * 

* Returns true if the given {@link Circle}s are intersecting and places the * penetration vector and depth in the given {@link Penetration} object. *

* If the {@link Circle} centers are coincident then the penetration {@link Vector2} * will be the zero {@link Vector2}, however, the penetration depth will be * correct. In this case its up to the caller to determine a reasonable penetration * {@link Vector2}. * @param circle1 the first {@link Circle} * @param transform1 the first {@link Circle}'s {@link Transform} * @param circle2 the second {@link Circle} * @param transform2 the second {@link Circle}'s {@link Transform} * @param penetration the {@link Penetration} object to fill * @return boolean */ public static final boolean detect(Circle circle1, Transform transform1, Circle circle2, Transform transform2, Penetration penetration) { // get their world centers Vector2 ce1 = transform1.getTransformed(circle1.getCenter()); Vector2 ce2 = transform2.getTransformed(circle2.getCenter()); // create a vector from one center to the other Vector2 v = ce2.subtract(ce1); // check the magnitude against the sum of the radii double radii = circle1.getRadius() + circle2.getRadius(); // get the magnitude squared double mag = v.getMagnitudeSquared(); // check difference if (mag < radii * radii) { // then we have a collision penetration.depth = radii - v.normalize(); penetration.normal.x = v.x; penetration.normal.y = v.y; return true; } return false; } /** * Fast method for determining a collision between two {@link Circle}s. *

* Returns true if the given {@link Circle}s are intersecting. * @param circle1 the first {@link Circle} * @param transform1 the first {@link Circle}'s {@link Transform} * @param circle2 the second {@link Circle} * @param transform2 the second {@link Circle}'s {@link Transform} * @return boolean true if the two circles intersect */ public static final boolean detect(Circle circle1, Transform transform1, Circle circle2, Transform transform2) { // get their world centers Vector2 ce1 = transform1.getTransformed(circle1.getCenter()); Vector2 ce2 = transform2.getTransformed(circle2.getCenter()); // create a vector from one center to the other Vector2 v = ce2.subtract(ce1); // check the magnitude against the sum of the radii double radii = circle1.getRadius() + circle2.getRadius(); // get the magnitude squared double mag = v.getMagnitudeSquared(); // check difference if (mag < radii * radii) { // then we have a collision return true; } return false; } /** * Fast method for determining the distance between two {@link Circle}s. *

* Returns true if the given {@link Circle}s are separated and places the * separating vector and distance in the given {@link Separation} object. * @param circle1 the first {@link Circle} * @param transform1 the first {@link Circle}'s {@link Transform} * @param circle2 the second {@link Circle} * @param transform2 the second {@link Circle}'s {@link Transform} * @param separation the {@link Separation} object to fill * @return boolean */ public static final boolean distance(Circle circle1, Transform transform1, Circle circle2, Transform transform2, Separation separation) { // get their world centers Vector2 ce1 = transform1.getTransformed(circle1.getCenter()); Vector2 ce2 = transform2.getTransformed(circle2.getCenter()); // get the radii double r1 = circle1.getRadius(); double r2 = circle2.getRadius(); // create a vector from one center to the other Vector2 v = ce1.to(ce2); // check the magnitude against the sum of the radii double radii = r1 + r2; // get the magnitude squared double mag = v.getMagnitudeSquared(); // check difference if (mag >= radii * radii) { // then the circles are separated separation.distance = v.normalize() - radii; separation.normal.x = v.x; separation.normal.y = v.y; separation.point1.x = ce1.x + v.x * r1; separation.point1.y = ce1.y + v.y * r1; separation.point2.x = ce2.x - v.x * r2; separation.point2.y = ce2.y - v.y * r2; return true; } return false; } /** * Performs a ray cast against the given circle. * @param ray the {@link Ray} * @param maxLength the maximum ray length * @param circle the {@link Circle} * @param transform the {@link Circle}'s {@link Transform} * @param raycast the {@link Raycast} result * @return boolean true if the ray intersects the circle * @since 2.0.0 */ public static final boolean raycast(Ray ray, double maxLength, Circle circle, Transform transform, Raycast raycast) { // solve the problem algebraically Vector2 s = ray.getStart(); Vector2 d = ray.getDirectionVector(); Vector2 ce = transform.getTransformed(circle.getCenter()); double r = circle.getRadius(); // make sure the start of the ray is not contained in the circle if (circle.contains(s, transform)) return false; // any point on a ray can be found by the parametric equation: // P = tD + S // any point on a circle can be found by: // (x - h)^2 + (y - k)^2 = r^2 where h and k are the x and y center coordinates // substituting the first equation into the second yields a quadratic equation: // |D|^2t^2 + 2D.dot(S - C)t + (S - C)^2 - r^2 = 0 // using the quadratic equation we can solve for t where // a = |D|^2 // b = 2D.dot(S - C) // c = (S - C)^2 - r^2 Vector2 sMinusC = s.difference(ce); // mag(d)^2 double a = d.dot(d); // 2d.dot(s - c) double b = 2 * d.dot(sMinusC); // (s - c)^2 - r^2 double c = sMinusC.dot(sMinusC) - r * r; // precompute double inv2a = 1.0 / (2.0 * a); double b24ac = b * b - 4 * a * c; // check for negative inside the sqrt if (b24ac < 0.0) { // if the computation inside the sqrt is // negative then this indicates that the // ray is parallel to the circle return false; } double sqrt = Math.sqrt(b24ac); // compute the two values of t double t0 = (-b + sqrt) * inv2a; double t1 = (-b - sqrt) * inv2a; // find the correct t // t cannot be negative since this would make the point // in the opposite direction of the ray's direction double t = 0.0; // check for negative value if (t0 < 0.0) { // check for negative value if (t1 < 0.0) { // return the ray does not intersect the circle return false; } else { // t1 is the answer t = t1; } } else { // check for negative value if (t1 < 0.0) { // t0 is the answer t = t0; } else if (t0 < t1) { // t0 is the answer t = t0; } else { // t1 is the answer t = t1; } } // check the value of t if (maxLength > 0.0 && t > maxLength) { // if the smallest non-negative t is larger // than the maximum length then return false return false; } // compute the hit point Vector2 p = d.product(t).add(s); // compute the normal Vector2 n = ce.to(p); n.normalize(); // populate the raycast result raycast.point.x = p.x; raycast.point.y = p.y; raycast.normal.x = n.x; raycast.normal.y = n.y; raycast.distance = t; // return success return true; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy