JavafX 8 3D Z Order. Overlapping shape behaviour is wrong. - java

I have a JavaFX 3D scene with a bunch of boxes and spheres added at random locations. It seems like the depth order is all wrong and I'm not sure why. I have tried to use
myNode.setDepthTest(DepthTest.ENABLE) but that doesn't seem to help. I've attached an application which should demonstrate the problem.
Any idea what I might be doing wrong here? Any help much appreciated.
import javafx.application.Application;
import javafx.application.ConditionalFeature;
import javafx.application.Platform;
import javafx.event.EventHandler;
import javafx.scene.DepthTest;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.SubScene;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Box;
import javafx.scene.shape.Shape3D;
import javafx.scene.shape.Sphere;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Translate;
import javafx.stage.Stage;
public class Array3DTest extends Application {
double mousePosX;
double mousePosY;
double mouseOldX;
double mouseOldY;
double mouseDeltaX;
double mouseDeltaY;
/**
* This is the group which rotates
*/
Group root3D;
/**
* The camnera to
*/
private Rotate rotateY;
private Rotate rotateX;
private Translate translate;
public Array3DTest( ){
}
public Group createScene(){
// Create and position camera
PerspectiveCamera camera = new PerspectiveCamera(true);
camera.setFarClip(15000);
camera.setNearClip(0.1);
camera.setDepthTest(DepthTest.ENABLE);
camera.getTransforms().addAll(
rotateY=new Rotate(0, Rotate.Y_AXIS),
rotateX=new Rotate(0, Rotate.X_AXIS),
translate=new Translate(250, 250, -1000));
root3D=new Group();
root3D.getChildren().add(camera);
root3D.setDepthTest(DepthTest.ENABLE);
final PhongMaterial redMaterial = new PhongMaterial();
redMaterial.setSpecularColor(Color.ORANGE);
redMaterial.setDiffuseColor(Color.RED);
for (int i=0; i<50; i++){
Shape3D mySphere;
if (i%2==0) mySphere = new Box(100,100, 100);
else mySphere= new Sphere(30);
mySphere.setTranslateX(Math.random()*500);
mySphere.setTranslateY(Math.random()*500);
mySphere.setTranslateZ(Math.random()*200);
mySphere.setMaterial(redMaterial);
mySphere.setDepthTest(DepthTest.ENABLE);
root3D.getChildren().add(mySphere);
}
// Use a SubScene to mix 3D and 3D stuff.
SubScene subScene = new SubScene(root3D, 500,500);
subScene.setFill(Color.WHITE);
subScene.setCamera(camera);
subScene.setDepthTest(DepthTest.ENABLE);
Group group = new Group();
group.getChildren().add(subScene);
handleMouse(subScene);
return group;
}
private void handleMouse(SubScene scene) {
scene.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent me) {
mousePosX = me.getSceneX();
mousePosY = me.getSceneY();
mouseOldX = me.getSceneX();
mouseOldY = me.getSceneY();
}
});
scene.setOnScroll(new EventHandler<ScrollEvent>() {
#Override public void handle(ScrollEvent event) {
System.out.println("Scroll Event: "+event.getDeltaX() + " "+event.getDeltaY());
translate.setZ(translate.getZ()+ event.getDeltaY() *0.001*translate.getZ()); // +
}
});
scene.setOnMouseDragged(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent me) {
mouseOldX = mousePosX;
mouseOldY = mousePosY;
mousePosX = me.getSceneX();
mousePosY = me.getSceneY();
mouseDeltaX = (mousePosX - mouseOldX);
mouseDeltaY = (mousePosY - mouseOldY);
double modifier = 1.0;
double modifierFactor = 0.1;
if (me.isControlDown()) {
modifier = 0.1;
}
if (me.isShiftDown()) {
modifier = 10.0;
}
if (me.isPrimaryButtonDown()) {
rotateY.setAngle(rotateY.getAngle() + mouseDeltaX * modifierFactor * modifier * 2.0); // +
rotateX.setAngle(rotateX.getAngle() - mouseDeltaY * modifierFactor * modifier * 2.0); // -
}
if (me.isSecondaryButtonDown()) {
translate.setX(translate.getX() -mouseDeltaX * modifierFactor * modifier * 5);
translate.setY(translate.getY() - mouseDeltaY * modifierFactor * modifier * 5); // +
}
}
});
}
#Override
public void start(Stage primaryStage) throws Exception {
System.out.println(
"3D supported? " +
Platform.isSupported(ConditionalFeature.SCENE3D)
);
Group group=createScene();
primaryStage.setResizable(false);
Scene scene = new Scene(group, 500,500, true);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

Since you are using a SubScene object, it's convenient that you enable deep buffer like you already do for the Scene. Also, enabling anti-aliasing will help too.
According to JavaDoc:
The depthBuffer and antiAliasing flags are conditional features. With the respective default values of: false and SceneAntialiasing.DISABLED.
So just use the other constructor for SubScene:
SubScene subScene = new SubScene(root3D, 500, 500, true, SceneAntialiasing.BALANCED);
and you'll notice the difference.

Related

How to set axis (triad) at fixed position on screen in JavaFX?

How to set axis (triad) at fixed position on screen in JavaFX? I am currently developing one application in which I want to show axis (triad) at fixed position on my screen (i.e. bottom-left corner). I want rotation of axis should be in sync with the main object. Zoom and Translate operation should not be applied to axis.
But I am facing some difficulties to show axis at specific position on screen.
I have used screenToLocal method to get fixed position in scene but it only returns Point2D object which is not helpful to set 3D translate values.
Can you please give me solution for this problem?
Source code based on this example is as below:
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.geometry.BoundingBox;
import javafx.geometry.Bounds;
import javafx.geometry.Point2D;
import javafx.geometry.Point3D;
import javafx.scene.DepthTest;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.paint.Color;
import javafx.scene.paint.Material;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Box;
import javafx.scene.shape.Cylinder;
import javafx.scene.shape.DrawMode;
import javafx.scene.shape.MeshView;
import javafx.scene.shape.Sphere;
import javafx.scene.shape.TriangleMesh;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Transform;
import javafx.scene.transform.Translate;
import javafx.stage.Stage;
public class TrafoTest extends Application {
final Group root = new Group();
Group axis = new Group();
final XformWorld world = new XformWorld();
final PerspectiveCamera camera = new PerspectiveCamera(true);
final XformCamera cameraXform = new XformCamera();
final XformCamera cameraXform2 = new XformCamera();
final XformCamera cameraXform3 = new XformCamera();
private static final double CAMERA_INITIAL_DISTANCE = -1000;
private static final double CAMERA_NEAR_CLIP = 0.1;
private static final double CAMERA_FAR_CLIP = 10000.0;
private static final double MOUSE_SPEED = 1;
private static final double ROTATION_SPEED = 4.0;
private static final double TRACK_SPEED = 0.02;
double mousePosX, mousePosY, mouseOldX, mouseOldY, mouseDeltaX, mouseDeltaY;
double mouseFactorX, mouseFactorY;
Stage stage;
#Override
public void start(Stage primaryStage) {
root.getChildren().add(world);
root.setDepthTest(DepthTest.ENABLE);
buildCamera();
buildBodySystem();
Scene scene = new Scene(root, 800, 600, true);
scene.setFill(Color.GREY);
handleMouse(scene);
this.stage = primaryStage;
primaryStage.setTitle("TrafoTest");
primaryStage.setScene(scene);
primaryStage.show();
scene.setCamera(camera);
mouseFactorX = 180.0 / scene.getWidth();
mouseFactorY = 180.0 / scene.getHeight();
}
private void buildCamera() {
root.getChildren().add(cameraXform);
cameraXform.getChildren().add(cameraXform2);
cameraXform2.getChildren().add(cameraXform3);
cameraXform3.getChildren().add(camera);
camera.setNearClip(CAMERA_NEAR_CLIP);
camera.setFarClip(CAMERA_FAR_CLIP);
camera.setTranslateZ(CAMERA_INITIAL_DISTANCE);
}
private void buildBodySystem() {
PhongMaterial whiteMaterial = new PhongMaterial();
whiteMaterial.setDiffuseColor(Color.WHITE);
whiteMaterial.setSpecularColor(Color.LIGHTBLUE);
Box box = new Box(400, 200, 100);
box.setMaterial(whiteMaterial);
box.setDrawMode(DrawMode.LINE);
PhongMaterial redMaterial = new PhongMaterial();
redMaterial.setDiffuseColor(Color.DARKRED);
redMaterial.setSpecularColor(Color.RED);
Sphere sphere = new Sphere(5);
sphere.setMaterial(redMaterial);
sphere.setTranslateX(200.0);
sphere.setTranslateY(-100.0);
sphere.setTranslateZ(-50.0);
axis = drawReferenceFrame();
world.getChildren().addAll(axis);
world.getChildren().add(box);
world.getChildren().addAll(sphere);
}
private void handleMouse(Scene scene) {
scene.setOnMousePressed((MouseEvent me) -> {
mousePosX = me.getSceneX();
mousePosY = me.getSceneY();
mouseOldX = me.getSceneX();
mouseOldY = me.getSceneY();
});
scene.setOnMouseDragged((MouseEvent me) -> {
mouseOldX = mousePosX;
mouseOldY = mousePosY;
mousePosX = me.getSceneX();
mousePosY = me.getSceneY();
mouseDeltaX = (mousePosX - mouseOldX);
mouseDeltaY = (mousePosY - mouseOldY);
if (me.isPrimaryButtonDown()) {
cameraXform.ry(mouseDeltaX * 180.0 / scene.getWidth());
cameraXform.rx(-mouseDeltaY * 180.0 / scene.getHeight());
BoundingBox point = (BoundingBox) root.screenToLocal(new BoundingBox(root.getLayoutX()+350, root.getLayoutY()+650, 0, 0,0, 20));
System.out.println(point);
axis.setTranslateX(point.getMinX());
axis.setTranslateY(point.getMinY());
axis.setTranslateZ(point.getMinZ());
} else if (me.isSecondaryButtonDown()) {
cameraXform2.setTx((cameraXform2.t.getX() + (-mouseDeltaX)*MOUSE_SPEED*TRACK_SPEED));
cameraXform2.setTy((cameraXform2.t.getY() + (-mouseDeltaY)*MOUSE_SPEED*TRACK_SPEED));
camera.setTranslateZ(camera.getTranslateZ() + mouseDeltaY);
}
});
scene.setOnScroll(new EventHandler<ScrollEvent>() {
#Override
public void handle(ScrollEvent event) {
double z = cameraXform3.getTranslateZ();
double newZ = z - event.getDeltaY() * MOUSE_SPEED * 0.05;
cameraXform3.setTranslateZ(newZ);
}
});
}
public static void main(String[] args) {
launch(args);
}
private Group drawReferenceFrame(){
Group G1= new Group();
Cylinder CX = new Cylinder(2,25);
Cylinder CY = new Cylinder(2,25);
Cylinder CZ = new Cylinder(2,25);
Sphere S = new Sphere(4);
Material mat =new PhongMaterial(Color.WHITE);
PhongMaterial Xmat = new PhongMaterial();
Xmat.setDiffuseColor(Color.GREEN);
PhongMaterial Ymat = new PhongMaterial();
Ymat.setDiffuseColor(Color.BLUE);
PhongMaterial Zmat = new PhongMaterial();
Zmat.setDiffuseColor(Color.RED);
S.setMaterial(Zmat);
CY.setMaterial(mat);
// CY.setRotationAxis(Rotate.X_AXIS);
// CY.setRotate(90);
CY.setTranslateY(-12.5);
CX.setMaterial(mat);
CX.setTranslateX(15);
CX.setRotationAxis(Rotate.Z_AXIS);
CX.setRotate(90);
CZ.setMaterial(mat);
CZ.setRotationAxis(Rotate.X_AXIS);
CZ.setRotate(90);
CZ.setTranslateZ(-12.5);
G1.getChildren().addAll(CX,CY,CZ,S);
TriangleMesh coneMeshY = createCone(3.5f, 7.5f);
TriangleMesh coneMeshX = createCone(3.5f, 7.5f);
TriangleMesh coneMeshZ = createCone(3.5f, 7.5f);
MeshView yCone = new MeshView(coneMeshY);
MeshView xCone = new MeshView(coneMeshX);
MeshView zCone = new MeshView(coneMeshZ);
yCone.setMaterial(Ymat);
yCone.setTranslateY(-32.5);
yCone.setDrawMode(DrawMode.FILL);
xCone.setMaterial(Xmat);
xCone.setTranslateY(-3.75);
xCone.setRotationAxis(Rotate.Z_AXIS);
xCone.setRotate(90);
xCone.setTranslateX(28.5);
xCone.setDrawMode(DrawMode.FILL);
zCone.setRotationAxis(Rotate.X_AXIS);
zCone.setTranslateY(-3.75);
zCone.setRotate(90);
zCone.setTranslateZ(-28.5);
zCone.setDrawMode(DrawMode.FILL);
zCone.setMaterial(Zmat);
G1.getChildren().addAll(xCone,yCone,zCone);
// G1.setScale(0.45);
return G1;
}
private TriangleMesh createCone( float radius, float height) {
int divisions=500;
TriangleMesh mesh = new TriangleMesh();
mesh.getPoints().addAll(0,0,0);
double segment_angle = 2.0 * Math.PI / divisions;
float x, z;
double angle;
double halfCount = (Math.PI / 2 - Math.PI / (divisions / 2));
for(int i=divisions+1;--i >= 0; ) {
angle = segment_angle * i;
x = (float)(radius * Math.cos(angle - halfCount));
z = (float)(radius * Math.sin(angle - halfCount));
mesh.getPoints().addAll(x,height,z);
}
mesh.getPoints().addAll(0,height,0);
mesh.getTexCoords().addAll(0,0);
for(int i=1;i<=divisions;i++) {
mesh.getFaces().addAll(
0,0,i+1,0,i,0, //COunter clock wise
divisions+2,0,i,0,i+1,0 // Clock wise
);
}
return mesh;
}
}
class XformWorld extends Group {
final Translate t = new Translate(0.0, 0.0, 0.0);
final Rotate rx = new Rotate(0, 0, 0, 0, Rotate.X_AXIS);
final Rotate ry = new Rotate(0, 0, 0, 0, Rotate.Y_AXIS);
final Rotate rz = new Rotate(0, 0, 0, 0, Rotate.Z_AXIS);
public XformWorld() {
super();
this.getTransforms().addAll(t, rx, ry, rz);
}
}
class XformCamera extends Group {
Point3D px = new Point3D(1.0, 0.0, 0.0);
Point3D py = new Point3D(0.0, 1.0, 0.0);
Rotate r;
Transform tx = new Rotate();
Translate t = new Translate();
public XformCamera() {
super();
}
public void rx(double angle) {
r = new Rotate(angle, px);
this.tx = tx.createConcatenation(r);
this.getTransforms().clear();
this.getTransforms().addAll(tx);
}
public void ry(double angle) {
r = new Rotate(angle, py);
this.tx = tx.createConcatenation(r);
this.getTransforms().clear();
this.getTransforms().addAll(tx);
}
public void setTx(double x) {
t.setX(x);
this.getTransforms().clear();
this.getTransforms().addAll(t);
}
public void setTy(double y) {
t.setY(y);
this.getTransforms().clear();
this.getTransforms().addAll(t);
}
}
In above code, first I have translated the axis at left-bottom corner of my screen, but after rotating the main object (ie. BOX and SPHERE), axis is also translated.I wanted to rotate main object and axis about their own origins.
If I tried to zoom in (changed camera Z position), then also I axis location changed with respect to screen.
In above cases, I wanted to display my axis at left-bottom corner of the screen and Rotation of the axis should be in sync with the main object.
I wanted the axis to be fixed at left, bottom corner of the screen and axis should rotate about it's own origin.
This variation leaves the axes at the origin, moves the box to
P = (size / 2, -size / 2, -size / 2)
relative to the axes, and pans the camera toward the bottom, center of the screen. Uncomment the call to camera.setTranslateX() to pan left. Moving the mouse causes the group to rotate about the axes' origin. Press shift to rotate about z, and use the mouse wheel to dolly the camera.
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Box;
import javafx.scene.transform.Rotate;
import javafx.stage.Stage;
/**
* #see http://stackoverflow.com/a/37734966/230513
* #see http://stackoverflow.com/a/37714700/230513
* #see http://stackoverflow.com/a/37685167/230513
* #see http://stackoverflow.com/a/37370840/230513
*/
public class TriadBox extends Application {
private static final double SIZE = 300;
private final Content content = Content.create(SIZE);
private static final class Content {
private static final double WIDTH = 3;
private final Group group = new Group();
private final Rotate rx = new Rotate(0, Rotate.X_AXIS);
private final Rotate ry = new Rotate(0, Rotate.Y_AXIS);
private final Rotate rz = new Rotate(0, Rotate.Z_AXIS);
private final Box xAxis;
private final Box yAxis;
private final Box zAxis;
private final Box box;
private static Content create(double size) {
Content c = new Content(size);
c.group.getChildren().addAll(c.box, c.xAxis, c.yAxis, c.zAxis);
c.group.getTransforms().addAll(c.rz, c.ry, c.rx);
return c;
}
private Content(double size) {
xAxis = createBox(size * 2, WIDTH, WIDTH);
yAxis = createBox(WIDTH, size * 2, WIDTH);
zAxis = createBox(WIDTH, WIDTH, size * 2);
double edge = 3 * size / 4;
box = new Box(edge, edge, edge);
box.setMaterial(new PhongMaterial(Color.CORAL));
box.setTranslateX(size / 2);
box.setTranslateY(-size / 2);
box.setTranslateZ(-size / 2);
}
private Box createBox(double w, double h, double d) {
Box b = new Box(w, h, d);
b.setMaterial(new PhongMaterial(Color.AQUA));
return b;
}
}
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setTitle("JavaFX 3D");
Scene scene = new Scene(content.group, SIZE * 2, SIZE * 2, true);
primaryStage.setScene(scene);
scene.setFill(Color.BLACK);
scene.setOnMouseMoved((final MouseEvent e) -> {
if (e.isShiftDown()) {
content.rz.setAngle(e.getSceneX() * 360 / scene.getWidth());
} else {
content.rx.setAngle(e.getSceneY() * 360 / scene.getHeight());
content.ry.setAngle(e.getSceneX() * 360 / scene.getWidth());
}
});
PerspectiveCamera camera = new PerspectiveCamera(true);
camera.setFarClip(SIZE * 6);
//camera.setTranslateX(SIZE / 2);
camera.setTranslateY(-SIZE / 2);
camera.setTranslateZ(-4.5 * SIZE);
scene.setCamera(camera);
scene.setOnScroll((final ScrollEvent e) -> {
camera.setTranslateZ(camera.getTranslateZ() + e.getDeltaY());
});
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
It may be easier to have separate groups for the rotatable content versus the fixed triad. In the example below, cube contains a single child, box, and three transforms, rx, ry and rz. In contrast, axes contains three axes with no transforms. The axes are translated to the lower left corner as a group, leaving the mouse handler free to manipulate the Rotate instances belonging to cube.
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Box;
import javafx.scene.transform.Rotate;
import javafx.stage.Stage;
/**
* #see http://stackoverflow.com/a/37685167/230513
* #see http://stackoverflow.com/a/37370840/230513
*/
public class TriadBox extends Application {
private static final double SIZE = 300;
private final Content content = Content.create(SIZE);
private static final class Content {
private static final double WIDTH = 3;
private final Group group = new Group();
private final Group cube = new Group();
private final Group axes = new Group();
private final Rotate rx = new Rotate(0, Rotate.X_AXIS);
private final Rotate ry = new Rotate(0, Rotate.Y_AXIS);
private final Rotate rz = new Rotate(0, Rotate.Z_AXIS);
private final Box xAxis;
private final Box yAxis;
private final Box zAxis;
private final Box box;
private static Content create(double size) {
Content c = new Content(size);
c.cube.getChildren().add(c.box);
c.cube.getTransforms().addAll(c.rz, c.ry, c.rx);
c.axes.getChildren().addAll(c.xAxis, c.yAxis, c.zAxis);
c.axes.setTranslateX(-size / 2);
c.axes.setTranslateY(size / 2);
c.group.getChildren().addAll(c.cube, c.axes);
return c;
}
private Content(double size) {
xAxis = createBox(size*2, WIDTH, WIDTH);
yAxis = createBox(WIDTH, size*2, WIDTH);
zAxis = createBox(WIDTH, WIDTH, size*2);
double edge = 3 * SIZE / 4;
box = new Box(edge, edge, edge);
box.setMaterial(new PhongMaterial(Color.CORAL));
}
private Box createBox(double w, double h, double d) {
Box b = new Box(w, h, d);
b.setMaterial(new PhongMaterial(Color.AQUA));
return b;
}
}
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setTitle("JavaFX 3D");
Scene scene = new Scene(content.group, SIZE * 2, SIZE * 2, true);
primaryStage.setScene(scene);
scene.setFill(Color.BLACK);
scene.setOnMouseMoved((final MouseEvent e) -> {
content.rx.setAngle(e.getSceneY() * 360 / scene.getHeight());
content.ry.setAngle(e.getSceneX() * 360 / scene.getWidth());
});
PerspectiveCamera camera = new PerspectiveCamera(true);
camera.setFarClip(SIZE * 6);
camera.setTranslateZ(-3 * SIZE);
scene.setCamera(camera);
scene.setOnScroll((final ScrollEvent e) -> {
camera.setTranslateZ(camera.getTranslateZ() + e.getDeltaY());
});
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
How to set axis (triad) at fixed position…I also wanted axis rotation in sync with box.
In this example, the Content.create() factory method translates the triad of axes is to the point
P = (-size / 2, size / 2, 0)
Because its separate group has no transforms, the triad's origin "sticks" to that point in the space viewed by the camera.
In contrast, the example below translates the triad of axes is to the point
P = (-size / 2, size / 2, size / 2)
Because the axes are in the same group as box, they undergo the same rotations.
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Box;
import javafx.scene.transform.Rotate;
import javafx.stage.Stage;
/**
* #see https://stackoverflow.com/a/37714700/230513
* #see https://stackoverflow.com/a/37685167/230513
* #see https://stackoverflow.com/a/37370840/230513
*/
public class TriadBox extends Application {
private static final double SIZE = 300;
private final Content content = Content.create(SIZE);
private static final class Content {
private static final double WIDTH = 3;
private final Group group = new Group();
private final Rotate rx = new Rotate(0, Rotate.X_AXIS);
private final Rotate ry = new Rotate(0, Rotate.Y_AXIS);
private final Rotate rz = new Rotate(0, Rotate.Z_AXIS);
private final Box xAxis;
private final Box yAxis;
private final Box zAxis;
private final Box box;
private static Content create(double size) {
Content c = new Content(size);
c.group.getChildren().addAll(c.box, c.xAxis, c.yAxis, c.zAxis);
c.group.getTransforms().addAll(c.rz, c.ry, c.rx);
return c;
}
private Content(double size) {
xAxis = createBox(size * 2, WIDTH, WIDTH);
xAxis.setTranslateY(size / 2);
xAxis.setTranslateZ(size / 2);
yAxis = createBox(WIDTH, size * 2, WIDTH);
yAxis.setTranslateX(-size / 2);
yAxis.setTranslateZ(size / 2);
zAxis = createBox(WIDTH, WIDTH, size * 2);
zAxis.setTranslateX(-size / 2);
zAxis.setTranslateY(size / 2);
double edge = 3 * size / 4;
box = new Box(edge, edge, edge);
box.setMaterial(new PhongMaterial(Color.CORAL));
}
private Box createBox(double w, double h, double d) {
Box b = new Box(w, h, d);
b.setMaterial(new PhongMaterial(Color.AQUA));
return b;
}
}
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setTitle("JavaFX 3D");
Scene scene = new Scene(content.group, SIZE * 2, SIZE * 2, true);
primaryStage.setScene(scene);
scene.setFill(Color.BLACK);
scene.setOnMouseMoved((final MouseEvent e) -> {
if (e.isShiftDown()) {
content.rz.setAngle(e.getSceneX() * 360 / scene.getWidth());
} else {
content.rx.setAngle(e.getSceneY() * 360 / scene.getHeight());
content.ry.setAngle(e.getSceneX() * 360 / scene.getWidth());
}
});
PerspectiveCamera camera = new PerspectiveCamera(true);
camera.setFarClip(SIZE * 6);
camera.setTranslateZ(-3.5 * SIZE);
scene.setCamera(camera);
scene.setOnScroll((final ScrollEvent e) -> {
camera.setTranslateZ(camera.getTranslateZ() + e.getDeltaY());
});
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I wanted to rotate both main object and axis about their own center.
For that, I would return to the original example that has separate groups for the content and triad. The variation below adds the same threes Rotate transforms to both cube and axes. As a result, the setOnMouseMoved() implementation causes the the two to rotate in synchrony. Mouse over the stage to see the effect.
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Box;
import javafx.scene.transform.Rotate;
import javafx.stage.Stage;
/**
* #see https://stackoverflow.com/a/37743539/230513
* #see https://stackoverflow.com/a/37370840/230513
*/
public class TriadBox extends Application {
private static final double SIZE = 300;
private final Content content = Content.create(SIZE);
private static final class Content {
private static final double WIDTH = 3;
private final Group group = new Group();
private final Group cube = new Group();
private final Group axes = new Group();
private final Rotate rx = new Rotate(0, Rotate.X_AXIS);
private final Rotate ry = new Rotate(0, Rotate.Y_AXIS);
private final Rotate rz = new Rotate(0, Rotate.Z_AXIS);
private final Box xAxis;
private final Box yAxis;
private final Box zAxis;
private final Box box;
private static Content create(double size) {
Content c = new Content(size);
c.cube.getChildren().add(c.box);
c.cube.getTransforms().addAll(c.rz, c.ry, c.rx);
c.cube.setTranslateX(size / 2);
c.axes.getChildren().addAll(c.xAxis, c.yAxis, c.zAxis);
c.axes.getTransforms().addAll(c.rz, c.ry, c.rx);
c.axes.setTranslateX(-size / 2);
c.group.getChildren().addAll(c.cube, c.axes);
return c;
}
private Content(double size) {
xAxis = createBox(size, WIDTH, WIDTH);
yAxis = createBox(WIDTH, size, WIDTH);
zAxis = createBox(WIDTH, WIDTH, size);
double edge = 3 * size / 5;
box = new Box(edge, edge, edge);
box.setMaterial(new PhongMaterial(Color.CORAL));
}
private Box createBox(double w, double h, double d) {
Box b = new Box(w, h, d);
b.setMaterial(new PhongMaterial(Color.AQUA));
return b;
}
}
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setTitle("JavaFX 3D");
Scene scene = new Scene(content.group, SIZE * 2, SIZE * 2, true);
primaryStage.setScene(scene);
scene.setFill(Color.BLACK);
scene.setOnMouseMoved((final MouseEvent e) -> {
if (e.isShiftDown()) {
content.rz.setAngle(e.getSceneX() * 360 / scene.getWidth());
} else {
content.rx.setAngle(e.getSceneY() * 360 / scene.getHeight());
content.ry.setAngle(e.getSceneX() * 360 / scene.getWidth());
}
});
PerspectiveCamera camera = new PerspectiveCamera(true);
camera.setFarClip(SIZE * 6);
camera.setTranslateZ(-4 * SIZE);
scene.setCamera(camera);
scene.setOnScroll((final ScrollEvent e) -> {
camera.setTranslateZ(camera.getTranslateZ() + e.getDeltaY());
});
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
In order to see what you were seeing, I made these changes to TrafoTest and set the scene to black.
I then modified this example to make the scene look more like yours, adding a sphere on one corner and shifting the axes to the corner opposite. I substituted a variation of your custom rotation Group and your mouse handler from here. The new group also rotates around z when isShiftDown(). Also note the slightly simpler camera dolly in the setOnScroll() handler. In particular, note that the axes can be translated independently as a group. Try this variation in Content.create().
c.axes.setTranslateX(c.axes.getTranslateX() - 20);
c.axes.setTranslateY(c.axes.getTranslateY() + 20);
c.axes.setTranslateZ(c.axes.getTranslateZ() + 20);
import javafx.application.Application;
import javafx.geometry.Point3D;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Box;
import javafx.scene.shape.DrawMode;
import javafx.scene.shape.Sphere;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Transform;
import javafx.stage.Stage;
/**
* #see https://stackoverflow.com/a/37755149/230513
* #see https://stackoverflow.com/a/37743539/230513
* #see https://stackoverflow.com/a/37370840/230513
*/
public class TriadBox extends Application {
private static final double SIZE = 300;
private final Content content = Content.create(SIZE);
private double mousePosX, mousePosY, mouseOldX, mouseOldY, mouseDeltaX, mouseDeltaY;
private static final class Content {
private static final double WIDTH = 3;
private final Xform group = new Xform();
private final Group cube = new Group();
private final Group axes = new Group();
private final Box xAxis;
private final Box yAxis;
private final Box zAxis;
private final Box box;
private final Sphere sphere;
private static Content create(double size) {
Content c = new Content(size);
c.cube.getChildren().addAll(c.box, c.sphere);
c.axes.getChildren().addAll(c.xAxis, c.yAxis, c.zAxis);
c.group.getChildren().addAll(c.cube, c.axes);
return c;
}
private Content(double size) {
double edge = 3 * size / 4;
xAxis = createBox(edge, WIDTH, WIDTH, edge);
yAxis = createBox(WIDTH, edge / 2, WIDTH, edge);
zAxis = createBox(WIDTH, WIDTH, edge / 4, edge);
box = new Box(edge, edge / 2, edge / 4);
box.setDrawMode(DrawMode.LINE);
sphere = new Sphere(8);
PhongMaterial redMaterial = new PhongMaterial();
redMaterial.setDiffuseColor(Color.CORAL.darker());
redMaterial.setSpecularColor(Color.CORAL);
sphere.setMaterial(redMaterial);
sphere.setTranslateX(edge / 2);
sphere.setTranslateY(-edge / 4);
sphere.setTranslateZ(-edge / 8);
}
private Box createBox(double w, double h, double d, double edge) {
Box b = new Box(w, h, d);
b.setMaterial(new PhongMaterial(Color.AQUA));
b.setTranslateX(-edge / 2 + w / 2);
b.setTranslateY(edge / 4 - h / 2);
b.setTranslateZ(edge / 8 - d / 2);
return b;
}
}
private static class Xform extends Group {
private final Point3D px = new Point3D(1.0, 0.0, 0.0);
private final Point3D py = new Point3D(0.0, 1.0, 0.0);
private Rotate r;
private Transform t = new Rotate();
public void rx(double angle) {
r = new Rotate(angle, px);
this.t = t.createConcatenation(r);
this.getTransforms().clear();
this.getTransforms().addAll(t);
}
public void ry(double angle) {
r = new Rotate(angle, py);
this.t = t.createConcatenation(r);
this.getTransforms().clear();
this.getTransforms().addAll(t);
}
public void rz(double angle) {
r = new Rotate(angle);
this.t = t.createConcatenation(r);
this.getTransforms().clear();
this.getTransforms().addAll(t);
}
}
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setTitle("JavaFX 3D");
Scene scene = new Scene(content.group, SIZE * 2, SIZE * 2, true);
primaryStage.setScene(scene);
scene.setFill(Color.BLACK);
PerspectiveCamera camera = new PerspectiveCamera(true);
camera.setFarClip(SIZE * 6);
camera.setTranslateZ(-2 * SIZE);
scene.setCamera(camera);
scene.setOnMousePressed((MouseEvent e) -> {
mousePosX = e.getSceneX();
mousePosY = e.getSceneY();
mouseOldX = e.getSceneX();
mouseOldY = e.getSceneY();
});
scene.setOnMouseDragged((MouseEvent e) -> {
mouseOldX = mousePosX;
mouseOldY = mousePosY;
mousePosX = e.getSceneX();
mousePosY = e.getSceneY();
mouseDeltaX = (mousePosX - mouseOldX);
mouseDeltaY = (mousePosY - mouseOldY);
if (e.isShiftDown()) {
content.group.rz(-mouseDeltaX * 180.0 / scene.getWidth());
} else if (e.isPrimaryButtonDown()) {
content.group.rx(+mouseDeltaY * 180.0 / scene.getHeight());
content.group.ry(-mouseDeltaX * 180.0 / scene.getWidth());
} else if (e.isSecondaryButtonDown()) {
camera.setTranslateX(camera.getTranslateX() - mouseDeltaX * 0.1);
camera.setTranslateY(camera.getTranslateY() - mouseDeltaY * 0.1);
camera.setTranslateZ(camera.getTranslateZ() + mouseDeltaY);
}
});
scene.setOnScroll((final ScrollEvent e) -> {
camera.setTranslateZ(camera.getTranslateZ() + e.getDeltaY());
});
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

JavaFx: Check current position of PathTransition while running

I want to get the current position (x,y) of a Circle (javafx.scene.shape.Circle) i am moving via a PathTransition, while the transition is running/happening.
So i need some kind of task, that checks the position of the circle every 50 milliseconds (for example).
I also tried this solution Current circle position of javafx transition which was suggested on Stack Overflow, but i didn't seem to work for me.
Circle projectile = new Circle(Playground.PROJECTILE_SIZE, Playground.PROJECTILE_COLOR);
root.getChildren().add(projectile);
double duration = distance / Playground.PROJECTILE_SPEED;
double xOff = (0.5-Math.random())*Playground.WEAPON_OFFSET;
double yOff = (0.5-Math.random())*Playground.WEAPON_OFFSET;
Line shotLine = new Line(player.getCurrentX(), player.getCurrentY(), aimLine.getEndX() + xOff, aimLine.getEndY() + yOff);
shotLine.setEndX(shotLine.getEndX() + (Math.random()*Playground.WEAPON_OFFSET));
PathTransition pt = new PathTransition(Duration.seconds(duration), shotLine, projectile);
// Linear movement for linear speed
pt.setInterpolator(Interpolator.LINEAR);
pt.setOnFinished(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
// Remove bullet after hit/expiration
projectile.setVisible(false);
root.getChildren().remove(projectile);
}
});
projectile.translateXProperty().addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
double x = collider.getTranslateX() - projectile.getTranslateX();
double y = collider.getTranslateY() - projectile.getTranslateY();
double distance = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
System.out.println("Distance: "+ distance);
if (distance < 50) {
System.out.println("hit");
}
}
});
pt.play();
A PathTransition will move a node by manipulating its translateX and translateY properties. (A TranslateTransition works the same way.)
It's hard to answer your question definitively as your code is so incomplete, but if the projectile and collider have the same parent in the scene graph, converting the initial coordinates of the projectile and collider by calling localToParent will give the coordinates in the parent, including the translation. So you can observe the translateX and translateY properties and use that conversion to check for a collision. If they have different parents, you can do the same with localToScene instead and just convert both to coordinates relative to the scene.
Here's a quick SSCCE. Use the left and right arrows to aim, space to shoot:
import javafx.animation.Animation;
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.geometry.Point2D;
import javafx.scene.Scene;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.scene.transform.Rotate;
import javafx.stage.Stage;
import javafx.util.Duration;
public class ShootingGame extends Application {
#Override
public void start(Stage primaryStage) {
final double width = 400 ;
final double height = 400 ;
final double targetRadius = 25 ;
final double projectileRadius = 5 ;
final double weaponLength = 25 ;
final double weaponX = width / 2 ;
final double weaponStartY = height ;
final double weaponEndY = height - weaponLength ;
final double targetStartX = targetRadius ;
final double targetY = targetRadius * 2 ;;
Pane root = new Pane();
Circle target = new Circle(targetStartX, targetY, targetRadius, Color.BLUE);
TranslateTransition targetMotion = new TranslateTransition(Duration.seconds(2), target);
targetMotion.setByX(350);
targetMotion.setAutoReverse(true);
targetMotion.setCycleCount(Animation.INDEFINITE);
targetMotion.play();
Line weapon = new Line(weaponX, weaponStartY, weaponX, weaponEndY);
weapon.setStrokeWidth(5);
Rotate weaponRotation = new Rotate(0, weaponX, weaponStartY);
weapon.getTransforms().add(weaponRotation);
Scene scene = new Scene(root, width, height);
scene.setOnKeyPressed(e -> {
if (e.getCode() == KeyCode.LEFT) {
weaponRotation.setAngle(Math.max(-45, weaponRotation.getAngle() - 2));
}
if (e.getCode() == KeyCode.RIGHT) {
weaponRotation.setAngle(Math.min(45, weaponRotation.getAngle() + 2));
}
if (e.getCode() == KeyCode.SPACE) {
Point2D weaponEnd = weapon.localToParent(weaponX, weaponEndY);
Circle projectile = new Circle(weaponEnd.getX(), weaponEnd.getY(), projectileRadius);
TranslateTransition shot = new TranslateTransition(Duration.seconds(1), projectile);
shot.setByX(Math.tan(Math.toRadians(weaponRotation.getAngle())) * height);
shot.setByY(-height);
shot.setOnFinished(event -> root.getChildren().remove(projectile));
BooleanBinding hit = Bindings.createBooleanBinding(() -> {
Point2D targetLocation = target.localToParent(targetStartX, targetY);
Point2D projectileLocation = projectile.localToParent(weaponEnd);
return (targetLocation.distance(projectileLocation) < targetRadius + projectileRadius) ;
}, projectile.translateXProperty(), projectile.translateYProperty());
hit.addListener((obs, wasHit, isNowHit) -> {
if (isNowHit) {
System.out.println("Hit");
root.getChildren().remove(projectile);
root.getChildren().remove(target);
targetMotion.stop();
shot.stop();
}
});
root.getChildren().add(projectile);
shot.play();
}
});
root.getChildren().addAll(target, weapon);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

JavaFX 3D rotations

My problems are:
the below code is based on the "MolecularSampleApp" from the oracle tutorial site, but very much simplified. It only shows a box and a red sphere for orientation purpose. The sequence of rotations is around x-axis then y-axis and finally z-axis. Following rotations are apparently done in coordinate-axes that are rotated with the rotations before.
(I checked that again and again with a cube and sequences of 90° rotations) So, when the user rotates the camera view with the mouse, this is very unintuitive, because the rotation behaviour changes after rotating around the vertical screen axis (because the horizontal axis will then be rotated too).
Try it with my code below, or with the MolecularSampleApp - its the same unnatural feeling. Is there an easy way to get over this?
But what I don't even understand is the behaviour when the mousePressed-code is performed: Here, the camera is always rotated in a FIXED system! The axes are NOT rotated with the camera, although its essentially the same code (except the rotation angle is of course not accumulated here). Anybody knows how is this possible?
package trafotest;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.scene.DepthTest;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Box;
import javafx.scene.shape.Sphere;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Translate;
import javafx.stage.Stage;
public class TrafoTest extends Application {
final Group root = new Group();
final XformWorld world = new XformWorld();
final PerspectiveCamera camera = new PerspectiveCamera(true);
final XformCamera cameraXform = new XformCamera();
private static final double CAMERA_INITIAL_DISTANCE = -1000;
private static final double CAMERA_NEAR_CLIP = 0.1;
private static final double CAMERA_FAR_CLIP = 10000.0;
double mousePosX, mousePosY, mouseOldX, mouseOldY, mouseDeltaX, mouseDeltaY;
#Override
public void start(Stage primaryStage) {
root.getChildren().add(world);
root.setDepthTest(DepthTest.ENABLE);
buildCamera();
buildBodySystem();
Scene scene = new Scene(root, 800, 600, true);
scene.setFill(Color.GREY);
handleMouse(scene);
primaryStage.setTitle("Transformationen");
primaryStage.setScene(scene);
primaryStage.show();
scene.setCamera(camera);
}
private void buildCamera() {
root.getChildren().add(cameraXform);
cameraXform.getChildren().add(camera);
camera.setNearClip(CAMERA_NEAR_CLIP);
camera.setFarClip(CAMERA_FAR_CLIP);
camera.setTranslateZ(CAMERA_INITIAL_DISTANCE);
}
private void buildBodySystem() {
PhongMaterial whiteMaterial = new PhongMaterial();
whiteMaterial.setDiffuseColor(Color.WHITE);
whiteMaterial.setSpecularColor(Color.LIGHTBLUE);
Box box = new Box(400, 200, 100);
box.setMaterial(whiteMaterial);
PhongMaterial redMaterial = new PhongMaterial();
redMaterial.setDiffuseColor(Color.DARKRED);
redMaterial.setSpecularColor(Color.RED);
Sphere sphere = new Sphere(5);
sphere.setMaterial(redMaterial);
sphere.setTranslateZ(-50.0);
world.getChildren().addAll(box);
world.getChildren().addAll(sphere);
}
private void handleMouse(Scene scene) {
scene.setOnMousePressed((MouseEvent me) -> {
mousePosX = me.getSceneX();
mousePosY = me.getSceneY();
mouseOldX = me.getSceneX();
mouseOldY = me.getSceneY();
// this is done after clicking and the rotations are apearently
// performed in coordinates that are NOT rotated with the camera.
// (pls activate the two lines below for clicking)
//cameraXform.rx.setAngle(-90.0);
//cameraXform.ry.setAngle(180.0);
});
scene.setOnMouseDragged((MouseEvent me) -> {
mouseOldX = mousePosX;
mouseOldY = mousePosY;
mousePosX = me.getSceneX();
mousePosY = me.getSceneY();
mouseDeltaX = (mousePosX - mouseOldX);
mouseDeltaY = (mousePosY - mouseOldY);
if (me.isPrimaryButtonDown()) {
// this is done when the mouse is dragged and each rotation is
// performed in coordinates, that are rotated with the camera.
cameraXform.ry.setAngle(cameraXform.ry.getAngle() + mouseDeltaX * 0.2);
cameraXform.rx.setAngle(cameraXform.rx.getAngle() - mouseDeltaY * 0.2);
}
});
}
public static void main(String[] args) {
launch(args);
}
}
class XformWorld extends Group {
final Translate t = new Translate(0.0, 0.0, 0.0);
final Rotate rx = new Rotate(0, 0, 0, 0, Rotate.X_AXIS);
final Rotate ry = new Rotate(0, 0, 0, 0, Rotate.Y_AXIS);
final Rotate rz = new Rotate(0, 0, 0, 0, Rotate.Z_AXIS);
public XformWorld() {
super();
this.getTransforms().addAll(t, rx, ry, rz);
}
}
class XformCamera extends Group {
final Translate t = new Translate(0.0, 0.0, 0.0);
final Rotate rx = new Rotate(0, 0, 0, 0, Rotate.X_AXIS);
final Rotate ry = new Rotate(0, 0, 0, 0, Rotate.Y_AXIS);
final Rotate rz = new Rotate(0, 0, 0, 0, Rotate.Z_AXIS);
public XformCamera() {
super();
this.getTransforms().addAll(t, rx, ry, rz);
}
}
I found a solution that works for me, just in case someone else is interested too:
package trafotest;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.geometry.Point3D;
import javafx.scene.DepthTest;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Box;
import javafx.scene.shape.Sphere;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Transform;
import javafx.scene.transform.Translate;
import javafx.stage.Stage;
public class TrafoTest extends Application {
final Group root = new Group();
final XformWorld world = new XformWorld();
final PerspectiveCamera camera = new PerspectiveCamera(true);
final XformCamera cameraXform = new XformCamera();
private static final double CAMERA_INITIAL_DISTANCE = -1000;
private static final double CAMERA_NEAR_CLIP = 0.1;
private static final double CAMERA_FAR_CLIP = 10000.0;
double mousePosX, mousePosY, mouseOldX, mouseOldY, mouseDeltaX, mouseDeltaY;
double mouseFactorX, mouseFactorY;
#Override
public void start(Stage primaryStage) {
root.getChildren().add(world);
root.setDepthTest(DepthTest.ENABLE);
buildCamera();
buildBodySystem();
Scene scene = new Scene(root, 800, 600, true);
scene.setFill(Color.GREY);
handleMouse(scene);
primaryStage.setTitle("TrafoTest");
primaryStage.setScene(scene);
primaryStage.show();
scene.setCamera(camera);
mouseFactorX = 180.0 / scene.getWidth();
mouseFactorY = 180.0 / scene.getHeight();
}
private void buildCamera() {
root.getChildren().add(cameraXform);
cameraXform.getChildren().add(camera);
camera.setNearClip(CAMERA_NEAR_CLIP);
camera.setFarClip(CAMERA_FAR_CLIP);
camera.setTranslateZ(CAMERA_INITIAL_DISTANCE);
}
private void buildBodySystem() {
PhongMaterial whiteMaterial = new PhongMaterial();
whiteMaterial.setDiffuseColor(Color.WHITE);
whiteMaterial.setSpecularColor(Color.LIGHTBLUE);
Box box = new Box(400, 200, 100);
box.setMaterial(whiteMaterial);
PhongMaterial redMaterial = new PhongMaterial();
redMaterial.setDiffuseColor(Color.DARKRED);
redMaterial.setSpecularColor(Color.RED);
Sphere sphere = new Sphere(5);
sphere.setMaterial(redMaterial);
sphere.setTranslateX(200.0);
sphere.setTranslateY(-100.0);
sphere.setTranslateZ(-50.0);
world.getChildren().addAll(box);
world.getChildren().addAll(sphere);
}
private void handleMouse(Scene scene) {
scene.setOnMousePressed((MouseEvent me) -> {
mousePosX = me.getSceneX();
mousePosY = me.getSceneY();
mouseOldX = me.getSceneX();
mouseOldY = me.getSceneY();
});
scene.setOnMouseDragged((MouseEvent me) -> {
mouseOldX = mousePosX;
mouseOldY = mousePosY;
mousePosX = me.getSceneX();
mousePosY = me.getSceneY();
mouseDeltaX = (mousePosX - mouseOldX);
mouseDeltaY = (mousePosY - mouseOldY);
if (me.isPrimaryButtonDown()) {
cameraXform.ry(mouseDeltaX * 180.0 / scene.getWidth());
cameraXform.rx(-mouseDeltaY * 180.0 / scene.getHeight());
} else if (me.isSecondaryButtonDown()) {
camera.setTranslateZ(camera.getTranslateZ() + mouseDeltaY);
}
});
}
public static void main(String[] args) {
launch(args);
}
}
class XformWorld extends Group {
final Translate t = new Translate(0.0, 0.0, 0.0);
final Rotate rx = new Rotate(0, 0, 0, 0, Rotate.X_AXIS);
final Rotate ry = new Rotate(0, 0, 0, 0, Rotate.Y_AXIS);
final Rotate rz = new Rotate(0, 0, 0, 0, Rotate.Z_AXIS);
public XformWorld() {
super();
this.getTransforms().addAll(t, rx, ry, rz);
}
}
class XformCamera extends Group {
Point3D px = new Point3D(1.0, 0.0, 0.0);
Point3D py = new Point3D(0.0, 1.0, 0.0);
Rotate r;
Transform t = new Rotate();
public XformCamera() {
super();
}
public void rx(double angle) {
r = new Rotate(angle, px);
this.t = t.createConcatenation(r);
this.getTransforms().clear();
this.getTransforms().addAll(t);
}
public void ry(double angle) {
r = new Rotate(angle, py);
this.t = t.createConcatenation(r);
this.getTransforms().clear();
this.getTransforms().addAll(t);
}
}

Moving LinearGradient in a given Shape

Is there a way to animate a "move" of LinearGradient nested into a given Shape? Let's say for example make the LinearGradient move to right or to left? I thought about the translateXproperty or the Path object, but I want to move only the LinearGradient.
EDIT 28.09.2014
Thank you #James_D for the valueable help. But in the context of my project I've needed a different solution. I've worked it out, and the result can be seen below on the working example:
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.scene.paint.CycleMethod;
import javafx.scene.paint.LinearGradient;
import javafx.scene.paint.Stop;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class LinearGradientAnimation extends Application {
private Rectangle rect;
private Timeline timeline;
private double widthOfOneGradientCycle = 20.0;
private double gradientSlopeDegree = 45.0;
private double xStartStatic = 100.0;
private double yStartStatic = 100.0;
private double xEndStatic = xStartStatic + (widthOfOneGradientCycle * Math.cos(Math.toRadians(gradientSlopeDegree)));
private double yEndStatic = yStartStatic + (widthOfOneGradientCycle * Math.sin(Math.toRadians(gradientSlopeDegree)));
public Parent createContent() {
/* layout */
BorderPane layout = new BorderPane();
/* layout -> Rectangle */
rect = new Rectangle(0, 0, 200, 200);
/* layout -> Rectangle -> LinearGradient Animation */
timeline = new Timeline();
for (int i = 0; i < 10; i++) {
int innerIterator = i;
KeyFrame kf = new KeyFrame(Duration.millis(30 * innerIterator), new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent ae) {
double runningRadius = innerIterator * (widthOfOneGradientCycle / 10);
double xStartDynamic = xStartStatic + (runningRadius * Math.cos(Math.toRadians(gradientSlopeDegree)));
double yStartDynamic = yStartStatic + (runningRadius * Math.sin(Math.toRadians(gradientSlopeDegree)));
double xEndDynamic = xEndStatic + (runningRadius * Math.cos(Math.toRadians(gradientSlopeDegree)));
double yEnddynamic = yEndStatic + (runningRadius * Math.sin(Math.toRadians(gradientSlopeDegree)));
LinearGradient gradient = new LinearGradient(xStartDynamic, yStartDynamic, xEndDynamic, yEnddynamic,
false, CycleMethod.REPEAT, new Stop[] {
new Stop(0.0, Color.WHITE),
new Stop(0.5, Color.BLACK),
new Stop(1.0, Color.WHITE)
});
rect.setFill(gradient);
}
});
timeline.getKeyFrames().add(kf);
}
timeline.setCycleCount(Timeline.INDEFINITE);
/* return layout */
layout.setCenter(rect);
return layout;
}
#Override
public void start(Stage stage) throws Exception {
stage.setScene(new Scene(createContent()));
stage.setWidth(300);
stage.setHeight(300);
stage.show();
timeline.play();
}
public static void main(String[] args) {
launch(args);
}
}
https://gist.github.com/bluevoxel/44dcf297ee9503e72114

Zooming in and out using Scroll Event

I am trying to make a group which contains Rectangle and Text objects to scale up and down using the ScrollEvent. I am having trouble getting the correct scale to use from the delta of the scroll. The following code is what I have at the moment. Any help will be appreciated.
Thanks
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.input.ScrollEvent;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Text;
import javafx.stage.Stage;
// demonstrates scaling a test pane with content in it.
public class ScaleTest extends Application {
Group page;
double textScale =1.0;
public static void main(String[] args) {
launch();
}
#Override
public void start(Stage stage) throws Exception {
stage.setTitle("scale test");
Group root = new Group();
page= new Group();
Rectangle r = new Rectangle(300,300);
Text t = new Text("asodjoijdjiasjdoijaisjdijasijdiajsidjoiasjiodjiajosijdiojaisjdoijaoisjdiojaoisjdjaoisjdiojsoidj\njdoasijdoajsoidjoaisjoidjaoisjdiojs");
t.setFill(Color.FLORALWHITE);
t.setWrappingWidth(280);
t.setX(10);
t.setY(20);
page.getChildren().addAll(r,t);
root.getChildren().add(page);
Scene scene = new Scene(root,800,600);
this.setSceneEvents(scene);
stage.setScene(scene);
stage.show();
}
private void setSceneEvents(final Scene scene) {
//handles mouse scrolling
scene.setOnScroll(
new EventHandler<ScrollEvent>() {
#Override
public void handle(ScrollEvent event) {
double xscale = page.getScaleX() * event.getDeltaY()/35;
double yscale= page.getScaleY() * event.getDeltaY()/35;
System.out.println("xscale: "+ xscale);
System.out.println("yscale: "+ yscale);
//page.setScaleX(page.getScaleX() * event.getDeltaY()/35);
// page.setScaleY(page.getScaleY() * event.getDeltaY()/35);
event.consume();
}
});
}
}
|deltaY| is for all scroll events the same. Also note that deltaY is negative for scrolling down, and the scaling always is normalized at 1.0. So i suggest to use a "zoom factor" which determines the scaling. Try something like:
private void setSceneEvents(final Scene scene) {
//handles mouse scrolling
scene.setOnScroll(
new EventHandler<ScrollEvent>() {
#Override
public void handle(ScrollEvent event) {
double zoomFactor = 1.05;
double deltaY = event.getDeltaY();
if (deltaY < 0){
zoomFactor = 2.0 - zoomFactor;
}
System.out.println(zoomFactor);
page.setScaleX(page.getScaleX() * zoomFactor);
page.setScaleY(page.getScaleY() * zoomFactor);
event.consume();
}
});
}

Categories

Resources