I have a javafx application and a rectangle inside it and I use rectangle.setFill() for filling the rectangle with an image. I want to know how I can reverse the image of my rectangle. ( I want to reverse it both vertically and horizontally and their combinations.)
Let's say the image I put on my rectangle is blue and has a red circle on the upper right part. I want to have images with the red circle being located in the lower right, lower left, and upper left side of the rectangle.
I have found some solutions with Canvas and GraphicsContext but it seems they are not applicable to Rectangle. Any solutions for Rectangle?
Also, I have put my rectangle in an anchorpane, if it's important to know.
The easiest option is probably to transform the node, rather than trying to flip the image itself. Two benefits of that are:
JavaFX provides easy ways to transform (translate, rotate, scale, etc.) nodes, and
You can use a single Image for all nodes.
The simplest transform to use in this case, as pointed out by #mipa, is scaling. To flip the node horizontally use node.setScaleX(-1). To flip the node vertically use node.setScaleY(-1).
Here's an example showing all four of your desired orientations:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.layout.GridPane;
import javafx.scene.paint.ImagePattern;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
double width = 250;
double height = 250;
Image image = new Image(/* your image URL */, width, height, true, true);
ImagePattern fill = new ImagePattern(image);
Rectangle normal = new Rectangle(width, height, fill);
Rectangle horizontal = new Rectangle(width, height, fill);
Rectangle vertical = new Rectangle(width, height, fill);
Rectangle both = new Rectangle(width, height, fill);
flipNode(horizontal, true, false);
flipNode(vertical, false, true);
flipNode(both, true, true);
GridPane grid = new GridPane();
grid.setVgap(10);
grid.setHgap(10);
grid.setPadding(new Insets(10));
grid.setAlignment(Pos.CENTER);
grid.add(normal, 0, 0);
grid.add(horizontal, 1, 0);
grid.add(vertical, 0, 1);
grid.add(both, 1, 1);
primaryStage.setScene(new Scene(grid));
primaryStage.show();
}
private void flipNode(Node node, boolean horiztonally, boolean vertically) {
node.setScaleX(horiztonally ? -1 : 1);
node.setScaleY(vertically ? -1 : 1);
}
}
The other transform you could use is rotation, but the above is easier.
Related
import javafx.geometry.VPos;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.scene.text.TextAlignment;
import javafx.scene.text.TextBoundsType;
import javafx.stage.Stage;
public class TextSize extends javafx.application.Application {
#Override
public void start(Stage stage) {
var rectangle = new Rectangle(600, 100, Color.YELLOWGREEN);
var text = new Text(0, 50, "EXAMPLING");
text.setFont(Font.font("Monospaced", 100));
text.setBoundsType(TextBoundsType.VISUAL); // Removes text padding, but not the one on the left.
text.setTextAlignment(TextAlignment.CENTER); // Doesn't align text horizontally.
text.setTextOrigin(VPos.CENTER); // Aligns text vertically.
stage.setScene(new Scene(new Pane(rectangle, text)));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I need to edit the text.
I primarily want to set the correct Text size. Specifically, the distance between the Text and each edge of the Rectangle should be at least 5 pixels. I could achieve this by defining the size of the Text in pixels. In this case, the text height should be a maximum of 90px and the text length should be a maximum of 590px. However, the font size is not defined in pixels. Even if I enter it using fxml, it has no effect. How do I define it, please?
I want to place the text in the center of the Rectangle. Therefore, I need the position of the Text to define its center. var text = new Text(300, 50, "EXAMPLING"); But, as I said in the comments in the code, I couldn't align the text horizontally and remove the padding on the left (As can be seen from the picture). You will probably say that I should use StackPane. But, this is just an example, so I want to know if it can be achieved without it, please?
This Font has all the letters the same width, but I don't like it very much. Do you know of a better Font (Arial style), which also has the same width letters?
Please help.
Thank you
UPDATE:
I solved the size(1.) and font(3.) of the text using this code.
var rectangle = new Rectangle(600, 100, Color.YELLOWGREEN);
var text = new Text(0, rectangle.getHeight() * 0.5, "EXAMPLING");
text.setFont(Font.loadFont(getClass().getResourceAsStream("GT Pressura Mono Regular Regular.ttf"), rectangle.getHeight() * 1.3));
text.setBoundsType(TextBoundsType.VISUAL);
var max_width = rectangle.getWidth() - 7;
for (var i = text.getFont().getSize() - 0.5; text.getLayoutBounds().getWidth() >= max_width; i -= 0.5) {
text.setFont(new Font(text.getFont().getName(), i));
}
text.setTextAlignment(TextAlignment.CENTER);
text.setTextOrigin(VPos.CENTER);
stage.setScene(new Scene(new Pane(rectangle, text)));
stage.show();
However, I still don't know how to solve the positioning(2.).
I am trying to write code that will draw 3 shapes diagonally across a grid. The first two shapes are a square and a circle, which I was able to do.
The third shape, however, is giving me some grief. I am supposed to draw a cross (T version, not X), and every time I write out the code it comes out looking like a sideways, ⊢. I know I am just missing something simple, but I would really appreciate the help!
Here is the full code for my Shapes program.
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.scene.paint.Color;
public class Shapes extends Application {
public void start(Stage primaryStage) {
// This will build the shapes which include a Square, Circle, and 2 Lines.
// All shapes will have a width of 3.
// This Rectangle will be colored like the Square on a playstation controller
Rectangle square = new Rectangle(65, 65, 65, 65);
square.setStroke(Color.rgb(243, 211, 234));
square.setStrokeWidth(3);
square.setFill(Color.rgb(243, 211, 234));
// A circle colored like the Circle on the playstation controller.
Circle circle = new Circle(40);
circle.setStroke(Color.rgb(241, 188, 194));
circle.setStrokeWidth(3);
circle.setFill(Color.rgb(241, 188, 194));
// Lines colored like the Cross button on a playstation controller.
Line line1 = new Line(-50, 75, 50, 75);
line1.setStroke(Color.rgb(165, 191, 214));
line1.setStrokeWidth(3);
Line line2 = new Line(0, 0, 0, 100);
line2.setStroke(Color.rgb(165, 191, 214));
line2.setStrokeWidth(3);
// Setup the GridPane in the center of the stage which will also pad out from the edge of the window.
GridPane pane = new GridPane();
pane.setAlignment(Pos.CENTER);
pane.setPadding(new Insets(11.5, 12.5, 13.5, 14.5));
// Place each object in it's respective place on the pane.
// Square top left, Circle, middle, Cross bottom right.
pane.add(square, 0, 0);
pane.add(circle, 1, 1);
pane.add(line1, 2, 2);
pane.add(line2, 2, 2);
// Create the scene to display the program.
Scene scene = new Scene(pane);
primaryStage.setTitle("Shapes");
primaryStage.setScene(scene);
primaryStage.show();
primaryStage.setResizable(false);
}
public static void main(String[] args) {
launch(args);
}
}
And here is the specific snippet I am having trouble with.
// Lines colored like the Cross button on a playstation controller.
Line line1 = new Line(-50, 75, 50, 75);
line1.setStroke(Color.rgb(165, 191, 214));
line1.setStrokeWidth(3);
Line line2 = new Line(0, 0, 0, 100);
line2.setStroke(Color.rgb(165, 191, 214));
line2.setStrokeWidth(3);
I do need the horizontal line to be a bit higher up on the pane. It should resemble a "Christian cross."
Any help is much appreciated.
It looks like the geometry is OK, but the alignment of line2 is wrong. Among the several ways to center it,
Set the alignment explicitly for the relevant GridPane child node:
pane.setHalignment(line2, HPos.CENTER);
Add the lines to a Pane having the desired layout; StackPane, for example, defaults to Pos.CENTER:
StackPane lines = new StackPane(line1, line2);
pane.add(lines, 2, 2);
As an aside, judicious use of constants will make tinkering a little easier. For example, use a scale value to keep sizes proportional, as shown here:
private static final int N = 50;
…
Rectangle square = new Rectangle(2 * N, 2 * N);
Circle circle = new Circle(N);
Line line1 = new Line(-N, 0, N, 0);
Line line2 = new Line(0, -N, 0, N);
I do need the horizontal line to be a bit higher up on the pane. It should resemble a "Christian cross."
Using the approach suggested by #fabian, adjust the horizontal line's endpoints as desired; note the changes for a Latin cross, seen in the image below:
Line line1 = new Line(-N, 0, N, 0); // Greek
Line line1 = new Line(-N, -N/3, N, -N/3); // Latin
…
pane.add(new Group(line1, line2), 2, 2);
GridPane aligns it's children inside the cells you add them to. This results in the relative position of the Lines changing. To fix this I recommend wrapping the Lines in a parent that does not reposition it's children, e.g. Group.
The following change will result in a "christian cross"-like shape rotated by 180°.
// pane.add(line1, 2, 2);
// pane.add(line2, 2, 2);
pane.add(new Group(line1, line2), 2, 2);
I am having an issue with scaling a group containing shapes, and dragging the shapes around.
In the example below, I have a group containing 2 rectangles, after scaling the group, if I drag one of the rectangles, the other will go negative the distance of the other, i.e., if I drag the rectangle r2 down, the rectangle r will go up, etc
I am wondering if this is some sort of bug, or what the issue is here?
The code below is a working example of what my issue is.
NOTE: I ran a test on the bounds, and it only changes when the dragged rectangle moves outside the current bounds.
if moving to the bottom right the new bounds will be something similar to
BoundingBox [minX:250.0, minY:250.0, minZ:0.0, width:301.7606201171875, height:338.6553955078125, depth:0.0, maxX:551.7606201171875, maxY:588.6553955078125, maxZ:0.0]
Even though the other rectangle moves outside of the minX and minY bounds.
import javafx.scene.shape.Rectangle;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class SampleScale extends Application {
#Override
public void start(Stage primaryStage) {
Group g = new Group();
Rectangle r = new Rectangle(250,250,10,10);
Rectangle r2 = new Rectangle(260,260,10,10);
r2.setFill(Color.RED);
r2.setOnMouseDragged(event
->
{
r2.setTranslateX(r2.getTranslateX() + (event.getX() -r2.getX()));
r2.setTranslateY(r2.getTranslateY() + (event.getY() -r2.getY()));
g.autosize();
event.consume();
});
Pane p = new Pane();
p.setPrefSize(1000, 1000);
g.getChildren().addAll(r,r2);
p.getChildren().addAll(g);// comment out to test bottom code;
//default works
//g.setScaleX(1);
//g.setScaleY(1);
//causes other node to move negative distance of the other object
// i.e., if r2 is dragged down, r will move up, etc.
g.setScaleX(2);
g.setScaleY(2);
//-------------------------
//this works if not placed inside a group
// p.getChildren().addAll(r,r2);
// r.setScaleX(2);
// r.setScaleY(2);
// r2.setScaleX(2);
// r2.setScaleY(2);
Scene scene = new Scene(p, 1300, 1250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
The scaleX and scaleY properties use the center of a node as pivot point for scaling. Since moving r2 resizes the Group, the other children also move.
You could apply a Scale transform with pivot point (0, 0) instead:
// g.setScaleX(2);
// g.setScaleY(2);
g.getTransforms().add(new Scale(2, 2, 0, 0));
I am playing with JavaFX and in the class start(Stage theStage) I have the following code:
/*... Scene, stage, canvas ...*/
GraphicsContext gc = canvas.getGraphicsContext2D();
Image imgage = new Image("gfx/image.png");
gc.drawImage(image, 256, 256);
How I draw it rotated without rotating the axis? I've found and tried this:
gc.save(); // saves the current state on stack, including the current transform
gc.rotate(45);
gc.drawImage(image);
gc.restore();
However it also transforms the axis and when I move image using changing position x & y .. it goes wrong, because it makes axis rotate too. I've been thinking about recalculating movement using sin and cos functions, but I am sure there is such an easier solution.
It may works with BufferedImage, but they draws it by using different Graphics class and I dont know how to make it work together.
EDIT: okey, how to rotate Image only without roatiting the whole GraphicsContext?
SOLVED:
Thanks all for a help :)) I have solved it by using this >>
ImageView iv = new ImageView(new Image( "gfx/earth.png"));
iv.setRotate(40);
SnapshotParameters params = new SnapshotParameters();
params.setFill(Color.TRANSPARENT);
Image rotatedImage = iv.snapshot(params, null);
gc.drawImage(rotatedImage, 0, 0);
Let's see if I was able to understand your question...
how to rotate Image only without rotating the whole GraphicsContext?
You can't do that using just a GraphicsContext. You need to rotate the GraphicsContext to get a rotated image rendered onto it using the drawImage API. To prevent the rotation operation impacting other Canvas drawing operations, you can save and restore the GraphicsContext before and after performing the rotation (as it appears you are already doing from the code in your question).
However, one way to accomplish what you wish without rotating the GraphicsContext is to use a SceneGraph in combination with a Canvas. Place the Image in a ImageView, apply a rotate transform to the image, snapshot it to get a rotated image, then draw the rotated image into your Canvas.
ImageView iv = new ImageView(image);
iv.setRotate(40);
SnapshotParameters params = new SnapshotParameters();
params.setFill(Color.TRANSPARENT);
Image rotatedImage = iv.snapshot(params, null);
gc.drawImage(rotatedImage, 0, 0);
Sample code:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.SnapshotParameters;
import javafx.scene.canvas.*;
import javafx.scene.image.*;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
/** Rotates and places it in a canvas */
public class RotatedImageInCanvas extends Application {
#Override public void start(Stage stage) {
Image image = new Image(
"http://worldpress.org/images/maps/world_600w.jpg", 350, 0, true, true
);
// creates a canvas on which rotated images are rendered.
Canvas canvas = new Canvas(600, 400);
GraphicsContext gc = canvas.getGraphicsContext2D();
ImageView iv = new ImageView(image);
iv.setRotate(40);
SnapshotParameters params = new SnapshotParameters();
params.setFill(Color.TRANSPARENT);
Image rotatedImage = iv.snapshot(params, null);
gc.drawImage(rotatedImage, 0, 0);
// supplies a tiled background image on which the canvas is drawn.
StackPane stack = new StackPane();
stack.setMaxSize(canvas.getWidth(), canvas.getHeight());
stack.setStyle("-fx-background-image: url('http://1.bp.blogspot.com/_wV5JMD1OISg/TDYTYxuxR4I/AAAAAAAAvSo/a0zT8nwPV8U/s400/louis-vuitton-nice-beautiful.jpg');");
stack.getChildren().add(
canvas
);
// places a resizable padded frame around the canvas.
StackPane frame = new StackPane();
frame.setPadding(new Insets(20));
frame.getChildren().add(stack);
stage.setScene(new Scene(frame, Color.BURLYWOOD));
stage.show();
}
public static void main(String[] args) { launch(RotatedImageInCanvas.class); }
}
Normally, I would not recommend mixing scene graph and canvas APIs as done in the above sample, but instead just code to only the scene graph API or only the canvas API.
For a sample of a canvas only solution, see the answer to:
How to draw image rotated on JavaFX Canvas?
In general, for most operations, I find working with the Scene Graph API simpler than coding to the Canvas API and recommend using the Scene Graph API rather than the Canvas API for most tasks.
A rather quick-and-dirty method that also works is to translate and rotate the entire GraphicsContext and then draw the image relative to 0 (in this case, the rotation is from the middle of the image). Then, after the image is drawn, reverse the action.
For example, if you have an image (img), a GraphicsContext (gc) and a position for the image (xp, yp), then:
// Translate and rotate.
gc.translate(xp, yp);
gc.rotate(degrees);
// Note how the image is drawn relative to 0. Since the image needs to be
// rotated around the image center, the center is simply half of the image
// dimension.
gc.drawImage( image, -image.getWidth/2.0, -image.getImageHeight/2.0);
// Reverse the translation and rotation once drawn.
gc.rotate(-degrees);
gc.translate(-xp, -yp);
According to the other comment you should add setViewport and setTransform like I did in my rotateImage function if you want to rotate the image around a point and don't want to change the center of the image.
public Image rotateImage(Image image, int rotation) {
ImageView iv = new ImageView(image);
SnapshotParameters params = new SnapshotParameters();
params.setFill(Color.TRANSPARENT);
params.setTransform(new Rotate(rotation, image.getHeight() / 2, image.getWidth() / 2));
params.setViewport(new Rectangle2D(0, 0, image.getHeight(), image.getWidth()));
return iv.snapshot(params, null);
}
In JavaFX, what is the difference between a Pane and a Group? I can't make out any difference.
A Group is not resizable (meaning that its size is not managed by its parent in the scene graph), and takes on the union of the bounds of its child nodes. (So, in other words, the local bounds of a Group will be the smallest rectangle containing the bounds of all the child nodes). If it is larger than the space it is allocated in its parent, it will be clipped.
By contrast, a Pane is resizable, so its size is set by its parent, which essentially determine its bounds.
Here is a quick demo. The Group is on top and the Pane below. Both contain a fixed blue square at (100,100) and a green square which is moved by pressing the left/right arrow keys. Note how at the beginning, the blue square appears in the top left corner of the group, because the local bounds of the group start at the top-leftmost point of all its child nodes (i.e. the local bounds of the group extend from (100, 100) right and down). As you move the green rectangles "off screen", the group adjusts its bounds to incorporate the changes, wherever possible, whereas the pane remains fixed.
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class GroupVsPaneDemo extends Application {
#Override
public void start(Stage primaryStage) {
Pane pane = new Pane();
Group group = new Group();
VBox.setVgrow(group, Priority.NEVER);
VBox.setVgrow(pane, Priority.NEVER);
VBox vbox = new VBox(group, pane);
Rectangle rect1 = new Rectangle(100, 100, 100, 100);
Rectangle rect2 = new Rectangle(100, 100, 100, 100);
Rectangle rect3 = new Rectangle(200, 200, 100, 100);
Rectangle rect4 = new Rectangle(200, 200, 100, 100);
rect1.setFill(Color.BLUE);
rect2.setFill(Color.BLUE);
rect3.setFill(Color.GREEN);
rect4.setFill(Color.GREEN);
group.getChildren().addAll(rect1, rect3);
pane.getChildren().addAll(rect2, rect4);
Scene scene = new Scene(vbox, 800, 800);
scene.addEventHandler(KeyEvent.KEY_PRESSED, e -> {
double deltaX ;
switch(e.getCode()) {
case LEFT:
deltaX = -10 ;
break ;
case RIGHT:
deltaX = 10 ;
break ;
default:
deltaX = 0 ;
}
rect3.setX(rect3.getX() + deltaX);
rect4.setX(rect4.getX() + deltaX);
});
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
The few important difference between Pane and Group is that :
Pane can have its own size, where as a Group will take on the collective bounds of its children and is not directly resizable.
Pane can be used when you want to position its nodes at absolute position.
Also, note that Group was designed to be very lightweight and doesn't support a lot of styles. For example, you can't set border or background color for the group.
See this answer for more details.