We have our vectors ready. Now, points are entirely analogous, insofar they are value objects:
final class Pt { public final double x; public final double y; public Pt(double x, double y) { this.x = x; this.y = y; } public static final Pt ORIGIN = new Pt(0, 0); public boolean equals(Object o) { if (o == null || !(o instanceof Pt)) return false; else if (this == o) return true; final Pt that = (Pt) o; return this.x == that.x && this.y == that.y; } public int hashCode() { return (int) ((Double.doubleToLongBits(x) >> 32) & 0xffffffffL) ^ (int) ((Double.doubleToLongBits(y) >> 32) & 0xffffffffL); } public String toString() { return String.format("(%g , %g)", x, y); }
There is a distinguished point, the origin.
It is important to understand the need to duplicate code that is essentially the same as in Vec
; in other words, it is not appropriate to refactor common code in classes Vec
and Pt
, for two reasons:
- First, a vector is not a point, nor vice-versa. There is not even a meaningful common supertype between them. The whole point of using the typed algebra is to manipulate formulas with the added security afforded by the typing rules
- Second, even if a private base class could abstract some code, the bulk of the code needed to correctly implement a base type is in
equals
. This method needs to cast to the static class of the object in order for the comparison to be meaningful (else it would be comparing a derived class to the base class), so the duplicateequals
are essential anyway
The Algebra has two "constructors" linking points and vectors: the point constructor and the vector constructor. Both operate on points, so naturally they are members of the Pt
class. Also, as Axiom 1 states, both operations are some kind of additive inverses one of the other:
public Pt at(Vec v) { return new Pt(x + v.i, y + v.j); } public Vec to(Pt p) { return new Vec(p.x - x, p.y - y); }
A natural operation on points is to find the distance between them. It is, quite simply, p.to(q).norm()
. However for convenience I include it directly in Pt
:
public double dist(Pt p) { return to(p).norm(); }
Linear interpolation, or lerp mixes two points to give a third:
public Pt lerp(Pt p, double k) { return at(to(p).scale(k)); }
In particular, the midpoint between two given points is:
public Pt mid(Pt p) { return lerp(p, 0.5); }
The set of all linear interpolants is obviously a line. The simplest way to determine one is however to give a point through which it passes and the direction it takes:
public Line towards(Vec v) { return new Line(this, v); } public Line through(Pt p) { return new Line(this, to(p)); } }
I will show next how to use lines.
No comments:
Post a Comment