1
14 package demo.yext.svg;
15
16 import yext.svg.io.SVGNodeRealizerSerializer;
17 import yext.svg.view.SVGModel;
18 import yext.svg.view.SVGNodeRealizer;
19
20 import y.io.GraphMLIOHandler;
21 import y.option.ComponentOptionItem;
22 import y.option.DefaultEditorFactory;
23 import y.option.Editor;
24 import y.option.EditorFactory;
25 import y.option.ItemEditor;
26 import y.option.OptionGroup;
27 import y.option.OptionHandler;
28 import y.option.OptionItem;
29 import y.util.D;
30 import y.view.EditMode;
31 import y.view.Graph2D;
32 import y.view.NodeRealizer;
33 import y.base.Node;
34 import y.base.NodeCursor;
35
36 import java.awt.BorderLayout;
37 import java.awt.Color;
38 import java.awt.Dimension;
39 import java.awt.EventQueue;
40 import java.awt.GridBagConstraints;
41 import java.awt.GridBagLayout;
42 import java.awt.event.ComponentAdapter;
43 import java.awt.event.ComponentEvent;
44 import java.beans.PropertyChangeEvent;
45 import java.beans.PropertyChangeListener;
46 import java.util.HashSet;
47 import java.util.Iterator;
48 import java.util.LinkedHashMap;
49 import java.util.Map;
50 import java.util.Set;
51 import java.net.URL;
52 import java.io.IOException;
53 import javax.swing.JFrame;
54 import javax.swing.JLabel;
55 import javax.swing.JMenu;
56 import javax.swing.JMenuBar;
57 import javax.swing.JPanel;
58 import javax.swing.JScrollPane;
59 import javax.swing.JSplitPane;
60 import javax.swing.JTextArea;
61
62
85 public class RenderingPerformanceDemo extends SVGExportDemo {
86 private static final String GRAPH =
87 "Graph";
88 private static final String IMAGE_CACHE_ENABLED =
89 "Image Cache Enabled";
90 private static final String IMAGE_CACHE_SIZE =
91 "Image Cache Size";
92 private static final String MAXIMUM_IMAGE_SIZE =
93 "Maximum Image Size (in bytes)";
94 private static final String MINIMUM_RELATIVE_IMAGE_SIZE =
95 "Minimum Relative Image Size";
96
97 private Map nameToUrl;
98
99 public RenderingPerformanceDemo() {
100 createNameToUrlMap();
101
102 if (nameToUrl != null && !nameToUrl.isEmpty()) {
103 readGraph((URL)nameToUrl.values().iterator().next());
104 }
105
106 createContent();
107 createListeners();
108 updateView();
109 }
110
111 private void createNameToUrlMap() {
112 nameToUrl = new LinkedHashMap();
113
114 final String[] names = {
115 "100Earths",
116 "mimeTypes",
117 "mixedSizes"
118 };
119 URL url;
120 for (int i = 0; i < names.length; ++i) {
121 final String resource = "resource/" + names[i] + ".graphml";
122 url = RenderingPerformanceDemo.class.getResource(resource);
123 if (url != null) {
124 nameToUrl.put(names[i], url);
125 } else {
126 System.err.println("Could not resolve " + resource + "!");
127 }
128 }
129 }
130
131 private void createListeners() {
132 final Dimension oldSize = view.getPreferredSize();
133 view.addComponentListener(new ComponentAdapter() {
134 public void componentResized( final ComponentEvent e ) {
135 final Dimension newSize = e.getComponent().getSize();
136 if (oldSize.width < newSize.width || oldSize.height < newSize.height) {
137 updateView();
138 view.removeComponentListener(this);
139 }
140 }
141 });
142 }
143
144
147 private void createContent() {
148 final String[] names;
149 if (nameToUrl != null && !nameToUrl.isEmpty()) {
150 names = new String[nameToUrl.size()];
151 int i = 0;
152 for (Iterator it = nameToUrl.keySet().iterator(); it.hasNext(); ++i) {
153 names[i] = (String)it.next();
154 }
155 } else {
156 names = new String[0];
157 }
158
159 OptionGroup group;
160
161 final OptionHandler oh = new OptionHandler("CacheOptions");
162 OptionItem item;
163
164 if (names.length > 0) {
165 group = new OptionGroup();
166 group.setAttribute(OptionGroup.ATTRIBUTE_TITLE, "Sample SVG Graph");
167 item = oh.addEnum(GRAPH, names, 0);
168 item.setAttribute(ItemEditor.ATTRIBUTE_AUTO_COMMIT, Boolean.TRUE);
169 group.addItem(item);
170 } else {
171 group = new OptionGroup();
172 group.setAttribute(OptionGroup.ATTRIBUTE_TITLE, "Sample SVG Graph");
173
174 final JLabel label = new JLabel("No graphs available.");
175 label.setForeground(Color.RED.darker());
176 item = new ComponentOptionItem("_COMPONENT", label) {
177 public boolean wantsVisibleName() {
178 return false;
179 }
180 };
181 oh.addItem(item);
182 group.addItem(item);
183 }
184
185 group = new OptionGroup();
186 group.setAttribute(OptionGroup.ATTRIBUTE_TITLE, "SVG Image Cache Options");
187
188 item = oh.addBool(IMAGE_CACHE_ENABLED, SVGModel.isImageCacheEnabled());
189 item.setTipText(
190 "<html><body><table width=\"240\"><tr><td>" +
191 "Enables or disables image caching. " +
192 "Image caching is a performance optimization that tries to " +
193 "minimize expensive vector graphics rendering to a cached " +
194 "intermediate image that will be displayed instead. To achieve " +
195 "nice rendering quality images are created and cached for " +
196 "multiple zoom levels." +
197 "</td></tr></table></body></html>"
198 );
199 item.setAttribute(ItemEditor.ATTRIBUTE_AUTO_COMMIT, Boolean.TRUE);
200 group.addItem(item);
201 item = oh.addInt(IMAGE_CACHE_SIZE, SVGModel.getImageCacheSize());
202 item.setTipText(
203 "<html><body><table width=\"240\"><tr><td>" +
204 "The maximum number of images that will be cached." +
205 "</td></tr></table></body></html>"
206 );
207 item.setAttribute(ItemEditor.ATTRIBUTE_AUTO_COMMIT, Boolean.TRUE);
208 group.addItem(item);
209 item = oh.addInt(MAXIMUM_IMAGE_SIZE, SVGModel.getMaximumImageSize());
210 item.setTipText(
211 "<html><body><table width=\"240\"><tr><td>" +
212 "The maximum size of images that will be cached. " +
213 "Image size is measured in bytes necessary to store it. " +
214 "For an image with size <code>W x H</code> the image size " +
215 "is <code>W * H * 4</code>. " +
216 "The width and height of an image is calulated by " +
217 "multiplying the width and height of the SVG by the scale " +
218 "factor of the graphics context it is rendered on." +
219 "</td></tr></table></body></html>"
220 );
221 item.setAttribute(ItemEditor.ATTRIBUTE_AUTO_COMMIT, Boolean.TRUE);
222 group.addItem(item);
223 item = oh.addDouble(MINIMUM_RELATIVE_IMAGE_SIZE, SVGModel.getMinimumRelativeImageSize());
224 item.setTipText(
225 "<html><body><table width=\"240\"><tr><td>" +
226 "The minimum relative image size for which precached image " +
227 "duplicates are reused. I.e. if the image cache is queried for " +
228 "an intermediate image for a given SVG definition and image " +
229 "size, a cache miss is avoided if there is a cached image for " +
230 "the given SVG definition and <code>query width/cached width " +
231 ">= minimumRelativeImageSize</code> as well as " +
232 "<code>query height/cached height >= minimumRelativeImageSize</code> " +
233 "hold." +
234 "</td></tr></table></body></html>"
235 );
236 item.setAttribute(ItemEditor.ATTRIBUTE_AUTO_COMMIT, Boolean.TRUE);
237 group.addItem(item);
238
239
240 final JTextArea jta = new JTextArea();
241 jta.setText(info());
242 jta.setEditable(false);
243 jta.setLineWrap(true);
244 jta.setWrapStyleWord(true);
245 final JScrollPane jsp = new JScrollPane(jta);
246 jsp.setPreferredSize(new Dimension(200, 100));
247 item = new ComponentOptionItem("Comment", jsp) {
248 public boolean wantsVisibleName() {
249 return false;
250 }
251 };
252 oh.addItem(item);
253
254 oh.addChildPropertyChangeListener(new PropertyChangeListener() {
255 public void propertyChange( PropertyChangeEvent evt ) {
256 if (!OptionItem.PROPERTY_VALUE.equals(evt.getPropertyName())) {
257 return;
258 }
259
260 final OptionItem src = (OptionItem)evt.getSource();
261 final String name = src.getName();
262 if (GRAPH.equals(name)) {
263 SVGModel.clearImageCache();
264 readGraph((URL)nameToUrl.get(evt.getNewValue()));
265 jta.setText(info());
266 } else if (IMAGE_CACHE_ENABLED.equals(name)) {
267 SVGModel.setImageCacheEnabled(((Boolean)evt.getNewValue()).booleanValue());
268 } else if (IMAGE_CACHE_SIZE.equals(name)) {
269 SVGModel.setImageCacheSize(((Number)evt.getNewValue()).intValue());
270 } else if (MAXIMUM_IMAGE_SIZE.equals(name)) {
271 SVGModel.setMaximumImageSize(((Number)evt.getNewValue()).intValue());
272 } else if (MINIMUM_RELATIVE_IMAGE_SIZE.equals(name)) {
273 SVGModel.setMinimumRelativeImageSize(((Number)evt.getNewValue()).doubleValue());
274 view.repaint();
275 }
276 }
277 });
278
279
280 final EditorFactory ef = new DefaultEditorFactory(); final Editor editor = ef.createEditor(oh);
282
283 final GridBagConstraints gbc = new GridBagConstraints();
284 final JPanel editorPane = new JPanel(new GridBagLayout());
285 gbc.fill = GridBagConstraints.HORIZONTAL;
286 gbc.gridx = 0;
287 gbc.gridy = 0;
288 gbc.weightx = 1.0;
289 gbc.weighty = 0.0;
290 editorPane.add(editor.getComponent(), gbc);
291 gbc.fill = GridBagConstraints.BOTH;
292 ++gbc.gridy;
293 gbc.weighty = 1.0;
294 editorPane.add(new JPanel(), gbc);
295
296 remove(view);
297 add(new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true,
298 editorPane, view),
299 BorderLayout.CENTER);
300 }
301
302
303 protected JMenuBar createMenuBar() {
304 JMenuBar jmb = new JMenuBar();
305 JMenu menu = new JMenu("File");
306 menu.add(new ExitAction());
307 jmb.add(menu);
308 return jmb;
309 }
310
311 protected EditMode createEditMode() {
312 final EditMode em = new EditMode() {
313 protected String getNodeTip( final Node v ) {
314 final NodeRealizer nr = getGraph2D().getRealizer(v);
315 final double zoom = view.getZoom();
316 final int bytes = (int)Math.ceil(nr.getWidth() *zoom) *
317 (int)Math.ceil(nr.getHeight()*zoom) * 4;
318 return "Corresponding image size: " + bytes + " bytes";
319 }
320 };
321 em.showNodeTips(true);
322 em.allowBendCreation(false);
323 em.allowEdgeCreation(false);
324 em.allowNodeCreation(false);
325 return em;
326 }
327
328 public void start(final String title) {
329 JFrame frame = new JFrame(title);
330 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
331 addContentTo(frame.getRootPane());
332 frame.pack();
333 frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
334 frame.setLocationRelativeTo(null);
335 frame.setVisible(true);
336 }
337
338 private void readGraph( final URL url ) {
339 if (url == null) {
340 return;
341 }
342
343 final Graph2D graph = view.getGraph2D();
344
345 final GraphMLIOHandler ioh = new GraphMLIOHandler();
346 ioh.addNodeRealizerSerializer(new SVGNodeRealizerSerializer());
347 try {
348 graph.clear();
349 ioh.read(view.getGraph2D(), url);
350 } catch (IOException ioe) {
351 D.show(ioe);
352 }
353
354 graph.setSelected(graph.nodes(), true);
355 graph.setBendsSelected(graph.edges(), true);
356
357 updateView();
358 }
359
360 private void updateView() {
361 view.fitContent();
362 view.setPaintDetailThreshold(0);
363 view.updateView();
364 }
365
366 private String info() {
367 final Graph2D graph = view.getGraph2D();
368
369 final StringBuffer sb = new StringBuffer();
370 sb.append(graph.N()).append(" node(s),\n");
371 sb.append(graph.E()).append(" edge(s),\n");
372
373 final Set urls = new HashSet();
374 for (NodeCursor nc = graph.nodes(); nc.ok(); nc.next()) {
375 final NodeRealizer nr = graph.getRealizer(nc.node());
376 if (nr instanceof SVGNodeRealizer) {
377 urls.add(((SVGNodeRealizer)nr).getSVGURL());
378 }
379 }
380 sb.append(urls.size()).append(" SVG image(s)");
381 return sb.toString();
382 }
383
384
385
389 public static void main( final String[] args ) {
390 EventQueue.invokeLater(new Runnable() {
391 public void run() {
392 initLnF();
393 (new RenderingPerformanceDemo()).start();
394 }
395 });
396 }
397 }
398