1

I need to use javaFX2 to do the following:

1-) Load an image and display it using imageView. [OK]

2-) Right click and choose the "Add Node" option and a black circle will appear in the screen and you can Drag the circle to any place of the image. [OK]

3-) Use the scroll wheel fo the mouse to scale up or down the imageView, giving the "Zoom Sensation" on the image. [OK]

3.1-) However, every time I scale the image I want my circles to follow the scale proportion, which means they cant stay in the same position of the screen. [Have no Idea]

My proble is the item number 3.1, because I dont know how to move my circles in the image in a way that they can appear to be in the place of the image after I scale up or down. I tried to use the setCenterX() and setCenterY() methods, and also the transition methods too but I couldnt find a way to do this.

Code to load my image:

@FXML
ImageView bluePrintView;
@FXML
private void loadBtnAction(ActionEvent event) {
    FileChooser fileChooser = new FileChooser();
    //Show open file dialog
    File file = fileChooser.showOpenDialog(null);
    /*Load Image*/
    System.out.println(file.getPath());
        Image img = new Image("file:" + file.getPath());
        bluePrintView.setImage(img);
        bluePrintView.setFitHeight(scrollPaneImage.getHeight());
        bluePrintView.setFitWidth(scrollPaneImage.getWidth());
        bluePrintView.setSmooth(true);
        bluePrintView.setScaleX(1);
        bluePrintView.setScaleY(1);
    event.consume();

}

Code to Scale the ImageView:

@FXML
private void zoomAction(ScrollEvent event) {
    if (event.getDeltaY() < 0) {
        if (bluePrintView.getScaleX() > 0.8 && bluePrintView.getScaleY() > 0.8) {
            bluePrintView.setScaleX(bluePrintView.getScaleX() - 0.1);
            bluePrintView.setScaleY(bluePrintView.getScaleY() - 0.1);
            System.out.println("ImageView(X,Y): "+bluePrintView.getFitHeight()+" "+bluePrintView.getFitWidth());
            System.out.println("Image(X,Y): "+bluePrintView.getImage().getHeight()+" "+bluePrintView.getImage().getWidth());
            if (!nodeList.isEmpty()) {
                for (int i = 0; i < nodeList.size(); i++) {
                       nodeList.get(i).zoomOutNode(bluePrintView.getFitHeight(),bluePrintView.getFitWidth());
                }
            }
        }
    } else {
        bluePrintView.setScaleX(bluePrintView.getScaleX() + 0.1);
        bluePrintView.setScaleY(bluePrintView.getScaleY() + 0.1);
        System.out.println("ImageView(X,Y): "+bluePrintView.getFitHeight()+" "+bluePrintView.getFitWidth());
        System.out.println("Image(X,Y): "+bluePrintView.getImage().getHeight()+" "+bluePrintView.getImage().getWidth());
        if (!nodeList.isEmpty()) {
            for (int i = 0; i < nodeList.size(); i++) {
                  nodeList.get(i).zoomInNode(bluePrintView.getFitHeight(),bluePrintView.getFitWidth());
            }
        }
    }
    event.consume();
}

Code to Create Circle and do some operations:

public class NodeShape {

final Circle node = new Circle();
double axisX = 0, axisY = 0;

public Circle createNode(Group rootGroup, double axisX, double axisY, double radius, String color) {
    node.setCenterX(axisX);
    node.setCenterY(axisY);
    node.setRadius(radius);
    node.fillProperty().setValue(Paint.valueOf(color));
    System.out.println(node.getTranslateX() + " " + node.getTranslateY());
    System.out.println("getCenter: " + node.getCenterX() + " " + node.getCenterY());
    rootGroup.getChildren().add(node);
    node.setOnMouseDragged(new EventHandler<MouseEvent>() {
        @Override
        public void handle(MouseEvent t) {
            node.setCenterX(t.getX());
            node.setCenterY(t.getY());
            NodeShape.this.axisX = t.getX();
            NodeShape.this.axisY = t.getY();
            System.out.println("Circle getTranslate: " + node.getTranslateX() + " " + node.getTranslateY());
            System.out.println("Circle getCenter: " + node.getCenterX() + " " + node.getCenterY());
            t.consume();
        }
    });
    node.setOnMouseClicked(new EventHandler<MouseEvent>() {
        @Override
        public void handle(MouseEvent t) {
            if (t.getButton().equals(MouseButton.PRIMARY)) {
                if (t.getClickCount() == 2) {
                    node.setVisible(false);
                    node.setDisable(true);
                }else if(t.getClickCount() == 1){
                    System.out.println("Circle Position: "+node.getCenterX()+" "+node.getCenterY());
                }
            }
            t.consume();
        }
    });
    node.setOnScroll(new EventHandler<ScrollEvent>() {
        @Override
        public void handle(ScrollEvent t) {
            if (t.getDeltaY() < 0) {
                if (node.getScaleX() > 0.8 && node.getScaleY() > 0.8) {
                    node.setScaleX(node.getScaleX() - 0.1);
                    node.setScaleY(node.getScaleY() - 0.1);
                }
            } else {
                node.setScaleX(node.getScaleX() + 0.1);
                node.setScaleY(node.getScaleY() + 0.1);
            }
            t.consume();
        }
    });
    return node;
}

public void zoomInNode(double imgHeight, double imgWidth) {
    node.setCenterX(0.1);
    //node.setTranslateY(imgHeight/1100 + 10);
    //node.setCenterX(node.getCenterX() + Math.abs(axisX - node.getRadius()));
    //node.setCenterY(node.getCenterY() + Math.abs(axisY - node.getRadius()));
    System.out.println("Circle getCenter: " + node.getCenterX() + " " + node.getCenterY());
    System.out.println("Circle getCenter: " + node.getTranslateX()+ " " + node.getTranslateY());
}

public void zoomOutNode(double imgHeight, double imgWidth) {
    node.setCenterX(-0.1);
 //   node.setTranslateY(imgHeight/200 - 10);
    //node.setCenterX(node.getCenterX() - Math.abs(axisX - node.getRadius()));
    //node.setCenterY(node.getCenterY() - Math.abs(axisY - node.getRadius()));
    System.out.println("Circle getCenter: " + node.getCenterX() + " " + node.getCenterY());
    System.out.println("Circle getCenter: " + node.getTranslateX()+ " " + node.getTranslateY());
}

public void zoomResetNode() {
    node.setCenterX(axisX);
    node.setCenterY(axisY);
}}

My FXML file code:

<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.image.*?>
<?import javafx.scene.layout.*?>

<AnchorPane id="AnchorPane" fx:id="rootPane" pickOnBounds="true" prefHeight="420.0" prefWidth="817.0" snapToPixel="true" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="blueprints.NewSonarViewController">
  <children>
    <Group id="Group" layoutX="0.0" layoutY="0.0">
      <children>
        <ScrollPane fx:id="scrollPaneImage" layoutX="0.0" layoutY="0.0" prefHeight="420.0" prefWidth="817.0">
          <content>
            <Group id="Group" fx:id="rootGroup">
              <children>
                <ImageView fx:id="bluePrintView" cache="false" fitHeight="419.0" fitWidth="816.0" layoutX="0.0" layoutY="0.0" onMouseClicked="#zoomResetAction" onScroll="#zoomAction" pickOnBounds="true" preserveRatio="false" rotate="0.0" visible="true" />
              </children>
            </Group>
          </content>
          <contextMenu>
            <ContextMenu>
              <items>
                <MenuItem mnemonicParsing="false" onAction="#loadBtnAction" text="Load Image" />
                <MenuItem mnemonicParsing="false" onAction="#addSynk" text="Add Synk" />
                <MenuItem mnemonicParsing="false" onAction="#addNode" text="AddNode" />
              </items>
            </ContextMenu>
          </contextMenu>
        </ScrollPane>
      </children>
    </Group>
  </children>
</AnchorPane>
tsukanomon
  • 1,220
  • 1
  • 19
  • 23
  • so you want to scale image without scaling circle and after scaling you want to have circle position relative to the scaled image, so if the circle is in the center of image, the circle should not move when you rescale image, is it correct? – Eeliya Aug 28 '13 at 11:35

2 Answers2

2

Well if I understood your question correct this workaround should solve your problem:

Imagine you have a image with width and height 100*200.

You have circle on image in (10,10) position.

After you rescale your image, the new width and height of your image become 150*300.

to calculate the new position of the circle just do this:

newCircleX = (currentCircleX * currentImageWidth) / oldImageWidth;
newCircleY = (currentCircleY * currentImageHeight) / oldImageHeight;

Hope this help.

Eeliya
  • 1,534
  • 5
  • 23
  • 37
  • Following your example, then currentCircleX = 10; currentImageWidth = 150; oldImageWidth = 100; Is that right ? – tsukanomon Aug 29 '13 at 02:49
2

My idea is that you add the ImageView and the Circle to your rootGroup and then apply the scale on the rootGroup. See my example below.

The view:

<AnchorPane id="AnchorPane" fx:id="rootPane" pickOnBounds="true" prefHeight="420.0" prefWidth="817.0" snapToPixel="true" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="javafx.scale.Controller">
  <children>
    <Group id="Group" layoutX="0.0" layoutY="0.0" onScroll="#scrolling">
      <children>
        <ScrollPane fx:id="scrollPaneImage" layoutX="0.0" layoutY="0.0" prefHeight="420.0" prefWidth="817.0">
          <content>
            <Group id="Group" fx:id="rootGroup">
            </Group>
          </content>
        </ScrollPane>
      </children>
    </Group>
  </children>
</AnchorPane>

The controller:

public class Controller {

    @FXML
    private Group rootGroup;
    private Group zoomGroup;

    private double scale = 1;

    public void initialize() {
        zoomGroup = new Group();

        Image image = new Image(getClass().getResourceAsStream("image.jpg"));
        ImageView imageView = new ImageView(image);
        imageView.setX(50);
        imageView.setY(50);

        Circle circle = new Circle();
        circle.setRadius(20);
        circle.setCenterX(100);
        circle.setCenterY(200);
        circle.setFill(Paint.valueOf("RED"));

        zoomGroup.getChildren().add(imageView);
        zoomGroup.getChildren().add(circle);
        rootGroup.getChildren().add(zoomGroup);
    }

    public void scrolling(ScrollEvent scrollEvent) {
        if (scrollEvent.getDeltaY() > 0) {
            scale += 0.1;
        } else if (scrollEvent.getDeltaY() < 0) {
            scale -= 0.1;
        }
        zoomGroup.setScaleX(scale);
        zoomGroup.setScaleY(scale);
    }
}
Katona
  • 4,816
  • 23
  • 27
  • It works very well, however it gave me another problem which is now when I scale my image, the scroll bars of the scrollpane dont see the group is scaled and because of that I cant navigate through the scaled image. Any work around ? – tsukanomon Aug 29 '13 at 13:28
  • @tsukanomon I updated the controller to solve your problem, but note that the scrolling interacts with the scrollpane which I think you can solve based on [this question](http://stackoverflow.com/questions/13238507/zooming-in-javafx-scrollevent-is-consumed-when-content-size-exceeds-scrollpane) – Katona Aug 29 '13 at 15:44
  • you are a f**ing genius man!!! Really worked. Thnks a LOT!!! And just if you have a little time..can you explain to me why adding one more group, in this case the zoomGroup, worked ? – tsukanomon Aug 29 '13 at 16:11
  • @tsukanomon I took the idea from [here](http://pixelduke.wordpress.com/2012/09/16/zooming-inside-a-scrollpane/) – Katona Aug 29 '13 at 16:24