1
14 package demo.view.realizer;
15
16 import demo.view.DemoBase;
17 import y.view.Bend;
18 import y.view.BendCursor;
19 import y.view.BendList;
20 import y.view.EdgeRealizer;
21 import y.view.GenericEdgePainter;
22 import y.view.GenericEdgeRealizer;
23 import y.view.LineType;
24 import y.view.NodeRealizer;
25 import y.view.PolyLinePathCalculator;
26 import y.view.Port;
27 import y.view.Arrow;
28 import y.base.ListCell;
29
30 import javax.swing.AbstractAction;
31 import javax.swing.ImageIcon;
32 import javax.swing.JComboBox;
33 import javax.swing.JToggleButton;
34 import javax.swing.JToolBar;
35 import java.awt.BasicStroke;
36 import java.awt.Color;
37 import java.awt.Graphics2D;
38 import java.awt.Insets;
39 import java.awt.Stroke;
40 import java.awt.event.ActionEvent;
41 import java.awt.event.ActionListener;
42 import java.awt.geom.*;
43 import java.net.URL;
44 import java.util.Map;
45
46
49 public class GenericEdgeRealizerDemo extends DemoBase
50 {
51 private static final boolean INITIAL_ANTIALIASING_STATE = true;
52
53
54 public GenericEdgeRealizerDemo()
55 {
56 super();
57
58 GenericEdgeRealizer ger = new GenericEdgeRealizer();
60 ger.setLineType(LineType.LINE_4);
62 ger.setLineColor(Color.green);
63
64 view.setAntialiasedPainting(INITIAL_ANTIALIASING_STATE);
65
66 GenericEdgeRealizer.Factory factory = GenericEdgeRealizer.getFactory();
68
69 Map implementationsMap = factory.createDefaultConfigurationMap();
73
74 implementationsMap.put(GenericEdgeRealizer.Painter.class, new CustomEdgePainter());
76 implementationsMap.put(GenericEdgeRealizer.PathCalculator.class, new MyFunnyPathCalculator());
78
79 factory.addConfiguration("Undulating", implementationsMap);
81
82 implementationsMap.put(GenericEdgeRealizer.PathCalculator.class, new MyPathCalculator());
83 factory.addConfiguration("QuadCurve", implementationsMap);
86
87 implementationsMap.put(GenericEdgeRealizer.PathCalculator.class, new PortMoverPathCalculator(new PolyLinePathCalculator()));
90 factory.addConfiguration("PolyLineAxesParallel", implementationsMap);
91
92
94 implementationsMap = factory.createDefaultConfigurationMap();
95 implementationsMap.put(GenericEdgeRealizer.Painter.class, new GenericEdgePainter());
96 implementationsMap.put(GenericEdgeRealizer.PathCalculator.class, new MyFunnyPathCalculator());
97 implementationsMap.put(GenericEdgeRealizer.BendPainter.class,
101 new CustomBendPainter(new Ellipse2D.Double(0,0,10,5),new Ellipse2D.Double(0,0,10,10), Color.blue, Color.red));
102 factory.addConfiguration("UndulatingCustomBends", implementationsMap);
103
104 implementationsMap = factory.createDefaultConfigurationMap();
105 implementationsMap.put(GenericEdgeRealizer.ArrowPainter.class, new CenterArrowPainter());
106 implementationsMap.put(GenericEdgeRealizer.PathCalculator.class, new UnclippedPathCalculator());
107 factory.addConfiguration("Unclipped", implementationsMap);
108
109 implementationsMap = factory.createDefaultConfigurationMap();
110 MultiArrowPainter arrowPainter = new MultiArrowPainter();
111 implementationsMap.put(GenericEdgeRealizer.ArrowPainter.class, arrowPainter);
112 factory.addConfiguration("MultiArrow", implementationsMap);
113
114
115 ger.setConfiguration("Undulating");
118 ger.setUserData("This is my own userData object.");
119 view.getGraph2D().setDefaultEdgeRealizer(ger);
120
121 loadGraph( "resource/genericEdgeRealizer.gml" );
122 }
123
124
125 protected JToolBar createToolBar()
126 {
127 JToolBar retValue;
128
129 retValue = super.createToolBar();
130
131 final JComboBox cb = new JComboBox(new Object[]{"Undulating", "QuadCurve", "PolyLineAxesParallel", "UndulatingCustomBends", "Unclipped", "MultiArrow"});
132 cb.addActionListener(new ActionListener()
133 {
134 public void actionPerformed(ActionEvent ae)
135 {
136 GenericEdgeRealizer genericEdgeRealizer = (GenericEdgeRealizer) view.getGraph2D().getDefaultEdgeRealizer();
137 String configurationName = cb.getSelectedItem().toString();
138 if("Unclipped".equals(configurationName)) {
139 genericEdgeRealizer.setTargetArrow(Arrow.STANDARD);
140 } else {
141 genericEdgeRealizer.setTargetArrow(Arrow.NONE);
142 }
143 genericEdgeRealizer.setConfiguration(configurationName);
144 }
145 });
146 retValue.addSeparator();
147 retValue.add(cb);
148
149 final URL iconUrl = getClass().getResource("resource/antialiasing.png");
150 final JToggleButton toggleAa = new JToggleButton(new AbstractAction("AA") {
151 {
152 if (iconUrl != null) {
153 putValue(AbstractAction.SMALL_ICON, new ImageIcon(iconUrl));
154 }
155 putValue(AbstractAction.SHORT_DESCRIPTION, "Toggle Anti-Aliasing");
156 }
157
158 public void actionPerformed(ActionEvent e) {
159 final boolean newAaState = !view.isAntialiasedPainting();
160 view.setAntialiasedPainting(newAaState);
161 view.updateView();
162 }
163 });
164 if (iconUrl != null) {
165 toggleAa.setText("");
166 toggleAa.setMargin(new Insets(0,0,0,0));
167 }
168 toggleAa.setSelected(INITIAL_ANTIALIASING_STATE);
169 retValue.addSeparator();
170 retValue.add(toggleAa);
171 return retValue;
172 }
173
174
178 static final class CustomEdgePainter extends GenericEdgePainter {
179 protected void paintPath(EdgeRealizer context, BendList bends, GeneralPath path, Graphics2D gfx, boolean selected) {
180 Stroke s = gfx.getStroke();
181 Color oldColor = gfx.getColor();
182 if (s instanceof BasicStroke){
183 Color c;
184 if (!context.isSelected()){
185 initializeLine(context, gfx, selected);
186 c = gfx.getColor();
187 gfx.setColor(new Color(0,0,0,64));
188 gfx.translate(4, 4);
189 gfx.draw(path);
190 gfx.translate(-4, -4);
191 } else {
192 initializeSelectionLine(context, gfx, selected);
193 c = gfx.getColor();
194 }
195 Color newC = context.isSelected() ? Color.red :c;
196 gfx.setColor(new Color(128 + newC.getRed()/ 2, 128 + newC.getGreen()/ 2,128 + newC.getBlue()/ 2));
197 gfx.translate(-1, -1);
198 gfx.draw(path);
199 gfx.setColor(new Color(newC.getRed()/ 2, newC.getGreen()/ 2,newC.getBlue()/ 2));
200 gfx.translate(2, 2);
201 gfx.draw(path);
202 gfx.translate(-1, -1);
203 gfx.setColor(c);
204 gfx.draw(path);
205 gfx.setColor(oldColor);
206 } else {
207 gfx.draw(path);
208 }
209 }
210 }
211
212
221 static final class PortMoverPathCalculator implements GenericEdgeRealizer.PathCalculator {
222 private GenericEdgeRealizer.PathCalculator innerCalculator;
223
224 PortMoverPathCalculator(GenericEdgeRealizer.PathCalculator innerCalculator){
225 this.innerCalculator = innerCalculator;
226 }
227
228 public byte calculatePath(EdgeRealizer context, BendList bends, GeneralPath path, Point2D sourceIntersectionPointOut,
229 Point2D targetIntersectionPointOut) {
230 final Port sp = context.getSourcePort();
231 final Port tp = context.getTargetPort();
232 final NodeRealizer snr = context.getSourceRealizer();
233 final NodeRealizer tnr = context.getTargetRealizer();
234 if (bends.size() > 0){
235 adjustPort(bends.firstBend(), snr, sp);
236 adjustPort((Bend) bends.last(), tnr, tp);
237 } else {
238 double minx = Math.max(snr.getX() , tnr.getX());
239 double maxx = Math.min(snr.getX() + snr.getWidth(), tnr.getX() + tnr.getWidth());
240 if (maxx >= minx){
241 double pos = (minx + maxx) * 0.5d;
242 sp.setOffsetX(pos - snr.getCenterX());
243 tp.setOffsetX(pos - tnr.getCenterX());
244 }
245 double miny = Math.max(snr.getY() , tnr.getY());
246 double maxy = Math.min(snr.getY() + snr.getHeight(), tnr.getY() + tnr.getHeight());
247 if (maxy >= miny){
248 double pos = (miny + maxy) * 0.5d;
249 sp.setOffsetY(pos - snr.getCenterY());
250 tp.setOffsetY(pos - tnr.getCenterY());
251 }
252 }
253 return innerCalculator.calculatePath(context, bends, path, sourceIntersectionPointOut, targetIntersectionPointOut);
254 }
255
256 private void adjustPort(Bend b, NodeRealizer realizer, Port port) {
257 double x = b.getX();
258 double y = b.getY();
259 boolean inXRange = x >= realizer.getX() && x <= realizer.getX() + realizer.getWidth();
260 boolean inYRange = y >= realizer.getY() && y <= realizer.getY() + realizer.getHeight();
261 if (inXRange && !inYRange){
262 port.setOffsetX(x - realizer.getCenterX());
263 }
264 if (inYRange && ! inXRange){
265 port.setOffsetY(y - realizer.getCenterY());
266 }
267 }
268 }
269
270
273 static final class MyPathCalculator extends PolyLinePathCalculator implements GenericEdgeRealizer.PathCalculator {
274 private final GeneralPath scratch = new GeneralPath();
275
276
277 public byte calculatePath(EdgeRealizer context, BendList bends, GeneralPath path, Point2D sourceIntersectionPointOut,
278 Point2D targetIntersectionPointOut) {
279 if (bends.size() == 0) {
280 return super.calculatePath(context, bends, path, sourceIntersectionPointOut, targetIntersectionPointOut);
281 } else {
282 final int npoints = bends.size();
283
284 path.reset();
285 scratch.reset();
286
287 NodeRealizer nr = context.getSourceRealizer();
288 Port pp = context.getSourcePort();
289 float lastPointx;
290 float lastPointy;
291 float secondLastPointx;
292 float secondLastPointy;
293 scratch.moveTo(lastPointx = (float) pp.getX(nr), lastPointy = (float) pp.getY(nr));
294
295 int index = 0;
296
297 secondLastPointx = lastPointx;
298 secondLastPointy = lastPointy;
299
300 BendCursor bc = bends.bends();
301
302 {
303 Bend b = bc.bend();
304 lastPointx = (float) b.getX();
305 lastPointy = (float) b.getY();
306 bc.next();
307 index++;
308 }
309
310 for (; index < npoints; bc.next(), index++) {
311 Bend b = bc.bend();
312 float nextPointx = (float) b.getX();
313 float nextPointy = (float) b.getY();
314 {
315 final float sx = 0.5f * lastPointx + secondLastPointx * 0.5f;
316 final float sy = 0.5f * lastPointy + secondLastPointy * 0.5f;
317 scratch.lineTo(sx, sy);
318 }
319 {
320 final float sx = 0.5f * nextPointx + lastPointx * 0.5f;
321 final float sy = 0.5f * nextPointy + lastPointy * 0.5f;
322 scratch.quadTo(lastPointx, lastPointy, sx, sy);
323 secondLastPointx = lastPointx;
324 secondLastPointy = lastPointy;
325 lastPointx = nextPointx;
326 lastPointy = nextPointy;
327 }
328 }
329
330 nr = context.getTargetRealizer();
331 pp = context.getTargetPort();
332
333 {
334 float nextPointx = (float) pp.getX(nr);
335 float nextPointy = (float) pp.getY(nr);
336 {
337 final float sx = 0.5f * lastPointx + secondLastPointx * 0.5f;
338 final float sy = 0.5f * lastPointy + secondLastPointy * 0.5f;
339 scratch.lineTo(sx, sy);
340 }
341 {
342 final float sx = 0.5f * nextPointx + lastPointx * 0.5f;
343 final float sy = 0.5f * nextPointy + lastPointy * 0.5f;
344 scratch.quadTo(lastPointx, lastPointy, sx, sy);
345 }
346 scratch.lineTo(nextPointx, nextPointy);
347 }
348 path.append(scratch.getPathIterator(null, 1.0), false);
349 }
350 return EdgeRealizer.calculateClippingAndIntersection(context, path, path, sourceIntersectionPointOut, targetIntersectionPointOut);
351 }
352 }
353
354
357 static final class MyFunnyPathCalculator extends PolyLinePathCalculator implements GenericEdgeRealizer.PathCalculator {
358 private final GeneralPath scratch = new GeneralPath();
359
360 public byte calculatePath(EdgeRealizer context, BendList bends, GeneralPath path, Point2D sourceIntersectionPointOut,
361 Point2D targetIntersectionPointOut) {
362 scratch.reset();
363
364 NodeRealizer nr = context.getSourceRealizer();
365 Port pp = context.getSourcePort();
366 float lastPointX;
367 float lastPointY;
368 scratch.moveTo(lastPointX = (float)pp.getX(nr), lastPointY = (float)pp.getY(nr));
369
370 int wobbleCount = 0;
371 for(BendCursor bc = bends.bends(); bc.ok(); bc.next())
372 {
373 Bend b = bc.bend();
374 float nextPointX = (float) b.getX();
375 float nextPointY = (float) b.getY();
376 float dx = nextPointX - lastPointX;
377 float dy = nextPointY - lastPointY;
378 float len = (float) Math.sqrt(dx * dx + dy * dy);
379 if (len > 0){
380 int count = (int) (len / 30) + 1;
381 for (int i = 0; i < count; i++){
382 final float height = wobbleCount%2 == 0 ? 10 : -10;
383 wobbleCount++;
384 scratch.quadTo(lastPointX + (i+0.5f)/((float)count) * dx + dy * height / len, lastPointY + (i+0.5f)/((float)count) * dy - dx * height/len, lastPointX + (i+1)/((float)count) * dx, lastPointY + (i+1)/((float)count) * dy);
385 }
386 } else {
387 scratch.lineTo(nextPointX, nextPointY);
388 }
389 lastPointX = nextPointX;
390 lastPointY = nextPointY;
391 }
392
393 nr = context.getTargetRealizer();
394 pp = context.getTargetPort();
395
396 {
397 float nextPointX = (float)pp.getX(nr);
398 float nextPointY = (float)pp.getY(nr);
399 float dx = nextPointX - lastPointX;
400 float dy = nextPointY - lastPointY;
401 float len = (float) Math.sqrt(dx * dx + dy * dy);
402 if (len > 0){
403 int count = (int) (len / 30) + 1;
404 for (int i = 0; i < count; i++){
405 final float height = wobbleCount%2 == 0 ? 10 : -10;
406 wobbleCount++;
407 scratch.quadTo(lastPointX + (i+0.5f)/((float)count) * dx + dy * height / len, lastPointY + (i+0.5f)/((float)count) * dy - dx * height/len, lastPointX + (i+1)/((float)count) * dx, lastPointY + (i+1)/((float)count) * dy);
408 }
409 } else {
410 scratch.lineTo(nextPointX, nextPointY);
411 }
412 }
413 path.reset();
414 return EdgeRealizer.calculateClippingAndIntersection(context, scratch, path, sourceIntersectionPointOut, targetIntersectionPointOut);
415 }
416 }
417
418
419
423 static final class CustomBendPainter implements GenericEdgeRealizer.BendPainter
424 {
425 private RectangularShape shape;
426 private RectangularShape selectedShape;
427 private Color fillColor;
428 private Color selectedFillColor;
429
430 CustomBendPainter(RectangularShape shape, RectangularShape selectedShape, Color fillColor, Color selectedFillColor)
431 {
432 this.selectedShape = selectedShape;
433 this.shape = shape;
434 this.fillColor = fillColor;
435 this.selectedFillColor = selectedFillColor;
436 }
437
438 public void paintBends(EdgeRealizer context, BendList bends, GeneralPath path, Graphics2D gfx, boolean selected) {
439 final Color oldColor = gfx.getColor();
440 for (BendCursor bendCursor = bends.bends(); bendCursor.ok(); bendCursor.next()){
441 Bend b = bendCursor.bend();
442 gfx.setColor((selected || b.isSelected()) ? this.selectedFillColor : this.fillColor);
443 final double x = b.getX();
444 final double y = b.getY();
445 RectangularShape shape = selected ? this.selectedShape : this.shape;
446 shape.setFrame(x - shape.getWidth()/2, y - shape.getHeight()/2, shape.getWidth(), shape.getHeight());
447 gfx.fill(shape);
448 }
449 gfx.setColor(oldColor);
450 }
451 }
452
453
458 public static final class CenterArrowPainter implements GenericEdgeRealizer.ArrowPainter {
459 public void paintArrows(EdgeRealizer context, BendList bends, GeneralPath path, Graphics2D gfx) {
460 Arrow targetArrow = context.getTargetArrow();
461 if (targetArrow != null){
462
463 Point2D sourceIntersection = context.getSourceIntersection();
464 Point2D targetIntersection = context.getTargetIntersection();
465
466 if (bends.size() > 0) {
467 int mid = bends.size() / 2;
468 if (mid > 0){
469 Bend bend = context.getBend(mid - 1);
470 sourceIntersection.setLocation(bend.getX(), bend.getY());
471 }
472 {
473 Bend bend = context.getBend(mid);
474 targetIntersection.setLocation(bend.getX(), bend.getY());
475 }
476 }
477
478 double centerX = (targetIntersection.getX() + sourceIntersection.getX()) * 0.5d;
479 double centerY = (targetIntersection.getY() + sourceIntersection.getY()) * 0.5d;
480 double dx = (targetIntersection.getX() - sourceIntersection.getX());
481 double dy = (targetIntersection.getY() - sourceIntersection.getY());
482 double l = Math.sqrt(dx * dx + dy * dy);
483 double arrowScaleFactor = context.getArrowScaleFactor();
484 if (l > 0){
485 targetArrow.paint(gfx, centerX, centerY, arrowScaleFactor * dx / l , arrowScaleFactor * dy / l);
486 }
487 }
488 }
489 }
490
491
495 public static final class MultiArrowPainter implements GenericEdgeRealizer.ArrowPainter {
496 private double threshold = 50d;
497 private Arrow arrow = Arrow.DELTA;
498 private Color color = Color.LIGHT_GRAY;
499
500
501
504 public double getThreshold() {
505 return threshold;
506 }
507
508
512 public void setThreshold(double threshold) {
513 this.threshold = threshold;
514 }
515
516
517
520 public Arrow getArrow() {
521 return arrow;
522 }
523
524
528 public void setArrow(Arrow arrow) {
529 this.arrow = arrow;
530 }
531
532
533
537 public Color getColor() {
538 return color;
539 }
540
541
545 public void setColor(Color color) {
546 this.color = color;
547 }
548
549
559 public void paintArrows(EdgeRealizer context, BendList bends, GeneralPath path, Graphics2D gfx) {
560 if (arrow != null){
561
562 PathIterator iter = path.getPathIterator(null, 1);
563 double[] curSeg = new double[2];
564 if(!iter.isDone()) {
565 iter.currentSegment(curSeg);
566 Point2D p1 = new Point2D.Double(curSeg[0],curSeg[1]);
567 Point2D p0 = new Point2D.Double();
568 for(iter.next(); !iter.isDone(); iter.next()) {
569 p0.setLocation(p1);
570 iter.currentSegment(curSeg);
571 p1.setLocation(curSeg[0], curSeg[1]);
572 paintArrow(p1, p0, context, gfx);
573 }
574 }
575 }
576 }
577
578 private void paintArrow(Point2D p1, Point2D p0, EdgeRealizer context, Graphics2D gfx) {
579 if (arrow != null) {
580 double centerX = (p1.getX() + p0.getX()) * 0.5d;
581 double centerY = (p1.getY() + p0.getY()) * 0.5d;
582 double dx = (p1.getX() - p0.getX());
583 double dy = (p1.getY() - p0.getY());
584 double l = Math.sqrt(dx * dx + dy * dy);
585 double dxNormalized = dx / l;
586 double dyNormalized = dy / l;
587 if (l > threshold) {
588 double arrowScaleFactor = context.getArrowScaleFactor();
589 double offset = arrowScaleFactor * (arrow.getArrowLength() + arrow.getClipLength()) * 0.5d;
590 double x = centerX + offset * dxNormalized;
591 double y = centerY + offset * dyNormalized;
592 Color oldColor = gfx.getColor();
593 gfx.setColor(color);
594 arrow.paint(gfx, x, y, arrowScaleFactor * dxNormalized, arrowScaleFactor * dyNormalized);
595 gfx.setColor(oldColor);
596 }
597 }
598 }
599 }
600
601
605 public static final class UnclippedPathCalculator implements GenericEdgeRealizer.PathCalculator {
606 public byte calculatePath(EdgeRealizer context, BendList bends, GeneralPath path, Point2D sourceIntersectionPointOut,
607 Point2D targetIntersectionPointOut) {
608 sourceIntersectionPointOut.setLocation(context.getSourcePort().getX(context.getSourceRealizer()), context.getSourcePort().getY(context.getSourceRealizer()));
609 targetIntersectionPointOut.setLocation(context.getTargetPort().getX(context.getTargetRealizer()), context.getTargetPort().getY(context.getTargetRealizer()));
610 path.reset();
611 path.moveTo((float)sourceIntersectionPointOut.getX(), (float)sourceIntersectionPointOut.getY());
612 for (ListCell cell = bends.firstCell(); cell != null; cell = cell.succ()){
613 Bend b = (Bend) cell.getInfo();
614 path.lineTo((float)b.getX(), (float)b.getY());
615 }
616 path.lineTo((float)targetIntersectionPointOut.getX(), (float)targetIntersectionPointOut.getY());
617
618 return EdgeRealizer.PATH_CLIPPED_AT_SOURCE_AND_TARGET;
619 }
620 }
621
622
627 public static void main(String[] args)
628 {
629 new GenericEdgeRealizerDemo().start("GenericEdgeRealizer Demo");
630 }
631 }
632