package org.simantics.district.network.ui.participants; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.util.Collection; import java.util.Collections; import java.util.Map; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import org.simantics.Simantics; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.common.request.ReadRequest; import org.simantics.db.exception.DatabaseException; import org.simantics.district.network.ontology.DistrictNetworkResource; import org.simantics.district.network.profile.RuntimeDynamicVisualisationsRequest; import org.simantics.district.network.ui.DistrictDiagramViewer; import org.simantics.district.network.ui.nodes.DeferredRenderingNode; import org.simantics.district.network.ui.nodes.DistrictNetworkEdgeArrayNode; import org.simantics.district.network.ui.nodes.DistrictNetworkHoverInfoNode; import org.simantics.district.network.ui.nodes.DynamicVisualisationContributionsNode; import org.simantics.district.network.ui.nodes.ElevationServerNode; import org.simantics.district.network.ui.styles.DistrictNetworkHoverInfoStyle; import org.simantics.district.network.ui.styles.DistrictNetworkHoverInfoStyle.StyleResult; import org.simantics.district.network.visualisations.model.ColorBarOptions; import org.simantics.district.network.visualisations.model.DynamicColorContribution; import org.simantics.district.network.visualisations.model.DynamicSizeContribution; import org.simantics.district.network.visualisations.model.DynamicVisualisation; import org.simantics.district.network.visualisations.model.SizeBarOptions; import org.simantics.g2d.canvas.ICanvasContext; import org.simantics.g2d.canvas.impl.AbstractCanvasParticipant; import org.simantics.g2d.canvas.impl.SGNodeReflection.SGInit; import org.simantics.maps.elevation.server.SingletonTiffTileInterface; import org.simantics.scenegraph.INode; import org.simantics.scenegraph.g2d.G2DParentNode; import org.simantics.scenegraph.g2d.events.EventHandlerReflection.EventHandler; import org.simantics.scenegraph.g2d.events.command.CommandEvent; import org.simantics.utils.datastructures.Pair; import org.simantics.utils.datastructures.hints.HintListenerAdapter; import org.simantics.utils.datastructures.hints.IHintContext.Key; import org.simantics.utils.datastructures.hints.IHintListener; import org.simantics.utils.datastructures.hints.IHintObservable; import org.simantics.utils.threads.IThreadWorkQueue; import org.simantics.utils.threads.ThreadUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class DynamicVisualisationContributionsParticipant extends AbstractCanvasParticipant { private static final Logger LOGGER = LoggerFactory.getLogger(DynamicVisualisationContributionsParticipant.class); IHintListener hintListener = new HintListenerAdapter() { public void hintChanged(IHintObservable sender, Key key, Object oldValue, Object newValue) { ICanvasContext cc = getContext(); if (cc != null) { updateNode(); cc.getContentContext().setDirty(); } } }; private DynamicVisualisationContributionsNode node; private AffineTransform transform; private DistrictNetworkHoverInfoNode hoverInfoNode; private DeferredRenderingNode deferredEdgeArrowRendererNode; private ElevationServerNode showElevationServerBoundingBoxNode; public DynamicVisualisationContributionsParticipant(AffineTransform tr) { this.transform = tr; } @Override public void addedToContext(ICanvasContext ctx) { super.addedToContext(ctx); getHintStack().addKeyHintListener(getThread(), DistrictDiagramViewer.KEY_MAP_COLORING_OBJECTS, hintListener); getHintStack().addKeyHintListener(getThread(), DistrictDiagramViewer.KEY_MAP_COLOR_BAR_OPTIONS, hintListener); getHintStack().addKeyHintListener(getThread(), DistrictDiagramViewer.KEY_MAP_SIZING_OBJECTS, hintListener); getHintStack().addKeyHintListener(getThread(), DistrictDiagramViewer.KEY_MAP_SIZE_BAR_OPTIONS, hintListener); getHintStack().addKeyHintListener(getThread(), DistrictDiagramViewer.KEY_SHOW_ELEVATION_SERVER, hintListener); } @Override public void removedFromContext(ICanvasContext ctx) { // Ensure hover polling is stopped if (hoverUpdateSchedule != null && !hoverUpdateSchedule.isDone()) { hoverUpdateSchedule.cancel(false); } getHintStack().removeKeyHintListener(getThread(), DistrictDiagramViewer.KEY_MAP_COLORING_OBJECTS, hintListener); getHintStack().removeKeyHintListener(getThread(), DistrictDiagramViewer.KEY_MAP_COLOR_BAR_OPTIONS, hintListener); getHintStack().removeKeyHintListener(getThread(), DistrictDiagramViewer.KEY_MAP_SIZING_OBJECTS, hintListener); getHintStack().removeKeyHintListener(getThread(), DistrictDiagramViewer.KEY_MAP_SIZE_BAR_OPTIONS, hintListener); getHintStack().removeKeyHintListener(getThread(), DistrictDiagramViewer.KEY_SHOW_ELEVATION_SERVER, hintListener); super.removedFromContext(ctx); } @SGInit public void initSG(G2DParentNode parent) { node = parent.addNode(DynamicVisualisationContributionsNode.ID, DynamicVisualisationContributionsNode.class); node.setTransform(transform); node.setEnabled(true); node.setZIndex(1000); hoverInfoNode = parent.addNode("districtNetworkHoverInfoNode", DistrictNetworkHoverInfoNode.class); hoverInfoNode.setLookupId("districtNetworkHoverInfoNode"); hoverInfoNode.setTransform(transform); hoverInfoNode.setZIndex(Integer.MAX_VALUE - 500); Pair> dearn = DistrictNetworkEdgeArrayNode.renderer(); deferredEdgeArrowRendererNode = parent.addNode(dearn.first, dearn.second); showElevationServerBoundingBoxNode = parent.addNode(ElevationServerNode.ID, ElevationServerNode.class); showElevationServerBoundingBoxNode.setTransform(transform); } @EventHandler(priority = 0) protected boolean handleKeyEvent(CommandEvent e) { if (e.command.equals(DistrictDiagramViewer.MAP_COLOR_BAR_OPTIONS_CHANGE)) { System.out.println(e); return true; } return false; } protected void updateNode() { node.setDynamicColoringObjects(getDynamicColoringObjects()); node.setColorBarOptions(getColorBarOptions()); node.setDynamicSizingObjects(getDynamicSizingObjects()); node.setSizeBarOptions(getSizeBarOptions()); showElevationServerBoundingBoxNode.setRectangles(getRectangles()); } private Collection getRectangles() { Boolean enabled = getHint(DistrictDiagramViewer.KEY_SHOW_ELEVATION_SERVER); if (enabled != null && enabled) return SingletonTiffTileInterface.getBoundingBoxes(); return Collections.emptyList(); } private Map getDynamicColoringObjects() { return getHint(DistrictDiagramViewer.KEY_MAP_COLORING_OBJECTS); } private ColorBarOptions getColorBarOptions() { return getHint(DistrictDiagramViewer.KEY_MAP_COLOR_BAR_OPTIONS); } private Map getDynamicSizingObjects() { return getHint(DistrictDiagramViewer.KEY_MAP_SIZING_OBJECTS); } private SizeBarOptions getSizeBarOptions() { return getHint(DistrictDiagramViewer.KEY_MAP_SIZE_BAR_OPTIONS); } private ScheduledFuture hoverUpdateSchedule; private static final Object COMPLETE = new Object(); public void hoverNode(Resource runtimeDiagram, Resource mapElement, INode hoveredNode, int zoomLevel) { IThreadWorkQueue thread = getThread(); Simantics.getSession().asyncRequest(new ReadRequest() { @Override public void run(ReadGraph graph) throws DatabaseException { DynamicVisualisation visualisation = graph.syncRequest(new RuntimeDynamicVisualisationsRequest(runtimeDiagram)); if (visualisation == null) return; cancelCurrentHoverUpdate(); hoverUpdateSchedule = ThreadUtils.getNonBlockingWorkExecutor().scheduleWithFixedDelay( () -> updateHoverInfo(runtimeDiagram, mapElement, hoveredNode, zoomLevel, visualisation, thread), 0, visualisation.getInterval(), TimeUnit.MILLISECONDS); } }); } private void updateHoverInfo(Resource runtimeDiagram, Resource mapElement, INode hoveredNode, int zoomLevel, DynamicVisualisation visualisation, IThreadWorkQueue thread) { CompletableFuture future = new CompletableFuture<>(); try { Simantics.getSession().syncRequest(new ReadRequest() { @Override public void run(ReadGraph graph) throws DatabaseException { Set mapElementTypes = graph.getTypes(mapElement); if (mapElementTypes.isEmpty()) { future.complete(COMPLETE); return; } DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); boolean doHover = (mapElementTypes.contains(DN.Vertex) && visualisation.isKeyVariablesVertexHover()) || (mapElementTypes.contains(DN.Edge) && visualisation.isKeyVariablesEdgesHover()); StyleResult results = DistrictNetworkHoverInfoStyle.doCalculateStyleResult(graph, runtimeDiagram, mapElement); if (results != null) { Point2D location = DistrictNetworkHoverInfoStyle.calculatePoint(hoveredNode, zoomLevel, null); thread.asyncExec(() -> { if (isRemoved()) return; if (doHover) { hoverInfoNode.setLabels(results.getLabels()); hoverInfoNode.setOrigin(results.getOrigin()); hoverInfoNode.setMousePosition(location); hoverInfoNode.setHoveredNode(hoveredNode); } else { hoverInfoNode.setHoveredNode(null); } future.complete(COMPLETE); }); } else { future.complete(COMPLETE); } } }); } catch (DatabaseException e) { future.completeExceptionally(e); } // this waits until everything is done try { future.get(); } catch (InterruptedException | ExecutionException e) { LOGGER.debug("Interrupted hovering", e); } } public boolean doHover(boolean hover, boolean isConnectionTool) { if (!hover) cancelCurrentHoverUpdate(); return hoverInfoNode.hover(hover, isConnectionTool); } private void cancelCurrentHoverUpdate() { if (hoverUpdateSchedule != null && !hoverUpdateSchedule.isDone()) hoverUpdateSchedule.cancel(false); } }