I have a program that constitutes a Cube class that extends from the Group class, in this Cube I create a Polygon that serves as one of the faces of this Cube, when facing it it renders normal, but when the camera perspective rotates back the polygon of After creating a strange white line over the front one, I noticed that this happens due to the order in which items are created and added to the container via getChildren (). add (), this line cannot be rendered independent the order in which cubes and faces are created and added to the container?
Class cube.java
import java.util.ArrayList;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Point3D;
import javafx.geometry.Pos;
import javafx.scene.CacheHint;
import javafx.scene.DepthTest;
import javafx.scene.Group;
import javafx.scene.control.Button;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Polygon;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Translate;
public class Cube extends Group{
public final Rotate rx = new Rotate(0, Rotate.X_AXIS);
public final Rotate ry = new Rotate(0, Rotate.Y_AXIS);
public final Rotate rz = new Rotate(0, Rotate.Z_AXIS);
public final Translate translate;
public final double x;
public final double y;
public final double z;
public final Group group;
public static final ArrayList<Cube> instances = new ArrayList<Cube>();
public static final int A_LEFT = 0;
public static final int A_RIGHT = 1;
public static final int A_TOP = 2;
public static final int A_BOTTOM = 3;
public static final int A_FRONT = 4;
public static final int A_BACK = 5;
public Cube(double tam, double x, double y, double z, Color cor, Group grupo) {
this.group = grupo;
setDepthTest(DepthTest.ENABLE);
setCacheHint(CacheHint.SCALE_AND_ROTATE);
setCache(true);
Cube.instances.add(this);
//System.out.print(Cube.instances.size());
grupo.getChildren().add(this);
this.x = x;
this.y = y;
this.z = z;
translate = new Translate(x,y,z);
getTransforms().addAll(rx,ry,rz, translate);
Button b = new Button("Test Button");
b.setOnMouseClicked(new EventHandler<Event>() {
#Override
public void handle(Event event) {
System.out.println("Test click");
}
});
HBox frontFace = this.createFace(tam, tam, tam*-0.5, tam*-0.5, tam*-0.5, Rotate.X_AXIS, 0, cor.deriveColor(0.0, 1.0, (1 - 0.5 * 1), 1.0));
HBox backFace = this.createFace(tam, tam, tam*-0.5, tam*-0.5, tam*0.5, Rotate.Y_AXIS, 180d, cor.deriveColor(0.0, 1.0, (1 - 0.4 * 1), 1.0));
HBox leftFace = this.createFace(tam, tam, tam*-1, tam*-0.5, 0, Rotate.Y_AXIS, 90d, cor.deriveColor(0.0, 1.0, (1 - 0.3 * 1), 1.0));
HBox rightFace = this.createFace(tam, tam, 0, tam*-0.5, 0, Rotate.Y_AXIS, -90d, cor.deriveColor(0.0, 1.0, (1 - 0.2 * 1), 1.0));
HBox bottomFace = this.createFace(tam, tam, tam*-0.5, 0, 0, Rotate.X_AXIS, 90d, cor.deriveColor(0.0, 1.0, (1 - 0.1 * 1), 1.0));
HBox topFace = this.createFace(tam, tam, tam*-0.5, tam*-1, 0, Rotate.X_AXIS, -90d, cor.deriveColor(0.0, 1.0, (1 - 0.6 * 1), 1.0));
Polygon polygon = new Polygon();
polygon.getPoints().addAll(new Double[]{
55.0, 0.0,
110.0, 110.0,
0.0, 110.0
});
polygon.setStroke(Color.BROWN);
polygon.setStrokeWidth(0);
polygon.setSmooth(false);
polygon.setCacheHint(CacheHint.SCALE_AND_ROTATE);
backFace.getChildren().add(polygon);
backFace.setBackground(new Background(new BackgroundFill(Color.TRANSPARENT, CornerRadii.EMPTY, Insets.EMPTY)));
polygon.setFill(Color.BLACK);
polygon.setStroke(null);
// leftFace.getChildren().add(polygon2);
ImageView anImage = new ImageView(new Image("android.png",110,110,false,true));
anImage.setFitWidth(100);
anImage.setPreserveRatio(true);
anImage.setCacheHint(CacheHint.SCALE_AND_ROTATE);
anImage.setSmooth(true);
anImage.setCache(true);
// backFace.getChildren().add(anImage);
/*BackgroundImage myBI= new BackgroundImage(new Image("android.png",110,110,false,true),
BackgroundRepeat.REPEAT, BackgroundRepeat.NO_REPEAT, BackgroundPosition.DEFAULT,
BackgroundSize.DEFAULT);*/
//frontFace.setBackground(new Background(myBI));
frontFace.setStyle("-fx-background-color:rgba(0,0,0,0)");
rightFace.setOnMouseClicked(e -> this.createNextCube(e, Cube.A_RIGHT));
leftFace.setOnMouseClicked(e -> this.createNextCube(e, Cube.A_LEFT));
topFace.setOnMouseClicked(e -> this.createNextCube(e, Cube.A_TOP));
bottomFace.setOnMouseClicked(e -> this.createNextCube(e, Cube.A_BOTTOM));
frontFace.setOnMouseClicked(e -> this.createNextCube(e, Cube.A_FRONT));
backFace.setOnMouseClicked(e -> this.createNextCube(e, Cube.A_BACK));
//frontFace.getChildren().addAll(b);
getChildren().addAll(
frontFace,
leftFace,
rightFace,
topFace,
bottomFace,
backFace
);
}
public HBox createFace(double w, double h, double x, double y, double z, Point3D axis, double rotation, Color cor) {
HBox g = new HBox();
g.setPrefSize(w, h);
g.setTranslateX(x);
g.setTranslateY(y);
g.setTranslateZ(z);
g.setAlignment(Pos.CENTER);
g.setRotationAxis(axis);
g.setRotate(rotation);
g.setBackground(new Background(new BackgroundFill(cor, CornerRadii.EMPTY, Insets.EMPTY)));
return g;
}
public Cube createNextCube(MouseEvent e, int side) {
if(e.getButton() == MouseButton.PRIMARY) {
double px = this.x;
double py = this.y;
double pz = this.z;
switch(side) {
case Cube.A_RIGHT:
px += 110;
break;
case Cube.A_LEFT:
px -= 110;
break;
case Cube.A_TOP:
py -= 110;
break;
case Cube.A_BOTTOM:
py += 110;
break;
case Cube.A_FRONT:
pz -= 110;
break;
case Cube.A_BACK:
pz += 110;
break;
}
return new Cube(110, px, py, pz, Color.ALICEBLUE, this.group);
}
return null;
}
}
Class main.java
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.CacheHint;
import javafx.scene.DepthTest;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.SceneAntialiasing;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Translate;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Main extends Application{
private PerspectiveCamera camera;
private Rotate cameraRotateX, cameraRotateY, cameraRotateZ;
private double antX;
private double antY;
public void init(Stage primaryStage) {
camera = new PerspectiveCamera();
cameraRotateX = new Rotate(0, Rotate.X_AXIS);
cameraRotateY = new Rotate(0, Rotate.Y_AXIS);
cameraRotateZ = new Rotate(0, Rotate.Z_AXIS);
Translate cameraTranslate = new Translate(-700 / 2, -300 / 2, 0);
camera.getTransforms().addAll(
cameraRotateX,
cameraRotateY,
cameraRotateZ,
cameraTranslate);
Group root = new Group();
root.setDepthTest(DepthTest.ENABLE);
primaryStage.setResizable(true);
primaryStage.setScene(new Scene(root, 700, 300, true, SceneAntialiasing.BALANCED));
primaryStage.getScene().setCamera(camera);
// root.getTransforms().addAll(
//new Translate(700 / 2, 300 / 2),
//new Rotate(180, Rotate.X_AXIS)
// );
//Group gp = new Group();
// gp.setDepthTest(DepthTest.);
//gp.setCache(true);
camera.setNearClip(0.01);
Cube cubo = new Cube(110, 0,0,0, Color.RED, root);
Timeline animation = new Timeline();
animation.getKeyFrames().addAll(
new KeyFrame(Duration.ZERO,
new KeyValue(cubo.ry.angleProperty(), 0d),
new KeyValue(cubo.rx.angleProperty(), 0d)
),
new KeyFrame(Duration.seconds(30),
new KeyValue(cubo.ry.angleProperty(), 360d),
new KeyValue(cubo.rx.angleProperty(), 360d)
)
);
animation.setCycleCount(Animation.INDEFINITE);
//root.getChildren().add(gp);
root.setCache(true);
// root.setCacheHint(CacheHint.SCALE_AND_ROTATE);
//animation.play();
primaryStage.getScene().setOnMousePressed(e -> pressed(e));
primaryStage.getScene().setOnMouseDragged(e -> moved(e));
primaryStage.getScene().setOnScroll(e -> cameraTranslate.setZ(cameraTranslate.getZ()+e.getDeltaY()));
primaryStage.getScene().heightProperty().addListener(h -> cameraTranslate.setY(-1*(primaryStage.getScene().getHeight())/2));
primaryStage.getScene().widthProperty().addListener(w -> cameraTranslate.setX(-1*(primaryStage.getScene().getWidth())/2));
}
public void pressed(MouseEvent ev) {
antX = ev.getSceneX();
antY = ev.getSceneY();
}
public void moved(MouseEvent ev) {
double movX = ev.getSceneX() - antX;
double movY = ev.getSceneY() - antY;
cameraRotateX.setAngle(cameraRotateX.getAngle() + movY);
cameraRotateY.setAngle(cameraRotateY.getAngle() + movX);
antX = ev.getSceneX();
antY = ev.getSceneY();
}
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage palco) throws Exception {
init(palco);
palco.show();
}
}
I'm trying to build a space invaders like game.
I've drawn a square and I want to move it down incrementally by using a loop and thread.sleep. However, the square just gets drawn immediately. I understand there are animation paths that could be used but I want to stay low level and just use a coordinate system.
Is there a way of making a timeline animation by using a loop like this?
package com.company;
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.*;
import javafx.scene.layout.*;
import javafx.scene.shape.Rectangle;
public class Main extends Application {
public static void main(String[] args) {
// write your code here
launch(args);
}
public void start(Stage myStage) throws InterruptedException {
myStage.setTitle("space invaders");
Pane rootNode= new Pane();
Scene myScene=new Scene(rootNode, 400, 800);
myStage.setScene(myScene);
Rectangle r = new Rectangle();
myStage.show();
rootNode.getChildren().add(r);
r.setX(50);
r.setY(50);
r.setWidth(20);
r.setHeight(20);
for (int i = 0; i < 500; i++){
Thread.sleep(2000);
r.setTranslateY(i);
}
}
}
This is a terrible implementation. I would probably use AnimationTimer. This is done with Timeline. It's basically moving right or left. If you hit the right or left bound, drop then move in the opposite direction.
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import javafx.animation.KeyFrame;
import javafx.animation.PauseTransition;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
/**
*
* #author blj0011
*/
public class JavaFXApplication343 extends Application
{
int invaderWidth = 30;
int invaderHeight = 10;
int gapBetweenInvaderX = 5;
int gapBetweenInvaderY = 5;
int locationTrackerX;
int locationTrackerY;
int screenWidth = 300;
int screenHeight = 400;
double timeBetweenFrames = .25;
boolean direction = true;
Timeline timeline;
#Override
public void start(Stage primaryStage)
{
Pane pane = new Pane();
locationTrackerX = (screenWidth - (invaderWidth * 6 + gapBetweenInvaderX * 5)) / 2;
locationTrackerY = (screenHeight - (invaderHeight * 6 + gapBetweenInvaderY * 5)) / 7;
List<Rectangle> invaders = new ArrayList();
for (int i = 0; i < 36; i++) {
Rectangle rectangle = new Rectangle(locationTrackerX, locationTrackerY, invaderWidth, invaderHeight);
rectangle.setFill(Color.YELLOW);
invaders.add(rectangle);
System.out.println(locationTrackerX);
locationTrackerX += invaderWidth + gapBetweenInvaderX;
if ((i + 1) % 6 == 0) {
locationTrackerX = (screenWidth / 2) - ((invaderWidth * 6 + gapBetweenInvaderX * 5) / 2);
locationTrackerY += invaderHeight + gapBetweenInvaderY;
}
}
timeline = new Timeline(new KeyFrame(Duration.seconds(timeBetweenFrames), (event) -> {
//Check to see if invader hits bounds
Optional<Rectangle> hitRightOptional = invaders.stream().filter(invader -> invader.getBoundsInLocal().getMaxX() >= pane.getWidth()).findFirst();
Optional<Rectangle> hitLeftOptional = invaders.stream().filter(invader -> invader.getBoundsInLocal().getMinX() <= 0).findFirst();
//Move invaders
if (hitRightOptional.isPresent()) {
invaders.forEach((tempInvader) -> tempInvader.setY(tempInvader.getY() + 10));
timeline.stop();
PauseTransition pause = new PauseTransition(Duration.seconds(timeBetweenFrames));
pause.setOnFinished((pauseEvent) -> {
invaders.forEach(invader -> invader.setX(invader.getX() - 10));
timeline.play();
});
pause.play();
direction = false;
}
else if (hitLeftOptional.isPresent()) {
invaders.forEach((tempInvader) -> tempInvader.setY(tempInvader.getY() + 10));
timeline.stop();
PauseTransition pause = new PauseTransition(Duration.seconds(timeBetweenFrames));
pause.setOnFinished((pauseEvent) -> {
invaders.forEach(invader -> invader.setX(invader.getX() + 10));
timeline.play();
});
pause.play();
direction = true;
}
else {
if (direction) {
invaders.forEach(invader -> invader.setX(invader.getX() + 10));
}
else {
invaders.forEach(invader -> invader.setX(invader.getX() - 10));
}
}
}));
timeline.setCycleCount(Timeline.INDEFINITE);
Button btn = new Button();
btn.setText("Start Game");
btn.setOnAction((ActionEvent event) -> {
timeline.play();
btn.setDisable(true);
});
pane.getChildren().addAll(invaders);
pane.setPrefSize(screenWidth, screenHeight);
VBox root = new VBox(pane, new StackPane(btn));
Scene scene = new Scene(root, screenWidth, screenHeight);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args)
{
launch(args);
}
}
Solved :
Task task = new Task<Void>() {
#Override public Void call() throws InterruptedException {
for (int i = 1; i <= 800; i=i+10) {
Thread.sleep(25);
//updateProgress(i, max);
r.setTranslateY(i);
}
return null;
}
};
new Thread(task).start();
I have this Stage and when im showing it . I want it to display at bottom of the screen and then go up to the center screen.
just like a notification .
my code
try {
Stage window = new Stage();
window.initModality(Modality.APPLICATION_MODAL);
window.setTitle(Message);
AnchorPane layout = (AnchorPane) FXMLLoader.load(Notification.class.getResource("window.fxml"));
Scene scene = new Scene(layout);
window.setScene(scene);
window.showAndWait();
} catch (Exception e) {
System.out.println(e);
}
Create a property specifically for the Timeline, and then add a listener to it:
double startPos = ... ;
double endPos = ... ;
DoubleProperty y = new SimpleDoubleProperty(startPos);
Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(1), new KeyValue(y, endPos)));
y.addListener((obs, oldValue, newValue) ->
window.setY(newValue.doubleValue()));
timeline.play();
SSCCE:
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.geometry.Rectangle2D;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.stage.Popup;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.util.Duration;
public class SlidingNotificationWindow extends Application {
#Override
public void start(Stage primaryStage) {
Button button = new Button("Show notification");
button.setOnAction(e -> {
Popup window = new Popup();
StackPane content = new StackPane(new Label("Notification"));
content.setStyle("-fx-background-color: aquamarine; -fx-padding: 40;");
content.setOnMouseClicked(evt -> window.hide());
window.getContent().add(content);
window.setWidth(120);
window.setHeight(75);
Rectangle2D primaryScreenBounds = Screen.getPrimary().getVisualBounds();
double startPos = primaryScreenBounds.getMaxY();
double endPos = 2*primaryScreenBounds.getMinY()/3 + primaryScreenBounds.getMaxY() / 3 ;
DoubleProperty y = new SimpleDoubleProperty(startPos);
Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(1), new KeyValue(y, endPos)));
y.addListener((obs, oldValue, newValue) ->
window.setY(newValue.doubleValue()));
timeline.play();
window.setX(primaryScreenBounds.getMaxX() - 120);
window.show(primaryStage);
});
StackPane root = new StackPane(button);
primaryStage.setScene(new Scene(root, 350, 120));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
You need to add a Timer where the window will be moved. This is because the Window/Stage only have ReadOnlyProperties for X and Y values, so you cannot make a simple Timeline Animation with Keyframes.
You need to play a bit with the height and width of the primaryScreenBounds, so it will fit your needs.
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.geometry.Rectangle2D;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.stage.Screen;
import javafx.stage.Stage;
public class TimelineEvents extends Application {
private AnimationTimer timer;
Rectangle2D primaryScreenBounds = Screen.getPrimary().getVisualBounds();
double x = primaryScreenBounds.getMaxX() - 180;
double y = primaryScreenBounds.getMaxY() - 180;
double centerX = primaryScreenBounds.getWidth() / 2;
double centerY = primaryScreenBounds.getHeight() / 2;
double tickX = (x - centerX) / primaryScreenBounds.getWidth() * 10;
double tickY = (y - centerY) / primaryScreenBounds.getHeight() * 10;
#Override
public void start(Stage stage) {
Button btn = new Button();
btn.setText("Show Message");
btn.setOnAction((ActionEvent event) -> {
Label l = new Label("Message");
BorderPane root = new BorderPane(l);
Scene s = new Scene(root, 100, 100);
Stage window = new Stage();
window.setX(x);
window.setY(y);
timer = new AnimationTimer() {
#Override
public void handle(long l) {
x = x - tickX;
y = y - tickY;
if (x >= centerX) {
window.setX(x);
window.setY(y);
} else {
stop();
}
}
};
timer.start();
window.setScene(s);
window.showAndWait();
});
BorderPane pane = new BorderPane(btn);
Scene scene = new Scene(pane, 200, 200);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
Application.launch(args);
}
}
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.
I have this JavaFX application that lets you plot locations on a map and connect them.
I do this by drawing a map as a background image on a canvas and then drawing circles and lines on it. I have made the circles clickable by using the contains() method in the Circle class, but how can I make the lines clickable?
edit: Look at this example where I just draw a line and set an event handler:
Canvas canvas = new Canvas();
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.setLineWidth(5);
gc.strokeLine(100, 100, 200, 200);
canvas.setOnMouseClicked(event -> {
double x = event.getX(), y = event.getY();
});
My question is simply this: how do I finish the event handler so that it detects if the click is inside the line I just drew?
You should create a canvas and add the nodes (Circle, Line, etc) to it. Then you add mouse listeners to the nodes.
Example:
import java.util.ArrayList;
import java.util.List;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.stage.Stage;
public class DragNodes extends Application {
public static List<Circle> circles = new ArrayList<Circle>();
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
Group root = new Group();
Canvas canvas = new Canvas(300, 300);
GraphicsContext gc = canvas.getGraphicsContext2D();
drawShapes(gc);
Circle circle1 = new Circle(50);
circle1.setStroke(Color.GREEN);
circle1.setFill(Color.GREEN.deriveColor(1, 1, 1, 0.7));
circle1.relocate(100, 100);
Circle circle2 = new Circle(50);
circle2.setStroke(Color.BLUE);
circle2.setFill(Color.BLUE.deriveColor(1, 1, 1, 0.7));
circle2.relocate(200, 200);
Line line = new Line(circle1.getLayoutX(), circle1.getLayoutY(), circle2.getLayoutX(), circle2.getLayoutY());
line.setStrokeWidth(20);
Pane overlay = new Pane();
overlay.getChildren().addAll(circle1, circle2, line);
MouseGestures mg = new MouseGestures();
mg.makeDraggable(circle1);
mg.makeDraggable(circle2);
mg.makeDraggable(line);
root.getChildren().addAll(canvas, overlay);
primaryStage.setScene(new Scene(root, 800, 600));
primaryStage.show();
}
private void drawShapes(GraphicsContext gc) {
gc.setStroke(Color.RED);
gc.strokeRoundRect(10, 10, 230, 230, 10, 10);
}
public static class MouseGestures {
double orgSceneX, orgSceneY;
double orgTranslateX, orgTranslateY;
public void makeDraggable(Node node) {
node.setOnMousePressed(circleOnMousePressedEventHandler);
node.setOnMouseDragged(circleOnMouseDraggedEventHandler);
}
EventHandler<MouseEvent> circleOnMousePressedEventHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent t) {
orgSceneX = t.getSceneX();
orgSceneY = t.getSceneY();
if (t.getSource() instanceof Circle) {
Circle p = ((Circle) (t.getSource()));
orgTranslateX = p.getCenterX();
orgTranslateY = p.getCenterY();
} else {
Node p = ((Node) (t.getSource()));
orgTranslateX = p.getTranslateX();
orgTranslateY = p.getTranslateY();
}
}
};
EventHandler<MouseEvent> circleOnMouseDraggedEventHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent t) {
double offsetX = t.getSceneX() - orgSceneX;
double offsetY = t.getSceneY() - orgSceneY;
double newTranslateX = orgTranslateX + offsetX;
double newTranslateY = orgTranslateY + offsetY;
if (t.getSource() instanceof Circle) {
Circle p = ((Circle) (t.getSource()));
p.setCenterX(newTranslateX);
p.setCenterY(newTranslateY);
} else {
Node p = ((Node) (t.getSource()));
p.setTranslateX(newTranslateX);
p.setTranslateY(newTranslateY);
}
}
};
}
}