2

I wrote the following code for adding and deleting points or circles with mouse events. The next step is joining them with a line while I'm creating them (creating a polygon). I'm completely stuck and do not know where to start. I am looking for documentation but I will appreciate if somebody could point me in the right direction.

package application;

//import all needed classes
import javafx.collections.ObservableList;
import javafx.scene.input.MouseButton;
import javafx.application.Application;
import javafx.scene.shape.Circle;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.scene.Node;

public class Main extends Application {

    // using the base class of layout panes
    Pane pane = new Pane();

    @Override
    public void start(Stage primaryStage) {

        // what to do when the mouse is clicked
        pane.setOnMouseClicked(e -> {
            double drawX = e.getX();// position of mouse in X axle
            double drawY = e.getY();// position of mouse in y axle

            if (e.getButton() == MouseButton.PRIMARY) {// get the position of the mouse point when user left click
                Circle circle = makeCircle(drawX, drawY);// using the makeCircle method to draw the circle where the mouse is at the click
                pane.getChildren().add(circle);
            } else if (e.getButton() == MouseButton.SECONDARY) {
                deleteCircle(drawX, drawY);// using the deleteCircle function to delete the circle if right click on the circle
            }

        });

        // container to show all context in a 500px by 500px windows
        try {
            Scene scene = new Scene(pane, 500, 500);// size of the scene
            primaryStage.setScene(scene);
            primaryStage.show();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // method to draw the circle when left click
    private Circle makeCircle(double drawX, double drawY) {
        Circle circle = new Circle(drawX, drawY, 7, Color.CORAL);// create the circle and its properties(color: coral to see it better)
        circle.setStroke(Color.BLACK);// create the stroke so the circle is more visible
        return circle;
    }

    // method to delete the circle using the ObservableList class
    private void deleteCircle(double deletX, double deleteY) {

        // loop to create my list of circles 'til this moment
        ObservableList<Node> list = pane.getChildren();
        for (int i = list.size() - 1; i >= 0; i--) {
            Node circle = list.get(i);

            // checking which circle I want to delete
            if (circle instanceof Circle && circle.contains(deletX, deleteY)) {
                pane.getChildren().remove(circle);
                break;
            }
        }
    }

    public static void main(String[] args) {
        Application.launch(args);
    }
}
Pang
  • 9,564
  • 146
  • 81
  • 122
gnimm
  • 17
  • 4

1 Answers1

2

I would add a change listener to the children of your pane, add a polygon to the pane and if there are more then 2 Circles, you redraw the polygon:

public class Main extends Application {

    // using the base class of layout panes
    Pane pane = new Pane();
    Polygon polygon = new Polygon();

    @Override
    public void start(Stage primaryStage) {

        // what to do when the mouse is clicked
        pane.setOnMouseClicked(e -> {
            double drawX = e.getX();// position of mouse in X axle
            double drawY = e.getY();// position of mouse in y axle

            if (e.getButton() == MouseButton.PRIMARY) {// get the position of the mouse point when user left click
                Circle circle = makeCircle(drawX, drawY);// using the makeCircle method to draw the circle where the mouse is at the click
                pane.getChildren().add(circle);
            } else if (e.getButton() == MouseButton.SECONDARY) {
                deleteCircle(drawX, drawY);// using the deleteCircle function to delete the circle if right click on the circle
            }

        });

        pane.getChildren().add(polygon);

        pane.getChildren().addListener(new ListChangeListener<Node>() {
            @Override
            public void onChanged(Change<? extends Node> c) {
                int numOfCircles = pane.getChildren().size() - 1;
                if ( numOfCircles >= 2 ) {
                    polygon.setStroke(Color.BLACK);
                    polygon.getPoints().clear();
                    for ( int i = 0; i <= numOfCircles; i++ ) {
                        Node node = pane.getChildren().get(i);
                        if ( node.getClass() == Circle.class ) {
                            polygon.getPoints().addAll(
                                    ((Circle) node).getCenterX(),
                                    ((Circle) node).getCenterY()
                            );
                        }
                    }
                    System.out.println(polygon.getPoints());
                }
            }
        });

        // container to show all context in a 500px by 500px windows
        try {
            Scene scene = new Scene(pane, 500, 500);// size of the scene
            primaryStage.setScene(scene);
            primaryStage.show();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // method to draw the circle when left click
    private Circle makeCircle(double drawX, double drawY) {
        Circle circle = new Circle(drawX, drawY, 7, Color.CORAL);// create the circle and its properties(color: coral to see it better)
        circle.setStroke(Color.BLACK);// create the stroke so the circle is more visible
        return circle;
    }

    // method to delete the circle using the ObservableList class
    private void deleteCircle(double deletX, double deleteY) {

        // loop to create my list of circles 'til this moment
        ObservableList<Node> list = pane.getChildren();
        for (int i = list.size() - 1; i >= 0; i--) {
            Node circle = list.get(i);

            // checking which circle I want to delete
            if (circle instanceof Circle && circle.contains(deletX, deleteY)) {
                pane.getChildren().remove(circle);
                break;
            }
        }
    }

    public static void main(String[] args) {
        Application.launch(args);
    }

}

You can also not use the change listener, but call the redraw whenever you add or delete a circle. In addition you can also add an onclick listener to each circle calling the delete function and deleting in a more efficient manner:

public class Main extends Application {

    // using the base class of layout panes
    Pane pane = new Pane();
    Polygon polygon = new Polygon();

    @Override
    public void start(Stage primaryStage) {

        // what to do when the mouse is clicked
        pane.setOnMouseClicked(e -> {
            double drawX = e.getX();// position of mouse in X axle
            double drawY = e.getY();// position of mouse in y axle

            if (e.getButton() == MouseButton.PRIMARY) {// get the position of the mouse point when user left click
                Circle circle = makeCircle(drawX, drawY);// using the makeCircle method to draw the circle where the mouse is at the click
                pane.getChildren().add(circle);
                circle.setOnMouseClicked( event -> {
                    deleteCircle(circle);
                    // consume event so that the pane on click does not get called
                    event.consume();
                });
                redrawPolygon();
            }

        });

        polygon = new Polygon();
        polygon.setFill(Color.TRANSPARENT);
        polygon.setStroke(Color.BLACK);
        pane.getChildren().add(polygon);

        // container to show all context in a 500px by 500px windows
        try {
            Scene scene = new Scene(pane, 500, 500);// size of the scene
            primaryStage.setScene(scene);
            primaryStage.show();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // method to draw the circle when left click
    private Circle makeCircle(double drawX, double drawY) {
        Circle circle = new Circle(drawX, drawY, 7, Color.CORAL);// create the circle and its properties(color: coral to see it better)
        circle.setStroke(Color.BLACK);// create the stroke so the circle is more visible
        return circle;
    }

    private void redrawPolygon() {
        int numOfCircles = pane.getChildren().size() - 1;
        if ( numOfCircles > 0 ) {
            polygon.getPoints().clear();
            for ( int i = 0; i <= numOfCircles; i++ ) {
                Node node = pane.getChildren().get(i);
                if ( node.getClass() == Circle.class ) {
                    polygon.getPoints().addAll(
                            ((Circle) node).getCenterX(),
                            ((Circle) node).getCenterY()
                    );
                }
            }
        }
    }

    private void deleteCircle(Circle circle){
        pane.getChildren().remove(circle);
        redrawPolygon();
    }

    public static void main(String[] args) {
        Application.launch(args);
    }

}

If you want to only use one click handler you could also do it this way:

   pane.setOnMouseClicked(e -> {

        if ( e.getTarget().getClass() == Circle.class ) {
            deleteCircle((Circle)e.getTarget());
        } else {
            Circle circle = makeCircle(e.getX(), e.getY());// using the makeCircle method to draw the circle where the mouse is at the click
            pane.getChildren().add(circle);
            redrawPolygon();
        }

    });
JohnRW
  • 748
  • 7
  • 22
  • Thank you so much! I really appreciate your help. I will give it a try when I get home today. – gnimm Mar 22 '16 at 12:24
  • It is working great. Now my next step is that I don't want the lines to cross each other. I know i need to use an algorithm. Does somebody knows what is the name of the algorithm and/or how implement it in my code? – gnimm Mar 22 '16 at 23:00
  • There are other solutions for this on stack overflow. [here is my own though](https://gist.github.com/anonymous/8b4a0e85ccf897d9cec0). What i am doing is calculating a center and then the angle of all points compared to that center. After that i sort by angle and draw the polygon using the sorted list. [This is another solution](http://stackoverflow.com/questions/14263284/create-non-intersecting-polygon-passing-through-all-given-points). – JohnRW Mar 23 '16 at 07:21
  • [Here is a picture to explain my solution](http://i.imgur.com/qWNSZ20.png). The red dots are our polygon points. The black dot is our center. The yellow line is the base line that i use to calculate the angles of the black lines with. The black lines are the lines through the center and a polygon point. – JohnRW Mar 23 '16 at 07:24
  • It might not be the fastest / most efficient way, but its what i could come up with the fastest :D – JohnRW Mar 23 '16 at 07:24
  • Thank you so much! that works for me. Looking into the links you posted, I understand the whole concept now. I will try to simplify my code now in a more efficient way. Again, thank you! – gnimm Mar 23 '16 at 18:15
  • @grimm Would you mark it as an answer if it did solve your question? – dance2die Sep 23 '17 at 21:59