/*
* Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javafx.scene.shape;
import com.sun.javafx.geom.BaseBounds;
import com.sun.javafx.geom.BoxBounds;
import com.sun.javafx.geom.transform.BaseTransform;
import com.sun.javafx.scene.DirtyBits;
import com.sun.javafx.scene.NodeHelper;
import com.sun.javafx.scene.paint.MaterialHelper;
import com.sun.javafx.scene.shape.Shape3DHelper;
import com.sun.javafx.sg.prism.NGShape3D;
import javafx.application.ConditionalFeature;
import javafx.application.Platform;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.WeakChangeListener;
import javafx.scene.Node;
import javafx.scene.paint.Material;
import javafx.scene.paint.PhongMaterial;
import com.sun.javafx.logging.PlatformLogger;
The Shape3D
base class provides definitions of common properties for objects that represent some form of 3D geometric shape. These properties include:
- The
Material
to be applied to the fillable interior of the shape or the outline of the shape (see setMaterial
). - The draw model properties that defines how to render its geometry (see
setDrawMode
). - The face culling properties that defines which face to cull (see
setCullFace
).
Note that this is a conditional feature. See ConditionalFeature.SCENE3D
for more information.
An application should not extend the Shape3D class directly. Doing so may lead to
an UnsupportedOperationException being thrown.
Since: JavaFX 8.0
/**
* The {@code Shape3D} base class provides definitions of common properties for
* objects that represent some form of 3D geometric shape. These properties
* include:
* <ul>
* <li>The {@link Material} to be applied to the fillable interior of the
* shape or the outline of the shape (see {@link #setMaterial}).
* <li>The draw model properties that defines how to render its geometry (see {@link #setDrawMode}).
* <li>The face culling properties that defines which face to cull (see {@link #setCullFace}).
* </ul>
*
* Note that this is a conditional feature. See
* {@link javafx.application.ConditionalFeature#SCENE3D ConditionalFeature.SCENE3D}
* for more information.
*
* <p>
* An application should not extend the Shape3D class directly. Doing so may lead to
* an UnsupportedOperationException being thrown.
* </p>
*
* @since JavaFX 8.0
*/
public abstract class Shape3D extends Node {
static {
// This is used by classes in different packages to get access to
// private and package private methods.
Shape3DHelper.setShape3DAccessor(new Shape3DHelper.Shape3DAccessor() {
@Override
public void doUpdatePeer(Node node) {
((Shape3D) node).doUpdatePeer();
}
@Override
public BaseBounds doComputeGeomBounds(Node node,
BaseBounds bounds, BaseTransform tx) {
return ((Shape3D) node).doComputeGeomBounds(bounds, tx);
}
@Override
public boolean doComputeContains(Node node, double localX, double localY) {
return ((Shape3D) node).doComputeContains(localX, localY);
}
});
}
// NOTE: Need a way to specify shape tessellation resolution, may use metric relate to window resolution
// Will not support dynamic refinement in FX8
// TODO: 3D - May provide user convenient utility to compose images in a single image for shapes such as Box or Cylinder
private static final PhongMaterial DEFAULT_MATERIAL = new PhongMaterial();
protected Shape3D() {
if (!Platform.isSupported(ConditionalFeature.SCENE3D)) {
String logname = Shape3D.class.getName();
PlatformLogger.getLogger(logname).warning("System can't support "
+ "ConditionalFeature.SCENE3D");
}
}
PredefinedMeshManager manager = PredefinedMeshManager.getInstance();
Key key;
Used by the caching mechanism to compare between instances of the same shape.
Each shape implements equals and hashCode using its base parameters.
/**
* Used by the caching mechanism to compare between instances of the same shape.
* Each shape implements equals and hashCode using its base parameters.
*/
abstract static class Key {
@Override
public abstract boolean equals(Object obj);
@Override
public abstract int hashCode();
}
Defines the material this Shape3D
. The default material is null. If Material
is null, a PhongMaterial with a diffuse color of Color.LIGHTGRAY is used for rendering. @defaultValue null
/**
* Defines the material this {@code Shape3D}.
* The default material is null. If {@code Material} is null, a PhongMaterial
* with a diffuse color of Color.LIGHTGRAY is used for rendering.
*
* @defaultValue null
*/
private ObjectProperty<Material> material;
public final void setMaterial(Material value) {
materialProperty().set(value);
}
public final Material getMaterial() {
return material == null ? null : material.get();
}
public final ObjectProperty<Material> materialProperty() {
if (material == null) {
material = new SimpleObjectProperty<Material>(Shape3D.this,
"material") {
private Material old = null;
private final ChangeListener<Boolean> materialChangeListener =
(observable, oldValue, newValue) -> {
if (newValue) {
NodeHelper.markDirty(Shape3D.this, DirtyBits.MATERIAL);
}
};
private final WeakChangeListener<Boolean> weakMaterialChangeListener =
new WeakChangeListener(materialChangeListener);
@Override protected void invalidated() {
if (old != null) {
MaterialHelper.dirtyProperty(old).removeListener(weakMaterialChangeListener);
}
Material newMaterial = get();
if (newMaterial != null) {
MaterialHelper.dirtyProperty(newMaterial).addListener(weakMaterialChangeListener);
}
NodeHelper.markDirty(Shape3D.this, DirtyBits.MATERIAL);
NodeHelper.geomChanged(Shape3D.this);
old = newMaterial;
}
};
}
return material;
}
Defines the draw mode used to render this Shape3D
. DrawMode.LINE
is not available on embedded platforms. If drawMode
is set to DrawMode.LINE
on an embedded platform the default value of DrawMode.FILL
will be used instead. @defaultValue DrawMode.FILL
/**
* Defines the draw mode used to render this {@code Shape3D}.
* {@link DrawMode#LINE} is not available on embedded platforms.
* If {@code drawMode} is set to {@link DrawMode#LINE} on an embedded
* platform the default value of {@link DrawMode#FILL} will be used instead.
*
* @defaultValue {@link DrawMode#FILL}
*/
private ObjectProperty<DrawMode> drawMode;
public final void setDrawMode(DrawMode value) {
drawModeProperty().set(value);
}
public final DrawMode getDrawMode() {
return drawMode == null ? DrawMode.FILL : drawMode.get();
}
public final ObjectProperty<DrawMode> drawModeProperty() {
if (drawMode == null) {
drawMode = new SimpleObjectProperty<DrawMode>(Shape3D.this,
"drawMode", DrawMode.FILL) {
@Override
protected void invalidated() {
NodeHelper.markDirty(Shape3D.this, DirtyBits.NODE_DRAWMODE);
}
};
}
return drawMode;
}
Defines the cullFace this Shape3D
. @defaultValue CullFace.BACK
/**
* Defines the cullFace this {@code Shape3D}.
*
* @defaultValue CullFace.BACK
*/
private ObjectProperty<CullFace> cullFace;
public final void setCullFace(CullFace value) {
cullFaceProperty().set(value);
}
public final CullFace getCullFace() {
return cullFace == null ? CullFace.BACK : cullFace.get();
}
public final ObjectProperty<CullFace> cullFaceProperty() {
if (cullFace == null) {
cullFace = new SimpleObjectProperty<CullFace>(Shape3D.this,
"cullFace", CullFace.BACK) {
@Override
protected void invalidated() {
NodeHelper.markDirty(Shape3D.this, DirtyBits.NODE_CULLFACE);
}
};
}
return cullFace;
}
/*
* Note: This method MUST only be called via its accessor method.
*/
private BaseBounds doComputeGeomBounds(BaseBounds bounds, BaseTransform tx) {
// TODO: 3D - Evaluate this logic
return new BoxBounds(0, 0, 0, 0, 0, 0);
}
/*
* Note: This method MUST only be called via its accessor method.
*/
private boolean doComputeContains(double localX, double localY) {
return false;
}
/*
* Note: This method MUST only be called via its accessor method.
*/
private void doUpdatePeer() {
final NGShape3D peer = NodeHelper.getPeer(this);
if (NodeHelper.isDirty(this, DirtyBits.MATERIAL)) {
Material mat = getMaterial() == null ? DEFAULT_MATERIAL : getMaterial();
MaterialHelper.updatePG(mat); // new material should be updated
peer.setMaterial(MaterialHelper.getNGMaterial(mat));
}
if (NodeHelper.isDirty(this, DirtyBits.NODE_DRAWMODE)) {
peer.setDrawMode(getDrawMode() == null ? DrawMode.FILL : getDrawMode());
}
if (NodeHelper.isDirty(this, DirtyBits.NODE_CULLFACE)) {
peer.setCullFace(getCullFace() == null ? CullFace.BACK : getCullFace());
}
}
}