diff --git a/src/main/java/pythagoras/d/AffineTransform.java b/src/main/java/pythagoras/d/AffineTransform.java
index c2d29ad..9f7f213 100644
--- a/src/main/java/pythagoras/d/AffineTransform.java
+++ b/src/main/java/pythagoras/d/AffineTransform.java
@@ -309,6 +309,12 @@ public class AffineTransform extends AbstractTransform
(y * m00 - x * m01) * rdet);
}
+ @Override // from Transform
+ public Vector transformPoint (IVector v, Vector into) {
+ double x = v.x(), y = v.y();
+ return into.set(m00*x + m10*y + tx, m01*x + m11*y + ty);
+ }
+
@Override // from Transform
public Vector transform (IVector v, Vector into) {
double x = v.x(), y = v.y();
diff --git a/src/main/java/pythagoras/d/Box.java b/src/main/java/pythagoras/d/Box.java
index b34a5bc..02da7f5 100644
--- a/src/main/java/pythagoras/d/Box.java
+++ b/src/main/java/pythagoras/d/Box.java
@@ -366,54 +366,45 @@ public class Box implements IBox, Serializable
_maxExtent.z >= omin.z() && _minExtent.z <= omax.z();
}
- // /**
- // * Determines whether the specified ray intersects this box.
- // */
- // public boolean intersects (Ray3D ray) {
- // Vector3 dir = ray.direction();
- // return
- // Math.abs(dir.x) > MathUtil.EPSILON &&
- // (intersectsX(ray, _minExtent.x) || intersectsX(ray, _maxExtent.x)) ||
- // Math.abs(dir.y) > MathUtil.EPSILON &&
- // (intersectsY(ray, _minExtent.y) || intersectsY(ray, _maxExtent.y)) ||
- // Math.abs(dir.z) > MathUtil.EPSILON &&
- // (intersectsZ(ray, _minExtent.z) || intersectsZ(ray, _maxExtent.z));
- // }
+ @Override // from IBox
+ public boolean intersects (IRay3 ray) {
+ IVector3 dir = ray.direction();
+ return
+ Math.abs(dir.x()) > MathUtil.EPSILON &&
+ (intersectsX(ray, _minExtent.x) || intersectsX(ray, _maxExtent.x)) ||
+ Math.abs(dir.y()) > MathUtil.EPSILON &&
+ (intersectsY(ray, _minExtent.y) || intersectsY(ray, _maxExtent.y)) ||
+ Math.abs(dir.z()) > MathUtil.EPSILON &&
+ (intersectsZ(ray, _minExtent.z) || intersectsZ(ray, _maxExtent.z));
+ }
- // /**
- // * Finds the location of the (first) intersection between the specified ray and this box.
- // * This will be the ray origin if the ray starts inside the box.
- // *
- // * @param result a vector to hold the location of the intersection.
- // * @return true if the ray intersects the box (in which case the result vector will be
- // * populated with the location of the intersection), false if not.
- // */
- // public boolean intersection (Ray3D ray, Vector3 result) {
- // Vector3 origin = ray.origin();
- // if (contains(origin)) {
- // result.set(origin);
- // return true;
- // }
- // Vector3 dir = ray.direction();
- // double t = Float.MAX_VALUE;
- // if (Math.abs(dir.x) > MathUtil.EPSILON) {
- // t = Math.min(t, intersectionX(ray, _minExtent.x));
- // t = Math.min(t, intersectionX(ray, _maxExtent.x));
- // }
- // if (Math.abs(dir.y) > MathUtil.EPSILON) {
- // t = Math.min(t, intersectionY(ray, _minExtent.y));
- // t = Math.min(t, intersectionY(ray, _maxExtent.y));
- // }
- // if (Math.abs(dir.z) > MathUtil.EPSILON) {
- // t = Math.min(t, intersectionZ(ray, _minExtent.z));
- // t = Math.min(t, intersectionZ(ray, _maxExtent.z));
- // }
- // if (t == Float.MAX_VALUE) {
- // return false;
- // }
- // origin.addScaled(dir, t, result);
- // return true;
- // }
+ @Override // from IBox
+ public boolean intersection (IRay3 ray, Vector3 result) {
+ IVector3 origin = ray.origin();
+ if (contains(origin)) {
+ result.set(origin);
+ return true;
+ }
+ IVector3 dir = ray.direction();
+ double t = Float.MAX_VALUE;
+ if (Math.abs(dir.x()) > MathUtil.EPSILON) {
+ t = Math.min(t, intersectionX(ray, _minExtent.x));
+ t = Math.min(t, intersectionX(ray, _maxExtent.x));
+ }
+ if (Math.abs(dir.y()) > MathUtil.EPSILON) {
+ t = Math.min(t, intersectionY(ray, _minExtent.y));
+ t = Math.min(t, intersectionY(ray, _maxExtent.y));
+ }
+ if (Math.abs(dir.z()) > MathUtil.EPSILON) {
+ t = Math.min(t, intersectionZ(ray, _minExtent.z));
+ t = Math.min(t, intersectionZ(ray, _maxExtent.z));
+ }
+ if (t == Float.MAX_VALUE) {
+ return false;
+ }
+ origin.addScaled(dir, t, result);
+ return true;
+ }
@Override // documentation inherited
public String toString () {
@@ -434,98 +425,98 @@ public class Box implements IBox, Serializable
return _minExtent.equals(obox._minExtent) && _maxExtent.equals(obox._maxExtent);
}
- // /**
- // * Helper method for {@link #intersects(Ray3D)}. Determines whether the ray intersects the box
- // * at the plane where x equals the value specified.
- // */
- // protected boolean intersectsX (Ray3D ray, double x) {
- // Vector3 origin = ray.origin(), dir = ray.direction();
- // double t = (x - origin.x) / dir.x;
- // if (t < 0f) {
- // return false;
- // }
- // double iy = origin.y + t*dir.y, iz = origin.z + t*dir.z;
- // return iy >= _minExtent.y && iy <= _maxExtent.y &&
- // iz >= _minExtent.z && iz <= _maxExtent.z;
- // }
+ /**
+ * Helper method for {@link #intersects(Ray3)}. Determines whether the ray intersects the box
+ * at the plane where x equals the value specified.
+ */
+ protected boolean intersectsX (IRay3 ray, double x) {
+ IVector3 origin = ray.origin(), dir = ray.direction();
+ double t = (x - origin.x()) / dir.x();
+ if (t < 0f) {
+ return false;
+ }
+ double iy = origin.y() + t*dir.y(), iz = origin.z() + t*dir.z();
+ return iy >= _minExtent.y && iy <= _maxExtent.y &&
+ iz >= _minExtent.z && iz <= _maxExtent.z;
+ }
- // /**
- // * Helper method for {@link #intersects(Ray3D)}. Determines whether the ray intersects the box
- // * at the plane where y equals the value specified.
- // */
- // protected boolean intersectsY (Ray3D ray, double y) {
- // Vector3 origin = ray.origin(), dir = ray.direction();
- // double t = (y - origin.y) / dir.y;
- // if (t < 0f) {
- // return false;
- // }
- // double ix = origin.x + t*dir.x, iz = origin.z + t*dir.z;
- // return ix >= _minExtent.x && ix <= _maxExtent.x &&
- // iz >= _minExtent.z && iz <= _maxExtent.z;
- // }
+ /**
+ * Helper method for {@link #intersects(Ray3)}. Determines whether the ray intersects the box
+ * at the plane where y equals the value specified.
+ */
+ protected boolean intersectsY (IRay3 ray, double y) {
+ IVector3 origin = ray.origin(), dir = ray.direction();
+ double t = (y - origin.y()) / dir.y();
+ if (t < 0f) {
+ return false;
+ }
+ double ix = origin.x() + t*dir.x(), iz = origin.z() + t*dir.z();
+ return ix >= _minExtent.x && ix <= _maxExtent.x &&
+ iz >= _minExtent.z && iz <= _maxExtent.z;
+ }
- // /**
- // * Helper method for {@link #intersects(Ray3D)}. Determines whether the ray intersects the box
- // * at the plane where z equals the value specified.
- // */
- // protected boolean intersectsZ (Ray3D ray, double z) {
- // Vector3 origin = ray.origin(), dir = ray.direction();
- // double t = (z - origin.z) / dir.z;
- // if (t < 0f) {
- // return false;
- // }
- // double ix = origin.x + t*dir.x, iy = origin.y + t*dir.y;
- // return ix >= _minExtent.x && ix <= _maxExtent.x &&
- // iy >= _minExtent.y && iy <= _maxExtent.y;
- // }
+ /**
+ * Helper method for {@link #intersects(Ray3)}. Determines whether the ray intersects the box
+ * at the plane where z equals the value specified.
+ */
+ protected boolean intersectsZ (IRay3 ray, double z) {
+ IVector3 origin = ray.origin(), dir = ray.direction();
+ double t = (z - origin.z()) / dir.z();
+ if (t < 0f) {
+ return false;
+ }
+ double ix = origin.x() + t*dir.x(), iy = origin.y() + t*dir.y();
+ return ix >= _minExtent.x && ix <= _maxExtent.x &&
+ iy >= _minExtent.y && iy <= _maxExtent.y;
+ }
- // /**
- // * Helper method for {@link #intersection}. Finds the t value where the ray
- // * intersects the box at the plane where x equals the value specified, or returns
- // * {@link Float#MAX_VALUE} if there is no such intersection.
- // */
- // protected double intersectionX (Ray3D ray, double x) {
- // Vector3 origin = ray.origin(), dir = ray.direction();
- // double t = (x - origin.x) / dir.x;
- // if (t < 0f) {
- // return Float.MAX_VALUE;
- // }
- // double iy = origin.y + t*dir.y, iz = origin.z + t*dir.z;
- // return (iy >= _minExtent.y && iy <= _maxExtent.y &&
- // iz >= _minExtent.z && iz <= _maxExtent.z) ? t : Float.MAX_VALUE;
- // }
+ /**
+ * Helper method for {@link #intersection}. Finds the t value where the ray
+ * intersects the box at the plane where x equals the value specified, or returns
+ * {@link Float#MAX_VALUE} if there is no such intersection.
+ */
+ protected double intersectionX (IRay3 ray, double x) {
+ IVector3 origin = ray.origin(), dir = ray.direction();
+ double t = (x - origin.x()) / dir.x();
+ if (t < 0f) {
+ return Float.MAX_VALUE;
+ }
+ double iy = origin.y() + t*dir.y(), iz = origin.z() + t*dir.z();
+ return (iy >= _minExtent.y && iy <= _maxExtent.y &&
+ iz >= _minExtent.z && iz <= _maxExtent.z) ? t : Float.MAX_VALUE;
+ }
- // /**
- // * Helper method for {@link #intersection}. Finds the t value where the ray
- // * intersects the box at the plane where y equals the value specified, or returns
- // * {@link Float#MAX_VALUE} if there is no such intersection.
- // */
- // protected double intersectionY (Ray3D ray, double y) {
- // Vector3 origin = ray.origin(), dir = ray.direction();
- // double t = (y - origin.y) / dir.y;
- // if (t < 0f) {
- // return Float.MAX_VALUE;
- // }
- // double ix = origin.x + t*dir.x, iz = origin.z + t*dir.z;
- // return (ix >= _minExtent.x && ix <= _maxExtent.x &&
- // iz >= _minExtent.z && iz <= _maxExtent.z) ? t : Float.MAX_VALUE;
- // }
+ /**
+ * Helper method for {@link #intersection}. Finds the t value where the ray
+ * intersects the box at the plane where y equals the value specified, or returns
+ * {@link Float#MAX_VALUE} if there is no such intersection.
+ */
+ protected double intersectionY (IRay3 ray, double y) {
+ IVector3 origin = ray.origin(), dir = ray.direction();
+ double t = (y - origin.y()) / dir.y();
+ if (t < 0f) {
+ return Float.MAX_VALUE;
+ }
+ double ix = origin.x() + t*dir.x(), iz = origin.z() + t*dir.z();
+ return (ix >= _minExtent.x && ix <= _maxExtent.x &&
+ iz >= _minExtent.z && iz <= _maxExtent.z) ? t : Float.MAX_VALUE;
+ }
- // /**
- // * Helper method for {@link #intersection}. Finds the t value where the ray
- // * intersects the box at the plane where z equals the value specified, or returns
- // * {@link Float#MAX_VALUE} if there is no such intersection.
- // */
- // protected double intersectionZ (Ray3D ray, double z) {
- // Vector3 origin = ray.origin(), dir = ray.direction();
- // double t = (z - origin.z) / dir.z;
- // if (t < 0f) {
- // return Float.MAX_VALUE;
- // }
- // double ix = origin.x + t*dir.x, iy = origin.y + t*dir.y;
- // return (ix >= _minExtent.x && ix <= _maxExtent.x &&
- // iy >= _minExtent.y && iy <= _maxExtent.y) ? t : Float.MAX_VALUE;
- // }
+ /**
+ * Helper method for {@link #intersection}. Finds the t value where the ray
+ * intersects the box at the plane where z equals the value specified, or returns
+ * {@link Float#MAX_VALUE} if there is no such intersection.
+ */
+ protected double intersectionZ (IRay3 ray, double z) {
+ IVector3 origin = ray.origin(), dir = ray.direction();
+ double t = (z - origin.z()) / dir.z();
+ if (t < 0f) {
+ return Float.MAX_VALUE;
+ }
+ double ix = origin.x() + t*dir.x(), iy = origin.y() + t*dir.y();
+ return (ix >= _minExtent.x && ix <= _maxExtent.x &&
+ iy >= _minExtent.y && iy <= _maxExtent.y) ? t : Float.MAX_VALUE;
+ }
/** The box's minimum extent. */
protected final Vector3 _minExtent = new Vector3();
diff --git a/src/main/java/pythagoras/d/IBox.java b/src/main/java/pythagoras/d/IBox.java
index 02ba7ff..c1b9fc0 100644
--- a/src/main/java/pythagoras/d/IBox.java
+++ b/src/main/java/pythagoras/d/IBox.java
@@ -156,8 +156,18 @@ public interface IBox
*/
Box expand (double x, double y, double z, Box result);
- // /**
- // * Determines whether the specified ray intersects this box.
- // */
- // boolean intersects (Ray3D ray);
+ /**
+ * Determines whether the specified ray intersects this box.
+ */
+ boolean intersects (IRay3 ray);
+
+ /**
+ * Finds the location of the (first) intersection between the specified ray and this box. This
+ * will be the ray origin if the ray starts inside the box.
+ *
+ * @param result a vector to hold the location of the intersection.
+ * @return true if the ray intersects the box (in which case the result vector will be
+ * populated with the location of the intersection), false if not.
+ */
+ boolean intersection (IRay3 ray, Vector3 result);
}
diff --git a/src/main/java/pythagoras/d/IPlane.java b/src/main/java/pythagoras/d/IPlane.java
index 3829f9a..c9e0354 100644
--- a/src/main/java/pythagoras/d/IPlane.java
+++ b/src/main/java/pythagoras/d/IPlane.java
@@ -32,7 +32,7 @@ public interface IPlane
// *
// * @return a new plane containing the result.
// */
- // Plane transform (Transform transform);
+ // Plane transform (Transform3D transform);
// /**
// * Transforms this plane by the specified transformation, placing the result in the object
@@ -40,7 +40,7 @@ public interface IPlane
// *
// * @return a reference to the result plane, for chaining.
// */
- // Plane transform (Transform transform, Plane result);
+ // Plane transform (Transform3D transform, Plane result);
/**
* Negates this plane.
@@ -56,19 +56,19 @@ public interface IPlane
*/
Plane negate (Plane result);
- // /**
- // * Computes the intersection of the supplied ray with this plane, placing the result
- // * in the given vector (if the ray intersects).
- // *
- // * @return true if the ray intersects the plane (in which case the result will contain
- // * the point of intersection), false if not.
- // */
- // boolean intersection (Ray3D ray, Vector3 result);
+ /**
+ * Computes the intersection of the supplied ray with this plane, placing the result
+ * in the given vector (if the ray intersects).
+ *
+ * @return true if the ray intersects the plane (in which case the result will contain
+ * the point of intersection), false if not.
+ */
+ boolean intersection (IRay3 ray, Vector3 result);
- // /**
- // * Computes the signed distance to this plane along the specified ray.
- // *
- // * @return the signed distance, or {@link Float#NaN} if the ray runs parallel to the plane.
- // */
- // double distance (Ray3D ray);
+ /**
+ * Computes the signed distance to this plane along the specified ray.
+ *
+ * @return the signed distance, or {@link Float#NaN} if the ray runs parallel to the plane.
+ */
+ double distance (IRay3 ray);
}
diff --git a/src/main/java/pythagoras/d/IRay2.java b/src/main/java/pythagoras/d/IRay2.java
new file mode 100644
index 0000000..dc78f30
--- /dev/null
+++ b/src/main/java/pythagoras/d/IRay2.java
@@ -0,0 +1,72 @@
+//
+// Pythagoras - a collection of geometry classes
+// http://github.com/samskivert/pythagoras
+
+package pythagoras.d;
+
+/**
+ * Provides read-only access to a {@link Ray2}.
+ */
+public interface IRay2
+{
+ /**
+ * Returns a reference to the ray's point of origin.
+ */
+ IVector origin ();
+
+ /**
+ * Returns a reference to the ray's unit direction vector.
+ */
+ IVector direction ();
+
+ /**
+ * Transforms this ray.
+ *
+ * @return a new ray containing the result.
+ */
+ Ray2 transform (Transform transform);
+
+ /**
+ * Transforms this ray, placing the result in the object provided.
+ *
+ * @return a reference to the result ray, for chaining.
+ */
+ Ray2 transform (Transform transform, Ray2 result);
+
+ /**
+ * Determines whether the ray intersects the specified point.
+ */
+ boolean intersects (IVector pt);
+
+ /**
+ * Finds the intersection between the ray and a line segment with the given start and end
+ * points.
+ *
+ * @return true if the ray intersected the segment (in which case the result will contain the
+ * point of intersection), false otherwise.
+ */
+ boolean getIntersection (IVector start, IVector end, Vector result);
+
+ /**
+ * Finds the intersection between the ray and a capsule with the given start point, end point,
+ * and radius.
+ *
+ * @return true if the ray intersected the circle (in which case the result will contain the
+ * point of intersection), false otherwise.
+ */
+ boolean getIntersection (IVector start, IVector end, double radius, Vector result);
+
+ /**
+ * Finds the intersection between the ray and a circle with the given center and radius.
+ *
+ * @return true if the ray intersected the circle (in which case the result will contain the
+ * point of intersection), false otherwise.
+ */
+ boolean getIntersection (IVector center, double radius, Vector result);
+
+ /**
+ * Computes the nearest point on the Ray to the supplied point.
+ * @return {@code result} for chaining.
+ */
+ Vector getNearestPoint (IVector point, Vector result);
+}
diff --git a/src/main/java/pythagoras/d/IRay3.java b/src/main/java/pythagoras/d/IRay3.java
new file mode 100644
index 0000000..72f280a
--- /dev/null
+++ b/src/main/java/pythagoras/d/IRay3.java
@@ -0,0 +1,35 @@
+//
+// Pythagoras - a collection of geometry classes
+// http://github.com/samskivert/pythagoras
+
+package pythagoras.d;
+
+/**
+ * Provides read-only access to a {@link Ray3}.
+ */
+public interface IRay3
+{
+ /**
+ * Returns a reference to the ray's point of origin.
+ */
+ IVector3 origin ();
+
+ /**
+ * Returns a reference to the ray's unit direction vector.
+ */
+ IVector3 direction ();
+
+ // /**
+ // * Transforms this ray.
+ // *
+ // * @return a new ray containing the result.
+ // */
+ // Ray3 transform (Transform3D transform);
+
+ // /**
+ // * Transforms this ray, placing the result in the object provided.
+ // *
+ // * @return a reference to the result ray, for chaining.
+ // */
+ // Ray3 transform (Transform3D transform, Ray3 result);
+}
diff --git a/src/main/java/pythagoras/d/IdentityTransform.java b/src/main/java/pythagoras/d/IdentityTransform.java
index 267e388..daa88f4 100644
--- a/src/main/java/pythagoras/d/IdentityTransform.java
+++ b/src/main/java/pythagoras/d/IdentityTransform.java
@@ -86,6 +86,11 @@ public class IdentityTransform extends AbstractTransform
return into.set(p);
}
+ @Override // from Transform
+ public Vector transformPoint (IVector v, Vector into) {
+ return into.set(v);
+ }
+
@Override // from Transform
public Vector transform (IVector v, Vector into) {
return into.set(v);
diff --git a/src/main/java/pythagoras/d/NonUniformTransform.java b/src/main/java/pythagoras/d/NonUniformTransform.java
index 8777c39..3a4fd91 100644
--- a/src/main/java/pythagoras/d/NonUniformTransform.java
+++ b/src/main/java/pythagoras/d/NonUniformTransform.java
@@ -231,6 +231,11 @@ public class NonUniformTransform extends AbstractTransform
return Points.inverseTransform(p.x(), p.y(), scaleX, scaleY, rotation, tx, ty, into);
}
+ @Override // from Transform
+ public Vector transformPoint (IVector v, Vector into) {
+ return Vectors.transform(v.x(), v.y(), scaleX, scaleY, rotation, tx, ty, into);
+ }
+
@Override // from Transform
public Vector transform (IVector v, Vector into) {
return Vectors.transform(v.x(), v.y(), scaleX, scaleY, rotation, into);
diff --git a/src/main/java/pythagoras/d/Plane.java b/src/main/java/pythagoras/d/Plane.java
index 8f05548..ab7d8c7 100644
--- a/src/main/java/pythagoras/d/Plane.java
+++ b/src/main/java/pythagoras/d/Plane.java
@@ -129,7 +129,7 @@ public class Plane implements IPlane, Serializable
// *
// * @return a reference to this plane, for chaining.
// */
- // public Plane transformLocal (Transform transform) {
+ // public Plane transformLocal (Transform3D transform) {
// return transform(transform, this);
// }
@@ -163,12 +163,12 @@ public class Plane implements IPlane, Serializable
}
// @Override // from IPlane
- // public Plane transform (Transform transform) {
+ // public Plane transform (Transform3D transform) {
// return transform(transform, new Plane());
// }
// @Override // from IPlane
- // public Plane transform (Transform transform, Plane result) {
+ // public Plane transform (Transform3D transform, Plane result) {
// transform.transformPointLocal(_normal.mult(-constant, _v1));
// transform.transformVector(_normal, _v2).normalizeLocal();
// return result.fromPointNormal(_v1, _v2);
@@ -186,29 +186,29 @@ public class Plane implements IPlane, Serializable
return result;
}
- // @Override // from IPlane
- // public boolean intersection (Ray3D ray, Vector3 result) {
- // double distance = distance(ray);
- // if (Double.isNaN(distance) || distance < 0f) {
- // return false;
- // } else {
- // ray.origin().addScaled(ray.direction(), distance, result);
- // return true;
- // }
- // }
+ @Override // from IPlane
+ public boolean intersection (IRay3 ray, Vector3 result) {
+ double distance = distance(ray);
+ if (Float.isNaN(distance) || distance < 0f) {
+ return false;
+ } else {
+ ray.origin().addScaled(ray.direction(), distance, result);
+ return true;
+ }
+ }
- // @Override // from IPlane
- // public double distance (Ray3D ray) {
- // double dividend = -distance(ray.origin());
- // double divisor = _normal.dot(ray.direction());
- // if (Math.abs(dividend) < MathUtil.EPSILON) {
- // return 0f; // origin is on plane
- // } else if (Math.abs(divisor) < MathUtil.EPSILON) {
- // return Float.NaN; // ray is parallel to plane
- // } else {
- // return dividend / divisor;
- // }
- // }
+ @Override // from IPlane
+ public double distance (IRay3 ray) {
+ double dividend = -distance(ray.origin());
+ double divisor = _normal.dot(ray.direction());
+ if (Math.abs(dividend) < MathUtil.EPSILON) {
+ return 0f; // origin is on plane
+ } else if (Math.abs(divisor) < MathUtil.EPSILON) {
+ return Float.NaN; // ray is parallel to plane
+ } else {
+ return dividend / divisor;
+ }
+ }
@Override
public int hashCode () {
diff --git a/src/main/java/pythagoras/d/Quaternion.java b/src/main/java/pythagoras/d/Quaternion.java
index bc00eab..2e68c44 100644
--- a/src/main/java/pythagoras/d/Quaternion.java
+++ b/src/main/java/pythagoras/d/Quaternion.java
@@ -308,7 +308,7 @@ public class Quaternion implements IQuaternion, Serializable
@Override // from IQuaternion
public boolean hasNaN () {
- return Double.isNaN(x) || Double.isNaN(y) || Double.isNaN(z) || Double.isNaN(w);
+ return Float.isNaN(x) || Float.isNaN(y) || Float.isNaN(z) || Float.isNaN(w);
}
@Override // from IQuaternion
diff --git a/src/main/java/pythagoras/d/Ray2.java b/src/main/java/pythagoras/d/Ray2.java
new file mode 100644
index 0000000..256756f
--- /dev/null
+++ b/src/main/java/pythagoras/d/Ray2.java
@@ -0,0 +1,234 @@
+//
+// Pythagoras - a collection of geometry classes
+// http://github.com/samskivert/pythagoras
+
+package pythagoras.d;
+
+/**
+ * A ray consisting of an origin point and a unit direction vector.
+ */
+public class Ray2 implements IRay2
+{
+ /** The ray's point of origin. */
+ public final Vector origin = new Vector();
+
+ /** The ray's unit direction vector. */
+ public final Vector direction = new Vector();
+
+ /**
+ * Creates a ray with the values contained in the supplied origin point and unit direction
+ * vector.
+ */
+ public Ray2 (Vector origin, Vector direction) {
+ set(origin, direction);
+ }
+
+ /**
+ * Copy constructor.
+ */
+ public Ray2 (Ray2 other) {
+ set(other);
+ }
+
+ /**
+ * Creates an empty (invalid) ray.
+ */
+ public Ray2 () {
+ }
+
+ /**
+ * Copies the parameters of another ray.
+ *
+ * @return a reference to this ray, for chaining.
+ */
+ public Ray2 set (IRay2 other) {
+ return set(other.origin(), other.direction());
+ }
+
+ /**
+ * Sets the ray parameters to the values contained in the supplied vectors.
+ *
+ * @return a reference to this ray, for chaining.
+ */
+ public Ray2 set (IVector origin, IVector direction) {
+ this.origin.set(origin);
+ this.direction.set(direction);
+ return this;
+ }
+
+ /**
+ * Transforms this ray in-place.
+ *
+ * @return a reference to this ray, for chaining.
+ */
+ public Ray2 transformLocal (Transform transform) {
+ return transform(transform, this);
+ }
+
+ @Override // from IRay2
+ public IVector origin () {
+ return origin;
+ }
+
+ @Override // from IRay2
+ public IVector direction () {
+ return direction;
+ }
+
+ @Override // from IRay2
+ public Ray2 transform (Transform transform) {
+ return transform(transform, new Ray2());
+ }
+
+ @Override // from IRay2
+ public Ray2 transform (Transform transform, Ray2 result) {
+ transform.transformPoint(origin, result.origin);
+ transform.transform(direction, result.direction).normalizeLocal();
+ return result;
+ }
+
+ @Override // from IRay2
+ public boolean intersects (IVector pt) {
+ if (Math.abs(direction.x) > Math.abs(direction.y)) {
+ double t = (pt.x() - origin.x) / direction.x;
+ return t >= 0f && origin.y + t*direction.y == pt.y();
+ } else {
+ double t = (pt.y() - origin.y) / direction.y;
+ return t >= 0f && origin.x + t*direction.x == pt.x();
+ }
+ }
+
+ @Override // from IRay2
+ public boolean getIntersection (IVector start, IVector end, Vector result) {
+ // ray is a + t*b, segment is c + s*d
+ double ax = origin.x, ay = origin.y;
+ double bx = direction.x, by = direction.y;
+ double cx = start.x(), cy = start.y();
+ double dx = end.x() - start.x(), dy = end.y() - start.y();
+
+ double divisor = bx*dy - by*dx;
+ if (Math.abs(divisor) < Math.EPSILON) {
+ // the lines are parallel (or the segment is zero-length)
+ double t = Math.min(getIntersection(start), getIntersection(end));
+ boolean isect = (t != Float.MAX_VALUE);
+ if (isect) {
+ origin.addScaled(direction, t, result);
+ }
+ return isect;
+ }
+ double cxax = cx - ax, cyay = cy - ay;
+ double s = (by*cxax - bx*cyay) / divisor;
+ if (s < 0f || s > 1f) {
+ return false;
+ }
+ double t = (dy*cxax - dx*cyay) / divisor;
+ boolean isect = (t >= 0f);
+ if (isect) {
+ origin.addScaled(direction, t, result);
+ }
+ return isect;
+ }
+
+ @Override // from IRay2
+ public boolean getIntersection (IVector start, IVector end, double radius, Vector result) {
+ double startx = start.x(), starty = start.y();
+ // compute the segment's line parameters
+ double a = starty - end.y(), b = end.x() - startx;
+ double len = Math.hypot(a, b);
+ if (len < Math.EPSILON) { // start equals end; check as circle
+ return getIntersection(start, radius, result);
+ }
+ double rlen = 1f / len;
+ a *= rlen;
+ b *= rlen;
+ double c = -a*startx - b*starty;
+
+ // find out where the origin lies with respect to the top and bottom
+ double dist = a*origin.x + b*origin.y + c;
+ boolean above = (dist > +radius), below = (dist < -radius);
+ double x, y;
+ if (above || below) { // check the intersection with the top/bottom boundary
+ double divisor = a*direction.x + b*direction.y;
+ if (Math.abs(divisor) < Math.EPSILON) { // lines are parallel
+ return false;
+ }
+ c += (above ? -radius : +radius);
+ double t = (-a*origin.x - b*origin.y - c) / divisor;
+ if (t < 0f) { // wrong direction
+ return false;
+ }
+ x = origin.x + t*direction.x;
+ y = origin.y + t*direction.y;
+
+ } else { // middle; check the origin
+ x = origin.x;
+ y = origin.y;
+ }
+ // see where the test point lies with respect to the start and end boundaries
+ double tmp = a;
+ a = b;
+ b = -tmp;
+ c = -a*startx - b*starty;
+ dist = a*x + b*y + c;
+ if (dist < 0f) { // before start
+ return getIntersection(start, radius, result);
+ } else if (dist > len) { // after end
+ return getIntersection(end, radius, result);
+ } else { // middle
+ result.set(x, y);
+ return true;
+ }
+ }
+
+ @Override // from IRay2
+ public boolean getIntersection (IVector center, double radius, Vector result) {
+ // see if we start inside the circle
+ if (origin.distanceSq(center) <= radius*radius) {
+ result.set(origin);
+ return true;
+ }
+ // then if we intersect the circle
+ double ax = origin.x - center.x(), ay = origin.y - center.y();
+ double b = 2f*(direction.x*ax + direction.y*ay);
+ double c = ax*ax + ay*ay - radius*radius;
+ double radicand = b*b - 4f*c;
+ if (radicand < 0f) {
+ return false;
+ }
+ double t = (-b - Math.sqrt(radicand)) * 0.5f;
+ boolean isect = (t >= 0f);
+ if (isect) {
+ origin.addScaled(direction, t, result);
+ }
+ return isect;
+ }
+
+ @Override // from IRay2
+ public Vector getNearestPoint (IVector point, Vector result) {
+ if (result == null) {
+ result = new Vector();
+ }
+ double r = point.subtract(origin).dot(direction);
+ result.set(origin.add(direction.scale(r)));
+ return result;
+ }
+
+ @Override
+ public String toString () {
+ return "[origin=" + origin + ", direction=" + direction + "]";
+ }
+
+ /**
+ * Returns the parameter of the ray when it intersects the supplied point, or
+ * {@link Float#MAX_VALUE} if there is no such intersection.
+ */
+ protected double getIntersection (IVector pt) {
+ if (Math.abs(direction.x) > Math.abs(direction.y)) {
+ double t = (pt.x() - origin.x) / direction.x;
+ return (t >= 0f && origin.y + t*direction.y == pt.y()) ? t : Float.MAX_VALUE;
+ } else {
+ double t = (pt.y() - origin.y) / direction.y;
+ return (t >= 0f && origin.x + t*direction.x == pt.x()) ? t : Float.MAX_VALUE;
+ }
+ }
+}
diff --git a/src/main/java/pythagoras/d/Ray3.java b/src/main/java/pythagoras/d/Ray3.java
new file mode 100644
index 0000000..5218f1b
--- /dev/null
+++ b/src/main/java/pythagoras/d/Ray3.java
@@ -0,0 +1,94 @@
+//
+// Pythagoras - a collection of geometry classes
+// http://github.com/samskivert/pythagoras
+
+package pythagoras.d;
+
+/**
+ * A ray consisting of an origin point and a unit direction vector.
+ */
+public class Ray3 implements IRay3
+{
+ /** The ray's point of origin. */
+ public final Vector3 origin = new Vector3();
+
+ /** The ray's unit direction vector. */
+ public final Vector3 direction = new Vector3();
+
+ /**
+ * Creates a ray with the values contained in the supplied origin point and unit direction
+ * vector.
+ */
+ public Ray3 (Vector3 origin, Vector3 direction) {
+ set(origin, direction);
+ }
+
+ /**
+ * Copy constructor.
+ */
+ public Ray3 (Ray3 other) {
+ set(other);
+ }
+
+ /**
+ * Creates an empty (invalid) ray.
+ */
+ public Ray3 () {
+ }
+
+ /**
+ * Copies the parameters of another ray.
+ *
+ * @return a reference to this ray, for chaining.
+ */
+ public Ray3 set (Ray3 other) {
+ return set(other.origin(), other.direction());
+ }
+
+ /**
+ * Sets the ray parameters to the values contained in the supplied vectors.
+ *
+ * @return a reference to this ray, for chaining.
+ */
+ public Ray3 set (Vector3 origin, Vector3 direction) {
+ this.origin.set(origin);
+ this.direction.set(direction);
+ return this;
+ }
+
+ // /**
+ // * Transforms this ray in-place.
+ // *
+ // * @return a reference to this ray, for chaining.
+ // */
+ // public Ray3 transformLocal (Transform3D transform) {
+ // return transform(transform, this);
+ // }
+
+ @Override // from IRay3
+ public Vector3 origin () {
+ return origin;
+ }
+
+ @Override // from IRay3
+ public Vector3 direction () {
+ return direction;
+ }
+
+ // @Override // from IRay3
+ // public Ray3 transform (Transform3D transform) {
+ // return transform(transform, new Ray3());
+ // }
+
+ // @Override // from IRay3
+ // public Ray3 transform (Transform3D transform, Ray3 result) {
+ // transform.transformPoint(origin, result.origin);
+ // transform.transformVector(direction, result.direction).normalizeLocal();
+ // return result;
+ // }
+
+ @Override
+ public String toString () {
+ return "[origin=" + origin + ", direction=" + direction + "]";
+ }
+}
diff --git a/src/main/java/pythagoras/d/RigidTransform.java b/src/main/java/pythagoras/d/RigidTransform.java
index e05b6da..ea657ad 100644
--- a/src/main/java/pythagoras/d/RigidTransform.java
+++ b/src/main/java/pythagoras/d/RigidTransform.java
@@ -145,7 +145,7 @@ public class RigidTransform extends AbstractTransform
}
@Override // from Transform
- public void transform (IPoint[] src, int srcOff, Point[] dst, int dstOff, int count) {
+ public void transform (IPoint[] src, int srcOff, Point[] dst, int dstOff, int count) {
double sina = Math.sin(rotation), cosa = Math.cos(rotation);
for (int ii = 0; ii < count; ii++) {
IPoint s = src[srcOff++];
@@ -169,6 +169,11 @@ public class RigidTransform extends AbstractTransform
return Points.inverseTransform(p.x(), p.y(), 1, 1, rotation, tx, ty, into);
}
+ @Override // from Transform
+ public Vector transformPoint (IVector v, Vector into) {
+ return Vectors.transform(v.x(), v.y(), 1, 1, rotation, tx, ty, into);
+ }
+
@Override // from Transform
public Vector transform (IVector v, Vector into) {
return v.rotate(rotation, into);
diff --git a/src/main/java/pythagoras/d/Transform.java b/src/main/java/pythagoras/d/Transform.java
index 2e642d6..bb97705 100644
--- a/src/main/java/pythagoras/d/Transform.java
+++ b/src/main/java/pythagoras/d/Transform.java
@@ -176,6 +176,12 @@ public interface Transform
* @throws NoninvertibleTransformException if the transform is not invertible. */
Point inverseTransform (IPoint p, Point into);
+ /** Transforms the supplied vector as a point (accounting for translation), writing the result
+ * into {@code into}.
+ * @param into a vector into which to store the result, may be the same object as {@code v}.
+ * @return {@code into}, for chaining. */
+ Vector transformPoint (IVector v, Vector into);
+
/** Transforms the supplied vector, writing the result into {@code into}.
* @param into a vector into which to store the result, may be the same object as {@code v}.
* @return {@code into}, for chaining. */
diff --git a/src/main/java/pythagoras/d/UniformTransform.java b/src/main/java/pythagoras/d/UniformTransform.java
index 193e691..93dc962 100644
--- a/src/main/java/pythagoras/d/UniformTransform.java
+++ b/src/main/java/pythagoras/d/UniformTransform.java
@@ -197,6 +197,11 @@ public class UniformTransform extends AbstractTransform
return Points.inverseTransform(p.x(), p.y(), scale, scale, rotation, tx, ty, into);
}
+ @Override // from Transform
+ public Vector transformPoint (IVector p, Vector into) {
+ return Vectors.transform(p.x(), p.y(), scale, scale, rotation, tx, ty, into);
+ }
+
@Override // from Transform
public Vector transform (IVector v, Vector into) {
return Vectors.transform(v.x(), v.y(), scale, scale, rotation, into);
diff --git a/src/main/java/pythagoras/d/Vectors.java b/src/main/java/pythagoras/d/Vectors.java
index 0a3d262..514f9a9 100644
--- a/src/main/java/pythagoras/d/Vectors.java
+++ b/src/main/java/pythagoras/d/Vectors.java
@@ -85,12 +85,21 @@ public class Vectors
return Math.abs(v1.x() - v2.x()) <= epsilon && Math.abs(v1.y() - v2.y()) <= epsilon;
}
+ /** Transforms a vector as specified (as a point, accounting for translation), storing the
+ * result in the vector provided.
+ * @return a reference to the result vector, for chaining. */
+ public static Vector transform (double x, double y, double sx, double sy, double rotation,
+ double tx, double ty, Vector result) {
+ return transform(x, y, sx, sy, Math.sin(rotation), Math.cos(rotation), tx, ty,
+ result);
+ }
+
/**
- * Transforms a point as specified, storing the result in the point provided.
+ * Transforms a vector as specified, storing the result in the vector provided.
* @return a reference to the result vector, for chaining.
*/
public static Vector transform (double x, double y, double sx, double sy, double rotation,
- Vector result) {
+ Vector result) {
return transform(x, y, sx, sy, Math.sin(rotation), Math.cos(rotation), result);
}
@@ -103,8 +112,16 @@ public class Vectors
return result.set((x*cosa - y*sina) * sx, (x*sina + y*cosa) * sy);
}
+ /** Transforms a vector as specified (as a point, accounting for translation), storing the
+ * result in the vector provided.
+ * @return a reference to the result vector, for chaining. */
+ public static Vector transform (double x, double y, double sx, double sy, double sina, double cosa,
+ double tx, double ty, Vector result) {
+ return result.set((x*cosa - y*sina) * sx + tx, (x*sina + y*cosa) * sy + ty);
+ }
+
/**
- * Inverse transforms a point as specified, storing the result in the point provided.
+ * Inverse transforms a vector as specified, storing the result in the vector provided.
* @return a reference to the result vector, for chaining.
*/
public static Vector inverseTransform (double x, double y, double sx, double sy, double rotation,