001: /*
002: * $RCSfile: Cylinder.java,v $
003: *
004: * Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions
008: * are met:
009: *
010: * - Redistribution of source code must retain the above copyright
011: * notice, this list of conditions and the following disclaimer.
012: *
013: * - Redistribution in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in
015: * the documentation and/or other materials provided with the
016: * distribution.
017: *
018: * Neither the name of Sun Microsystems, Inc. or the names of
019: * contributors may be used to endorse or promote products derived
020: * from this software without specific prior written permission.
021: *
022: * This software is provided "AS IS," without a warranty of any
023: * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
024: * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
025: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
026: * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
027: * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
028: * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
029: * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
030: * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
031: * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
032: * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
033: * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
034: * POSSIBILITY OF SUCH DAMAGES.
035: *
036: * You acknowledge that this software is not designed, licensed or
037: * intended for use in the design, construction, operation or
038: * maintenance of any nuclear facility.
039: *
040: * $Revision: 1.6 $
041: * $Date: 2007/04/24 18:50:59 $
042: * $State: Exp $
043: */
044:
045: package com.sun.j3d.utils.geometry;
046:
047: import com.sun.j3d.utils.geometry.*;
048: import java.io.*;
049: import java.util.*;
050: import javax.media.j3d.*;
051: import javax.vecmath.*;
052:
053: /**
054: * Cylinder is a geometry primitive defined with a radius and a height.
055: * It is a capped cylinder centered at the origin with its central axis
056: * aligned along the Y-axis.
057: * <p>
058: * When a texture is applied to a cylinder, the texture is applied to the
059: * caps and the body different. A texture is mapped CCW from the back of the
060: * body. The top and bottom caps are mapped such that the texture appears
061: * front facing when the caps are rotated 90 degrees toward the viewer.
062: * <p>
063: * By default all primitives with the same parameters share their
064: * geometry (e.g., you can have 50 shperes in your scene, but the
065: * geometry is stored only once). A change to one primitive will
066: * effect all shared nodes. Another implication of this
067: * implementation is that the capabilities of the geometry are shared,
068: * and once one of the shared nodes is live, the capabilities cannot
069: * be set. Use the GEOMETRY_NOT_SHARED flag if you do not wish to
070: * share geometry among primitives with the same parameters.
071: */
072:
073: public class Cylinder extends Primitive {
074: float radius, height;
075: int xdivisions, ydivisions;
076:
077: static final int MID_REZ_DIV_X = 15;
078: static final int MID_REZ_DIV_Y = 1;
079:
080: /**
081: * Designates the body of the cylinder. Used by <code>getShape</code>.
082: *
083: * @see Cylinder#getShape
084: */
085: public static final int BODY = 0;
086:
087: /**
088: * Designates the top end-cap of the cylinder.
089: * Used by <code>getShape</code>.
090: *
091: * @see Cylinder#getShape
092: */
093: public static final int TOP = 1;
094:
095: /**
096: * Designates the bottom end-cap of the cylinder.
097: * Used by <code>getShape</code>.
098: *
099: * @see Cylinder#getShape
100: */
101: public static final int BOTTOM = 2;
102:
103: /**
104: * Constructs a default cylinder of radius of 1.0 and height
105: * of 2.0. Normals are generated by default, texture
106: * coordinates are not. Resolution defaults to 15 divisions
107: * along X axis and 1 along the Y axis.
108: */
109: public Cylinder() {
110: this (1.0f, 2.0f, GENERATE_NORMALS, MID_REZ_DIV_X,
111: MID_REZ_DIV_Y, null);
112: }
113:
114: /**
115: * Constructs a default cylinder of a given radius and height.
116: * Normals are generated by default, texture coordinates are not.
117: * @param radius Radius
118: * @param height Height
119: */
120: public Cylinder(float radius, float height) {
121: this (radius, height, GENERATE_NORMALS, MID_REZ_DIV_X,
122: MID_REZ_DIV_Y, null);
123: }
124:
125: /**
126: * Constructs a default cylinder of a given radius, height, and
127: * appearance. Normals are generated by default, texture
128: * coordinates are not.
129: * @param radius Radius
130: * @param height Height
131: * @param ap Appearance
132: */
133: public Cylinder(float radius, float height, Appearance ap) {
134: this (radius, height, GENERATE_NORMALS, MID_REZ_DIV_X,
135: MID_REZ_DIV_Y, ap);
136: }
137:
138: /**
139: *
140: * Constructs a default cylinder of a given radius, height,
141: * primitive flags and appearance.
142: * @param radius Radius
143: * @param height Height
144: * @param primflags Flags
145: * @param ap Appearance
146: */
147: public Cylinder(float radius, float height, int primflags,
148: Appearance ap) {
149: this (radius, height, primflags, MID_REZ_DIV_X, MID_REZ_DIV_Y,
150: ap);
151: }
152:
153: /**
154: * Obtains the Shape3D node associated with a given part of the cylinder.
155: * This allows users to modify the appearance or geometry
156: * of individual parts.
157: * @param partId The part to return (BODY, TOP, or BOTTOM).
158: * @return The Shape3D object associated with the partID. If an
159: * invalid partId is passed in, null is returned.
160: */
161: public Shape3D getShape(int partId) {
162: if (partId > BOTTOM || partId < BODY)
163: return null;
164: return (Shape3D) getChild(partId);
165: }
166:
167: /** Sets appearance of the cylinder. This will set each part of the
168: * cylinder (TOP,BOTTOM,BODY) to the same appearance. To set each
169: * part's appearance separately, use getShape(partId) to get the
170: * individual shape and call shape.setAppearance(ap).
171: */
172: public void setAppearance(Appearance ap) {
173: ((Shape3D) getChild(BODY)).setAppearance(ap);
174: ((Shape3D) getChild(TOP)).setAppearance(ap);
175: ((Shape3D) getChild(BOTTOM)).setAppearance(ap);
176: }
177:
178: /**
179: * Gets the appearance of the specified part of the cylinder.
180: *
181: * @param partId identifier for a given subpart of the cylinder
182: *
183: * @return The appearance object associated with the partID. If an
184: * invalid partId is passed in, null is returned.
185: *
186: * @since Java 3D 1.2.1
187: */
188: public Appearance getAppearance(int partId) {
189: if (partId > BOTTOM || partId < BODY)
190: return null;
191: return getShape(partId).getAppearance();
192: }
193:
194: /**
195: * Constructs a customized cylinder of a given radius, height,
196: * resolution (X and Y dimensions), and appearance. The
197: * resolution is defined in terms of number of subdivisions
198: * along the object's X axis (width) and Y axis (height). More divisions
199: * lead to more finely tesselated objects.
200: * @param radius Radius
201: * @param height Height
202: * @param xdivision Number of divisions along X direction.
203: * @param ydivision Number of divisions along height of cylinder.
204: * @param primflags Primitive flags.
205: * @param ap Appearance
206: */
207: public Cylinder(float radius, float height, int primflags,
208: int xdivision, int ydivision, Appearance ap) {
209: super ();
210:
211: this .radius = radius;
212: this .height = height;
213: this .xdivisions = xdivision;
214: this .ydivisions = ydivision;
215: flags = primflags;
216: boolean outside = (flags & GENERATE_NORMALS_INWARD) == 0;
217: boolean texCoordYUp = (flags & GENERATE_TEXTURE_COORDS_Y_UP) != 0;
218: // Create many body of the cylinder.
219: Quadrics q = new Quadrics();
220: GeomBuffer gbuf = null;
221: Shape3D shape[] = new Shape3D[3];
222:
223: GeomBuffer cache = getCachedGeometry(Primitive.CYLINDER,
224: (float) BODY, radius, height, xdivision, ydivision,
225: primflags);
226: if (cache != null) {
227: // System.out.println("using cached geometry");
228: shape[BODY] = new Shape3D(cache.getComputedGeometry());
229: numVerts += cache.getNumVerts();
230: numTris += cache.getNumTris();
231: } else {
232: gbuf = q.cylinder((double) height, (double) radius,
233: xdivision, ydivision, 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.CYLINDER, (float) BODY, radius,
239: height, xdivision, ydivision, primflags, gbuf);
240: }
241:
242: if ((flags & ENABLE_APPEARANCE_MODIFY) != 0) {
243: (shape[BODY]).setCapability(Shape3D.ALLOW_APPEARANCE_READ);
244: (shape[BODY]).setCapability(Shape3D.ALLOW_APPEARANCE_WRITE);
245: }
246:
247: if ((flags & ENABLE_GEOMETRY_PICKING) != 0) {
248: (shape[BODY]).setCapability(Shape3D.ALLOW_GEOMETRY_READ);
249: }
250:
251: this .addChild(shape[BODY]);
252:
253: // Create top of cylinder
254: cache = getCachedGeometry(Primitive.TOP_DISK, radius, radius,
255: height / 2.0f, xdivision, xdivision, primflags);
256: if (cache != null) {
257: // System.out.println("using cached top");
258: shape[TOP] = new Shape3D(cache.getComputedGeometry());
259: numVerts += cache.getNumVerts();
260: numTris += cache.getNumTris();
261: } else {
262: gbuf = q.disk((double) radius, xdivision, height / 2.0,
263: outside, texCoordYUp);
264: shape[TOP] = new Shape3D(gbuf.getGeom(flags));
265: numVerts += gbuf.getNumVerts();
266: numTris += gbuf.getNumTris();
267: if ((primflags & Primitive.GEOMETRY_NOT_SHARED) == 0) {
268: cacheGeometry(Primitive.TOP_DISK, radius, radius,
269: height / 2.0f, xdivision, xdivision, primflags,
270: gbuf);
271: }
272: }
273:
274: if ((flags & ENABLE_APPEARANCE_MODIFY) != 0) {
275: (shape[TOP]).setCapability(Shape3D.ALLOW_APPEARANCE_READ);
276: (shape[TOP]).setCapability(Shape3D.ALLOW_APPEARANCE_WRITE);
277: }
278:
279: if ((flags & ENABLE_GEOMETRY_PICKING) != 0) {
280: (shape[TOP]).setCapability(Shape3D.ALLOW_GEOMETRY_READ);
281: }
282:
283: this .addChild(shape[TOP]);
284:
285: // Create bottom
286: cache = getCachedGeometry(Primitive.BOTTOM_DISK, radius,
287: radius, -height / 2.0f, xdivision, xdivision, primflags);
288: if (cache != null) {
289: // System.out.println("using cached bottom");
290: shape[BOTTOM] = new Shape3D(cache.getComputedGeometry());
291: numVerts += cache.getNumVerts();
292: numTris += cache.getNumTris();
293: } else {
294: gbuf = q.disk((double) radius, xdivision, -height / 2.0,
295: !outside, texCoordYUp);
296: shape[BOTTOM] = new Shape3D(gbuf.getGeom(flags));
297: numVerts += gbuf.getNumVerts();
298: numTris += gbuf.getNumTris();
299: if ((primflags & Primitive.GEOMETRY_NOT_SHARED) == 0) {
300: cacheGeometry(Primitive.BOTTOM_DISK, radius, radius,
301: -height / 2.0f, xdivision, xdivision,
302: primflags, gbuf);
303: }
304: }
305:
306: if ((flags & ENABLE_APPEARANCE_MODIFY) != 0) {
307: (shape[BOTTOM])
308: .setCapability(Shape3D.ALLOW_APPEARANCE_READ);
309: (shape[BOTTOM])
310: .setCapability(Shape3D.ALLOW_APPEARANCE_WRITE);
311: }
312:
313: if ((flags & ENABLE_GEOMETRY_PICKING) != 0) {
314: (shape[BOTTOM]).setCapability(Shape3D.ALLOW_GEOMETRY_READ);
315: }
316:
317: this .addChild(shape[BOTTOM]);
318:
319: // Set Appearance
320: if (ap == null) {
321: setAppearance();
322: } else
323: setAppearance(ap);
324: }
325:
326: /**
327: * Used to create a new instance of the node. This routine is called
328: * by <code>cloneTree</code> to duplicate the current node.
329: * <code>cloneNode</code> should be overridden by any user subclassed
330: * objects. All subclasses must have their <code>cloneNode</code>
331: * method consist of the following lines:
332: * <P><blockquote><pre>
333: * public Node cloneNode(boolean forceDuplicate) {
334: * UserSubClass usc = new UserSubClass();
335: * usc.duplicateNode(this, forceDuplicate);
336: * return usc;
337: * }
338: * </pre></blockquote>
339: * @param forceDuplicate when set to <code>true</code>, causes the
340: * <code>duplicateOnCloneTree</code> flag to be ignored. When
341: * <code>false</code>, the value of each node's
342: * <code>duplicateOnCloneTree</code> variable determines whether
343: * NodeComponent data is duplicated or copied.
344: *
345: * @see Node#cloneTree
346: * @see Node#duplicateNode
347: * @see NodeComponent#setDuplicateOnCloneTree
348: */
349: public Node cloneNode(boolean forceDuplicate) {
350: Cylinder c = new Cylinder(radius, height, flags, xdivisions,
351: ydivisions, getAppearance());
352: c.duplicateNode(this , forceDuplicate);
353: return c;
354: }
355:
356: /**
357: * Copies all node information from <code>originalNode</code> into
358: * the current node. This method is called from the
359: * <code>cloneNode</code> method which is, in turn, called by the
360: * <code>cloneTree</code> method.
361: * <P>
362: * For any <i>NodeComponent</i> objects
363: * contained by the object being duplicated, each <i>NodeComponent</i>
364: * object's <code>duplicateOnCloneTree</code> value is used to determine
365: * whether the <i>NodeComponent</i> should be duplicated in the new node
366: * or if just a reference to the current node should be placed in the
367: * new node. This flag can be overridden by setting the
368: * <code>forceDuplicate</code> parameter in the <code>cloneTree</code>
369: * method to <code>true</code>.
370: *
371: * @param originalNode the original node to duplicate.
372: * @param forceDuplicate when set to <code>true</code>, causes the
373: * <code>duplicateOnCloneTree</code> flag to be ignored. When
374: * <code>false</code>, the value of each node's
375: * <code>duplicateOnCloneTree</code> variable determines whether
376: * NodeComponent data is duplicated or copied.
377: *
378: * @see Node#cloneTree
379: * @see Node#cloneNode
380: * @see NodeComponent#setDuplicateOnCloneTree
381: */
382: public void duplicateNode(Node originalNode, boolean forceDuplicate) {
383: super .duplicateNode(originalNode, forceDuplicate);
384: }
385:
386: /**
387: * Returns the radius of the cylinder
388: *
389: * @since Java 3D 1.2.1
390: */
391: public float getRadius() {
392: return radius;
393: }
394:
395: /**
396: * Returns the height of the cylinder
397: *
398: * @since Java 3D 1.2.1
399: */
400: public float getHeight() {
401: return height;
402: }
403:
404: /**
405: * Returns the number divisions along the X direction
406: *
407: * @since Java 3D 1.2.1
408: */
409: public int getXdivisions() {
410: return xdivisions;
411: }
412:
413: /**
414: * Returns the number of divisions along the height of the cylinder
415: *
416: * @since Java 3D 1.2.1
417: */
418: public int getYdivisions() {
419: return ydivisions;
420: }
421:
422: }
|