1   /****************************************************************************
2    **
3    ** This file is part of yFiles-2.6. 
4    ** 
5    ** yWorks proprietary/confidential. Use is subject to license terms.
6    **
7    ** Redistribution of this file or of an unauthorized byte-code version
8    ** of this file is strictly forbidden.
9    **
10   ** Copyright (c) 2000-2008 by yWorks GmbH, Vor dem Kreuzberg 28, 
11   ** 72070 Tuebingen, Germany. All rights reserved.
12   **
13   ***************************************************************************/
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  /**
47   * This class demonstrates various usages of the {@link y.view.GenericEdgeRealizer} class.
48   */
49  public class GenericEdgeRealizerDemo extends DemoBase
50  {
51    private static final boolean INITIAL_ANTIALIASING_STATE = true;
52  
53    /** Creates the GenericEdgeRealizer demo. */
54    public GenericEdgeRealizerDemo()
55    {
56      super();
57  
58      // Take a default GenericEdgeRealizer...
59      GenericEdgeRealizer ger = new GenericEdgeRealizer();
60      // ... and make it real flashy.
61      ger.setLineType(LineType.LINE_4);
62      ger.setLineColor(Color.green);
63  
64      view.setAntialiasedPainting(INITIAL_ANTIALIASING_STATE);
65  
66      // Get the factory to register custom styles/configurations.
67      GenericEdgeRealizer.Factory factory = GenericEdgeRealizer.getFactory();
68  
69      // Retrieve a map that holds the default GenericEdgeRealizer configuration.
70      // The implementations contained therein can be replaced one by one in order
71      // to create custom configurations...
72      Map implementationsMap = factory.createDefaultConfigurationMap();
73  
74      // The edge path is painted 3D-ish and with a drop shadow.
75      implementationsMap.put(GenericEdgeRealizer.Painter.class, new CustomEdgePainter());
76      // The path is calculated to be undulating.
77      implementationsMap.put(GenericEdgeRealizer.PathCalculator.class, new MyFunnyPathCalculator());
78  
79      // Add the first configuration to the factory.
80      factory.addConfiguration("Undulating", implementationsMap);
81  
82      implementationsMap.put(GenericEdgeRealizer.PathCalculator.class, new MyPathCalculator());
83      // Add the second configuration to the factory.
84      // NB: It uses the same type of painter as the previous configuration.
85      factory.addConfiguration("QuadCurve", implementationsMap);
86  
87      // Special behavior for an otherwise normal poly-line edge path calculator:
88      // first and last segment of the edge path are kept axes-parallel.
89      implementationsMap.put(GenericEdgeRealizer.PathCalculator.class, new PortMoverPathCalculator(new PolyLinePathCalculator()));
90      factory.addConfiguration("PolyLineAxesParallel", implementationsMap);
91  
92      // Default edge painter implementation.
93  
94      implementationsMap = factory.createDefaultConfigurationMap();
95      implementationsMap.put(GenericEdgeRealizer.Painter.class, new GenericEdgePainter());
96      implementationsMap.put(GenericEdgeRealizer.PathCalculator.class, new MyFunnyPathCalculator());
97      // Bends are rendered differently depending on their selection state.
98      // - normal rendering: blue ellipse (height is half of width)
99      // - rendering when bend is selected: red ellipse
100     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     // Initialize the GenericEdgeRealizer instance to one of the types we just
116     // registered with the factory.
117     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   /** Creates a toolbar that allows to switch the default edge realizer type. */
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   /**
175    * A custom EdgePainter implementation that draws the edge path 3D-ish and adds
176    * a drop shadow also.
177    */
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   /**
213    * A custom PathCalculator implementation that keeps the first and last segment
214    * of an edge path axes-parallel.
215    * To achieve this behavior, the edge's source port and target port are moved
216    * to match any movement of the bend at the opposite of the respective segment.
217    * <p>
218    * If the edge path has only a single segment, it is drawn axes-parallel as soon
219    * as the projections of the two nodes overlap on either x-axis or y-axis.
220    */
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   /**
271    * A custom PathCalculator implementation that draws a quad curve edge path.
272    */
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   /**
355    * A custom PathCalculator implementation that draws an undulating edge path.
356    */
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   /**
420    * A custom BendPainter implementation that renders bends differently depending
421    * on the bend's selection state, but also the edge's selection state.
422    */
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   /**
454    * A simple ArrowPainter implementation that
455    * paints the arrow at the center of the segment in the
456    * middle of the poly-line control path.
457    */
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   /**
492    * An ArrowPainter implementation that paints an arrow at the center of each long enough segment of an edge.
493    * The edge is assumed to be a straight line edge.
494    */
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     /**
502      * @return the minimum length of a segment, for which an arrow is drawn
503      */
504     public double getThreshold() {
505       return threshold;
506     }
507 
508     /**
509      * sets the minimum length of a segment, for which an arrow is drawn
510      * @param threshold the minimum length
511      */
512     public void setThreshold(double threshold) {
513       this.threshold = threshold;
514     }
515 
516 
517     /**
518      * @return the arrow used for drawing
519      */
520     public Arrow getArrow() {
521       return arrow;
522     }
523 
524     /**
525      * sets the arrow used for drawing
526      * @param arrow an arrow
527      */
528     public void setArrow(Arrow arrow) {
529       this.arrow = arrow;
530     }
531 
532 
533     /**
534      * returns the color used for drawing the arrows
535      * @return the color of the arrows
536      */
537     public Color getColor() {
538       return color;
539     }
540 
541     /**
542      * sets the color used for drawing the arrows
543      * @param color a color
544      */
545     public void setColor(Color color) {
546       this.color = color;
547     }
548 
549     /**
550      * paints arrows at each segment of an edge, which is long enough
551      * @param context the realizer of the edge
552      * @param bends the bends of the edge
553      * @param path the path of the edge
554      * @param gfx the graphics to paint on
555      * @see #setThreshold(double)
556      * @see #setColor(Color)
557      * @see #setArrow(Arrow)
558      */
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   /**
602    * A simple custom PathCalculator implementation that
603    * performs no clipping of the ends at the adjacent nodes.
604    */
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   /**
623    * Launcher method.
624    * Execute this class to see sample instantiations of {@link GenericEdgeRealizer}
625    * in action.
626    */
627   public static void main(String[] args)
628   {
629     new GenericEdgeRealizerDemo().start("GenericEdgeRealizer Demo");
630   }
631 }
632