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