| MindMapRenderer.java |
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