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 }