001    package physics3d;
002    
003    
004    /**
005     * PlaneCircle represents a plane circle in 3 space
006     * 
007     * @specfield centerPoint : Vect3 // center of circle
008     * @specfield normal : Vect3 // a unit vector perpendicular to the circle
009     * @specfield radius : double // radius of circle
010     * @specfield texture : String // texture of this object
011     */
012    public strictfp class PlaneCircle implements PhysicsShape {
013      private final Vect3 centerPoint;
014    
015      private final Vect3 normal;
016    
017      private final double radius;
018    
019      private final String texture;
020    
021      // Rep. Invariant:
022      // centerPoint != null &&
023      // normal != null &&
024      // radius >= 0.0
025    
026      // Abstraction Function:
027      // The circle with a center at 'centerPoint' and a radius 'radius'
028      // oriented in the 'normal' direction
029    
030      /**
031       * @requires <code>r</code> >= 0, <code>center</code> != null,
032       *           <code>normal</code> != null, <code>normal.rho()</code> != 0,
033       *           <code>texture</code> != null,
034       * 
035       * 
036       * @effects Creates a new circle with the specified size and location and
037       *          orientation in 3D.
038       * 
039       * @param center
040       *          the center point of the circle
041       * @param normal
042       *          the orientation of the circle
043       * @param r
044       *          the radius of the circle
045       * 
046       */
047      public PlaneCircle(Vect3 center, Vect3 normal, double r, String texture) {
048        if ((r < 0) || (center == null) || (normal == null)) {
049          throw new IllegalArgumentException();
050        }
051        this.centerPoint = center;
052        this.normal = normal.times(1 / normal.rho());
053        this.radius = r;
054        this.texture = texture;
055        // checkRep();
056      }
057    
058      /**
059       * @requires <code>r</code> >= 0, <code>center</code> != null,
060       *           <code>normal</code> != null, <code>normal.rho()</code> > 0
061       * 
062       * @effects Creates a new circle with the specified size and location and
063       *          orientation in 3D.
064       * 
065       * @param center
066       *          the center point of the circle
067       * @param normal
068       *          the orientation of the circle
069       * @param r
070       *          the radius of the circle
071       */
072      public PlaneCircle(Vect3 center, Vect3 normal, double r) {
073        if ((r < 0) || (center == null) || (normal == null)) {
074          throw new IllegalArgumentException();
075        }
076        this.centerPoint = center;
077        this.normal = normal.unitSize();
078        this.radius = r;
079        this.texture = null;
080        // checkRep();
081      }
082    
083      @SuppressWarnings("unused")
084      private void checkRep() {
085        if (centerPoint == null || normal == null || radius < 0.0) {
086          throw new RuntimeException();
087        }
088      }
089    
090      // Observers --------------------------------------
091    
092      /**
093       * @return the center point of this circle.
094       */
095      public Vect3 getCenter() {
096        // checkRep();
097        return centerPoint;
098      }
099    
100      /**
101       * @return the unit vector perpendicular to the given circle with correct
102       *         orientation as specified at initilization.
103       */
104      public Vect3 getNormal() {
105        // checkRep();
106        return normal;
107      }
108    
109      /**
110       * @return the radius of this circle.
111       */
112      public double getRadius() {
113        // checkRep();
114        return radius;
115      }
116    
117      /**
118       * @return texture of this object
119       */
120      public String getTexture() {
121        // checkRep();
122        return this.texture;
123      }
124    
125      /**
126       * @return the plane containing <code>this</code>
127       */
128      public Plane planeContainingObject() {
129        // checkRep();
130        return new Plane(normal, centerPoint);
131      }
132    
133      /**
134       * @requires v != null
135       * @return true if <code>p</code> is within the geometric shape created by
136       *         the intersection of a sphere of <code>radius</code> centered at
137       *         <code>centerPoint</code> and the geometric shape created by
138       *         moving <code>this</code> up and down <code>this.normal</code>
139       *         by <code>GameConstants.TOLERANCE</code>, false otherwise
140       */
141      public boolean containsPoint(Vect3 p) {
142        // checkRep();
143        Vect3 v = p.minus(centerPoint);
144        Vect3 vPerp = v.projectOnToB(this.getNormal());
145        Vect3 vPar = v.minus(vPerp);
146        if (vPar.rho() < radius && vPerp.rho() < GameConstants.TOLERANCE) {
147          return true;
148        }
149        return false;
150      }
151    
152      /**
153       * @requires <code>p</code> is not null
154       * @return the minimum distance between <code>p</code> and <code>this</code>
155       */
156      public double minDistanceToObjectFromP(Vect3 p) {
157        // checkRep();
158        Vect3 v = this.planeContainingObject().perpVectorFromPlaneToPoint(p);
159        Vect3 pOnPlane = p.minus(v);
160        if (this.containsPoint(pOnPlane)) {
161          return v.rho();
162        }
163        Vect3 obliqueVect = p.minus(centerPoint);
164        Vect3 perpVect = obliqueVect.projectOnToB(normal);
165        Vect3 parallelVect = obliqueVect.minus(perpVect);
166        Vect3 point = centerPoint.plus(parallelVect.unitSize().times(radius));
167        return p.minus(point).rho();
168      }
169    
170      /**
171       * @requires center != null, axis != null, axis.rho() > 0, tAngle != null.
172       * @return A PlaneCircle rotated about the line created by center and axis
173       *         counterclockwise by an amount tAngle with same texture
174       */
175      public PlaneCircle rotateAboutCwithAxisAandAngleT(Vect3 center, Vect3 axis,
176          Angle tAngle) {
177        return new PlaneCircle(this.getCenter().minus(center).rotateAroundVect(
178            axis, tAngle).plus(center), this.getNormal().rotateAroundVect(axis,
179            tAngle), this.getRadius(), this.texture);
180      }
181    
182      /**
183       * @requires t != null
184       * @return A PlaneCircle translated by t with same texture
185       */
186      public PlaneCircle translateByT(Vect3 t) {
187        return new PlaneCircle(this.getCenter().plus(t), this.getNormal(), this
188            .getRadius(), this.texture);
189      }
190    
191      // object methods
192      //    
193      // public boolean equals(PlaneCircle c) {
194      // if (c == null) return false;
195      // return (radius == c.radius) && centerPoint.equals(c.centerPoint)
196      // && (Math.abs(normal.dot(c.normal)) == 1);
197      // }
198      //
199      // public boolean equals(Object o) {
200      // if (o instanceof PlaneCircle)
201      // return equals((PlaneCircle) o);
202      // else
203      // return false;
204      // }
205      //
206      public String toString() {
207        return "[Circle center=" + centerPoint + " radius=" + radius
208            + " normal vector=" + normal + "]";
209      }
210      //
211      // public int hashCode() {
212      // return centerPoint.hashCode();
213      // }
214    
215    public ShapeClassification getShapeClassification() {
216            return ShapeClassification.PLANE_CIRCLE;
217    }
218    }