1
14 package demo.view.realizer;
15
16 import demo.view.DemoBase;
17 import demo.view.DemoDefaults;
18
19 import y.base.Edge;
20 import y.base.Node;
21 import y.geom.AffineLine;
22 import y.geom.YPoint;
23 import y.geom.YVector;
24 import y.view.DefaultLabelConfiguration;
25 import y.view.EdgeLabel;
26 import y.view.EdgeRealizer;
27 import y.view.Graph2D;
28 import y.view.LineType;
29 import y.view.NodeLabel;
30 import y.view.NodeRealizer;
31 import y.view.SmartEdgeLabelModel;
32 import y.view.SmartNodeLabelModel;
33 import y.view.YLabel;
34 import y.view.Arrow;
35 import y.view.YRenderingHints;
36
37 import java.awt.Color;
38 import java.awt.EventQueue;
39 import java.awt.Graphics2D;
40 import java.awt.Shape;
41 import java.awt.Stroke;
42 import java.awt.geom.Area;
43 import java.awt.geom.GeneralPath;
44 import java.awt.geom.Line2D;
45 import java.awt.geom.PathIterator;
46 import java.awt.geom.RoundRectangle2D;
47 import java.awt.geom.Point2D;
48 import java.util.Locale;
49 import java.util.Map;
50
51
57 public class YLabelConfigurationDemo extends DemoBase {
58
62 public static void main(String[] args) {
63 EventQueue.invokeLater(new Runnable() {
64 public void run() {
65 Locale.setDefault(Locale.ENGLISH);
66 initLnF();
67 (new YLabelConfigurationDemo()).start();
68 }
69 });
70 }
71
72
73 public YLabelConfigurationDemo() {
74 super();
75 Graph2D graph2D;
76 {
77 YLabel.Factory factory = NodeLabel.getFactory();
79
80 Map implementationsMap = factory.createDefaultConfigurationMap();
84
85 implementationsMap.put(YLabel.Painter.class, new MyPainter());
87
88 factory.addConfiguration("Bubble", implementationsMap);
90
91 graph2D = view.getGraph2D();
93 NodeRealizer realizer = graph2D.getDefaultNodeRealizer();
94 NodeLabel label = realizer.getLabel();
95 label.setLabelModel(new SmartNodeLabelModel());
96 label.setConfiguration("Bubble");
97 label.setLineColor(Color.DARK_GRAY);
98 label.setBackgroundColor(new Color(202,227,255));
99 }
100
101 {
102 YLabel.Factory factory = EdgeLabel.getFactory();
104 Map implementationsMap = factory.createDefaultConfigurationMap();
105 implementationsMap.put(YLabel.Painter.class, new MyPainter());
106 factory.addConfiguration("Bubble", implementationsMap);
107 graph2D = view.getGraph2D();
108 EdgeRealizer realizer = graph2D.getDefaultEdgeRealizer();
109 EdgeLabel label = realizer.getLabel();
110 label.setLabelModel(new SmartEdgeLabelModel());
111 label.setDistance(30);
112 label.setConfiguration("Bubble");
113 label.setLineColor(Color.DARK_GRAY);
114 label.setBackgroundColor(new Color(202,227,255));
115 }
116
117 loadGraph("resource/bubble.graphml");
119 DemoDefaults.applyRealizerDefaults(view.getGraph2D(), true, true);
120 view.getGraph2D().getDefaultEdgeRealizer().setTargetArrow(Arrow.NONE);
121 }
122
123
124
128 static final class MyPainter extends DefaultLabelConfiguration {
129
130 private static final Color SELECTION_COLOR = new Color(0, 40, 158);
131
132
133 public void paintBox(YLabel label, Graphics2D gfx, double x, double y, double width, double height) {
134 Color oldColor = gfx.getColor();
136 Stroke oldStroke = gfx.getStroke();
137
138 Shape shape = new RoundRectangle2D.Double(x, y, width, height, Math.min(width / 3, 10), Math.min(height / 3, 10));
140
141 double cx = x + width * 0.5d;
142 double cy = y + height * 0.5d;
143
144 if (label instanceof NodeLabel) {
145 NodeRealizer labelRealizer = ((NodeLabel) label).getRealizer();
147 Node node = ((NodeLabel) label).getNode();
148 Graph2D graph2D = ((Graph2D) node.getGraph());
149 NodeRealizer nodeRealizer = graph2D.getRealizer(node);
150
151 double tx = graph2D.getCenterX(node);
152 double ty = graph2D.getCenterY(node);
153
154 if(!nodeRealizer.contains(cx, cy)) {
156 double dirX = cx - labelRealizer.getCenterX();
157 double dirY = cy - labelRealizer.getCenterY();
158 Point2D result = new Point2D.Double();
159 nodeRealizer.findIntersection(tx, ty, cx, cy, result);
160 double l0 = Math.sqrt(dirX * dirX + dirY * dirY);
161 if(l0 > 0) {
162 double halfNodeWidth = nodeRealizer.getWidth() * 0.5 + 5;
163 halfNodeWidth = (dirX > 0) ? halfNodeWidth : -1.0 * halfNodeWidth;
164 tx = result.getX() + 5 * dirX / l0;
165 ty = result.getY() + 5 * dirY / l0;
166 }
167 }
168
169 double dx = cx - tx;
171 double dy = cy - ty;
172 double l = Math.sqrt(dx * dx + dy * dy);
173 if (l > 0) {
174 double size = Math.min(width, height) * 0.25;
175 GeneralPath p = new GeneralPath();
176 p.moveTo((float) tx, (float) ty);
177 p.lineTo((float) (cx + dy * size / l), (float) (cy - dx * size / l));
178 p.lineTo((float) (cx - dy * size / l), (float) (cy + dx * size / l));
179 p.closePath();
180 Area area = new Area(shape);
181 area.add(new Area(p));
182 shape = area;
183 }
184
185 } else if (label instanceof EdgeLabel) {
186 Edge edge = ((EdgeLabel) label).getEdge();
188 Graph2D graph2D = ((Graph2D) edge.getGraph());
189 EdgeRealizer edgeRealizer = graph2D.getRealizer(edge);
190 GeneralPath path = edgeRealizer.getPath();
191 double[] result = PointPathProjector.calculateClosestPathPoint(path, cx, cy);
192 double dx = cx - result[0];
193 double dy = cy - result[1];
194 double l = Math.sqrt(dx * dx + dy * dy);
195
196 if (l > 0) {
198 double tx = result[0] + 5 * dx / l;
199 double ty = result[1] + 5 * dy / l;
200 Line2D line = new Line2D.Double(cx, cy, tx, ty);
201 gfx.setColor(new Color(0, 0, 0, 64));
202 gfx.draw(line);
203 }
204 }
205
206 Color backgroundColor = label.getBackgroundColor();
208 if (backgroundColor != null) {
209 gfx.setColor(new Color(0, 0, 0, 64));
211 gfx.translate(5, 5);
212 gfx.fill(shape);
213 gfx.translate(-5, -5);
214 gfx.setColor(backgroundColor);
216 gfx.fill(shape);
217 }
218
219 Color lineColor = label.getLineColor();
221 if (label.isSelected()
222 && YRenderingHints.isSelectionPaintingEnabled(gfx)) {
223 lineColor = SELECTION_COLOR;
224 gfx.setStroke(LineType.LINE_2);
225 }
226 if (lineColor != null) {
227 gfx.setColor(lineColor);
228 gfx.draw(shape);
229 }
230
231 gfx.setColor(oldColor);
232 gfx.setStroke(oldStroke);
233 }
234
235 }
236
237
238 static class PointPathProjector {
239 private PointPathProjector() {
240 }
241
242
254 static double[] calculateClosestPathPoint(GeneralPath path, double px, double py) {
255 double[] result = new double[6];
256 YPoint point = new YPoint(px, py);
257 double pathLength = 0;
258
259 CustomPathIterator pi = new CustomPathIterator(path, 1.0);
260 double[] curSeg = new double[4];
261 double minDist;
262 if (pi.ok()) {
263 curSeg = pi.segment();
264 minDist = YPoint.distance(px, py, curSeg[0], curSeg[1]);
265 result[0] = curSeg[0];
266 result[1] = curSeg[1];
267 result[2] = minDist;
268 result[3] = 0.0;
269 result[4] = 0.0;
270 result[5] = 0.0;
271 } else {
272 throw new IllegalStateException("path without any coordinates");
274 }
275
276 int segmentIndex = 0;
277 double lastPathLength = 0.0;
278 do {
279 YPoint segmentStart = new YPoint(curSeg[0], curSeg[1]);
280 YPoint segmentEnd = new YPoint(curSeg[2], curSeg[3]);
281 YVector segmentDirection = new YVector(segmentEnd, segmentStart);
282 double segmentLength = segmentDirection.length();
283 pathLength += segmentLength;
284 segmentDirection.norm();
285
286 AffineLine currentSegment = new AffineLine(segmentStart, segmentDirection);
287 AffineLine throughPoint = new AffineLine(point, YVector.orthoNormal(segmentDirection));
288 YPoint crossing = AffineLine.getCrossing(currentSegment, throughPoint);
289 YVector crossingVector = new YVector(crossing, segmentStart);
290
291 YVector segmentVector = new YVector(segmentEnd, segmentStart);
292 double indexEnd = YVector.scalarProduct(segmentVector, segmentDirection);
293 double indexCrossing = YVector.scalarProduct(crossingVector, segmentDirection);
294
295 double dist;
296 double segmentRatio;
297 YPoint nearestOnSegment;
298 if (indexCrossing <= 0.0) {
299 dist = YPoint.distance(point, segmentStart);
300 nearestOnSegment = segmentStart;
301 segmentRatio = 0.0;
302 } else if (indexCrossing >= indexEnd) {
303 dist = YPoint.distance(point, segmentEnd);
304 nearestOnSegment = segmentEnd;
305 segmentRatio = 1.0;
306 } else {
307 dist = YPoint.distance(point, crossing);
308 nearestOnSegment = crossing;
309 segmentRatio = indexCrossing / indexEnd;
310 }
311
312 if (dist < minDist) {
313 minDist = dist;
314 result[0] = nearestOnSegment.getX();
315 result[1] = nearestOnSegment.getY();
316 result[2] = minDist;
317 result[3] = segmentIndex;
318 result[4] = segmentRatio;
319 result[5] = segmentLength * segmentRatio + lastPathLength;
320 }
321
322 segmentIndex++;
323 lastPathLength = pathLength;
324 pi.next();
325 } while (pi.ok());
326
327 if (pathLength > 0) {
328 result[5] = result[5] / pathLength;
329 } else {
330 result[5] = 0.0;
331 }
332 return result;
333 }
334
335
336 static class CustomPathIterator {
337 private double[] cachedSegment;
338 private boolean moreToGet;
339 private PathIterator pathIterator;
340
341 public CustomPathIterator(GeneralPath path, double flatness) {
342 pathIterator = (new GeneralPath(path)).getPathIterator(null, flatness);
344 cachedSegment = new double[4];
345 getFirstSegment();
346 }
347
348 public boolean ok() {
349 return moreToGet;
350 }
351
352 public final double[] segment() {
353 if (moreToGet) {
354 return cachedSegment;
355 } else {
356 return null;
357 }
358 }
359
360 public void next() {
361 if (!pathIterator.isDone()) {
362 float[] curSeg = new float[2];
363 cachedSegment[0] = cachedSegment[2];
364 cachedSegment[1] = cachedSegment[3];
365 pathIterator.currentSegment(curSeg);
366 cachedSegment[2] = curSeg[0];
367 cachedSegment[3] = curSeg[1];
368 pathIterator.next();
369 } else {
370 moreToGet = false;
371 }
372 }
373
374 private void getFirstSegment() {
375 float[] curSeg = new float[2];
376 if (!pathIterator.isDone()) {
377 pathIterator.currentSegment(curSeg);
378 cachedSegment[0] = curSeg[0];
379 cachedSegment[1] = curSeg[1];
380 pathIterator.next();
381 moreToGet = true;
382 } else {
383 moreToGet = false;
384 }
385 if (!pathIterator.isDone()) {
386 pathIterator.currentSegment(curSeg);
387 cachedSegment[2] = curSeg[0];
388 cachedSegment[3] = curSeg[1];
389 pathIterator.next();
390 moreToGet = true;
391 } else {
392 moreToGet = false;
393 }
394 }
395 }
396 }
397 }
398