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