1
28 package demo.view.flowchart.layout;
29
30 import y.base.Edge;
31 import y.base.NodeCursor;
32 import y.geom.LineSegment;
33 import y.geom.YLineSegmentCursor;
34 import y.geom.YPoint;
35 import y.geom.YPointPath;
36 import y.geom.YRectangle;
37 import y.layout.DiscreteNodeLabelModel;
38 import y.layout.EdgeLabelLayout;
39 import y.layout.LabelCandidate;
40 import y.layout.LayoutGraph;
41 import y.layout.NodeLabelLayout;
42 import y.layout.NodeLabelModel;
43 import y.layout.ProfitModel;
44
45 import java.util.HashMap;
46 import java.util.Map;
47
48
51 class FlowchartLabelProfitModel implements ProfitModel {
52 private static final double MIN_PREFERRED_PLACEMENT_DISTANCE = 3.0;
53 private static final double MAX_PREFERRED_PLACEMENT_DISTANCE = 40.0;
54
55 private final LayoutGraph graph;
56 private final Map label2OriginalBox;
57
58 FlowchartLabelProfitModel(LayoutGraph graph) {
59 this.graph = graph;
60 this.label2OriginalBox = new HashMap();
61
62 for (NodeCursor nc = graph.nodes(); nc.ok(); nc.next()) {
63 final NodeLabelLayout[] nll = graph.getLabelLayout(nc.node());
64
65 for (int i = 0; i < nll.length; i++) {
66 final NodeLabelModel nlm = nll[i].getLabelModel();
67 if (nlm instanceof DiscreteNodeLabelModel) {
68 label2OriginalBox.put(nll[i], nll[i].getModelParameter());
69 }
70 }
71 }
72 }
73
74 public double getProfit(LabelCandidate candidate) {
75 return candidate.getOwner() instanceof EdgeLabelLayout ?
76 calcEdgeLabelProfit(graph, candidate) :
77 calcNodeLabelProfit(candidate);
78 }
79
80 private double calcNodeLabelProfit(LabelCandidate candidate) {
81 final NodeLabelLayout nl = (NodeLabelLayout) candidate.getOwner();
82
83 if (nl.getLabelModel() instanceof DiscreteNodeLabelModel) {
84 final int pos = ((Number) candidate.getParameter()).intValue();
85 final int originalPos = ((Number) label2OriginalBox.get(nl)).intValue();
86
87 if (pos == originalPos) {
88 return 1.0;
89 } else {
90 switch (pos) {
91 case DiscreteNodeLabelModel.NORTH:
92 case DiscreteNodeLabelModel.SOUTH:
93 case DiscreteNodeLabelModel.WEST:
94 case DiscreteNodeLabelModel.EAST:
95 return 0.95;
96 case DiscreteNodeLabelModel.NORTH_EAST:
97 case DiscreteNodeLabelModel.NORTH_WEST:
98 case DiscreteNodeLabelModel.SOUTH_EAST:
99 case DiscreteNodeLabelModel.SOUTH_WEST:
100 return 0.9;
101 default:
102 return 0.0;
103 }
104 }
105 } else {
106 return 0.0;
107 }
108 }
109
110 private static double calcEdgeLabelProfit(LayoutGraph g, LabelCandidate candidate) {
111 final Edge e = g.getFeature((EdgeLabelLayout) candidate.getOwner());
112
113 if (FlowchartElements.isRegularEdge(g, e)) {
114 final double eLength = calcPathLength(g.getPath(e));
115 final double maxPreferredPlacementDistance = Math.max(MAX_PREFERRED_PLACEMENT_DISTANCE, eLength * 0.2);
116 final double minDistToSource = getDistance(candidate.getBoundingBox(), g.getSourcePointAbs(e));
117
118 if (minDistToSource > maxPreferredPlacementDistance) {
119 return 0.0;
120 } else if (minDistToSource < MIN_PREFERRED_PLACEMENT_DISTANCE) {
121 return 0.5;
122 } else {
123 return 1.0 - (minDistToSource / maxPreferredPlacementDistance);
124 }
125 } else {
126 return 0.0;
127 }
128 }
129
130 static double calcPathLength(final YPointPath path) {
131 double length = 0.0;
132 for (YLineSegmentCursor cur = path.lineSegments(); cur.ok(); cur.next()) {
133 length += cur.lineSegment().length();
134 }
135 return length;
136 }
137
138 static double getDistance(YRectangle r, YPoint q) {
139 if (r.contains(q)) {
140 return 0.0;
141 } else {
142 YPoint upperLeft = r.getLocation();
144 YPoint lowerLeft = new YPoint(upperLeft.x, upperLeft.y + r.getHeight());
145 YPoint lowerRight = new YPoint(lowerLeft.x + r.getWidth(), lowerLeft.y);
146 YPoint upperRight = new YPoint(lowerRight.x, upperLeft.y);
147
148 double minDist = Double.MAX_VALUE;
150 LineSegment rLeftSeg = new LineSegment(upperLeft, lowerLeft);
151 minDist = Math.min(minDist, getDistance(rLeftSeg, q));
152 LineSegment rRightSeg = new LineSegment(upperRight, lowerRight);
153 minDist = Math.min(minDist, getDistance(rRightSeg, q));
154 LineSegment rTopSeg = new LineSegment(upperLeft, upperRight);
155 minDist = Math.min(minDist, getDistance(rTopSeg, q));
156 LineSegment rBottomSeg = new LineSegment(lowerLeft, lowerRight);
157 minDist = Math.min(minDist, getDistance(rBottomSeg, q));
158 return minDist;
159 }
160 }
161
162 static double getDistance(LineSegment line, YPoint q) {
163 final double x1 = line.getFirstEndPoint().x;
164 final double y1 = line.getFirstEndPoint().y;
165
166 final double x2 = line.getSecondEndPoint().x - x1;
168 final double y2 = line.getSecondEndPoint().y - y1;
169 double pX = q.getX() - x1;
170 double pY = q.getY() - y1;
171
172 final double projSquaredDist;
174 if ((pX * x2 + pY * y2) <= 0.0) {
175 projSquaredDist = 0.0;
176 } else {
177 pX = x2 - pX;
178 pY = y2 - pY;
179 final double tmp = pX * x2 + pY * y2;
180 projSquaredDist = tmp <= 0.0 ? 0.0 : tmp * tmp / (x2 * x2 + y2 * y2);
181 }
182
183 final double squaredDist = pX * pX + pY * pY - projSquaredDist;
184 return squaredDist < 0.0 ? 0.0 : Math.sqrt(squaredDist);
185 }
186 }
187