]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.g2d/src/org/simantics/g2d/elementclass/connection/ConnectionValidator.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.g2d / src / org / simantics / g2d / elementclass / connection / ConnectionValidator.java
index 67a5caa73c569b01147783a470020cdae237bbd4..cc5ccd015d6b8cc3547c2eb885efe92cc8a4dc99 100644 (file)
-/*******************************************************************************\r
- * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
- * in 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.g2d.elementclass.connection;\r
-\r
-import java.awt.geom.AffineTransform;\r
-import java.awt.geom.Path2D;\r
-import java.awt.geom.PathIterator;\r
-import java.awt.geom.Point2D;\r
-import java.awt.geom.Rectangle2D;\r
-import java.util.ArrayList;\r
-import java.util.Collection;\r
-import java.util.HashSet;\r
-import java.util.Iterator;\r
-import java.util.LinkedList;\r
-import java.util.List;\r
-import java.util.Set;\r
-\r
-import org.simantics.g2d.canvas.ICanvasContext;\r
-import org.simantics.g2d.diagram.IDiagram;\r
-import org.simantics.g2d.diagram.handler.Topology;\r
-import org.simantics.g2d.diagram.handler.Validator;\r
-import org.simantics.g2d.diagram.handler.Topology.Connection;\r
-import org.simantics.g2d.diagram.handler.impl.DiagramIssue;\r
-import org.simantics.g2d.diagram.participant.pointertool.TerminalUtil;\r
-import org.simantics.g2d.element.ElementUtils;\r
-import org.simantics.g2d.element.IElement;\r
-import org.simantics.g2d.element.handler.BendsHandler;\r
-import org.simantics.g2d.element.handler.EdgeVisuals;\r
-import org.simantics.g2d.element.handler.BendsHandler.AngleType;\r
-import org.simantics.g2d.element.handler.BendsHandler.Bend;\r
-import org.simantics.g2d.element.handler.EdgeVisuals.EdgeEnd;\r
-import org.simantics.g2d.routing.algorithm1.Rectangle;\r
-import org.simantics.g2d.utils.GeometryUtils;\r
-import org.simantics.g2d.utils.PathUtils;\r
-import org.simantics.g2d.utils.geom.DirectionSet;\r
-import org.simantics.utils.datastructures.TreeProblem;\r
-import org.simantics.utils.datastructures.TreeProblem.ProblemNode;\r
-\r
-/**\r
- * Checks whether Topology meets geometry  \r
- * \r
- * @author Toni Kalajainen\r
- */\r
-public class ConnectionValidator implements Validator {\r
-\r
-\r
-\r
-       public static final ConnectionValidator INSTANCE = new ConnectionValidator(); \r
-       private static final double TOLERANCE = 0.001;\r
-       \r
-       private static final Suggestion DISCONNECT_EDGES = new Suggestion() {\r
-               @Override\r
-               public boolean fix(IDiagram d, ICanvasContext ctx) {                    \r
-                       return false;\r
-               }\r
-               @Override\r
-               public String getMessage() {\r
-                       return "Disconnect edges";\r
-               }\r
-       };\r
-       \r
-       public static final double OBSTACLE_MARGINAL = 10.0; \r
-       \r
-       static Collection<Rectangle> createObstacles(IDiagram d) {\r
-               ArrayList<Rectangle> obstacles = new ArrayList<Rectangle>();\r
-               for(IElement e : d.getElements())\r
-               {\r
-                       if (e.getElementClass().containsClass(EdgeVisuals.class)) continue;\r
-                       Rectangle2D rect = ElementUtils.getElementBounds(e);\r
-                       obstacles.add(new Rectangle(\r
-                               rect.getMinX()-OBSTACLE_MARGINAL,\r
-                               rect.getMinY()-OBSTACLE_MARGINAL,\r
-                               rect.getMaxX()+OBSTACLE_MARGINAL,\r
-                               rect.getMaxY()+OBSTACLE_MARGINAL\r
-                       ));\r
-               }\r
-               return obstacles;\r
-       }\r
-       \r
-       // Force path to follow its path requirements \r
-       private static final Suggestion FORCE_PATH_TO_REQUIREMENTS = new Suggestion() {\r
-               @Override\r
-               public boolean fix(IDiagram d, ICanvasContext ctx) {\r
-                       \r
-                       // Get all edges\r
-                       ArrayList<PathRequirement> reqs = new ArrayList<PathRequirement>();                     \r
-                       for (IElement e : d.getElements())\r
-                       {\r
-                               BendsHandler bends = e.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);\r
-                               if (bends==null) continue;\r
-                               Path2D path = bends.getPath(e);\r
-                               assert(path!=null);\r
-                               reqs.clear();\r
-                               createPathRequirement(e, reqs);                 \r
-                               if (reqs.isEmpty()) continue;\r
-                               if (pathFollowsRequirements(path, reqs)) continue;\r
-                               \r
-                               AngleType at = bends.getAngleType(e);\r
-                               if (at==AngleType.Linear) {\r
-                                       path = createPathLinear(reqs);\r
-                               } else if (at==AngleType.RightAngled) {\r
-                                       path = createPathRightAngled(reqs);                                     \r
-                               } else if (at==AngleType.Quadratic) {\r
-                                       path = createPathQuadratic(reqs);\r
-                               } else if (at==AngleType.Line) {\r
-                                       path = createPathLine(reqs);\r
-                               } else assert(false);\r
-                               \r
-                               // Make up something!\r
-                               if (path==null)\r
-                                       path = createPathLine(reqs);\r
-\r
-                               AffineTransform elementToDiagramAt = ElementUtils.getInvTransform(e);\r
-                               path.transform(elementToDiagramAt);\r
-                               \r
-                               bends.setPath(e, path);\r
-                       }                       \r
-                       return false;\r
-               }\r
-               @Override\r
-               public String getMessage() {\r
-                       return "Update path";\r
-               }\r
-       };\r
-       private static final Issue PATH_IS_BROKEN = \r
-               new DiagramIssue("Path does not follow its requirements (connects and bends)", FORCE_PATH_TO_REQUIREMENTS, DISCONNECT_EDGES);\r
-       \r
-       @Override\r
-       public void validate(IDiagram d, ICanvasContext ctx, Collection<Issue> lst) {\r
-               if (!validateDiagramOk(d, ctx, lst))\r
-               {\r
-                       lst.add(PATH_IS_BROKEN);\r
-               }\r
-       }\r
-       \r
-       /**\r
-        * Return true if connections of a diagram are ok.\r
-        * @param d\r
-        * @param ctx\r
-        * @param lst\r
-        * @return\r
-        */\r
-       boolean validateDiagramOk(IDiagram d, ICanvasContext ctx, Collection<Issue> lst)\r
-       {\r
-               // Get all edges\r
-               ArrayList<PathRequirement> reqs = new ArrayList<PathRequirement>();\r
-               for (IElement e : d.getElements())\r
-               {\r
-                       BendsHandler bends = e.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);\r
-                       if (bends==null) continue;\r
-                       Path2D path = bends.getPath(e);\r
-\r
-                       reqs.clear();\r
-                       createPathRequirement(e, reqs);                 \r
-                       if (reqs.isEmpty()) continue;\r
-                       if (pathFollowsRequirements(path, reqs)) continue;\r
-                       return false;\r
-               }\r
-               return true;\r
-       }\r
-       \r
-       /**\r
-        * Forces the path to follow path requirements.\r
-        *  \r
-        * @param e\r
-        * @param reqs path requirements.\r
-        * @return true if path matches requirements\r
-        */\r
-       public static boolean pathFollowsRequirements(Path2D path, List<PathRequirement> reqs)\r
-       {\r
-               if (reqs.size()==0) return true;\r
-               // To validate path we need to make sure that \r
-               // 1) path goes thru control points             \r
-               // 2) The tangent of the path at control points is in direction set\r
-               int                             reqIndex        = 0;\r
-               PathRequirement                 req                     = reqs.get(reqIndex++); \r
-               PathIterator            pi                      = path.getPathIterator(null);\r
-               Iterator<double[]>      lsi                     = PathUtils.toLineIterator(pi);\r
-               if (!lsi.hasNext()) return false;\r
-               \r
-               // check begin\r
-               double[]                        seg                     = lsi.next();\r
-               Point2D                         pos                     = PathUtils.getLinePos(seg, 0);\r
-               Rectangle2D                     rect            = new Rectangle2D.Double(pos.getX()-TOLERANCE, pos.getY()-TOLERANCE, 2*TOLERANCE, 2*TOLERANCE);\r
-               \r
-               // Check pos\r
-               if (!rect.contains(pos)) return false;\r
-               // Check tangent\r
-               Point2D                         tang            = PathUtils.getLineTangent(seg, 0);\r
-               double                          dir                     = GeometryUtils.getCompassDirection(tang);\r
-               Double                          closestDir      = req.set.getClosestDirection(dir, 0.1);\r
-               if (closestDir == null) return false; \r
-               \r
-               while (lsi.hasNext()) {\r
-                       seg                             = lsi.next();\r
-                       pos                             = PathUtils.getLinePos(seg, 1);\r
-                       rect                    = new Rectangle2D.Double(pos.getX()-TOLERANCE, pos.getY()-TOLERANCE, 2*TOLERANCE, 2*TOLERANCE);\r
-                       \r
-                       if (rect.contains(pos)) {\r
-                               // check tangent\r
-                               tang                    = PathUtils.getLineTangent(seg, 1);\r
-                               dir                             = GeometryUtils.getCompassDirection(tang);\r
-                               closestDir              = req.set.getClosestDirection(dir, 0.1);\r
-                               if (closestDir != null) \r
-                               {\r
-                                       // next requirement\r
-                                       req = reqs.get(reqIndex++);\r
-                               }\r
-                       }                       \r
-               }\r
-               return reqIndex==reqs.size();\r
-       }\r
-\r
-       static class LinearEdgeSolutionNode implements ProblemNode {\r
-               List<PathRequirement> reqs;\r
-               LinearEdgeSolutionNode prevBranch;\r
-               int index;\r
-               double cost;\r
-               Point2D p0, cp, p1;\r
-               Point2D v0, v1, v1o; // v0=direction vector from p0, v1=direction vector to p1\r
-               // v1o = opposite of v1o\r
-               @Override\r
-               public void branch(Collection<ProblemNode> list) {\r
-                       PathRequirement r0 = reqs.get(index  );\r
-                       PathRequirement r1 = reqs.get(index+1);\r
-                       Point2D         p0 = r0.pos;\r
-                       Point2D         p1 = r1.pos;\r
-                       Point2D         cp1 = new Point2D.Double();\r
-                       Point2D         cp2 = new Point2D.Double();\r
-                       Set<Object> controlPoints = new HashSet<Object>();\r
-                       for (Point2D v0 : r0.set.getUnitVectors())\r
-                               for (Point2D v1 : r1.set.getUnitVectors())\r
-                               {\r
-                                       Point2D i       = PathUtils.findIntersection(p0, v0, p1, v1);\r
-                                       if (i!=null) {\r
-                                               controlPoints.add(i);\r
-                                               \r
-                                       } else {\r
-                                               // Impossible requirements\r
-                                               int flags = PathUtils.findNearestPoints(p0, v0, p1, v1, cp1, cp2); \r
-                                               if ((flags & 1) == 1) \r
-                                                       controlPoints.add(cp1);\r
-                                               if ((flags & 2) == 2)\r
-                                                       controlPoints.add(cp2);\r
-                                               \r
-                                       }\r
-                               }\r
-                       if (controlPoints.isEmpty()) {\r
-                               Point2D i = new Point2D.Double(\r
-                                               (p0.getX() + p1.getX())/2,\r
-                                               (p0.getY() + p1.getY())/2);\r
-                               controlPoints.add(i);\r
-                       }\r
-                       for (Object dada : controlPoints) {\r
-                               Point2D c0 = (Point2D) dada;\r
-                               double price = p0.distance(c0)+c0.distance(p1);\r
-                               price -= p0.distance(p1);\r
-                               \r
-                               LinearEdgeSolutionNode s = new LinearEdgeSolutionNode();                                        \r
-                               s.cost = cost + price;\r
-                               s.prevBranch = this;\r
-                               s.reqs = reqs;\r
-                               s.index = index+1;\r
-                               s.p0 = p0;\r
-                               s.p1 = p1;\r
-                               if (!c0.equals(p0) && !c0.equals(p1)) \r
-                                       s.cp = c0;\r
-                               \r
-                               Point2D np = s.cp == null ? p1 : c0;\r
-                               double dist = p0.distance(np);\r
-                               s.v0 = new Point2D.Double( (p0.getX() - np.getX())/dist, (p0.getY() - np.getY())/dist);\r
-\r
-                               np = s.cp == null ? p0 : c0;\r
-                               dist = p1.distance(np);\r
-                               s.v1 = new Point2D.Double( (p1.getX() - np.getX())/dist, (p1.getY() - np.getY())/dist);\r
-                               s.v1o = new Point2D.Double( (np.getX() - p1.getX())/dist, (np.getY() - p1.getY())/dist); \r
-\r
-                               // Penalty for changing direction \r
-                               if (v1!=null && !s.v0.equals(v1)) \r
-                                       s.cost += 1;\r
-                               // Penalty for going back\r
-                               if (v1o!=null && v1o.equals(s.v0)) \r
-                                       s.cost += 2;\r
-                               \r
-                               list.add(s);\r
-                       }\r
-               }\r
-               @Override\r
-               public double getCost() {\r
-                       return cost;\r
-               }\r
-               @Override\r
-               public boolean isComplete() {\r
-                       return index>=reqs.size()-1;\r
-               }\r
-               /**\r
-                * Create path from root to this node\r
-                * @return\r
-                */\r
-               public List<LinearEdgeSolutionNode> toList() {\r
-                       LinkedList<LinearEdgeSolutionNode> res = new LinkedList<LinearEdgeSolutionNode>();\r
-                       LinearEdgeSolutionNode branch = this;\r
-                       while (branch!=null) {\r
-                               res.addFirst(branch);\r
-                               branch = branch.prevBranch;\r
-                       }\r
-                       return res;\r
-               }\r
-               public Path2D toPath() {\r
-                       Path2D p = new Path2D.Double();                 \r
-                       for (LinearEdgeSolutionNode s : toList()) \r
-                       {\r
-                               if (s.p0==null) continue;\r
-                               Point2D cur = p.getCurrentPoint();\r
-                               if (cur==null) \r
-                                       p.moveTo(s.p0.getX(), s.p0.getY());\r
-                               \r
-                               cur = p.getCurrentPoint();\r
-                               if ((s.cp!=null)&&(cur==null || !cur.equals(s.cp))) \r
-                                       p.lineTo(s.cp.getX(), s.cp.getY());                             \r
-                               \r
-                               cur = p.getCurrentPoint();\r
-                               if (cur==null || !cur.equals(s.p1)) \r
-                                       p.lineTo(s.p1.getX(), s.p1.getY());                             \r
-                       }\r
-                       \r
-                       return p;\r
-               }\r
-       }\r
-       \r
-       /**\r
-        * Create a linear path that follows path requirements\r
-        * @param reqs requirements\r
-        * @return path\r
-        */\r
-       public static Path2D createPathLinear(final List<PathRequirement> reqs)\r
-       {\r
-               LinearEdgeSolutionNode s = new LinearEdgeSolutionNode();\r
-               s.reqs = reqs;\r
-               LinearEdgeSolutionNode solution = (LinearEdgeSolutionNode) TreeProblem.findSolution(s, 2);\r
-               if (solution==null) return null;                \r
-               return solution.toPath();               \r
-       }\r
-       \r
-       /**\r
-        * Create a line that follows path requirements\r
-        * @param reqs\r
-        * @return\r
-        */\r
-       public static Path2D createPathLine(final List<PathRequirement> reqs)\r
-       {\r
-               Path2D result = new Path2D.Double();\r
-               int index = 0;\r
-               for (PathRequirement r : reqs) {\r
-                       if (index++==0)\r
-                               result.moveTo(r.pos.getX(), r.pos.getY());\r
-                       else\r
-                               result.lineTo(r.pos.getX(), r.pos.getY());\r
-               }\r
-               return result;\r
-       }\r
-       \r
-\r
-       /**\r
-        * Create a right-angled (90 deg angles) path that follows requirements \r
-        * @param reqs requirements\r
-        * @return path\r
-        */\r
-       public static Path2D createPathRightAngled(List<PathRequirement> reqs)\r
-       {\r
-               Path2D res = new Path2D.Double();\r
-//             int i = 0;\r
-//             for (PathRequirement r : reqs)\r
-//             {\r
-//                             \r
-//             }                       \r
-               \r
-               return res;\r
-       }\r
-       \r
-       /**\r
-        * Create a path that follows requirements\r
-        * @param reqs requirements\r
-        * @return path\r
-        */\r
-       public static Path2D createPathQuadratic(List<PathRequirement> reqs)\r
-       {\r
-               Path2D res = new Path2D.Double();\r
-//             int i = 0;\r
-//             for (PathRequirement r : reqs)\r
-//             {\r
-//                     \r
-//             }                       \r
-               \r
-               return res;\r
-       }\r
-       \r
-       \r
-       /**\r
-        * Path Requirement is a point thru which the path must "travel".\r
-        */\r
-       static class PathRequirement {\r
-               Point2D pos;  // Position on diagram\r
-               DirectionSet set; // Allowed directions\r
-               boolean isBegin, isEnd;\r
-       }\r
-       \r
-       /**\r
-        * Takes a look at an edge and creates a path travel plan (requiremetn).\r
-        * Beginning of e must point to (possibly) connected terminal.\r
-        * End of e must point to (possibly) connected terminal.\r
-        * \r
-        * @param e\r
-        * @param pathRequirement Path requirements\r
-        */\r
-       public static void createPathRequirement(IElement e, List<PathRequirement> pathRequirement)\r
-       {\r
-               Topology t = ElementUtils.getDiagram(e).getDiagramClass().getSingleItem(Topology.class);\r
-               pathRequirement.clear();\r
-               if (t==null) return;\r
-               \r
-               // Add begin\r
-               Connection c1 = t.getConnection(e, EdgeEnd.Begin);\r
-               if (c1!=null) {\r
-                       PathRequirement req = new PathRequirement();\r
-                       AffineTransform at = TerminalUtil.getTerminalPosOnDiagram(c1.node, c1.terminal);                        \r
-                       req.pos = new Point2D.Double( at.getTranslateX(), at.getTranslateY() );\r
-                       req.set = ElementUtils.getTerminalDirection(c1.node, c1.terminal);\r
-                       req.isBegin = true;\r
-                       pathRequirement.add(req);\r
-               } \r
-               \r
-               // Add control points\r
-               AffineTransform elementTransform = ElementUtils.getTransform(e);\r
-               BendsHandler bends = e.getElementClass().getSingleItem(BendsHandler.class);             \r
-               ArrayList<Bend> controlPoints = new ArrayList<Bend>();\r
-               bends.getBends(e, controlPoints);\r
-               for (Bend cp : controlPoints) \r
-               {\r
-                       Point2D pos = new Point2D.Double();\r
-                       bends.getBendPosition(e, cp, pos);\r
-                       elementTransform.transform(pos, pos);\r
-                       PathRequirement req = new PathRequirement();\r
-                       req.set = DirectionSet.NESW;\r
-                       req.pos = pos;\r
-                       pathRequirement.add(req);\r
-               }\r
-       \r
-               // TODO lisää transformit\r
-               \r
-               \r
-               // Add end\r
-               Connection c2 = t.getConnection(e, EdgeEnd.End);\r
-               if (c2!=null) {\r
-                       PathRequirement req = new PathRequirement();\r
-                       AffineTransform at = TerminalUtil.getTerminalPosOnDiagram(c2.node, c2.terminal);                        \r
-                       req.pos = new Point2D.Double( at.getTranslateX(), at.getTranslateY() );\r
-                       req.set                                 = ElementUtils.getTerminalDirection(c2.node, c2.terminal);\r
-                       req.set                                 = req.set.createInverse();\r
-                       req.isEnd                               = true;\r
-                       pathRequirement.add(req);\r
-               }               \r
-               \r
-       }\r
-\r
-}\r
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 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.g2d.elementclass.connection;
+
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Path2D;
+import java.awt.geom.PathIterator;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+import org.simantics.g2d.canvas.ICanvasContext;
+import org.simantics.g2d.diagram.IDiagram;
+import org.simantics.g2d.diagram.handler.Topology;
+import org.simantics.g2d.diagram.handler.Validator;
+import org.simantics.g2d.diagram.handler.Topology.Connection;
+import org.simantics.g2d.diagram.handler.impl.DiagramIssue;
+import org.simantics.g2d.diagram.participant.pointertool.TerminalUtil;
+import org.simantics.g2d.element.ElementUtils;
+import org.simantics.g2d.element.IElement;
+import org.simantics.g2d.element.handler.BendsHandler;
+import org.simantics.g2d.element.handler.EdgeVisuals;
+import org.simantics.g2d.element.handler.BendsHandler.AngleType;
+import org.simantics.g2d.element.handler.BendsHandler.Bend;
+import org.simantics.g2d.element.handler.EdgeVisuals.EdgeEnd;
+import org.simantics.g2d.routing.algorithm1.Rectangle;
+import org.simantics.g2d.utils.GeometryUtils;
+import org.simantics.g2d.utils.PathUtils;
+import org.simantics.g2d.utils.geom.DirectionSet;
+import org.simantics.utils.datastructures.TreeProblem;
+import org.simantics.utils.datastructures.TreeProblem.ProblemNode;
+
+/**
+ * Checks whether Topology meets geometry  
+ * 
+ * @author Toni Kalajainen
+ */
+public class ConnectionValidator implements Validator {
+
+
+
+       public static final ConnectionValidator INSTANCE = new ConnectionValidator(); 
+       private static final double TOLERANCE = 0.001;
+       
+       private static final Suggestion DISCONNECT_EDGES = new Suggestion() {
+               @Override
+               public boolean fix(IDiagram d, ICanvasContext ctx) {                    
+                       return false;
+               }
+               @Override
+               public String getMessage() {
+                       return "Disconnect edges";
+               }
+       };
+       
+       public static final double OBSTACLE_MARGINAL = 10.0; 
+       
+       static Collection<Rectangle> createObstacles(IDiagram d) {
+               ArrayList<Rectangle> obstacles = new ArrayList<Rectangle>();
+               for(IElement e : d.getElements())
+               {
+                       if (e.getElementClass().containsClass(EdgeVisuals.class)) continue;
+                       Rectangle2D rect = ElementUtils.getElementBounds(e);
+                       obstacles.add(new Rectangle(
+                               rect.getMinX()-OBSTACLE_MARGINAL,
+                               rect.getMinY()-OBSTACLE_MARGINAL,
+                               rect.getMaxX()+OBSTACLE_MARGINAL,
+                               rect.getMaxY()+OBSTACLE_MARGINAL
+                       ));
+               }
+               return obstacles;
+       }
+       
+       // Force path to follow its path requirements 
+       private static final Suggestion FORCE_PATH_TO_REQUIREMENTS = new Suggestion() {
+               @Override
+               public boolean fix(IDiagram d, ICanvasContext ctx) {
+                       
+                       // Get all edges
+                       ArrayList<PathRequirement> reqs = new ArrayList<PathRequirement>();                     
+                       for (IElement e : d.getElements())
+                       {
+                               BendsHandler bends = e.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);
+                               if (bends==null) continue;
+                               Path2D path = bends.getPath(e);
+                               assert(path!=null);
+                               reqs.clear();
+                               createPathRequirement(e, reqs);                 
+                               if (reqs.isEmpty()) continue;
+                               if (pathFollowsRequirements(path, reqs)) continue;
+                               
+                               AngleType at = bends.getAngleType(e);
+                               if (at==AngleType.Linear) {
+                                       path = createPathLinear(reqs);
+                               } else if (at==AngleType.RightAngled) {
+                                       path = createPathRightAngled(reqs);                                     
+                               } else if (at==AngleType.Quadratic) {
+                                       path = createPathQuadratic(reqs);
+                               } else if (at==AngleType.Line) {
+                                       path = createPathLine(reqs);
+                               } else assert(false);
+                               
+                               // Make up something!
+                               if (path==null)
+                                       path = createPathLine(reqs);
+
+                               AffineTransform elementToDiagramAt = ElementUtils.getInvTransform(e);
+                               path.transform(elementToDiagramAt);
+                               
+                               bends.setPath(e, path);
+                       }                       
+                       return false;
+               }
+               @Override
+               public String getMessage() {
+                       return "Update path";
+               }
+       };
+       private static final Issue PATH_IS_BROKEN = 
+               new DiagramIssue("Path does not follow its requirements (connects and bends)", FORCE_PATH_TO_REQUIREMENTS, DISCONNECT_EDGES);
+       
+       @Override
+       public void validate(IDiagram d, ICanvasContext ctx, Collection<Issue> lst) {
+               if (!validateDiagramOk(d, ctx, lst))
+               {
+                       lst.add(PATH_IS_BROKEN);
+               }
+       }
+       
+       /**
+        * Return true if connections of a diagram are ok.
+        * @param d
+        * @param ctx
+        * @param lst
+        * @return
+        */
+       boolean validateDiagramOk(IDiagram d, ICanvasContext ctx, Collection<Issue> lst)
+       {
+               // Get all edges
+               ArrayList<PathRequirement> reqs = new ArrayList<PathRequirement>();
+               for (IElement e : d.getElements())
+               {
+                       BendsHandler bends = e.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);
+                       if (bends==null) continue;
+                       Path2D path = bends.getPath(e);
+
+                       reqs.clear();
+                       createPathRequirement(e, reqs);                 
+                       if (reqs.isEmpty()) continue;
+                       if (pathFollowsRequirements(path, reqs)) continue;
+                       return false;
+               }
+               return true;
+       }
+       
+       /**
+        * Forces the path to follow path requirements.
+        *  
+        * @param e
+        * @param reqs path requirements.
+        * @return true if path matches requirements
+        */
+       public static boolean pathFollowsRequirements(Path2D path, List<PathRequirement> reqs)
+       {
+               if (reqs.size()==0) return true;
+               // To validate path we need to make sure that 
+               // 1) path goes thru control points             
+               // 2) The tangent of the path at control points is in direction set
+               int                             reqIndex        = 0;
+               PathRequirement                 req                     = reqs.get(reqIndex++); 
+               PathIterator            pi                      = path.getPathIterator(null);
+               Iterator<double[]>      lsi                     = PathUtils.toLineIterator(pi);
+               if (!lsi.hasNext()) return false;
+               
+               // check begin
+               double[]                        seg                     = lsi.next();
+               Point2D                         pos                     = PathUtils.getLinePos(seg, 0);
+               Rectangle2D                     rect            = new Rectangle2D.Double(pos.getX()-TOLERANCE, pos.getY()-TOLERANCE, 2*TOLERANCE, 2*TOLERANCE);
+               
+               // Check pos
+               if (!rect.contains(pos)) return false;
+               // Check tangent
+               Point2D                         tang            = PathUtils.getLineTangent(seg, 0);
+               double                          dir                     = GeometryUtils.getCompassDirection(tang);
+               Double                          closestDir      = req.set.getClosestDirection(dir, 0.1);
+               if (closestDir == null) return false; 
+               
+               while (lsi.hasNext()) {
+                       seg                             = lsi.next();
+                       pos                             = PathUtils.getLinePos(seg, 1);
+                       rect                    = new Rectangle2D.Double(pos.getX()-TOLERANCE, pos.getY()-TOLERANCE, 2*TOLERANCE, 2*TOLERANCE);
+                       
+                       if (rect.contains(pos)) {
+                               // check tangent
+                               tang                    = PathUtils.getLineTangent(seg, 1);
+                               dir                             = GeometryUtils.getCompassDirection(tang);
+                               closestDir              = req.set.getClosestDirection(dir, 0.1);
+                               if (closestDir != null) 
+                               {
+                                       // next requirement
+                                       req = reqs.get(reqIndex++);
+                               }
+                       }                       
+               }
+               return reqIndex==reqs.size();
+       }
+
+       static class LinearEdgeSolutionNode implements ProblemNode {
+               List<PathRequirement> reqs;
+               LinearEdgeSolutionNode prevBranch;
+               int index;
+               double cost;
+               Point2D p0, cp, p1;
+               Point2D v0, v1, v1o; // v0=direction vector from p0, v1=direction vector to p1
+               // v1o = opposite of v1o
+               @Override
+               public void branch(Collection<ProblemNode> list) {
+                       PathRequirement r0 = reqs.get(index  );
+                       PathRequirement r1 = reqs.get(index+1);
+                       Point2D         p0 = r0.pos;
+                       Point2D         p1 = r1.pos;
+                       Point2D         cp1 = new Point2D.Double();
+                       Point2D         cp2 = new Point2D.Double();
+                       Set<Object> controlPoints = new HashSet<Object>();
+                       for (Point2D v0 : r0.set.getUnitVectors())
+                               for (Point2D v1 : r1.set.getUnitVectors())
+                               {
+                                       Point2D i       = PathUtils.findIntersection(p0, v0, p1, v1);
+                                       if (i!=null) {
+                                               controlPoints.add(i);
+                                               
+                                       } else {
+                                               // Impossible requirements
+                                               int flags = PathUtils.findNearestPoints(p0, v0, p1, v1, cp1, cp2); 
+                                               if ((flags & 1) == 1) 
+                                                       controlPoints.add(cp1);
+                                               if ((flags & 2) == 2)
+                                                       controlPoints.add(cp2);
+                                               
+                                       }
+                               }
+                       if (controlPoints.isEmpty()) {
+                               Point2D i = new Point2D.Double(
+                                               (p0.getX() + p1.getX())/2,
+                                               (p0.getY() + p1.getY())/2);
+                               controlPoints.add(i);
+                       }
+                       for (Object dada : controlPoints) {
+                               Point2D c0 = (Point2D) dada;
+                               double price = p0.distance(c0)+c0.distance(p1);
+                               price -= p0.distance(p1);
+                               
+                               LinearEdgeSolutionNode s = new LinearEdgeSolutionNode();                                        
+                               s.cost = cost + price;
+                               s.prevBranch = this;
+                               s.reqs = reqs;
+                               s.index = index+1;
+                               s.p0 = p0;
+                               s.p1 = p1;
+                               if (!c0.equals(p0) && !c0.equals(p1)) 
+                                       s.cp = c0;
+                               
+                               Point2D np = s.cp == null ? p1 : c0;
+                               double dist = p0.distance(np);
+                               s.v0 = new Point2D.Double( (p0.getX() - np.getX())/dist, (p0.getY() - np.getY())/dist);
+
+                               np = s.cp == null ? p0 : c0;
+                               dist = p1.distance(np);
+                               s.v1 = new Point2D.Double( (p1.getX() - np.getX())/dist, (p1.getY() - np.getY())/dist);
+                               s.v1o = new Point2D.Double( (np.getX() - p1.getX())/dist, (np.getY() - p1.getY())/dist); 
+
+                               // Penalty for changing direction 
+                               if (v1!=null && !s.v0.equals(v1)) 
+                                       s.cost += 1;
+                               // Penalty for going back
+                               if (v1o!=null && v1o.equals(s.v0)) 
+                                       s.cost += 2;
+                               
+                               list.add(s);
+                       }
+               }
+               @Override
+               public double getCost() {
+                       return cost;
+               }
+               @Override
+               public boolean isComplete() {
+                       return index>=reqs.size()-1;
+               }
+               /**
+                * Create path from root to this node
+                * @return
+                */
+               public List<LinearEdgeSolutionNode> toList() {
+                       LinkedList<LinearEdgeSolutionNode> res = new LinkedList<LinearEdgeSolutionNode>();
+                       LinearEdgeSolutionNode branch = this;
+                       while (branch!=null) {
+                               res.addFirst(branch);
+                               branch = branch.prevBranch;
+                       }
+                       return res;
+               }
+               public Path2D toPath() {
+                       Path2D p = new Path2D.Double();                 
+                       for (LinearEdgeSolutionNode s : toList()) 
+                       {
+                               if (s.p0==null) continue;
+                               Point2D cur = p.getCurrentPoint();
+                               if (cur==null) 
+                                       p.moveTo(s.p0.getX(), s.p0.getY());
+                               
+                               cur = p.getCurrentPoint();
+                               if ((s.cp!=null)&&(cur==null || !cur.equals(s.cp))) 
+                                       p.lineTo(s.cp.getX(), s.cp.getY());                             
+                               
+                               cur = p.getCurrentPoint();
+                               if (cur==null || !cur.equals(s.p1)) 
+                                       p.lineTo(s.p1.getX(), s.p1.getY());                             
+                       }
+                       
+                       return p;
+               }
+       }
+       
+       /**
+        * Create a linear path that follows path requirements
+        * @param reqs requirements
+        * @return path
+        */
+       public static Path2D createPathLinear(final List<PathRequirement> reqs)
+       {
+               LinearEdgeSolutionNode s = new LinearEdgeSolutionNode();
+               s.reqs = reqs;
+               LinearEdgeSolutionNode solution = (LinearEdgeSolutionNode) TreeProblem.findSolution(s, 2);
+               if (solution==null) return null;                
+               return solution.toPath();               
+       }
+       
+       /**
+        * Create a line that follows path requirements
+        * @param reqs
+        * @return
+        */
+       public static Path2D createPathLine(final List<PathRequirement> reqs)
+       {
+               Path2D result = new Path2D.Double();
+               int index = 0;
+               for (PathRequirement r : reqs) {
+                       if (index++==0)
+                               result.moveTo(r.pos.getX(), r.pos.getY());
+                       else
+                               result.lineTo(r.pos.getX(), r.pos.getY());
+               }
+               return result;
+       }
+       
+
+       /**
+        * Create a right-angled (90 deg angles) path that follows requirements 
+        * @param reqs requirements
+        * @return path
+        */
+       public static Path2D createPathRightAngled(List<PathRequirement> reqs)
+       {
+               Path2D res = new Path2D.Double();
+//             int i = 0;
+//             for (PathRequirement r : reqs)
+//             {
+//                             
+//             }                       
+               
+               return res;
+       }
+       
+       /**
+        * Create a path that follows requirements
+        * @param reqs requirements
+        * @return path
+        */
+       public static Path2D createPathQuadratic(List<PathRequirement> reqs)
+       {
+               Path2D res = new Path2D.Double();
+//             int i = 0;
+//             for (PathRequirement r : reqs)
+//             {
+//                     
+//             }                       
+               
+               return res;
+       }
+       
+       
+       /**
+        * Path Requirement is a point thru which the path must "travel".
+        */
+       static class PathRequirement {
+               Point2D pos;  // Position on diagram
+               DirectionSet set; // Allowed directions
+               boolean isBegin, isEnd;
+       }
+       
+       /**
+        * Takes a look at an edge and creates a path travel plan (requiremetn).
+        * Beginning of e must point to (possibly) connected terminal.
+        * End of e must point to (possibly) connected terminal.
+        * 
+        * @param e
+        * @param pathRequirement Path requirements
+        */
+       public static void createPathRequirement(IElement e, List<PathRequirement> pathRequirement)
+       {
+               Topology t = ElementUtils.getDiagram(e).getDiagramClass().getSingleItem(Topology.class);
+               pathRequirement.clear();
+               if (t==null) return;
+               
+               // Add begin
+               Connection c1 = t.getConnection(e, EdgeEnd.Begin);
+               if (c1!=null) {
+                       PathRequirement req = new PathRequirement();
+                       AffineTransform at = TerminalUtil.getTerminalPosOnDiagram(c1.node, c1.terminal);                        
+                       req.pos = new Point2D.Double( at.getTranslateX(), at.getTranslateY() );
+                       req.set = ElementUtils.getTerminalDirection(c1.node, c1.terminal);
+                       req.isBegin = true;
+                       pathRequirement.add(req);
+               } 
+               
+               // Add control points
+               AffineTransform elementTransform = ElementUtils.getTransform(e);
+               BendsHandler bends = e.getElementClass().getSingleItem(BendsHandler.class);             
+               ArrayList<Bend> controlPoints = new ArrayList<Bend>();
+               bends.getBends(e, controlPoints);
+               for (Bend cp : controlPoints) 
+               {
+                       Point2D pos = new Point2D.Double();
+                       bends.getBendPosition(e, cp, pos);
+                       elementTransform.transform(pos, pos);
+                       PathRequirement req = new PathRequirement();
+                       req.set = DirectionSet.NESW;
+                       req.pos = pos;
+                       pathRequirement.add(req);
+               }
+       
+               // TODO lisää transformit
+               
+               
+               // Add end
+               Connection c2 = t.getConnection(e, EdgeEnd.End);
+               if (c2!=null) {
+                       PathRequirement req = new PathRequirement();
+                       AffineTransform at = TerminalUtil.getTerminalPosOnDiagram(c2.node, c2.terminal);                        
+                       req.pos = new Point2D.Double( at.getTranslateX(), at.getTranslateY() );
+                       req.set                                 = ElementUtils.getTerminalDirection(c2.node, c2.terminal);
+                       req.set                                 = req.set.createInverse();
+                       req.isEnd                               = true;
+                       pathRequirement.add(req);
+               }               
+               
+       }
+
+}