1   /****************************************************************************
2    * This demo file is part of yFiles for Java 2.14.
3    * Copyright (c) 2000-2017 by yWorks GmbH, Vor dem Kreuzberg 28,
4    * 72070 Tuebingen, Germany. All rights reserved.
5    * 
6    * yFiles demo files exhibit yFiles for Java functionalities. Any redistribution
7    * of demo files in source code or binary form, with or without
8    * modification, is not permitted.
9    * 
10   * Owners of a valid software license for a yFiles for Java version that this
11   * demo is shipped with are allowed to use the demo source code as basis
12   * for their own yFiles for Java powered applications. Use of such programs is
13   * governed by the rights and conditions as set out in the yFiles for Java
14   * license agreement.
15   * 
16   * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESS OR IMPLIED
17   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18   * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
19   * NO EVENT SHALL yWorks BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
21   * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23   * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24   * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26   *
27   ***************************************************************************/
28  package demo.view.mindmap;
29  
30  import y.base.Edge;
31  import y.base.EdgeCursor;
32  import y.base.Node;
33  import y.base.NodeCursor;
34  import y.base.YList;
35  import y.util.Cursors;
36  import y.view.EdgeRealizer;
37  import y.view.Graph2D;
38  import y.view.Graph2DTraversal;
39  import y.view.NodeLabel;
40  import y.view.NodeRealizer;
41  import y.view.OrderRenderer;
42  
43  import java.awt.Graphics2D;
44  import java.awt.Rectangle;
45  import java.awt.geom.Rectangle2D;
46  import java.util.Iterator;
47  
48  /**
49   * Renders the elements of a mind map in a specific order. This specific
50   * rendering order is
51   * <ol>
52   * <li>normal edges,</li>
53   * <li>nodes,</li>
54   * <li>cross-reference edges, and</li>
55   * <li>node labels</li>
56   * </ol>
57   */
58  class MindMapRenderer implements OrderRenderer {
59    /**
60     * The element order for hit-testing and rendering the mind map.
61     */
62    private final Graph2DTraversal order;
63  
64    /**
65     * Initializes a new <code>MindMapRenderer</code> instance.
66     */
67    MindMapRenderer() {
68      this.order = new MindMapTraversal();
69    }
70  
71    /**
72     * Returns the element order for hit-testing and rendering the mind map in
73     * high-detail mode.
74     */
75    public Graph2DTraversal getPaintOrder() {
76      return order;
77    }
78  
79    /**
80     * Returns the element order for hit-testing and rendering the mind map in
81     * low-detail mode.
82     */
83    public Graph2DTraversal getSloppyPaintOrder() {
84      return order;
85    }
86  
87    /**
88     * Renders the given graph in high-detail mode.
89     * @param gfx the graphics context to paint upon.
90     * @param graph the mind map to paint.
91     */
92    public void paint( final Graphics2D gfx, final Graph2D graph) {
93      paintCore(gfx, graph, false);
94    }
95  
96    /**
97     * Renders the given graph in low-detail mode.
98     * @param gfx the graphics context to paint upon.
99     * @param graph the mind map to paint.
100    */
101   public void paintSloppy( final Graphics2D gfx, final Graph2D graph ) {
102     paintCore(gfx, graph, true);
103   }
104 
105   /**
106    * Renders the given graph.
107    * @param gfx the graphics context to paint upon.
108    * @param graph the mind map to paint.
109    * @param sloppy if <code>true</code> low-detail rendering is used; otherwise
110    * high-detail rendering is used.
111    */
112   private void paintCore(
113           final Graphics2D gfx, final Graph2D graph, final boolean sloppy
114   ) {
115     Rectangle clip = gfx.getClipBounds();
116     if (clip == null) {
117       clip = graph.getBoundingBox();
118     }
119     final double cx = clip.getX();
120     final double cy = clip.getY();
121     final double cw = clip.getWidth();
122     final double ch = clip.getHeight();
123 
124     final Rectangle2D.Double tmp = new Rectangle2D.Double();
125     final int types =
126             Graph2DTraversal.NODES |
127             Graph2DTraversal.EDGES |
128             Graph2DTraversal.NODE_LABELS;
129     for (Iterator it = order.firstToLast(graph, types); it.hasNext();) {
130       final Object o = it.next();
131 
132       if (o instanceof Node) {
133         final NodeRealizer nr = graph.getRealizer((Node) o);
134         // check if the node is in the visible region before painting
135         tmp.setFrame(0, 0, -1, -1);
136         nr.calcUnionRect(tmp);
137         if (tmp.intersects(cx, cy, cw, ch)) {
138           // node is in the visible region, so paint it
139           if (sloppy) {
140             nr.paintSloppy(gfx);
141           } else {
142             nr.paint(gfx);
143           }
144         }
145       } else if (o instanceof Edge) {
146         final EdgeRealizer er = graph.getRealizer((Edge) o);
147         // check if the edge is in the visible region before painting
148         if (er.intersects(clip)) {
149           // edge is in the visible region, so paint it
150           if (sloppy) {
151             er.paintSloppy(gfx);
152           } else {
153             er.paint(gfx);
154           }
155         }
156       } else if (o instanceof NodeLabel) {
157         final NodeLabel nl = (NodeLabel) o;
158         // check if the label is in the visible region before painting
159         if (nl.intersects(cx, cy, cw, ch)) {
160           // label is in the visible region, so paint it
161           nl.paint(gfx);
162         }
163       }
164     }
165   }
166 
167 
168   /**
169    * Specifies the element order for hit-testing and rendering a mind map.
170    */
171   private static final class MindMapTraversal implements Graph2DTraversal {
172     /**
173      * Returns the element order for rendering the mind map.
174      */
175     public Iterator firstToLast(Graph2D graph, int elementTypes) {
176       return collectElements(graph, elementTypes).iterator();
177     }
178 
179     /**
180      * Returns the element order for hit testing in the mind map.
181      */
182     public Iterator lastToFirst(Graph2D graph, int elementTypes) {
183       final YList list = collectElements(graph, elementTypes);
184       return Cursors.createReverseIterator(list.cursor());
185     }
186 
187     //collect elements in firstToLast order
188     private YList collectElements( final Graph2D graph, final int types ) {
189       final YList elements = new YList();
190       final YList crossReferences = new YList();
191       final YList bends = new YList();
192 
193       //first add edges
194       final boolean addEdges = (Graph2DTraversal.EDGES & types) != 0;
195       final boolean addBends = (Graph2DTraversal.BENDS & types) != 0;
196       if (addEdges || addBends) {
197         final ViewModel model = ViewModel.instance;
198         for (EdgeCursor ec = graph.edges(); ec.ok(); ec.next()) {
199           //save cross references to add them later
200           if (model.isCrossReference(ec.edge())) {
201             if (addEdges) {
202               crossReferences.add(ec.edge());
203             }
204             final EdgeRealizer er = graph.getRealizer(ec.edge());
205             if (addBends) {
206               bends.addAll(er.bends());
207             }
208           } else {
209             if (addEdges) {
210               elements.add(ec.edge());
211             }
212           }
213         }
214       }
215 
216 
217       final YList nodeLabels = new YList();
218 
219       //next, add nodes
220       final boolean addNodes = (Graph2DTraversal.NODES & types) != 0;
221       final boolean addNodeLabels = (Graph2DTraversal.NODE_LABELS & types) != 0;
222       if (addNodes || addNodeLabels) {
223         for (NodeCursor nc = graph.nodes(); nc.ok(); nc.next()) {
224           //save node labels to add them later
225           if (addNodeLabels) {
226             final NodeRealizer nr = graph.getRealizer(nc.node());
227             for (int i = 0, n = nr.labelCount(); i < n; ++i) {
228               nodeLabels.add(nr.getLabel(i));
229             }
230           }
231           if (addNodes) {
232             elements.add(nc.node());
233           }
234         }
235       }
236 
237       //add cross edges
238       elements.splice(crossReferences);
239       //add bends
240       elements.splice(bends);
241       //finally add node labels
242       elements.splice(nodeLabels);
243       return elements;
244     }
245   }
246 }
247