package org.simantics.district.network.ui.nodes; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; import java.awt.geom.Line2D; import java.awt.geom.Path2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import org.simantics.scenegraph.ParentNode; import org.simantics.scenegraph.g2d.G2DNode; import org.simantics.scenegraph.g2d.nodes.spatial.RTreeNode; import org.simantics.scenegraph.utils.NodeUtil; import org.simantics.utils.datastructures.Pair; public class DistrictNetworkEdgeArrayNode extends G2DNode implements DeferredNode { private static final long serialVersionUID = -8377444630205663419L; public static final String NODE_KEY = "DISTRICT_NETWORK_EDGE_ARRAY"; private static final String RENDERER_ID = "edgeArrayDeferredRenderer"; private DistrictNetworkEdgeNode edgeNode = null; private Double arrowLength; private Path2D arrowPath; @Override public void render(Graphics2D g2d) { ParentNode root = (ParentNode) NodeUtil.getNearestParentOfType(this, RTreeNode.class); DeferredRenderingNode deferred = root != null ? (DeferredRenderingNode) root.getNode(RENDERER_ID) : null; if (deferred != null) deferred.deferNode(g2d.getTransform(), this); else renderDeferred(g2d); } @Override public void renderDeferred(Graphics2D g2d) { // Draw arrow if (arrowLength != null) { double scale = DistrictNetworkEdgeNode.scaleStroke ? (Double) g2d.getRenderingHint(DistrictRenderingHints.KEY_VIEW_SCALE_UNDER_SPATIAL_ROOT) : 1.0; g2d.setColor(Color.BLACK); float lw = DistrictNetworkEdgeNode.STROKE.getLineWidth() / (float)scale; g2d.setStroke(new BasicStroke(lw, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); double l = arrowLength; double w = 2 * (double) lw * Math.signum(l); if (Math.abs(w) > Math.abs(l)) w = l; double offset = 2 * (double) lw; int zoomLevel = (Integer) g2d.getRenderingHint(DistrictRenderingHints.KEY_VIEW_ZOOM_LEVEL); Point2D centerPoint = edgeNode.getCenterPoint(zoomLevel); Point2D direction = edgeNode.getDirection(zoomLevel); double centerX = centerPoint.getX(), centerY = centerPoint.getY(); double deltaX = direction.getX(), deltaY = direction.getY(); // Ensure the line is always rendered on top of the edge // to prevent overlap with static info rendered below it. double odx = offset * deltaY; double ody = offset * deltaX; if (odx < 0) { odx = -odx; ody = -ody; } double x0 = centerX - l/2 * deltaX + ody; double y0 = centerY - l/2 * deltaY - odx; double x1 = centerX + (l/2 - w) * deltaX + ody; double y1 = centerY + (l/2 - w) * deltaY - odx; g2d.draw(new Line2D.Double(x0, y0, x1, y1)); arrowPath = new Path2D.Double(); arrowPath.moveTo(x1 + w * deltaX, y1 + w * deltaY); arrowPath.lineTo(x1 + w * deltaY, y1 - w * deltaX); arrowPath.lineTo(x1 - w * deltaY, y1 + w * deltaX); arrowPath.closePath(); g2d.fill(arrowPath); } } @Override public Rectangle2D getBoundsInLocal() { return null; } public void setEdgeNode(DistrictNetworkEdgeNode edgeNode) { this.edgeNode = edgeNode; } public void setArrowLength(Double length) { this.arrowLength = length; } public static Pair> renderer() { return Pair.make(RENDERER_ID, DeferredRenderingNode.class); } }