49 * A {@code Cylinder} is a 3D geometry primitive created with a given radius and height. 50 * It is centered at the origin. 51 * 52 * @since JavaFX 8.0 53 */ 54 public class Cylinder extends Shape3D { 55 static { 56 // This is used by classes in different packages to get access to 57 // private and package private methods. 58 CylinderHelper.setCylinderAccessor(new CylinderHelper.CylinderAccessor() { 59 @Override 60 public NGNode doCreatePeer(Node node) { 61 return ((Cylinder) node).doCreatePeer(); 62 } 63 64 @Override 65 public void doUpdatePeer(Node node) { 66 ((Cylinder) node).doUpdatePeer(); 67 } 68 69 }); 70 } 71 static final int DEFAULT_DIVISIONS = 64; 72 static final double DEFAULT_RADIUS = 1; 73 static final double DEFAULT_HEIGHT = 2; 74 75 private int divisions = DEFAULT_DIVISIONS; 76 private TriangleMesh mesh; 77 78 { 79 // To initialize the class helper at the begining each constructor of this class 80 CylinderHelper.initHelper(this); 81 } 82 /** 83 * Creates a new instance of {@code Cylinder} of radius of 1.0 and height of 2.0. 84 * Resolution defaults to 15 divisions along X and Z axis. 85 */ 86 public Cylinder() { 87 this(DEFAULT_RADIUS, DEFAULT_HEIGHT, DEFAULT_DIVISIONS); 88 } 121 * @defaultValue 2.0 122 */ 123 private DoubleProperty height; 124 125 public final void setHeight(double value) { 126 heightProperty().set(value); 127 } 128 129 public final double getHeight() { 130 return height == null ? 2 : height.get(); 131 } 132 133 public final DoubleProperty heightProperty() { 134 if (height == null) { 135 height = new SimpleDoubleProperty(Cylinder.this, "height", DEFAULT_HEIGHT) { 136 @Override 137 public void invalidated() { 138 NodeHelper.markDirty(Cylinder.this, DirtyBits.MESH_GEOM); 139 manager.invalidateCylinderMesh(key); 140 key = 0; 141 impl_geomChanged(); 142 } 143 }; 144 } 145 return height; 146 } 147 148 /** 149 * Defines the radius in the Z plane of the Cylinder. 150 * 151 * @defaultValue 1.0 152 */ 153 private DoubleProperty radius; 154 155 public final void setRadius(double value) { 156 radiusProperty().set(value); 157 } 158 159 public final double getRadius() { 160 return radius == null ? 1 : radius.get(); 161 } 162 163 public final DoubleProperty radiusProperty() { 164 if (radius == null) { 165 radius = new SimpleDoubleProperty(Cylinder.this, "radius", DEFAULT_RADIUS) { 166 @Override 167 public void invalidated() { 168 NodeHelper.markDirty(Cylinder.this, DirtyBits.MESH_GEOM); 169 manager.invalidateCylinderMesh(key); 170 key = 0; 171 impl_geomChanged(); 172 } 173 }; 174 } 175 return radius; 176 } 177 178 /** 179 * Retrieves the divisions attribute use to generate this cylinder. 180 * 181 * @return the divisions attribute. 182 */ 183 public int getDivisions() { 184 return divisions; 185 } 186 187 /* 188 * Note: This method MUST only be called via its accessor method. 189 */ 190 private void doUpdatePeer() { 191 if (NodeHelper.isDirty(this, DirtyBits.MESH_GEOM)) { 195 if (h < 0 || r < 0) { 196 peer.updateMesh(null); 197 } else { 198 if (key == 0) { 199 key = generateKey(h, r, divisions); 200 } 201 mesh = manager.getCylinderMesh(h, r, divisions, key); 202 mesh.updatePG(); 203 peer.updateMesh(mesh.getPGTriangleMesh()); 204 } 205 } 206 } 207 208 /* 209 * Note: This method MUST only be called via its accessor method. 210 */ 211 private NGNode doCreatePeer() { 212 return new NGCylinder(); 213 } 214 215 /** 216 * @treatAsPrivate implementation detail 217 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 218 */ 219 @Deprecated 220 @Override 221 public BaseBounds impl_computeGeomBounds(BaseBounds bounds, BaseTransform tx) { 222 final float h = (float) getHeight(); 223 final float r = (float) getRadius(); 224 225 if (r < 0 || h < 0) { 226 return bounds.makeEmpty(); 227 } 228 229 final float hh = h * 0.5f; 230 231 bounds = bounds.deriveWithNewBounds(-r, -hh, -r, r, hh, r); 232 bounds = tx.transform(bounds, bounds); 233 return bounds; 234 } 235 236 /** 237 * @treatAsPrivate implementation detail 238 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 239 */ 240 @Deprecated 241 @Override 242 protected boolean impl_computeContains(double localX, double localY) { 243 double w = getRadius(); 244 double hh = getHeight()*.5f; 245 return -w <= localX && localX <= w && 246 -hh <= localY && localY <= hh; 247 } 248 249 /** 250 * @treatAsPrivate implementation detail 251 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 252 */ 253 @Deprecated 254 @Override 255 protected boolean impl_computeIntersects(PickRay pickRay, PickResultChooser pickResult) { 256 257 final boolean exactPicking = divisions < DEFAULT_DIVISIONS && mesh != null; 258 259 final double r = getRadius(); 260 final Vec3d dir = pickRay.getDirectionNoClone(); 261 final double dirX = dir.x; 262 final double dirY = dir.y; 263 final double dirZ = dir.z; 264 final Vec3d origin = pickRay.getOriginNoClone(); 265 final double originX = origin.x; 266 final double originY = origin.y; 267 final double originZ = origin.z; 268 final double h = getHeight(); 269 final double halfHeight = h / 2.0; 270 final CullFace cullFace = getCullFace(); 271 272 // Check the open cylinder first 273 274 // Coeficients of a quadratic equation desribing intersection with an infinite cylinder 275 final double a = dirX * dirX + dirZ * dirZ; | 49 * A {@code Cylinder} is a 3D geometry primitive created with a given radius and height. 50 * It is centered at the origin. 51 * 52 * @since JavaFX 8.0 53 */ 54 public class Cylinder extends Shape3D { 55 static { 56 // This is used by classes in different packages to get access to 57 // private and package private methods. 58 CylinderHelper.setCylinderAccessor(new CylinderHelper.CylinderAccessor() { 59 @Override 60 public NGNode doCreatePeer(Node node) { 61 return ((Cylinder) node).doCreatePeer(); 62 } 63 64 @Override 65 public void doUpdatePeer(Node node) { 66 ((Cylinder) node).doUpdatePeer(); 67 } 68 69 @Override 70 public BaseBounds doComputeGeomBounds(Node node, 71 BaseBounds bounds, BaseTransform tx) { 72 return ((Cylinder) node).doComputeGeomBounds(bounds, tx); 73 } 74 75 @Override 76 public boolean doComputeContains(Node node, double localX, double localY) { 77 return ((Cylinder) node).doComputeContains(localX, localY); 78 } 79 80 @Override 81 public boolean doComputeIntersects(Node node, PickRay pickRay, 82 PickResultChooser pickResult) { 83 return ((Cylinder) node).doComputeIntersects(pickRay, pickResult); 84 } 85 }); 86 } 87 static final int DEFAULT_DIVISIONS = 64; 88 static final double DEFAULT_RADIUS = 1; 89 static final double DEFAULT_HEIGHT = 2; 90 91 private int divisions = DEFAULT_DIVISIONS; 92 private TriangleMesh mesh; 93 94 { 95 // To initialize the class helper at the begining each constructor of this class 96 CylinderHelper.initHelper(this); 97 } 98 /** 99 * Creates a new instance of {@code Cylinder} of radius of 1.0 and height of 2.0. 100 * Resolution defaults to 15 divisions along X and Z axis. 101 */ 102 public Cylinder() { 103 this(DEFAULT_RADIUS, DEFAULT_HEIGHT, DEFAULT_DIVISIONS); 104 } 137 * @defaultValue 2.0 138 */ 139 private DoubleProperty height; 140 141 public final void setHeight(double value) { 142 heightProperty().set(value); 143 } 144 145 public final double getHeight() { 146 return height == null ? 2 : height.get(); 147 } 148 149 public final DoubleProperty heightProperty() { 150 if (height == null) { 151 height = new SimpleDoubleProperty(Cylinder.this, "height", DEFAULT_HEIGHT) { 152 @Override 153 public void invalidated() { 154 NodeHelper.markDirty(Cylinder.this, DirtyBits.MESH_GEOM); 155 manager.invalidateCylinderMesh(key); 156 key = 0; 157 NodeHelper.geomChanged(Cylinder.this); 158 } 159 }; 160 } 161 return height; 162 } 163 164 /** 165 * Defines the radius in the Z plane of the Cylinder. 166 * 167 * @defaultValue 1.0 168 */ 169 private DoubleProperty radius; 170 171 public final void setRadius(double value) { 172 radiusProperty().set(value); 173 } 174 175 public final double getRadius() { 176 return radius == null ? 1 : radius.get(); 177 } 178 179 public final DoubleProperty radiusProperty() { 180 if (radius == null) { 181 radius = new SimpleDoubleProperty(Cylinder.this, "radius", DEFAULT_RADIUS) { 182 @Override 183 public void invalidated() { 184 NodeHelper.markDirty(Cylinder.this, DirtyBits.MESH_GEOM); 185 manager.invalidateCylinderMesh(key); 186 key = 0; 187 NodeHelper.geomChanged(Cylinder.this); 188 } 189 }; 190 } 191 return radius; 192 } 193 194 /** 195 * Retrieves the divisions attribute use to generate this cylinder. 196 * 197 * @return the divisions attribute. 198 */ 199 public int getDivisions() { 200 return divisions; 201 } 202 203 /* 204 * Note: This method MUST only be called via its accessor method. 205 */ 206 private void doUpdatePeer() { 207 if (NodeHelper.isDirty(this, DirtyBits.MESH_GEOM)) { 211 if (h < 0 || r < 0) { 212 peer.updateMesh(null); 213 } else { 214 if (key == 0) { 215 key = generateKey(h, r, divisions); 216 } 217 mesh = manager.getCylinderMesh(h, r, divisions, key); 218 mesh.updatePG(); 219 peer.updateMesh(mesh.getPGTriangleMesh()); 220 } 221 } 222 } 223 224 /* 225 * Note: This method MUST only be called via its accessor method. 226 */ 227 private NGNode doCreatePeer() { 228 return new NGCylinder(); 229 } 230 231 /* 232 * Note: This method MUST only be called via its accessor method. 233 */ 234 private BaseBounds doComputeGeomBounds(BaseBounds bounds, BaseTransform tx) { 235 final float h = (float) getHeight(); 236 final float r = (float) getRadius(); 237 238 if (r < 0 || h < 0) { 239 return bounds.makeEmpty(); 240 } 241 242 final float hh = h * 0.5f; 243 244 bounds = bounds.deriveWithNewBounds(-r, -hh, -r, r, hh, r); 245 bounds = tx.transform(bounds, bounds); 246 return bounds; 247 } 248 249 /* 250 * Note: This method MUST only be called via its accessor method. 251 */ 252 private boolean doComputeContains(double localX, double localY) { 253 double w = getRadius(); 254 double hh = getHeight()*.5f; 255 return -w <= localX && localX <= w && 256 -hh <= localY && localY <= hh; 257 } 258 259 /* 260 * Note: This method MUST only be called via its accessor method. 261 */ 262 private boolean doComputeIntersects(PickRay pickRay, PickResultChooser pickResult) { 263 264 final boolean exactPicking = divisions < DEFAULT_DIVISIONS && mesh != null; 265 266 final double r = getRadius(); 267 final Vec3d dir = pickRay.getDirectionNoClone(); 268 final double dirX = dir.x; 269 final double dirY = dir.y; 270 final double dirZ = dir.z; 271 final Vec3d origin = pickRay.getOriginNoClone(); 272 final double originX = origin.x; 273 final double originY = origin.y; 274 final double originZ = origin.z; 275 final double h = getHeight(); 276 final double halfHeight = h / 2.0; 277 final CullFace cullFace = getCullFace(); 278 279 // Check the open cylinder first 280 281 // Coeficients of a quadratic equation desribing intersection with an infinite cylinder 282 final double a = dirX * dirX + dirZ * dirZ; |