-/*******************************************************************************\r
- * Copyright (c) 2007, 2011 Association for Decentralized Information Management in\r
- * Industry THTH ry.\r
- * All rights reserved. This program and the accompanying materials\r
- * are made available under the terms of the Eclipse Public License v1.0\r
- * which accompanies this distribution, and is available at\r
- * http://www.eclipse.org/legal/epl-v10.html\r
- *\r
- * Contributors:\r
- * VTT Technical Research Centre of Finland - initial API and implementation\r
- *******************************************************************************/\r
-package org.simantics.scenegraph.g2d.nodes.connection;\r
-\r
-import java.awt.AlphaComposite;\r
-import java.awt.BasicStroke;\r
-import java.awt.Color;\r
-import java.awt.Composite;\r
-import java.awt.Graphics2D;\r
-import java.awt.Shape;\r
-import java.awt.Stroke;\r
-import java.awt.geom.AffineTransform;\r
-import java.awt.geom.Point2D;\r
-import java.awt.geom.Rectangle2D;\r
-import java.util.ArrayList;\r
-import java.util.Collection;\r
-\r
-import org.simantics.diagram.connection.RouteGraph;\r
-import org.simantics.diagram.connection.RouteLineHalf;\r
-import org.simantics.diagram.connection.actions.IAction;\r
-import org.simantics.diagram.connection.rendering.IRouteGraphRenderer;\r
-import org.simantics.scenegraph.utils.Quality;\r
-import org.simantics.scenegraph.utils.QualityHints;\r
-\r
-/**\r
- * @author Tuukka Lehtonen\r
- */\r
-public class HighlightActionPointsAction implements IAction {\r
-\r
- public static enum Action {\r
- REMOVE,\r
- RECONNECT\r
- }\r
-\r
- public static class Pick {\r
- public static final Pick MISS = new Pick(null, null);\r
-\r
- Action action;\r
- RouteLineHalf line;\r
-\r
- public Pick(Action action, RouteLineHalf line) {\r
- this.action = action;\r
- this.line = line;\r
- }\r
-\r
- public boolean hasAction(Action action) {\r
- return this.action == action;\r
- }\r
-\r
- public boolean hasResult() {\r
- return action != null && line != null;\r
- }\r
-\r
- public boolean matches(Action action, RouteLineHalf line) {\r
- if (this.action == null || this.line == null)\r
- return false;\r
- return this.action.equals(action) && this.line.equals(line);\r
- }\r
- }\r
-\r
- private static final Shape CROSS_SHAPE = ActionShapes.CROSS_SHAPE;\r
- private static final Shape SCISSOR_SHAPE = ActionShapes.transformShape(ActionShapes.SCISSOR_SHAPE, 1, 1, 0, 0, -Math.PI/2);\r
-\r
- private static final Color CROSS_COLOR = new Color(0xe4, 0x40, 0x61);\r
- private static final Color SCISSOR_COLOR = new Color(20, 20, 20);\r
-\r
- public static final Stroke STROKE = new BasicStroke(0.1f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);\r
- public static final AlphaComposite NO_HIT_COMPOSITE = AlphaComposite.SrcOver.derive(0.8f);\r
- public static final AlphaComposite HIT_COMPOSITE = AlphaComposite.SrcOver.derive(0.2f);\r
-\r
- public static final double DEGENERATED_LINE_LENGTH = 1;\r
- public static final double CUT_DIST_FROM_END = 0.75;\r
-\r
- RouteGraph rg;\r
-\r
- transient Collection<RouteLineHalf> lhs = new ArrayList<RouteLineHalf>();\r
- transient AffineTransform transform = new AffineTransform();\r
- transient Rectangle2D rect = new Rectangle2D.Double();\r
- transient Point2D point = new Point2D.Double();\r
-\r
- public HighlightActionPointsAction(RouteGraph rg) {\r
- this.rg = rg;\r
- }\r
-\r
- public void setRouteGraph(RouteGraph rg) {\r
- this.rg = rg;\r
- }\r
-\r
- @Override\r
- public void render(Graphics2D g, IRouteGraphRenderer renderer, double mouseX, double mouseY) {\r
- // Cannot perform cut or delete segment actions\r
- // on connections between 2 terminals.\r
- boolean simpleConnection = (rg.isSimpleConnection() || rg.getTerminals().size() <= 2);\r
- boolean branchedConnection = rg.getTerminals().size() > 2;\r
- if (!branchedConnection || simpleConnection)\r
- return;\r
-\r
- AffineTransform preTr = g.getTransform();\r
- double realViewScale = 1.0 / getScale(preTr);\r
- //System.out.println(realViewScale);\r
- // Don't render any of the actions if they could not be seen anyway.\r
- if (realViewScale > 0.7)\r
- return;\r
-\r
- lhs.clear();\r
- rg.getLineHalves(lhs);\r
-\r
- Pick pick = pickAction(rg, lhs, preTr, mouseX, mouseY);\r
-\r
- Composite originalComposite = g.getComposite();\r
- Composite basicComposite = pick.action != null ? HIT_COMPOSITE : NO_HIT_COMPOSITE;\r
- g.setComposite(basicComposite);\r
-\r
- // Always render these in high quality because otherwise the shapes\r
- // will render with ugly artifacts when zoom level is a bit higher.\r
- QualityHints origQualityHints = QualityHints.getQuality(g);\r
- QualityHints.getHints(Quality.HIGH).setQuality(g);\r
-\r
- // Render line removal markers\r
- if (!simpleConnection) {\r
- g.setPaint(CROSS_COLOR);\r
- for (RouteLineHalf lh : lhs) {\r
- if (removeLocation(lh, point) == null)\r
- continue;\r
- boolean hit = pick.matches(Action.REMOVE, lh);\r
- if (hit)\r
- g.setComposite(originalComposite);\r
- g.translate(point.getX(), point.getY());\r
- g.fill(CROSS_SHAPE);\r
- g.setTransform(preTr);\r
- if (hit)\r
- g.setComposite(basicComposite);\r
- }\r
- }\r
-\r
- // Render reconnection markers if the connection is branched.\r
- if (branchedConnection) {\r
- g.setPaint(SCISSOR_COLOR);\r
- for (RouteLineHalf lh : lhs) {\r
- if (reconnectLocation(lh, point) == null)\r
- continue;\r
- boolean hit = pick.matches(Action.RECONNECT, lh);\r
- if (hit)\r
- g.setComposite(originalComposite);\r
- transform.setToTranslation(point.getX(), point.getY());\r
- if (!lh.getLine().isHorizontal())\r
- transform.rotate(Math.PI/2);\r
- transform.translate(0, 0.35);\r
- g.transform(transform);\r
- g.fill(SCISSOR_SHAPE);\r
- g.setTransform(preTr);\r
- if (hit)\r
- g.setComposite(basicComposite);\r
- }\r
- }\r
-\r
- origQualityHints.setQuality(g);\r
- g.setComposite(originalComposite);\r
- }\r
-\r
- public Pick pickAction(RouteGraph rg, AffineTransform viewTr, double mouseX, double mouseY) {\r
- boolean branchedConnection = rg.getTerminals().size() > 2;\r
- boolean simpleConnection = (rg.isSimpleConnection() || rg.getTerminals().size() <= 2);\r
- if (!branchedConnection || simpleConnection)\r
- return null;\r
-\r
- lhs.clear();\r
- return pickAction(rg, rg.getLineHalves(lhs), viewTr, mouseX, mouseY);\r
- }\r
-\r
- public Pick pickAction(RouteGraph rg, Collection<RouteLineHalf> lhs, AffineTransform viewTr, double mouseX, double mouseY) {\r
- boolean branchedConnection = rg.getTerminals().size() > 2;\r
- boolean simpleConnection = (rg.isSimpleConnection() || rg.getTerminals().size() <= 2);\r
- if (!branchedConnection || simpleConnection || viewTr == null)\r
- return Pick.MISS;\r
-\r
- double viewScale = 1.0 / getScale(viewTr);\r
- if (viewScale > 0.7)\r
- return Pick.MISS;\r
-\r
- double nearest = Double.MAX_VALUE;\r
- RouteLineHalf selected = null;\r
- Action selectedAction = null;\r
-\r
- // Pick line removal markers\r
- if (!simpleConnection) {\r
- double s = ActionShapes.CROSS_WIDTH * 0.25;\r
- for (RouteLineHalf lh : lhs) {\r
- if (removeLocation(lh, point) == null)\r
- continue;\r
- double x = point.getX();\r
- double y = point.getY();\r
- rect.setFrameFromCenter(x, y, x-s, y-s);\r
- boolean hit = rect.contains(mouseX, mouseY);\r
- if (hit) {\r
- double distSq = distSq(x, y, mouseX, mouseY);\r
- if (distSq < nearest) {\r
- nearest = distSq;\r
- selected = lh;\r
- selectedAction = Action.REMOVE;\r
- }\r
- }\r
- }\r
- }\r
-\r
- // Pick reconnection markers if the connection is branched.\r
- if (branchedConnection) {\r
- double w = ActionShapes.SCISSOR_HEIGHT * 0.4;\r
- double h = ActionShapes.SCISSOR_WIDTH * 0.3;\r
- for (RouteLineHalf lh : lhs) {\r
- if (reconnectLocation(lh, point) == null)\r
- continue;\r
- double x = point.getX();\r
- double y = point.getY();\r
- rect.setFrameFromCenter(x, y, x-w, y-h);\r
- boolean hit = rect.contains(mouseX, mouseY);\r
- if (hit) {\r
- double distSq = distSq(x, y, mouseX, mouseY);\r
- if (distSq < nearest) {\r
- nearest = distSq;\r
- selected = lh;\r
- selectedAction = Action.RECONNECT;\r
- }\r
- }\r
- }\r
- }\r
-\r
- return selected == null ? Pick.MISS : new Pick(selectedAction, selected);\r
- }\r
-\r
- private static Point2D removeLocation(RouteLineHalf lh, Point2D p) {\r
- if (lh.getLine().getTerminal() == null)\r
- return null;\r
-\r
- double x = lh.getLink().getX();\r
- double y = lh.getLink().getY();\r
- if (lh.getLine().isHorizontal()) {\r
- x = (lh.getLine().getBegin().getX() + lh.getLine().getEnd().getX()) * .5;\r
- } else {\r
- y = (lh.getLine().getBegin().getY() + lh.getLine().getEnd().getY()) * .5;\r
- }\r
- p.setLocation(x, y);\r
- return p;\r
- }\r
-\r
- private static Point2D reconnectLocation(RouteLineHalf lh, Point2D p) {\r
- if (lh.getLine().getLength() < DEGENERATED_LINE_LENGTH*3)\r
- return null;\r
-\r
- final double dist = CUT_DIST_FROM_END;\r
- double x = lh.getLink().getX();\r
- double y = lh.getLink().getY();\r
- if (lh.getLine().isHorizontal()) {\r
- if (lh.getLink() == lh.getLine().getBegin())\r
- x += dist*2;\r
- else\r
- x -= dist*2;\r
- } else {\r
- if (lh.getLink() == lh.getLine().getBegin())\r
- y += dist*2;\r
- else\r
- y -= dist*2;\r
- }\r
- p.setLocation(x, y);\r
- return p;\r
- }\r
-\r
- private static double distSq(double x1, double y1, double x2, double y2) {\r
- double dx = x2 - x1;\r
- double dy = y2 - y1;\r
- return dx * dx + dy * dy;\r
- }\r
-\r
- private static double getScale(AffineTransform at)\r
- {\r
- double m00 = at.getScaleX();\r
- double m11 = at.getScaleY();\r
- double m10 = at.getShearY();\r
- double m01 = at.getShearX();\r
-\r
- return Math.sqrt(Math.abs(m00*m11 - m10*m01));\r
- }\r
-\r
-}\r
+/*******************************************************************************
+ * 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.Color;
+import java.awt.Composite;
+import java.awt.Graphics2D;
+import java.awt.Shape;
+import java.awt.Stroke;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+import java.util.ArrayList;
+import java.util.Collection;
+
+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;
+import org.simantics.scenegraph.utils.Quality;
+import org.simantics.scenegraph.utils.QualityHints;
+
+/**
+ * @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);
+ }
+ }
+
+ private static final Shape CROSS_SHAPE = ActionShapes.CROSS_SHAPE;
+ private static final Shape SCISSOR_SHAPE = ActionShapes.transformShape(ActionShapes.SCISSOR_SHAPE, 1, 1, 0, 0, -Math.PI/2);
+
+ private static final Color CROSS_COLOR = new Color(0xe4, 0x40, 0x61);
+ private static final Color SCISSOR_COLOR = new Color(20, 20, 20);
+
+ public static final Stroke STROKE = new BasicStroke(0.1f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
+ public static final AlphaComposite NO_HIT_COMPOSITE = AlphaComposite.SrcOver.derive(0.8f);
+ public static final AlphaComposite HIT_COMPOSITE = AlphaComposite.SrcOver.derive(0.2f);
+
+ public static final double DEGENERATED_LINE_LENGTH = 1;
+ public static final double CUT_DIST_FROM_END = 0.75;
+
+ RouteGraph rg;
+
+ transient Collection<RouteLineHalf> lhs = new ArrayList<RouteLineHalf>();
+ transient AffineTransform transform = new AffineTransform();
+ transient Rectangle2D rect = new Rectangle2D.Double();
+ transient Point2D point = new Point2D.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;
+ if (!branchedConnection || simpleConnection)
+ return;
+
+ AffineTransform preTr = g.getTransform();
+ double realViewScale = 1.0 / getScale(preTr);
+ //System.out.println(realViewScale);
+ // Don't render any of the actions if they could not be seen anyway.
+ if (realViewScale > 0.7)
+ return;
+
+ lhs.clear();
+ rg.getLineHalves(lhs);
+
+ Pick pick = pickAction(rg, lhs, preTr, mouseX, mouseY);
+
+ Composite originalComposite = g.getComposite();
+ Composite basicComposite = pick.action != null ? HIT_COMPOSITE : NO_HIT_COMPOSITE;
+ g.setComposite(basicComposite);
+
+ // Always render these in high quality because otherwise the shapes
+ // will render with ugly artifacts when zoom level is a bit higher.
+ QualityHints origQualityHints = QualityHints.getQuality(g);
+ QualityHints.getHints(Quality.HIGH).setQuality(g);
+
+ // Render line removal markers
+ if (!simpleConnection) {
+ g.setPaint(CROSS_COLOR);
+ for (RouteLineHalf lh : lhs) {
+ if (removeLocation(lh, point) == null)
+ continue;
+ boolean hit = pick.matches(Action.REMOVE, lh);
+ if (hit)
+ g.setComposite(originalComposite);
+ g.translate(point.getX(), point.getY());
+ g.fill(CROSS_SHAPE);
+ g.setTransform(preTr);
+ if (hit)
+ g.setComposite(basicComposite);
+ }
+ }
+
+ // Render reconnection markers if the connection is branched.
+ if (branchedConnection) {
+ g.setPaint(SCISSOR_COLOR);
+ for (RouteLineHalf lh : lhs) {
+ if (reconnectLocation(lh, point) == null)
+ continue;
+ boolean hit = pick.matches(Action.RECONNECT, lh);
+ if (hit)
+ g.setComposite(originalComposite);
+ transform.setToTranslation(point.getX(), point.getY());
+ if (!lh.getLine().isHorizontal())
+ transform.rotate(Math.PI/2);
+ transform.translate(0, 0.35);
+ g.transform(transform);
+ g.fill(SCISSOR_SHAPE);
+ g.setTransform(preTr);
+ if (hit)
+ g.setComposite(basicComposite);
+ }
+ }
+
+ origQualityHints.setQuality(g);
+ 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<RouteLineHalf> 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;
+
+ double viewScale = 1.0 / getScale(viewTr);
+ if (viewScale > 0.7)
+ return Pick.MISS;
+
+ double nearest = Double.MAX_VALUE;
+ RouteLineHalf selected = null;
+ Action selectedAction = null;
+
+ // Pick line removal markers
+ if (!simpleConnection) {
+ double s = ActionShapes.CROSS_WIDTH * 0.25;
+ for (RouteLineHalf lh : lhs) {
+ if (removeLocation(lh, point) == null)
+ continue;
+ double x = point.getX();
+ double y = point.getY();
+ rect.setFrameFromCenter(x, y, x-s, y-s);
+ 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;
+ }
+ }
+ }
+ }
+
+ // Pick reconnection markers if the connection is branched.
+ if (branchedConnection) {
+ double w = ActionShapes.SCISSOR_HEIGHT * 0.4;
+ double h = ActionShapes.SCISSOR_WIDTH * 0.3;
+ for (RouteLineHalf lh : lhs) {
+ if (reconnectLocation(lh, point) == null)
+ continue;
+ double x = point.getX();
+ double y = point.getY();
+ rect.setFrameFromCenter(x, y, x-w, y-h);
+ boolean hit = rect.contains(mouseX, mouseY);
+ if (hit) {
+ double distSq = distSq(x, y, mouseX, mouseY);
+ if (distSq < nearest) {
+ nearest = distSq;
+ selected = lh;
+ selectedAction = Action.RECONNECT;
+ }
+ }
+ }
+ }
+
+ return selected == null ? Pick.MISS : new Pick(selectedAction, selected);
+ }
+
+ private static Point2D removeLocation(RouteLineHalf lh, Point2D p) {
+ if (lh.getLine().getTerminal() == null)
+ return null;
+
+ 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;
+ }
+ p.setLocation(x, y);
+ return p;
+ }
+
+ private static Point2D reconnectLocation(RouteLineHalf lh, Point2D p) {
+ if (lh.getLine().getLength() < DEGENERATED_LINE_LENGTH*3)
+ return null;
+
+ final double dist = CUT_DIST_FROM_END;
+ 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;
+ }
+ p.setLocation(x, y);
+ return p;
+ }
+
+ 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));
+ }
+
+}