Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 18cc141fce4dce99d2f66f41936ac82dc766948b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
/*******************************************************************************
 * Copyright (c) 2015, 2017 THALES GLOBAL SERVICES and others
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Obeo - initial API and implementation
 *******************************************************************************/
package org.eclipse.sirius.diagram.ui.internal.edit.parts.locator;

import java.util.List;

import org.eclipse.draw2d.ConnectionLocator;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.PointList;
import org.eclipse.draw2d.geometry.PrecisionPoint;
import org.eclipse.draw2d.geometry.Straight;
import org.eclipse.draw2d.geometry.Transform;
import org.eclipse.draw2d.geometry.Vector;
import org.eclipse.gmf.runtime.diagram.ui.editparts.LabelEditPart;
import org.eclipse.gmf.runtime.diagram.ui.internal.util.LabelViewConstants;
import org.eclipse.gmf.runtime.draw2d.ui.geometry.LineSeg;
import org.eclipse.gmf.runtime.draw2d.ui.geometry.PointListUtilities;
import org.eclipse.gmf.runtime.notation.Bounds;
import org.eclipse.sirius.diagram.ui.business.internal.bracket.locators.BracketLabelLocator;
import org.eclipse.sirius.diagram.ui.internal.edit.parts.DEdgeBeginNameEditPart;
import org.eclipse.sirius.diagram.ui.internal.edit.parts.DEdgeEndNameEditPart;
import org.eclipse.sirius.diagram.ui.internal.edit.parts.DEdgeNameEditPart;
import org.eclipse.sirius.diagram.ui.part.SiriusVisualIDRegistry;
import org.eclipse.sirius.ext.base.Option;
import org.eclipse.sirius.ext.base.Options;

import com.google.common.base.Preconditions;

/**
 * Utility class used to compute the position of a label according to its edge
 * move (old points and new points list).
 *
 * @author <a href="mailto:laurent.fasani@obeo.fr">Laurent Fasani</a>
 * @author <a href="mailto:laurent.redor@obeo.fr">Laurent Redor</a>
 */
public class EdgeLabelQuery {

    private final static double DISTANCE_TOLERANCE = 0.001;

    /** Status for {@link #getSameLineStatus(Vector, LineSeg)} method. */
    private static final int NOT_ON_SAME_LINE = 0;

    /** Status for {@link #getSameLineStatus(Vector, LineSeg)} method. */
    private static final int ON_SAME_LINE_SAME_DIRECTION = 1;

    /** Status for {@link #getSameLineStatus(Vector, LineSeg)} method. */
    private static final int ON_SAME_LINE_OPPOSITE_DIRECTION = 2;

    /** BendPoint list before the edge modification. */
    private PointList oldBendPointList;

    /** BendPoint list corresponding to the edge modification. */
    private PointList newBendPointList;

    /** The routing status of edge on which the label is. */
    private boolean isEdgeWithObliqueRoutingStyle;

    /** The old offset of the label */
    private Point oldLabelOffset;

    /**
     * The keyPoint of the label (
     * {@link org.eclipse.gmf.runtime.diagram.ui.editparts.LabelEditPart#getKeyPoint()}
     * ).
     */
    private Integer keyPoint;

    private List<LineSeg> oldEdgeSegments;

    private List<LineSeg> newEdgeSegments;

    /**
     * True if the parent's label is a bracketEdge (specific locator for center
     * label and possibility to rotate the middle segment), false otherwise.
     */
    private boolean isOnBracketEdge;

    /** The size of the label. */
    private Dimension labelSize;

    /**
     * Return the default snap back position according to the keyPoint of the
     * label.
     *
     * @param keyPoint
     *            The keyPoint of the label (
     *            {@link org.eclipse.gmf.runtime.diagram.ui.editparts.LabelEditPart#getKeyPoint()}
     *            ).
     * @return the default snap back position according to the keyPoint.
     */
    public static Point getSnapBackPosition(Integer keyPoint) {
        int percentage = getLocation(keyPoint);
        Point snapBackPosition;
        if (LabelViewConstants.SOURCE_LOCATION == percentage) {
            snapBackPosition = LabelEditPart.getSnapBackPosition(SiriusVisualIDRegistry.getType(DEdgeBeginNameEditPart.VISUAL_ID));
        } else if (LabelViewConstants.TARGET_LOCATION == percentage) {
            snapBackPosition = LabelEditPart.getSnapBackPosition(SiriusVisualIDRegistry.getType(DEdgeEndNameEditPart.VISUAL_ID));
        } else {
            snapBackPosition = LabelEditPart.getSnapBackPosition(SiriusVisualIDRegistry.getType(DEdgeNameEditPart.VISUAL_ID));
        }
        return snapBackPosition;
    }

    /**
     * Calculates the label offset from the reference point given the label
     * center and a points list.<BR>
     * This is another implementation of
     * {@link org.eclipse.gmf.runtime.diagram.ui.internal.figures.LabelHelper#offsetFromRelativeCoordinate(org.eclipse.draw2d.IFigure, org.eclipse.draw2d.geometry.Rectangle, Point)}
     *
     * @param labelCenter
     *            the label center for the <code>IFigure</code> to calculate the
     *            offset for
     * @param points
     *            the <code>PointList</code> of the edge that contains the
     *            label. The label offset is relative to this
     *            <code>PointList</code>.
     * @param ref
     *            the <code>Point</code> that is the reference point that the
     *            offset is based on.
     * @return a <code>Point</code> which represents a value offset from the
     *         <code>ref</code> point oriented based on the nearest line
     *         segment.
     */
    public static Point offsetFromRelativeCoordinate(Point labelCenter, PointList points, Point ref) {
        Vector fromAnchorToLabelCenterPointVector = new Vector(labelCenter.x - ref.x, labelCenter.y - ref.y);
        @SuppressWarnings("rawtypes")
        List lineSegments = PointListUtilities.getLineSegments(points);
        LineSeg segmentContainingLabelAnchor = PointListUtilities.getNearestSegment(lineSegments, ref.x, ref.y);
        Vector rotatedVector = getRotatedVector(fromAnchorToLabelCenterPointVector, segmentContainingLabelAnchor, false);

        return new PrecisionPoint(rotatedVector.x, rotatedVector.y);
    }

    /**
     * Calculates the relative coordinate that is equivalent to the offset from
     * the reference point, that can be used to set the label center location.
     * <BR>
     *
     * This is another implementation of
     * {@link org.eclipse.gmf.runtime.diagram.ui.internal.figures.LabelHelper#relativeCoordinateFromOffset(org.eclipse.draw2d.IFigure, Point, Point)}
     * . See bugzilla 476305 for more detail.
     *
     * @param points
     *            the <code>PointList</code> of the edge that contains the
     *            label. The label offset is relative to this
     *            <code>PointList</code>.
     * @param ref
     *            a <code>Point</code> located on the parent which the offset
     *            value is relative to.
     * @param offset
     *            a <code>Point</code> which represents a value offset from the
     *            <code>ref</code> point oriented based on the nearest line
     *            segment.
     * @return a <code>Point</code> that is the relative coordinate of the label
     *         that can be used to set it's center location.
     */
    public static Point relativeCenterCoordinateFromOffset(PointList points, Point ref, Point offset) {
        Vector fromAnchorToLabelCenterPointVector = new Vector(offset.x, offset.y);
        @SuppressWarnings("rawtypes")
        List lineSegments = PointListUtilities.getLineSegments(points);
        LineSeg segmentContainingLabelAnchor = PointListUtilities.getNearestSegment(lineSegments, ref.x, ref.y);
        Vector rotatedVector = getRotatedVector(fromAnchorToLabelCenterPointVector, segmentContainingLabelAnchor, true);

        return new PrecisionPoint(ref.x + rotatedVector.x, ref.y + rotatedVector.y);
    }

    /**
     * Default constructor.
     *
     * @param oldBendPointList
     *            Bendpoint list before the edge modification
     * @param newBendPointList
     *            Bendpoint list after the edge modification
     * @param isEdgeWithObliqueRoutingStyle
     *            status of the edge from which to get the previous position of
     *            the bendpoints and from which to get the three labels
     * @param oldLabelOffset
     *            The old offset.
     * @param labelSize
     *            The size of the label
     * @param keyPoint
     *            The keyPoint of the label (
     *            {@link org.eclipse.gmf.runtime.diagram.ui.editparts.LabelEditPart#getKeyPoint()}
     *            )
     * @param isOnBracketEdge
     *            True if the parent's label is a bracketEdge (specific locator
     *            for center label and possibility to rotate the middle
     *            segment), false otherwise.
     */
    // @SuppressWarnings("unchecked")
    public EdgeLabelQuery(PointList oldBendPointList, PointList newBendPointList, boolean isEdgeWithObliqueRoutingStyle, Point oldLabelOffset, Dimension labelSize, Integer keyPoint,
            boolean isOnBracketEdge) {
        this.isEdgeWithObliqueRoutingStyle = isEdgeWithObliqueRoutingStyle;

        this.oldBendPointList = oldBendPointList;
        Preconditions.checkState(newBendPointList.size() > 0);
        this.newBendPointList = newBendPointList;
        this.oldLabelOffset = oldLabelOffset;
        this.labelSize = labelSize;
        this.keyPoint = keyPoint;
        this.isOnBracketEdge = isOnBracketEdge;

        // compute lineSegments from bendPoints
        oldEdgeSegments = PointListUtilities.getLineSegments(oldBendPointList);
        newEdgeSegments = PointListUtilities.getLineSegments(newBendPointList);
    }

    /**
     * Calculate the new GMF label offset: the label offset defines the position
     * of the label compared to labelAnchor point. <br>
     * The new Label offset is computed taking into account:<br>
     * <ul>
     * <li>the label anchor point move (start, center or end)</li>
     * <li>the orientation of the segment owning the label anchor. Indeed, the
     * label offset is displayed by draw2D relatively to the direction of the
     * edge segment including the anchor of the label</li>
     * <li>the expected move of the label according to the functional
     * specification</li>
     * </ul>
     * .
     *
     * @return the new offset of the label
     */
    public Point calculateGMFLabelOffset() {
        if (areBendpointsIdentical() && areSegmentsValid()) {
            return oldLabelOffset;
        } else {
            int anchorPointRatio = getLocation(keyPoint);
            Point oldAnchorPoint = getAnchorPoint(oldBendPointList, anchorPointRatio);
            Point oldLabelCenter = relativeCenterCoordinateFromOffset(oldBendPointList, oldAnchorPoint, oldLabelOffset);

            Point newAnchorPoint = getAnchorPoint(newBendPointList, anchorPointRatio);

            Point newLabelCenter = calculateNewCenterLocation(oldLabelCenter, getStandardLabelCenterLocation(newBendPointList, keyPoint));
            return offsetFromRelativeCoordinate(newLabelCenter, newBendPointList, newAnchorPoint);
        }
    }

    private Point getAnchorPoint(PointList pointList, int anchorPointRatio) {
        if (isOnBracketEdge && LabelViewConstants.MIDDLE_LOCATION == anchorPointRatio) {
            return BracketLabelLocator.getReferencePoint(pointList);
        } else {
            return PointListUtilities.calculatePointRelativeToLine(pointList, 0, anchorPointRatio, true);
        }
    }

    /**
     * Check if all segments of new and old points are valid (no segment with
     * same origin and terminus).
     *
     * @return true if segments are valid, false otherwise.
     */
    private boolean areSegmentsValid() {
        boolean areSegmentsValid = true;
        for (LineSeg lineSeg : newEdgeSegments) {
            if (lineSeg.getOrigin().equals(lineSeg.getTerminus())) {
                areSegmentsValid = false;
                break;
            }
        }

        if (areSegmentsValid) {
            for (LineSeg lineSeg : oldEdgeSegments) {
                if (lineSeg.getOrigin().equals(lineSeg.getTerminus())) {
                    areSegmentsValid = false;
                    break;
                }
            }
        }
        return areSegmentsValid;
    }

    /**
     * Check if the old points and the new one are the same.
     *
     * @return true if the old points are the same as the new, false otherwise.
     */
    private boolean areBendpointsIdentical() {
        boolean areBendpointsIdentical = true;
        if (newBendPointList.size() == oldBendPointList.size()) {
            for (int i = 0; i < newBendPointList.size(); i++) {
                Point newPoint = newBendPointList.getPoint(i);
                Point oldPoint = oldBendPointList.getPoint(i);
                if (!newPoint.equals(oldPoint)) {
                    areBendpointsIdentical = false;
                    break;
                }
            }
        } else {
            areBendpointsIdentical = false;
        }

        return areBendpointsIdentical;
    }

    /**
     * Calculate the new center location of the label according to functional
     * specification.
     *
     * @param oldCenterLabel
     *            The old center location of the label.
     * @param newDefaultLocation
     *            The standard label location according to the label keyPoint (
     *            {@link org.eclipse.gmf.runtime.diagram.ui.editparts.LabelEditPart#getKeyPoint()}
     *            and the default snap back position (
     *            {@link LabelEditPart#getSnapBackPosition(String)}.
     * @return
     */
    private Point calculateNewCenterLocation(Point oldCenterLabel, Point newDefaultLocation) {
        Vector fromOldToNewCenterVector = null;

        // Step 1 : Calculate old reference point (the nearest point on the edge
        // from the center of the label).
        LineSeg oldNearestSeg = PointListUtilities.getNearestSegment(oldEdgeSegments, oldCenterLabel.x, oldCenterLabel.y);
        Point oldNearestPoint;
        // If old nearest segment is horizontal or vertical, we don't have to
        // use LineSeg.perpIntersect method.
        if (oldNearestSeg.isHorizontal()) {
            oldNearestPoint = new PrecisionPoint(oldCenterLabel.preciseX(), oldNearestSeg.getOrigin().preciseY());
        } else if (oldNearestSeg.isVertical()) {
            oldNearestPoint = new PrecisionPoint(oldNearestSeg.getOrigin().preciseX(), oldCenterLabel.preciseY());
        } else {
            oldNearestPoint = oldNearestSeg.perpIntersect(oldCenterLabel.x, oldCenterLabel.y);
        }

        // Step 2 : Is there a new segment and an old segment on the same line?
        // Case of segment increased or decreased (and eventually inverted)
        Option<Vector> fromOldToNewRefPoint = getVectorFromOldToNewForSegmentsOnSameLine(oldNearestSeg, oldNearestPoint, oldCenterLabel);
        if (fromOldToNewRefPoint.some()) {
            // In this case the vector for the reference point is the same than
            // for the label center.
            fromOldToNewCenterVector = fromOldToNewRefPoint.get();
        } else { // No identical segment line has been found
            // RECTILINEAR and TREE routing
            if (!isEdgeWithObliqueRoutingStyle) {
                // Get projection of oldNearestPoint on newSegments along
                // oldRefVector
                LineSeg oldRefVectorIntoSegment = null;
                if (oldCenterLabel.equals(oldNearestPoint)) {
                    // Get a segment perpendicular to oldRefSegment going
                    // through oldNearestPoint
                    oldRefVectorIntoSegment = new LineSeg(oldNearestPoint, new PrecisionPoint(oldNearestPoint.x + (oldNearestSeg.getOrigin().y - oldNearestSeg.getTerminus().y),
                            oldNearestPoint.y - (oldNearestSeg.getOrigin().x - oldNearestSeg.getTerminus().x)));
                } else {
                    oldRefVectorIntoSegment = new LineSeg(oldCenterLabel, oldNearestPoint);
                }

                // Is there a new segment at the same index as old segment and
                // with same axis? Case of rectilinear segment move.
                fromOldToNewCenterVector = getVectorForSegmentMoveCase(oldNearestSeg, oldNearestPoint, oldCenterLabel);
                if (fromOldToNewCenterVector == null && isOnBracketEdge) {
                    // Is there a new segment at the same index as old segment
                    // and with opposite axis? Case of change orientation of the
                    // bracket edge
                    fromOldToNewCenterVector = getVectorForBracketEdgeOrientationChangeCase(oldNearestSeg, oldNearestPoint, oldCenterLabel);
                }
                if (fromOldToNewCenterVector == null) {
                    for (LineSeg lineSeg : newEdgeSegments) {
                        PointList linesIntersections = oldRefVectorIntoSegment.getLinesIntersections(lineSeg);
                        // intersection should be, at more, one point
                        if (linesIntersections.size() == 1 && lineSeg.distanceToPoint(linesIntersections.getPoint(0).x, linesIntersections.getPoint(0).y) <= Math.sqrt(2)) {
                            Vector tempLabelMove = new Vector(linesIntersections.getPoint(0).x - oldNearestPoint.x, linesIntersections.getPoint(0).y - oldNearestPoint.y);
                            if (fromOldToNewCenterVector == null || tempLabelMove.getLength() < fromOldToNewCenterVector.getLength()) {
                                fromOldToNewCenterVector = tempLabelMove;
                            }
                        }
                    }
                    // Compare the minimalLabelMove with the default location.
                    // If the default location is nearer reset the labelMove.
                    Vector fromOldNearestPointToStandardLocation = new Vector(newDefaultLocation.x - oldNearestPoint.x, newDefaultLocation.y - oldNearestPoint.y);
                    if (fromOldToNewCenterVector == null || fromOldNearestPointToStandardLocation.getLength() < fromOldToNewCenterVector.getLength()) {
                        fromOldToNewCenterVector = null;
                    }
                }
            } else if (newEdgeSegments.size() == oldEdgeSegments.size()) {
                // The newNearestSegment has the same index in newEdgeSegments
                // than oldNearestSegment in oldEdgeSegments
                LineSeg newRefSeg = newEdgeSegments.get(oldEdgeSegments.indexOf(oldNearestSeg));
                // Keep ratio on segment for newRefPoint
                double oldRatio = oldNearestSeg.projection(oldCenterLabel.x, oldCenterLabel.y);
                Point refPointOnNewSegWithOldRatio = new PrecisionPoint(newRefSeg.getOrigin().x + oldRatio * (newRefSeg.getTerminus().x - newRefSeg.getOrigin().x),
                        newRefSeg.getOrigin().y + oldRatio * (newRefSeg.getTerminus().y - newRefSeg.getOrigin().y));
                fromOldToNewCenterVector = new Vector(refPointOnNewSegWithOldRatio.x - oldNearestPoint.x, refPointOnNewSegWithOldRatio.y - oldNearestPoint.y);
                if (oldRatio < 0 || 1 < oldRatio) {
                    // If the old label location is outside of the segment, we
                    // ensure that the distance between the label and the edge
                    // is not higher than before. In this case, we reset the
                    // location to its default.
                    double oldDistance = oldNearestSeg.distanceToPoint(oldCenterLabel.x(), oldCenterLabel.y());
                    Point potentialNewCenter = oldCenterLabel.getTranslated(fromOldToNewCenterVector.x, fromOldToNewCenterVector.y);
                    double newDistance = newRefSeg.distanceToPoint(potentialNewCenter.x(), potentialNewCenter.y());
                    if (newDistance > oldDistance) {
                        fromOldToNewCenterVector = null;
                    }
                }
            }
        }
        if (fromOldToNewCenterVector == null) {
            return newDefaultLocation;
        } else {
            return oldCenterLabel.getTranslated(fromOldToNewCenterVector.x, fromOldToNewCenterVector.y);
        }
    }

    /**
     * Check if we are in case of a rectilinear segment move: there is a new
     * segment at the same index as old nearest segment and with the same axis.
     * Return the corresponding vector from old to new center in this case, null
     * otherwise.
     *
     * @param oldNearestSeg
     *            The segment that is the nearest from the center of the label
     *            in the old points list.
     * @return the corresponding vector from old to new center in case of
     *         rectilinear segment move, null otherwise.
     */
    private Vector getVectorForSegmentMoveCase(LineSeg oldNearestSeg, Point oldNearestPoint, Point oldCenterLabel) {
        Vector fromOldToNewCenterVector = null;
        if (newEdgeSegments.size() == oldEdgeSegments.size()) {
            int index = oldEdgeSegments.indexOf(oldNearestSeg);
            LineSeg newNearestSegment = newEdgeSegments.get(index);
            if (oldNearestSeg.isHorizontal() == newNearestSegment.isHorizontal()) {
                Vector oldVector = new Vector(oldNearestSeg.getTerminus().x - oldNearestSeg.getOrigin().x, oldNearestSeg.getTerminus().y - oldNearestSeg.getOrigin().y);
                Vector newVector = new Vector(newNearestSegment.getTerminus().x - newNearestSegment.getOrigin().x, newNearestSegment.getTerminus().y - newNearestSegment.getOrigin().y);
                boolean oppositeDirection = false;
                if (oldVector.getLength() != 0 && newVector.getLength() != 0) {
                    oppositeDirection = oldVector.getAngle(newVector) == 180;
                }
                fromOldToNewCenterVector = applyOldRatioOnNewSegment(oldNearestSeg, oldNearestPoint, oldCenterLabel, newNearestSegment, oppositeDirection, false);
            }
        }
        return fromOldToNewCenterVector;
    }

    /**
     * Check if we are in case of a orientation segment change: there is a new
     * segment at the same index as old nearest segment and with apposite axis.
     * Return the corresponding vector from old to new center in this case, null
     * otherwise.
     *
     * @param oldNearestSeg
     *            The segment that is the nearest from the center of the label
     *            in the old points list.
     * @return the corresponding vector from old to new center in case of
     *         orientation segment change, null otherwise.
     */
    private Vector getVectorForBracketEdgeOrientationChangeCase(LineSeg oldNearestSeg, Point oldNearestPoint, Point oldCenterLabel) {
        Vector fromOldToNewCenterVector = null;
        if (newEdgeSegments.size() == oldEdgeSegments.size()) {
            int index = oldEdgeSegments.indexOf(oldNearestSeg);
            LineSeg newNearestSegment = newEdgeSegments.get(index);
            if (oldNearestSeg.isHorizontal() != newNearestSegment.isHorizontal()) {
                Vector oldVector = new Vector(oldNearestSeg.getTerminus().x - oldNearestSeg.getOrigin().x, oldNearestSeg.getTerminus().y - oldNearestSeg.getOrigin().y);
                Vector newVector = new Vector(newNearestSegment.getTerminus().x - newNearestSegment.getOrigin().x, newNearestSegment.getTerminus().y - newNearestSegment.getOrigin().y);
                double angleInDegree = Math.toDegrees(angleBetween2Lines(oldVector, newVector));
                fromOldToNewCenterVector = applyOldRatioOnNewOrthogonalSegment(oldNearestSeg, oldNearestPoint, oldCenterLabel, newNearestSegment, angleInDegree == 90 || angleInDegree == -270);
            }
        }
        return fromOldToNewCenterVector;
    }

    private Option<Vector> getVectorFromOldToNewForSegmentsOnSameLine(LineSeg oldRefSeg, Point oldRefPoint, Point oldCenterLabel) {
        Option<Vector> result = Options.newNone();
        LineSeg newSegmentOnSameLineWithSameDirection = null;
        LineSeg newSegmentOnSameLineWithOppositeDirection = null;
        // Firstly, for points lists with same nb of segments, search if the
        // vector, at
        // the same index, is on the same line
        int sameLineStatus = NOT_ON_SAME_LINE;
        if (newEdgeSegments.size() == oldEdgeSegments.size()) {
            LineSeg newSegAtSameIndex = newEdgeSegments.get(oldEdgeSegments.indexOf(oldRefSeg));
            sameLineStatus = getSameLineStatus(oldRefSeg, newSegAtSameIndex);
            if (ON_SAME_LINE_SAME_DIRECTION == sameLineStatus) {
                newSegmentOnSameLineWithSameDirection = newSegAtSameIndex;
            } else if (ON_SAME_LINE_OPPOSITE_DIRECTION == sameLineStatus) {
                newSegmentOnSameLineWithOppositeDirection = newSegAtSameIndex;
            }
        }
        if (NOT_ON_SAME_LINE == sameLineStatus) {
            // If this is not the case search on all new segments.
            for (LineSeg newSeg : newEdgeSegments) {
                sameLineStatus = getSameLineStatus(oldRefSeg, newSeg);
                if (ON_SAME_LINE_SAME_DIRECTION == sameLineStatus) {
                    newSegmentOnSameLineWithSameDirection = newSeg;
                    break;
                } else if (ON_SAME_LINE_OPPOSITE_DIRECTION == sameLineStatus) {
                    newSegmentOnSameLineWithOppositeDirection = newSeg;
                    // Continue to search a potential segment in the same
                    // direction.
                }
            }
        }

        LineSeg newRefSeg = newSegmentOnSameLineWithSameDirection;
        if (newRefSeg == null) {
            newRefSeg = newSegmentOnSameLineWithOppositeDirection;
        }

        if (newRefSeg != null) {
            result = Options.newSome(applyOldRatioOnNewSegment(oldRefSeg, oldRefPoint, oldCenterLabel, newRefSeg, newSegmentOnSameLineWithOppositeDirection != null, true));
        }
        return result;
    }

    /**
     * Check if the <code>segment</code> is on the same line as the
     * <code>referenceSegment</code> and if it is in the same direction or not.
     *
     * @param referenceSegment
     *            The reference segment.
     * @param segment
     *            The segment to test
     * @return one of these statuses {@link #NOT_ON_SAME_LINE},
     *         {@link #ON_SAME_LINE_SAME_DIRECTION} or
     *         {@link #ON_SAME_LINE_OPPOSITE_DIRECTION}.
     */
    private int getSameLineStatus(LineSeg referenceSegment, LineSeg segment) {
        int result = NOT_ON_SAME_LINE;
        if (segment.length() != 0) {
            Vector referenceVector = new Vector(referenceSegment.getTerminus().x - referenceSegment.getOrigin().x, referenceSegment.getTerminus().y - referenceSegment.getOrigin().y);
            Vector vector = new Vector(segment.getTerminus().x - segment.getOrigin().x, segment.getTerminus().y - segment.getOrigin().y);
            if (referenceVector.getLength() == 0 || vector.getLength() == 0) {
                if ((vector.getLength() == 0 && referenceSegment.containsPoint(segment.getOrigin(), 0))
                        || (referenceVector.getLength() == 0 && segment.containsPoint(referenceSegment.getOrigin(), 0))) {
                    result = ON_SAME_LINE_SAME_DIRECTION;
                }
            } else {
                double angle = referenceVector.getAngle(vector);
                if (angle == 0 || angle == 180) {
                    Straight straight = new Straight(new PrecisionPoint(segment.getOrigin()), new PrecisionPoint(segment.getTerminus()));
                    double distToInfiniteLine = straight.getDistance(new Vector(referenceSegment.getOrigin().x, referenceSegment.getOrigin().y));
                    if (distToInfiniteLine < DISTANCE_TOLERANCE) {
                        if (angle == 180) {
                            result = ON_SAME_LINE_OPPOSITE_DIRECTION;
                        } else {
                            result = ON_SAME_LINE_SAME_DIRECTION;
                        }
                    }
                }
            }
        }
        return result;
    }

    private Vector applyOldRatioOnNewSegment(LineSeg oldRefSeg, Point oldRefPoint, Point oldCenterLabel, LineSeg newRefSeg, boolean oppositeDirection, boolean sameLine) {
        Vector result;
        double newRatio = newRefSeg.projection(oldCenterLabel.x, oldCenterLabel.y);
        if (sameLine && newRatio >= 0 && newRatio <= 1) {
            // If the orthogonal projection is inside segment (between 0 and
            // 1), the reference point does not move.
            result = new Vector(0, 0);
        } else {
            Point newRefPoint;
            double oldRatio = oldRefSeg.projection(oldCenterLabel.x, oldCenterLabel.y);
            if (!oppositeDirection) {
                newRefPoint = new PrecisionPoint(newRefSeg.getOrigin().x + oldRatio * (newRefSeg.getTerminus().x - newRefSeg.getOrigin().x),
                        newRefSeg.getOrigin().y + oldRatio * (newRefSeg.getTerminus().y - newRefSeg.getOrigin().y));
            } else {
                newRefPoint = new PrecisionPoint(newRefSeg.getOrigin().x - oldRatio * (newRefSeg.getOrigin().x - newRefSeg.getTerminus().x),
                        newRefSeg.getOrigin().y - oldRatio * (newRefSeg.getOrigin().y - newRefSeg.getTerminus().y));
            }
            if (!sameLine && newRatio >= 0 && newRatio <= 1) {
                // If the orthogonal projection is inside segment (between 0 and
                // 1), we keep the oldRefPoint one axis
                if (newRefSeg.isHorizontal()) {
                    newRefPoint.setX(oldRefPoint.x);
                } else {
                    newRefPoint.setY(oldRefPoint.y);
                }
            }
            Vector vectorFromOldToNewRefPoint = new Vector(newRefPoint.x - oldRefPoint.x, newRefPoint.y - oldRefPoint.y);
            if (oldRatio >= 0 && oldRatio <= 1) {
                // Keep ratio on segment for newRefPoint (if it was
                // previously inside segment)
                result = vectorFromOldToNewRefPoint;
            } else {
                // If the label is previously outside of the segment, we
                // keep the shortest point (new or old one).
                Point potentialNewCenter = oldCenterLabel.getTranslated(vectorFromOldToNewRefPoint.x, vectorFromOldToNewRefPoint.y);
                if ((newRatio > 1 && newRatio < newRefSeg.projection(potentialNewCenter.x, potentialNewCenter.y))
                        || (newRatio < 0 && newRatio > newRefSeg.projection(potentialNewCenter.x, potentialNewCenter.y))) {
                    result = new Vector(0, 0);
                } else {
                    result = vectorFromOldToNewRefPoint;
                }
            }
        }
        return result;
    }

    private Vector applyOldRatioOnNewOrthogonalSegment(LineSeg oldRefSeg, Point oldRefPoint, Point oldCenterLabel, LineSeg newRefSeg, boolean is90Angle) {
        double oldRatio = oldRefSeg.projection(oldCenterLabel.x, oldCenterLabel.y);
        Transform rotateTransform = new Transform();
        // Get the new reference point and the rotation to apply
        Point newRefPoint;
        if (is90Angle) {
            // As GMF coordinates system is reversed (y positive orientation is
            // from top to bottom), we reverse the rotation to apply
            rotateTransform.setRotation(Math.toRadians(-90));
            if (0 <= oldRatio && oldRatio <= 1) {
                // Apply same ratio on the new segment to compute the new
                // reference point
                newRefPoint = new PrecisionPoint(newRefSeg.getOrigin().x - oldRatio * (newRefSeg.getOrigin().x - newRefSeg.getTerminus().x),
                        newRefSeg.getOrigin().y - oldRatio * (newRefSeg.getOrigin().y - newRefSeg.getTerminus().y));
            } else if (oldRatio > 1) {
                // Just apply the vector from old terminus to old reference
                // point to the new terminus
                Vector vectorFromOldTerminusToOldRefPoint = new Vector(oldRefPoint.x - oldRefSeg.getTerminus().x, oldRefPoint.y - oldRefSeg.getTerminus().y);
                Point vectorFromNewTerminusToNewRefPoint = rotateTransform.getTransformed(vectorFromOldTerminusToOldRefPoint.toPoint());
                newRefPoint = newRefSeg.getTerminus().getTranslated(vectorFromNewTerminusToNewRefPoint);
            } else {
                Vector vectorFromOldOriginToOldRefPoint = new Vector(oldRefPoint.x - oldRefSeg.getOrigin().x, oldRefPoint.y - oldRefSeg.getOrigin().y);
                Point vectorFromNewOriginToNewRefPoint = rotateTransform.getTransformed(vectorFromOldOriginToOldRefPoint.toPoint());
                newRefPoint = newRefSeg.getOrigin().getTranslated(vectorFromNewOriginToNewRefPoint);
            }
        } else {
            // As GMF coordinates system is reversed (y positive orientation is
            // from top to bottom), we reverse the rotation to apply
            rotateTransform.setRotation(Math.toRadians(90));
            if (0 <= oldRatio && oldRatio <= 1) {
                // Apply same ratio on the new segment to compute the new
                // reference point (but inverse the origin and the terminus)
                newRefPoint = new PrecisionPoint(newRefSeg.getOrigin().x + oldRatio * (newRefSeg.getTerminus().x - newRefSeg.getOrigin().x),
                        newRefSeg.getOrigin().y + oldRatio * (newRefSeg.getTerminus().y - newRefSeg.getOrigin().y));
            } else if (oldRatio > 1) {
                // Just apply the vector from old terminus to old reference
                // point to the new terminus
                Vector vectorFromOldTerminusToOldRefPoint = new Vector(oldRefPoint.x - oldRefSeg.getTerminus().x, oldRefPoint.y - oldRefSeg.getTerminus().y);
                Point vectorFromNewTerminusToNewRefPoint = rotateTransform.getTransformed(vectorFromOldTerminusToOldRefPoint.toPoint());
                newRefPoint = newRefSeg.getTerminus().getTranslated(vectorFromNewTerminusToNewRefPoint);
            } else {
                Vector vectorFromOldOriginToOldRefPoint = new Vector(oldRefPoint.x - oldRefSeg.getOrigin().x, oldRefPoint.y - oldRefSeg.getOrigin().y);
                Point vectorFromNewOriginToNewRefPoint = rotateTransform.getTransformed(vectorFromOldOriginToOldRefPoint.toPoint());
                newRefPoint = newRefSeg.getOrigin().getTranslated(vectorFromNewOriginToNewRefPoint);
            }
        }
        Vector vectorFromOldRefPointToOldCenterLabel = new Vector(oldCenterLabel.x - oldRefPoint.x, oldCenterLabel.y - oldRefPoint.y);
        Point fromNewRefPointToNewCenterLabel = rotateTransform.getTransformed(vectorFromOldRefPointToOldCenterLabel.toPoint());
        // Adjust the vector to apply according to orientation change (width
        // becomes height and conversely)
        if (newRefSeg.isHorizontal()) {
            if (0 <= oldRatio && oldRatio <= 1) {
                int invertedWidthHeight = labelSize.width - labelSize.height;
                if (newRefSeg.getOrigin().x > newRefSeg.getTerminus().x) {
                    invertedWidthHeight = -invertedWidthHeight;
                }
                fromNewRefPointToNewCenterLabel.translate(0, invertedWidthHeight / 2);
            } else if (oldRatio > 1) {
                int invertedWidthHeight = labelSize.width - labelSize.height;
                if (newRefSeg.getOrigin().x > newRefSeg.getTerminus().x) {
                    invertedWidthHeight = -invertedWidthHeight;
                }
                fromNewRefPointToNewCenterLabel.translate(invertedWidthHeight / 2, 0);
            } else {
                fromNewRefPointToNewCenterLabel.translate(0, 0);
                int invertedWidthHeight = labelSize.width - labelSize.height;
                if (newRefSeg.getOrigin().x < newRefSeg.getTerminus().x) {
                    invertedWidthHeight = -invertedWidthHeight;
                }
                fromNewRefPointToNewCenterLabel.translate(invertedWidthHeight / 2, 0);
            }
        } else {
            if (0 <= oldRatio && oldRatio <= 1) {
                int invertedWidthHeight = labelSize.width - labelSize.height;
                if (newRefSeg.getOrigin().y > newRefSeg.getTerminus().y) {
                    invertedWidthHeight = -invertedWidthHeight;
                }
                fromNewRefPointToNewCenterLabel.translate(invertedWidthHeight / 2, 0);
            } else if (oldRatio > 1) {
                int invertedWidthHeight = labelSize.height - labelSize.width;
                if (newRefSeg.getOrigin().y > newRefSeg.getTerminus().y) {
                    invertedWidthHeight = -invertedWidthHeight;
                }
                fromNewRefPointToNewCenterLabel.translate(0, invertedWidthHeight / 2);
            } else {
                int invertedWidthHeight = labelSize.height - labelSize.width;
                if (newRefSeg.getOrigin().y < newRefSeg.getTerminus().y) {
                    invertedWidthHeight = -invertedWidthHeight;
                }
                fromNewRefPointToNewCenterLabel.translate(0, invertedWidthHeight / 2);
            }
        }
        // Apply the rotated vector on new reference point to have the new
        // center
        Point newCenterLabel = newRefPoint.getTranslated(fromNewRefPointToNewCenterLabel);
        // Compute the vector from old center to new center
        return new Vector(newCenterLabel.x - oldCenterLabel.x, newCenterLabel.y - oldCenterLabel.y);
    }

    /**
     * Get the rotated vector according to the segment orientation.
     *
     * @param vector
     *            vector to be rotated
     * @param segment
     *            reference segment
     * @param inverseRotation
     *            if true, inverse rotation
     * @return the rotated Vector
     */
    private static Vector getRotatedVector(Vector vector, LineSeg segment, boolean inverseRotation) {
        Vector result = new Vector(vector.x, vector.y);
        if (vector.x != 0 || vector.y != 0) {
            double angle = angleBetween2Lines(new LineSeg(new Point(0, 0), new Point(1, 0)), segment) * (inverseRotation ? -1 : 1);
            Transform rotateTransform = new Transform();
            rotateTransform.setRotation(angle);
            Point rotatedPoint = rotateTransform.getTransformed(vector.toPoint());

            result.x = rotatedPoint.x;
            result.y = rotatedPoint.y;
        }
        return result;
    }

    /**
     * Get the signed angle between two segments.
     *
     * @param line1
     * @param line2
     * @return the signed angle in radian.
     */
    private static double angleBetween2Lines(LineSeg line1, LineSeg line2) {
        if (line1 == null || line2 == null) {
            return 0;
        }
        double angle1 = Math.atan2(line1.getOrigin().y - line1.getTerminus().y, line1.getOrigin().x - line1.getTerminus().x);
        double angle2 = Math.atan2(line2.getOrigin().y - line2.getTerminus().y, line2.getOrigin().x - line2.getTerminus().x);
        return angle1 - angle2;
    }

    /**
     * Get the signed angle between two vectors.
     *
     * @param vector1
     *            The first vector
     * @param vector2
     *            The second vector
     * @return the signed angle in radian.
     */
    private static double angleBetween2Lines(Vector vector1, Vector vector2) {
        if (vector1 == null || vector2 == null) {
            return 0;
        }
        double angle1 = Math.atan2(vector1.y, vector1.x);
        double angle2 = Math.atan2(vector2.y, vector2.x);
        return angle1 - angle2;
    }

    /**
     * Get the standard center location according to the label keyPoint (
     * {@link org.eclipse.gmf.runtime.diagram.ui.editparts.LabelEditPart#getKeyPoint()}
     * ) and the default snap back position (
     * {@link LabelEditPart#getSnapBackPosition(String)}
     *
     * @param pointsList
     *            The points of the edge of the label.
     * @param keyPoint
     *            The keyPoint of the label (
     *            {@link org.eclipse.gmf.runtime.diagram.ui.editparts.LabelEditPart#getKeyPoint()}
     *            ).
     * @return The center of the label {@link Bounds} if this label is located
     *         by default.
     */
    private Point getStandardLabelCenterLocation(PointList pointsList, Integer keyPoint) {
        int percentage = getLocation(keyPoint);
        Point newAnchorPoint = getAnchorPoint(pointsList, percentage);
        Point snapBackPosition = getSnapBackPosition(keyPoint);
        Point standardLabelCenter = newAnchorPoint.getTranslated(snapBackPosition);
        return standardLabelCenter;
    }

    /**
     * Get the location among {@link LabelViewConstants} constants where to
     * relocate the label figure.
     *
     * @return the location among {@link LabelViewConstants} constants
     */
    private static int getLocation(Integer keyPoint) {
        int location = LabelViewConstants.MIDDLE_LOCATION;
        switch (keyPoint) {
        case ConnectionLocator.SOURCE:
            location = LabelViewConstants.TARGET_LOCATION;
            break;
        case ConnectionLocator.TARGET:
            location = LabelViewConstants.SOURCE_LOCATION;
            break;
        case ConnectionLocator.MIDDLE:
            location = LabelViewConstants.MIDDLE_LOCATION;
            break;
        default:
            location = LabelViewConstants.MIDDLE_LOCATION;
            break;
        }
        return location;
    }
}

Back to the top