48 * The {@code Sphere} class defines a 3 dimensional sphere with the specified size.
49 * A {@code Sphere} is a 3D geometry primitive created with a given radius.
50 * It is centered at the origin.
51 *
52 * @since JavaFX 8.0
53 */
54 public class Sphere extends Shape3D {
55 static {
56 // This is used by classes in different packages to get access to
57 // private and package private methods.
58 SphereHelper.setSphereAccessor(new SphereHelper.SphereAccessor() {
59 @Override
60 public NGNode doCreatePeer(Node node) {
61 return ((Sphere) node).doCreatePeer();
62 }
63
64 @Override
65 public void doUpdatePeer(Node node) {
66 ((Sphere) node).doUpdatePeer();
67 }
68 });
69 }
70
71 static final int DEFAULT_DIVISIONS = 64;
72 static final double DEFAULT_RADIUS = 1;
73 private int divisions = DEFAULT_DIVISIONS;
74 private TriangleMesh mesh;
75
76 /**
77 * Creates a new instance of {@code Sphere} of radius of 1.0.
78 * The resolution defaults to MID_RESOLUTION divisions along sphere's axes.
79 */
80 public Sphere() {
81 this(DEFAULT_RADIUS, DEFAULT_DIVISIONS);
82 }
83
84 /**
85 * Creates a new instance of {@code Sphere} of a given radius.
86 * The resolution defaults to MID_RESOLUTION divisions along sphere's axes.
87 *
115 * @defaultValue 1.0
116 */
117 private DoubleProperty radius;
118
119 public final void setRadius(double value) {
120 radiusProperty().set(value);
121 }
122
123 public final double getRadius() {
124 return radius == null ? 1 : radius.get();
125 }
126
127 public final DoubleProperty radiusProperty() {
128 if (radius == null) {
129 radius = new SimpleDoubleProperty(Sphere.this, "radius", DEFAULT_RADIUS) {
130 @Override
131 public void invalidated() {
132 NodeHelper.markDirty(Sphere.this, DirtyBits.MESH_GEOM);
133 manager.invalidateSphereMesh(key);
134 key = 0;
135 impl_geomChanged();
136 }
137 };
138 }
139 return radius;
140 }
141
142 /**
143 * Retrieves the divisions attribute use to generate this sphere.
144 *
145 * @return the divisions attribute.
146 */
147 public int getDivisions() {
148 return divisions;
149 }
150
151 /*
152 * Note: This method MUST only be called via its accessor method.
153 */
154 private NGNode doCreatePeer() {
155 return new NGSphere();
158 /*
159 * Note: This method MUST only be called via its accessor method.
160 */
161 private void doUpdatePeer() {
162 if (NodeHelper.isDirty(this, DirtyBits.MESH_GEOM)) {
163 final NGSphere pgSphere = NodeHelper.getPeer(this);
164 final float r = (float) getRadius();
165 if (r < 0) {
166 pgSphere.updateMesh(null);
167 } else {
168 if (key == 0) {
169 key = generateKey(r, divisions);
170 }
171 mesh = manager.getSphereMesh(r, divisions, key);
172 mesh.updatePG();
173 pgSphere.updateMesh(mesh.getPGTriangleMesh());
174 }
175 }
176 }
177
178 /**
179 * @treatAsPrivate implementation detail
180 * @deprecated This is an internal API that is not intended for use and will be removed in the next version
181 */
182 @Deprecated
183 @Override
184 public BaseBounds impl_computeGeomBounds(BaseBounds bounds, BaseTransform tx) {
185 final float r = (float) getRadius();
186
187 if (r < 0) {
188 return bounds.makeEmpty();
189 }
190
191 bounds = bounds.deriveWithNewBounds(-r, -r, -r, r, r ,r);
192 bounds = tx.transform(bounds, bounds);
193 return bounds;
194 }
195
196 /**
197 * @treatAsPrivate implementation detail
198 * @deprecated This is an internal API that is not intended for use and will be removed in the next version
199 */
200 @Deprecated
201 @Override
202 protected boolean impl_computeContains(double localX, double localY) {
203 double r = getRadius();
204 double n2 = localX * localX + localY * localY;
205 return n2 <= r * r;
206 }
207
208 /**
209 * @treatAsPrivate implementation detail
210 * @deprecated This is an internal API that is not intended for use and will be removed in the next version
211 */
212 @Deprecated
213 @Override
214 protected boolean impl_computeIntersects(PickRay pickRay, PickResultChooser pickResult) {
215
216 final boolean exactPicking = divisions < DEFAULT_DIVISIONS && mesh != null;
217
218 final double r = getRadius();
219 final Vec3d dir = pickRay.getDirectionNoClone();
220 final double dirX = dir.x;
221 final double dirY = dir.y;
222 final double dirZ = dir.z;
223 final Vec3d origin = pickRay.getOriginNoClone();
224 final double originX = origin.x;
225 final double originY = origin.y;
226 final double originZ = origin.z;
227
228 // Coeficients of a quadratic equation desribing intersection with sphere
229 final double a = dirX * dirX + dirY * dirY + dirZ * dirZ;
230 final double b = 2 * (dirX * originX + dirY * originY + dirZ * originZ);
231 final double c = originX * originX + originY * originY + originZ * originZ - r * r;
232
233 final double discriminant = b * b - 4 * a * c;
234 if (discriminant < 0) {
|
48 * The {@code Sphere} class defines a 3 dimensional sphere with the specified size.
49 * A {@code Sphere} is a 3D geometry primitive created with a given radius.
50 * It is centered at the origin.
51 *
52 * @since JavaFX 8.0
53 */
54 public class Sphere extends Shape3D {
55 static {
56 // This is used by classes in different packages to get access to
57 // private and package private methods.
58 SphereHelper.setSphereAccessor(new SphereHelper.SphereAccessor() {
59 @Override
60 public NGNode doCreatePeer(Node node) {
61 return ((Sphere) node).doCreatePeer();
62 }
63
64 @Override
65 public void doUpdatePeer(Node node) {
66 ((Sphere) node).doUpdatePeer();
67 }
68
69 @Override
70 public BaseBounds doComputeGeomBounds(Node node,
71 BaseBounds bounds, BaseTransform tx) {
72 return ((Sphere) node).doComputeGeomBounds(bounds, tx);
73 }
74
75 @Override
76 public boolean doComputeContains(Node node, double localX, double localY) {
77 return ((Sphere) node).doComputeContains(localX, localY);
78 }
79
80 @Override
81 public boolean doComputeIntersects(Node node, PickRay pickRay,
82 PickResultChooser pickResult) {
83 return ((Sphere) node).doComputeIntersects(pickRay, pickResult);
84 }
85 });
86 }
87
88 static final int DEFAULT_DIVISIONS = 64;
89 static final double DEFAULT_RADIUS = 1;
90 private int divisions = DEFAULT_DIVISIONS;
91 private TriangleMesh mesh;
92
93 /**
94 * Creates a new instance of {@code Sphere} of radius of 1.0.
95 * The resolution defaults to MID_RESOLUTION divisions along sphere's axes.
96 */
97 public Sphere() {
98 this(DEFAULT_RADIUS, DEFAULT_DIVISIONS);
99 }
100
101 /**
102 * Creates a new instance of {@code Sphere} of a given radius.
103 * The resolution defaults to MID_RESOLUTION divisions along sphere's axes.
104 *
132 * @defaultValue 1.0
133 */
134 private DoubleProperty radius;
135
136 public final void setRadius(double value) {
137 radiusProperty().set(value);
138 }
139
140 public final double getRadius() {
141 return radius == null ? 1 : radius.get();
142 }
143
144 public final DoubleProperty radiusProperty() {
145 if (radius == null) {
146 radius = new SimpleDoubleProperty(Sphere.this, "radius", DEFAULT_RADIUS) {
147 @Override
148 public void invalidated() {
149 NodeHelper.markDirty(Sphere.this, DirtyBits.MESH_GEOM);
150 manager.invalidateSphereMesh(key);
151 key = 0;
152 NodeHelper.geomChanged(Sphere.this);
153 }
154 };
155 }
156 return radius;
157 }
158
159 /**
160 * Retrieves the divisions attribute use to generate this sphere.
161 *
162 * @return the divisions attribute.
163 */
164 public int getDivisions() {
165 return divisions;
166 }
167
168 /*
169 * Note: This method MUST only be called via its accessor method.
170 */
171 private NGNode doCreatePeer() {
172 return new NGSphere();
175 /*
176 * Note: This method MUST only be called via its accessor method.
177 */
178 private void doUpdatePeer() {
179 if (NodeHelper.isDirty(this, DirtyBits.MESH_GEOM)) {
180 final NGSphere pgSphere = NodeHelper.getPeer(this);
181 final float r = (float) getRadius();
182 if (r < 0) {
183 pgSphere.updateMesh(null);
184 } else {
185 if (key == 0) {
186 key = generateKey(r, divisions);
187 }
188 mesh = manager.getSphereMesh(r, divisions, key);
189 mesh.updatePG();
190 pgSphere.updateMesh(mesh.getPGTriangleMesh());
191 }
192 }
193 }
194
195 /*
196 * Note: This method MUST only be called via its accessor method.
197 */
198 private BaseBounds doComputeGeomBounds(BaseBounds bounds, BaseTransform tx) {
199 final float r = (float) getRadius();
200
201 if (r < 0) {
202 return bounds.makeEmpty();
203 }
204
205 bounds = bounds.deriveWithNewBounds(-r, -r, -r, r, r ,r);
206 bounds = tx.transform(bounds, bounds);
207 return bounds;
208 }
209
210 /*
211 * Note: This method MUST only be called via its accessor method.
212 */
213 private boolean doComputeContains(double localX, double localY) {
214 double r = getRadius();
215 double n2 = localX * localX + localY * localY;
216 return n2 <= r * r;
217 }
218
219 /*
220 * Note: This method MUST only be called via its accessor method.
221 */
222 private boolean doComputeIntersects(PickRay pickRay, PickResultChooser pickResult) {
223
224 final boolean exactPicking = divisions < DEFAULT_DIVISIONS && mesh != null;
225
226 final double r = getRadius();
227 final Vec3d dir = pickRay.getDirectionNoClone();
228 final double dirX = dir.x;
229 final double dirY = dir.y;
230 final double dirZ = dir.z;
231 final Vec3d origin = pickRay.getOriginNoClone();
232 final double originX = origin.x;
233 final double originY = origin.y;
234 final double originZ = origin.z;
235
236 // Coeficients of a quadratic equation desribing intersection with sphere
237 final double a = dirX * dirX + dirY * dirY + dirZ * dirZ;
238 final double b = 2 * (dirX * originX + dirY * originY + dirZ * originZ);
239 final double c = originX * originX + originY * originY + originZ * originZ - r * r;
240
241 final double discriminant = b * b - 4 * a * c;
242 if (discriminant < 0) {
|