X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;f=bundles%2Forg.simantics.scenegraph%2Fsrc%2Forg%2Fsimantics%2Fscenegraph%2Fg2d%2Fnodes%2Fconnection%2FHighlightActionPointsAction.java;fp=bundles%2Forg.simantics.scenegraph%2Fsrc%2Forg%2Fsimantics%2Fscenegraph%2Fg2d%2Fnodes%2Fconnection%2FHighlightActionPointsAction.java;h=04d08e3ec155242b1b6d58fad89f62adbab6e3d7;hb=969bd23cab98a79ca9101af33334000879fb60c5;hp=0000000000000000000000000000000000000000;hpb=866dba5cd5a3929bbeae85991796acb212338a08;p=simantics%2Fplatform.git diff --git a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/connection/HighlightActionPointsAction.java b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/connection/HighlightActionPointsAction.java new file mode 100644 index 000000000..04d08e3ec --- /dev/null +++ b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/connection/HighlightActionPointsAction.java @@ -0,0 +1,313 @@ +/******************************************************************************* + * Copyright (c) 2007, 2011 Association for Decentralized Information Management in + * Industry THTH ry. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * VTT Technical Research Centre of Finland - initial API and implementation + *******************************************************************************/ +package org.simantics.scenegraph.g2d.nodes.connection; + +import java.awt.AlphaComposite; +import java.awt.BasicStroke; +import java.awt.Composite; +import java.awt.Graphics2D; +import java.awt.Stroke; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; + +import javax.imageio.ImageIO; + +import org.simantics.diagram.connection.RouteGraph; +import org.simantics.diagram.connection.RouteLineHalf; +import org.simantics.diagram.connection.actions.IAction; +import org.simantics.diagram.connection.rendering.IRouteGraphRenderer; + +/** + * @author Tuukka Lehtonen + */ +public class HighlightActionPointsAction implements IAction { + + public static enum Action { + REMOVE, + RECONNECT + } + + public static class Pick { + public static final Pick MISS = new Pick(null, null); + + Action action; + RouteLineHalf line; + + public Pick(Action action, RouteLineHalf line) { + this.action = action; + this.line = line; + } + + public boolean hasAction(Action action) { + return this.action == action; + } + + public boolean hasResult() { + return action != null && line != null; + } + + public boolean matches(Action action, RouteLineHalf line) { + if (this.action == null || this.line == null) + return false; + return this.action.equals(action) && this.line.equals(line); + } + } + + static BufferedImage cross; + static BufferedImage cut; + + static { + cross = safeReadImage("cross.png"); + cut = safeReadImage("cut.png"); + } + + public static final Stroke STROKE = new BasicStroke(0.1f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND); + public static final AlphaComposite COMPOSITE = AlphaComposite.SrcOver.derive(0.6f); + + public static final double DEGENERATED_LINE_LENGTH = 1; + public static final double CUT_DIST_FROM_END = 0.5; + + RouteGraph rg; + + transient Collection lhs = new ArrayList(); + transient AffineTransform transform = new AffineTransform(); + transient AffineTransform transform2 = new AffineTransform(); + transient Rectangle2D rect = new Rectangle2D.Double(); + + public HighlightActionPointsAction(RouteGraph rg) { + this.rg = rg; + } + + public void setRouteGraph(RouteGraph rg) { + this.rg = rg; + } + + @Override + public void render(Graphics2D g, IRouteGraphRenderer renderer, double mouseX, double mouseY) { + // Cannot perform cut or delete segment actions on connections between 2 + // terminals. + boolean simpleConnection = (rg.isSimpleConnection() || rg.getTerminals().size() <= 2); + boolean branchedConnection = rg.getTerminals().size() > 2; + + lhs.clear(); + rg.getLineHalves(lhs); + + AffineTransform preTr = g.getTransform(); + double viewScale = 1.0 / getScale(preTr); + transform2.setToScale(viewScale, viewScale); + Composite originalComposite = g.getComposite(); + g.setComposite(COMPOSITE); + + Pick pick = pickAction(rg, lhs, preTr, mouseX, mouseY); + + if (!simpleConnection) { + double crossW = cross.getWidth()*viewScale*.5; + double crossH = cross.getHeight()*viewScale*.5; + + // Render line removal markers + for (RouteLineHalf lh : lhs) { +// if (lh.getLine().getLength() < DEGENERATED_LINE_LENGTH) +// continue; +// if (!lh.getLine().isTransient()) +// continue; + if (lh.getLine().getTerminal() == null) + continue; + double x = lh.getLink().getX(); + double y = lh.getLink().getY(); + if (lh.getLine().isHorizontal()) { + x = (lh.getLine().getBegin().getX() + lh.getLine().getEnd().getX()) * .5; + } else { + y = (lh.getLine().getBegin().getY() + lh.getLine().getEnd().getY()) * .5; + } + + boolean hit = pick.matches(Action.REMOVE, lh); + + if (hit) + g.setComposite(originalComposite); + transform.setToTranslation(x-crossW, y-crossH); + g.transform(transform); + g.drawImage(cross, transform2, null); + g.setTransform(preTr); + if (hit) + g.setComposite(COMPOSITE); + } + } + + // Render reconnection markers if the connection is branched. + if (branchedConnection) { + double cutW = cut.getWidth()*viewScale*.5; + double cutH = cut.getHeight()*viewScale*.5; + + final double dist = CUT_DIST_FROM_END; + for (RouteLineHalf lh : lhs) { + if (lh.getLine().getLength() < DEGENERATED_LINE_LENGTH*3) + continue; + double x = lh.getLink().getX(); + double y = lh.getLink().getY(); + if (lh.getLine().isHorizontal()) { + if (lh.getLink() == lh.getLine().getBegin()) + x += dist*2; + else + x -= dist*2; + } else { + if (lh.getLink() == lh.getLine().getBegin()) + y += dist*2; + else + y -= dist*2; + } + + boolean hit = pick.matches(Action.RECONNECT, lh); + + if (hit) + g.setComposite(originalComposite); + transform.setToTranslation(x-cutW, y-cutH); + if (!lh.getLine().isHorizontal()) { + transform.rotate(Math.PI/2, cutW, cutH); + } + g.transform(transform); + g.drawImage(cut, transform2, null); + g.setTransform(preTr); + if (hit) + g.setComposite(COMPOSITE); + } + } + + g.setComposite(originalComposite); + } + + public Pick pickAction(RouteGraph rg, AffineTransform viewTr, double mouseX, double mouseY) { + boolean branchedConnection = rg.getTerminals().size() > 2; + boolean simpleConnection = (rg.isSimpleConnection() || rg.getTerminals().size() <= 2); + if (!branchedConnection || simpleConnection) + return null; + + lhs.clear(); + return pickAction(rg, rg.getLineHalves(lhs), viewTr, mouseX, mouseY); + } + + public Pick pickAction(RouteGraph rg, Collection lhs, AffineTransform viewTr, double mouseX, double mouseY) { + boolean branchedConnection = rg.getTerminals().size() > 2; + boolean simpleConnection = (rg.isSimpleConnection() || rg.getTerminals().size() <= 2); + if (!branchedConnection || simpleConnection || viewTr == null) + return Pick.MISS; + + lhs.clear(); + rg.getLineHalves(lhs); + + RouteLineHalf selected = null; + Action selectedAction = null; + double nearest = Double.MAX_VALUE; + double viewScale = 1.0 / getScale(viewTr); + + if (!simpleConnection) { + double crossW = cross.getWidth()*viewScale*.5; + double crossH = cross.getHeight()*viewScale*.5; + + // Render line removal markers + for (RouteLineHalf lh : lhs) { +// if (lh.getLine().getLength() < DEGENERATED_LINE_LENGTH) +// continue; +// if (!lh.getLine().isTransient()) +// continue; + if (lh.getLine().getTerminal() == null) + continue; + double x = lh.getLink().getX(); + double y = lh.getLink().getY(); + if (lh.getLine().isHorizontal()) { + x = (lh.getLine().getBegin().getX() + lh.getLine().getEnd().getX()) * .5; + } else { + y = (lh.getLine().getBegin().getY() + lh.getLine().getEnd().getY()) * .5; + } + + rect.setFrameFromCenter(x, y, x-crossW, y-crossH); + boolean hit = rect.contains(mouseX, mouseY); + if (hit) { + double distSq = distSq(x, y, mouseX, mouseY); + if (distSq < nearest) { + nearest = distSq; + selected = lh; + selectedAction = Action.REMOVE; + } + } + } + } + + // Render reconnection markers if the connection is branched. + if (branchedConnection) { + double cutW = cut.getWidth()*viewScale*.5; + double cutH = cut.getHeight()*viewScale*.5; + + final double dist = CUT_DIST_FROM_END; + for (RouteLineHalf lh : lhs) { + if (lh.getLine().getLength() < DEGENERATED_LINE_LENGTH*3) + continue; + double x = lh.getLink().getX(); + double y = lh.getLink().getY(); + if (lh.getLine().isHorizontal()) { + if (lh.getLink() == lh.getLine().getBegin()) + x += dist*2; + else + x -= dist*2; + } else { + if (lh.getLink() == lh.getLine().getBegin()) + y += dist*2; + else + y -= dist*2; + } + + rect.setFrameFromCenter(x, y, x-cutW, y-cutH); + boolean hit = rect.contains(mouseX, mouseY); + if (hit) { + double distSq = distSq(x, y, mouseX, mouseY); + if (distSq < nearest) { + nearest = dist; + selected = lh; + selectedAction = Action.RECONNECT; + } + } + } + } + + return selected == null ? Pick.MISS : new Pick(selectedAction, selected); + } + + private static double distSq(double x1, double y1, double x2, double y2) { + double dx = x2 - x1; + double dy = y2 - y1; + return dx * dx + dy * dy; + } + + private static double getScale(AffineTransform at) + { + double m00 = at.getScaleX(); + double m11 = at.getScaleY(); + double m10 = at.getShearY(); + double m01 = at.getShearX(); + + return Math.sqrt(Math.abs(m00*m11 - m10*m01)); + } + + private static BufferedImage safeReadImage(String name) { + try { + URL url = HighlightActionPointsAction.class.getResource(name); + return ImageIO.read(url); + } catch (IOException e) { + return null; + } + } + +}