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.PGBox; 035import com.sun.javafx.sg.PGNode; 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; 042 043/** 044 * The {@code Box} class defines a 3 dimensional box with the specified size. 045 * A {@code Box} is a 3D geometry primitive created with a given depth, width, 046 * and height. It is centered at the origin. 047 * 048 * @since JavaFX 8 049 */ 050public class Box extends Shape3D { 051 052 private TriangleMesh mesh; 053 054 /** 055 * Creates a new instance of {@code Box} of dimension 2 by 2 by 2. 056 */ 057 058 public static final double DEFAULT_SIZE = 2; 059 060 public Box() { 061 this(DEFAULT_SIZE, DEFAULT_SIZE, DEFAULT_SIZE); 062 } 063 064 /** 065 * Creates a new instance of {@code Box} of dimension width by height 066 * by depth. 067 */ 068 public Box(double width, double height, double depth) { 069 setWidth(width); 070 setHeight(height); 071 setDepth(depth); 072 } 073 074 /** 075 * Defines the depth or the Z dimension of the Box. 076 * 077 * @defaultValue 2.0 078 */ 079 private DoubleProperty depth; 080 081 public final void setDepth(double value) { 082 depthProperty().set(value); 083 } 084 085 public final double getDepth() { 086 return depth == null ? 2 : depth.get(); 087 } 088 089 public final DoubleProperty depthProperty() { 090 if (depth == null) { 091 depth = new SimpleDoubleProperty(Box.this, "depth", DEFAULT_SIZE) { 092 @Override 093 public void invalidated() { 094 impl_markDirty(DirtyBits.MESH_GEOM); 095 manager.invalidateBoxMesh(key); 096 key = 0; 097 } 098 }; 099 } 100 return depth; 101 } 102 103 /** 104 * Defines the height or the Y dimension of the Box. 105 * 106 * @defaultValue 2.0 107 */ 108 private DoubleProperty height; 109 110 public final void setHeight(double value) { 111 heightProperty().set(value); 112 } 113 114 public final double getHeight() { 115 return height == null ? 2 : height.get(); 116 } 117 118 public final DoubleProperty heightProperty() { 119 if (height == null) { 120 height = new SimpleDoubleProperty(Box.this, "height", DEFAULT_SIZE) { 121 @Override 122 public void invalidated() { 123 impl_markDirty(DirtyBits.MESH_GEOM); 124 manager.invalidateBoxMesh(key); 125 key = 0; 126 } 127 }; 128 } 129 return height; 130 } 131 132 /** 133 * Defines the width or the X dimension of the Box. 134 * 135 * @defaultValue 2.0 136 */ 137 private DoubleProperty width; 138 139 public final void setWidth(double value) { 140 widthProperty().set(value); 141 } 142 143 public final double getWidth() { 144 return width == null ? 2 : width.get(); 145 } 146 147 public final DoubleProperty widthProperty() { 148 if (width == null) { 149 width = new SimpleDoubleProperty(Box.this, "width", DEFAULT_SIZE) { 150 @Override 151 public void invalidated() { 152 impl_markDirty(DirtyBits.MESH_GEOM); 153 manager.invalidateBoxMesh(key); 154 key = 0; 155 } 156 }; 157 } 158 return width; 159 } 160 /** 161 * @treatAsPrivate implementation detail 162 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 163 */ 164 @Deprecated 165 @Override 166 protected PGNode impl_createPGNode() { 167 return Toolkit.getToolkit().createPGBox(); 168 } 169 170 /** 171 * @treatAsPrivate implementation detail 172 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 173 */ 174 @Deprecated 175 public void impl_updatePG() { 176 super.impl_updatePG(); 177 if (impl_isDirty(DirtyBits.MESH_GEOM)) { 178 PGBox pgBox = (PGBox) impl_getPGNode(); 179 final float w = (float) getWidth(); 180 final float h = (float) getHeight(); 181 final float d = (float) getDepth(); 182 if (w < 0 || h < 0 || d < 0) { 183 pgBox.updateMesh(null); 184 } else { 185 if (key == 0) { 186 key = generateKey(w, h, d); 187 } 188 mesh = manager.getBoxMesh(w, h, d, key); 189 mesh.impl_updatePG(); 190 pgBox.updateMesh(mesh.impl_getPGTriangleMesh()); 191 } 192 } 193 } 194 195 /** 196 * @treatAsPrivate implementation detail 197 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 198 */ 199 @Deprecated 200 @Override 201 public BaseBounds impl_computeGeomBounds(BaseBounds bounds, BaseTransform tx) { 202 final float w = (float) getWidth(); 203 final float h = (float) getHeight(); 204 final float d = (float) getDepth(); 205 206 if (w < 0 || h < 0 || d < 0) { 207 return bounds.makeEmpty(); 208 } 209 210 final float hw = w * 0.5f; 211 final float hh = h * 0.5f; 212 final float hd = d * 0.5f; 213 214 bounds = bounds.deriveWithNewBounds(-hw, -hh, -hd, hw, hh, hd); 215 bounds = tx.transform(bounds, bounds); 216 return bounds; 217 } 218 219 /** 220 * @treatAsPrivate implementation detail 221 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 222 */ 223 @Deprecated 224 @Override 225 protected boolean impl_computeContains(double localX, double localY) { 226 double w = getWidth(); 227 double h = getHeight(); 228 return -w <= localX && localX <= w && 229 -h <= localY && localY <= h; 230 } 231 232 /** 233 * @treatAsPrivate implementation detail 234 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 235 */ 236 @Deprecated 237 @Override 238 protected boolean impl_computeIntersects(PickRay pickRay, PickResultChooser pickResult) { 239 240 final double w = getWidth(); 241 final double h = getHeight(); 242 final double d = getDepth(); 243 final double hWidth = w / 2.0; 244 final double hHeight = h / 2.0; 245 final double hDepth = d / 2.0; 246 final Vec3d dir = pickRay.getDirectionNoClone(); 247 final double invDirX = dir.x == 0.0 ? Double.POSITIVE_INFINITY : (1.0 / dir.x); 248 final double invDirY = dir.y == 0.0 ? Double.POSITIVE_INFINITY : (1.0 / dir.y); 249 final double invDirZ = dir.z == 0.0 ? Double.POSITIVE_INFINITY : (1.0 / dir.z); 250 final Vec3d origin = pickRay.getOriginNoClone(); 251 final double originX = origin.x; 252 final double originY = origin.y; 253 final double originZ = origin.z; 254 final boolean signX = invDirX < 0.0; 255 final boolean signY = invDirY < 0.0; 256 final boolean signZ = invDirZ < 0.0; 257 258 double t0 = Double.NEGATIVE_INFINITY; 259 double t1 = Double.POSITIVE_INFINITY; 260 char side0 = '0'; 261 char side1 = '0'; 262 263 if (Double.isInfinite(invDirX)) { 264 if (-hWidth <= originX && hWidth >= originX) { 265 // move on, we are inside for the whole length 266 } else { 267 return false; 268 } 269 } else { 270 t0 = ((signX ? hWidth : -hWidth) - originX) * invDirX; 271 t1 = ((signX ? -hWidth : hWidth) - originX) * invDirX; 272 side0 = signX ? 'X' : 'x'; 273 side1 = signX ? 'x' : 'X'; 274 } 275 276 if (Double.isInfinite(invDirY)) { 277 if (-hHeight <= originY && hHeight >= originY) { 278 // move on, we are inside for the whole length 279 } else { 280 return false; 281 } 282 } else { 283 final double ty0 = ((signY ? hHeight : -hHeight) - originY) * invDirY; 284 final double ty1 = ((signY ? -hHeight : hHeight) - originY) * invDirY; 285 286 if ((t0 > ty1) || (ty0 > t1)) { 287 return false; 288 } 289 if (ty0 > t0) { 290 side0 = signY ? 'Y' : 'y'; 291 t0 = ty0; 292 } 293 if (ty1 < t1) { 294 side1 = signY ? 'y' : 'Y'; 295 t1 = ty1; 296 } 297 } 298 299 if (Double.isInfinite(invDirZ)) { 300 if (-hDepth <= originZ && hDepth >= originZ) { 301 // move on, we are inside for the whole length 302 } else { 303 return false; 304 } 305 } else { 306 double tz0 = ((signZ ? hDepth : -hDepth) - originZ) * invDirZ; 307 double tz1 = ((signZ ? -hDepth : hDepth) - originZ) * invDirZ; 308 309 if ((t0 > tz1) || (tz0 > t1)) { 310 return false; 311 } 312 if (tz0 > t0) { 313 side0 = signZ ? 'Z' : 'z'; 314 t0 = tz0; 315 } 316 if (tz1 < t1) { 317 side1 = signZ ? 'z' : 'Z'; 318 t1 = tz1; 319 } 320 } 321 322 char side = side0; 323 double t = t0; 324 final CullFace cullFace = getCullFace(); 325 final double minDistance = pickRay.getNearClip(); 326 final double maxDistance = pickRay.getFarClip(); 327 328 if (t0 > maxDistance) { 329 return false; 330 } 331 if (t0 < minDistance || cullFace == CullFace.FRONT) { 332 if (t1 >= minDistance && t1 <= maxDistance && cullFace != CullFace.BACK) { 333 side = side1; 334 t = t1; 335 } else { 336 return false; 337 } 338 } 339 340 if (Double.isInfinite(t) || Double.isNaN(t)) { 341 // We've got a nonsense pick ray or box size. 342 return false; 343 } 344 345 if (pickResult != null && pickResult.isCloser(t)) { 346 Point3D point = PickResultChooser.computePoint(pickRay, t); 347 348 Point2D txtCoords = null; 349 350 switch (side) { 351 case 'x': // left 352 txtCoords = new Point2D( 353 0.5 - point.getZ() / d, 354 0.5 + point.getY() / h); 355 break; 356 case 'X': // right 357 txtCoords = new Point2D( 358 0.5 + point.getZ() / d, 359 0.5 + point.getY() / h); 360 break; 361 case 'y': // top 362 txtCoords = new Point2D( 363 0.5 + point.getX() / w, 364 0.5 - point.getZ() / d); 365 break; 366 case 'Y': // bottom 367 txtCoords = new Point2D( 368 0.5 + point.getX() / w, 369 0.5 + point.getZ() / d); 370 break; 371 case 'z': // front 372 txtCoords = new Point2D( 373 0.5 + point.getX() / w, 374 0.5 + point.getY() / h); 375 break; 376 case 'Z': // back 377 txtCoords = new Point2D( 378 0.5 - point.getX() / w, 379 0.5 + point.getY() / h); 380 break; 381 default: 382 // No hit with any of the planes. We must have had a zero 383 // pick ray direction vector. Should never happen. 384 return false; 385 } 386 387 pickResult.offer(this, t, PickResult.FACE_UNDEFINED, point, txtCoords); 388 } 389 390 return true; 391 } 392 393 static TriangleMesh createMesh(float w, float h, float d) { 394 395 // NOTE: still create mesh for degenerated box 396 float hw = w / 2f; 397 float hh = h / 2f; 398 float hd = d / 2f; 399 400 float points[] = { 401 -hw, -hh, -hd, 402 hw, -hh, -hd, 403 hw, hh, -hd, 404 -hw, hh, -hd, 405 -hw, -hh, hd, 406 hw, -hh, hd, 407 hw, hh, hd, 408 -hw, hh, hd}; 409 410 float texCoords[] = {0, 0, 1, 0, 1, 1, 0, 1}; 411 412 int faceSmoothingGroups[] = { 413 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4 414 }; 415 416 int faces[] = { 417 0, 0, 2, 2, 1, 1, 418 2, 2, 0, 0, 3, 3, 419 1, 0, 6, 2, 5, 1, 420 6, 2, 1, 0, 2, 3, 421 5, 0, 7, 2, 4, 1, 422 7, 2, 5, 0, 6, 3, 423 4, 0, 3, 2, 0, 1, 424 3, 2, 4, 0, 7, 3, 425 3, 0, 6, 2, 2, 1, 426 6, 2, 3, 0, 7, 3, 427 4, 0, 1, 2, 5, 1, 428 1, 2, 4, 0, 0, 3, 429 }; 430 431 TriangleMesh mesh = new TriangleMesh(); 432 mesh.getPoints().setAll(points); 433 mesh.getTexCoords().setAll(texCoords); 434 mesh.getFaces().setAll(faces); 435 mesh.getFaceSmoothingGroups().setAll(faceSmoothingGroups); 436 437 return mesh; 438 } 439 440 private static int generateKey(float w, float h, float d) { 441 int hash = 3; 442 hash = 97 * hash + Float.floatToIntBits(w); 443 hash = 97 * hash + Float.floatToIntBits(h); 444 hash = 97 * hash + Float.floatToIntBits(d); 445 return hash; 446 } 447}