I am wondering how to draw sharp thin lines using JavaFX. I would like my lines to be black, and 1 pixel high. Here is what I have at the moment:
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
BorderPane root = new BorderPane();
root.setSnapToPixel(true);
Scene scene = new Scene(root,400,400);
Line line = new Line();
Line line2 = new Line();
line.setStartX(0.0f);
line.setEndX(100f);
line.setStartY(30f);
line.setEndY(30f);
line.setStrokeWidth(1f);
line.setStrokeType(StrokeType.OUTSIDE);
line.setStroke(Color.BLACK);
line2.setStartX(50.0f);
line2.setEndX(200f);
line2.setStartY(100f);
line2.setEndY(100f);
line2.setStrokeWidth(1f);
line2.setStrokeType(StrokeType.OUTSIDE);
line2.setStroke(Color.BLACK);
root.getChildren().addAll(line, line2);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
Here is what I get :
The lines are quite thick, and calling the setStrokeWidth() method with a value < 1 has no effect on the height, but makes the black color fade off. Any idea how to get a 1 pixel high line?
I can achieve it by using rectangles with a height of 1 pixel, but it seems a bit dirty.
If you use StrokeType.CENTERED, and start the x/y values on a half unit, then the lines appear to be hairlines to me.
public void start(Stage primaryStage) {
try {
BorderPane root = new BorderPane();
root.setSnapToPixel(true);
Scene scene = new Scene(root, 400, 400);
Line line = new Line();
Line line2 = new Line();
line.setStartX(0.5);
line.setEndX(100.5);
line.setStartY(30.5);
line.setEndY(30.5);
line.setStrokeWidth(1.0);
line.setStrokeType(StrokeType.CENTERED);
line.setStroke(Color.BLACK);
line2.setStartX(50.5);
line2.setEndX(200.5);
line2.setStartY(100.5);
line2.setEndY(100.5);
line2.setStrokeWidth(1.0);
line2.setStrokeType(StrokeType.CENTERED);
line2.setStroke(Color.BLACK);
root.getChildren().addAll(line, line2);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
My guess is that unit numbers in JavaFX are for the corners of the pixels, so specifying location + 0.5 places the line in the middle of said pixel.
Related
Please, consider the following code:
public class JavaFxTest4 extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
Button button = new Button();
button.setText("dialog");
button.setOnAction((e) -> {
Dialog<?> d = new Dialog<>();
final Window window = d.getDialogPane().getScene().getWindow();
Stage stage = (Stage) window;
stage.setMinHeight(450);
stage.setMaxHeight(450);
stage.setHeight(450);
stage.setMinWidth(600);
stage.setMaxWidth(600);
stage.setWidth(600);
window.setY(300); //<---- note this line
window.setX(660); //<---- note this line
d.showAndWait();
});
VBox root = new VBox();
root.getChildren().addAll(button);
var scene = new Scene(root, 1920, 1000);
primaryStage.setScene(scene);
primaryStage.show();
}
}
As you can see window position is 660 (x) and 300 (y). And this is the result:
As you can see x position is correct, but y position is not. Is this a bug or I misunderstand something? I use javafx 19-ea+3 and openjdk version "14.0.2" on Ubuntu 20.
It is a bug in JavaFX. Issue is here
Here is my following code:
public void start(Stage primaryStage) throws Exception {
Pane root = new Pane();
Scene scene = new Scene(root, 500, 500, Color.WHITE);
ImageView image = new ImageView(new Image(getClass().getResourceAsStream("dice.jpeg")));
image.setX(35);
image.setY(225);
image.setFitWidth(50);
image.setFitHeight(70);
root.getChildren().add(image);
Line line = new Line(20, 40, 120, 40);
line.setStroke(Color.RED);
line.setStrokeWidth(10);
root.getChildren().add(line);
if (line.getBoundsInParent().intersects(image.getBoundsInParent())) {
System.out.println("intersect");
}
Timeline timeline = new Timeline(
new KeyFrame(
Duration.seconds(2),
new KeyValue(line.translateYProperty(), 600)
));
timeline.setCycleCount(Animation.INDEFINITE);
timeline.setAutoReverse(false);
timeline.play();
primaryStage.setScene(scene);
primaryStage.show();
}
I want this System.out.println("intersect") message to be print when the line and image intersect, but when I run my code it doesn't work. Can anyone please tell me what am I doing wrong? Any help is appreciated!
I have come up with a solution. This involves using a thread to check all the time while the other code is still running. The error in your code is it only checked once when the animation had not started running.
Obviously, my code is not perfect so you may want to clean it up to suit your needs:
public class Test extends Application{
public static void main(String[] args) {
Test.launch(args);
//The thread which checks continues to run so you have to stop it
//(There is definitly a better way to do this)
System.exit(0);
}
#Override
public void start(Stage primaryStage) throws Exception {
Pane root = new Pane();
Scene scene = new Scene(root, 500, 500, Color.WHITE);
ImageView image = new ImageView(new Image(getClass().getResourceAsStream("dice.jpeg")));
image.setX(35);
image.setY(225);
image.setFitWidth(50);
image.setFitHeight(70);
root.getChildren().add(image);
Line line = new Line(20, 40, 120, 40);
line.setStroke(Color.RED);
line.setStrokeWidth(10);
root.getChildren().add(line);
//new code
Thread t = new Thread(){
#Override
public void run(){
while(true){
try {
//you could add a Thread.sleep so it only checks every x miliseconds
Thread.sleep(40);
if (line.getBoundsInParent().intersects(image.getBoundsInParent())) {
System.out.println("intersect");
}
} catch (InterruptedException ex) {
Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
};
t.start();
//end of new code
Timeline timeline = new Timeline(
new KeyFrame(
Duration.seconds(2),
new KeyValue(line.translateYProperty(), 600)
));
timeline.setCycleCount(Animation.INDEFINITE);
timeline.setAutoReverse(false);
timeline.play();
primaryStage.setScene(scene);
primaryStage.show();
}
}
I have strange problems with JavaFX that look a lot like a bug. I want to do the following:
Entering fullscren when starting my application
Press escape to exit the application (not fullscreen, the entire application)
So far, I have the following code:
public class AppTest extends Application {
public static void main(String[] args) {
launch(args);
}
public void start(Stage stage) {
stage.setOnCloseRequest(t -> {
Platform.exit();
System.exit(0);
});
stage.setFullScreenExitHint("Press ESCAPE to exit");
stage.setFullScreenExitKeyCombination(KeyCombination.NO_MATCH);
stage.setFullScreen(true);
Rectangle2D screenBounds = Screen.getPrimary().getBounds();
stage.setX(screenBounds.getMinX());
stage.setY(screenBounds.getMinY());
double screenWidth = screenBounds.getWidth();
double screenHeight = screenBounds.getHeight();
stage.setWidth(screenWidth);
stage.setHeight(screenHeight);
Group root = new Group();
Scene scene = new Scene(root);
stage.setScene(scene);
scene.setOnKeyTyped(event -> {
if(event.getCode() == KeyCode.ESCAPE) {
stage.close();
}
});
Canvas canvas = new Canvas(screenWidth, screenHeight);
root.getChildren().add(canvas);
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.setFill(Color.BLUE);
gc.fillRect(0,0, screenWidth, screenHeight);
stage.show();
}
}
I'm on macOS.
In general it goes fullscreen. I'm saying in general because the real version of this code doesn't always. Sometimes, it is just a maximized window.
Then, when pressing escape, I get a maximized window instead of exiting the application.
How can I fix that?
Change:
scene.setOnKeyTyped
To:
scene.setOnKeyReleased
This explains why.
Im working on a project for school and I'm having trouble adding a child pane to a parent pane. All the code compiles except when I get to the pane.getChildren().add(Matrix); . Im able to get the code to compile when I have all the code in main, but I really want to have main call a class and create the pane there then add it to the parent pane. Im not to worried about it looking pretty right now, just want to find a way to get it to work. If anyone could help get me going in the right direction I would really appreciate it.
The compiler gives me
Button1.java:34: error: identifier expected
pane.getChildren().add(Matrix);
Button1.java:34: error: ';' expected
pane.getChildren().add(Matrix);
public class Button1 extends Application {
public void start(Stage primaryStage) {
Scene scene = new Scene(pane, 700, 500);
primaryStage.setTitle("3 pains 1 window "); // Set the stage title
primaryStage.setScene(scene); // Place the scene in the stage
primaryStage.show(); // Display the stage
}
public static void main(String[] args) {
Application.launch(args);
}
GridPane pane = new GridPane();
MatrixPane Matrix = new MatrixPane();
pane.getChildren().add(Matrix);
}
class MatrixPane extends Pane {
double HEIGHT = 500;
double WIDTH = 200;
private GridPane pane = new GridPane();
public MatrixPane() {
}
public void fillpane() {
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
TextField text = new TextField(Integer.toString((int)(Math.random() * 2)));
text.setMinWidth(WIDTH / 8.0);
text.setMaxWidth(WIDTH / 10.0);
text.setMinHeight(HEIGHT / 8.0);
text.setMaxHeight(HEIGHT / 10.0);
pane.add(text, j, i);
}
}
}
}
That line have to be inside of a method, I suggest that it should be inside of start
public void start(Stage primaryStage) {
pane.getChildren().add(Matrix);
...
You have missed to include the following section inside a method().
Looks like Transform class in JavaFX contains 3 coordinates (x, y and z).
What will happen if I rotate out of plane? Will it do perspective?
UPDATE
Actually object disappears if rotated out of plane even by 1 degree. Why?
public class TransformTry extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Drawing Operations Test");
Group root = new Group();
Canvas canvas = new Canvas(300, 250);
//canvas.getTransforms().add(new Rotate(1, new Point3D(0, 1, 0))); // square disappears
canvas.getTransforms().add(new Rotate(1, new Point3D(0, 0, 1))); // rotates correctly
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.strokeRect(-50, -50, 100, 100);
root.getChildren().add(canvas);
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
}
UPDATE 2
I found a way to use perspective camera, but it works strange: it looks always from above the center of a window, so out-of-plane object changes on window resize:
public class ShapeTry01 extends Application {
#Override
public void start(Stage stage) throws Exception {
Shape shape = new Rectangle(100,100,50,50);
shape.setStroke(Color.RED);
shape.setFill(null);
Group group = new Group();
group.getChildren().add(shape);
//group.getTransforms().add(new Rotate(10, new Point3D(0, 0, 1)));
group.getTransforms().add(new Rotate(10, new Point3D(0, 1, 0)));
PerspectiveCamera camera = new PerspectiveCamera();
camera.setFieldOfView(45);
Scene scene = new Scene(group);
scene.setCamera( camera );
stage.setTitle("ShapeTry01");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
JavaFX 2 was started with 3D in mind, thus a lot of API has z coordinate mentioned. But actually useful 3D was added only in JavaFX8 (to be released in next year).
You can see documentation here: http://docs.oracle.com/javafx/8/3d_graphics/jfxpub-3d_graphics.htm and try developers preview: https://jdk8.java.net/download.html