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