1
14 package demo.view.realizer;
15
16 import demo.view.DemoBase;
17 import y.base.Edge;
18 import y.base.Node;
19 import y.geom.AffineLine;
20 import y.geom.YPoint;
21 import y.geom.YVector;
22 import y.view.DefaultLabelConfiguration;
23 import y.view.EdgeLabel;
24 import y.view.EdgeRealizer;
25 import y.view.Graph2D;
26 import y.view.NodeLabel;
27 import y.view.NodeRealizer;
28 import y.view.Util;
29 import y.view.YLabel;
30
31 import java.awt.Color;
32 import java.awt.Graphics2D;
33 import java.awt.Insets;
34 import java.awt.Shape;
35 import java.awt.geom.Area;
36 import java.awt.geom.GeneralPath;
37 import java.awt.geom.Line2D;
38 import java.awt.geom.PathIterator;
39 import java.awt.geom.RoundRectangle2D;
40 import java.awt.geom.Point2D;
41 import java.util.Map;
42
43
48 public class YLabelConfigurationDemo extends DemoBase {
49
53 public static void main(String[] args) {
54 new YLabelConfigurationDemo().start("YLabel Configuration Demo");
55 }
56
57
58 public YLabelConfigurationDemo() {
59 super();
60 view.setAntialiasedPainting(true);
61 Graph2D graph2D;
62 {
63 YLabel.Factory factory = NodeLabel.getFactory();
65
66 Map implementationsMap = factory.createDefaultConfigurationMap();
70
71 implementationsMap.put(YLabel.Painter.class, new MyPainter());
73
74 factory.addConfiguration("Bubble", implementationsMap);
76
77 graph2D = view.getGraph2D();
79 NodeRealizer realizer = graph2D.getDefaultNodeRealizer();
80 NodeLabel label = realizer.getLabel();
81 label.setModel(NodeLabel.FREE);
82 label.setOffset(50, 50);
83 label.setConfiguration("Bubble");
84 label.setInsets(new Insets(10, 10, 10, 10));
85 label.setLineColor(Color.DARK_GRAY);
86 label.setBackgroundColor(Color.YELLOW);
87 }
88
89 {
90 YLabel.Factory factory = EdgeLabel.getFactory();
92 Map implementationsMap = factory.createDefaultConfigurationMap();
93 implementationsMap.put(YLabel.Painter.class, new MyPainter());
94 factory.addConfiguration("Bubble", implementationsMap);
95 graph2D = view.getGraph2D();
96 EdgeRealizer realizer = graph2D.getDefaultEdgeRealizer();
97 EdgeLabel label = realizer.getLabel();
98 label.setModel(EdgeLabel.SIX_POS);
99 label.setDistance(30);
100 label.setConfiguration("Bubble");
101 label.setInsets(new Insets(10, 10, 10, 10));
102 label.setLineColor(Color.DARK_GRAY);
103 label.setBackgroundColor(Color.YELLOW);
104 }
105
106 loadGraph("resource/bubble.ygf");
108 }
109
110
111
115 static final class MyPainter extends DefaultLabelConfiguration {
116
117 public void paintBox(YLabel label, Graphics2D gfx, double x, double y, double width, double height) {
118
119 Shape shape = new RoundRectangle2D.Double(x, y, width, height, Math.min(width / 3, 10), Math.min(height / 3, 10));
121
122 double cx = x + width * 0.5d;
123 double cy = y + height * 0.5d;
124
125 if (label instanceof NodeLabel) {
126 NodeRealizer labelRealizer = ((NodeLabel) label).getRealizer();
128 Node node = ((NodeLabel) label).getNode();
129 Graph2D graph2D = ((Graph2D) node.getGraph());
130 NodeRealizer nodeRealizer = graph2D.getRealizer(node);
131
132 double tx = graph2D.getCenterX(node);
133 double ty = graph2D.getCenterY(node);
134
135 if(!nodeRealizer.contains(cx, cy)) {
137 double dirX = cx - labelRealizer.getCenterX();
138 double dirY = cy - labelRealizer.getCenterY();
139 Point2D result = new Point2D.Double();
140 nodeRealizer.findIntersection(tx, ty, cx, cy, result);
141 double l0 = Math.sqrt(dirX * dirX + dirY * dirY);
142 if(l0 > 0) {
143 double halfNodeWidth = nodeRealizer.getWidth() * 0.5 + 5;
144 halfNodeWidth = (dirX > 0) ? halfNodeWidth : -1.0 * halfNodeWidth;
145 tx = result.getX() + 5 * dirX / l0;
146 ty = result.getY() + 5 * dirY / l0;
147 }
148 }
149
150 double dx = cx - tx;
152 double dy = cy - ty;
153 double l = Math.sqrt(dx * dx + dy * dy);
154 if (l > 0) {
155 double size = Math.min(width, height) * 0.25;
156 GeneralPath p = new GeneralPath();
157 p.moveTo((float) tx, (float) ty);
158 p.lineTo((float) (cx + dy * size / l), (float) (cy - dx * size / l));
159 p.lineTo((float) (cx - dy * size / l), (float) (cy + dx * size / l));
160 p.closePath();
161 Area area = new Area(shape);
162 area.add(new Area(p));
163 shape = area;
164 }
165
166 } else if (label instanceof EdgeLabel) {
167 Edge edge = ((EdgeLabel) label).getEdge();
169 Graph2D graph2D = ((Graph2D) edge.getGraph());
170 EdgeRealizer edgeRealizer = graph2D.getRealizer(edge);
171 GeneralPath path = edgeRealizer.getPath();
172 double[] result = PointPathProjector.calculateClosestPathPoint(path, cx, cy);
173 double dx = cx - result[0];
174 double dy = cy - result[1];
175 double l = Math.sqrt(dx * dx + dy * dy);
176
177 if (l > 0) {
179 double tx = result[0] + 5 * dx / l;
180 double ty = result[1] + 5 * dy / l;
181 Line2D line = new Line2D.Double(cx, cy, tx, ty);
182 gfx.setColor(new Color(0, 0, 0, 64));
183 gfx.draw(line);
184 }
185 }
186
187 Color backgroundColor = label.getBackgroundColor();
189 if (backgroundColor != null) {
190 gfx.setColor(new Color(0, 0, 0, 64));
192 gfx.translate(5, 5);
193 gfx.fill(shape);
194 gfx.translate(-5, -5);
195 gfx.setColor(backgroundColor);
197 gfx.fill(shape);
198 }
199
200 Color lineColor = label.getLineColor();
202 if (lineColor != null) {
203 gfx.setColor(lineColor);
204 gfx.draw(shape);
205 }
206 }
207
208 }
209
210
211 static class PointPathProjector {
212 private PointPathProjector() {
213 }
214
215
227 static double[] calculateClosestPathPoint(GeneralPath path, double px, double py) {
228 double[] result = new double[6];
229 YPoint point = new YPoint(px, py);
230 double pathLength = 0;
231
232 CustomPathIterator pi = new CustomPathIterator(path, 1.0);
233 double[] curSeg = new double[4];
234 double minDist;
235 if (pi.ok()) {
236 curSeg = pi.segment();
237 minDist = YPoint.distance(px, py, curSeg[0], curSeg[1]);
238 result[0] = curSeg[0];
239 result[1] = curSeg[1];
240 result[2] = minDist;
241 result[3] = 0.0;
242 result[4] = 0.0;
243 result[5] = 0.0;
244 } else {
245 throw new IllegalStateException("path without any coordinates");
247 }
248
249 int segmentIndex = 0;
250 double lastPathLength = 0.0;
251 do {
252 YPoint segmentStart = new YPoint(curSeg[0], curSeg[1]);
253 YPoint segmentEnd = new YPoint(curSeg[2], curSeg[3]);
254 YVector segmentDirection = new YVector(segmentEnd, segmentStart);
255 double segmentLength = segmentDirection.length();
256 pathLength += segmentLength;
257 segmentDirection.norm();
258
259 AffineLine currentSegment = new AffineLine(segmentStart, segmentDirection);
260 AffineLine throughPoint = new AffineLine(point, YVector.orthoNormal(segmentDirection));
261 YPoint crossing = AffineLine.getCrossing(currentSegment, throughPoint);
262 YVector crossingVector = new YVector(crossing, segmentStart);
263
264 YVector segmentVector = new YVector(segmentEnd, segmentStart);
265 double indexEnd = YVector.scalarProduct(segmentVector, segmentDirection);
266 double indexCrossing = YVector.scalarProduct(crossingVector, segmentDirection);
267
268 double dist;
269 double segmentRatio;
270 YPoint nearestOnSegment;
271 if (indexCrossing <= 0.0) {
272 dist = YPoint.distance(point, segmentStart);
273 nearestOnSegment = segmentStart;
274 segmentRatio = 0.0;
275 } else if (indexCrossing >= indexEnd) {
276 dist = YPoint.distance(point, segmentEnd);
277 nearestOnSegment = segmentEnd;
278 segmentRatio = 1.0;
279 } else {
280 dist = YPoint.distance(point, crossing);
281 nearestOnSegment = crossing;
282 segmentRatio = indexCrossing / indexEnd;
283 }
284
285 if (dist < minDist) {
286 minDist = dist;
287 result[0] = nearestOnSegment.getX();
288 result[1] = nearestOnSegment.getY();
289 result[2] = minDist;
290 result[3] = segmentIndex;
291 result[4] = segmentRatio;
292 result[5] = segmentLength * segmentRatio + lastPathLength;
293 }
294
295 segmentIndex++;
296 lastPathLength = pathLength;
297 pi.next();
298 } while (pi.ok());
299
300 if (pathLength > 0) {
301 result[5] = result[5] / pathLength;
302 } else {
303 result[5] = 0.0;
304 }
305 return result;
306 }
307
308
309 static class CustomPathIterator {
310 private double[] cachedSegment;
311 private boolean moreToGet;
312 private PathIterator pathIterator;
313
314 public CustomPathIterator(GeneralPath path, double flatness) {
315 pathIterator = (new GeneralPath(path)).getPathIterator(Util.TRANSFORM, flatness);
317 cachedSegment = new double[4];
318 getFirstSegment();
319 }
320
321 public boolean ok() {
322 return moreToGet;
323 }
324
325 public final double[] segment() {
326 if (moreToGet) {
327 return cachedSegment;
328 } else {
329 return null;
330 }
331 }
332
333 public void next() {
334 if (!pathIterator.isDone()) {
335 float[] curSeg = new float[2];
336 cachedSegment[0] = cachedSegment[2];
337 cachedSegment[1] = cachedSegment[3];
338 pathIterator.currentSegment(curSeg);
339 cachedSegment[2] = curSeg[0];
340 cachedSegment[3] = curSeg[1];
341 pathIterator.next();
342 } else {
343 moreToGet = false;
344 }
345 }
346
347 private void getFirstSegment() {
348 float[] curSeg = new float[2];
349 if (!pathIterator.isDone()) {
350 pathIterator.currentSegment(curSeg);
351 cachedSegment[0] = curSeg[0];
352 cachedSegment[1] = curSeg[1];
353 pathIterator.next();
354 moreToGet = true;
355 } else {
356 moreToGet = false;
357 }
358 if (!pathIterator.isDone()) {
359 pathIterator.currentSegment(curSeg);
360 cachedSegment[2] = curSeg[0];
361 cachedSegment[3] = curSeg[1];
362 pathIterator.next();
363 moreToGet = true;
364 } else {
365 moreToGet = false;
366 }
367 }
368 }
369 }
370 }
371