LineChart JavaFX -saving to png - How to control the size - javafx

I cannot figure out how to control the size of the LineChart saved to png. The chart looks great on the screen, in it's own window, but it gets scrunched vertically when saved. I've tried to figure out how to control the size, but to no avail. It must be a simple setting, but...
Any help would be appreciated.
With the following code, excluding the code that sets the data values:
LineChart<Number, Number> xyChart = new LineChart<>(xAxis, yAxis);
StackPane layout = new StackPane();
layout.setPadding(new Insets(10, 10, 10, 10));
xyChart.prefWidthProperty().bind(window.widthProperty());
xyChart.prefHeightProperty().bind(window.heightProperty());
layout.getChildren().add(xyChart);
VBox mainVBox = new VBox();
mainVBox.getChildren().addAll(menuHBox, layout);
Scene scene = new Scene(mainVBox, 800, 600);
window.setScene(scene);
xyChart.setAnimated(false);
WritableImage snapShot = scene.snapshot(null);
try {
ImageIO.write(SwingFXUtils.fromFXImage(snapShot, null), "png", new File("test3.png"));
} catch (IOException e) {
}
The chart as displayed on the screen is
window grab
whereas the png file that is written is
upload of the png file

Try saving the image after the window is shown, as layouts and sizes are computed on a required basis, and may not give the same values before.
Another solution would - of course - be to explicitly set the preferred size of the node.
xyChart.setPrefWidth(...);
xyChart.setPrefHeight(...);
The reason why bindings don't help is because the window itself doesn't have the computed size yet, as previously said.

Related

JavaFX 3D boxes overlapping each other

I am having a problem with JavaFX 3D, the problem is as follows:
When I turn my perspective camera around, the last added box (blue box) overlaps the first added box (red box), here is a screenshot:
can anyone tell me why is this happening? And is there a way to fix it? (the boxes are literally 2 box classes with a width, height, depth, position and color)
Minimal reproducible example since somebody asked for it:
Box box1 = new Box();
Box box2 = new Box();
box1.setWidth(300);
box2.setWidth(300);
box1.setHeight(300);
box2.setHeight(300);
box1.setDepth(300);
box2.setDepth(300);
box1.setTranslateX(300);
box2.setTranslateX(300);
box1.setTranslateY(300);
box2.setTranslateX(300);
Group root = new Group();
root.getChildren().addAll(box, box2);
PerspectiveCamera cam = new PerspectiveCamera();
Scene scene = new Scene(root);
scene.setCamera(camera);
stage.setScene(scene);
stage.show();
where stage is the stage inside public void start(Stage stage), JavaFX's default run method (any class that extends Application should implement it)
You probably have to add a subscene with the depth buffer enabled. See: https://openjfx.io/javadoc/15/javafx.graphics/javafx/scene/SubScene.html#%3Cinit%3E(javafx.scene.Parent,double,double,boolean,javafx.scene.SceneAntialiasing)
Solution:
I used the following constructor:
new Scene(root, WIDTH, HEIGHT, true, SceneAntialiasing.BALANCED);
instead of:
new Scene(root);

JavaFX - Get Coordinates of Node Relative to its Parent

I am making a simple graphical interface for saving previously generated images. All images come to me square but I want to allow for some cropping functionality (more precisely cutting off equal parts from the bottom and top of the image). I want to do this by allowing the user to drag a shaded region over the image which will tell the user that this region will be cropped out. See the below image for details. To enable this drag functionality I have added small triangles that I want the user to drag which in turn will move the shaded regions about. However the coordinates for the triangles are all weird and seem nonsensical. Therefor I was wondering what the best way is to get the coordinates of the triangles in relation to the ImageView (or their first common parent node) in terms of ImageView-side-lengths. So if the triangle is in the center its coordinates are [0.5, 0.5] for instance.
The Image view will be moving around inside the scene and will also be changing size so it is vital that I can get the coordinates relative to not only the ImageView but also to the size of the ImageView.
Here is also the surrounding hierarchy of nodes if that helps. The Polygons are the triangles and the regions are the rectangles.
Thanks for all forms of help!
Node.getBoundsInParent returns the bounds of a node in it's parent coordinates. E.g. polygon.getBoundsInParent() would return the bounds in the VBox.
If you need to "go up" one additional step, you can use parent.localToParent to do this. vBox.localToParent(boundsInVbox) returns the bounds in the coordinate system of the AnchorPane.
To get values relative to the size of the image, you simply need to divide by it's size.
The following example only allows you to move the cover regions to in one direction and does not check, if the regions intersect, but it should be sufficient to demonstrate the approach.
The interesting part is the event handler of the button. It restricts the viewport of the second image to the part of the first image that isn't covered.
private static void setSideAnchors(Node node) {
AnchorPane.setLeftAnchor(node, 0d);
AnchorPane.setRightAnchor(node, 0d);
}
#Override
public void start(Stage primaryStage) {
// create covering area
Region topRegion = new Region();
topRegion.setStyle("-fx-background-color: white;");
Polygon topArrow = new Polygon(0, 0, 20, 0, 10, 20);
topArrow.setFill(Color.WHITE);
VBox top = new VBox(topRegion, topArrow);
top.setAlignment(Pos.TOP_CENTER);
topArrow.setOnMouseClicked(evt -> {
topRegion.setPrefHeight(topRegion.getPrefHeight() + 10);
});
// create bottom covering area
Region bottomRegion = new Region();
bottomRegion.setStyle("-fx-background-color: white;");
Polygon bottomArrow = new Polygon(0, 20, 20, 20, 10, 0);
bottomArrow.setFill(Color.WHITE);
VBox bottom = new VBox(bottomArrow, bottomRegion);
bottom.setAlignment(Pos.BOTTOM_CENTER);
bottomArrow.setOnMouseClicked(evt -> {
bottomRegion.setPrefHeight(bottomRegion.getPrefHeight() + 10);
});
Image image = new Image("https://upload.wikimedia.org/wikipedia/commons/thumb/e/ec/Mona_Lisa%2C_by_Leonardo_da_Vinci%2C_from_C2RMF_retouched.jpg/402px-Mona_Lisa%2C_by_Leonardo_da_Vinci%2C_from_C2RMF_retouched.jpg");
ImageView imageView = new ImageView(image);
setSideAnchors(top);
setSideAnchors(bottom);
setSideAnchors(imageView);
AnchorPane.setTopAnchor(top, 0d);
AnchorPane.setBottomAnchor(bottom, 0d);
AnchorPane.setTopAnchor(imageView, 0d);
AnchorPane.setBottomAnchor(imageView, 0d);
AnchorPane container = new AnchorPane(imageView, top, bottom);
ImageView imageViewRestricted = new ImageView(image);
Button button = new Button("restrict");
button.setOnAction(evt -> {
// determine bouns of Regions in AnchorPane
Bounds topBounds = top.localToParent(topRegion.getBoundsInParent());
Bounds bottomBounds = bottom.localToParent(bottomRegion.getBoundsInParent());
// set viewport accordingly
imageViewRestricted.setViewport(new Rectangle2D(
0,
topBounds.getMaxY(),
image.getWidth(),
bottomBounds.getMinY() - topBounds.getMaxY()));
});
HBox root = new HBox(container, button, imageViewRestricted);
root.setFillHeight(false);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}

Display JavaFX Image without blur/dithering when zooming in

I'm trying to create a image editor tool, where I have X- and Y-axis. Now I want to support the user by seeing each pixel when zooming in so that they can work more exactly. In comparison you should take a look at paint.net where you can see the pixels when zooming in.
I'll give you a code snippet and perhaps you might have a hint for me where I should dig into (API or code examples). I've found nothing like it in the JAVAFX API documentation. I want to use an ImageView instance but not a Canvas.
ScrollPane scrollPane = new ScrollPane ();
BorderPane borderPane = new BorderPane();
Group group = new Group();
DoubleProperty zoomProperty = new SimpleDoubleProperty(1);
Scale scaleTransformation = new Scale();
scaleTransformation.xProperty().bind(zoomProperty);
scaleTransformation.yProperty().bind(zoomProperty);
ImageView viewer = new ImageView(getBackgroundImage());
viewer.setFitWidth(600);
viewer.setPreserveRatio(true);
viewer.setDisable(true);
viewer.setSmooth(false);
group.getChildren().add(viewer);
this.addEventFilter(ScrollEvent.SCROLL, new EventHandler<ScrollEvent>(){
#Override
public void handle(ScrollEvent se) {
if (se.isControlDown() && se.getDeltaY() > 0) {
double value = zoomProperty.get() * 1.1;
zoomProperty.set(value);
se.consume();
} else if (se.isControlDown() && se.getDeltaY() < 0) {
double value = zoomProperty.get() / 1.1;
zoomProperty.set(value);
se.consume();
}
}
});
borderPane.setCenter(group);
scrollPane.setHbarPolicy(ScrollBarPolicy.ALWAYS);
scrollPane.setVbarPolicy(ScrollBarPolicy.ALWAYS);
scrollPane setContent(new Group(borderPane));
Thanks for you help.
EDIT
The related question and it's answers do not solve my problem (JavaFX ImageView without any smoothing). The solution 1-4 do not work. Only the hack does. I guess I could be related the node structure I use. I've adjusted my code.
I have found one solution (referring to the possible duplicate) and using a variant of method 1.
In my image constructor I set the requested size to something large - so for an image that I know is about 1000 pixels wide I request an image 10000 pixels wide:
Image image = new Image(file.toURI().toString(), 10000.0, 10000.0, true, false);
I am not sure if this is giving a true view of the pixels, or if it is smoothing at a very low level...... but it works for my needs.

JavaFX - Slim images to prevent overloading the heapspace?

Due to programming an Image Viewer with JavaFX I implemented an image library with following structure:
HBox - ScrollPane - VBox
This is the basic structure, the library objects are constructed as following:
StackPane - ImageView - Label
Because there's no setAlignment method for ScrollPane, I put the whole thing in a HBox, even though I don't need the other features of it.
These methods are responsible for the generation and initialization of the library:
private HBox createScrollableLibrary()
{
libraryContent = new VBox();
libraryContent.setPadding(new Insets(10));
libraryContent.setAlignment(Pos.CENTER);
libraryContent.setStyle("-fx-background-color: transparent");
libraryContent.setSpacing(10);
libraryScroll = new ScrollPane();
libraryScroll.setHbarPolicy(ScrollBarPolicy.NEVER);
libraryScroll.setStyle("-fx-background: rgb(0, 0, 0, 0.3);"
+ "-fx-background-color: rgb(0, 0, 0, 0.3);");
libraryScroll.setMinWidth(135);
libraryScroll.setMaxWidth(135);
libraryScroll.setContent(libraryContent);
HBox root = new HBox();
root.setPickOnBounds(false);
root.getChildren().add(libraryScroll);
return root;
}
And:
public void initLibrary()
{
Runnable work = () ->
{
ObservableList<StackPane> previewList = FXCollections.observableArrayList();
ImageView preview;
Label label;
StackPane previewAndLabel;
for(int i = 0; i < picturePaths.size(); i++)
{
String path = picturePaths.get(i).toURI().toString();
preview = new ImageView(new Image(path));
preview.setPreserveRatio(true);
preview.setFitWidth(100);
//label = new Label(new File(path).getName());
label = new Label("IMAGE");
StackPane.setAlignment(label, Pos.CENTER_LEFT);
previewAndLabel = new StackPane();
previewAndLabel.getChildren().addAll(preview, label);
previewList.add(previewAndLabel);
}
Platform.runLater(() -> GUI.getLibraryContent().getChildren().addAll(previewList));
};
loading = new Thread(work);
if(loading.isAlive())
loading.interrupt();
loading.start();
}
Info: picturePaths is an ObservableList which contains the paths of all selected images which in turn are selected with a FileChooser.
Now my question(s):
I know it's very inefficient to use large sized pictures as preview images.
Even though I increased my heap space to 1GB it slows down and throws a java.lang.OutOfMemoryError exception when I'm adding more pictures, I'm guessing the limit is about 20-25 of large sized pictures.
How can I "slim" a normal picture with, for example a resolution of 1920x1080 to 1024x720 ? Or is there another opportunity to make my library use small and "fast" images? And can I buffer my already "calculated" images to make my Image Viewer faster?
Appreciate any tips and help. Please critisize my code if needed!
One can try setting the size constraints on the preview pictures during the Image constructor and not afterwards.

Get Pane size without adding it to a scene (in JavaFX)?

If I create a Pane (with nodes inside it), how can I compute the width/height without rendering it on-screen?
I am trying to create a document (in memory) and send it to a printer. The problem is that I need to compute how many pages and to do that I need to get the dimensions of the document.
A quick example I'm experimenting with:
Label testLabel1 = new Label("TEST1");
Label testLabel2 = new Label("TEST no. 2");
GridPane testGl = new GridPane();
testGl.add( testLabel1, 1, 1 );
testGl.add( testLabel2, 2, 2 );
VBox testVBox = new VBox( testGl );
Pane testPane = new Pane( testVBox );
//I read that this might be required
testPane.applyCss();
testPane.layout();
//Also that a delay is needed for the fx tread to update testPane
// (but shouldn't this all be in the same thread since it is in the same function?
// It doesn't seem to help).
Platform.runLater( ()->{
System.out.println( ">>> "+ testPane.getBoundsInLocal().getWidth() );
});
All I ever output is >>> 0.0. Note that in the program I'm developing, I have multiple Panes inside a "container" Pane. Hence, the variable testPane.
Thank you.
You need to add the Pane to a Scene for the layouting to work. There is no need to display the Scene however and there is no need to keep the Pane in this scene:
Label testLabel1 = new Label("TEST1");
Label testLabel2 = new Label("TEST no. 2");
GridPane testGl = new GridPane();
testGl.add(testLabel1, 1, 1);
testGl.add(testLabel2, 2, 2);
VBox testVBox = new VBox(testGl);
Pane testPane = new Pane(testVBox);
// add testPane to some scene before layouting
Scene testScene = new Scene(testPane);
testPane.applyCss();
testPane.layout();
System.out.println(">>> " + testPane.getBoundsInLocal().getWidth());
// Pane could be removed from scene
Group replacement = new Group();
testScene.setRoot(replacement);
Note that if you want to remove the root from a Scene you have to replace it with a non-null node.

Resources