package cc.sketchchair.sketch; import toxi.geom.Vec2D.Axis; //#IF JAVA //#ENDIF JAVA //#NOPROCESS public class Vec2D { public float x; public float y; Vec2D X_AXIS = new Vec2D(1, 0); Vec2D Y_AXIS = new Vec2D(0, 1); public static final Vec2D fromTheta(float theta) { return new Vec2D((float) Math.cos(theta), (float) Math.sin(theta)); } public Vec2D() { x = 0; y = 0; } /** * Creates a new vector with the given coordinates * * @param x * @param y */ public Vec2D(float x, float y) { this.x = x; this.y = y; } /** * Creates a new vector with the coordinates of the given vector * * @param v * vector to be copied */ public Vec2D(Vec2D v) { set(v); } /** * Adds vector {a,b,c} and returns result as new vector. * * @param a * X coordinate * @param b * Y coordinate * @return result as new vector */ public final Vec2D add(float a, float b) { return new Vec2D(x + a, y + b); } /** * Add vector v and returns result as new vector. * * @param v * vector to add * @return result as new vector */ public final Vec2D add(Vec2D v) { return new Vec2D(x + v.x, y + v.y); } /** * Adds vector {a,b,c} and overrides coordinates with result. * * @param a * X coordinate * @param b * Y coordinate * @return itself */ public final Vec2D addSelf(float a, float b) { x += a; y += b; return this; } /** * Adds vector v and overrides coordinates with result. * * @param v * vector to add * @return itself */ public final Vec2D addSelf(Vec2D v) { x += v.x; y += v.y; return this; } /** * Computes the angle between this vector and vector V. This function * assumes both vectors are normalized, if this can't be guaranteed, use the * alternative implementation {@link #angleBetween(Vec2D, boolean)} * * @param v * vector * @return angle in radians, or NaN if vectors are parallel */ public final float angleBetween(Vec2D v) { return (float) Math.acos(dot(v)); } /** * Computes the angle between this vector and vector V * * @param v * vector * @param forceNormalize * true, if normalized versions of the vectors are to be used * (Note: only copies will be used, original vectors will not be * altered by this method) * @return angle in radians, or NaN if vectors are parallel */ public final float angleBetween(Vec2D v, boolean forceNormalize) { float theta; if (forceNormalize) { theta = getNormalized().dot(v.getNormalized()); } else { theta = dot(v); } return (float) Math.acos(theta); } /** * Sets all vector components to 0. * * @return itself */ public final Vec2D clear() { x = y = 0; return this; } /** * Computes the closest point on the given line segment. * * @param a * start point of line segment * @param b * end point of line segment * @return closest point on the line segment a -> b */ public Vec2D closestPointOnLine(Vec2D a, Vec2D b) { final Vec2D v = b.sub(a); final float t = sub(a).dot(v) / v.magSquared(); // Check to see if t is beyond the extents of the line segment if (t < 0.0f) { return a; } if (t > 1.0f) { return b; } // Return the point between 'a' and 'b' return a.add(v.scaleSelf(t)); } /** * Finds and returns the closest point on any of the edges of the given * triangle. * * @param a * triangle vertex * @param b * triangle vertex * @param c * triangle vertex * @return closest point */ public Vec2D closestPointOnTriangle(Vec2D a, Vec2D b, Vec2D c) { Vec2D Rab = closestPointOnLine(a, b); Vec2D Rbc = closestPointOnLine(b, c); Vec2D Rca = closestPointOnLine(c, a); float dAB = sub(Rab).magnitude(); float dBC = sub(Rbc).magnitude(); float dCA = sub(Rca).magnitude(); float min = dAB; Vec2D result = Rab; if (dBC < min) { min = dBC; result = Rbc; } if (dCA < min) { result = Rca; } return result; } /** * Compares the length of the vector with another one. * * @param v * vector to compare with * @return -1 if other vector is longer, 0 if both are equal or else +1 */ public int compareTo(Vec2D v) { if (x == v.x && y == v.y) { return 0; } return (int) (magSquared() - v.magSquared()); } /** * @return a new independent instance/copy of a given vector */ public final Vec2D copy() { return new Vec2D(this); } /** * Calculates the cross-product with the given vector. * * @param v * vector * @return the magnitude of the vector that would result from a regular 3D * cross product of the input vectors, taking their Z values * implicitly as 0 (i.e. treating the 2D space as a plane in the 3D * space). The 3D cross product will be perpendicular to that plane, * and thus have 0 X & Y components (thus the scalar returned is the * Z value of the 3D cross product vector). * @see <a href="http://stackoverflow.com/questions/243945/">Stackoverflow * entry</a> */ public float cross(Vec2D v) { return (x * v.y) - (y * v.x); } /** * Calculates distance to another vector * * @param v * non-null vector * @return distance or Float.NaN if v=null */ public final float distanceTo(Vec2D v) { if (v != null) { float dx = x - v.x; float dy = y - v.y; return (float) Math.sqrt(dx * dx + dy * dy); } else { return 0; } } /** * Calculates the squared distance to another vector * * @see #magSquared() * @param v * non-null vector * @return distance or NaN if v=null */ public final float distanceToSquared(Vec2D v) { if (v != null) { float dx = x - v.x; float dy = y - v.y; return dx * dx + dy * dy; } else { return 0; } } /** * Computes the scalar product (dot product) with the given vector. * * @see <a href="http://en.wikipedia.org/wiki/Dot_product">Wikipedia entry< * /a> * * @param v * @return dot product */ public final float dot(Vec2D v) { return x * v.x + y * v.y; } public boolean equals(Object obj) { if (obj instanceof Vec2D) { final Vec2D v = (Vec2D) obj; return x == v.x && y == v.y; } return false; } public float getComponent(Axis id) { switch (id) { case X: return x; case Y: return y; } return 0; } /** * Scales vector uniformly by factor -1 ( v = -v ) * * @return result as new vector */ public final Vec2D getInverted() { return new Vec2D(-x, -y); } /** * Creates a copy of the vector with its magnitude limited to the length * given * * @param lim * new maximum magnitude * @return result as new vector */ public final Vec2D getLimited(float lim) { if (magSquared() > lim * lim) { return getNormalized().scaleSelf(lim); } return new Vec2D(this); } /** * Produces the normalized version as a new vector * * @return new vector */ public final Vec2D getNormalized() { return new Vec2D(this).normalize(); } /** * Produces a new vector normalized to the given length. * * @param len * new desired length * * @return new vector */ public Vec2D getNormalizedTo(float len) { return getNormalized().scaleSelf(len); } public final Vec2D getPerpendicular() { return new Vec2D(this).perpendicular(); } public final Vec2D getReciprocal() { return copy().reciprocal(); } /** * Creates a new vector rotated by the given angle around the Z axis. * * @param theta * @return rotated vector */ public final Vec2D getRotated(float theta) { return new Vec2D(this).rotate(theta); } /** * Creates a new vector in which all components are replaced with the signum * of their original values. In other words if a components value was * negative its new value will be -1, if zero => 0, if positive => +1 * * @return result vector */ public Vec2D getSignum() { return new Vec2D(this).signum(); } /** * Computes the vector's direction in the XY plane (for example for 2D * points). The positive X axis equals 0 degrees. * * @return rotation angle */ public final float heading() { return (float) Math.atan2(y, x); } /** * Interpolates the vector towards the given target vector, using linear * interpolation * * @param v * target vector * @param f * interpolation factor (should be in the range 0..1) * @return result as new vector */ public final Vec2D interpolateTo(Vec2D v, float f) { return new Vec2D(x + (v.x - x) * f, y + (v.y - y) * f); } /** * Interpolates the vector towards the given target vector, using linear * interpolation * * @param v * target vector * @param f * interpolation factor (should be in the range 0..1) * @return itself, result overrides current vector */ public final Vec2D interpolateToSelf(Vec2D v, float f) { x += (v.x - x) * f; y += (v.y - y) * f; return this; } /** * Calculates the distance of the vector to the given sphere in the * specified direction. A sphere is defined by a 3D point and a radius. * Normalized directional vectors expected. * * @param rayDir * intersection direction * @param circleOrigin * @param circleRadius * @return distance to sphere in world units, -1 if no intersection. */ public float intersectRayCircle(Vec2D rayDir, Vec2D circleOrigin, float circleRadius) { Vec2D q = circleOrigin.sub(this); float distSquared = q.magSquared(); float v = q.dot(rayDir); float d = circleRadius * circleRadius - (distSquared - v * v); // If there was no intersection, return -1 if (d < 0.0) { return -1; } // Return the distance to the [first] intersecting point return v - (float) Math.sqrt(d); } /** * Scales vector uniformly by factor -1 ( v = -v ), overrides coordinates * with result * * @return itself */ public final Vec2D invert() { x *= -1; y *= -1; return this; } /** * Checks if the point is inside the given sphere. * * @param sO * circle origin/centre * @param sR * circle radius * @return true, if point is in sphere */ public boolean isInCircle(Vec2D sO, float sR) { float d = sub(sO).magSquared(); return (d <= sR * sR); } /** * Checks if vector has a magnitude of 0 * * @return true, if vector = {0,0,0} */ public final boolean isZeroVector() { return x == 0 && y == 0; } /** * Limits the vector's magnitude to the length given * * @param lim * new maximum magnitude * @return itself */ public final Vec2D limit(float lim) { if (magSquared() > lim * lim) { return normalize().scaleSelf(lim); } return this; } /** * Calculates the magnitude/eucledian length of the vector * * @return vector length */ public final float magnitude() { return (float) Math.sqrt(x * x + y * y); } /** * Calculates only the squared magnitude/length of the vector. Useful for * inverse square law applications and/or for speed reasons or if the real * eucledian distance is not required (e.g. sorting). * * Please note the vector should contain cartesian (not polar) coordinates * in order for this function to work. The magnitude of polar vectors is * stored in the x component. * * @return squared magnitude (x^2 + y^2) */ public final float magSquared() { return x * x + y * y; } /** * Normalizes the vector so that its magnitude = 1 * * @return itself */ public final Vec2D normalize() { float mag = x * x + y * y; if (mag > 0) { mag = 1f / (float) Math.sqrt(mag); x *= mag; y *= mag; } return this; } /** * Normalizes the vector to the given length. * * @param len * desired length * @return itself */ public Vec2D normalizeTo(float len) { return normalize().scaleSelf(len); } public final Vec2D perpendicular() { float t = x; x = -y; y = t; return this; } public final Vec2D reciprocal() { x = 1f / x; y = 1f / y; return this; } /** * Rotates the vector by the given angle around the Z axis. * * @param theta * @return itself */ public final Vec2D rotate(float theta) { float co = (float) Math.cos(theta); float si = (float) Math.sin(theta); float xx = co * x - si * y; y = si * x + co * y; x = xx; return this; } /** * Scales vector uniformly and returns result as new vector. * * @param s * scale factor * @return new vector */ public final Vec2D scale(float s) { return new Vec2D(x * s, y * s); } /** * Scales vector non-uniformly and returns result as new vector. * * @param a * scale factor for X coordinate * @param b * scale factor for Y coordinate * @return new vector */ public final Vec2D scale(float a, float b) { return new Vec2D(x * a, y * b); } /** * Scales vector non-uniformly by vector v and returns result as new vector * * @param s * scale vector * @return new vector */ public final Vec2D scale(Vec2D s) { return new Vec2D(x * s.x, y * s.y); } /** * Scales vector uniformly and overrides coordinates with result * * @param s * scale factor * @return itself */ public Vec2D scaleSelf(float s) { x *= s; y *= s; return this; } /** * Scales vector non-uniformly by vector {a,b,c} and overrides coordinates * with result * * @param a * scale factor for X coordinate * @param b * scale factor for Y coordinate * @return itself */ public final Vec2D scaleSelf(float a, float b) { x *= a; y *= b; return this; } /** * Scales vector non-uniformly by vector v and overrides coordinates with * result * * @param s * scale vector * @return itself */ public final Vec2D scaleSelf(Vec2D s) { x *= s.x; y *= s.y; return this; } /** * Overrides coordinates with the given values * * @param x * @param y * @return itself */ public final Vec2D set(float x, float y) { this.x = x; this.y = y; return this; } /** * Overrides coordinates with the ones of the given vector * * @param v * vector to be copied * @return itself */ public final Vec2D set(Vec2D v) { x = v.x; y = v.y; return this; } public Vec2D setComponent(Axis id, float val) { switch (id) { case X: x = val; break; case Y: y = val; break; } return this; } /** * Replaces all vector components with the signum of their original values. * In other words if a components value was negative its new value will be * -1, if zero => 0, if positive => +1 * * @return itself */ public Vec2D signum() { x = (x < 0 ? -1 : x == 0 ? 0 : 1); y = (y < 0 ? -1 : y == 0 ? 0 : 1); return this; } /** * Subtracts vector {a,b,c} and returns result as new vector. * * @param a * X coordinate * @param b * Y coordinate * @return result as new vector */ public final Vec2D sub(float a, float b) { return new Vec2D(x - a, y - b); } public final Vec2D sub(Vec2D v) { return new Vec2D(x - v.x, y - v.y); } public final Vec2D subSelf(float a, float b) { x -= a; y -= b; return this; } public final Vec2D subSelf(Vec2D v) { x -= v.x; y -= v.y; return this; } public Vec2D tangentNormalOfEllipse(Vec2D eO, Vec2D eR) { Vec2D p = this.sub(eO); float xr2 = eR.x * eR.x; float yr2 = eR.y * eR.y; return new Vec2D(p.x / xr2, p.y / yr2).normalize(); } public float[] toArray() { return new float[] { x, y }; } public Vec2D toCartesian() { float xx = (float) (x * Math.cos(y)); y = (float) (x * Math.sin(y)); x = xx; return this; } public Vec2D toPolar() { float r = (float) Math.sqrt(x * x + y * y); y = (float) Math.atan2(y, x); x = r; return this; } public String toString() { StringBuffer sb = new StringBuffer(32); sb.append("{x:").append(x).append(", y:").append(y).append("}"); return sb.toString(); } }