4 Replies Latest reply: Nov 13, 2012 2:29 PM by jsmith RSS

    How to modify a line once drawn?

    935521
      This code plots a XY LineChart and draw lines on chart by left mouse click and if mouse hover, line get selected turns to red and can be deleted or moved.
      import javafx.application.Application;
      import javafx.event.EventHandler;
      import javafx.scene.Group;
      import javafx.scene.Scene;
      import javafx.scene.chart.CategoryAxis;
      import javafx.scene.chart.LineChart;
      import javafx.scene.chart.NumberAxis;
      import javafx.scene.chart.XYChart;
      import javafx.scene.control.Label;
      import javafx.scene.input.MouseEvent;
      import javafx.scene.layout.BorderPane;
      import javafx.scene.layout.Pane;
      import javafx.scene.paint.Color;
      import javafx.scene.shape.Line;
      import javafx.scene.shape.LineTo;
      import javafx.scene.shape.MoveTo;
      import javafx.scene.shape.Path;
      import javafx.stage.Stage;
       
      public class LinesEdit extends Application {
       
          Path path;
       
          public static void main(String[] args) {
              launch(args);
          }
       
          @Override
          public void start(Stage stage) {
       
              final CategoryAxis xAxis = new CategoryAxis();
              final NumberAxis yAxis = new NumberAxis(1, 21, 0.1);
              yAxis.setTickUnit(1);
              yAxis.setPrefWidth(35);
              yAxis.setMinorTickCount(10);
              yAxis.setTickLabelFormatter(new NumberAxis.DefaultFormatter(yAxis) {
                  @Override
                  public String toString(Number object) {
                      String label;
                      label = String.format("%7.2f", object.floatValue());
                      return label;
                  }
              });
              final LineChart<String, Number> lineChart = new LineChart<String, Number>(xAxis, yAxis);
       
              lineChart.setCreateSymbols(false);
              lineChart.setAlternativeRowFillVisible(false);
              lineChart.setLegendVisible(false);
       
              XYChart.Series series1 = new XYChart.Series();
       
              series1.getData().add(new XYChart.Data("Jan", 1));
              series1.getData().add(new XYChart.Data("Feb", 4));
              series1.getData().add(new XYChart.Data("Mar", 2.5));
              series1.getData().add(new XYChart.Data("Apr", 5));
              series1.getData().add(new XYChart.Data("May", 6));
              series1.getData().add(new XYChart.Data("Jun", 8));
              series1.getData().add(new XYChart.Data("Jul", 12));
              series1.getData().add(new XYChart.Data("Aug", 8));
              series1.getData().add(new XYChart.Data("Sep", 11));
              series1.getData().add(new XYChart.Data("Oct", 13));
              series1.getData().add(new XYChart.Data("Nov", 10));
              series1.getData().add(new XYChart.Data("Dec", 20));
       
              BorderPane bp = new BorderPane();
              bp.setCenter(lineChart);
              Scene scene = new Scene(bp, 800, 600);
              lineChart.setAnimated(false);
              lineChart.getData().addAll(series1);
              
              LinesEdit.MouseHandler mh = new LinesEdit.MouseHandler( bp );
              bp.setOnMouseClicked( mh );
              bp.setOnMouseMoved( mh );
       
              stage.setScene(scene);
       
              path = new Path();
              path.setStrokeWidth(1);
              path.setStroke(Color.BLACK);
       
              scene.setOnMouseDragged(mh);
              scene.setOnMousePressed(mh);
              bp.getChildren().add(path);
              stage.setScene(scene);
              stage.show();
          }    
             
          class MouseHandler implements EventHandler< MouseEvent > {
          private boolean gotFirst    = false;
          private Line    line;
          private Pane    pane;
          private double  x1, y1, x2, y2;
          private LineHandler lineHandler;
             
          public MouseHandler( Pane pane ) {
              this.pane = pane;
              lineHandler = new LineHandler(pane);
          }
          
          class LineHandler implements EventHandler< MouseEvent > {
          double  x, y;
          Pane pane;
      
          public LineHandler(Pane pane){
              this.pane = pane;
          }
          @Override
          public void handle( MouseEvent e ) {
              Line l = (Line) e.getSource();
      
              // remove line on right click
              if( e.getEventType() == MouseEvent.MOUSE_PRESSED
                      && e.isSecondaryButtonDown() ) {
                  pane.getChildren().remove( l );
              } else if( e.getEventType() == MouseEvent.MOUSE_DRAGGED
                      && e.isPrimaryButtonDown() ) {
                  double tx = e.getX();
                  double ty = e.getY();
                  double dx = tx - x;
                  double dy = ty - y;
                  l.setStartX( l.getStartX() + dx );
                  l.setStartY( l.getStartY() + dy );
                  l.setEndX( l.getEndX() + dx );
                  l.setEndY( l.getEndY() + dy );
                  x = tx;
                  y = ty;
              } else if( e.getEventType() == MouseEvent.MOUSE_ENTERED ) {
                  // just to show that the line is selected
                  x = e.getX();
                  y = e.getY();
                  l.setStroke( Color.RED );
              } else if( e.getEventType() == MouseEvent.MOUSE_EXITED ) {
                  l.setStroke( Color.BLACK );
              }
              // should not pass event to the parent
              e.consume();
          }
      }   
          @Override
          public void handle( MouseEvent event ) {
              if( event.getEventType() == MouseEvent.MOUSE_CLICKED ) {
                  if( !gotFirst ) {
                      x1 = x2 = event.getX();
                      y1 = y2 = event.getY();
                      line = new Line( x1, y1, x2, y2 );
      
                      pane.getChildren().add( line );
      
                      gotFirst = true;
                  } 
                  else {
                      line.setOnMouseEntered( lineHandler );
                      line.setOnMouseExited( lineHandler );
                      line.setOnMouseDragged( lineHandler );
                      line.setOnMousePressed( lineHandler );
                      // to consume the event
                      line.setOnMouseClicked( lineHandler );
                      line.setOnMouseReleased( lineHandler );
                      line = null;
                      gotFirst = false;
                  }
              } 
                  else {
                      if( line != null ) {
                          x2 = event.getX();
                          y2 = event.getY();
                          // update line
                          line.setEndX( x2 );
                          line.setEndY( y2 );
                  }
               }
            }
         }
      }
      What I would like to do now is to change line length or slope by selecting one of the end or tail points (A and B in the picture)

      [http://s16.postimage.org/f32p2a8md/A02503.png]

      and set new end x,y point. How to accomplish this?

      Thanks.
        • 1. Re: How to modify a line once drawn?
          shakir.gusaroff
          Hi. You can use OnContextMenuRequested event handler to edit and delete the line:
          import javafx.application.Application;
          import javafx.event.ActionEvent;
          import javafx.event.EventHandler;
          import javafx.scene.Scene;
          import javafx.scene.control.Button;
          import javafx.scene.layout.StackPane;
          import javafx.stage.Stage;
          
          import javafx.application.Application;
          import javafx.event.EventHandler;
          import javafx.geometry.Side;
          import javafx.scene.Group;
          import javafx.scene.Scene;
          import javafx.scene.chart.CategoryAxis;
          import javafx.scene.chart.LineChart;
          import javafx.scene.chart.NumberAxis;
          import javafx.scene.chart.XYChart;
          import javafx.scene.control.ContextMenu;
          import javafx.scene.control.Label;
          import javafx.scene.control.MenuItem;
          import javafx.scene.input.ContextMenuEvent;
          import javafx.scene.input.MouseEvent;
          import javafx.scene.layout.BorderPane;
          import javafx.scene.layout.Pane;
          import javafx.scene.paint.Color;
          import javafx.scene.shape.Line;
          import javafx.scene.shape.LineTo;
          import javafx.scene.shape.MoveTo;
          import javafx.scene.shape.Path;
          import javafx.stage.Stage;
          import javafx.stage.WindowEvent;
          
          public class LinesEdit extends Application {
          
              Path path;
          
              public static void main(String[] args) {
                  launch(args);
              }
          
              @Override
              public void start(Stage stage) {
          
                  final CategoryAxis xAxis = new CategoryAxis();
                  final NumberAxis yAxis = new NumberAxis(1, 21, 0.1);
                  yAxis.setTickUnit(1);
                  yAxis.setPrefWidth(35);
                  yAxis.setMinorTickCount(10);
                  yAxis.setTickLabelFormatter(new NumberAxis.DefaultFormatter(yAxis) {
                      @Override
                      public String toString(Number object) {
                          String label;
                          label = String.format("%7.2f", object.floatValue());
                          return label;
                      }
                  });
                  final LineChart<String, Number> lineChart = new LineChart<String, Number>(xAxis, yAxis);
          
                  lineChart.setCreateSymbols(false);
                  lineChart.setAlternativeRowFillVisible(false);
                  lineChart.setLegendVisible(false);
          
                  XYChart.Series series1 = new XYChart.Series();
          
                  series1.getData().add(new XYChart.Data("Jan", 1));
                  series1.getData().add(new XYChart.Data("Feb", 4));
                  series1.getData().add(new XYChart.Data("Mar", 2.5));
                  series1.getData().add(new XYChart.Data("Apr", 5));
                  series1.getData().add(new XYChart.Data("May", 6));
                  series1.getData().add(new XYChart.Data("Jun", 8));
                  series1.getData().add(new XYChart.Data("Jul", 12));
                  series1.getData().add(new XYChart.Data("Aug", 8));
                  series1.getData().add(new XYChart.Data("Sep", 11));
                  series1.getData().add(new XYChart.Data("Oct", 13));
                  series1.getData().add(new XYChart.Data("Nov", 10));
                  series1.getData().add(new XYChart.Data("Dec", 20));
          
                  BorderPane bp = new BorderPane();
                  bp.setCenter(lineChart);
                  Scene scene = new Scene(bp, 800, 600);
                  lineChart.setAnimated(false);
                  lineChart.getData().addAll(series1);
          
                  LinesEdit.MouseHandler mh = new LinesEdit.MouseHandler(bp);
                  bp.setOnMouseClicked(mh);
                  bp.setOnMouseMoved(mh);
          
                  stage.setScene(scene);
          
                  path = new Path();
                  path.setStrokeWidth(1);
                  path.setStroke(Color.BLACK);
          
                  scene.setOnMouseDragged(mh);
                  scene.setOnMousePressed(mh);
                  bp.getChildren().add(path);
                  stage.setScene(scene);
                  stage.show();
              }
          
              class MouseHandler implements EventHandler< MouseEvent> {
          
                  private boolean gotFirst = false;
                  private Line line;
                  private Pane pane;
                  private double x1, y1, x2, y2;
                  private LineHandler lineHandler;
          
                  public MouseHandler(Pane pane) {
                      this.pane = pane;
                      lineHandler = new LineHandler(pane);
                  }
          
                  class LineHandler implements EventHandler< MouseEvent> {
          
                      double x, y;
                      Pane pane;
          
                      public LineHandler(Pane pane) {
                          this.pane = pane;
                      }
          
                      @Override
                      public void handle(final MouseEvent e) {
                          final Line l = (Line) e.getSource();
                          final double x_ = e.getX();
                          final double y_ = e.getY();
          
                          final ContextMenu contextMenu = new ContextMenu();
                          MenuItem item1 = new MenuItem("Edit");
                          item1.setOnAction(new EventHandler<ActionEvent>() {
                              public void handle(ActionEvent e) {
          
          
                                  l.setEndX(x_);
                                  l.setEndY(y_);
          
          
          
                                  System.out.println("Edit");
                              }
                          });
          
                          MenuItem item2 = new MenuItem("Delete");
                          item2.setOnAction(new EventHandler<ActionEvent>() {
                              public void handle(ActionEvent e) {
          
                                  pane.getChildren().remove(l);
          
          
                                  System.out.println("Delete");
                              }
                          });
          
          
                          contextMenu.getItems().addAll(item1, item2);
          
          
          
          
          
                          l.setOnContextMenuRequested(
                                  new EventHandler<ContextMenuEvent>() {
                                      @Override
                                      public void handle(ContextMenuEvent event) {
                                          contextMenu.show(l, Side.RIGHT, 0, 0);
          
                                          System.out.println("Hello World!");
                                      }
                                  });
          
          
          
                          // remove line on right click
                          if (e.getEventType()
                                  == MouseEvent.MOUSE_PRESSED
                                  && e.isSecondaryButtonDown()) {
                              //   pane.getChildren().remove( l );
                          } else if (e.getEventType()
                                  == MouseEvent.MOUSE_DRAGGED
                                  && e.isPrimaryButtonDown()) {
                              double tx = e.getX();
                              double ty = e.getY();
                              double dx = tx - x;
                              double dy = ty - y;
                              l.setStartX(l.getStartX() + dx);
                              l.setStartY(l.getStartY() + dy);
                              l.setEndX(l.getEndX() + dx);
                              l.setEndY(l.getEndY() + dy);
                              x = tx;
                              y = ty;
                          } else if (e.getEventType()
                                  == MouseEvent.MOUSE_ENTERED) {
                              // just to show that the line is selected
                              x = e.getX();
                              y = e.getY();
                              l.setStroke(Color.RED);
                          } else if (e.getEventType()
                                  == MouseEvent.MOUSE_EXITED) {
                              l.setStroke(Color.BLACK);
                          }
                          // should not pass event to the parent
          
                          e.consume();
                      }
                  }
          
                  @Override
                  public void handle(MouseEvent event) {
                      if (event.getEventType() == MouseEvent.MOUSE_CLICKED) {
                          if (!gotFirst) {
                              x1 = x2 = event.getX();
                              y1 = y2 = event.getY();
                              line = new Line(x1, y1, x2, y2);
          
                              pane.getChildren().add(line);
          
                              gotFirst = true;
                          } else {
          
          
          
                              line.setOnMouseEntered(lineHandler);
                              line.setOnMouseExited(lineHandler);
                              line.setOnMouseDragged(lineHandler);
                              line.setOnMousePressed(lineHandler);
                              // to consume the event
                              line.setOnMouseClicked(lineHandler);
                              line.setOnMouseReleased(lineHandler);
                              line = null;
                              gotFirst = false;
                          }
                      } else {
                          if (line != null) {
                              x2 = event.getX();
                              y2 = event.getY();
                              // update line
                              line.setEndX(x2);
                              line.setEndY(y2);
                          }
                      }
                  }
              }
          }
          • 2. Re: How to modify a line once drawn?
            935521
            Hi Shakir,

            great code as usual! Few last questions,

            1) how can I have contextMenu always prompted at pointer end?

            2) your code shorten the line, how to change length or slope by clicking or selectiong one of the A or B line ends?

            Thank you very much!
            • 3. Re: How to modify a line once drawn?
              famedoro
              you can replace the code line :
              contextMenu.show(l, Side.RIGHT, 0, 0);

              with

              contextMenu.show(l, e.getScreenX() + 1, e.getScreenY());
              • 4. Re: How to modify a line once drawn?
                jsmith
                Bhupendra posted a nice answer to this question here:
                http://stackoverflow.com/questions/13361590/javafx-2-x-how-to-modify-a-line-once-drawn