package physics; import java.util.Objects; import javafx.geometry.Point2D; /** * An idealized mechanical spring. * * @author Christoph Burschka <christoph@burschka.de> */ public class Spring { private final double length; private final double strength; private final Point2D a, b; /** * Create a new spring. * * The spring is anchored at a specific point relative to the origin. * * @param length The equilibrium length. * @param strength The stiffness constant k. * @param a anchor point on a * @param b anchor point on b */ public Spring(double length, double strength, Point2D a, Point2D b) { assert length > 0; assert strength > 0; this.length = length; this.strength = strength; this.a = a; this.b = b; } public Spring(double length, double strength) { this(length, strength, new Point2D(0, 0), new Point2D(0, 0)); } /** * Calculate the spring's currently exerted force. * * @param distance The current (positive) length l of the spring. * @return The force (positive force is directed inward to the other point). */ private double getForce(double distance) { return (distance - length) * strength; } /** * Calculate the spring's currently exerted force. * * If the spring is infinitely compressed, the direction of the force is * undefined. The return value will be a zero vector. * * @param a the first point * @param b the second point * @return the force acting on the first point (flip sign for second) */ public Point2D getForce(Point2D a, Point2D b) { Point2D relative = b.add(this.b).subtract(a.add(this.a)); double distance = relative.magnitude(); if (distance > 0) { return relative.multiply(getForce(distance) / distance); } else { // Apply force in a random direction: double force = getForce(0); double angle = Math.random() * 2 * Math.PI; return new Point2D(Math.sin(angle) * force, Math.cos(angle)*force); } } public Spring reverse() { return new Spring(length, strength, b, a); } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final Spring other = (Spring) obj; if (Double.doubleToLongBits(this.length) != Double.doubleToLongBits(other.length)) { return false; } if (Double.doubleToLongBits(this.strength) != Double.doubleToLongBits(other.strength)) { return false; } if (!Objects.equals(this.a, other.a)) { return false; } return Objects.equals(this.b, other.b); } @Override public int hashCode() { int hash = 3; hash = 53 * hash + (int) (Double.doubleToLongBits(this.length) ^ (Double.doubleToLongBits(this.length) >>> 32)); hash = 53 * hash + (int) (Double.doubleToLongBits(this.strength) ^ (Double.doubleToLongBits(this.strength) >>> 32)); hash = 53 * hash + Objects.hashCode(this.a); hash = 53 * hash + Objects.hashCode(this.b); return hash; } }