1   /****************************************************************************
2    **
3    ** This file is part of yFiles-2.9. 
4    ** 
5    ** yWorks proprietary/confidential. Use is subject to license terms.
6    **
7    ** Redistribution of this file or of an unauthorized byte-code version
8    ** of this file is strictly forbidden.
9    **
10   ** Copyright (c) 2000-2011 by yWorks GmbH, Vor dem Kreuzberg 28, 
11   ** 72070 Tuebingen, Germany. All rights reserved.
12   **
13   ***************************************************************************/
14  package demo.view.flowchart.layout;
15  
16  import y.base.Edge;
17  import y.base.NodeCursor;
18  import y.geom.LineSegment;
19  import y.geom.YLineSegmentCursor;
20  import y.geom.YPoint;
21  import y.geom.YPointPath;
22  import y.geom.YRectangle;
23  import y.layout.DiscreteNodeLabelModel;
24  import y.layout.EdgeLabelLayout;
25  import y.layout.LabelCandidate;
26  import y.layout.LayoutGraph;
27  import y.layout.NodeLabelLayout;
28  import y.layout.NodeLabelModel;
29  import y.layout.ProfitModel;
30  
31  import java.util.HashMap;
32  import java.util.Map;
33  
34  /**
35   * A label profit model for the {@link FlowchartLayouter}.
36   */
37  class FlowchartLabelProfitModel implements ProfitModel {
38    private static final double MIN_PREFERRED_PLACEMENT_DISTANCE = 3.0;
39    private static final double MAX_PREFERRED_PLACEMENT_DISTANCE = 40.0;
40  
41    private final LayoutGraph graph;
42    private final Map label2OriginalBox;
43  
44    FlowchartLabelProfitModel(LayoutGraph graph) {
45      this.graph = graph;
46      this.label2OriginalBox = new HashMap();
47  
48      for (NodeCursor nc = graph.nodes(); nc.ok(); nc.next()) {
49        final NodeLabelLayout[] nll = graph.getLabelLayout(nc.node());
50  
51        for (int i = 0; i < nll.length; i++) {
52          final NodeLabelModel nlm = nll[i].getLabelModel();
53          if (nlm instanceof DiscreteNodeLabelModel) {
54            label2OriginalBox.put(nll[i], nll[i].getModelParameter());
55          }
56        }
57      }
58    }
59  
60    public double getProfit(LabelCandidate candidate) {
61      return candidate.getOwner() instanceof EdgeLabelLayout ?
62          calcEdgeLabelProfit(graph, candidate) :
63          calcNodeLabelProfit(candidate);
64    }
65  
66    private double calcNodeLabelProfit(LabelCandidate candidate) {
67      final NodeLabelLayout nl = (NodeLabelLayout) candidate.getOwner();
68  
69      if (nl.getLabelModel() instanceof DiscreteNodeLabelModel) {
70        final int pos = ((Number) candidate.getParameter()).intValue();
71        final int originalPos = ((Number) label2OriginalBox.get(nl)).intValue();
72  
73        if (pos == originalPos) {
74          return 1.0;
75        } else {
76          switch (pos) {
77            case DiscreteNodeLabelModel.NORTH:
78            case DiscreteNodeLabelModel.SOUTH:
79            case DiscreteNodeLabelModel.WEST:
80            case DiscreteNodeLabelModel.EAST:
81              return 0.95;
82            case DiscreteNodeLabelModel.NORTH_EAST:
83            case DiscreteNodeLabelModel.NORTH_WEST:
84            case DiscreteNodeLabelModel.SOUTH_EAST:
85            case DiscreteNodeLabelModel.SOUTH_WEST:
86              return 0.9;
87            default:
88              return 0.0;
89          }
90        }
91      } else {
92        return 0.0;
93      }
94    }
95  
96    private static double calcEdgeLabelProfit(LayoutGraph g, LabelCandidate candidate) {
97      final Edge e = g.getFeature((EdgeLabelLayout) candidate.getOwner());
98  
99      if (FlowchartElements.isRegularEdge(g, e)) {
100       final double eLength = calcPathLength(g.getPath(e));
101       final double maxPreferredPlacementDistance = Math.max(MAX_PREFERRED_PLACEMENT_DISTANCE, eLength * 0.2);
102       final double minDistToSource = getDistance(candidate.getBoundingBox(), g.getSourcePointAbs(e));
103 
104       if (minDistToSource > maxPreferredPlacementDistance) {
105         return 0.0;
106       } else if (minDistToSource < MIN_PREFERRED_PLACEMENT_DISTANCE) {
107         return 0.5;
108       } else {
109         return 1.0 - (minDistToSource / maxPreferredPlacementDistance);
110       }
111     } else {
112       return 0.0;
113     }
114   }
115 
116   static double calcPathLength(final YPointPath path) {
117     double length = 0.0;
118     for (YLineSegmentCursor cur = path.lineSegments(); cur.ok(); cur.next()) {
119       length += cur.lineSegment().length();
120     }
121     return length;
122   }
123 
124   static double getDistance(YRectangle r, YPoint q) {
125     if (r.contains(q)) {
126       return 0.0;
127     } else {
128       //determine corners of the rectangle
129       YPoint upperLeft = r.getLocation();
130       YPoint lowerLeft = new YPoint(upperLeft.x, upperLeft.y + r.getHeight());
131       YPoint lowerRight = new YPoint(lowerLeft.x + r.getWidth(), lowerLeft.y);
132       YPoint upperRight = new YPoint(lowerRight.x, upperLeft.y);
133 
134       //determine minDist to one of the four border segments
135       double minDist = Double.MAX_VALUE;
136       LineSegment rLeftSeg = new LineSegment(upperLeft, lowerLeft);
137       minDist = Math.min(minDist, getDistance(rLeftSeg, q));
138       LineSegment rRightSeg = new LineSegment(upperRight, lowerRight);
139       minDist = Math.min(minDist, getDistance(rRightSeg, q));
140       LineSegment rTopSeg = new LineSegment(upperLeft, upperRight);
141       minDist = Math.min(minDist, getDistance(rTopSeg, q));
142       LineSegment rBottomSeg = new LineSegment(lowerLeft, lowerRight);
143       minDist = Math.min(minDist, getDistance(rBottomSeg, q));
144       return minDist;
145     }
146   }
147 
148   static double getDistance(LineSegment line, YPoint q) {
149     final double x1 = line.getFirstEndPoint().x;
150     final double y1 = line.getFirstEndPoint().y;
151 
152     //adjust vectors relative to first endpoints of line
153     final double x2 = line.getSecondEndPoint().x - x1;
154     final double y2 = line.getSecondEndPoint().y - y1;
155     double pX = q.getX() - x1;
156     double pY = q.getY() - y1;
157 
158     //calculate distance
159     final double projSquaredDist;
160     if ((pX * x2 + pY * y2) <= 0.0) {
161       projSquaredDist = 0.0;
162     } else {
163       pX = x2 - pX;
164       pY = y2 - pY;
165       final double tmp = pX * x2 + pY * y2;
166       projSquaredDist = tmp <= 0.0 ? 0.0 : tmp * tmp / (x2 * x2 + y2 * y2);
167     }
168 
169     final double squaredDist = pX * pX + pY * pY - projSquaredDist;
170     return squaredDist < 0.0 ? 0.0 : Math.sqrt(squaredDist);
171   }
172 }
173