package org.openpnp.gui.components.nav; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; import org.openpnp.ConfigurationListener; import org.openpnp.events.JobLoadedEvent; import org.openpnp.model.BoardLocation; import org.openpnp.model.Configuration; import org.openpnp.model.LengthUnit; import org.openpnp.model.Location; import org.openpnp.spi.Camera; import org.openpnp.util.UiUtils; import com.google.common.eventbus.Subscribe; import javafx.application.Platform; import javafx.embed.swing.JFXPanel; import javafx.event.EventHandler; import javafx.geometry.Point2D; import javafx.scene.Group; import javafx.scene.Node; import javafx.scene.Scene; import javafx.scene.input.MouseEvent; import javafx.scene.input.ScrollEvent; import javafx.scene.layout.Pane; import javafx.scene.paint.Color; import javafx.scene.shape.Line; import javafx.scene.transform.Scale; import javafx.scene.transform.Translate; @SuppressWarnings("serial") public class FxNavigationView extends JFXPanel { Scene scene; Pane root; MachineView machineView; // TODO: Probably should move to the MachineView or it's own BoardsView. Group boards = new Group(); Line jogTargetLine; Scale zoomTx = new Scale(1, 1, 0, 0); Translate viewTx = new Translate(0, 0); public FxNavigationView() { Platform.runLater(new Runnable() { @Override public void run() { setScene(createScene()); Configuration.get().addListener(configurationListener); } }); addComponentListener(componentListener); Configuration.get().getBus().register(this); } private Scene createScene() { root = new Pane(); // Flip Y so the coordinate system is that of OpenPnP root.setScaleY(-1); scene = new Scene(root, Color.ALICEBLUE); scene.setOnScroll(zoomHandler); scene.setOnDragDetected(jogDragStartHandler); scene.setOnMouseDragged(jogDragHandler); scene.setOnMouseDragReleased(jogDragEndHandler); // root.setOnMouseClicked(e -> { // if (e.getClickCount() == 2) { // zoomToFit((Node) e.getTarget()); // } // }); return scene; } private void zoomToFit() { zoomToFit(machineView); } private void zoomToFit(Node node) { if (node == null) { return; } double zoom = getMinimumZoom(node); zoomTx.setX(zoom); zoomTx.setY(zoom); viewTx.setX(-node.getBoundsInLocal().getMinX()); viewTx.setY(-node.getBoundsInLocal().getMinY()); } private double getMinimumZoom() { return getMinimumZoom(machineView); } /** * Returns the minimum zoom level that will allow the Node to fit within the bounds * of the view. * @return */ private double getMinimumZoom(Node node) { if (machineView == null) { return 1; } double viewWidth = getWidth() - getInsets().left - getInsets().right; double viewHeight = getHeight() - getInsets().top - getInsets().bottom; double width = node.getBoundsInLocal().getWidth(); double height = node.getBoundsInLocal().getHeight(); double widthRatio = width / viewWidth; double heightRatio = height / viewHeight; double scaledHeight, scaledWidth; if (heightRatio > widthRatio) { double aspectRatio = width / height; scaledHeight = (int) viewHeight; scaledWidth = (int) (scaledHeight * aspectRatio); } else { double aspectRatio = height / width; scaledWidth = (int) viewWidth; scaledHeight = (int) (scaledWidth * aspectRatio); } double minimumZoom = scaledWidth / width; return minimumZoom; } @Subscribe public void jobLoaded(JobLoadedEvent e) { Platform.runLater(() -> { boards.getChildren().clear(); for (BoardLocation boardLocation : e.job.getBoardLocations()) { BoardLocationView boardLocationView = new BoardLocationView(boardLocation); boards.getChildren().add(boardLocationView); } zoomToFit(); }); } EventHandler<MouseEvent> jogDragStartHandler = new EventHandler<MouseEvent>() { @Override public void handle(MouseEvent e) { scene.startFullDrag(); try { Camera camera = Configuration.get().getMachine().getDefaultHead().getDefaultCamera(); Location location = camera.getLocation().convertToUnits(LengthUnit.Millimeters); Point2D start = machineView.localToScene(location.getX(), location.getY()); start = root.sceneToLocal(start); Point2D end = root.sceneToLocal(e.getX(), e.getY()); jogTargetLine = new Line(start.getX(), start.getY(), end.getX(), end.getY()); jogTargetLine.setStroke(Color.WHITE); root.getChildren().add(jogTargetLine); } catch (Exception ex) { } } }; EventHandler<MouseEvent> jogDragHandler = new EventHandler<MouseEvent>() { @Override public void handle(MouseEvent e) { if (jogTargetLine == null) { return; } Point2D end = root.sceneToLocal(e.getX(), e.getY()); jogTargetLine.setEndX(end.getX()); jogTargetLine.setEndY(end.getY()); } }; EventHandler<MouseEvent> jogDragEndHandler = new EventHandler<MouseEvent>() { @Override public void handle(MouseEvent e) { try { root.getChildren().remove(jogTargetLine); final Camera camera = Configuration.get().getMachine().getDefaultHead().getDefaultCamera(); Point2D point = machineView.sceneToLocal(e.getX(), e.getY()); final Location location = camera.getLocation().derive(point.getX(), point.getY(), null, null); UiUtils.submitUiMachineTask(() -> { camera.moveTo(location); }); } catch (Exception ex) { } } }; EventHandler<ScrollEvent> zoomHandler = new EventHandler<ScrollEvent>() { @Override public void handle(final ScrollEvent e) { e.consume(); Point2D before = machineView.sceneToLocal(e.getX(), e.getY()); double zoom = zoomTx.getX(); zoom += (e.getDeltaY() * 0.01); if (zoom <= getMinimumZoom()) { zoomToFit(); } else { zoomTx.setX(zoom); zoomTx.setY(zoom); Point2D after = machineView.sceneToLocal(e.getX(), e.getY()); Point2D delta = after.subtract(before); viewTx.setX(viewTx.getX() + delta.getX()); viewTx.setY(viewTx.getY() + delta.getY()); } } }; ComponentListener componentListener = new ComponentListener() { @Override public void componentShown(ComponentEvent e) { zoomToFit(); } @Override public void componentResized(ComponentEvent e) {} @Override public void componentMoved(ComponentEvent e) {} @Override public void componentHidden(ComponentEvent e) {} }; ConfigurationListener configurationListener = new ConfigurationListener.Adapter() { @Override public void configurationComplete(Configuration configuration) throws Exception { Platform.runLater(() -> { machineView = new MachineView(configuration.getMachine()); machineView.getTransforms().add(zoomTx); machineView.getTransforms().add(viewTx); root.getChildren().add(machineView); machineView.getChildren().add(0, boards); zoomToFit(); }); } }; }