Spec-Zone .ru
спецификации, руководства, описания, API
|
001/* 002 * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. Oracle designates this 008 * particular file as subject to the "Classpath" exception as provided 009 * by Oracle in the LICENSE file that accompanied this code. 010 * 011 * This code is distributed in the hope that it will be useful, but WITHOUT 012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 014 * version 2 for more details (a copy is included in the LICENSE file that 015 * accompanied this code). 016 * 017 * You should have received a copy of the GNU General Public License version 018 * 2 along with this work; if not, write to the Free Software Foundation, 019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 020 * 021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 022 * or visit www.oracle.com if you need additional information or have any 023 * questions. 024 */ 025 026package javafx.scene.shape; 027 028import com.sun.javafx.geom.BaseBounds; 029import com.sun.javafx.geom.PickRay; 030import com.sun.javafx.geom.Vec3d; 031import com.sun.javafx.geom.transform.BaseTransform; 032import com.sun.javafx.scene.DirtyBits; 033import com.sun.javafx.scene.input.PickResultChooser; 034import com.sun.javafx.sg.PGNode; 035import com.sun.javafx.sg.PGSphere; 036import com.sun.javafx.tk.Toolkit; 037import javafx.beans.property.DoubleProperty; 038import javafx.beans.property.SimpleDoubleProperty; 039import javafx.geometry.Point2D; 040import javafx.geometry.Point3D; 041import javafx.scene.input.PickResult; 042import javafx.scene.transform.Rotate; 043 044/** 045 * The {@code Sphere} class defines a 3 dimensional sphere with the specified size. 046 * A {@code Sphere} is a 3D geometry primitive created with a given radius. 047 * It is centered at the origin. 048 * 049 * @since JavaFX 8 050 */ 051public class Sphere extends Shape3D { 052 053 static final int DEFAULT_DIVISIONS = 64; 054 static final double DEFAULT_RADIUS = 1; 055 private int divisions = DEFAULT_DIVISIONS; 056 private TriangleMesh mesh; 057 058 /** 059 * Creates a new instance of {@code Sphere} of radius of 1.0. 060 * The resolution defaults to MID_RESOLUTION divisions along sphere's axes. 061 */ 062 public Sphere() { 063 this(DEFAULT_RADIUS, DEFAULT_DIVISIONS); 064 } 065 066 /** 067 * Creates a new instance of {@code Sphere} of a given radius. 068 * The resolution defaults to MID_RESOLUTION divisions along sphere's axes. 069 * 070 * @param radius Radius 071 */ 072 public Sphere(double radius) { 073 this(radius, DEFAULT_DIVISIONS); 074 } 075 076 /** 077 * Creates a new instance of {@code Sphere} of a given radius and number 078 * of divisions. 079 * The resolution is defined in terms of number of subdivisions along the 080 * sphere's axes. More divisions lead to more finely tesselated objects. 081 * 082 * Note that divisions should be at least 1. Any value less than that will be 083 * clamped to 1. 084 * 085 * @param radius Radius 086 * @param divisions Divisions 087 */ 088 public Sphere(double radius, int divisions) { 089 this.divisions = divisions < 1 ? 1: divisions; 090 setRadius(radius); 091 } 092 093 /** 094 * Defines the radius of the Sphere. 095 * 096 * @defaultValue 1.0 097 */ 098 private DoubleProperty radius; 099 100 public final void setRadius(double value) { 101 radiusProperty().set(value); 102 } 103 104 public final double getRadius() { 105 return radius == null ? 1 : radius.get(); 106 } 107 108 public final DoubleProperty radiusProperty() { 109 if (radius == null) { 110 radius = new SimpleDoubleProperty(Sphere.this, "radius", DEFAULT_RADIUS) { 111 @Override 112 public void invalidated() { 113 impl_markDirty(DirtyBits.MESH_GEOM); 114 manager.invalidateSphereMesh(key); 115 key = 0; 116 } 117 }; 118 } 119 return radius; 120 } 121 122 /** 123 * Retrieves the divisions attribute use to generate this sphere. 124 * 125 * @return the divisions attribute. 126 */ 127 public int getDivisions() { 128 return divisions; 129 } 130 131 /** 132 * @treatAsPrivate implementation detail 133 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 134 */ 135 @Deprecated 136 @Override 137 protected PGNode impl_createPGNode() { 138 return Toolkit.getToolkit().createPGSphere(); 139 } 140 141 /** 142 * @treatAsPrivate implementation detail 143 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 144 */ 145 @Deprecated 146 public void impl_updatePG() { 147 super.impl_updatePG(); 148 if (impl_isDirty(DirtyBits.MESH_GEOM)) { 149 PGSphere pgSphere = (PGSphere) impl_getPGNode(); 150 final float r = (float) getRadius(); 151 if (r < 0) { 152 pgSphere.updateMesh(null); 153 } else { 154 if (key == 0) { 155 key = generateKey(r, divisions); 156 } 157 mesh = manager.getSphereMesh(r, divisions, key); 158 mesh.impl_updatePG(); 159 pgSphere.updateMesh(mesh.impl_getPGTriangleMesh()); 160 } 161 } 162 } 163 164 /** 165 * @treatAsPrivate implementation detail 166 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 167 */ 168 @Deprecated 169 @Override 170 public BaseBounds impl_computeGeomBounds(BaseBounds bounds, BaseTransform tx) { 171 final float r = (float) getRadius(); 172 173 if (r < 0) { 174 return bounds.makeEmpty(); 175 } 176 177 bounds = bounds.deriveWithNewBounds(-r, -r, -r, r, r ,r); 178 bounds = tx.transform(bounds, bounds); 179 return bounds; 180 } 181 182 /** 183 * @treatAsPrivate implementation detail 184 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 185 */ 186 @Deprecated 187 @Override 188 protected boolean impl_computeContains(double localX, double localY) { 189 double r = getRadius(); 190 double n2 = localX * localX + localY * localY; 191 return n2 <= r * r; 192 } 193 194 /** 195 * @treatAsPrivate implementation detail 196 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 197 */ 198 @Deprecated 199 @Override 200 protected boolean impl_computeIntersects(PickRay pickRay, PickResultChooser pickResult) { 201 202 final boolean exactPicking = divisions < DEFAULT_DIVISIONS && mesh != null; 203 204 final double r = getRadius(); 205 final Vec3d dir = pickRay.getDirectionNoClone(); 206 final double dirX = dir.x; 207 final double dirY = dir.y; 208 final double dirZ = dir.z; 209 final Vec3d origin = pickRay.getOriginNoClone(); 210 final double originX = origin.x; 211 final double originY = origin.y; 212 final double originZ = origin.z; 213 214 // Coeficients of a quadratic equation desribing intersection with sphere 215 final double a = dirX * dirX + dirY * dirY + dirZ * dirZ; 216 final double b = 2 * (dirX * originX + dirY * originY + dirZ * originZ); 217 final double c = originX * originX + originY * originY + originZ * originZ - r * r; 218 219 final double discriminant = b * b - 4 * a * c; 220 if (discriminant < 0) { 221 // No real roots of the equation, missed the shape 222 return false; 223 } 224 225 final double distSqrt = Math.sqrt(discriminant); 226 final double q = (b < 0) ? (-b - distSqrt) / 2.0 : (-b + distSqrt) / 2.0; 227 228 double t0 = q / a; 229 double t1 = c / q; 230 231 if (t0 > t1) { 232 final double temp = t0; 233 t0 = t1; 234 t1 = temp; 235 } 236 237 final double minDistance = pickRay.getNearClip(); 238 final double maxDistance = pickRay.getFarClip(); 239 240 if (t1 < minDistance || t0 > maxDistance) { 241 // the sphere is out of clipping planes 242 return false; 243 } 244 245 double t = t0; 246 final CullFace cullFace = getCullFace(); 247 if (t0 < minDistance || cullFace == CullFace.FRONT) { 248 if (t1 <= maxDistance && getCullFace() != CullFace.BACK) { 249 // picking the back wall 250 t = t1; 251 } else { 252 // we are inside the sphere with the back wall culled, but the 253 // exact picking still needs to be done because the front faced 254 // triangles may still be in front of us 255 if (!exactPicking) { 256 return false; 257 } 258 } 259 } 260 261 if (Double.isInfinite(t) || Double.isNaN(t)) { 262 // We've got a nonsense pick ray or sphere size. 263 return false; 264 } 265 266 if (exactPicking) { 267 return mesh.impl_computeIntersects(pickRay, pickResult, this, cullFace, false); 268 } 269 270 if (pickResult != null && pickResult.isCloser(t)) { 271 final Point3D point = PickResultChooser.computePoint(pickRay, t); 272 273 // computing texture coords 274 final Point3D proj = new Point3D(point.getX(), 0, point.getZ()); 275 final Point3D cross = proj.crossProduct(Rotate.Z_AXIS); 276 double angle = proj.angle(Rotate.Z_AXIS); 277 if (cross.getY() > 0) { 278 angle = 360 - angle; 279 } 280 Point2D txtCoords = new Point2D(1 - angle / 360, 0.5 + point.getY() / (2 * r)); 281 282 pickResult.offer(this, t, PickResult.FACE_UNDEFINED, point, txtCoords); 283 } 284 return true; 285 } 286 287 private static int correctDivisions(int div) { 288 return ((div + 3) / 4) * 4; 289 } 290 291 static TriangleMesh createMesh(int div, float r) { 292 div = correctDivisions(div); 293 294 // NOTE: still create mesh for degenerated sphere 295 final int div2 = div / 2; 296 297 final int nPoints = (div + 1) * (div2 - 1) + 2; 298 final int nTPoints = (div + 1) * (div2 - 1) + div * 2; 299 final int nFaces = div * (div2 - 2) * 2 + div * 2; 300 301 final float rDiv = 1.f / div; 302 303 float points[] = new float[nPoints * 3]; 304 float tPoints[] = new float[nTPoints * 2]; 305 int faces[] = new int[nFaces * 6]; 306 int smoothing[] = new int[nFaces]; 307 308 int pPos = 0, tPos = 0; 309 310 for (int y = 0; y < div2 - 1; ++y) { 311 float va = rDiv * (y + 1 - div2 / 2) * 2 * (float) Math.PI; 312 float sin_va = (float) Math.sin(va); 313 float cos_va = (float) Math.cos(va); 314 315 float ty = 0.5f + sin_va * 0.5f; 316 for (int i = 0; i <= div; ++i) { 317 double a = (i < div) ? rDiv * i * 2 * (float) Math.PI : 0; 318 float hSin = (float) Math.sin(a); 319 float hCos = (float) Math.cos(a); 320 points[pPos + 0] = hSin * cos_va * r; 321 points[pPos + 2] = hCos * cos_va * r; 322 points[pPos + 1] = sin_va * r; 323 tPoints[tPos + 0] = 1- rDiv * i; 324 tPoints[tPos + 1] = ty; 325 pPos += 3; tPos += 2; 326 } 327 } 328 329 points[pPos + 0] = 0; 330 points[pPos + 1] = -r; 331 points[pPos + 2] = 0; 332 points[pPos + 3] = 0; 333 points[pPos + 4] = r; 334 points[pPos + 5] = 0; 335 pPos += 6; 336 337 int pS = (div2 - 1) * (div + 1); 338 339 float textureDelta = 1.f / 256; 340 for (int i = 0; i != div; ++i) { 341 tPoints[tPos + 0] = rDiv * (0.5f + i); 342 tPoints[tPos + 1] = textureDelta; 343 tPos += 2; 344 } 345 346 for (int i = 0; i != div; ++i) { 347 tPoints[tPos + 0] = rDiv * (0.5f + i); 348 tPoints[tPos + 1] = 1 - textureDelta; 349 tPos += 2; 350 } 351 352 int fIndex = 0; 353 for (int y = 0; y < div2 - 2; ++y) { 354 for (int x = 0; x < div; ++x) { 355 int p0 = y * (div + 1) + x; 356 int p1 = p0 + 1; 357 int p2 = p0 + (div + 1); 358 int p3 = p1 + (div + 1); 359 360 // add p0, p1, p2 361 faces[fIndex+0] = p0; 362 faces[fIndex+1] = p0; 363 faces[fIndex+2] = p1; 364 faces[fIndex+3] = p1; 365 faces[fIndex+4] = p2; 366 faces[fIndex+5] = p2; 367 fIndex += 6; 368 369 // add p3, p2, p1 370 faces[fIndex+0] = p3; 371 faces[fIndex+1] = p3; 372 faces[fIndex+2] = p2; 373 faces[fIndex+3] = p2; 374 faces[fIndex+4] = p1; 375 faces[fIndex+5] = p1; 376 fIndex += 6; 377 } 378 } 379 380 int p0 = pS; 381 int tB = pS; 382 for (int x = 0; x != div; ++x) { 383 int p2 = x, p1 = x + 1, t0 = tB + x; 384 faces[fIndex+0] = p0; 385 faces[fIndex+1] = t0; 386 faces[fIndex+2] = p1; 387 faces[fIndex+3] = p1; 388 faces[fIndex+4] = p2; 389 faces[fIndex+5] = p2; 390 fIndex += 6; 391 } 392 393 p0 = p0 + 1; 394 tB = tB + div; 395 int pB = (div2 - 2) * (div + 1); 396 397 for (int x = 0; x != div; ++x) { 398 int p1 = pB + x, p2 = pB + x + 1, t0 = tB + x; 399 faces[fIndex+0] = p0; 400 faces[fIndex+1] = t0; 401 faces[fIndex+2] = p1; 402 faces[fIndex+3] = p1; 403 faces[fIndex+4] = p2; 404 faces[fIndex+5] = p2; 405 fIndex += 6; 406 } 407 408 for (int i = 0; i < nFaces; ++i) { 409 smoothing[i] = 1; 410 } 411 412 TriangleMesh m = new TriangleMesh(); 413 m.getPoints().setAll(points); 414 m.getTexCoords().setAll(tPoints); 415 m.getFaces().setAll(faces); 416 m.getFaceSmoothingGroups().setAll(smoothing); 417 return m; 418 } 419 420 private static int generateKey(float r, int div) { 421 int hash = 5; 422 hash = 23 * hash + Float.floatToIntBits(r); 423 hash = 23 * hash + div; 424 return hash; 425 } 426}