1 /*
2  * $RCSfile: Cone.java,v $
3  *
4  * Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * - Redistribution of source code must retain the above copyright
11  *   notice, this list of conditions and the following disclaimer.
12  *
13  * - Redistribution in binary form must reproduce the above copyright
14  *   notice, this list of conditions and the following disclaimer in
15  *   the documentation and/or other materials provided with the
16  *   distribution.
17  *
18  * Neither the name of Sun Microsystems, Inc. or the names of
19  * contributors may be used to endorse or promote products derived
20  * from this software without specific prior written permission.
21  *
22  * This software is provided "AS IS," without a warranty of any
23  * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
24  * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
25  * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
26  * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
27  * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
28  * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
29  * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
30  * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
31  * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
32  * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
33  * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
34  * POSSIBILITY OF SUCH DAMAGES.
35  *
36  * You acknowledge that this software is not designed, licensed or
37  * intended for use in the design, construction, operation or
38  * maintenance of any nuclear facility.
39  *
40  * $Revision: 1.6 $
41  * $Date: 2007/04/24 18:50:59 $
42  * $State: Exp $
43  */
44 
45 package com.sun.j3d.utils.geometry;
46 
47 import com.sun.j3d.utils.geometry.*;
48 import java.io.*;
49 import java.util.*;
50 import javax.media.j3d.*;
51 import javax.vecmath.*;
52 
53 /**
54  * Cone is a geometry primitive defined with a radius and a height.
55  * It is a capped cone centered at the origin with its central axis
56  * aligned along the Y-axis. The center of the cone is defined to be
57  * the center of its bounding box (rather than its centroid).
58  * <p>
59  * If the GENERATE_TEXTURE_COORDS flag is set, the texture coordinates
60  * are generated such that the texture gets mapped onto the cone similarly
61  * to how it gets mapped onto a cylinder, the difference being the top
62  * cap is nonexistent.
63  * <p>
64  * By default all primitives with the same parameters share their
65  * geometry (e.g., you can have 50 shperes in your scene, but the
66  * geometry is stored only once). A change to one primitive will
67  * effect all shared nodes.  Another implication of this
68  * implementation is that the capabilities of the geometry are shared,
69  * and once one of the shared nodes is live, the capabilities cannot
70  * be set.  Use the GEOMETRY_NOT_SHARED flag if you do not wish to
71  * share geometry among primitives with the same parameters.
72  */
73 public class Cone extends Primitive {
74   float radius, height;
75   int xdivisions, ydivisions;
76   static final int MID_REZ_DIV_X = 15;
77   static final int MID_REZ_DIV_Y = 1;
78 
79   /**
80    * Designates the body of the cone.  Used by <code>getShape</code>.
81    *
82    * @see Cone#getShape
83    */
84   public static final int BODY = 0;
85 
86   /**
87    * Designates the end-cap of the cone.  Used by <code>getShape</code>.
88    *
89    * @see Cone#getShape
90    */
91   public static final int CAP = 1;
92 
93   /**
94    *   Constructs a default Cone of radius of 1.0 and height
95    *   of 2.0. Resolution defaults to 15 divisions along X and axis
96    *   and 1 along the Y axis.  Normals are generated, texture
97    *   coordinates are not.
98    */
Cone()99   public Cone(){
100     this(1.0f, 2.0f, GENERATE_NORMALS, MID_REZ_DIV_X, MID_REZ_DIV_Y, null);
101   }
102 
103   /**
104    *
105    *   Constructs a default Cone of a given radius and height.  Normals
106    *   are generated, texture coordinates are not.
107    *   @param radius Radius
108    *   @param height Height
109    */
Cone(float radius, float height)110   public Cone (float radius, float height)
111   {
112     this(radius, height, GENERATE_NORMALS, MID_REZ_DIV_X, MID_REZ_DIV_Y, null);
113   }
114 
115   /**
116    *
117    *   Constructs a default cone of a given radius, height,
118    *   and appearance. Normals are generated, texture coordinates are not.
119    *   @param radius Radius
120    *   @param height Height
121    *   @param ap Appearance
122    *
123    * @since Java 3D 1.2.1
124    */
Cone(float radius, float height, Appearance ap)125   public Cone (float radius, float height, Appearance ap)
126   {
127     this(radius, height, GENERATE_NORMALS, MID_REZ_DIV_X, MID_REZ_DIV_Y, ap);
128   }
129 
130   /**
131    *
132    *   Constructs a default cone of a given radius, height,
133    *   primitive flags, and appearance.
134    *   @param radius Radius
135    *   @param height Height
136    *   @param primflags Primitive flags
137    *   @param ap Appearance
138    */
Cone(float radius, float height, int primflags, Appearance ap)139   public Cone (float radius, float height, int primflags, Appearance ap)
140   {
141     this(radius, height, primflags, MID_REZ_DIV_X, MID_REZ_DIV_Y, ap);
142   }
143 
144   /**
145    * Obtains the Shape3D node associated with one of the parts of the
146    * cone (the body or the cap). This allows users to modify the appearance
147    * or geometry of individual parts.
148    * @param partId The part to return (BODY or CAP).
149    * @return The Shape3D object associated with the partId.  If an
150    * invalid partId is passed in, null is returned.
151    */
getShape(int partId)152   public Shape3D getShape(int partId){
153       if (partId > CAP || partId < BODY) return null;
154       return (Shape3D)getChild(partId);
155   }
156 
157 
158   /**
159    * Sets appearance of the cone. This will set each part of the
160    *  cone (cap & body) to the same appearance. To set each
161    *  part's appearance separately, use getShape(partId) to get the
162    *  individual shape and call shape.setAppearance(ap).
163    */
setAppearance(Appearance ap)164   public void setAppearance(Appearance ap){
165       ((Shape3D)getChild(BODY)).setAppearance(ap);
166       ((Shape3D)getChild(CAP)).setAppearance(ap);
167   }
168 
169     /**
170      * Gets the appearance of the specified part of the cone.
171      *
172      * @param partId identifier for a given subpart of the cone
173      *
174      * @return The appearance object associated with the partID.  If an
175      * invalid partId is passed in, null is returned.
176      *
177      * @since Java 3D 1.2.1
178      */
getAppearance(int partId)179     public Appearance getAppearance(int partId) {
180 	if (partId > CAP || partId < BODY) return null;
181 	return getShape(partId).getAppearance();
182     }
183 
184 
185   /**
186    *   Constructs a customized Cone of a given radius, height, flags,
187    *   resolution (X and Y dimensions), and appearance. The
188    *   resolution is defined in terms of number of subdivisions
189    *   along the object's X axis (width) and Y axis (height). More divisions
190    *   lead to finer tesselated objects.
191    *   <p>
192    *   If appearance is null, the default white appearance will be used.
193    *   @param radius Radius
194    *   @param height Height
195    *   @param xdivision Number of divisions along X direction.
196    *   @param ydivision Number of divisions along the height of cone.
197    *   @param primflags flags
198    *   @param ap Appearance
199    */
200 
Cone(float radius, float height, int primflags, int xdivision, int ydivision, Appearance ap)201   public Cone(float radius, float height, int primflags,
202 	      int xdivision, int ydivision,
203 	      Appearance ap)
204   {
205     super();
206 
207     Shape3D shape[] = new Shape3D[2];
208     this.radius = radius;
209     this.height = height;
210     xdivisions = xdivision;
211     ydivisions = ydivision;
212     flags = primflags;
213     boolean outside = (flags & GENERATE_NORMALS_INWARD) == 0;
214     boolean texCoordYUp = (flags & GENERATE_TEXTURE_COORDS_Y_UP) != 0;
215     Quadrics q = new Quadrics();
216     GeomBuffer gbuf = null;
217 
218     GeomBuffer cache = getCachedGeometry(Primitive.CONE,
219 					 radius, 0.0f, height,
220 					 xdivision, ydivision, primflags);
221     if (cache != null){
222 // 	System.out.println("using cached geometry");
223 	shape[BODY] = new Shape3D(cache.getComputedGeometry());
224 	numVerts += cache.getNumVerts();
225 	numTris += cache.getNumTris();
226     }
227     else {
228       // the body of the cone consists of the top of the cone and if
229       // ydivisions is greater than 1, the body of the cone.
230 	gbuf = q.coneTop((double)(height/2.0 - height/ydivisions),
231 			 (double)(radius/ydivisions), height/ydivisions,
232 			 xdivisions, 1.0-1.0/(double)ydivisions,
233 			 outside, texCoordYUp);
234       shape[BODY] = new Shape3D(gbuf.getGeom(flags));
235       numVerts += gbuf.getNumVerts();
236       numTris += gbuf.getNumTris();
237       if ((primflags & Primitive.GEOMETRY_NOT_SHARED) == 0) {
238 	  cacheGeometry(Primitive.CONE,
239 			radius, 0.0f, height,
240 			xdivision, ydivision, primflags, gbuf);
241       }
242     }
243 
244     // only need to add a body if the ydivisions is greater than 1
245     if (ydivisions > 1) {
246 	cache = getCachedGeometry(Primitive.CONE_DIVISIONS, radius, 0.0f,
247 				  height, xdivision, ydivision, primflags);
248 	if (cache != null) {
249 // 	    System.out.println("using cached divisions");
250 	    shape[BODY].addGeometry(cache.getComputedGeometry());
251 	    numVerts += cache.getNumVerts();
252 	    numTris += cache.getNumTris();
253 	}
254 	else {
255 	    gbuf = q.coneBody(-(double)(height/2.0),
256 			      (double)(height/2.0-height/ydivisions),
257 			      (double)radius, (double)(radius/ydivisions),
258 			      xdivisions, ydivisions-1, 1.0/(double)ydivisions,
259 			      outside, texCoordYUp);
260 	    shape[BODY].addGeometry(gbuf.getGeom(flags));
261 	    numVerts += gbuf.getNumVerts();
262 	    numTris += gbuf.getNumTris();
263 	    if ((primflags & Primitive.GEOMETRY_NOT_SHARED) == 0) {
264 		 cacheGeometry(Primitive.CONE_DIVISIONS, radius, 0.0f, height,
265 			       xdivision, ydivision, primflags, gbuf);
266 	    }
267 	}
268     }
269 
270     if ((flags & ENABLE_APPEARANCE_MODIFY) != 0) {
271 	(shape[BODY]).setCapability(Shape3D.ALLOW_APPEARANCE_READ);
272 	(shape[BODY]).setCapability(Shape3D.ALLOW_APPEARANCE_WRITE);
273     }
274 
275     if ((flags & ENABLE_GEOMETRY_PICKING) != 0) {
276         (shape[BODY]).setCapability(Shape3D.ALLOW_GEOMETRY_READ);
277     }
278 
279     this.addChild(shape[BODY]);
280 
281     // Create bottom cap.
282     cache = getCachedGeometry(Primitive.BOTTOM_DISK, radius,
283 			      radius, -height/2.0f, xdivision, xdivision,
284 			      primflags);
285     if (cache != null) {
286 // 	System.out.println("using cached bottom");
287 	shape[CAP] = new Shape3D(cache.getComputedGeometry());
288 	numVerts += cache.getNumVerts();
289 	numTris += cache.getNumTris();
290     }
291     else {
292 	gbuf = q.disk((double)radius, xdivision, -(double)height/2.0,
293 		      !outside, texCoordYUp);
294 	shape[CAP] = new Shape3D(gbuf.getGeom(flags));
295 	numVerts += gbuf.getNumVerts();
296 	numTris += gbuf.getNumTris();
297 	if ((primflags & Primitive.GEOMETRY_NOT_SHARED) == 0) {
298 	    cacheGeometry(Primitive.BOTTOM_DISK, radius, radius, -height/2.0f,
299 			  xdivision, xdivision, primflags, gbuf);
300 	}
301     }
302 
303     if ((flags & ENABLE_APPEARANCE_MODIFY) != 0) {
304 	(shape[CAP]).setCapability(Shape3D.ALLOW_APPEARANCE_READ);
305 	(shape[CAP]).setCapability(Shape3D.ALLOW_APPEARANCE_WRITE);
306     }
307 
308     if ((flags & ENABLE_GEOMETRY_PICKING) != 0) {
309         (shape[CAP]).setCapability(Shape3D.ALLOW_GEOMETRY_READ);
310     }
311 
312 //     Transform3D t2 = new Transform3D();
313 
314     // Flip it to match up the texture coords.
315 /* This causes the bottom not to match up for odd xdivision values
316     objectMat = new Matrix4d();
317     objectMat.setIdentity();
318     rotMat = new Matrix4d();
319     rotMat.setIdentity();
320     rotMat.rotZ(Math.PI);
321     objectMat.mul(objectMat, rotMat);
322     t2.set(objectMat);
323 */
324 
325     this.addChild(shape[CAP]);
326 
327     if (ap == null){
328       setAppearance();
329     }
330     else setAppearance(ap);
331   }
332 
333     /**
334      * Used to create a new instance of the node.  This routine is called
335      * by <code>cloneTree</code> to duplicate the current node.
336      * <code>cloneNode</code> should be overridden by any user subclassed
337      * objects.  All subclasses must have their <code>cloneNode</code>
338      * method consist of the following lines:
339      * <P><blockquote><pre>
340      *     public Node cloneNode(boolean forceDuplicate) {
341      *         UserSubClass usc = new UserSubClass();
342      *         usc.duplicateNode(this, forceDuplicate);
343      *         return usc;
344      *     }
345      * </pre></blockquote>
346      * @param forceDuplicate when set to <code>true</code>, causes the
347      *  <code>duplicateOnCloneTree</code> flag to be ignored.  When
348      *  <code>false</code>, the value of each node's
349      *  <code>duplicateOnCloneTree</code> variable determines whether
350      *  NodeComponent data is duplicated or copied.
351      *
352      * @see Node#cloneTree
353      * @see Node#duplicateNode
354      * @see NodeComponent#setDuplicateOnCloneTree
355      */
cloneNode(boolean forceDuplicate)356     public Node cloneNode(boolean forceDuplicate) {
357         Cone c = new Cone(radius, height, flags, xdivisions,
358                           ydivisions, getAppearance());
359         c.duplicateNode(this, forceDuplicate);
360         return c;
361     }
362 
363     /**
364      * Copies all node information from <code>originalNode</code> into
365      * the current node.  This method is called from the
366      * <code>cloneNode</code> method which is, in turn, called by the
367      * <code>cloneTree</code> method.
368      * <P>
369      * For any <i>NodeComponent</i> objects
370      * contained by the object being duplicated, each <i>NodeComponent</i>
371      * object's <code>duplicateOnCloneTree</code> value is used to determine
372      * whether the <i>NodeComponent</i> should be duplicated in the new node
373      * or if just a reference to the current node should be placed in the
374      * new node.  This flag can be overridden by setting the
375      * <code>forceDuplicate</code> parameter in the <code>cloneTree</code>
376      * method to <code>true</code>.
377      *
378      * @param originalNode the original node to duplicate.
379      * @param forceDuplicate when set to <code>true</code>, causes the
380      *  <code>duplicateOnCloneTree</code> flag to be ignored.  When
381      *  <code>false</code>, the value of each node's
382      *  <code>duplicateOnCloneTree</code> variable determines whether
383      *  NodeComponent data is duplicated or copied.
384      *
385      * @see Node#cloneTree
386      * @see Node#cloneNode
387      * @see NodeComponent#setDuplicateOnCloneTree
388      */
duplicateNode(Node originalNode, boolean forceDuplicate)389     public void duplicateNode(Node originalNode, boolean forceDuplicate) {
390         super.duplicateNode(originalNode, forceDuplicate);
391     }
392 
393     /**
394      * Returns the radius of the cone
395      *
396      * @since Java 3D 1.2.1
397      */
getRadius()398     public float getRadius() {
399 	return radius;
400     }
401 
402     /**
403      * Returns the height of the cone
404      *
405      * @since Java 3D 1.2.1
406      */
getHeight()407     public float getHeight() {
408 	return height;
409     }
410 
411     /**
412      * Returns the number divisions along the X direction
413      *
414      * @since Java 3D 1.2.1
415      */
getXdivisions()416     public int getXdivisions() {
417 	return xdivisions;
418     }
419 
420     /**
421      * Returns the number of divisions along the height of the cone
422      *
423      * @since Java 3D 1.2.1
424      */
getYdivisions()425     public int getYdivisions() {
426 	return ydivisions;
427     }
428 
429 }
430