]> gerrit.simantics Code Review - simantics/platform.git/blob - 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
1 package org.simantics.diagram.elements;\r
2 \r
3 import java.awt.geom.AffineTransform;\r
4 import java.awt.geom.Point2D;\r
5 import java.awt.geom.Rectangle2D;\r
6 import java.util.ArrayList;\r
7 import java.util.Collection;\r
8 import java.util.Collections;\r
9 import java.util.Comparator;\r
10 import java.util.List;\r
11 \r
12 import org.simantics.db.ReadGraph;\r
13 import org.simantics.db.Resource;\r
14 import org.simantics.db.UndoContext;\r
15 import org.simantics.db.WriteGraph;\r
16 import org.simantics.db.common.CommentMetadata;\r
17 import org.simantics.db.common.request.IndexRoot;\r
18 import org.simantics.db.common.request.WriteRequest;\r
19 import org.simantics.db.exception.DatabaseException;\r
20 import org.simantics.db.exception.ManyObjectsForFunctionalRelationException;\r
21 import org.simantics.db.exception.NoSingleResultException;\r
22 import org.simantics.db.exception.ServiceException;\r
23 import org.simantics.db.request.Write;\r
24 import org.simantics.diagram.query.DiagramRequests;\r
25 import org.simantics.diagram.stubs.DiagramResource;\r
26 import org.simantics.diagram.synchronization.graph.DiagramGraphUtil;\r
27 import org.simantics.g2d.diagram.DiagramClass;\r
28 import org.simantics.g2d.diagram.IDiagram;\r
29 import org.simantics.g2d.diagram.impl.Diagram;\r
30 import org.simantics.g2d.element.ElementClass;\r
31 import org.simantics.g2d.element.ElementUtils;\r
32 import org.simantics.g2d.element.IElement;\r
33 import org.simantics.g2d.element.impl.Element;\r
34 import org.simantics.g2d.utils.GeometryUtils;\r
35 import org.simantics.scl.commands.Command;\r
36 import org.simantics.scl.commands.Commands;\r
37 import org.simantics.ui.SimanticsUI;\r
38 \r
39 /**\r
40  * Tools to align, rotate, and flip diagram elements.\r
41  * \r
42  * TODO : We need to add capability hints to elements to prevent rotating and mirroring elements that do not support that.\r
43  *        Example: mirrored text does not make any sense.\r
44  * \r
45  * \r
46  * @author Marko Luukkainen <marko.luukkainen@vtt.fi> (implementation)\r
47  * @author Tuukka Lehtonen (documentation)\r
48  */\r
49 public final class ElementTransforms {\r
50 \r
51     public static enum SIDE { LEFT, RIGHT, TOP, BOTTOM, VERT, HORIZ, VERT_BTW, HORIZ_BTW };\r
52 \r
53     /**\r
54      * Align the specified set of diagram element resources in line with each\r
55      * other calculated by the specified side.\r
56      * \r
57      * <p>\r
58      * Alignment requires at least two elements to do anything.\r
59      * \r
60      * @param resources diagram element resources to rotate\r
61      * @param side the side of each element to use for distancing. Does not support between aligments.\r
62      */\r
63     public static void align(final Resource resources[], final SIDE side) {\r
64         if (resources.length < 2)\r
65             return;\r
66         if (side == SIDE.HORIZ_BTW || side == SIDE.VERT_BTW )\r
67             return;\r
68 \r
69         SimanticsUI.getSession().asyncRequest(new WriteRequest() {\r
70 \r
71             @Override\r
72             public void perform(WriteGraph graph) throws DatabaseException {\r
73                 graph.markUndoPoint();\r
74                 IDiagram hints = Diagram.spawnNew(DiagramClass.DEFAULT);\r
75 \r
76                 List<AlignElement> elements = new ArrayList<AlignElement>();\r
77                 for (Resource r : resources) {\r
78                     AlignElement e = create(graph, hints, r);\r
79                     if (e != null)\r
80                         elements.add(e);\r
81                 }\r
82                 if (elements.size() < 2)\r
83                     return;\r
84                 double mx = 0;\r
85                 double my = 0;\r
86                 for (AlignElement e : elements) {\r
87                     mx += e.transform[4];\r
88                     my += e.transform[5];\r
89                 }\r
90                 mx /= elements.size();\r
91                 my /= elements.size();\r
92 \r
93                 // prevent moving symbols into the same position\r
94                 int count = 0;\r
95                 for (AlignElement e : elements) {\r
96                     if (side == SIDE.VERT || side == SIDE.LEFT || side == SIDE.RIGHT) {\r
97                         if (Math.abs(e.transform[5] - my) < 0.1) {\r
98                             count++;\r
99                         }\r
100                     } else {\r
101                         if (Math.abs(e.transform[4] - mx) < 0.1) {\r
102                             count++;\r
103                         }\r
104                     }\r
105                 }\r
106                 if (count > 1)\r
107                     return;\r
108 \r
109                 if (side == SIDE.HORIZ || side == SIDE.VERT) {\r
110 \r
111 \r
112                     for (AlignElement e : elements) {\r
113 \r
114                         if (side == SIDE.VERT)\r
115                             DiagramGraphUtil.changeTransform(graph, e.element, new double[]{e.transform[0],e.transform[1],e.transform[2],e.transform[3],mx,e.transform[5]});\r
116                         else if (side == SIDE.HORIZ) {\r
117                             DiagramGraphUtil.changeTransform(graph, e.element, new double[]{e.transform[0],e.transform[1],e.transform[2],e.transform[3],e.transform[4],my});\r
118                         }\r
119                     }\r
120                 } else {\r
121                     double lx, rx;\r
122                     double ty, by;\r
123                     lx = elements.get(0).transform[4] + elements.get(0).rotatedBounds.getMinX();\r
124                     rx = elements.get(0).transform[4] + elements.get(0).rotatedBounds.getMaxX();\r
125 \r
126                     ty = elements.get(0).transform[5] + elements.get(0).rotatedBounds.getMinY();\r
127                     by = elements.get(0).transform[5] + elements.get(0).rotatedBounds.getMaxY();\r
128 \r
129                     for (int i = 1; i < elements.size(); i++) {\r
130                         double tlx, trx;\r
131                         double tty, tby;\r
132                         tlx = elements.get(i).transform[4] + elements.get(i).rotatedBounds.getMinX();\r
133                         trx = elements.get(i).transform[4] + elements.get(i).rotatedBounds.getMaxX();\r
134 \r
135                         tty = elements.get(i).transform[5] + elements.get(i).rotatedBounds.getMinY();\r
136                         tby = elements.get(i).transform[5] + elements.get(i).rotatedBounds.getMaxY();\r
137                         if (tlx < lx)\r
138                             lx = tlx;\r
139                         if (trx > rx)\r
140                             rx = trx;\r
141                         if (tty < ty)\r
142                             ty = tty;\r
143                         if (tby > by)\r
144                             by = tby;\r
145 \r
146                     }\r
147 \r
148                     for (AlignElement e : elements) {\r
149                         mx = e.transform[4];\r
150                         my = e.transform[5];\r
151                         if (side == SIDE.LEFT) {\r
152                             mx = lx - e.rotatedBounds.getMinX() ;\r
153                         } else if (side == SIDE.RIGHT) {\r
154                             mx = rx - e.rotatedBounds.getMaxX();\r
155                         } else if (side == SIDE.TOP) {\r
156                             my = ty - e.rotatedBounds.getMinY();\r
157                         } else {\r
158                             my = by - e.rotatedBounds.getMaxY();\r
159                         }\r
160                         DiagramGraphUtil.changeTransform(graph, e.element, new double[]{e.transform[0],e.transform[1],e.transform[2],e.transform[3],mx,my});\r
161 \r
162                     }\r
163 \r
164                 }\r
165 \r
166             }\r
167         });\r
168     }\r
169 \r
170     /**\r
171      * Distance specified set of diagram element resources equally. Distancing\r
172      * is performed on the specified side of the each element.\r
173      * \r
174      * <p>\r
175      * Distancing requires at least three elements to work.\r
176      * \r
177      * @param resources diagram element resources to rotate\r
178      * @param side the side of each element to use for distancing\r
179      */\r
180     public static void dist(final Resource resources[], final SIDE side) {\r
181 \r
182         if (resources.length < 3)\r
183             return;\r
184 \r
185         SimanticsUI.getSession().asyncRequest(new WriteRequest() {\r
186 \r
187             @Override\r
188             public void perform(WriteGraph graph) throws DatabaseException {\r
189                 graph.markUndoPoint();\r
190                 IDiagram hints = Diagram.spawnNew(DiagramClass.DEFAULT);\r
191 \r
192                 List<AlignElement> elements = new ArrayList<AlignElement>();\r
193                 for (Resource r : resources) {\r
194                     //System.out.println(r + " " + GraphUtils.getReadableName(graph, r));\r
195                     AlignElement e = create(graph, hints, r);\r
196                     if (e != null)\r
197                         elements.add(e);\r
198                 }\r
199                 if (elements.size() < 3)\r
200                     return;\r
201                 switch (side) {\r
202                     case LEFT: {\r
203                         Collections.sort(elements, new XComparator());\r
204                         AlignElement left = elements.get(0);\r
205                         AlignElement right = elements.get(elements.size() - 1);\r
206 \r
207                         double leftEdge = left.transform[4] + left.rotatedBounds.getMinX();\r
208                         double rightEdge = right.transform[4] + right.rotatedBounds.getMinX();\r
209 \r
210                         double totalDist = rightEdge - leftEdge;\r
211                         double dist = totalDist / (elements.size() - 1);\r
212                         double d = leftEdge;\r
213 \r
214                         for (int i = 1; i < elements.size() -1; i++) {\r
215                             d += dist;\r
216                             AlignElement e = elements.get(i);\r
217 \r
218                             double mx = d - e.rotatedBounds.getMinX();\r
219 \r
220                             DiagramGraphUtil.changeTransform(graph, e.element, new double[]{e.transform[0],e.transform[1],e.transform[2],e.transform[3],mx,e.transform[5]});\r
221                         }\r
222 \r
223                         break;\r
224                     }\r
225                     case VERT: {\r
226                         Collections.sort(elements, new XComparator());\r
227                         AlignElement left = elements.get(0);\r
228                         AlignElement right = elements.get(elements.size() - 1);\r
229 \r
230                         double leftEdge = left.transform[4];\r
231                         double rightEdge = right.transform[4];\r
232 \r
233                         double totalDist = rightEdge - leftEdge;\r
234                         double dist = totalDist / (elements.size() - 1);\r
235                         double d = leftEdge;\r
236 \r
237                         for (int i = 1; i < elements.size() -1; i++) {\r
238                             d += dist;\r
239                             AlignElement e = elements.get(i);\r
240 \r
241                             double mx = d;\r
242 \r
243                             DiagramGraphUtil.changeTransform(graph, e.element, new double[]{e.transform[0],e.transform[1],e.transform[2],e.transform[3],mx,e.transform[5]});\r
244                         }\r
245 \r
246                         break;\r
247                     }\r
248                     case RIGHT:{\r
249                         Collections.sort(elements, new XComparator());\r
250                         AlignElement left = elements.get(0);\r
251                         AlignElement right = elements.get(elements.size() - 1);\r
252 \r
253                         double leftEdge = left.transform[4] + left.rotatedBounds.getMaxX();\r
254                         double rightEdge = right.transform[4] + right.rotatedBounds.getMaxX();\r
255 \r
256                         double totalDist = rightEdge - leftEdge;\r
257                         double dist = totalDist / (elements.size() - 1);\r
258                         double d = leftEdge;\r
259 \r
260                         for (int i = 1; i < elements.size() -1; i++) {\r
261                             d += dist;\r
262                             AlignElement e = elements.get(i);\r
263 \r
264                             double mx = d - e.rotatedBounds.getMaxX();\r
265 \r
266                             DiagramGraphUtil.changeTransform(graph, e.element, new double[]{e.transform[0],e.transform[1],e.transform[2],e.transform[3],mx,e.transform[5]});\r
267                         }\r
268                         break;\r
269                     }\r
270                     case VERT_BTW:{\r
271                         Collections.sort(elements, new XComparator());\r
272                         AlignElement left = elements.get(0);\r
273                         AlignElement right = elements.get(elements.size() - 1);\r
274 \r
275                         double leftEdge = left.transform[4] + left.rotatedBounds.getMaxX();\r
276                         double rightEdge = right.transform[4] + right.rotatedBounds.getMinX();\r
277 \r
278                         double totalDist = rightEdge - leftEdge;\r
279                         double totalElementSize = 0;\r
280                         for (int i = 1; i < elements.size() -1; i++) {\r
281                             totalElementSize += elements.get(i).rotatedBounds.getWidth();\r
282                         }\r
283                         double totalAvail = totalDist - totalElementSize;\r
284                         double dist = totalAvail / (elements.size() - 1);\r
285                         double d = leftEdge;\r
286 \r
287                         for (int i = 1; i < elements.size() -1; i++) {\r
288                             d += dist;\r
289                             AlignElement e = elements.get(i);\r
290 \r
291                             double mx = d - e.rotatedBounds.getMinX();\r
292                             d += e.bounds.getWidth();\r
293 \r
294                             DiagramGraphUtil.changeTransform(graph, e.element, new double[]{e.transform[0],e.transform[1],e.transform[2],e.transform[3],mx,e.transform[5]});\r
295                         }\r
296 \r
297                         break;\r
298                     }\r
299                     case BOTTOM: {\r
300                         Collections.sort(elements, new YComparator());\r
301                         AlignElement top = elements.get(0);\r
302                         AlignElement bottom = elements.get(elements.size() - 1);\r
303 \r
304                         double topEdge = top.transform[5] + top.rotatedBounds.getMaxY();\r
305                         double bottomEdge = bottom.transform[5] + bottom.rotatedBounds.getMaxY();\r
306 \r
307                         double totalDist = bottomEdge - topEdge;\r
308                         double dist = totalDist / (elements.size() - 1);\r
309                         double d = topEdge;\r
310 \r
311                         for (int i = 1; i < elements.size() -1; i++) {\r
312                             d += dist;\r
313                             AlignElement e = elements.get(i);\r
314 \r
315                             double my = d - e.rotatedBounds.getMaxY();\r
316 \r
317                             DiagramGraphUtil.changeTransform(graph, e.element, new double[]{e.transform[0],e.transform[1],e.transform[2],e.transform[3],e.transform[4],my});\r
318                         }\r
319 \r
320                         break;\r
321                     }\r
322                     case TOP: {\r
323                         Collections.sort(elements, new YComparator());\r
324                         AlignElement top = elements.get(0);\r
325                         AlignElement bottom = elements.get(elements.size() - 1);\r
326 \r
327                         double topEdge = top.transform[5] + top.rotatedBounds.getMinY();\r
328                         double bottomEdge = bottom.transform[5] + bottom.rotatedBounds.getMinY();\r
329 \r
330                         double totalDist = bottomEdge - topEdge;\r
331                         double dist = totalDist / (elements.size() - 1);\r
332                         double d = topEdge;\r
333 \r
334                         for (int i = 1; i < elements.size() -1; i++) {\r
335                             d += dist;\r
336                             AlignElement e = elements.get(i);\r
337 \r
338                             double my = d - e.rotatedBounds.getMinY();\r
339 \r
340                             DiagramGraphUtil.changeTransform(graph, e.element, new double[]{e.transform[0],e.transform[1],e.transform[2],e.transform[3],e.transform[4],my});\r
341                         }\r
342 \r
343                         break;\r
344                     }\r
345                     case HORIZ: {\r
346                         Collections.sort(elements, new YComparator());\r
347                         AlignElement top = elements.get(0);\r
348                         AlignElement bottom = elements.get(elements.size() - 1);\r
349 \r
350                         double topEdge = top.transform[5];\r
351                         double bottomEdge = bottom.transform[5];\r
352 \r
353                         double totalDist = bottomEdge - topEdge;\r
354                         double dist = totalDist / (elements.size() - 1);\r
355                         double d = topEdge;\r
356 \r
357                         for (int i = 1; i < elements.size() -1; i++) {\r
358                             d += dist;\r
359                             AlignElement e = elements.get(i);\r
360 \r
361                             double my = d;\r
362 \r
363                             DiagramGraphUtil.changeTransform(graph, e.element, new double[]{e.transform[0],e.transform[1],e.transform[2],e.transform[3],e.transform[4],my});\r
364                         }\r
365 \r
366                         break;\r
367                     }\r
368                     case HORIZ_BTW: {\r
369                         Collections.sort(elements, new YComparator());\r
370                         AlignElement top = elements.get(0);\r
371                         AlignElement bottom = elements.get(elements.size() - 1);\r
372 \r
373                         double topEdge = top.transform[5] + top.rotatedBounds.getMaxY();\r
374                         double bottomEdge = bottom.transform[5] + bottom.rotatedBounds.getMinY();\r
375 \r
376                         double totalDist = bottomEdge - topEdge;\r
377                         double totalElementSize = 0;\r
378                         for (int i = 1; i < elements.size() -1; i++) {\r
379                             totalElementSize += elements.get(i).rotatedBounds.getHeight();\r
380                         }\r
381                         double totalAvail = totalDist - totalElementSize;\r
382                         double dist = totalAvail / (elements.size() - 1);\r
383                         double d = topEdge;\r
384 \r
385                         for (int i = 1; i < elements.size() -1; i++) {\r
386                             d += dist;\r
387                             AlignElement e = elements.get(i);\r
388 \r
389                             double my = d - e.rotatedBounds.getMinY();\r
390                             d += e.rotatedBounds.getHeight();\r
391 \r
392                             DiagramGraphUtil.changeTransform(graph, e.element, new double[]{e.transform[0],e.transform[1],e.transform[2],e.transform[3],e.transform[4],my});\r
393                         }\r
394 \r
395                         break;\r
396                     }\r
397 \r
398                 }\r
399 \r
400 \r
401             }\r
402         });\r
403 \r
404     }\r
405 \r
406     /**\r
407      * Rotate specified set of diagram element resources around the center of\r
408      * mass of the specified element selection.\r
409      * \r
410      * @param resources diagram element resources to rotate\r
411      * @param clockwise <code>true</code> to rotate 90 degrees clockwise,\r
412      *        <code>false</code> to rotate 90 degrees counter-clockwise\r
413      */\r
414     public static void rotate(final Resource resources[], final boolean clockwise) {\r
415         SimanticsUI.getSession().asyncRequest(new WriteRequest() {\r
416             @Override\r
417             public void perform(WriteGraph graph) throws DatabaseException {\r
418                 graph.markUndoPoint();\r
419                 IDiagram hints = Diagram.spawnNew(DiagramClass.DEFAULT);\r
420 \r
421                 DiagramResource DIA = DiagramResource.getInstance(graph);\r
422                 \r
423                 List<AlignElement> elements = new ArrayList<AlignElement>();\r
424                 List<Resource> connections = new ArrayList<Resource>();\r
425                 for (Resource r : resources) {\r
426                     AlignElement e = create(graph, hints, r);\r
427                     if (e != null)\r
428                         elements.add(e);\r
429                     else if(graph.isInstanceOf(r, DIA.RouteGraphConnection))\r
430                         connections.add(r);\r
431                 }\r
432                 if (elements.size() < 1)\r
433                     return;\r
434 \r
435                 // Add comment to change set.\r
436                 CommentMetadata cm = graph.getMetadata(CommentMetadata.class);\r
437                 graph.addMetadata( cm.add("Rotate " + elements.size() + " elements " + (clockwise ? "clockwise" : "counter-clockwise")) );\r
438 \r
439                 AffineTransform at = clockwise ? AffineTransform.getQuadrantRotateInstance(1)\r
440                         : AffineTransform.getQuadrantRotateInstance(3);\r
441 \r
442                 if (elements.size() == 1 && connections.isEmpty()) {\r
443                     for (AlignElement e : elements) {\r
444                         AffineTransform eat = new AffineTransform(e.transform[0], e.transform[1], e.transform[2], e.transform[3], 0, 0);\r
445                         eat.preConcatenate(at);\r
446                         \r
447                         DiagramGraphUtil.changeTransform(graph, e.element, \r
448                                 new double[]{eat.getScaleX(),eat.getShearY(),eat.getShearX(),eat.getScaleY(),e.transform[4],e.transform[5]});\r
449                     }\r
450                 } else {\r
451                     Rectangle2D selectionBounds = null;\r
452                     for (AlignElement e : elements) {\r
453                         if (selectionBounds != null) {\r
454                             selectionBounds.add(e.transform[4], e.transform[5]);\r
455                         } else {\r
456                             selectionBounds = new Rectangle2D.Double(e.transform[4], e.transform[5], 0, 0);\r
457                         }\r
458                     }\r
459                     \r
460                     double cx = selectionBounds.getCenterX();\r
461                     double cy = selectionBounds.getCenterY();\r
462 \r
463                     for (AlignElement e : elements) {\r
464                         double x = e.transform[4];\r
465                         double y = e.transform[5];                        \r
466                         double dx = x - cx;\r
467                         double dy = y - cy;\r
468                         Point2D r = at.transform(new Point2D.Double(dx, dy), null);\r
469                         double mx = r.getX() + cx;\r
470                         double my = r.getY() + cy;\r
471                         AffineTransform eat = new AffineTransform(e.transform[0], e.transform[1], e.transform[2], e.transform[3], 0, 0);\r
472                         eat.preConcatenate(at);\r
473                         \r
474                         DiagramGraphUtil.changeTransform(graph, e.element, \r
475                                 new double[]{eat.getScaleX(),eat.getShearY(),eat.getShearX(),eat.getScaleY(),mx,my});\r
476                     }\r
477                     \r
478                     if(!connections.isEmpty()) {\r
479                         Command rotateConnection = Commands.get(graph, "Simantics/Diagram/rotateConnection");\r
480                         Resource model = graph.syncRequest(new IndexRoot(connections.get(0)));\r
481                         for(Resource r : connections)\r
482                             rotateConnection.execute(graph, model, r, cx, cy, clockwise);\r
483                     }\r
484                 }\r
485             }\r
486         });\r
487     }\r
488 \r
489     /**\r
490      * Flip specified set of diagram element resources around either the x or\r
491      * y-axis specified by the mass center of the selection bounding box.\r
492      * Each element is considered to weigh an equal amount.\r
493      * \r
494      * @param resources diagram element resources to flip\r
495      * @param xAxis <code>true</code> to flip around x-axis, <code>false</code>\r
496      *        for y-axis\r
497      */\r
498     public static void flip(final Resource resources[], final boolean xAxis) {\r
499         SimanticsUI.getSession().asyncRequest(new WriteRequest() {\r
500             @Override\r
501             public void perform(WriteGraph graph) throws DatabaseException {\r
502                 graph.markUndoPoint();\r
503                 IDiagram hints = Diagram.spawnNew(DiagramClass.DEFAULT);\r
504                 \r
505                 DiagramResource DIA = DiagramResource.getInstance(graph);\r
506 \r
507                 List<AlignElement> elements = new ArrayList<AlignElement>();\r
508                 List<Resource> connections = new ArrayList<Resource>();\r
509                 for (Resource r : resources) {\r
510                     AlignElement e = create(graph, hints, r);\r
511                     if (e != null)\r
512                         elements.add(e);\r
513                     else if(graph.isInstanceOf(r, DIA.RouteGraphConnection))\r
514                         connections.add(r);\r
515                 }\r
516                 if (elements.size() < 1)\r
517                     return;\r
518 \r
519                 // Add comment to change set.\r
520                 CommentMetadata cm = graph.getMetadata(CommentMetadata.class);\r
521                 graph.addMetadata( cm.add("Flip " + elements.size() + " elements " + (xAxis ? "vertically" : "horizontally")) );\r
522 \r
523                 if (elements.size() == 1 && connections.isEmpty()) {\r
524                     for (AlignElement e : elements) {\r
525                         if (xAxis) {\r
526                                   AffineTransform at = new AffineTransform(e.transform);\r
527                               AffineTransform at2 = AffineTransform.getScaleInstance(1, -1);\r
528                               at.preConcatenate(at2);\r
529                               DiagramGraphUtil.changeTransform(graph, e.element, new double[]{at.getScaleX(),at.getShearY(),at.getShearX(),at.getScaleY(),e.transform[4],e.transform[5]});\r
530                         } else {\r
531                                  AffineTransform at = new AffineTransform(e.transform);\r
532                              AffineTransform at2 = AffineTransform.getScaleInstance(-1, 1);\r
533                              at.preConcatenate(at2);\r
534                              DiagramGraphUtil.changeTransform(graph, e.element, new double[]{at.getScaleX(),at.getShearY(),at.getShearX(),at.getScaleY(),e.transform[4],e.transform[5]});\r
535                         }\r
536                     }\r
537                 } else {\r
538 \r
539                     Rectangle2D selectionBounds = null;\r
540                     for (AlignElement e : elements) {\r
541                         if (selectionBounds != null) {\r
542                             selectionBounds.add(e.transform[4], e.transform[5]);\r
543                         } else {\r
544                             selectionBounds = new Rectangle2D.Double(e.transform[4], e.transform[5], 0, 0);\r
545                         }\r
546                     }\r
547 \r
548                     for (AlignElement e : elements) {\r
549                         if (xAxis) {\r
550                             double y = e.transform[5];\r
551                             double cy = selectionBounds.getCenterY();\r
552                             double my = cy + cy - y;\r
553                             AffineTransform at = new AffineTransform(e.transform);\r
554                             AffineTransform at2 = AffineTransform.getScaleInstance(1, -1);\r
555                             at.preConcatenate(at2);\r
556                             DiagramGraphUtil.changeTransform(graph, e.element, new double[]{at.getScaleX(),at.getShearY(),at.getShearX(),at.getScaleY(),e.transform[4],my});\r
557                         } else {\r
558                             double x = e.transform[4];\r
559                             double cx = selectionBounds.getCenterX();\r
560                             double mx = cx + cx - x;\r
561                             \r
562                             AffineTransform at = new AffineTransform(e.transform);\r
563                             AffineTransform at2 = AffineTransform.getScaleInstance(-1, 1);\r
564                             at.preConcatenate(at2);\r
565                             DiagramGraphUtil.changeTransform(graph, e.element, new double[]{at.getScaleX(),at.getShearY(),at.getShearX(),at.getScaleY(),mx,e.transform[5]});\r
566                         }\r
567                     }\r
568                     \r
569                     if(!connections.isEmpty()) {\r
570                         Command flipConnection = Commands.get(graph, "Simantics/Diagram/flipConnection");\r
571                         Resource model = graph.syncRequest(new IndexRoot(connections.get(0)));\r
572                         for(Resource r : connections)\r
573                             flipConnection.execute(graph, model, r, xAxis, \r
574                                     xAxis ? selectionBounds.getCenterY() \r
575                                             : selectionBounds.getCenterX());\r
576                     }\r
577                 }\r
578 \r
579             }\r
580         });\r
581     }\r
582 \r
583     private static AlignElement create(ReadGraph graph, IDiagram hints, Resource r) throws ManyObjectsForFunctionalRelationException, NoSingleResultException, ServiceException, DatabaseException {\r
584         DiagramResource dr = DiagramResource.getInstance(graph);\r
585 \r
586         if (graph.isInstanceOf(r, dr.Element) && !graph.isInstanceOf(r, dr.Connection) /*&& !graph.isInstanceOf(r, dr.Monitor)*/) {\r
587             double transform[] = graph.getPossibleRelatedValue(r, dr.HasTransform);\r
588             ElementClass ec = graph.syncRequest(DiagramRequests.getElementClass(graph.getSingleType(r, dr.Element), hints));\r
589             IElement e = Element.spawnNew(ec);\r
590             Rectangle2D bounds = ElementUtils.getElementBounds(e);\r
591             if (transform != null && bounds != null) {\r
592                 return new AlignElement(r, transform, bounds);\r
593             }\r
594         }\r
595         return null;\r
596     }\r
597 \r
598 \r
599     private static class AlignElement {\r
600         public Resource element;\r
601         public double[] transform;\r
602         public Rectangle2D bounds;\r
603         public Rectangle2D rotatedBounds;\r
604 //        public Rectangle2D transformedBounds;\r
605 \r
606         public AlignElement(Resource element, double[] transform, Rectangle2D bounds) {\r
607             this.element = element;\r
608             this.transform = transform;\r
609             this.bounds = bounds;\r
610 //            this.transformedBounds = getBounds();\r
611             this.rotatedBounds = getRotatedBounds();\r
612         }\r
613 \r
614 //        public Rectangle2D getBounds() {\r
615 //            AffineTransform at = new AffineTransform(transform[0], transform[1], transform[2], transform[3], transform[4], transform[5]);\r
616 //            return GeometryUtils.transformShape(bounds, at).getBounds2D();\r
617 //        }\r
618 \r
619         public Rectangle2D getRotatedBounds() {\r
620             AffineTransform at = new AffineTransform(transform[0], transform[1], transform[2], transform[3], 0.0, 0.0);\r
621             return GeometryUtils.transformShape(bounds, at).getBounds2D();\r
622         }\r
623 \r
624         public double getDeterminant() {\r
625             return transform[0] * transform[3] - transform[1] * transform[2];\r
626         }\r
627 \r
628     }\r
629 \r
630     private static class XComparator implements Comparator<AlignElement> {\r
631         @Override\r
632         public int compare(AlignElement o1, AlignElement o2) {\r
633             if (o1.transform[4] < o2.transform[4])\r
634                 return -1;\r
635             if (o1.transform[4] > o2.transform[4])\r
636                 return 1;\r
637             return 0;\r
638 \r
639         }\r
640     }\r
641 \r
642     private static class YComparator implements Comparator<AlignElement> {\r
643         @Override\r
644         public int compare(AlignElement o1, AlignElement o2) {\r
645             if (o1.transform[5] < o2.transform[5])\r
646                 return -1;\r
647             if (o1.transform[5] > o2.transform[5])\r
648                 return 1;\r
649             return 0;\r
650 \r
651         }\r
652     }\r
653 \r
654     /**\r
655      * Set 2D affine transforms for the listed diagram element resources.\r
656      * \r
657      * @param elements diagram element resources to set transforms for\r
658      * @param transforms transforms for each element\r
659      */\r
660     public static Write setTransformRequest(final Collection<TransformedObject> elements)\r
661     {\r
662         return new WriteRequest() {\r
663             @Override\r
664             public void perform(WriteGraph graph) throws DatabaseException {\r
665                 for (TransformedObject element : elements)\r
666                     DiagramGraphUtil.changeTransform(graph, element.element, element.transform);\r
667             }\r
668         };\r
669     }\r
670 \r
671     /**\r
672      * Set 2D affine transforms for the listed diagram element resources.\r
673      * \r
674      * @param undoContext the database undo context to use for the returned\r
675      *        request\r
676      * @param elements diagram element resources to set transforms for\r
677      * @param transforms transforms for each element\r
678      * @param preConcatenate <code>true</code> to pre-concatenate the\r
679      *        transforms, <code>false</code> to concatenate\r
680      */\r
681     public static Write concatenateTransformRequest(\r
682             UndoContext undoContext,\r
683             final Collection<TransformedObject> elements,\r
684             final boolean preConcatenate)\r
685     {\r
686         return new WriteRequest() {\r
687             @Override\r
688             public void perform(WriteGraph graph) throws DatabaseException {\r
689                 for (TransformedObject element : elements) {\r
690                     AffineTransform at = DiagramGraphUtil.getTransform(graph, element.element);\r
691                     if (preConcatenate)\r
692                         at.preConcatenate(element.transform);\r
693                     else\r
694                         at.concatenate(element.transform);\r
695                     DiagramGraphUtil.setTransform(graph, element.element, at);\r
696                 }\r
697             }\r
698         };\r
699     }\r
700 \r
701     public static class TransformedObject {\r
702         public final Resource        element;\r
703         public final AffineTransform transform;\r
704 \r
705         public TransformedObject(Resource element) {\r
706             this.element = element;\r
707             this.transform = new AffineTransform();\r
708         }\r
709         public TransformedObject(Resource element, AffineTransform transform) {\r
710             this.element = element;\r
711             this.transform = transform;\r
712         }\r
713     }\r
714 \r
715 }\r