Hi I am supposed to create function which would do flood fill on a Pane containing Shapes using Java. It is supposed to behave just like MSPaint, I dont need to later move rectangles Lines or other shapes. I was thinking converting Pane into Image and then work with pixels and then clear all Panes children and insert it as a image but I cant make it work.
code example:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class Paint extends Application {
public static void main(String[] args) {
launch(args);
}
private Pane pane;
#Override
public void start(Stage primaryStage) {
pane= new Pane();
primaryStage.setTitle("Fill");
Scene scene = new Scene(pane,500,600);
primaryStage.setScene(scene);
primaryStage.show();
pane.setOnMousePressed((e)->{
doFill(e.getX(),e.getY());
});
//RECT 1
Rectangle rect1=new Rectangle(1,100,200,300);
rect1.setStroke(Color.BLACK);
rect1.setStrokeWidth(2);
rect1.setFill(Color.WHITE);
//RECT 2
Rectangle rect2=new Rectangle(50,150,200,400);
rect2.setStroke(Color.BLACK);
rect2.setStrokeWidth(2);
rect2.setFill(Color.WHITE);
//LINE
Line line=new Line(0,0,200,550);
rect2.setStroke(Color.BLACK);
rect2.setStrokeWidth(2);
pane.getChildren().addAll(rect1,rect2,line);
}
private void doFill(double eventX, double eventY){
//**TODO**
}
}
Got managed to do that function even though its a bit messy. So for everyone who is getting anxious over this:
private void doFill(double eventX, double eventY,boolean b){
WritableImage i=pane.snapshot(new SnapshotParameters(), null);
ArrayList<Integer> pozicie=new ArrayList<Integer>();
ArrayList<Character> strany=new ArrayList<Character>();
pozicie.add((int)eventX);
pozicie.add((int)eventY);
int c=i.getPixelReader().getColor((int)eventX,(int)eventY).hashCode();
if(c==usedColor.hashCode()){
//System.out.println("same color");
return;}
strany.add('a');
while(pozicie.size()!=0){
char strana=strany.remove(0);
int x=pozicie.remove(0);
int y=pozicie.remove(0);
i.getPixelWriter().setColor(x, y, usedColor);
if(strana=='d'){
//iba dole
if(y<pane.getHeight()-2 && i.getPixelReader().getColor(x, y+1).hashCode()==c){
pozicie.add(x);
pozicie.add(y+1);
strany.add('d');
}
}
else if(strana=='u'){
//iba hore
if( y>100 && i.getPixelReader().getColor(x, y-1).hashCode()==c){
pozicie.add(x);
pozicie.add(y-1);
strany.add('u');
}
}
else{
if(x>2 && i.getPixelReader().getColor(x-1, y).hashCode()==c){
pozicie.add(x-1);
pozicie.add(y);
strany.add('l');
}
if(x<pane.getWidth()-2 && i.getPixelReader().getColor(x+1, y).hashCode()==c){
pozicie.add(x+1);
pozicie.add(y);
strany.add('r');
}
if( y>101 && i.getPixelReader().getColor(x, y-1).hashCode()==c){
pozicie.add(x);
pozicie.add(y-1);
strany.add('u');
}
if(y<pane.getHeight()-2 && i.getPixelReader().getColor(x, y+1).hashCode()==c){
pozicie.add(x);
pozicie.add(y+1);
strany.add('d');
}
}
}
pane.getChildren().clear();
pane.getChildren().add(new ImageView(i));
}
Related
I'm currently creating a layout in JavaFX. However, the text of my buttons is showing in some places and for some buttons the text is just showing as '...'.
I want to display the whole text in the buttons while keeping the window size as 500x500.
Here's my code, I'm creating 10 buttons and only some of them are showing their text. Is there any way to set the button sizes constant throught the layout while also displaying their text?
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
public class MainGUI extends Application {
private Button[] buttons=new Button[10];
private Stage window;
private GridPane layout;
public int count=3;
#Override
public void start (Stage primaryStage)
{
window=primaryStage;
layout=new GridPane();
layout.setPadding(new Insets(100));
layout.setVgap(10);
layout.setHgap(10);
for(int i=0;i<10;i++)
{
buttons[i]=new Button();
}
buttons[0].setText("y=0.5x");
buttons[1].setText("y=x");
buttons[2].setText("y=2x");
buttons[3].setText("y=0.1x^2");
buttons[4].setText("y=x^2");
buttons[5].setText("y=10x^2");
buttons[6].setText("y=3 sin(x pi/10)");
buttons[7].setText("y=6 sin(x pi/10)");
buttons[8].setText("y=6 sin(x pi/5)");
buttons[9].setText("y= 5 sin(x pi/10) + (5/3)sin(3x pi/10) + sin(x pi/2) + (5/7)sin(7x pi/10)");
for(int j=0;j<10;j++)
{
if (count==3)
{
count=0;
}
if(j<=2)
{
GridPane.setConstraints(buttons[j],count,0);
}
else if ((j>2) && (j<=5))
{
GridPane.setConstraints(buttons[j],count,1);
}
else if((j>5) && (j<=8))
{
GridPane.setConstraints(buttons[j],count,2);
}
else if((j>8) && (j<=9))
{
GridPane.setConstraints(buttons[j],1,3);
}
count++;
}
layout.getChildren().addAll(buttons);
Scene scene=new Scene(layout,600,600);
window.setScene(scene);
window.show();
}
public static void main(String[] args)
{
launch(args);
}
}
This is how it looks with 0 insets :
As you can see it is quiet tight.
An easy alternative would be to make the last long button span over two columns:
GridPane.setConstraints(buttons[j],1,3,2,1);
Which results in
For more control over the layout consider using some child panes. For example:
I'm using JavaFX to create a Java application which is able to apply a TranslateTransition to a generic node and recall it continuously.
I retrieved a simple right arrow from this url https://www.google.it/search?q=arrow.png&espv=2&source=lnms&tbm=isch&sa=X&ved=0ahUKEwiGheeJvYrTAhWMB5oKHU3-DxgQ_AUIBigB&biw=1600&bih=764#imgrc=rH0TbMkQY2kUaM:
and used it to create the node to translate.
This is my AnimatedNode class:
package application.model.utils.addon;
import javafx.animation.TranslateTransition;
import javafx.scene.Node;
import javafx.util.Duration;
public class AnimatedNode {
private Node node;
private double positionY;
private TranslateTransition translateTransition;
private boolean animated;
private int reverse = 1;
public AnimatedNode(Node node, double animationTime) {
setPositionY(0.0);
setNode(node);
setTranslateTransition(animationTime);
}
public void play() {
if(translateTransition != null && !isAnimated()) {
setAnimated(true);
new Thread() {
#Override
public void run() {
while(isAnimated()) {
translateTransition.setToY(positionY + 50 * reverse);
translateTransition.play();
reverse = -reverse;
setPositionY(translateTransition.getToY());
}
}
}.start();
}
}
public void stop() {
setAnimated(false);
}
public Node getNode() {
return node;
}
private void setNode(Node node) {
this.node = node;
}
public TranslateTransition getTranslateTransition() {
return translateTransition;
}
private void setTranslateTransition(double animationTime) {
translateTransition = new TranslateTransition();
if(node != null) {
translateTransition.setDuration(Duration.seconds(animationTime));
translateTransition.setNode(node);
}
}
public double getPositionY() {
return positionY;
}
private void setPositionY(double positionY) {
this.positionY = positionY;
}
public boolean isAnimated() {
return animated;
}
private void setAnimated(boolean animated) {
this.animated = animated;
}
}
and this is the Application class
package test;
import application.model.utils.addon.AnimatedNode;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
public class Test extends Application {
private final String TITLE = "Test application";
private final double WIDTH = 600;
private final double HEIGHT = 400;
private final String ARROW_PATH = "file:resources/png/arrow.png";
private BorderPane rootPane;
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle(TITLE);
rootPane = new BorderPane();
rootPane.setPrefSize(WIDTH, HEIGHT);
Image image = new Image(ARROW_PATH);
ImageView imageView = new ImageView(image);
imageView.setFitWidth(WIDTH);
imageView.setFitHeight(HEIGHT);
imageView.setPreserveRatio(true);
AnimatedNode animatedNode = new AnimatedNode(imageView, 0.7);
Pane pane = new Pane();
pane.getChildren().add(animatedNode.getNode());
pane.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent arg0) {
if(arg0.getButton().equals(MouseButton.PRIMARY))
animatedNode.play();
if(arg0.getButton().equals(MouseButton.SECONDARY))
animatedNode.stop();
}
});
rootPane.setCenter(pane);
Scene scene = new Scene(rootPane, WIDTH, HEIGHT);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
The node is added to a generic pane; the pane has a MouseListener. I can start the TranslateTransition by using the primary button of the mouse and stop it with the secondary one.
I used a Thread in the play() method of AnimatedNode but I still have a continuous delay in the transition.
Is this the best way to perform the transition? Can I improve my code?
Thanks a lot for your support.
Sample
This is a simplified example which demonstrates a continuous animation started and stopped by left and right mouse clicks.
import javafx.animation.*;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.image.*;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import javafx.util.Duration;
public class BouncingCat extends Application {
private static final double WIDTH = 100;
private static final double HEIGHT = 100;
private final String ARROW_PATH =
"http://icons.iconarchive.com/icons/iconka/meow-2/64/cat-rascal-icon.png";
// image source: http://www.iconka.com
#Override
public void start(Stage stage) {
Image image = new Image(ARROW_PATH);
ImageView imageView = new ImageView(image);
TranslateTransition animation = new TranslateTransition(
Duration.seconds(0.7), imageView
);
animation.setCycleCount(Animation.INDEFINITE);
animation.setFromY(0);
animation.setToY(50);
animation.setAutoReverse(true);
Pane pane = new Pane(imageView);
Scene scene = new Scene(pane, WIDTH, HEIGHT);
scene.setOnMouseClicked(e -> {
switch (e.getButton()) {
case PRIMARY:
animation.play();
break;
case SECONDARY:
animation.pause();
break;
}
});
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Advice
You don't need a Thread when you have a Transition. JavaFX will render updated transition frames automatically each pulse.
I don't advise keeping track of properties in a class, when those same values are already represented in the underlying tools you use.
For example:
replace int reverse = 1; with transition.setAutoReverse(true) or transition.setRate(1) (or -1).
replace animated with transition.getStatus().
instead of double positionY, set the toY of the transition.
I wouldn't advise calling your class AnimatedNode unless it extended node, otherwise it is confusing, instead call it something like AnimationControl.
import javafx.animation.TranslateTransition;
import javafx.scene.Node;
import javafx.util.Duration;
public class AnimationControl {
private final TranslateTransition translateTransition;
public AnimationControl(Duration duration, Node node) {
translateTransition = new TranslateTransition(duration, node);
}
public TranslateTransition getTranslateTransition() {
return translateTransition;
}
}
You only need to encapsulate the node and the transition in the AnimationControl and not other fields unless you need further functionality not apparent in your question and not already provided by Node or Transition. If you have that extra functionality then you can enhance the AnimationControl class above to add it.
Exposing the node and the translate transition is enough, as if the user wants to manage the animation, such as starting and stopping it, then the user can just get it from the AnimationControl class. Depending on your use case, the entire AnimationControl class might be unnecessary as you might not need the encapsulation it provides and might instead prefer to just work directly with the node and the transition (as demoed in the sample).
I have two different StackPanes with HBoxes I want to change the image of a label in the second StackPane with a Label(MouseListener) in the first StackPane I think the problem is that the Label doesn't gets repainted or reloaded
First StackPane:
Label label= new Label("",new ImageView(ClearSpace));
label.addEventHandler(MouseEvent.MOUSE_CLICKED, (MouseEvent event) -> {
HotBar hb = new HotBar();
if(hb.getX1() == 0){
hb.setImageX1(5);
}
event.consume();
});
Second StackPane(HotBar):
public Label x1;
Image image= new Image(getClass().getResourceAsStream("/resources/images/Test.png"));
...
Label x1 = new Label("",new ImageView(image));
...
public void setImage(int i){
if(i == 5){
x1.setGraphic(new ImageView(image2));
}
}
I think these are the importantst parts of hte code
setImage() is definetly working if you use it below Label x1 = ... it works
In your EventHandler you create a new instance of HotBar on which you do changes, but this instance is not linked to the scene.
Instead you should pass the instance of HotBar into the other class and use that in your event handler.
package helloworld;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
/**
* Created by matt on 3/22/16.
*/
public class SwapLabel extends Application {
int i = 0;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
Pane root = new Pane();
Image img1 = new Image("http://www.logotemplater.com/freelogostemplates/voope-free-logo-template.png", true);
Image img2 = new Image("http://www.logotemplater.com/freelogostemplates/zoozz-vector-logo-template-sample.png", true);
Label l = new Label("", new ImageView(img1));
l.addEventHandler(MouseEvent.MOUSE_CLICKED, e->{
i = (i+1)%2;
if(i==0){
l.setGraphic(new ImageView(img1));
}else{
l.setGraphic(new ImageView(img2));
}
});
root.getChildren().add(l);
primaryStage.setScene(new Scene(root, 200, 200));
primaryStage.show();
}
}
This appears to be what you are trying to do, but this works. So most likely something else is happening.
I'm writing a code for a bouncing ball program and everything has worked so far but I don't know how to set a background image for my stage. I'm beginner in java (well , obviously) and tried to use ImageView class to set a background for my stage. but no luck so far.
Thank you so much .
package particlesystemexample;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.input.KeyCode;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class ParticleSystemExample extends Application {
#Override
public void start(Stage primaryStage) {
ParticleAnimationPane paPane = new ParticleAnimationPane();
// mouse actions to pause and resume animation
//paPane.setOnMousePressed(e -> paPane.pause()); // Java 8.0
paPane.setOnMousePressed(new EventHandler<MouseEvent>() {
public void handle(MouseEvent me) {
paPane.init(); }
});
// paPane.setOnMouseReleased(e -> paPane.play()); // Java 8.0
paPane.setOnMouseReleased(new EventHandler<MouseEvent>() {
public void handle(MouseEvent me) {
paPane.play(); }
});
// buttons to increase and decrease animation speed
paPane.setOnKeyPressed(e -> {
if(e.getCode() == KeyCode.UP){
paPane.increaseSpeed();
}
else if (e.getCode() == KeyCode.DOWN){
paPane.decreaseSpeed();
}
});
// Create a scene and place it on stage
Scene scene = new Scene(paPane, 450, 350);
primaryStage.setTitle("Particle System Example");
primaryStage.setScene(scene);
primaryStage.show();
paPane.init(); // initialize the particles
paPane.requestFocus();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
Edit :
I tried using this code to display the image but its not working ...
StackPane sp = new StackPane();
Image img = new Image("http://mikecann.co.uk/wp-content/uploads/2009/12/javafx_logo_color_1.jpg");
ImageView imgView = new ImageView(img);
sp.getChildren().add(imgView);
Personally, I use the BufferedImage and Image classes to deal with all of my image needs.
You can use the Image class for this one.
Here is an example:
try {
Graphics.drawImage(ImageIO.read(new File("Picture.png")), fX, fY, fW, fH, sX, sY, sW, sH,null);
} catch (IOException e) {
e.printStackTrace();
}
The things starting with F are the bounds for the rectangle that you will be painting it on.
The things starting with s are the bounds for what part of the picture you are painting.
I'm not exactly sure what the null at the end corresponds to. I just ignore it and everything works out fine.
I am using JavaFX to build a GUI and I'm having problems on knowing the mouse location when resized. The idea is that if a mouse gets on the very edge of the GUI it changes to a double sided arrow indicating that you can now press the mouse and resize the window.
I need the location of the mouse pointer on this edge, but I don't know how to do that. I need to know in which direction the window is resized.
Updated Added new options and discussed pros and cons.
This is tricky. The issue is that the window keeps track of its top, left, width, and height. When it is resized from the right or bottom, things are easy enough: the width or height change. But when it is resized from the left, both x and width must change. These two changes do not happen atomically, as x and width are stored as two independent properties.
The first approach to this is to keep track of the mouse coordinates, and just see if it's in the left half or the right half. (Obviously you can do the same with the height.) This approach is independent of any implementation details, but the user can cause it to fail by being extremely careful with the mouse. If you move the mouse to the right edge of the window to the exact pixel of the window boundary, then resize, you can see incorrect output.
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
BorderPane root = new BorderPane();
Scene scene = new Scene(root,400,400);
class MouseLocation {
double x,y ;
}
MouseLocation mouseLocation = new MouseLocation();
scene.setOnMouseMoved(event -> {
mouseLocation.x = event.getX();
mouseLocation.y = event.getY();
});
primaryStage.widthProperty().addListener((obs, oldWidth, newWidth) -> {
if (mouseLocation.x < primaryStage.getWidth() / 2) {
System.out.println("Resized from left");
} else {
System.out.println("Resized from right");
}
});
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
The second approach is to keep track of the last known horizontal range of the window (minX and maxX), and update that range when the width changes. Then you can check to see whether the minX or maxX has changed. The problem with this approach is that it's dependent on undocumented implementation details. It appears (on my system, using the current version, etc) that when the window is resized from the left, x is changed first, then the width is changed. If that were to change in a subsequent release, the following would break:
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
BorderPane root = new BorderPane();
Scene scene = new Scene(root,400,400);
class MutableDouble {
double value ;
}
MutableDouble windowLeftEdge = new MutableDouble();
primaryStage.widthProperty().addListener((obs, oldWidth, newWidth) -> {
if (primaryStage.getX() == windowLeftEdge.value) {
System.out.println("Resized from right");
} else {
System.out.println("Resized from left");
}
windowLeftEdge.value = primaryStage.getX() ;
});
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
The third approach (that I can think of) is to coalesce changes that happen quickly into a single change. This is a bit tricky to program correctly, so instead of doing it from scratch, I used the third party ReactFX framework which models "event streams" and has a built-in mechanism for combining events that happen in quick succession. This is probably the most robust of the three solutions presented here, but at the cost of either a degree of complexity, or the inclusion of an external framework.
import java.time.Duration;
import org.reactfx.Change;
import org.reactfx.EventStream;
import org.reactfx.EventStreams;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.value.ObservableValue;
import javafx.geometry.BoundingBox;
import javafx.geometry.Bounds;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class ReactFXVersion extends Application {
#Override
public void start(Stage primaryStage) {
BorderPane root = new BorderPane();
Scene scene = new Scene(root,400,400);
ObservableValue<Bounds> windowBounds = Bindings.createObjectBinding(() ->
new BoundingBox(primaryStage.getX(), primaryStage.getY(), primaryStage.getWidth(), primaryStage.getHeight()),
primaryStage.xProperty(), primaryStage.yProperty(), primaryStage.widthProperty(), primaryStage.heightProperty());
EventStream<Change<Bounds>> bounds = EventStreams.changesOf(windowBounds)
.reduceSuccessions((previousChange, nextChange) ->
new Change<>(previousChange.getOldValue(), nextChange.getNewValue()),
Duration.ofMillis(10));
bounds.subscribe(boundsChange -> {
Bounds newBounds = boundsChange.getNewValue();
Bounds oldBounds = boundsChange.getOldValue();
if (newBounds.getWidth() != oldBounds.getWidth()) {
if (newBounds.getMinX() != oldBounds.getMinX()) {
System.out.println("Resized from left");
} else if (newBounds.getMaxX() != oldBounds.getMaxX()) {
System.out.println("Resized from right");
}
}
if (newBounds.getHeight() != oldBounds.getHeight()) {
if (newBounds.getMinY() != oldBounds.getMinY()) {
System.out.println("Resized from top");
} else if (newBounds.getMaxY() != oldBounds.getMaxY()) {
System.out.println("Resized from bottom");
}
}
});
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}