import java.awt.geom.Rectangle2D;
import java.lang.reflect.Constructor;
import java.util.Collection;
+import java.util.List;
import java.util.Map;
import org.simantics.diagram.connection.RouteGraph;
import org.simantics.scenegraph.ISelectionPainterNode;
import org.simantics.scenegraph.g2d.G2DNode;
import org.simantics.scenegraph.g2d.G2DParentNode;
+import org.simantics.scenegraph.g2d.G2DSceneGraph;
import org.simantics.scenegraph.g2d.IG2DNode;
import org.simantics.scenegraph.g2d.events.EventTypes;
import org.simantics.scenegraph.g2d.events.KeyEvent.KeyPressedEvent;
import org.simantics.scenegraph.g2d.events.command.Commands;
import org.simantics.scenegraph.g2d.nodes.GridNode;
import org.simantics.scenegraph.g2d.nodes.LinkNode;
+import org.simantics.scenegraph.g2d.nodes.NavigationNode;
+import org.simantics.scenegraph.g2d.nodes.SVGNodeAssignment;
import org.simantics.scenegraph.g2d.nodes.connection.HighlightActionPointsAction.Action;
import org.simantics.scenegraph.g2d.nodes.connection.HighlightActionPointsAction.Pick;
import org.simantics.scenegraph.g2d.snap.ISnapAdvisor;
+import org.simantics.scenegraph.utils.ColorUtil;
import org.simantics.scenegraph.utils.GeometryUtils;
import org.simantics.scenegraph.utils.InitValueSupport;
import org.simantics.scenegraph.utils.NodeUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import gnu.trove.map.hash.THashMap;
*/
public class RouteGraphNode extends G2DNode implements ISelectionPainterNode, InitValueSupport {
- private static final long serialVersionUID = -917194130412280965L;
+ private static final Logger LOGGER = LoggerFactory.getLogger(RouteGraphNode.class);
+
+ private static final long serialVersionUID = -917194130412280965L;
private static final double TOLERANCE = IAction.TOLERANCE;
private static final Stroke SELECTION_STROKE = new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER);
protected transient Map<Object,ILineEndStyle> dynamicStyles = null;
+
+ private transient boolean ignoreSelection = false;
@Override
public void initValues() {
wrapRenderer();
}
+ private static float tryParseFloat(String s, float def) {
+ try {
+ return Float.parseFloat(s);
+ } catch (NumberFormatException e) {
+ LOGGER.error("Could not parse '" + s + "' into float.");
+ return def;
+ }
+ }
+
+ /*
+ * 1.0 BUTT MITER 1.0 0.0
+ */
+ private static Stroke parseStroke(String definition) {
+
+ float width = 1.0f;
+ int cap = BasicStroke.CAP_BUTT;
+ int join = BasicStroke.JOIN_MITER;
+ float miterLimit = 1.0f;
+ float[] dash = { 1, 0};
+ float dash_phase = 0;
+
+ String[] parts = definition.split(" ");
+
+ if(parts.length > 0) {
+ width = tryParseFloat(parts[0], width);
+ }
+ if(parts.length > 1) {
+ if("BUTT".equals(parts[1])) cap = BasicStroke.CAP_BUTT;
+ else if("ROUND".equals(parts[1])) cap = BasicStroke.CAP_ROUND;
+ else if("SQUARE".equals(parts[1])) cap = BasicStroke.CAP_SQUARE;
+ }
+ if(parts.length > 2) {
+ if("BEVEL".equals(parts[2])) cap = BasicStroke.JOIN_BEVEL;
+ else if("MITER".equals(parts[2])) cap = BasicStroke.JOIN_MITER;
+ else if("ROUND".equals(parts[2])) cap = BasicStroke.JOIN_ROUND;
+ }
+ if(parts.length > 3) {
+ miterLimit = tryParseFloat(parts[3], miterLimit);
+ }
+ if(parts.length > 4) {
+ dash_phase = tryParseFloat(parts[4], dash_phase);
+ }
+ if(parts.length > 6) {
+ dash = new float[parts.length - 5];
+ for(int i=5;i<parts.length;i++) {
+ dash[i-5] = tryParseFloat(parts[i], 1.0f);
+ }
+ }
+
+
+ try {
+ return new BasicStroke(width, cap, join, miterLimit, dash, dash_phase);
+ } catch (IllegalArgumentException e) {
+ return new BasicStroke();
+ }
+
+ }
+
+ public void setAssignments(List<SVGNodeAssignment> assignments) {
+ for(SVGNodeAssignment ass : assignments) {
+ if("dynamicColor".equals(ass.elementId)) {
+ setDynamicColor(ColorUtil.hexColor(ass.value));
+ } else if("dynamicStroke".equals(ass.elementId)) {
+ setDynamicStroke(parseStroke(ass.value));
+ }
+ }
+ }
+
+ public void setIgnoreSelection(boolean value) {
+ ignoreSelection = value;
+ }
+
+ public boolean getIgnoreSelection() {
+ return ignoreSelection;
+ }
+
@PropertySetter("color")
@SyncField(value = {"dynamicColor"})
public void setDynamicColor(Color color) {
* @param after
* @return <code>true</code> if changes were fired
*/
- private boolean setRouteGraphAndFireChanges(RouteGraph before, RouteGraph after) {
+ public boolean setRouteGraphAndFireChanges(RouteGraph before, RouteGraph after) {
RouteGraphDelta delta = new RouteGraphDelta(before, after);
if (!delta.isEmpty()) {
setRouteGraph(after);
Object aaHint = g.getRenderingHint(RenderingHints.KEY_ANTIALIASING);
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
- boolean selected = NodeUtil.isSelected(this, 1);
+ boolean selected = ignoreSelection ? false : NodeUtil.isSelected(this, 1);
+
+ rg.updateTerminals();
if (currentAction != null) {
currentAction.render(g, renderer, mouseX, mouseY);
return null;
}
- private double getSelectionStrokeWidth() {
+ public double getSelectionStrokeWidth() {
if (selectionStroke instanceof BasicStroke) {
BasicStroke bs = (BasicStroke) selectionStroke;
return bs.getLineWidth();
@Override
protected boolean mouseDragged(MouseDragBegin e) {
+ // Consume event if drag is possible.
+ // PointerInteractor will call handleDrag with the MouseDragBegin event for the route line that is closest to the cursor.
+ return currentAction != null;
+ }
+
+ public boolean handleDrag(MouseDragBegin e) {
if (dragAction != null && !e.hasAnyModifier(MouseEvent.ALL_MODIFIERS_MASK) && e.button == MouseEvent.LEFT_BUTTON) {
currentAction = dragAction;
dragAction = null;
}
}
if (newBranchPointPosition != null) {
- RouteLine line = rg.pickLine(mouseX, mouseY, pickTolerance);
+ RouteLine line = rg.pickLine(mouseX, mouseY, scaledPickTolerance());
if (line != null) {
newBranchPointPosition.setLocation(mouseX, mouseY);
SplittedRouteGraph.snapToLine(newBranchPointPosition, line);
return false;
}
//System.out.println("move action");
- dragAction = SnappingMoveAction.create(rg, mouseX, mouseY, pickTolerance, moveFilter, getSnapAdvisor());
+ dragAction = SnappingMoveAction.create(rg, mouseX, mouseY, scaledPickTolerance(), moveFilter, getSnapAdvisor());
//System.out.println("DRAG ACTION: " + dragAction);
}
return false;
}
+ private double scaledPickTolerance() {
+ NavigationNode nn = NodeUtil.findNearestParentNode(this, NavigationNode.class);
+ double scale = 1.0;
+ if (nn != null) {
+ scale = GeometryUtils.getScale(nn.getTransform());
+ }
+ double pickDistance = 0;
+ G2DSceneGraph sg = NodeUtil.getRootNode(nn != null ? nn : this);
+ if (sg != null) {
+ pickDistance = sg.getGlobalProperty(G2DSceneGraph.PICK_DISTANCE, pickDistance);
+ }
+ return Math.max(getSelectionStrokeWidth() / 2.0, pickDistance / scale);
+ }
+
/**
* Checks the selections data node in the scene graph for any links
* @return
return false;
if (!e.hasAnyModifier(MouseEvent.ALL_MODIFIERS_MASK) && e.keyCode == KeyEvent.VK_S) {
- Object target = rg.pick(mouseX, mouseY, pickTolerance, RouteGraph.PICK_PERSISTENT_LINES | RouteGraph.PICK_TRANSIENT_LINES);
+ Object target = rg.pick(mouseX, mouseY, scaledPickTolerance(), RouteGraph.PICK_PERSISTENT_LINES | RouteGraph.PICK_TRANSIENT_LINES);
return splitTarget(target);
}
else if (!e.hasAnyModifier(MouseEvent.ALT_MASK | MouseEvent.ALT_GRAPH_MASK | MouseEvent.CTRL_MASK) && (e.keyCode == KeyEvent.VK_R || e.keyCode == KeyEvent.VK_D)) {
- Object target = rg.pick(mouseX, mouseY, pickTolerance, RouteGraph.PICK_PERSISTENT_LINES);
+ Object target = rg.pick(mouseX, mouseY, scaledPickTolerance(), RouteGraph.PICK_PERSISTENT_LINES);
return deleteTarget(target);
}
else if (e.keyCode == KeyEvent.VK_ESCAPE) {
}
else if (e.keyCode == KeyEvent.VK_ALT) {
// Begin connection branching visualization.
- RouteLine line = rg.pickLine(mouseX, mouseY, pickTolerance);
+ RouteLine line = rg.pickLine(mouseX, mouseY, scaledPickTolerance());
if (branchable && line != null) {
newBranchPointPosition = new Point2D.Double(mouseX, mouseY);
SplittedRouteGraph.snapToLine(newBranchPointPosition, line);