1
28 package demo.view.realizer;
29
30 import java.awt.BasicStroke;
31 import java.awt.BorderLayout;
32 import java.awt.Color;
33 import java.awt.EventQueue;
34 import java.awt.Graphics2D;
35 import java.awt.Stroke;
36 import java.awt.event.MouseEvent;
37 import java.awt.geom.AffineTransform;
38 import java.awt.geom.Ellipse2D;
39 import java.awt.geom.GeneralPath;
40 import java.awt.geom.Line2D;
41 import java.awt.geom.PathIterator;
42 import java.awt.geom.Point2D;
43 import java.awt.geom.RectangularShape;
44 import java.util.ArrayList;
45 import java.util.Collections;
46 import java.util.Iterator;
47 import java.util.List;
48 import java.util.Locale;
49 import java.util.Map;
50
51 import javax.swing.Icon;
52 import javax.swing.JList;
53 import javax.swing.JScrollPane;
54 import javax.swing.ListSelectionModel;
55 import javax.swing.border.TitledBorder;
56 import javax.swing.event.ListSelectionEvent;
57 import javax.swing.event.ListSelectionListener;
58
59 import demo.view.DemoBase;
60 import demo.view.DemoDefaults;
61
62 import y.base.Edge;
63 import y.base.ListCell;
64 import y.geom.YPoint;
65 import y.geom.YVector;
66 import y.option.RealizerCellRenderer;
67 import y.view.Arrow;
68 import y.view.Bend;
69 import y.view.BendCursor;
70 import y.view.BendList;
71 import y.view.EdgeRealizer;
72 import y.view.FramedEdgePainter;
73 import y.view.GenericEdgePainter;
74 import y.view.GenericEdgeRealizer;
75 import y.view.LineType;
76 import y.view.NodeRealizer;
77 import y.view.PolyLinePathCalculator;
78 import y.view.Port;
79 import y.view.TooltipMode;
80 import y.view.YRenderingHints;
81
82
88 public class GenericEdgeRealizerDemo extends DemoBase {
89 private static final Color defaultEdgeColor = new Color(202, 227, 255);
90 private static final LineType defaultEdgeLineType = LineType.LINE_3;
91
92
95 public GenericEdgeRealizerDemo() {
96 super();
97
98 TitledBorder border = new TitledBorder("Edge Types:");
100 border.setTitlePosition(TitledBorder.BELOW_TOP);
101 JScrollPane scrollPane = new JScrollPane(createEdgeRealizerList());
102 scrollPane.setBorder(border);
103 contentPane.add(scrollPane, BorderLayout.WEST);
104
105 loadGraph("resource/genericEdgeRealizer.graphml");
106 DemoDefaults.applyRealizerDefaults(view.getGraph2D(), true, true);
107 }
108
109 protected void configureDefaultRealizers() {
110 super.configureDefaultRealizers();
111
112 GenericEdgeRealizer.Factory factory = GenericEdgeRealizer.getFactory();
114
115 Map implementationsMap = factory.createDefaultConfigurationMap();
119
120 implementationsMap.put(GenericEdgeRealizer.Painter.class, new CustomEdgePainter());
122 implementationsMap.put(GenericEdgeRealizer.PathCalculator.class, new UndulatingPathCalculator());
124
125 factory.addConfiguration("Undulating", implementationsMap);
127
128 implementationsMap.put(GenericEdgeRealizer.PathCalculator.class, new MyPathCalculator());
129 factory.addConfiguration("QuadCurve", implementationsMap);
132
133 implementationsMap.put(GenericEdgeRealizer.PathCalculator.class,
136 new PortMoverPathCalculator(new PolyLinePathCalculator()));
137 factory.addConfiguration("PolyLineAxesParallel", implementationsMap);
138
139
141 implementationsMap = factory.createDefaultConfigurationMap();
142 implementationsMap.put(GenericEdgeRealizer.Painter.class, new GenericEdgePainter());
143 implementationsMap.put(GenericEdgeRealizer.PathCalculator.class, new UndulatingPathCalculator());
144 implementationsMap.put(GenericEdgeRealizer.BendPainter.class,
148 new CustomBendPainter(new Ellipse2D.Double(0, 0, 10, 5), new Ellipse2D.Double(0, 0, 10, 10), Color.blue,
149 Color.red));
150 factory.addConfiguration("UndulatingCustomBends", implementationsMap);
151
152 implementationsMap = factory.createDefaultConfigurationMap();
153 implementationsMap.put(GenericEdgeRealizer.ArrowPainter.class, new CenterArrowPainter());
154 implementationsMap.put(GenericEdgeRealizer.PathCalculator.class, new UnclippedPathCalculator());
155 factory.addConfiguration("Unclipped", implementationsMap);
156
157 implementationsMap = factory.createDefaultConfigurationMap();
158 MultiArrowPainter arrowPainter = new MultiArrowPainter();
159 implementationsMap.put(GenericEdgeRealizer.ArrowPainter.class, arrowPainter);
160 factory.addConfiguration("MultiArrow", implementationsMap);
161
162
163 implementationsMap = factory.createDefaultConfigurationMap();
164 implementationsMap.put(GenericEdgeRealizer.Painter.class, new MultiColorEdgePainter());
165 factory.addConfiguration("MultiColorSegments", implementationsMap);
166
167 implementationsMap = factory.createDefaultConfigurationMap();
168 implementationsMap.put(GenericEdgeRealizer.Painter.class, new SignatureEdgePainter("foo", 50));
169 factory.addConfiguration("EdgeSignature", implementationsMap);
170
171 implementationsMap = factory.createDefaultConfigurationMap();
172 implementationsMap.put(GenericEdgeRealizer.Painter.class, new DoubleStrokedPainter());
173 factory.addConfiguration("DoubleStroked", implementationsMap);
174
175 implementationsMap = factory.createDefaultConfigurationMap();
176 implementationsMap.put(GenericEdgeRealizer.Painter.class, new FramedEdgePainter());
177 factory.addConfiguration("Framed", implementationsMap);
178
179 implementationsMap = factory.createDefaultConfigurationMap();
180 implementationsMap.put(GenericEdgeRealizer.Painter.class, new DashedFrameEdgePainter());
181 factory.addConfiguration("FramedDashed", implementationsMap);
182
183
186 GenericEdgeRealizer ger = new GenericEdgeRealizer();
188 ger.setLineType(defaultEdgeLineType);
190 ger.setLineColor(defaultEdgeColor);
191 ger.setConfiguration("Framed");
192 ger.setUserData("This is my own userData object.");
193 view.getGraph2D().setDefaultEdgeRealizer(ger);
194 }
195
196 protected TooltipMode createTooltipMode() {
197 TooltipMode tooltipMode = new TooltipMode() {
198
205 protected String getEdgeTip(Edge edge) {
206 String tipText = null;
207 if (getGraph2D().getRealizer(edge) instanceof GenericEdgeRealizer) {
208 GenericEdgeRealizer realizer = (GenericEdgeRealizer) getGraph2D().getRealizer(edge);
209 tipText = realizer.getConfiguration();
210 }
211 return tipText;
212 }
213 };
214 tooltipMode.setNodeTipEnabled(false);
215 return tooltipMode;
216 }
217
218
223 protected JList createEdgeRealizerList() {
224 final List configurations = new ArrayList(GenericEdgeRealizer.getFactory().getAvailableConfigurations());
225 Collections.sort(configurations);
226
227 final List edgeRealizers = new ArrayList();
229 for (Iterator iterator = configurations.iterator(); iterator.hasNext(); ) {
230 final String configuration = (String) iterator.next();
231 final GenericEdgeRealizer realizer = new GenericEdgeRealizer(configuration);
232 if ("DoubleStroked".equals(configuration)) {
233 realizer.setLineType(LineType.LINE_2);
234 } else if (configuration.startsWith("Framed")) {
235 realizer.setLineType(LineType.LINE_3);
236 realizer.setTargetArrow(Arrow.STANDARD);
237 } else if (configuration.startsWith("Undulating")) {
238 realizer.setStyleProperty("MyFunnyPathCalculator.Wavelength", new Integer(10));
239 } else {
240 realizer.setLineType(defaultEdgeLineType);
241 }
242 realizer.setLineColor(defaultEdgeColor);
243 edgeRealizers.add(realizer);
244 }
245
246 final JList result = new JList(edgeRealizers.toArray()) {
248 public String getToolTipText(MouseEvent evt) {
249 int index = locationToIndex(evt.getPoint());
250 return (String) configurations.get(index);
251 }
252 };
253
254 final int CELL_WIDTH = 150;
256 final int CELL_HEIGHT = 60;
257 result.setCellRenderer(new RealizerCellRenderer(CELL_WIDTH, CELL_HEIGHT) {
258 protected Icon createEdgeRealizerIcon(EdgeRealizer realizer, int iconWidth, int iconHeight) {
259 RealizerCellRenderer.EdgeRealizerIcon icon = new RealizerCellRenderer.EdgeRealizerIcon(realizer, iconWidth,
260 iconHeight) {
261 protected void paintRealizer(EdgeRealizer edgeRealizer, Graphics2D gfx) {
262 edgeRealizer.getPath();
263
264 NodeRealizer sourceNodeRealizer = edgeRealizer.getSourceRealizer();
265 sourceNodeRealizer.setSize(15.0, 15.0);
266 sourceNodeRealizer.setCenter(15.0, calculateSourceBend(edgeRealizer, CELL_WIDTH, CELL_HEIGHT).getY());
267 sourceNodeRealizer.setFillColor(DemoDefaults.DEFAULT_NODE_COLOR);
268 sourceNodeRealizer.setLineColor(DemoDefaults.DEFAULT_NODE_LINE_COLOR);
269 sourceNodeRealizer.setVisible(true);
270 sourceNodeRealizer.paint(gfx);
271
272 NodeRealizer targetNodeRealizer = edgeRealizer.getTargetRealizer();
273 targetNodeRealizer.setSize(15.0, 15.0);
274 targetNodeRealizer.setCenter(CELL_WIDTH - 15.0,
275 calculateTargetBend(edgeRealizer, CELL_WIDTH, CELL_HEIGHT).getY());
276 targetNodeRealizer.setFillColor(DemoDefaults.DEFAULT_NODE_COLOR);
277 targetNodeRealizer.setLineColor(DemoDefaults.DEFAULT_NODE_LINE_COLOR);
278 targetNodeRealizer.setVisible(true);
279 targetNodeRealizer.paint(gfx);
280
281 edgeRealizer.paint(gfx);
282 }
283 };
284 icon.setDrawingBends(false);
285 return icon;
286 }
287 });
288
289 result.addListSelectionListener(new ListSelectionListener() {
291 public void valueChanged(ListSelectionEvent e) {
292 EdgeRealizer edgeRealizer = (EdgeRealizer) result.getSelectedValue();
293 view.getGraph2D().setDefaultEdgeRealizer(edgeRealizer);
294 }
295 });
296
297 result.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
299
300 final int idx = indexOf(edgeRealizers, view.getGraph2D().getDefaultEdgeRealizer());
301 if (-1 < idx && idx < edgeRealizers.size()) {
302 result.setSelectedIndex(idx);
303 } else {
304 result.setSelectedIndex(0);
305 }
306 return result;
307 }
308
309
317 private static int indexOf( final List haystack, final EdgeRealizer needle ) {
318 final String c =
319 needle instanceof GenericEdgeRealizer
320 ? ((GenericEdgeRealizer) needle).getConfiguration()
321 : null;
322
323 int i = 0;
324 for (Iterator it = haystack.iterator(); it.hasNext(); ++i) {
325 final GenericEdgeRealizer next = (GenericEdgeRealizer) it.next();
326 if (c == null ? null == next.getConfiguration() : c.equals(next.getConfiguration())) {
327 return i;
328 }
329 }
330 return -1;
331 }
332
333
336 static final class CustomEdgePainter extends GenericEdgePainter {
337 protected void paintPath(EdgeRealizer context, BendList bends, GeneralPath path, Graphics2D gfx, boolean selected) {
338 Stroke s = gfx.getStroke();
339 Color oldColor = gfx.getColor();
340 if (s instanceof BasicStroke) {
341 Color c;
342 if (selected) {
343 initializeSelectionLine(context, gfx, selected);
344 c = gfx.getColor();
345 } else {
346 initializeLine(context, gfx, selected);
347 c = gfx.getColor();
348 gfx.setColor(new Color(0, 0, 0, 64));
349 gfx.translate(4, 4);
350 gfx.draw(path);
351 gfx.translate(-4, -4);
352 }
353 Color newC = selected ? Color.RED : c;
354 gfx.setColor(new Color(128 + newC.getRed() / 2, 128 + newC.getGreen() / 2, 128 + newC.getBlue() / 2));
355 gfx.translate(-1, -1);
356 gfx.draw(path);
357 gfx.setColor(new Color(newC.getRed() / 2, newC.getGreen() / 2, newC.getBlue() / 2));
358 gfx.translate(2, 2);
359 gfx.draw(path);
360 gfx.translate(-1, -1);
361 gfx.setColor(c);
362 gfx.draw(path);
363 gfx.setColor(oldColor);
364 } else {
365 gfx.draw(path);
366 }
367 }
368 }
369
370 static final class MultiColorEdgePainter extends GenericEdgePainter {
371 protected void paintPath(EdgeRealizer context, BendList bends, GeneralPath path, Graphics2D gfx, boolean selected) {
372 Color oldColor = gfx.getColor();
373 if (selected) {
374 initializeSelectionLine(context, gfx, selected);
375 } else {
376 initializeLine(context, gfx, selected);
377 }
378 double[] segmentCoords = new double[6];
379 PathIterator iterator = path.getPathIterator(new AffineTransform());
380 double lastX = 0;
381 double lastY = 0;
382 final Line2D.Double doubleLine = new Line2D.Double();
383 while (!iterator.isDone()) {
384 int type = iterator.currentSegment(segmentCoords);
385 switch (type) {
386 case PathIterator.SEG_MOVETO:
387 lastX = segmentCoords[0];
388 lastY = segmentCoords[1];
389 break;
390 case PathIterator.SEG_LINETO:
391 doubleLine.x1 = lastX;
392 doubleLine.y1 = lastY;
393 doubleLine.x2 = segmentCoords[0];
394 doubleLine.y2 = segmentCoords[1];
395 gfx.draw(doubleLine);
396 lastX = segmentCoords[0];
397 lastY = segmentCoords[1];
398 gfx.setColor(getNextColor(gfx.getColor()));
399 break;
400 default:
401 break;
402 }
403 iterator.next();
404 }
405 gfx.setColor(oldColor);
406 }
407
408
418 protected Color getNextColor(Color currentColor) {
419 float[] colorInHsb = Color.RGBtoHSB(currentColor.getRed(), currentColor.getGreen(), currentColor.getBlue(), null);
420 final float step = 0.07f;
421 return Color.getHSBColor(colorInHsb[0], colorInHsb[1], Math.max(0, colorInHsb[2] - step));
422 }
423 }
424
425
428 static final class DoubleStrokedPainter extends GenericEdgePainter {
429 protected void renderPath(
430 final EdgeRealizer context,
431 final Graphics2D gfx,
432 final GeneralPath path,
433 final boolean selected
434 ) {
435 final PathIterator it = path.getPathIterator(null);
436 if (!it.isDone()) {
437 final Line2D.Double line = new Line2D.Double();
438 final double[] buffer = new double[6];
439
440 while (!it.isDone()) {
441 switch (it.currentSegment(buffer)) {
442 case PathIterator.SEG_MOVETO:
443 line.x2 = buffer[0];
444 line.y2 = buffer[1];
445 break;
446 case PathIterator.SEG_LINETO:
447 line.x2 = buffer[0];
448 line.y2 = buffer[1];
449 renderSegment(gfx, line);
450 break;
451 case PathIterator.SEG_QUADTO:
452 line.x2 = buffer[2];
454 line.y2 = buffer[3];
455 renderSegment(gfx, line);
456 break;
457 case PathIterator.SEG_CUBICTO:
458 line.x2 = buffer[4];
460 line.y2 = buffer[5];
461 renderSegment(gfx, line);
462 break;
463 }
467
468 it.next();
469 line.x1 = line.x2;
470 line.y1 = line.y2;
471 }
472 }
473 }
474
475
480 private void renderSegment( final Graphics2D gfx, final Line2D.Double line ) {
481 final double x1 = line.x1;
482 final double y1 = line.y1;
483 final double x2 = line.x2;
484 final double y2 = line.y2;
485
486 try {
487 final double ox = y2 - y1;
488 final double oy = x1 - x2;
489 final double ol = Math.sqrt(ox * ox + oy * oy);
490
491 if (ol > 0) {
492 final double xOffset = (2 * ox) / ol;
493 final double yOffset = (2 * oy) / ol;
494 line.x1 = x1 + xOffset;
495 line.y1 = y1 + yOffset;
496 line.x2 = x2 + xOffset;
497 line.y2 = y2 + yOffset;
498 gfx.draw(line);
499 line.x1 = x1 - xOffset;
500 line.y1 = y1 - yOffset;
501 line.x2 = x2 - xOffset;
502 line.y2 = y2 - yOffset;
503 gfx.draw(line);
504 } else {
505 gfx.draw(line);
506 }
507 } finally {
508 line.x1 = x1;
509 line.y1 = y1;
510 line.x2 = x2;
511 line.y2 = y2;
512 }
513 }
514 }
515
516 static final class SignatureEdgePainter extends GenericEdgePainter {
517 private String signature;
518 private double distance;
519
520 public SignatureEdgePainter(String signature, double distance) {
521 this.signature = signature;
522 if (distance <= 0d) {
523 throw new IllegalArgumentException("distance <= 0");
524 }
525 this.distance = distance;
526 }
527
528 protected void paintPath(EdgeRealizer context, BendList bends, GeneralPath path, Graphics2D gfx, boolean selected) {
529 super.paintPath(context, bends, path, gfx, selected);
531
532 Color oldColor = gfx.getColor();
534 gfx.setColor(Color.BLACK);
535 double length = getPathLength(path);
536 int numberOfSignatures = (int) Math.floor(length / distance);
537 if (numberOfSignatures < 1) {
538 return;
539 }
540 double slack = length - numberOfSignatures * distance;
541 double distToGo = 0.5 * (slack + distance);
542 double currentLength = 0.0;
543 for (CustomPathIterator pi = new CustomPathIterator(path, 1.0); pi.ok(); pi.next()) {
544 YPoint segmentStart = pi.segmentStart();
545 YVector segmentDirection = pi.segmentDirection();
546 double segmentLength = segmentDirection.length();
547 while (distToGo < segmentLength && currentLength + distToGo < length) {
548 segmentDirection.scale(distToGo / segmentLength);
549 YPoint location = segmentStart.moveBy(segmentDirection.getX(), segmentDirection.getY());
550 AffineTransform oldTransform = gfx.getTransform();
551 AffineTransform newTransform = gfx.getTransform();
552 double theta = Math.atan2(segmentDirection.getY(), segmentDirection.getX());
553 newTransform.rotate(theta, location.getX(), location.getY());
554 gfx.setTransform(newTransform);
555 gfx.drawString(signature, (float) location.getX(), (float) location.getY());
556 gfx.setTransform(oldTransform);
557 segmentDirection = pi.segmentDirection();
558 distToGo += distance;
559 }
560 distToGo -= segmentLength;
561 currentLength += segmentLength;
562 }
563 gfx.setColor(oldColor);
564 }
565 }
566
567
570 public static final class DashedFrameEdgePainter extends FramedEdgePainter {
571 protected void paintForeground(
572 final EdgeRealizer context,
573 final Graphics2D gfx,
574 final GeneralPath path, final GeneralPath outline,
575 final boolean selected
576 ) {
577 final LineType s = context.getLineType();
578 gfx.setStroke(createInnerPattern(s.getLineWidth()));
579 gfx.draw(path);
580
581 super.paintForeground(context, gfx, path, outline, selected);
582 }
583
584
590 protected BasicStroke createInnerPattern( final float w ) {
591 final float dash = w * 1.5f;
592 return new BasicStroke(
593 w,
594 BasicStroke.CAP_BUTT,
595 BasicStroke.JOIN_MITER,
596 2,
597 new float[]{dash, w*2},
598 dash);
599 }
600 }
601
602
610 static final class PortMoverPathCalculator implements GenericEdgeRealizer.PathCalculator {
611 private GenericEdgeRealizer.PathCalculator innerCalculator;
612
613 PortMoverPathCalculator(GenericEdgeRealizer.PathCalculator innerCalculator) {
614 this.innerCalculator = innerCalculator;
615 }
616
617 public byte calculatePath(EdgeRealizer context, BendList bends, GeneralPath path,
618 Point2D sourceIntersectionPointOut,
619 Point2D targetIntersectionPointOut) {
620 final Port sp = context.getSourcePort();
621 final Port tp = context.getTargetPort();
622 final NodeRealizer snr = context.getSourceRealizer();
623 final NodeRealizer tnr = context.getTargetRealizer();
624 if (bends.size() > 0) {
625 adjustPort(bends.firstBend(), snr, sp);
626 adjustPort((Bend) bends.last(), tnr, tp);
627 } else {
628 double minx = Math.max(snr.getX(), tnr.getX());
629 double maxx = Math.min(snr.getX() + snr.getWidth(), tnr.getX() + tnr.getWidth());
630 if (maxx >= minx) {
631 double pos = (minx + maxx) * 0.5d;
632 sp.setOffsetX(pos - snr.getCenterX());
633 tp.setOffsetX(pos - tnr.getCenterX());
634 }
635 double miny = Math.max(snr.getY(), tnr.getY());
636 double maxy = Math.min(snr.getY() + snr.getHeight(), tnr.getY() + tnr.getHeight());
637 if (maxy >= miny) {
638 double pos = (miny + maxy) * 0.5d;
639 sp.setOffsetY(pos - snr.getCenterY());
640 tp.setOffsetY(pos - tnr.getCenterY());
641 }
642 }
643 return innerCalculator.calculatePath(context, bends, path, sourceIntersectionPointOut,
644 targetIntersectionPointOut);
645 }
646
647 private void adjustPort(Bend b, NodeRealizer realizer, Port port) {
648 double x = b.getX();
649 double y = b.getY();
650 boolean inXRange = x >= realizer.getX() && x <= realizer.getX() + realizer.getWidth();
651 boolean inYRange = y >= realizer.getY() && y <= realizer.getY() + realizer.getHeight();
652 if (inXRange && !inYRange) {
653 port.setOffsetX(x - realizer.getCenterX());
654 }
655 if (inYRange && !inXRange) {
656 port.setOffsetY(y - realizer.getCenterY());
657 }
658 }
659 }
660
661
664 static final class MyPathCalculator extends PolyLinePathCalculator implements GenericEdgeRealizer.PathCalculator {
665 private final GeneralPath scratch = new GeneralPath();
666
667
668 public byte calculatePath(EdgeRealizer context, BendList bends, GeneralPath path,
669 Point2D sourceIntersectionPointOut,
670 Point2D targetIntersectionPointOut) {
671 if (bends.size() == 0) {
672 return super.calculatePath(context, bends, path, sourceIntersectionPointOut, targetIntersectionPointOut);
673 } else {
674 final int npoints = bends.size();
675
676 path.reset();
677 scratch.reset();
678
679 NodeRealizer nr = context.getSourceRealizer();
680 Port pp = context.getSourcePort();
681 float lastPointx;
682 float lastPointy;
683 float secondLastPointx;
684 float secondLastPointy;
685 scratch.moveTo(lastPointx = (float) pp.getX(nr), lastPointy = (float) pp.getY(nr));
686
687 int index = 0;
688
689 secondLastPointx = lastPointx;
690 secondLastPointy = lastPointy;
691
692 BendCursor bc = bends.bends();
693
694 {
695 Bend b = bc.bend();
696 lastPointx = (float) b.getX();
697 lastPointy = (float) b.getY();
698 bc.next();
699 index++;
700 }
701
702 for (; index < npoints; bc.next(), index++) {
703 Bend b = bc.bend();
704 float nextPointx = (float) b.getX();
705 float nextPointy = (float) b.getY();
706 {
707 final float sx = 0.5f * lastPointx + secondLastPointx * 0.5f;
708 final float sy = 0.5f * lastPointy + secondLastPointy * 0.5f;
709 scratch.lineTo(sx, sy);
710 }
711 {
712 final float sx = 0.5f * nextPointx + lastPointx * 0.5f;
713 final float sy = 0.5f * nextPointy + lastPointy * 0.5f;
714 scratch.quadTo(lastPointx, lastPointy, sx, sy);
715 secondLastPointx = lastPointx;
716 secondLastPointy = lastPointy;
717 lastPointx = nextPointx;
718 lastPointy = nextPointy;
719 }
720 }
721
722 nr = context.getTargetRealizer();
723 pp = context.getTargetPort();
724
725 {
726 float nextPointx = (float) pp.getX(nr);
727 float nextPointy = (float) pp.getY(nr);
728 {
729 final float sx = 0.5f * lastPointx + secondLastPointx * 0.5f;
730 final float sy = 0.5f * lastPointy + secondLastPointy * 0.5f;
731 scratch.lineTo(sx, sy);
732 }
733 {
734 final float sx = 0.5f * nextPointx + lastPointx * 0.5f;
735 final float sy = 0.5f * nextPointy + lastPointy * 0.5f;
736 scratch.quadTo(lastPointx, lastPointy, sx, sy);
737 }
738 scratch.lineTo(nextPointx, nextPointy);
739 }
740 path.append(scratch.getPathIterator(null, 1.0), false);
741 }
742 return EdgeRealizer.calculateClippingAndIntersection(context, path, path, sourceIntersectionPointOut,
743 targetIntersectionPointOut);
744 }
745 }
746
747
750 static final class UndulatingPathCalculator extends PolyLinePathCalculator implements GenericEdgeRealizer.PathCalculator {
751 private final GeneralPath scratch = new GeneralPath();
752
753 public byte calculatePath(EdgeRealizer context, BendList bends, GeneralPath path,
754 Point2D sourceIntersectionPointOut,
755 Point2D targetIntersectionPointOut) {
756 scratch.reset();
757
758 NodeRealizer nr = context.getSourceRealizer();
759 Port pp = context.getSourcePort();
760 float lastPointX;
761 float lastPointY;
762 scratch.moveTo(lastPointX = (float) pp.getX(nr), lastPointY = (float) pp.getY(nr));
763
764 int wobbleCount = 0;
765 for (BendCursor bc = bends.bends(); bc.ok(); bc.next()) {
766 Bend b = bc.bend();
767 float nextPointX = (float) b.getX();
768 float nextPointY = (float) b.getY();
769 float dx = nextPointX - lastPointX;
770 float dy = nextPointY - lastPointY;
771 float len = (float) Math.sqrt(dx * dx + dy * dy);
772 if (len > 0) {
773 int count = (int) (len / getWavelength(context)) + 1;
774 for (int i = 0; i < count; i++) {
775 final float height = wobbleCount % 2 == 0 ? 10 : -10;
776 wobbleCount++;
777 scratch.quadTo(lastPointX + (i + 0.5f) / ((float) count) * dx + dy * height / len,
778 lastPointY + (i + 0.5f) / ((float) count) * dy - dx * height / len,
779 lastPointX + (i + 1) / ((float) count) * dx, lastPointY + (i + 1) / ((float) count) * dy);
780 }
781 } else {
782 scratch.lineTo(nextPointX, nextPointY);
783 }
784 lastPointX = nextPointX;
785 lastPointY = nextPointY;
786 }
787
788 nr = context.getTargetRealizer();
789 pp = context.getTargetPort();
790
791 {
792 float nextPointX = (float) pp.getX(nr);
793 float nextPointY = (float) pp.getY(nr);
794 float dx = nextPointX - lastPointX;
795 float dy = nextPointY - lastPointY;
796 float len = (float) Math.sqrt(dx * dx + dy * dy);
797 if (len > 0) {
798 int count = (int) (len / getWavelength(context)) + 1;
799 for (int i = 0; i < count; i++) {
800 final float height = wobbleCount % 2 == 0 ? 10 : -10;
801 wobbleCount++;
802 scratch.quadTo(lastPointX + (i + 0.5f) / ((float) count) * dx + dy * height / len,
803 lastPointY + (i + 0.5f) / ((float) count) * dy - dx * height / len,
804 lastPointX + (i + 1) / ((float) count) * dx, lastPointY + (i + 1) / ((float) count) * dy);
805 }
806 } else {
807 scratch.lineTo(nextPointX, nextPointY);
808 }
809 }
810 path.reset();
811 return EdgeRealizer.calculateClippingAndIntersection(context, scratch, path, sourceIntersectionPointOut,
812 targetIntersectionPointOut);
813 }
814
815 protected double getWavelength(EdgeRealizer context) {
816 Object o = ((GenericEdgeRealizer) context).getStyleProperty("MyFunnyPathCalculator.Wavelength");
817 if (o instanceof Number) {
818 return ((Number) o).doubleValue();
819 }
820 return 30;
821 }
822 }
823
824
825
829 static final class CustomBendPainter implements GenericEdgeRealizer.BendPainter {
830 private RectangularShape shape;
831 private RectangularShape selectedShape;
832 private Color fillColor;
833 private Color selectedFillColor;
834
835 CustomBendPainter(RectangularShape shape, RectangularShape selectedShape, Color fillColor,
836 Color selectedFillColor) {
837 this.selectedShape = selectedShape;
838 this.shape = shape;
839 this.fillColor = fillColor;
840 this.selectedFillColor = selectedFillColor;
841 }
842
843 public void paintBends(EdgeRealizer context, BendList bends, GeneralPath path, Graphics2D gfx, boolean selected) {
844 if (!bends.isEmpty()) {
845 final boolean useSelectionStyle = YRenderingHints.isSelectionPaintingEnabled(gfx);
846
847 final Color oldColor = gfx.getColor();
848 for (BendCursor bendCursor = bends.bends(); bendCursor.ok(); bendCursor.next()) {
849 Bend b = bendCursor.bend();
850 gfx.setColor(((selected || b.isSelected()) && useSelectionStyle) ? this.selectedFillColor : this.fillColor);
851 final double x = b.getX();
852 final double y = b.getY();
853 RectangularShape shape = selected ? this.selectedShape : this.shape;
854 shape.setFrame(x - shape.getWidth() / 2, y - shape.getHeight() / 2, shape.getWidth(), shape.getHeight());
855 gfx.fill(shape);
856 }
857 gfx.setColor(oldColor);
858 }
859 }
860 }
861
862
866 public static final class CenterArrowPainter implements GenericEdgeRealizer.ArrowPainter {
867 public void paintArrows(EdgeRealizer context, BendList bends, GeneralPath path, Graphics2D gfx) {
868 Arrow targetArrow = context.getTargetArrow();
869 if (targetArrow != null) {
870
871 Point2D sourceIntersection = context.getSourceIntersection();
872 Point2D targetIntersection = context.getTargetIntersection();
873
874 if (bends.size() > 0) {
875 int mid = bends.size() / 2;
876 if (mid > 0) {
877 Bend bend = context.getBend(mid - 1);
878 sourceIntersection.setLocation(bend.getX(), bend.getY());
879 }
880 {
881 Bend bend = context.getBend(mid);
882 targetIntersection.setLocation(bend.getX(), bend.getY());
883 }
884 }
885
886 double centerX = (targetIntersection.getX() + sourceIntersection.getX()) * 0.5d;
887 double centerY = (targetIntersection.getY() + sourceIntersection.getY()) * 0.5d;
888 double dx = (targetIntersection.getX() - sourceIntersection.getX());
889 double dy = (targetIntersection.getY() - sourceIntersection.getY());
890 double l = Math.sqrt(dx * dx + dy * dy);
891 double arrowScaleFactor = context.getArrowScaleFactor();
892 if (l > 0) {
893 targetArrow.paint(gfx, centerX, centerY, arrowScaleFactor * dx / l, arrowScaleFactor * dy / l);
894 }
895 }
896 }
897 }
898
899
903 public static final class MultiArrowPainter implements GenericEdgeRealizer.ArrowPainter {
904 private double threshold = 50d;
905 private Arrow arrow = Arrow.DELTA;
906 private Color color = Color.LIGHT_GRAY;
907
908
913 public Arrow getArrow() {
914 return arrow;
915 }
916
917
922 public void setArrow(Arrow arrow) {
923 this.arrow = arrow;
924 }
925
926
927
932 public Color getColor() {
933 return color;
934 }
935
936
941 public void setColor(Color color) {
942 this.color = color;
943 }
944
945
955 public void paintArrows(EdgeRealizer context, BendList bends, GeneralPath path, Graphics2D gfx) {
956 if (arrow != null) {
957
958 PathIterator iter = path.getPathIterator(null, 1);
959 double[] curSeg = new double[2];
960 if (!iter.isDone()) {
961 iter.currentSegment(curSeg);
962 Point2D p1 = new Point2D.Double(curSeg[0], curSeg[1]);
963 Point2D p0 = new Point2D.Double();
964 for (iter.next(); !iter.isDone(); iter.next()) {
965 p0.setLocation(p1);
966 iter.currentSegment(curSeg);
967 p1.setLocation(curSeg[0], curSeg[1]);
968 paintArrow(p1, p0, context, gfx);
969 }
970 }
971 }
972 }
973
974 private void paintArrow(Point2D p1, Point2D p0, EdgeRealizer context, Graphics2D gfx) {
975 if (arrow != null) {
976 double centerX = (p1.getX() + p0.getX()) * 0.5d;
977 double centerY = (p1.getY() + p0.getY()) * 0.5d;
978 double dx = (p1.getX() - p0.getX());
979 double dy = (p1.getY() - p0.getY());
980 double l = Math.sqrt(dx * dx + dy * dy);
981 double dxNormalized = dx / l;
982 double dyNormalized = dy / l;
983 if (l > threshold) {
984 double arrowScaleFactor = context.getArrowScaleFactor();
985 double offset = arrowScaleFactor * (arrow.getArrowLength() + arrow.getClipLength()) * 0.5d;
986 double x = centerX + offset * dxNormalized;
987 double y = centerY + offset * dyNormalized;
988 Color oldColor = gfx.getColor();
989 gfx.setColor(color);
990 arrow.paint(gfx, x, y, arrowScaleFactor * dxNormalized, arrowScaleFactor * dyNormalized);
991 gfx.setColor(oldColor);
992 }
993 }
994 }
995 }
996
997
1000 public static final class UnclippedPathCalculator implements GenericEdgeRealizer.PathCalculator {
1001 public byte calculatePath(EdgeRealizer context, BendList bends, GeneralPath path,
1002 Point2D sourceIntersectionPointOut,
1003 Point2D targetIntersectionPointOut) {
1004 sourceIntersectionPointOut.setLocation(context.getSourcePort().getX(context.getSourceRealizer()),
1005 context.getSourcePort().getY(context.getSourceRealizer()));
1006 targetIntersectionPointOut.setLocation(context.getTargetPort().getX(context.getTargetRealizer()),
1007 context.getTargetPort().getY(context.getTargetRealizer()));
1008 path.reset();
1009 path.moveTo((float) sourceIntersectionPointOut.getX(), (float) sourceIntersectionPointOut.getY());
1010 for (ListCell cell = bends.firstCell(); cell != null; cell = cell.succ()) {
1011 Bend b = (Bend) cell.getInfo();
1012 path.lineTo((float) b.getX(), (float) b.getY());
1013 }
1014 path.lineTo((float) targetIntersectionPointOut.getX(), (float) targetIntersectionPointOut.getY());
1015
1016 return EdgeRealizer.PATH_CLIPPED_AT_SOURCE_AND_TARGET;
1017 }
1018 }
1019
1020
1023 public static void main(String[] args) {
1024 EventQueue.invokeLater(new Runnable() {
1025 public void run() {
1026 Locale.setDefault(Locale.ENGLISH);
1027 initLnF();
1028 new GenericEdgeRealizerDemo().start("GenericEdgeRealizer Demo");
1029 }
1030 });
1031 }
1032
1033 private static double getPathLength(GeneralPath path) {
1034 double length = 0.0;
1035 for (CustomPathIterator pi = new CustomPathIterator(path, 1.0); pi.ok(); pi.next()) {
1036 length += pi.segmentDirection().length();
1037 }
1038 return length;
1039 }
1040
1041
1044 static class CustomPathIterator {
1045 private double[] cachedSegment;
1046 private boolean moreToGet;
1047 private PathIterator pathIterator;
1048
1049 public CustomPathIterator(GeneralPath path, double flatness) {
1050 pathIterator = (new GeneralPath(path)).getPathIterator(new AffineTransform(), flatness);
1052 cachedSegment = new double[4];
1053 getFirstSegment();
1054 }
1055
1056 public boolean ok() {
1057 return moreToGet;
1058 }
1059
1060 public boolean isDone() {
1061 return !moreToGet;
1062 }
1063
1064 public final double[] segment() {
1065 if (moreToGet) {
1066 return cachedSegment;
1067 } else {
1068 return null;
1069 }
1070 }
1071
1072 public YPoint segmentStart() {
1073 if (moreToGet) {
1074 return new YPoint(cachedSegment[0], cachedSegment[1]);
1075 } else {
1076 return null;
1077 }
1078 }
1079
1080 public YPoint segmentEnd() {
1081 if (moreToGet) {
1082 return new YPoint(cachedSegment[2], cachedSegment[3]);
1083 } else {
1084 return null;
1085 }
1086 }
1087
1088 public YVector segmentDirection() {
1089 if (moreToGet) {
1090 return new YVector(segmentEnd(), segmentStart());
1091 } else {
1092 return null;
1093 }
1094 }
1095
1096 public void next() {
1097 if (!pathIterator.isDone()) {
1098 float[] curSeg = new float[6];
1099 cachedSegment[0] = cachedSegment[2];
1100 cachedSegment[1] = cachedSegment[3];
1101 pathIterator.currentSegment(curSeg);
1102 cachedSegment[2] = curSeg[0];
1103 cachedSegment[3] = curSeg[1];
1104 pathIterator.next();
1105 } else {
1106 moreToGet = false;
1107 }
1108 }
1109
1110 private void getFirstSegment() {
1111 float[] curSeg = new float[6];
1112 if (!pathIterator.isDone()) {
1113 pathIterator.currentSegment(curSeg);
1114 cachedSegment[0] = curSeg[0];
1115 cachedSegment[1] = curSeg[1];
1116 pathIterator.next();
1117 moreToGet = true;
1118 } else {
1119 moreToGet = false;
1120 }
1121 if (!pathIterator.isDone()) {
1122 pathIterator.currentSegment(curSeg);
1123 cachedSegment[2] = curSeg[0];
1124 cachedSegment[3] = curSeg[1];
1125 pathIterator.next();
1126 moreToGet = true;
1127 } else {
1128 moreToGet = false;
1129 }
1130 }
1131 }
1132}
1133