001    package physics3d;
002    
003    public strictfp class Line {
004      private final Vect3 direction;
005    
006      private final Vect3 pointOnLine;
007    
008      // Rep. Invariant:
009      // direction != null &&
010      // pointOnLine != null &&
011      // direction.rho() == 1.0
012    
013      // Abstraction Function:
014      // The Line with a direction 'direction' and a point on the plane
015      // 'pointOnLine'
016    
017      /**
018       * @requires <code>direction</code> is not null, <code>pointOnLine</code>
019       *           is not null,
020       * @effects constructs a Line
021       */
022      public Line(Vect3 direction, Vect3 pointOnLine) {
023        this.direction = direction.unitSize();
024        this.pointOnLine = pointOnLine;
025        // checkRep();
026      }
027    
028      /**
029       * @requires p1, p2 != null !p1.equals(p2)
030       * @return the line that contains these two points
031       */
032      public static Line MakeLineFromTwoPoints(Vect3 p1, Vect3 p2) {
033        return new Line(p1.minus(p2).unitSize(), p1);
034      }
035    
036      @SuppressWarnings("unused")
037      private void checkRep() {
038        if (direction == null || pointOnLine == null || direction.rho() != 1.0) {
039          throw new RuntimeException();
040        }
041      }
042    
043      /**
044       * @return the direction of <code>this</code>
045       */
046      public Vect3 getDirection() {
047        // checkRep();
048        return direction;
049      }
050    
051      /**
052       * @return a point on <code>this</code>
053       */
054      public Vect3 getPointOnLine() {
055        // checkRep();
056        return pointOnLine;
057      }
058    
059      /**
060       * @requires v != null
061       * @return true if v is within <code>GameConstants.TOLERANCE</code> of
062       *         <code>this</code>
063       */
064      public boolean containsPoint(Vect3 v) {
065        if (getPointOnLineClosestToP(v).isAbout(v)) {
066          return true;
067        }
068        return false;
069      }
070    
071      /**
072       * @requires v != null
073       * @return the point on <code>this</code> such that
074       *         v.minus(p).cross(direction) = <code>Vect3.ZERO</code>
075       */
076      public Vect3 getPointOnLineClosestToP(Vect3 p) {
077        // checkRep();
078        Vect3 obliqueVect = p.minus(this.pointOnLine);
079        if (this.direction.rho() == 0) {
080          return this.pointOnLine;
081        }
082        Vect3 parallelVect = obliqueVect.projectOnToB(this.direction);
083        Vect3 perpVect = obliqueVect.minus(parallelVect);
084        return p.minus(perpVect);
085      }
086    }