package org.simantics.district.network.ui.contributions; import java.awt.geom.AffineTransform; import java.awt.geom.NoninvertibleTransformException; import java.awt.geom.Point2D; import java.lang.reflect.InvocationTargetException; import java.util.List; import java.util.Optional; import java.util.concurrent.TimeUnit; import javax.inject.Named; import org.eclipse.core.commands.ParameterizedCommand; import org.eclipse.e4.core.di.annotations.CanExecute; import org.eclipse.e4.core.di.annotations.Execute; import org.eclipse.e4.ui.model.application.ui.basic.MPart; import org.eclipse.e4.ui.services.IServiceConstants; import org.eclipse.jface.viewers.ISelection; import org.eclipse.ui.IEditorPart; import org.simantics.Simantics; import org.simantics.databoard.Bindings; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.WriteGraph; import org.simantics.db.common.request.ReadRequest; import org.simantics.db.common.request.WriteRequest; import org.simantics.db.exception.DatabaseException; import org.simantics.db.layer0.SelectionHints; import org.simantics.db.layer0.util.RemoverUtil; import org.simantics.db.request.Read; import org.simantics.diagram.stubs.DiagramResource; import org.simantics.district.network.DistrictNetworkUtil; import org.simantics.district.network.ModelledCRS; import org.simantics.district.network.ontology.DistrictNetworkResource; import org.simantics.district.network.ui.DNEdgeBuilder; import org.simantics.district.network.ui.DistrictDiagramEditor; import org.simantics.district.network.ui.NetworkDrawingParticipant; import org.simantics.g2d.canvas.ICanvasContext; import org.simantics.g2d.participant.MouseUtil; import org.simantics.g2d.participant.MouseUtil.MouseInfo; import org.simantics.ui.workbench.e4.E4WorkbenchUtils; import org.simantics.utils.threads.ThreadUtils; import org.simantics.utils.ui.ISelectionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ChangeRoutePointToVertexHandler { private static final Logger LOGGER = LoggerFactory.getLogger(ChangeRoutePointToVertexHandler.class); static List elements; static boolean cut = true; @CanExecute public boolean canExecute(@Named(IServiceConstants.ACTIVE_SELECTION) ISelection selection) { List elements = ISelectionUtils.getPossibleKeys(selection, SelectionHints.KEY_MAIN, Resource.class); if (elements.size() != 1) return false; try { return Simantics.getSession().syncRequest(new Read() { @Override public Boolean perform(ReadGraph graph) throws DatabaseException { DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); for (Resource selection : elements) { if (!graph.isInstanceOf(selection, DN.Edge)) { return false; } } return true; } }); } catch (DatabaseException e) { LOGGER.error("Could not evaluate if mapping can be changed for selection {}", elements, e); return false; } } @Execute public void execute(@Named(IServiceConstants.ACTIVE_PART) MPart mActiveEditorPart, @Named(IServiceConstants.ACTIVE_SELECTION) Object selection, ParameterizedCommand command) { final List elements = ISelectionUtils.getPossibleKeys(selection, SelectionHints.KEY_MAIN, Resource.class); Resource edge = elements.get(0); try { Point2D mouseClicked = mouseClickedOnEditor(mActiveEditorPart); if (mouseClicked != null) { IEditorPart activeEditorPart = E4WorkbenchUtils.getActiveIEditorPart(mActiveEditorPart); if (activeEditorPart == null) return; if (!(activeEditorPart instanceof DistrictDiagramEditor)) return; DistrictDiagramEditor editor = (DistrictDiagramEditor) activeEditorPart; Resource diagram = editor.getInputResource(); Simantics.getSession().asyncRequest(new ReadRequest() { @Override public void run(ReadGraph graph) throws DatabaseException { DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); double[] detailedGeom = graph.getPossibleRelatedValue(edge, DN.Edge_HasGeometry, Bindings.DOUBLE_ARRAY); if (detailedGeom != null) { double closestDistance = Double.MAX_VALUE; Point2D closestPoint = null; int j = 0; for (int i = 0; i < detailedGeom.length; i += 2) { double x = detailedGeom[i]; double y = detailedGeom[i + 1]; Point2D currentPoint = new Point2D.Double(x, y); double currentDistance = mouseClicked.distance(currentPoint); if (currentDistance < closestDistance) { closestDistance = currentDistance; closestPoint = currentPoint; j = i; } } final Point2D finalClosestPoint = closestPoint; DiagramResource DIA = DiagramResource.getInstance(graph); Resource currentStartVertex = graph.getSingleObject(edge, DN.HasStartVertex); double[] currentStartVertexCoords = graph.getRelatedValue(currentStartVertex, DIA.HasLocation, Bindings.DOUBLE_ARRAY); Resource currentEndVertex = graph.getSingleObject(edge, DN.HasEndVertex); double[] currentEndVertexCoords = graph.getRelatedValue(currentEndVertex, DIA.HasLocation, Bindings.DOUBLE_ARRAY); double[] detailedLeftEdgeGeometryCoords = new double[j]; for (int k = 0; k < j; k += 2) { double x = detailedGeom[k]; double y = detailedGeom[k + 1]; detailedLeftEdgeGeometryCoords[k] = x; detailedLeftEdgeGeometryCoords[k + 1] = y; } double[] detailedRightEdgeGeometryCoords = new double[detailedGeom.length - j - 2]; int i = 0; for (int k = j + 2; k < detailedGeom.length; k += 2) { double x = detailedGeom[k]; double y = detailedGeom[k + 1]; detailedRightEdgeGeometryCoords[i++] = x; detailedRightEdgeGeometryCoords[i++] = y; } Simantics.getSession().asyncRequest(new WriteRequest() { @Override public void perform(WriteGraph graph) throws DatabaseException { RemoverUtil.remove(graph, edge); ThreadUtils.getNonBlockingWorkExecutor().schedule(() -> { Simantics.getSession().asyncRequest(new WriteRequest() { @Override public void perform(WriteGraph graph) throws DatabaseException { // we might have closest point if (finalClosestPoint != null) { Resource mapping = graph.getSingleObject(diagram, DistrictNetworkResource.getInstance(graph).VertexDefaultMapping); double[] midVertexCoords = new double[] { finalClosestPoint.getX(), finalClosestPoint.getY() }; Resource createdVertex = DistrictNetworkUtil.createVertex(graph, diagram, midVertexCoords, 0, mapping); Optional leftEdge = DNEdgeBuilder.create(graph, diagram, currentStartVertexCoords, 0, midVertexCoords, 0, detailedLeftEdgeGeometryCoords, 0.001); Optional rightEdge = DNEdgeBuilder.create(graph, diagram, midVertexCoords, 0, currentEndVertexCoords, 0, detailedRightEdgeGeometryCoords, 0.001); } } }); }, 500, TimeUnit.MILLISECONDS); } }); } else { // no can do - or it would be possible to split the edge here in to two different edges } } }); } else { LOGGER.warn("No mouseClicked for editor {}", mActiveEditorPart); } } catch (InvocationTargetException e) { LOGGER.error("Could not change route point to vertex", e); } } private static Point2D mouseClickedOnEditor(MPart mActiveEditorPart) throws InvocationTargetException { IEditorPart activeEditorPart = E4WorkbenchUtils.getActiveIEditorPart(mActiveEditorPart); if (activeEditorPart == null) return null; if (!(activeEditorPart instanceof DistrictDiagramEditor)) return null; DistrictDiagramEditor editor = (DistrictDiagramEditor) activeEditorPart; ICanvasContext ctx = editor.getAdapter(ICanvasContext.class); NetworkDrawingParticipant drawingParticipant = ctx.getAtMostOneItemOfClass(NetworkDrawingParticipant.class); AffineTransform drawingTransform = drawingParticipant.getTransform(); MouseUtil util = ctx.getAtMostOneItemOfClass(MouseUtil.class); MouseInfo mouseInfo = util.getMousePressedInfo(0); if (mouseInfo == null) { return null; } Point2D canvasPosition = mouseInfo.canvasPosition; Point2D transformed = null; try { transformed = drawingTransform.inverseTransform(canvasPosition, new Point2D.Double()); } catch (NoninvertibleTransformException e) { LOGGER.error("Could not create inverse transform of {}", drawingTransform, e); throw new InvocationTargetException(e); } double x = ModelledCRS.xToLongitude(transformed.getX()); double y = ModelledCRS.yToLatitude(-transformed.getY()); return new Point2D.Double(x, y); } }