]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/DiagramUtils.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.g2d / src / org / simantics / g2d / diagram / DiagramUtils.java
index e93cc676becaf72408020b462fff014db4fc1ee1..ed812c21a556aa7517a38bd5778e474decbb9bdf 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.diagram;\r
-\r
-import java.awt.geom.AffineTransform;\r
-import java.awt.geom.Path2D;\r
-import java.awt.geom.Point2D;\r
-import java.awt.geom.Rectangle2D;\r
-import java.util.ArrayList;\r
-import java.util.Arrays;\r
-import java.util.Collection;\r
-import java.util.Collections;\r
-import java.util.Iterator;\r
-import java.util.List;\r
-import java.util.Map;\r
-import java.util.Set;\r
-import java.util.function.Consumer;\r
-\r
-import org.simantics.g2d.canvas.Hints;\r
-import org.simantics.g2d.canvas.ICanvasContext;\r
-import org.simantics.g2d.connection.ConnectionEntity;\r
-import org.simantics.g2d.connection.EndKeyOf;\r
-import org.simantics.g2d.connection.TerminalKeyOf;\r
-import org.simantics.g2d.connection.handler.ConnectionHandler;\r
-import org.simantics.g2d.diagram.handler.PickContext;\r
-import org.simantics.g2d.diagram.handler.PickRequest;\r
-import org.simantics.g2d.diagram.handler.Topology;\r
-import org.simantics.g2d.diagram.handler.Topology.Connection;\r
-import org.simantics.g2d.diagram.handler.TransactionContext;\r
-import org.simantics.g2d.diagram.handler.TransactionContext.Transaction;\r
-import org.simantics.g2d.diagram.handler.TransactionContext.TransactionType;\r
-import org.simantics.g2d.diagram.impl.Diagram;\r
-import org.simantics.g2d.diagram.participant.pointertool.TerminalUtil;\r
-import org.simantics.g2d.element.ElementHints;\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.BendsHandler.Bend;\r
-import org.simantics.g2d.element.handler.Children;\r
-import org.simantics.g2d.element.handler.EdgeVisuals.EdgeEnd;\r
-import org.simantics.g2d.element.handler.InternalSize;\r
-import org.simantics.g2d.element.handler.Transform;\r
-import org.simantics.g2d.element.impl.Element;\r
-import org.simantics.g2d.elementclass.BranchPoint;\r
-import org.simantics.g2d.elementclass.BranchPoint.Direction;\r
-import org.simantics.g2d.routing.ConnectionDirectionUtil;\r
-import org.simantics.g2d.routing.Constants;\r
-import org.simantics.g2d.routing.IConnection;\r
-import org.simantics.g2d.routing.IRouter2;\r
-import org.simantics.g2d.routing.TrivialRouter2;\r
-import org.simantics.scenegraph.utils.GeometryUtils;\r
-\r
-import gnu.trove.map.hash.THashMap;\r
-\r
-/**\r
- * @author Toni Kalajainen\r
- * @author Antti Villberg\r
- * @author Tuukka Lehtonen\r
- */\r
-public class DiagramUtils {\r
-\r
-    /**\r
-     * Get rectangle that contains all elements or null if there are no elements.\r
-     * @param d\r
-     * @return rectangle or null\r
-     */\r
-    public static Rectangle2D getContentRect(IDiagram d)\r
-    {\r
-        return getContentRect(d.getElements());\r
-    }\r
-\r
-    /**\r
-     * Get rectangle that contains all elements or null if there are no elements.\r
-     * @param d\r
-     * @return rectangle or null\r
-     */\r
-    public static Rectangle2D getContentRect(Collection<IElement> elements)\r
-    {\r
-        Rectangle2D diagramRect = null;\r
-        Rectangle2D elementRect = new Rectangle2D.Double();\r
-        for (IElement el : elements) {\r
-            if (ElementUtils.isHidden(el))\r
-                continue;\r
-\r
-            InternalSize size = el.getElementClass().getSingleItem(InternalSize.class);\r
-            elementRect.setRect(Double.NaN, Double.NaN, Double.NaN, Double.NaN);\r
-            size.getBounds(el, elementRect);\r
-                       if (!Double.isFinite(elementRect.getWidth()) || !Double.isFinite(elementRect.getHeight())\r
-                                       || !Double.isFinite(elementRect.getX()) || !Double.isFinite(elementRect.getY()))\r
-                continue;\r
-\r
-            Transform t = el.getElementClass().getSingleItem(Transform.class);\r
-            AffineTransform at = t.getTransform(el);\r
-            Rectangle2D transformedRect = GeometryUtils.transformRectangle(at, elementRect);\r
-            if (diagramRect==null)\r
-                diagramRect = new Rectangle2D.Double( transformedRect.getX(), transformedRect.getY(), transformedRect.getWidth(), transformedRect.getHeight() );\r
-            else\r
-                diagramRect.add(transformedRect);\r
-        }\r
-        return diagramRect;\r
-    }\r
-\r
-    public static void pick(\r
-            IDiagram d,\r
-            PickRequest request,\r
-            Collection<IElement> result)\r
-    {\r
-        PickContext pc = d.getDiagramClass().getSingleItem(PickContext.class);\r
-        pc.pick(d, request, result);\r
-    }\r
-\r
-    public static void invalidate(IDiagram d) {\r
-        //Task task = ThreadLog.BEGIN("DiagramUtils.invalidate");\r
-        d.setHint(Hints.KEY_DIRTY, Hints.VALUE_SG_DIRTY);\r
-        //task.end();\r
-    }\r
-\r
-    private static final ThreadLocal<List<IElement>> elements = new ThreadLocal<List<IElement>>() {\r
-        @Override\r
-        protected java.util.List<IElement> initialValue() {\r
-            return new ArrayList<IElement>();\r
-        }\r
-    };\r
-\r
-    /**\r
-     * @param d\r
-     * @param context\r
-     */\r
-    public static void validateAndFix(final IDiagram d, ICanvasContext context) {\r
-        //Task task = ThreadLog.BEGIN("DU.validateAndFix");\r
-        validateAndFix(d, d.getElements());\r
-        //task.end();\r
-    }\r
-\r
-    /**\r
-     * @param d\r
-     * @param elementsToFix\r
-     */\r
-    public static void validateAndFix(final IDiagram d, Collection<IElement> elementsToFix) {\r
-        //Task task = ThreadLog.BEGIN("DU.validateAndFix(IDiagram, Set<IElement>)");\r
-\r
-        IRouter2 defaultRouter = ElementUtils.getHintOrDefault(d, DiagramHints.ROUTE_ALGORITHM, TrivialRouter2.INSTANCE);\r
-        final Topology topology = d.getDiagramClass().getSingleItem(Topology.class);\r
-\r
-        // Validate-and-fix is single-threaded.\r
-        List<IElement> segments = elements.get();\r
-        final Collection<IElement> unmodifiableSegments = Collections.unmodifiableList(segments);\r
-\r
-        for (final IElement element : elementsToFix) {\r
-            if (!d.containsElement(element)) {\r
-                System.err.println("Fixing element not contained by diagram " + d + ": " + element);\r
-                continue;\r
-            }\r
-\r
-            ConnectionHandler ch = element.getElementClass().getAtMostOneItemOfClass(ConnectionHandler.class);\r
-            if (ch == null)\r
-                continue;\r
-\r
-            segments.clear();\r
-            ch.getSegments(element, segments);\r
-            if (segments.isEmpty())\r
-                continue;\r
-\r
-            // Get connection-specific router or use diagram default.\r
-            IRouter2 router = ElementUtils.getHintOrDefault(element, DiagramHints.ROUTE_ALGORITHM, defaultRouter);\r
-\r
-            for (final IElement e : unmodifiableSegments) {\r
-                if (e.getElementClass().containsClass(BendsHandler.class)) {\r
-                    router.route(new IConnection() {\r
-\r
-                        THashMap<IElement, Connector> branchPoints = new THashMap<IElement, Connector>();\r
-\r
-                        @Override\r
-                        public Connector getBegin(Object seg) {\r
-                            IElement e = (IElement)seg;\r
-                            Connection begin = topology.getConnection(e, EdgeEnd.Begin);\r
-                            Connector connector = begin == null ? null : branchPoints.get(begin.node);\r
-                            if(connector != null)\r
-                                return connector;\r
-                            connector = new Connector();\r
-                            if(begin==null) {\r
-                                BendsHandler bends =\r
-                                    e.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);\r
-                                List<Bend> bs = new ArrayList<Bend>();\r
-                                bends.getBends(e, bs);\r
-                                Point2D p = new Point2D.Double();\r
-                                if(bs.size() > 0)\r
-                                    bends.getBendPosition(e, bs.get(0), p);\r
-                                else\r
-                                    p.setLocation(0.0, 0.0);\r
-                                AffineTransform elementTransform = ElementUtils.getTransform(e);\r
-                                elementTransform.transform(p, p);\r
-                                connector.x = p.getX();\r
-                                connector.y = p.getY();\r
-                                connector.allowedDirections = 0xf;\r
-                            }\r
-                            else {\r
-                                AffineTransform at =\r
-                                    TerminalUtil.getTerminalPosOnDiagram(begin.node, begin.terminal);\r
-                                connector.x = at.getTranslateX();\r
-                                connector.y = at.getTranslateY();\r
-                                connector.parentObstacle = getObstacleShape(begin.node);\r
-                                BranchPoint bph = begin.node.getElementClass().getAtMostOneItemOfClass(BranchPoint.class);\r
-                                if(bph != null) {\r
-                                    branchPoints.put(begin.node, connector);\r
-                                    connector.allowedDirections = toAllowedDirections( bph.getDirectionPreference(begin.node, Direction.Any) );\r
-                                }\r
-                                else\r
-                                    ConnectionDirectionUtil.determineAllowedDirections(connector);\r
-                            }\r
-                            return connector;\r
-                        }\r
-\r
-                        private int toAllowedDirections(BranchPoint.Direction direction) {\r
-                            switch (direction) {\r
-                                case Any:\r
-                                    return 0xf;\r
-                                case Horizontal:\r
-                                    return Constants.EAST_FLAG | Constants.WEST_FLAG;\r
-                                case Vertical:\r
-                                    return Constants.NORTH_FLAG | Constants.SOUTH_FLAG;\r
-                                default:\r
-                                    throw new IllegalArgumentException("unrecognized direction: " + direction);\r
-                            }\r
-                        }\r
-\r
-                        @Override\r
-                        public Connector getEnd(Object seg) {\r
-                            IElement e = (IElement)seg;\r
-                            Connection end = topology.getConnection(e, EdgeEnd.End);\r
-                            Connector connector = end == null ? null : branchPoints.get(end.node);\r
-                            if(connector != null)\r
-                                return connector;\r
-                            connector = new Connector();\r
-                            if(end==null) {\r
-                                BendsHandler bends =\r
-                                    e.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);\r
-                                List<Bend> bs = new ArrayList<Bend>();\r
-                                bends.getBends(e, bs);\r
-                                Point2D p = new Point2D.Double();\r
-                                if(bs.size() > 0)\r
-                                    bends.getBendPosition(e, bs.get(bs.size()-1), p);\r
-                                else\r
-                                    p.setLocation(0.0, 0.0);\r
-                                AffineTransform elementTransform = ElementUtils.getTransform(e);\r
-                                elementTransform.transform(p, p);\r
-                                connector.x = p.getX();\r
-                                connector.y = p.getY();\r
-                                connector.allowedDirections = 0xf;\r
-                            }\r
-                            else {\r
-\r
-                                AffineTransform at =\r
-                                    TerminalUtil.getTerminalPosOnDiagram(end.node, end.terminal);\r
-                                connector.x = at.getTranslateX();\r
-                                connector.y = at.getTranslateY();\r
-                                connector.parentObstacle = getObstacleShape(end.node);\r
-                                BranchPoint bph = end.node.getElementClass().getAtMostOneItemOfClass(BranchPoint.class);\r
-                                if(bph != null) {\r
-                                    branchPoints.put(end.node, connector);\r
-                                    connector.allowedDirections = toAllowedDirections( bph.getDirectionPreference(end.node, Direction.Any) );\r
-                                }\r
-                                else\r
-                                    ConnectionDirectionUtil.determineAllowedDirections(connector);\r
-                            }\r
-                            return connector;\r
-                        }\r
-\r
-                        @Override\r
-                        public Collection<? extends Object> getSegments() {\r
-                            return unmodifiableSegments;\r
-                        }\r
-\r
-                        @Override\r
-                        public void setPath(Object seg, Path2D path) {\r
-                            IElement e = (IElement)seg;\r
-                            BendsHandler bends =\r
-                                e.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);\r
-                            AffineTransform elementTransform = ElementUtils.getInvTransform(e);\r
-                            path = (Path2D)path.clone();\r
-                            path.transform(elementTransform);\r
-                            bends.setPath(e, path);\r
-                        }\r
-                    });\r
-                    //task2.end();\r
-                }\r
-            }\r
-        }\r
-\r
-        // Don't leave dangling references behind.\r
-        segments.clear();\r
-\r
-        //task.end();\r
-    }\r
-\r
-    /**\r
-     * Execute the specified {@link Runnable} with in a diagram transaction\r
-     * using the {@link TransactionContext} handler available in the\r
-     * {@link DiagramClass} of the specified {@link Diagram}.\r
-     * \r
-     * @param diagram the diagram to execute the transaction for\r
-     * @param type read or write (exclusive)\r
-     * @param r the runnable to execute\r
-     * \r
-     * @throws IllegalArgumentException if the specified diagram does not have a\r
-     *         {@link TransactionContext} handler\r
-     */\r
-    public static void inDiagramTransaction(IDiagram diagram, TransactionType type, Runnable r) {\r
-        TransactionContext ctx = diagram.getDiagramClass().getAtMostOneItemOfClass(TransactionContext.class);\r
-        if (ctx == null)\r
-            throw new IllegalArgumentException("Diagram does not have a TransactionContext handler: " + diagram\r
-                    + ". Cannot execute runnable " + r);\r
-\r
-        Transaction txn = ctx.startTransaction(diagram, type);\r
-        try {\r
-            r.run();\r
-        } finally {\r
-            ctx.finishTransaction(diagram, txn);\r
-        }\r
-    }\r
-\r
-    /**\r
-     * Execute the specified {@link Callback} within a diagram write transaction\r
-     * using the {@link TransactionContext} handler available in the\r
-     * {@link DiagramClass} of the specified {@link Diagram}. The diagram must\r
-     * contain a valid value for the {@link DiagramHints#KEY_MUTATOR} hint which\r
-     * is passed to the specified callback as an argument. This utility takes\r
-     * care of clearing the diagram mutator before callback invocation and\r
-     * clearing/committing its modifications after callback invocation depending\r
-     * on its success.\r
-     * \r
-     * @param diagram the diagram to execute the transaction for\r
-     * @param callback the runnable to execute\r
-     * \r
-     * @throws IllegalArgumentException if the specified diagram does not have a\r
-     *         {@link TransactionContext} handler or if the diagram does not\r
-     *         have a valid value for the {@link DiagramHints#KEY_MUTATOR} hint\r
-     */\r
-    public static void mutateDiagram(IDiagram diagram, Consumer<DiagramMutator> callback) {\r
-        DiagramMutator mutator = diagram.getHint(DiagramHints.KEY_MUTATOR);\r
-        if (mutator == null)\r
-            throw new IllegalArgumentException("Diagram does not have an associated DiagramMutator (see DiagramHints.KEY_MUTATOR).");\r
-\r
-        TransactionContext ctx = diagram.getDiagramClass().getAtMostOneItemOfClass(TransactionContext.class);\r
-        if (ctx == null)\r
-            throw new IllegalArgumentException("Diagram does not have a TransactionContext handler: " + diagram\r
-                    + ". Cannot execute callback " + callback);\r
-\r
-        Transaction txn = ctx.startTransaction(diagram, TransactionType.WRITE);\r
-        boolean committed = false;\r
-        try {\r
-            mutator.clear();\r
-            callback.accept(mutator);\r
-            mutator.commit();\r
-            committed = true;\r
-        } finally {\r
-            if (!committed)\r
-                mutator.clear();\r
-            ctx.finishTransaction(diagram, txn);\r
-        }\r
-    }\r
-\r
-    /**\r
-     * Invokes a diagram mutation that synchronizes the hints of all the\r
-     * specified elements into the back-end.\r
-     * \r
-     * @param diagram the diagram to mutate\r
-     * @param elements the elements to synchronize to the back-end\r
-     */\r
-    public static void synchronizeHintsToBackend(IDiagram diagram, final IElement... elements) {\r
-        synchronizeHintsToBackend(diagram, Arrays.asList(elements));\r
-    }\r
-\r
-    /**\r
-     * Invokes a diagram mutation that synchronizes the hints of all the\r
-     * specified elements into the back-end.\r
-     * \r
-     * @param diagram the diagram to mutate\r
-     * @param elements the elements to synchronize to the back-end\r
-     */\r
-    public static void synchronizeHintsToBackend(IDiagram diagram, final Collection<IElement> elements) {\r
-        mutateDiagram(diagram, m -> {\r
-            for (IElement e : elements)\r
-                m.synchronizeHintsToBackend(e);\r
-        });\r
-    }\r
-\r
-    /**\r
-     * @param elements\r
-     * @return\r
-     */\r
-    public static Collection<IElement> withChildren(Collection<IElement> elements) {\r
-        ArrayList<IElement> result = new ArrayList<IElement>(elements.size()*2);\r
-        result.addAll(elements);\r
-        for (int pos = 0; pos < result.size(); ++pos) {\r
-            IElement element = result.get(pos);\r
-            Children children = element.getElementClass().getAtMostOneItemOfClass(Children.class);\r
-            if (children != null) {\r
-                children.getChildren(element, result);\r
-            }\r
-        }\r
-        return result;\r
-    }\r
-\r
-    /**\r
-     * @param elements\r
-     * @return\r
-     */\r
-    public static Collection<IElement> withDirectChildren(Collection<IElement> elements) {\r
-        ArrayList<IElement> result = new ArrayList<IElement>(elements);\r
-        return getDirectChildren(elements, result);\r
-    }\r
-\r
-    /**\r
-     * @param elements\r
-     * @param\r
-     * @return\r
-     */\r
-    public static Collection<IElement> getDirectChildren(Collection<IElement> elements, Collection<IElement> result) {\r
-        for (IElement element : elements) {\r
-            Children children = element.getElementClass().getAtMostOneItemOfClass(Children.class);\r
-            if (children != null)\r
-                children.getChildren(element, result);\r
-        }\r
-        return result;\r
-    }\r
-\r
-    /**\r
-     * @param diagram\r
-     * @param e\r
-     */\r
-    public static void testInclusion(IDiagram diagram, IElement e) {\r
-        BendsHandler bh = e.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);\r
-        BranchPoint bp = e.getElementClass().getAtMostOneItemOfClass(BranchPoint.class);\r
-\r
-        assertAndPrint(e,e instanceof Element);\r
-\r
-        if(bh == null && bp == null) {\r
-            assertAndPrint(e,diagram == e.peekDiagram());\r
-        } else {\r
-            assertAndPrint(e,e.peekDiagram() == null);\r
-            ConnectionEntity ce = e.getHint(ElementHints.KEY_CONNECTION_ENTITY);\r
-            assertAndPrint(e,ce != null);\r
-            assertAndPrint(e,diagram == ce.getConnection().getDiagram());\r
-        }\r
-    }\r
-\r
-    /**\r
-     * @param diagram\r
-     */\r
-    public static void testDiagram(IDiagram diagram) {\r
-        if (!(diagram instanceof Diagram))\r
-            return;\r
-\r
-        Collection<IElement> es = withChildren(diagram.getSnapshot());\r
-\r
-        for (IElement e : es) {\r
-            System.out.println("test element " + e + " " + e.getElementClass());\r
-\r
-            testInclusion(diagram, e);\r
-\r
-            Set<Map.Entry<TerminalKeyOf, Object>> entrySet = e.getHintsOfClass(TerminalKeyOf.class).entrySet();\r
-\r
-            for (Map.Entry<TerminalKeyOf, Object> entry : entrySet) {\r
-                Connection c = (Connection) entry.getValue();\r
-                testInclusion(diagram, c.node);\r
-                testInclusion(diagram, c.edge);\r
-            }\r
-\r
-            BendsHandler bh = e.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);\r
-\r
-            if (bh != null) {\r
-                Collection<Object> values = e.getHintsOfClass(EndKeyOf.class).values();\r
-                assertAndPrint(e, values.size() == 2);\r
-                Iterator<Object> it = values.iterator();\r
-                Connection e1 = (Connection)it.next();\r
-                Connection e2 = (Connection)it.next();\r
-                testInclusion(diagram, e1.node);\r
-                testInclusion(diagram, e1.edge);\r
-                testInclusion(diagram, e2.node);\r
-                testInclusion(diagram, e2.edge);\r
-                assertAndPrint(e, e1.end.equals(e2.end.other()));\r
-            }\r
-        }\r
-    }\r
-\r
-    public static void pruneDiagram(IDiagram diagram) {\r
-        if (!(diagram instanceof Diagram))\r
-            return;\r
-\r
-        Collection<IElement> es = withChildren(diagram.getSnapshot());\r
-\r
-        for (IElement e : es) {\r
-            BendsHandler bh = e.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);\r
-\r
-            if (bh != null) {\r
-                Set<Map.Entry<EndKeyOf, Object>> values = e.getHintsOfClass(EndKeyOf.class).entrySet();\r
-                if (values.size() == 2) {\r
-                    Iterator<Map.Entry<EndKeyOf, Object>> it = values.iterator();\r
-                    Map.Entry<EndKeyOf, Object> e1 = it.next();\r
-                    Map.Entry<EndKeyOf, Object> e2 = it.next();\r
-                    if (!(((Connection) e1.getValue()).node instanceof Element)) {\r
-                        e.removeHint(e1.getKey());\r
-                        System.out.println("###################### PRUNED: " /*+ ((Connection)e1.getValue()).node*/);\r
-                    }\r
-                    if (!(((Connection) e2.getValue()).node instanceof Element)) {\r
-                        e.removeHint(e2.getKey());\r
-                        System.out.println("###################### PRUNED: " /*+ ((Connection)e2.getValue()).node*/);\r
-                    }\r
-                }\r
-            }\r
-        }\r
-    }\r
-\r
-    private static void assertAndPrint(IElement element, boolean condition) {\r
-        if(!condition) {\r
-            System.out.println("ASSERTION FAILED FOR");\r
-            System.out.println("-" + element);\r
-            System.out.println("-" + element.getElementClass());\r
-            assert(condition);\r
-        }\r
-    }\r
-\r
-    public static Rectangle2D getObstacleShape(IElement e) {\r
-        Rectangle2D rect = ElementUtils.getElementBounds(e);\r
-        AffineTransform at = ElementUtils.getTransform(e);\r
-\r
-        Point2D p1 = new Point2D.Double();\r
-        Point2D p2 = new Point2D.Double();\r
-\r
-        p1.setLocation(rect.getMinX(), rect.getMinY());\r
-        at.transform(p1, p1);\r
-\r
-        p2.setLocation(rect.getMaxX(), rect.getMaxY());\r
-        at.transform(p2, p2);\r
-\r
-        double x0 = p1.getX();\r
-        double y0 = p1.getY();\r
-        double x1 = p2.getX();\r
-        double y1 = p2.getY();\r
-        if(x0 > x1) {\r
-            double temp = x0;\r
-            x0 = x1;\r
-            x1 = temp;\r
-        }\r
-        if(y0 > y1) {\r
-            double temp = y0;\r
-            y0 = y1;\r
-            y1 = temp;\r
-        }\r
-\r
-        double OBSTACLE_MARGINAL = 1.0;\r
-        return new Rectangle2D.Double(\r
-                x0-OBSTACLE_MARGINAL,\r
-                y0-OBSTACLE_MARGINAL,\r
-                (x1-x0)+OBSTACLE_MARGINAL*2,\r
-                (y1-y0)+OBSTACLE_MARGINAL*2\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.diagram;
+
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Path2D;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Consumer;
+
+import org.simantics.g2d.canvas.Hints;
+import org.simantics.g2d.canvas.ICanvasContext;
+import org.simantics.g2d.connection.ConnectionEntity;
+import org.simantics.g2d.connection.EndKeyOf;
+import org.simantics.g2d.connection.TerminalKeyOf;
+import org.simantics.g2d.connection.handler.ConnectionHandler;
+import org.simantics.g2d.diagram.handler.PickContext;
+import org.simantics.g2d.diagram.handler.PickRequest;
+import org.simantics.g2d.diagram.handler.Topology;
+import org.simantics.g2d.diagram.handler.Topology.Connection;
+import org.simantics.g2d.diagram.handler.TransactionContext;
+import org.simantics.g2d.diagram.handler.TransactionContext.Transaction;
+import org.simantics.g2d.diagram.handler.TransactionContext.TransactionType;
+import org.simantics.g2d.diagram.impl.Diagram;
+import org.simantics.g2d.diagram.participant.pointertool.TerminalUtil;
+import org.simantics.g2d.element.ElementHints;
+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.BendsHandler.Bend;
+import org.simantics.g2d.element.handler.Children;
+import org.simantics.g2d.element.handler.EdgeVisuals.EdgeEnd;
+import org.simantics.g2d.element.handler.InternalSize;
+import org.simantics.g2d.element.handler.Transform;
+import org.simantics.g2d.element.impl.Element;
+import org.simantics.g2d.elementclass.BranchPoint;
+import org.simantics.g2d.elementclass.BranchPoint.Direction;
+import org.simantics.g2d.routing.ConnectionDirectionUtil;
+import org.simantics.g2d.routing.Constants;
+import org.simantics.g2d.routing.IConnection;
+import org.simantics.g2d.routing.IRouter2;
+import org.simantics.g2d.routing.TrivialRouter2;
+import org.simantics.scenegraph.utils.GeometryUtils;
+
+import gnu.trove.map.hash.THashMap;
+
+/**
+ * @author Toni Kalajainen
+ * @author Antti Villberg
+ * @author Tuukka Lehtonen
+ */
+public class DiagramUtils {
+
+    /**
+     * Get rectangle that contains all elements or null if there are no elements.
+     * @param d
+     * @return rectangle or null
+     */
+    public static Rectangle2D getContentRect(IDiagram d)
+    {
+        return getContentRect(d.getElements());
+    }
+
+    /**
+     * Get rectangle that contains all elements or null if there are no elements.
+     * @param d
+     * @return rectangle or null
+     */
+    public static Rectangle2D getContentRect(Collection<IElement> elements)
+    {
+        Rectangle2D diagramRect = null;
+        Rectangle2D elementRect = new Rectangle2D.Double();
+        for (IElement el : elements) {
+            if (ElementUtils.isHidden(el))
+                continue;
+
+            InternalSize size = el.getElementClass().getSingleItem(InternalSize.class);
+            elementRect.setRect(Double.NaN, Double.NaN, Double.NaN, Double.NaN);
+            size.getBounds(el, elementRect);
+                       if (!Double.isFinite(elementRect.getWidth()) || !Double.isFinite(elementRect.getHeight())
+                                       || !Double.isFinite(elementRect.getX()) || !Double.isFinite(elementRect.getY()))
+                continue;
+
+            Transform t = el.getElementClass().getSingleItem(Transform.class);
+            AffineTransform at = t.getTransform(el);
+            Rectangle2D transformedRect = GeometryUtils.transformRectangle(at, elementRect);
+            if (diagramRect==null)
+                diagramRect = new Rectangle2D.Double( transformedRect.getX(), transformedRect.getY(), transformedRect.getWidth(), transformedRect.getHeight() );
+            else
+                diagramRect.add(transformedRect);
+        }
+        return diagramRect;
+    }
+
+    public static void pick(
+            IDiagram d,
+            PickRequest request,
+            Collection<IElement> result)
+    {
+        PickContext pc = d.getDiagramClass().getSingleItem(PickContext.class);
+        pc.pick(d, request, result);
+    }
+
+    public static void invalidate(IDiagram d) {
+        //Task task = ThreadLog.BEGIN("DiagramUtils.invalidate");
+        d.setHint(Hints.KEY_DIRTY, Hints.VALUE_SG_DIRTY);
+        //task.end();
+    }
+
+    private static final ThreadLocal<List<IElement>> elements = new ThreadLocal<List<IElement>>() {
+        @Override
+        protected java.util.List<IElement> initialValue() {
+            return new ArrayList<IElement>();
+        }
+    };
+
+    /**
+     * @param d
+     * @param context
+     */
+    public static void validateAndFix(final IDiagram d, ICanvasContext context) {
+        //Task task = ThreadLog.BEGIN("DU.validateAndFix");
+        validateAndFix(d, d.getElements());
+        //task.end();
+    }
+
+    /**
+     * @param d
+     * @param elementsToFix
+     */
+    public static void validateAndFix(final IDiagram d, Collection<IElement> elementsToFix) {
+        //Task task = ThreadLog.BEGIN("DU.validateAndFix(IDiagram, Set<IElement>)");
+
+        IRouter2 defaultRouter = ElementUtils.getHintOrDefault(d, DiagramHints.ROUTE_ALGORITHM, TrivialRouter2.INSTANCE);
+        final Topology topology = d.getDiagramClass().getSingleItem(Topology.class);
+
+        // Validate-and-fix is single-threaded.
+        List<IElement> segments = elements.get();
+        final Collection<IElement> unmodifiableSegments = Collections.unmodifiableList(segments);
+
+        for (final IElement element : elementsToFix) {
+            if (!d.containsElement(element)) {
+                System.err.println("Fixing element not contained by diagram " + d + ": " + element);
+                continue;
+            }
+
+            ConnectionHandler ch = element.getElementClass().getAtMostOneItemOfClass(ConnectionHandler.class);
+            if (ch == null)
+                continue;
+
+            segments.clear();
+            ch.getSegments(element, segments);
+            if (segments.isEmpty())
+                continue;
+
+            // Get connection-specific router or use diagram default.
+            IRouter2 router = ElementUtils.getHintOrDefault(element, DiagramHints.ROUTE_ALGORITHM, defaultRouter);
+
+            for (final IElement e : unmodifiableSegments) {
+                if (e.getElementClass().containsClass(BendsHandler.class)) {
+                    router.route(new IConnection() {
+
+                        THashMap<IElement, Connector> branchPoints = new THashMap<IElement, Connector>();
+
+                        @Override
+                        public Connector getBegin(Object seg) {
+                            IElement e = (IElement)seg;
+                            Connection begin = topology.getConnection(e, EdgeEnd.Begin);
+                            Connector connector = begin == null ? null : branchPoints.get(begin.node);
+                            if(connector != null)
+                                return connector;
+                            connector = new Connector();
+                            if(begin==null) {
+                                BendsHandler bends =
+                                    e.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);
+                                List<Bend> bs = new ArrayList<Bend>();
+                                bends.getBends(e, bs);
+                                Point2D p = new Point2D.Double();
+                                if(bs.size() > 0)
+                                    bends.getBendPosition(e, bs.get(0), p);
+                                else
+                                    p.setLocation(0.0, 0.0);
+                                AffineTransform elementTransform = ElementUtils.getTransform(e);
+                                elementTransform.transform(p, p);
+                                connector.x = p.getX();
+                                connector.y = p.getY();
+                                connector.allowedDirections = 0xf;
+                            }
+                            else {
+                                AffineTransform at =
+                                    TerminalUtil.getTerminalPosOnDiagram(begin.node, begin.terminal);
+                                connector.x = at.getTranslateX();
+                                connector.y = at.getTranslateY();
+                                connector.parentObstacle = getObstacleShape(begin.node);
+                                BranchPoint bph = begin.node.getElementClass().getAtMostOneItemOfClass(BranchPoint.class);
+                                if(bph != null) {
+                                    branchPoints.put(begin.node, connector);
+                                    connector.allowedDirections = toAllowedDirections( bph.getDirectionPreference(begin.node, Direction.Any) );
+                                }
+                                else
+                                    ConnectionDirectionUtil.determineAllowedDirections(connector);
+                            }
+                            return connector;
+                        }
+
+                        private int toAllowedDirections(BranchPoint.Direction direction) {
+                            switch (direction) {
+                                case Any:
+                                    return 0xf;
+                                case Horizontal:
+                                    return Constants.EAST_FLAG | Constants.WEST_FLAG;
+                                case Vertical:
+                                    return Constants.NORTH_FLAG | Constants.SOUTH_FLAG;
+                                default:
+                                    throw new IllegalArgumentException("unrecognized direction: " + direction);
+                            }
+                        }
+
+                        @Override
+                        public Connector getEnd(Object seg) {
+                            IElement e = (IElement)seg;
+                            Connection end = topology.getConnection(e, EdgeEnd.End);
+                            Connector connector = end == null ? null : branchPoints.get(end.node);
+                            if(connector != null)
+                                return connector;
+                            connector = new Connector();
+                            if(end==null) {
+                                BendsHandler bends =
+                                    e.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);
+                                List<Bend> bs = new ArrayList<Bend>();
+                                bends.getBends(e, bs);
+                                Point2D p = new Point2D.Double();
+                                if(bs.size() > 0)
+                                    bends.getBendPosition(e, bs.get(bs.size()-1), p);
+                                else
+                                    p.setLocation(0.0, 0.0);
+                                AffineTransform elementTransform = ElementUtils.getTransform(e);
+                                elementTransform.transform(p, p);
+                                connector.x = p.getX();
+                                connector.y = p.getY();
+                                connector.allowedDirections = 0xf;
+                            }
+                            else {
+
+                                AffineTransform at =
+                                    TerminalUtil.getTerminalPosOnDiagram(end.node, end.terminal);
+                                connector.x = at.getTranslateX();
+                                connector.y = at.getTranslateY();
+                                connector.parentObstacle = getObstacleShape(end.node);
+                                BranchPoint bph = end.node.getElementClass().getAtMostOneItemOfClass(BranchPoint.class);
+                                if(bph != null) {
+                                    branchPoints.put(end.node, connector);
+                                    connector.allowedDirections = toAllowedDirections( bph.getDirectionPreference(end.node, Direction.Any) );
+                                }
+                                else
+                                    ConnectionDirectionUtil.determineAllowedDirections(connector);
+                            }
+                            return connector;
+                        }
+
+                        @Override
+                        public Collection<? extends Object> getSegments() {
+                            return unmodifiableSegments;
+                        }
+
+                        @Override
+                        public void setPath(Object seg, Path2D path) {
+                            IElement e = (IElement)seg;
+                            BendsHandler bends =
+                                e.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);
+                            AffineTransform elementTransform = ElementUtils.getInvTransform(e);
+                            path = (Path2D)path.clone();
+                            path.transform(elementTransform);
+                            bends.setPath(e, path);
+                        }
+                    });
+                    //task2.end();
+                }
+            }
+        }
+
+        // Don't leave dangling references behind.
+        segments.clear();
+
+        //task.end();
+    }
+
+    /**
+     * Execute the specified {@link Runnable} with in a diagram transaction
+     * using the {@link TransactionContext} handler available in the
+     * {@link DiagramClass} of the specified {@link Diagram}.
+     * 
+     * @param diagram the diagram to execute the transaction for
+     * @param type read or write (exclusive)
+     * @param r the runnable to execute
+     * 
+     * @throws IllegalArgumentException if the specified diagram does not have a
+     *         {@link TransactionContext} handler
+     */
+    public static void inDiagramTransaction(IDiagram diagram, TransactionType type, Runnable r) {
+        TransactionContext ctx = diagram.getDiagramClass().getAtMostOneItemOfClass(TransactionContext.class);
+        if (ctx == null)
+            throw new IllegalArgumentException("Diagram does not have a TransactionContext handler: " + diagram
+                    + ". Cannot execute runnable " + r);
+
+        Transaction txn = ctx.startTransaction(diagram, type);
+        try {
+            r.run();
+        } finally {
+            ctx.finishTransaction(diagram, txn);
+        }
+    }
+
+    /**
+     * Execute the specified {@link Callback} within a diagram write transaction
+     * using the {@link TransactionContext} handler available in the
+     * {@link DiagramClass} of the specified {@link Diagram}. The diagram must
+     * contain a valid value for the {@link DiagramHints#KEY_MUTATOR} hint which
+     * is passed to the specified callback as an argument. This utility takes
+     * care of clearing the diagram mutator before callback invocation and
+     * clearing/committing its modifications after callback invocation depending
+     * on its success.
+     * 
+     * @param diagram the diagram to execute the transaction for
+     * @param callback the runnable to execute
+     * 
+     * @throws IllegalArgumentException if the specified diagram does not have a
+     *         {@link TransactionContext} handler or if the diagram does not
+     *         have a valid value for the {@link DiagramHints#KEY_MUTATOR} hint
+     */
+    public static void mutateDiagram(IDiagram diagram, Consumer<DiagramMutator> callback) {
+        DiagramMutator mutator = diagram.getHint(DiagramHints.KEY_MUTATOR);
+        if (mutator == null)
+            throw new IllegalArgumentException("Diagram does not have an associated DiagramMutator (see DiagramHints.KEY_MUTATOR).");
+
+        TransactionContext ctx = diagram.getDiagramClass().getAtMostOneItemOfClass(TransactionContext.class);
+        if (ctx == null)
+            throw new IllegalArgumentException("Diagram does not have a TransactionContext handler: " + diagram
+                    + ". Cannot execute callback " + callback);
+
+        Transaction txn = ctx.startTransaction(diagram, TransactionType.WRITE);
+        boolean committed = false;
+        try {
+            mutator.clear();
+            callback.accept(mutator);
+            mutator.commit();
+            committed = true;
+        } finally {
+            if (!committed)
+                mutator.clear();
+            ctx.finishTransaction(diagram, txn);
+        }
+    }
+
+    /**
+     * Invokes a diagram mutation that synchronizes the hints of all the
+     * specified elements into the back-end.
+     * 
+     * @param diagram the diagram to mutate
+     * @param elements the elements to synchronize to the back-end
+     */
+    public static void synchronizeHintsToBackend(IDiagram diagram, final IElement... elements) {
+        synchronizeHintsToBackend(diagram, Arrays.asList(elements));
+    }
+
+    /**
+     * Invokes a diagram mutation that synchronizes the hints of all the
+     * specified elements into the back-end.
+     * 
+     * @param diagram the diagram to mutate
+     * @param elements the elements to synchronize to the back-end
+     */
+    public static void synchronizeHintsToBackend(IDiagram diagram, final Collection<IElement> elements) {
+        mutateDiagram(diagram, m -> {
+            for (IElement e : elements)
+                m.synchronizeHintsToBackend(e);
+        });
+    }
+
+    /**
+     * @param elements
+     * @return
+     */
+    public static Collection<IElement> withChildren(Collection<IElement> elements) {
+        ArrayList<IElement> result = new ArrayList<IElement>(elements.size()*2);
+        result.addAll(elements);
+        for (int pos = 0; pos < result.size(); ++pos) {
+            IElement element = result.get(pos);
+            Children children = element.getElementClass().getAtMostOneItemOfClass(Children.class);
+            if (children != null) {
+                children.getChildren(element, result);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * @param elements
+     * @return
+     */
+    public static Collection<IElement> withDirectChildren(Collection<IElement> elements) {
+        ArrayList<IElement> result = new ArrayList<IElement>(elements);
+        return getDirectChildren(elements, result);
+    }
+
+    /**
+     * @param elements
+     * @param
+     * @return
+     */
+    public static Collection<IElement> getDirectChildren(Collection<IElement> elements, Collection<IElement> result) {
+        for (IElement element : elements) {
+            Children children = element.getElementClass().getAtMostOneItemOfClass(Children.class);
+            if (children != null)
+                children.getChildren(element, result);
+        }
+        return result;
+    }
+
+    /**
+     * @param diagram
+     * @param e
+     */
+    public static void testInclusion(IDiagram diagram, IElement e) {
+        BendsHandler bh = e.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);
+        BranchPoint bp = e.getElementClass().getAtMostOneItemOfClass(BranchPoint.class);
+
+        assertAndPrint(e,e instanceof Element);
+
+        if(bh == null && bp == null) {
+            assertAndPrint(e,diagram == e.peekDiagram());
+        } else {
+            assertAndPrint(e,e.peekDiagram() == null);
+            ConnectionEntity ce = e.getHint(ElementHints.KEY_CONNECTION_ENTITY);
+            assertAndPrint(e,ce != null);
+            assertAndPrint(e,diagram == ce.getConnection().getDiagram());
+        }
+    }
+
+    /**
+     * @param diagram
+     */
+    public static void testDiagram(IDiagram diagram) {
+        if (!(diagram instanceof Diagram))
+            return;
+
+        Collection<IElement> es = withChildren(diagram.getSnapshot());
+
+        for (IElement e : es) {
+            System.out.println("test element " + e + " " + e.getElementClass());
+
+            testInclusion(diagram, e);
+
+            Set<Map.Entry<TerminalKeyOf, Object>> entrySet = e.getHintsOfClass(TerminalKeyOf.class).entrySet();
+
+            for (Map.Entry<TerminalKeyOf, Object> entry : entrySet) {
+                Connection c = (Connection) entry.getValue();
+                testInclusion(diagram, c.node);
+                testInclusion(diagram, c.edge);
+            }
+
+            BendsHandler bh = e.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);
+
+            if (bh != null) {
+                Collection<Object> values = e.getHintsOfClass(EndKeyOf.class).values();
+                assertAndPrint(e, values.size() == 2);
+                Iterator<Object> it = values.iterator();
+                Connection e1 = (Connection)it.next();
+                Connection e2 = (Connection)it.next();
+                testInclusion(diagram, e1.node);
+                testInclusion(diagram, e1.edge);
+                testInclusion(diagram, e2.node);
+                testInclusion(diagram, e2.edge);
+                assertAndPrint(e, e1.end.equals(e2.end.other()));
+            }
+        }
+    }
+
+    public static void pruneDiagram(IDiagram diagram) {
+        if (!(diagram instanceof Diagram))
+            return;
+
+        Collection<IElement> es = withChildren(diagram.getSnapshot());
+
+        for (IElement e : es) {
+            BendsHandler bh = e.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);
+
+            if (bh != null) {
+                Set<Map.Entry<EndKeyOf, Object>> values = e.getHintsOfClass(EndKeyOf.class).entrySet();
+                if (values.size() == 2) {
+                    Iterator<Map.Entry<EndKeyOf, Object>> it = values.iterator();
+                    Map.Entry<EndKeyOf, Object> e1 = it.next();
+                    Map.Entry<EndKeyOf, Object> e2 = it.next();
+                    if (!(((Connection) e1.getValue()).node instanceof Element)) {
+                        e.removeHint(e1.getKey());
+                        System.out.println("###################### PRUNED: " /*+ ((Connection)e1.getValue()).node*/);
+                    }
+                    if (!(((Connection) e2.getValue()).node instanceof Element)) {
+                        e.removeHint(e2.getKey());
+                        System.out.println("###################### PRUNED: " /*+ ((Connection)e2.getValue()).node*/);
+                    }
+                }
+            }
+        }
+    }
+
+    private static void assertAndPrint(IElement element, boolean condition) {
+        if(!condition) {
+            System.out.println("ASSERTION FAILED FOR");
+            System.out.println("-" + element);
+            System.out.println("-" + element.getElementClass());
+            assert(condition);
+        }
+    }
+
+    public static Rectangle2D getObstacleShape(IElement e) {
+        Rectangle2D rect = ElementUtils.getElementBounds(e);
+        AffineTransform at = ElementUtils.getTransform(e);
+
+        Point2D p1 = new Point2D.Double();
+        Point2D p2 = new Point2D.Double();
+
+        p1.setLocation(rect.getMinX(), rect.getMinY());
+        at.transform(p1, p1);
+
+        p2.setLocation(rect.getMaxX(), rect.getMaxY());
+        at.transform(p2, p2);
+
+        double x0 = p1.getX();
+        double y0 = p1.getY();
+        double x1 = p2.getX();
+        double y1 = p2.getY();
+        if(x0 > x1) {
+            double temp = x0;
+            x0 = x1;
+            x1 = temp;
+        }
+        if(y0 > y1) {
+            double temp = y0;
+            y0 = y1;
+            y1 = temp;
+        }
+
+        double OBSTACLE_MARGINAL = 1.0;
+        return new Rectangle2D.Double(
+                x0-OBSTACLE_MARGINAL,
+                y0-OBSTACLE_MARGINAL,
+                (x1-x0)+OBSTACLE_MARGINAL*2,
+                (y1-y0)+OBSTACLE_MARGINAL*2
+        );
+    }
+
+}