]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.diagram/src/org/simantics/diagram/elements/ElementTransforms.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.diagram / src / org / simantics / diagram / elements / ElementTransforms.java
diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/elements/ElementTransforms.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/elements/ElementTransforms.java
new file mode 100644 (file)
index 0000000..a9cc2d4
--- /dev/null
@@ -0,0 +1,715 @@
+package org.simantics.diagram.elements;\r
+\r
+import java.awt.geom.AffineTransform;\r
+import java.awt.geom.Point2D;\r
+import java.awt.geom.Rectangle2D;\r
+import java.util.ArrayList;\r
+import java.util.Collection;\r
+import java.util.Collections;\r
+import java.util.Comparator;\r
+import java.util.List;\r
+\r
+import org.simantics.db.ReadGraph;\r
+import org.simantics.db.Resource;\r
+import org.simantics.db.UndoContext;\r
+import org.simantics.db.WriteGraph;\r
+import org.simantics.db.common.CommentMetadata;\r
+import org.simantics.db.common.request.IndexRoot;\r
+import org.simantics.db.common.request.WriteRequest;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.db.exception.ManyObjectsForFunctionalRelationException;\r
+import org.simantics.db.exception.NoSingleResultException;\r
+import org.simantics.db.exception.ServiceException;\r
+import org.simantics.db.request.Write;\r
+import org.simantics.diagram.query.DiagramRequests;\r
+import org.simantics.diagram.stubs.DiagramResource;\r
+import org.simantics.diagram.synchronization.graph.DiagramGraphUtil;\r
+import org.simantics.g2d.diagram.DiagramClass;\r
+import org.simantics.g2d.diagram.IDiagram;\r
+import org.simantics.g2d.diagram.impl.Diagram;\r
+import org.simantics.g2d.element.ElementClass;\r
+import org.simantics.g2d.element.ElementUtils;\r
+import org.simantics.g2d.element.IElement;\r
+import org.simantics.g2d.element.impl.Element;\r
+import org.simantics.g2d.utils.GeometryUtils;\r
+import org.simantics.scl.commands.Command;\r
+import org.simantics.scl.commands.Commands;\r
+import org.simantics.ui.SimanticsUI;\r
+\r
+/**\r
+ * Tools to align, rotate, and flip diagram elements.\r
+ * \r
+ * TODO : We need to add capability hints to elements to prevent rotating and mirroring elements that do not support that.\r
+ *        Example: mirrored text does not make any sense.\r
+ * \r
+ * \r
+ * @author Marko Luukkainen <marko.luukkainen@vtt.fi> (implementation)\r
+ * @author Tuukka Lehtonen (documentation)\r
+ */\r
+public final class ElementTransforms {\r
+\r
+    public static enum SIDE { LEFT, RIGHT, TOP, BOTTOM, VERT, HORIZ, VERT_BTW, HORIZ_BTW };\r
+\r
+    /**\r
+     * Align the specified set of diagram element resources in line with each\r
+     * other calculated by the specified side.\r
+     * \r
+     * <p>\r
+     * Alignment requires at least two elements to do anything.\r
+     * \r
+     * @param resources diagram element resources to rotate\r
+     * @param side the side of each element to use for distancing. Does not support between aligments.\r
+     */\r
+    public static void align(final Resource resources[], final SIDE side) {\r
+        if (resources.length < 2)\r
+            return;\r
+        if (side == SIDE.HORIZ_BTW || side == SIDE.VERT_BTW )\r
+            return;\r
+\r
+        SimanticsUI.getSession().asyncRequest(new WriteRequest() {\r
+\r
+            @Override\r
+            public void perform(WriteGraph graph) throws DatabaseException {\r
+                graph.markUndoPoint();\r
+                IDiagram hints = Diagram.spawnNew(DiagramClass.DEFAULT);\r
+\r
+                List<AlignElement> elements = new ArrayList<AlignElement>();\r
+                for (Resource r : resources) {\r
+                    AlignElement e = create(graph, hints, r);\r
+                    if (e != null)\r
+                        elements.add(e);\r
+                }\r
+                if (elements.size() < 2)\r
+                    return;\r
+                double mx = 0;\r
+                double my = 0;\r
+                for (AlignElement e : elements) {\r
+                    mx += e.transform[4];\r
+                    my += e.transform[5];\r
+                }\r
+                mx /= elements.size();\r
+                my /= elements.size();\r
+\r
+                // prevent moving symbols into the same position\r
+                int count = 0;\r
+                for (AlignElement e : elements) {\r
+                    if (side == SIDE.VERT || side == SIDE.LEFT || side == SIDE.RIGHT) {\r
+                        if (Math.abs(e.transform[5] - my) < 0.1) {\r
+                            count++;\r
+                        }\r
+                    } else {\r
+                        if (Math.abs(e.transform[4] - mx) < 0.1) {\r
+                            count++;\r
+                        }\r
+                    }\r
+                }\r
+                if (count > 1)\r
+                    return;\r
+\r
+                if (side == SIDE.HORIZ || side == SIDE.VERT) {\r
+\r
+\r
+                    for (AlignElement e : elements) {\r
+\r
+                        if (side == SIDE.VERT)\r
+                            DiagramGraphUtil.changeTransform(graph, e.element, new double[]{e.transform[0],e.transform[1],e.transform[2],e.transform[3],mx,e.transform[5]});\r
+                        else if (side == SIDE.HORIZ) {\r
+                            DiagramGraphUtil.changeTransform(graph, e.element, new double[]{e.transform[0],e.transform[1],e.transform[2],e.transform[3],e.transform[4],my});\r
+                        }\r
+                    }\r
+                } else {\r
+                    double lx, rx;\r
+                    double ty, by;\r
+                    lx = elements.get(0).transform[4] + elements.get(0).rotatedBounds.getMinX();\r
+                    rx = elements.get(0).transform[4] + elements.get(0).rotatedBounds.getMaxX();\r
+\r
+                    ty = elements.get(0).transform[5] + elements.get(0).rotatedBounds.getMinY();\r
+                    by = elements.get(0).transform[5] + elements.get(0).rotatedBounds.getMaxY();\r
+\r
+                    for (int i = 1; i < elements.size(); i++) {\r
+                        double tlx, trx;\r
+                        double tty, tby;\r
+                        tlx = elements.get(i).transform[4] + elements.get(i).rotatedBounds.getMinX();\r
+                        trx = elements.get(i).transform[4] + elements.get(i).rotatedBounds.getMaxX();\r
+\r
+                        tty = elements.get(i).transform[5] + elements.get(i).rotatedBounds.getMinY();\r
+                        tby = elements.get(i).transform[5] + elements.get(i).rotatedBounds.getMaxY();\r
+                        if (tlx < lx)\r
+                            lx = tlx;\r
+                        if (trx > rx)\r
+                            rx = trx;\r
+                        if (tty < ty)\r
+                            ty = tty;\r
+                        if (tby > by)\r
+                            by = tby;\r
+\r
+                    }\r
+\r
+                    for (AlignElement e : elements) {\r
+                        mx = e.transform[4];\r
+                        my = e.transform[5];\r
+                        if (side == SIDE.LEFT) {\r
+                            mx = lx - e.rotatedBounds.getMinX() ;\r
+                        } else if (side == SIDE.RIGHT) {\r
+                            mx = rx - e.rotatedBounds.getMaxX();\r
+                        } else if (side == SIDE.TOP) {\r
+                            my = ty - e.rotatedBounds.getMinY();\r
+                        } else {\r
+                            my = by - e.rotatedBounds.getMaxY();\r
+                        }\r
+                        DiagramGraphUtil.changeTransform(graph, e.element, new double[]{e.transform[0],e.transform[1],e.transform[2],e.transform[3],mx,my});\r
+\r
+                    }\r
+\r
+                }\r
+\r
+            }\r
+        });\r
+    }\r
+\r
+    /**\r
+     * Distance specified set of diagram element resources equally. Distancing\r
+     * is performed on the specified side of the each element.\r
+     * \r
+     * <p>\r
+     * Distancing requires at least three elements to work.\r
+     * \r
+     * @param resources diagram element resources to rotate\r
+     * @param side the side of each element to use for distancing\r
+     */\r
+    public static void dist(final Resource resources[], final SIDE side) {\r
+\r
+        if (resources.length < 3)\r
+            return;\r
+\r
+        SimanticsUI.getSession().asyncRequest(new WriteRequest() {\r
+\r
+            @Override\r
+            public void perform(WriteGraph graph) throws DatabaseException {\r
+                graph.markUndoPoint();\r
+                IDiagram hints = Diagram.spawnNew(DiagramClass.DEFAULT);\r
+\r
+                List<AlignElement> elements = new ArrayList<AlignElement>();\r
+                for (Resource r : resources) {\r
+                    //System.out.println(r + " " + GraphUtils.getReadableName(graph, r));\r
+                    AlignElement e = create(graph, hints, r);\r
+                    if (e != null)\r
+                        elements.add(e);\r
+                }\r
+                if (elements.size() < 3)\r
+                    return;\r
+                switch (side) {\r
+                    case LEFT: {\r
+                        Collections.sort(elements, new XComparator());\r
+                        AlignElement left = elements.get(0);\r
+                        AlignElement right = elements.get(elements.size() - 1);\r
+\r
+                        double leftEdge = left.transform[4] + left.rotatedBounds.getMinX();\r
+                        double rightEdge = right.transform[4] + right.rotatedBounds.getMinX();\r
+\r
+                        double totalDist = rightEdge - leftEdge;\r
+                        double dist = totalDist / (elements.size() - 1);\r
+                        double d = leftEdge;\r
+\r
+                        for (int i = 1; i < elements.size() -1; i++) {\r
+                            d += dist;\r
+                            AlignElement e = elements.get(i);\r
+\r
+                            double mx = d - e.rotatedBounds.getMinX();\r
+\r
+                            DiagramGraphUtil.changeTransform(graph, e.element, new double[]{e.transform[0],e.transform[1],e.transform[2],e.transform[3],mx,e.transform[5]});\r
+                        }\r
+\r
+                        break;\r
+                    }\r
+                    case VERT: {\r
+                        Collections.sort(elements, new XComparator());\r
+                        AlignElement left = elements.get(0);\r
+                        AlignElement right = elements.get(elements.size() - 1);\r
+\r
+                        double leftEdge = left.transform[4];\r
+                        double rightEdge = right.transform[4];\r
+\r
+                        double totalDist = rightEdge - leftEdge;\r
+                        double dist = totalDist / (elements.size() - 1);\r
+                        double d = leftEdge;\r
+\r
+                        for (int i = 1; i < elements.size() -1; i++) {\r
+                            d += dist;\r
+                            AlignElement e = elements.get(i);\r
+\r
+                            double mx = d;\r
+\r
+                            DiagramGraphUtil.changeTransform(graph, e.element, new double[]{e.transform[0],e.transform[1],e.transform[2],e.transform[3],mx,e.transform[5]});\r
+                        }\r
+\r
+                        break;\r
+                    }\r
+                    case RIGHT:{\r
+                        Collections.sort(elements, new XComparator());\r
+                        AlignElement left = elements.get(0);\r
+                        AlignElement right = elements.get(elements.size() - 1);\r
+\r
+                        double leftEdge = left.transform[4] + left.rotatedBounds.getMaxX();\r
+                        double rightEdge = right.transform[4] + right.rotatedBounds.getMaxX();\r
+\r
+                        double totalDist = rightEdge - leftEdge;\r
+                        double dist = totalDist / (elements.size() - 1);\r
+                        double d = leftEdge;\r
+\r
+                        for (int i = 1; i < elements.size() -1; i++) {\r
+                            d += dist;\r
+                            AlignElement e = elements.get(i);\r
+\r
+                            double mx = d - e.rotatedBounds.getMaxX();\r
+\r
+                            DiagramGraphUtil.changeTransform(graph, e.element, new double[]{e.transform[0],e.transform[1],e.transform[2],e.transform[3],mx,e.transform[5]});\r
+                        }\r
+                        break;\r
+                    }\r
+                    case VERT_BTW:{\r
+                        Collections.sort(elements, new XComparator());\r
+                        AlignElement left = elements.get(0);\r
+                        AlignElement right = elements.get(elements.size() - 1);\r
+\r
+                        double leftEdge = left.transform[4] + left.rotatedBounds.getMaxX();\r
+                        double rightEdge = right.transform[4] + right.rotatedBounds.getMinX();\r
+\r
+                        double totalDist = rightEdge - leftEdge;\r
+                        double totalElementSize = 0;\r
+                        for (int i = 1; i < elements.size() -1; i++) {\r
+                            totalElementSize += elements.get(i).rotatedBounds.getWidth();\r
+                        }\r
+                        double totalAvail = totalDist - totalElementSize;\r
+                        double dist = totalAvail / (elements.size() - 1);\r
+                        double d = leftEdge;\r
+\r
+                        for (int i = 1; i < elements.size() -1; i++) {\r
+                            d += dist;\r
+                            AlignElement e = elements.get(i);\r
+\r
+                            double mx = d - e.rotatedBounds.getMinX();\r
+                            d += e.bounds.getWidth();\r
+\r
+                            DiagramGraphUtil.changeTransform(graph, e.element, new double[]{e.transform[0],e.transform[1],e.transform[2],e.transform[3],mx,e.transform[5]});\r
+                        }\r
+\r
+                        break;\r
+                    }\r
+                    case BOTTOM: {\r
+                        Collections.sort(elements, new YComparator());\r
+                        AlignElement top = elements.get(0);\r
+                        AlignElement bottom = elements.get(elements.size() - 1);\r
+\r
+                        double topEdge = top.transform[5] + top.rotatedBounds.getMaxY();\r
+                        double bottomEdge = bottom.transform[5] + bottom.rotatedBounds.getMaxY();\r
+\r
+                        double totalDist = bottomEdge - topEdge;\r
+                        double dist = totalDist / (elements.size() - 1);\r
+                        double d = topEdge;\r
+\r
+                        for (int i = 1; i < elements.size() -1; i++) {\r
+                            d += dist;\r
+                            AlignElement e = elements.get(i);\r
+\r
+                            double my = d - e.rotatedBounds.getMaxY();\r
+\r
+                            DiagramGraphUtil.changeTransform(graph, e.element, new double[]{e.transform[0],e.transform[1],e.transform[2],e.transform[3],e.transform[4],my});\r
+                        }\r
+\r
+                        break;\r
+                    }\r
+                    case TOP: {\r
+                        Collections.sort(elements, new YComparator());\r
+                        AlignElement top = elements.get(0);\r
+                        AlignElement bottom = elements.get(elements.size() - 1);\r
+\r
+                        double topEdge = top.transform[5] + top.rotatedBounds.getMinY();\r
+                        double bottomEdge = bottom.transform[5] + bottom.rotatedBounds.getMinY();\r
+\r
+                        double totalDist = bottomEdge - topEdge;\r
+                        double dist = totalDist / (elements.size() - 1);\r
+                        double d = topEdge;\r
+\r
+                        for (int i = 1; i < elements.size() -1; i++) {\r
+                            d += dist;\r
+                            AlignElement e = elements.get(i);\r
+\r
+                            double my = d - e.rotatedBounds.getMinY();\r
+\r
+                            DiagramGraphUtil.changeTransform(graph, e.element, new double[]{e.transform[0],e.transform[1],e.transform[2],e.transform[3],e.transform[4],my});\r
+                        }\r
+\r
+                        break;\r
+                    }\r
+                    case HORIZ: {\r
+                        Collections.sort(elements, new YComparator());\r
+                        AlignElement top = elements.get(0);\r
+                        AlignElement bottom = elements.get(elements.size() - 1);\r
+\r
+                        double topEdge = top.transform[5];\r
+                        double bottomEdge = bottom.transform[5];\r
+\r
+                        double totalDist = bottomEdge - topEdge;\r
+                        double dist = totalDist / (elements.size() - 1);\r
+                        double d = topEdge;\r
+\r
+                        for (int i = 1; i < elements.size() -1; i++) {\r
+                            d += dist;\r
+                            AlignElement e = elements.get(i);\r
+\r
+                            double my = d;\r
+\r
+                            DiagramGraphUtil.changeTransform(graph, e.element, new double[]{e.transform[0],e.transform[1],e.transform[2],e.transform[3],e.transform[4],my});\r
+                        }\r
+\r
+                        break;\r
+                    }\r
+                    case HORIZ_BTW: {\r
+                        Collections.sort(elements, new YComparator());\r
+                        AlignElement top = elements.get(0);\r
+                        AlignElement bottom = elements.get(elements.size() - 1);\r
+\r
+                        double topEdge = top.transform[5] + top.rotatedBounds.getMaxY();\r
+                        double bottomEdge = bottom.transform[5] + bottom.rotatedBounds.getMinY();\r
+\r
+                        double totalDist = bottomEdge - topEdge;\r
+                        double totalElementSize = 0;\r
+                        for (int i = 1; i < elements.size() -1; i++) {\r
+                            totalElementSize += elements.get(i).rotatedBounds.getHeight();\r
+                        }\r
+                        double totalAvail = totalDist - totalElementSize;\r
+                        double dist = totalAvail / (elements.size() - 1);\r
+                        double d = topEdge;\r
+\r
+                        for (int i = 1; i < elements.size() -1; i++) {\r
+                            d += dist;\r
+                            AlignElement e = elements.get(i);\r
+\r
+                            double my = d - e.rotatedBounds.getMinY();\r
+                            d += e.rotatedBounds.getHeight();\r
+\r
+                            DiagramGraphUtil.changeTransform(graph, e.element, new double[]{e.transform[0],e.transform[1],e.transform[2],e.transform[3],e.transform[4],my});\r
+                        }\r
+\r
+                        break;\r
+                    }\r
+\r
+                }\r
+\r
+\r
+            }\r
+        });\r
+\r
+    }\r
+\r
+    /**\r
+     * Rotate specified set of diagram element resources around the center of\r
+     * mass of the specified element selection.\r
+     * \r
+     * @param resources diagram element resources to rotate\r
+     * @param clockwise <code>true</code> to rotate 90 degrees clockwise,\r
+     *        <code>false</code> to rotate 90 degrees counter-clockwise\r
+     */\r
+    public static void rotate(final Resource resources[], final boolean clockwise) {\r
+        SimanticsUI.getSession().asyncRequest(new WriteRequest() {\r
+            @Override\r
+            public void perform(WriteGraph graph) throws DatabaseException {\r
+                graph.markUndoPoint();\r
+                IDiagram hints = Diagram.spawnNew(DiagramClass.DEFAULT);\r
+\r
+                DiagramResource DIA = DiagramResource.getInstance(graph);\r
+                \r
+                List<AlignElement> elements = new ArrayList<AlignElement>();\r
+                List<Resource> connections = new ArrayList<Resource>();\r
+                for (Resource r : resources) {\r
+                    AlignElement e = create(graph, hints, r);\r
+                    if (e != null)\r
+                        elements.add(e);\r
+                    else if(graph.isInstanceOf(r, DIA.RouteGraphConnection))\r
+                        connections.add(r);\r
+                }\r
+                if (elements.size() < 1)\r
+                    return;\r
+\r
+                // Add comment to change set.\r
+                CommentMetadata cm = graph.getMetadata(CommentMetadata.class);\r
+                graph.addMetadata( cm.add("Rotate " + elements.size() + " elements " + (clockwise ? "clockwise" : "counter-clockwise")) );\r
+\r
+                AffineTransform at = clockwise ? AffineTransform.getQuadrantRotateInstance(1)\r
+                        : AffineTransform.getQuadrantRotateInstance(3);\r
+\r
+                if (elements.size() == 1 && connections.isEmpty()) {\r
+                    for (AlignElement e : elements) {\r
+                        AffineTransform eat = new AffineTransform(e.transform[0], e.transform[1], e.transform[2], e.transform[3], 0, 0);\r
+                        eat.preConcatenate(at);\r
+                        \r
+                        DiagramGraphUtil.changeTransform(graph, e.element, \r
+                                new double[]{eat.getScaleX(),eat.getShearY(),eat.getShearX(),eat.getScaleY(),e.transform[4],e.transform[5]});\r
+                    }\r
+                } else {\r
+                    Rectangle2D selectionBounds = null;\r
+                    for (AlignElement e : elements) {\r
+                        if (selectionBounds != null) {\r
+                            selectionBounds.add(e.transform[4], e.transform[5]);\r
+                        } else {\r
+                            selectionBounds = new Rectangle2D.Double(e.transform[4], e.transform[5], 0, 0);\r
+                        }\r
+                    }\r
+                    \r
+                    double cx = selectionBounds.getCenterX();\r
+                    double cy = selectionBounds.getCenterY();\r
+\r
+                    for (AlignElement e : elements) {\r
+                        double x = e.transform[4];\r
+                        double y = e.transform[5];                        \r
+                        double dx = x - cx;\r
+                        double dy = y - cy;\r
+                        Point2D r = at.transform(new Point2D.Double(dx, dy), null);\r
+                        double mx = r.getX() + cx;\r
+                        double my = r.getY() + cy;\r
+                        AffineTransform eat = new AffineTransform(e.transform[0], e.transform[1], e.transform[2], e.transform[3], 0, 0);\r
+                        eat.preConcatenate(at);\r
+                        \r
+                        DiagramGraphUtil.changeTransform(graph, e.element, \r
+                                new double[]{eat.getScaleX(),eat.getShearY(),eat.getShearX(),eat.getScaleY(),mx,my});\r
+                    }\r
+                    \r
+                    if(!connections.isEmpty()) {\r
+                        Command rotateConnection = Commands.get(graph, "Simantics/Diagram/rotateConnection");\r
+                        Resource model = graph.syncRequest(new IndexRoot(connections.get(0)));\r
+                        for(Resource r : connections)\r
+                            rotateConnection.execute(graph, model, r, cx, cy, clockwise);\r
+                    }\r
+                }\r
+            }\r
+        });\r
+    }\r
+\r
+    /**\r
+     * Flip specified set of diagram element resources around either the x or\r
+     * y-axis specified by the mass center of the selection bounding box.\r
+     * Each element is considered to weigh an equal amount.\r
+     * \r
+     * @param resources diagram element resources to flip\r
+     * @param xAxis <code>true</code> to flip around x-axis, <code>false</code>\r
+     *        for y-axis\r
+     */\r
+    public static void flip(final Resource resources[], final boolean xAxis) {\r
+        SimanticsUI.getSession().asyncRequest(new WriteRequest() {\r
+            @Override\r
+            public void perform(WriteGraph graph) throws DatabaseException {\r
+                graph.markUndoPoint();\r
+                IDiagram hints = Diagram.spawnNew(DiagramClass.DEFAULT);\r
+                \r
+                DiagramResource DIA = DiagramResource.getInstance(graph);\r
+\r
+                List<AlignElement> elements = new ArrayList<AlignElement>();\r
+                List<Resource> connections = new ArrayList<Resource>();\r
+                for (Resource r : resources) {\r
+                    AlignElement e = create(graph, hints, r);\r
+                    if (e != null)\r
+                        elements.add(e);\r
+                    else if(graph.isInstanceOf(r, DIA.RouteGraphConnection))\r
+                        connections.add(r);\r
+                }\r
+                if (elements.size() < 1)\r
+                    return;\r
+\r
+                // Add comment to change set.\r
+                CommentMetadata cm = graph.getMetadata(CommentMetadata.class);\r
+                graph.addMetadata( cm.add("Flip " + elements.size() + " elements " + (xAxis ? "vertically" : "horizontally")) );\r
+\r
+                if (elements.size() == 1 && connections.isEmpty()) {\r
+                    for (AlignElement e : elements) {\r
+                        if (xAxis) {\r
+                                 AffineTransform at = new AffineTransform(e.transform);\r
+                              AffineTransform at2 = AffineTransform.getScaleInstance(1, -1);\r
+                              at.preConcatenate(at2);\r
+                              DiagramGraphUtil.changeTransform(graph, e.element, new double[]{at.getScaleX(),at.getShearY(),at.getShearX(),at.getScaleY(),e.transform[4],e.transform[5]});\r
+                        } else {\r
+                                AffineTransform at = new AffineTransform(e.transform);\r
+                             AffineTransform at2 = AffineTransform.getScaleInstance(-1, 1);\r
+                             at.preConcatenate(at2);\r
+                             DiagramGraphUtil.changeTransform(graph, e.element, new double[]{at.getScaleX(),at.getShearY(),at.getShearX(),at.getScaleY(),e.transform[4],e.transform[5]});\r
+                        }\r
+                    }\r
+                } else {\r
+\r
+                    Rectangle2D selectionBounds = null;\r
+                    for (AlignElement e : elements) {\r
+                        if (selectionBounds != null) {\r
+                            selectionBounds.add(e.transform[4], e.transform[5]);\r
+                        } else {\r
+                            selectionBounds = new Rectangle2D.Double(e.transform[4], e.transform[5], 0, 0);\r
+                        }\r
+                    }\r
+\r
+                    for (AlignElement e : elements) {\r
+                        if (xAxis) {\r
+                            double y = e.transform[5];\r
+                            double cy = selectionBounds.getCenterY();\r
+                            double my = cy + cy - y;\r
+                            AffineTransform at = new AffineTransform(e.transform);\r
+                            AffineTransform at2 = AffineTransform.getScaleInstance(1, -1);\r
+                            at.preConcatenate(at2);\r
+                            DiagramGraphUtil.changeTransform(graph, e.element, new double[]{at.getScaleX(),at.getShearY(),at.getShearX(),at.getScaleY(),e.transform[4],my});\r
+                        } else {\r
+                            double x = e.transform[4];\r
+                            double cx = selectionBounds.getCenterX();\r
+                            double mx = cx + cx - x;\r
+                            \r
+                            AffineTransform at = new AffineTransform(e.transform);\r
+                            AffineTransform at2 = AffineTransform.getScaleInstance(-1, 1);\r
+                            at.preConcatenate(at2);\r
+                            DiagramGraphUtil.changeTransform(graph, e.element, new double[]{at.getScaleX(),at.getShearY(),at.getShearX(),at.getScaleY(),mx,e.transform[5]});\r
+                        }\r
+                    }\r
+                    \r
+                    if(!connections.isEmpty()) {\r
+                        Command flipConnection = Commands.get(graph, "Simantics/Diagram/flipConnection");\r
+                        Resource model = graph.syncRequest(new IndexRoot(connections.get(0)));\r
+                        for(Resource r : connections)\r
+                            flipConnection.execute(graph, model, r, xAxis, \r
+                                    xAxis ? selectionBounds.getCenterY() \r
+                                            : selectionBounds.getCenterX());\r
+                    }\r
+                }\r
+\r
+            }\r
+        });\r
+    }\r
+\r
+    private static AlignElement create(ReadGraph graph, IDiagram hints, Resource r) throws ManyObjectsForFunctionalRelationException, NoSingleResultException, ServiceException, DatabaseException {\r
+        DiagramResource dr = DiagramResource.getInstance(graph);\r
+\r
+        if (graph.isInstanceOf(r, dr.Element) && !graph.isInstanceOf(r, dr.Connection) /*&& !graph.isInstanceOf(r, dr.Monitor)*/) {\r
+            double transform[] = graph.getPossibleRelatedValue(r, dr.HasTransform);\r
+            ElementClass ec = graph.syncRequest(DiagramRequests.getElementClass(graph.getSingleType(r, dr.Element), hints));\r
+            IElement e = Element.spawnNew(ec);\r
+            Rectangle2D bounds = ElementUtils.getElementBounds(e);\r
+            if (transform != null && bounds != null) {\r
+                return new AlignElement(r, transform, bounds);\r
+            }\r
+        }\r
+        return null;\r
+    }\r
+\r
+\r
+    private static class AlignElement {\r
+        public Resource element;\r
+        public double[] transform;\r
+        public Rectangle2D bounds;\r
+        public Rectangle2D rotatedBounds;\r
+//        public Rectangle2D transformedBounds;\r
+\r
+        public AlignElement(Resource element, double[] transform, Rectangle2D bounds) {\r
+            this.element = element;\r
+            this.transform = transform;\r
+            this.bounds = bounds;\r
+//            this.transformedBounds = getBounds();\r
+            this.rotatedBounds = getRotatedBounds();\r
+        }\r
+\r
+//        public Rectangle2D getBounds() {\r
+//            AffineTransform at = new AffineTransform(transform[0], transform[1], transform[2], transform[3], transform[4], transform[5]);\r
+//            return GeometryUtils.transformShape(bounds, at).getBounds2D();\r
+//        }\r
+\r
+        public Rectangle2D getRotatedBounds() {\r
+            AffineTransform at = new AffineTransform(transform[0], transform[1], transform[2], transform[3], 0.0, 0.0);\r
+            return GeometryUtils.transformShape(bounds, at).getBounds2D();\r
+        }\r
+\r
+        public double getDeterminant() {\r
+            return transform[0] * transform[3] - transform[1] * transform[2];\r
+        }\r
+\r
+    }\r
+\r
+    private static class XComparator implements Comparator<AlignElement> {\r
+        @Override\r
+        public int compare(AlignElement o1, AlignElement o2) {\r
+            if (o1.transform[4] < o2.transform[4])\r
+                return -1;\r
+            if (o1.transform[4] > o2.transform[4])\r
+                return 1;\r
+            return 0;\r
+\r
+        }\r
+    }\r
+\r
+    private static class YComparator implements Comparator<AlignElement> {\r
+        @Override\r
+        public int compare(AlignElement o1, AlignElement o2) {\r
+            if (o1.transform[5] < o2.transform[5])\r
+                return -1;\r
+            if (o1.transform[5] > o2.transform[5])\r
+                return 1;\r
+            return 0;\r
+\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Set 2D affine transforms for the listed diagram element resources.\r
+     * \r
+     * @param elements diagram element resources to set transforms for\r
+     * @param transforms transforms for each element\r
+     */\r
+    public static Write setTransformRequest(final Collection<TransformedObject> elements)\r
+    {\r
+        return new WriteRequest() {\r
+            @Override\r
+            public void perform(WriteGraph graph) throws DatabaseException {\r
+                for (TransformedObject element : elements)\r
+                    DiagramGraphUtil.changeTransform(graph, element.element, element.transform);\r
+            }\r
+        };\r
+    }\r
+\r
+    /**\r
+     * Set 2D affine transforms for the listed diagram element resources.\r
+     * \r
+     * @param undoContext the database undo context to use for the returned\r
+     *        request\r
+     * @param elements diagram element resources to set transforms for\r
+     * @param transforms transforms for each element\r
+     * @param preConcatenate <code>true</code> to pre-concatenate the\r
+     *        transforms, <code>false</code> to concatenate\r
+     */\r
+    public static Write concatenateTransformRequest(\r
+            UndoContext undoContext,\r
+            final Collection<TransformedObject> elements,\r
+            final boolean preConcatenate)\r
+    {\r
+        return new WriteRequest() {\r
+            @Override\r
+            public void perform(WriteGraph graph) throws DatabaseException {\r
+                for (TransformedObject element : elements) {\r
+                    AffineTransform at = DiagramGraphUtil.getTransform(graph, element.element);\r
+                    if (preConcatenate)\r
+                        at.preConcatenate(element.transform);\r
+                    else\r
+                        at.concatenate(element.transform);\r
+                    DiagramGraphUtil.setTransform(graph, element.element, at);\r
+                }\r
+            }\r
+        };\r
+    }\r
+\r
+    public static class TransformedObject {\r
+        public final Resource        element;\r
+        public final AffineTransform transform;\r
+\r
+        public TransformedObject(Resource element) {\r
+            this.element = element;\r
+            this.transform = new AffineTransform();\r
+        }\r
+        public TransformedObject(Resource element, AffineTransform transform) {\r
+            this.element = element;\r
+            this.transform = transform;\r
+        }\r
+    }\r
+\r
+}\r