1
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
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 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 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 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 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