package org.simantics.district.network.ui; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.util.Collection; import java.util.List; import java.util.Set; import java.util.stream.Collectors; import org.simantics.g2d.canvas.ICanvasContext; import org.simantics.g2d.canvas.impl.DependencyReflection.Dependency; import org.simantics.g2d.canvas.impl.DependencyReflection.Reference; import org.simantics.g2d.diagram.DiagramHints; import org.simantics.g2d.diagram.IDiagram; import org.simantics.g2d.diagram.participant.Selection; import org.simantics.g2d.element.ElementUtils; import org.simantics.g2d.element.IElement; import org.simantics.g2d.participant.CanvasBoundsParticipant; import org.simantics.g2d.participant.PanZoomRotateHandler; import org.simantics.maps.MapScalingTransform; import org.simantics.scenegraph.g2d.events.EventHandlerReflection.EventHandler; import org.simantics.scenegraph.g2d.events.MouseEvent.MouseWheelMovedEvent; import org.simantics.scenegraph.g2d.events.command.Command; import org.simantics.scenegraph.g2d.events.command.CommandEvent; import org.simantics.scenegraph.g2d.events.command.Commands; import org.simantics.scenegraph.g2d.nodes.NavigationNode; public class DistrictPanZoomRotateHandler extends PanZoomRotateHandler { public static final int DISTRICT_TRANSLATE_AMOUNT = 2; @Dependency DistrictTransformUtil util; @Reference Selection selection; public DistrictPanZoomRotateHandler() { } @Override public void addedToContext(ICanvasContext ctx) { super.addedToContext(ctx); setHint(KEY_TRANSLATE_AMOUNT, DISTRICT_TRANSLATE_AMOUNT); } @Override protected Class getNavigationNodeClass() { return DistrictNavigationNode.class; } @Override public double getTranslateAmount() { return 15 * super.getTranslateAmount(); } @Override public double limitScaleFactor(double scaleFactor) { return scaleFactor; } @Override @EventHandler(priority = 1) public boolean handleEvent(CommandEvent e) { super.update(); Command c = e.command; boolean zoomDisabled = Boolean.TRUE.equals(getHint(KEY_DISABLE_ZOOM)) ? true : false; // custom handling of zoom to fit & selection if (Commands.ZOOM_TO_FIT.equals(c) && !zoomDisabled) { boolean result = zoomToFit(); if (!result) result = zoomToPage(); return result; } if (Commands.ZOOM_TO_PAGE.equals(c) && !zoomDisabled) { return zoomToPage(); } if (Commands.ZOOM_TO_SELECTION.equals(c) && !zoomDisabled) { return zoomToSelection(); } return super.handleEvent(e); } private boolean zoomToFit() { CanvasBoundsParticipant boundsParticipant = getContext().getAtMostOneItemOfClass(CanvasBoundsParticipant.class); if (boundsParticipant == null) return false; final Rectangle2D controlBounds = boundsParticipant.getControlBounds().getFrame(); if (controlBounds == null || controlBounds.isEmpty()) return false; IDiagram d = getHint(DiagramHints.KEY_DIAGRAM); if (d == null) return false; Rectangle2D diagramRect = ElementUtils.getSurroundingElementBoundsOnDiagram(getMapElements(d.getElements())); if (diagramRect == null) return false; if (diagramRect.isEmpty()) return false; org.simantics.scenegraph.utils.GeometryUtils.expandRectangle(diagramRect, 1); // System.out.println("zoomToFit(" + controlArea + ", " + diagramRect + ")"); util.fitArea(controlBounds, diagramRect, null); return true; } protected static List getMapElements(Collection elements) { List justMapElements = elements.stream() .filter(e -> "DistrictNetworkEdgeElement".equals(e.getElementClass().getId()) || "DistrictNetworkVertexElement".equals(e.getElementClass().getId())) .collect(Collectors.toList()); return justMapElements; } private boolean zoomToPage() { util.setTransform(new AffineTransform(2,0,0,2,270,270)); return true; } private boolean zoomToSelection() { CanvasBoundsParticipant boundsParticipant = getContext().getAtMostOneItemOfClass(CanvasBoundsParticipant.class); if (boundsParticipant == null) return false; final Rectangle2D controlBounds = boundsParticipant.getControlBounds().getFrame(); if (controlBounds == null || controlBounds.isEmpty()) return false; Set selections = selection.getAllSelections(); if (selections == null || selections.isEmpty()) { // no can do, return zoomToPage(); } Rectangle2D diagramRect = ElementUtils.getSurroundingElementBoundsOnDiagram(getMapElements(selections)); // Make sure that even empty bounds can be zoomed into. org.simantics.scenegraph.utils.GeometryUtils.expandRectangle(diagramRect, 1); util.fitArea(controlBounds, diagramRect, null); return true; } public static class DistrictNavigationNode extends NavigationNode { private static final long serialVersionUID = 5452897272925816875L; public DistrictNavigationNode() { setAdaptViewportToResizedControl(false); } @Override public Double getZoomInLimit() { return super.getZoomInLimit(); } @Override public Double getZoomOutLimit() { return super.getZoomOutLimit(); } @Override public void setAdaptViewportToResizedControl(Boolean adapt) { super.setAdaptViewportToResizedControl(false); // no op } @Override public boolean getAdaptViewportToResizedControl() { return false; } @Override public boolean mouseWheelMoved(MouseWheelMovedEvent me) { if (navigationEnabled && zoomEnabled) { // check if min/max zoom already AffineTransform transform = getTransform(); int zoomLevel = MapScalingTransform.zoomLevel(transform); if (0 < zoomLevel && zoomLevel < 20 || (zoomLevel == 0 && me.wheelRotation > 0) || (zoomLevel == 20 && me.wheelRotation < 0)) { double z; if (me.wheelRotation > 0) { z = DISTRICT_TRANSLATE_AMOUNT; } else { z = 1.0d / DISTRICT_TRANSLATE_AMOUNT; } //double scroll = Math.min(0.9, -me.wheelRotation / 20.0); //double z = 1 - scroll; double dx = (me.controlPosition.getX() - transform.getTranslateX()) / transform.getScaleX(); double dy = (me.controlPosition.getY() - transform.getTranslateY()) / transform.getScaleY(); dx = dx * (1 - z); dy = dy * (1 - z); // double limitedScale = limitScaleFactor(z); // if (limitedScale != 1.0) { translate(dx, dy); scale(z, z); transformChanged(); dropQuality(); repaint(); // } } else { // max zoom level reached } } return false; } } }