001    package physics3d;
002    
003    /**
004     * LateralCylinder represents the lateral part of a cylinder in 3 space
005     * 
006     * @specfield bottomCenter : Vect3 // center of bottom of cylinder
007     * @specfield topCenter : Vect3 // center of top of cylinder
008     * @specfield radius : double // radius of cylinder
009     * @specfield texture : String // texture of this object
010     */
011    public strictfp class LateralCylinder implements PhysicsShape {
012      private final Vect3 bottomCenter;
013    
014      private final Vect3 topCenter;
015    
016      private final double radius;
017    
018      private final Vect3 direction;
019    
020      private final String texture;
021    
022      // Rep. Invariant:
023      // bottomCenter != null &&
024      // topCenter != null &&
025      // !bottomCenter.equals(topCenter)
026      // radius >= 0.0
027    
028      // Abstraction Function:
029      // The lateral surface of a cylinder with radius 'radius' and
030      // top and bottom centers 'topCenter' and 'bottomCenter' respectively
031    
032      /**
033       * @requires radius >= 0 && bottomCenter != null && topCenter != null &&
034       *           !topCenter.equals(bottomCenter) && <code>texture</code> != null
035       * @effects creates a cylinder with radius 'radius' bottom center
036       *          'bottomCenter' and top center 'topCenter'
037       */
038    
039      public LateralCylinder(double radius, Vect3 bottomCenter, Vect3 topCenter,
040          String texture) {
041        this.radius = radius;
042        this.bottomCenter = bottomCenter;
043        this.topCenter = topCenter;
044        this.texture = texture;
045        direction = topCenter.minus(bottomCenter).unitSize();
046        // checkrep();
047      }
048    
049      /**
050       * @requires radius >= 0 && bottomCenter != null && topCenter != null &&
051       *           !topCenter.equals(bottomCenter)
052       * @effects creates a cylinder with radius 'radius' bottom center
053       *          'bottomCenter' and top center 'topCenter'
054       */
055    
056      public LateralCylinder(double radius, Vect3 bottomCenter, Vect3 topCenter) {
057        this.radius = radius;
058        this.bottomCenter = bottomCenter;
059        this.topCenter = topCenter;
060        this.direction = topCenter.minus(bottomCenter).unitSize();
061        this.texture = null;
062        // checkrep();
063      }
064    
065      @SuppressWarnings("unused")
066      private void checkRep() {
067        if (bottomCenter == null || topCenter == null || radius < 0.0
068            || bottomCenter.equals(topCenter)) {
069          throw new RuntimeException();
070        }
071      }
072    
073      /**
074       * @return the radius of this lateral cylinder
075       */
076      public double getRadius() {
077        // checkRep();
078        return this.radius;
079      }
080    
081      /**
082       * @return the top center of this lateral cylinder
083       */
084      public Vect3 getTopCenter() {
085        // checkRep();
086        return this.topCenter;
087      }
088    
089      /**
090       * @return the bottom center of this lateral cylinder
091       */
092      public Vect3 getBottomCenter() {
093        // checkRep();
094        return this.bottomCenter;
095      }
096    
097      /**
098       * @return the unit vector pointing from the bottom center to the top center
099       */
100      public Vect3 getDirection() {
101        // checkRep();
102        return this.direction;
103      }
104    
105      /**
106       * @return texture of this object
107       */
108      public String getTexture() {
109        // checkRep();
110        return this.texture;
111      }
112    
113      /**
114       * @requires p != null
115       * @return true if
116       *         <code>Vect3.isAbout(this.minDistanceToObjectFromP(p),0)</code>,
117       *         false otherwise
118       */
119      public boolean containsPoint(Vect3 p) {
120        // checkRep();
121        if (Vect3.isAbout(this.minDistanceToObjectFromP(p), 0)) {
122          return true;
123        }
124        return false;
125      }
126    
127      /**
128       * @requires <code>p</code> is not null
129       * @return the minimum distance between <code>p</code> and <code>this</code>
130       */
131      public double minDistanceToObjectFromP(Vect3 p) {
132        // checkRep();
133        Vect3 dir = this.getDirection();
134        Vect3 point = this.getBottomCenter();
135        Line line = new Line(dir, point);
136        // find point on line closest to our point
137        Vect3 pointOnNormal = line.getPointOnLineClosestToP(p);
138    
139        Vect3 v = p.minus(pointOnNormal);
140        double d1 = topCenter.minus(pointOnNormal).rho();
141        double d2 = bottomCenter.minus(pointOnNormal).rho();
142        double d3 = bottomCenter.minus(topCenter).rho();
143        if (Vect3.isAbout(d1 + d2, d3)) {
144          return Math.abs(v.rho() - this.radius);
145        }
146        Vect3 minCenter = topCenter;
147        if (d2 < d1) {
148          minCenter = bottomCenter;
149        }
150    
151        if (minCenter.minus(p).cross(this.direction).isAbout(Vect3.ZERO)) {
152    
153          double a = minCenter.minus(p).rho();
154          double b = radius;
155          return Math.sqrt(Math.pow(a, 2) + Math.pow(b, 2));
156        }
157    
158        Vect3 obliqueVect = p.minus(minCenter);
159        Vect3 parallelVect = obliqueVect.projectOnToB(direction);
160        Vect3 perpVect = obliqueVect.minus(parallelVect);
161        Vect3 p1 = minCenter.plus(perpVect.unitSize().times(radius));
162        return Math.abs(p.minus(p1).rho());
163      }
164    
165      /**
166       * @requires center != null, axis != null, axis.rho() > 0, tAngle != null.
167       * @return A LateralCylinder rotated about the line created by center and axis
168       *         counterclockwise by an amount tAngle with same texture.
169       */
170      public LateralCylinder rotateAboutCwithAxisAandAngleT(Vect3 center,
171          Vect3 axis, Angle tAngle) {
172        return new LateralCylinder(this.getRadius(), this.getBottomCenter().minus(
173            center).rotateAroundVect(axis, tAngle).plus(center), this
174            .getTopCenter().minus(center).rotateAroundVect(axis, tAngle).plus(
175                center), this.texture);
176      }
177    
178      /**
179       * @requires t != null
180       * @return A LateralCylinder translated by t with same texture
181       */
182      public LateralCylinder translateByT(Vect3 t) {
183        return new LateralCylinder(this.getRadius(),
184            this.getBottomCenter().plus(t), this.getTopCenter().plus(t),
185            this.texture);
186      }
187    
188      // Object methods --------------------------------------
189      // public boolean equals(LateralCylinder c) {
190      // if (c == null) return false;
191      // if(c.radius != radius){
192      // return false;
193      // }
194      // if(bottomCenter.equals(c.bottomCenter))
195      // {
196      // if(topCenter.equals(c.topCenter))
197      // {
198      // return true;
199      // }
200      // }
201      // else if(bottomCenter.equals(c.topCenter))
202      // {
203      // if(topCenter.equals(c.bottomCenter))
204      // {
205      // return true;
206      // }
207      // }
208      // return false;
209      // }
210      //
211      // public boolean equals(Object o) {
212      // if (o instanceof LateralCylinder)
213      // return equals((LateralCylinder) o);
214      // else
215      // return false;
216      // }
217      //
218      public String toString() {
219        return "[Lateral Cylinder bottom center=" + bottomCenter + " top center="
220            + topCenter + " radius=" + radius + "]";
221      }
222      //
223      // public int hashCode() {
224      // return bottomCenter.hashCode() + topCenter.hashCode();
225      // }
226    
227            public ShapeClassification getShapeClassification() {
228                    return ShapeClassification.LATERAL_CYLINDER;
229            }
230    }