1
2 package hep.wired.heprep.graphicspanel;
3
4 import java.awt.*;
5 import java.awt.event.*;
6 import java.awt.geom.*;
7 import java.util.*;
8 import java.util.List;
9 import javax.swing.*;
10 import javax.swing.event.*;
11 import javax.swing.undo.*;
12
13 import org.jdom.Element;
14
15 import org.freehep.application.Application;
16 import org.freehep.application.studio.Studio;
17 import org.freehep.graphics2d.BufferedPanel;
18 import org.freehep.graphics2d.VectorGraphics;
19 import org.freehep.graphics2d.PixelGraphics2D;
20 import org.freehep.xml.io.XMLIO;
21 import org.freehep.xml.io.XMLIOManager;
22
23 import hep.graphics.heprep.HepRep;
24 import hep.graphics.heprep.HepRepAttValue;
25 import hep.graphics.heprep.HepRepInstanceTree;
26 import hep.graphics.heprep.HepRepInstance;
27 import hep.graphics.heprep.HepRepIterator;
28 import hep.graphics.heprep.HepRepType;
29 import hep.graphics.heprep.util.HepRepUtil;
30
31 import hep.wired.feature.HasBoundingBox;
32 import hep.wired.feature.Resetable;
33 import hep.wired.feature.Rotateable3D;
34 import hep.wired.feature.Scaleable;
35 import hep.wired.feature.Scaleable2D;
36 import hep.wired.feature.Scaleable3D;
37 import hep.wired.feature.Translateable;
38 import hep.wired.feature.Translateable2D;
39 import hep.wired.feature.Translateable3D;
40 import hep.wired.viewport.RectangularViewPort;
41 import hep.wired.services.Feature;
42 import hep.wired.services.GraphicsPanel;
43 import hep.wired.services.RecordPlot;
44 import hep.wired.services.ViewPort;
45 import hep.wired.util.WiredRegistry;
46
47 import hep.wired.heprep.edit.SetProjection;
48 import hep.wired.heprep.feature.HasNearestPoint;
49 import hep.wired.heprep.interaction.HepRepInfoPanel;
50 import hep.wired.heprep.projection.AbstractProjection;
51 import hep.wired.heprep.projection.ParallelProjection;
52 import hep.wired.heprep.plugin.WiredPlugin;
53 import hep.wired.heprep.representation.DrawAsCache;
54 import hep.wired.heprep.representation.DrawAsPoint;
55 import hep.wired.heprep.services.DrawAs;
56 import hep.wired.heprep.services.GraphicsMode;
57 import hep.wired.heprep.services.Projection;
58 import hep.wired.heprep.tree.TreePanelModel;
59 import hep.wired.heprep.util.BoundingBoxGraphics2D;
60 import hep.wired.heprep.util.NearestPointPathConstructor;
61 import hep.wired.heprep.util.NullGraphics2D;
62 import hep.wired.heprep.util.PathGraphics2D;
63
64 /***
65 * Panel to display HepReps.
66 *
67 * @author Mark Donszelmann
68 * @version $Id: HepRepPanel.java 2136 2005-07-30 00:25:21Z duns $
69 */
70
71 public class HepRepPanel extends BufferedPanel
72 implements GraphicsPanel,
73 Resetable,
74 HasBoundingBox,
75 HasNearestPoint,
76 XMLIO {
77
78 private HepRepGraphicsMode mode;
79 private Projection projection;
80 private ViewPort viewPort;
81 private HepRepPanelCommandHandler commandHandler;
82
83 private HepRep heprep;
84 private TreePanelModel treePanelModel;
85
86 private transient HepRepGraphicsMode usedMode;
87 private transient HepRepGraphicsMode fastMode;
88 private transient Set
89 private transient DrawAsCache drawAsCache;
90 private transient boolean fast;
91 private transient InstanceListener instanceListener;
92 private transient Set
93
94 private static final long nDraws = 10;
95 private transient long tMin = 200;
96 private transient long tMax = 200;
97 private transient long tAverage = 200;
98
99 private transient HepRepAttValue initialPhi, initialTheta;
100 private transient HepRepAttValue initialScaleX, initialScaleY, initialScaleZ;
101 private transient HepRepAttValue initialTranslateX, initialTranslateY, initialTranslateZ;
102
103 private HepRepPanel() {
104 this(null, null);
105 }
106
107 public HepRepPanel(HepRepGraphicsMode mode) {
108 this(mode, null);
109 }
110
111 /***
112 * Creates a HepRep panel of dimension 800x600.
113 */
114 public HepRepPanel(HepRepGraphicsMode mode, ViewPort viewPort) {
115 this(mode, viewPort, new Dimension(800,600), Color.BLACK,
116 new ParallelProjection.XY());
117 }
118
119 private HepRepPanel(HepRepGraphicsMode mode, ViewPort viewPort,
120 Dimension preferredSize, Color background,
121 Projection projection) {
122 super(true);
123
124 this.mode = mode;
125 if (viewPort == null) viewPort = new RectangularViewPort(0, 0, 1000, 1000, getWidth(), getHeight());
126 this.viewPort = viewPort;
127 this.projection = projection;
128
129 heprep = null;
130
131 setPreferredSize(preferredSize);
132 setBackground(background);
133
134 setOpaque(true);
135
136 fastMode = new HepRepGraphicsMode(true);
137 fast = false;
138
139 highlight = null;
140
141 treePanelModel = new TreePanelModel();
142 treePanelModel.addChangeListener(new ChangeListener() {
143 public void stateChanged(ChangeEvent event) {
144 repaint();
145 }
146 });
147 }
148
149 public void setHepRepInstanceListener(InstanceListener listener) {
150 instanceListener = listener;
151 }
152
153 /***
154 * Returns a copy of this HepRep panel.
155 */
156 public GraphicsPanel copy() {
157 return new HepRepPanel((HepRepGraphicsMode)mode.copy(), viewPort.copy(),
158 getPreferredSize(), getBackground(),
159 getProjection().copy());
160 }
161
162 /***
163 * Sets the HepRep to be displayed in this panel and repaints.
164 */
165 public void setRecord(Object object) {
166 heprep = (HepRep)object;
167
168 treePanelModel.setHepRep(heprep);
169
170
171 if (heprep != null) {
172 HepRepIterator iterator = HepRepUtil.getInstances(heprep.getInstanceTreeList(), null, null, false);
173 if (iterator.hasNext()) {
174 HepRepInstance instance = iterator.nextInstance();
175
176 initialPhi = instance.getAttValue("ViewPhi");
177 initialTheta = instance.getAttValue("ViewTheta");
178
179 initialScaleX = instance.getAttValue("ViewScaleX");
180 if (initialScaleX == null) {
181 initialScaleX = instance.getAttValue("ViewScale");
182 }
183 initialScaleY = instance.getAttValue("ViewScaleY");
184 initialScaleZ = instance.getAttValue("ViewScaleZ");
185
186 if (initialScaleY == null) {
187 initialScaleY = initialScaleX;
188 }
189 if (initialScaleZ == null) {
190 initialScaleZ = initialScaleX;
191 }
192
193 initialTranslateX = instance.getAttValue("ViewTranslateX");
194 initialTranslateY = instance.getAttValue("ViewTranslateY");
195 initialTranslateZ = instance.getAttValue("ViewTranslateZ");
196 }
197 }
198
199 repaint();
200 }
201
202 /***
203 * Returns the HepRep displayed in this panel.
204 */
205 public Object getRecord() {
206 return heprep;
207 }
208
209 public TreePanelModel getTreePanelModel() {
210 return treePanelModel;
211 }
212
213 public void setSelectedTypes(Set
214 System.err.println("Selected: "+getSelectedTypes().size());
215 System.err.println("ignored");
216
217
218
219
220
221
222 }
223
224 public Set
225 return treePanelModel.getSelectedTypes();
226 }
227
228 public boolean supports(Class featureClass) {
229 return getFeature(featureClass) != null;
230 }
231
232 /***
233 * Returns the feature for a specific featureClass, or null if that featureClass if not
234 * in the projection of this panel.
235 */
236 public Feature getFeature(Class featureClass) {
237 if (featureClass.isAssignableFrom(this.getClass())) return this;
238 return projection.getFeature(featureClass);
239 }
240
241
242
243
244 public Object reset(Object newState) {
245 Resetable resetable = (Resetable)projection.getFeature(Resetable.class);
246 Object oldState = (resetable != null) ? resetable.reset(newState) : null;
247
248
249 if (initialScaleX != null) {
250 Scaleable3D scaleable3d = (Scaleable3D)getFeature(Scaleable3D.class);
251 if (scaleable3d != null) {
252 scaleable3d.setScale(initialScaleX.getDouble(), initialScaleY.getDouble(), initialScaleZ.getDouble());
253 } else {
254 Scaleable2D scaleable2d = (Scaleable2D)getFeature(Scaleable2D.class);
255 if (scaleable2d != null) {
256 scaleable2d.setScale(initialScaleX.getDouble(), initialScaleY.getDouble());
257 } else {
258 Scaleable scaleable = (Scaleable)getFeature(Scaleable.class);
259 if (scaleable != null) {
260 scaleable.setScale(initialScaleX.getDouble());
261 }
262 }
263 }
264 }
265
266
267 Rotateable3D rotateable3d = (Rotateable3D)getFeature(Rotateable3D.class);
268 if (rotateable3d != null) {
269 if (initialPhi != null) {
270 rotateable3d.rotate(initialPhi.getDouble(), 0.0, 0.0, 1.0);
271 }
272 if (initialTheta != null) {
273 rotateable3d.rotate(initialTheta.getDouble(), 0.0, 1.0, 0.0);
274 }
275 }
276
277
278 double stx = (initialTranslateX != null) ? initialTranslateX.getDouble() : 0;
279 double sty = (initialTranslateY != null) ? initialTranslateY.getDouble() : 0;
280 double stz = (initialTranslateZ != null) ? initialTranslateZ.getDouble() : 0;
281
282 Translateable3D translateable3D = (Translateable3D)getFeature(Translateable3D.class);
283 if (translateable3D != null) {
284 translateable3D.setTranslate(stx, sty, stz);
285 } else {
286 Translateable2D translateable2D = (Translateable2D)getFeature(Translateable2D.class);
287 if (translateable2D != null) {
288 translateable2D.setTranslate(stx, sty);
289 }
290 }
291
292 return oldState;
293 }
294
295 /***
296 * Sets the projection for this panel.
297 */
298 public void setProjection(Projection projection) {
299 this.projection = projection;
300
301 WiredPlugin plugin = WiredPlugin.getPlugin();
302
303
304 plugin.getInteractionPanel().update();
305
306
307 if ((plugin.getInteractionHandler() != null) && !plugin.getInteractionHandler().isSupportedBy(this)) {
308 plugin.setInteractionHandler(null);
309 }
310
311
312 plugin.getVariablePanel().update();
313 }
314
315 /***
316 * Returns the projection for this panel.
317 */
318 public Projection getProjection() {
319 return projection;
320 }
321
322 /***
323 * Returns the bounding box for this plot in its current projection/viewpoint.
324 */
325 public Rectangle2D getBoundingBoxForPlot() {
326 BoundingBoxGraphics2D bb = new BoundingBoxGraphics2D(true);
327 draw(bb, true, false);
328 return bb.getBoundingBox();
329 }
330
331 public Point2D getNearestPoint(Point2D p) {
332 NearestPointPathConstructor pathConstructor = new NearestPointPathConstructor(p);
333 PathGraphics2D pathGraphics = new PathGraphics2D(pathConstructor);
334 draw(pathGraphics, true, false);
335 return pathConstructor.getNearestPoint();
336 }
337
338 public void setFastMode(boolean fast) {
339 this.fast = fast;
340 }
341
342 public void setSelected(RecordPlot plot, boolean selected) {
343 if (selected) {
344 if (commandHandler == null) {
345 commandHandler = new HepRepPanelCommandHandler(plot);
346 UndoableEditSupport undoSupport = plot.getUndoableEditSupport();
347 if (undoSupport != null) undoSupport.addUndoableEditListener(commandHandler);
348 }
349 ((Studio)Application.getApplication()).getCommandTargetManager().add(commandHandler);
350 } else if (commandHandler != null) {
351 ((Studio)Application.getApplication()).getCommandTargetManager().remove(commandHandler);
352 }
353 }
354
355 public void highlight(Set
356 this.highlight = highlight;
357 }
358
359 public void repaint() {
360
361 if (commandHandler != null) commandHandler.setChanged();
362 super.repaint();
363 }
364
365 /***
366 * Paints the HepRep into this panel.
367 */
368 public void paintComponent(VectorGraphics graphics) {
369 try {
370
371 Color c = graphics.getColor();
372 graphics.setColor(getBackground());
373 Rectangle r = getBounds();
374 graphics.fillRect(r.x, r.y, r.width, r.height);
375 graphics.setColor(c);
376
377
378 graphics.setRenderingHint(PixelGraphics2D.KEY_SYMBOL_BLIT, PixelGraphics2D.VALUE_SYMBOL_BLIT_OFF);
379
380
381 BasicStroke cs = (BasicStroke)graphics.getStroke();
382 graphics.setStroke(new BasicStroke(cs.getLineWidth(),
383 cs.getEndCap(),
384 cs.getLineJoin(),
385 1.0f,
386 cs.getDashArray(),
387 cs.getDashPhase()));
388
389 long t = System.currentTimeMillis();
390
391
392 draw(graphics, fast, false);
393
394
395 if (false) {
396 t = System.currentTimeMillis()-t;
397 if (t != 0) {
398 long tNow = t/nDraws;
399 tMin = t < tAverage ? tMin + tNow - tMin/nDraws : tMin;
400 tMax = t > tAverage ? tMax + tNow - tMax/nDraws : tMax;
401 tAverage = tAverage + tNow - tAverage/nDraws;
402 if (usedMode != fastMode) {
403 System.err.println("Draw took: "+tMin+" <= "+tAverage+" <= "+tMax+" ms averaged over "+nDraws+" redraws.");
404 }
405 }
406 }
407 } catch (Throwable t) {
408
409
410 System.err.println(t.toString());
411 t.printStackTrace();
412 }
413 }
414
415 private void draw(VectorGraphics graphics, boolean fast, boolean pick) {
416 if (heprep == null) return;
417 List layers = (fast || !mode.useLayering) ? null : heprep.getLayerOrder();
418 draw(graphics, fast, layers, null, pick);
419 }
420
421 public void draw(VectorGraphics graphics, boolean fast, List layers, Set types, boolean pick) {
422 if (heprep == null) return;
423
424
425 if (types == null) {
426 types = getSelectedTypes();
427 } else {
428 types = new HashSet(types);
429 types.retainAll(getSelectedTypes());
430 }
431
432 usedMode = (fast) ? fastMode : mode;
433 missingDrawAsValues = new HashSet();
434
435
436 drawAsCache = new DrawAsCache();
437
438
439 if (usedMode.antiAlias) {
440 graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
441 RenderingHints.VALUE_ANTIALIAS_ON);
442 graphics.setRenderingHint(RenderingHints.KEY_RENDERING,
443 RenderingHints.VALUE_RENDER_QUALITY);
444 } else {
445 graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
446 RenderingHints.VALUE_ANTIALIAS_OFF);
447 graphics.setRenderingHint(RenderingHints.KEY_RENDERING,
448 RenderingHints.VALUE_RENDER_SPEED);
449 }
450
451 draw(graphics, heprep.getInstanceTreeList(), layers, types, pick);
452
453 if (!missingDrawAsValues.isEmpty()) {
454 System.err.print("Missing DrawAs values: ");
455 for (Iterator i=missingDrawAsValues.iterator(); i.hasNext(); ) {
456 System.err.print(i.next()+" ");
457 }
458 System.err.println();
459 }
460 }
461
462 private void draw(VectorGraphics graphics, List
463
464
465 HepRepIterator iterator = HepRepUtil.getInstances(trees, layers, types, usedMode.drawFrames);
466 AttributeCache atts = new AttributeCache(graphics, usedMode, iterator);
467
468 projection.setViewPort(viewPort);
469
470 int i=0;
471 while (iterator.hasNext()) {
472 HepRepInstance instance = iterator.nextInstance();
473 if (pick && !atts.isPickable()) continue;
474 if (atts.isVisible()) {
475 if (instanceListener != null) instanceListener.currentHepRepInstance(instance);
476
477
478
479 if ((highlight != null) && !highlight.contains(instance)) {
480 atts.overrideColor(Color.GRAY);
481 atts.overrideFrameColor(Color.BLACK);
482 atts.overrideFillColor(Color.GRAY);
483 }
484 draw(graphics, atts, instance, iterator.drawAsFrame());
485 i++;
486
487 if ((highlight != null) && !highlight.contains(instance)) {
488 atts.overrideColor(null);
489 atts.overrideFrameColor(null);
490 atts.overrideFillColor(null);
491 }
492 }
493 }
494
495 }
496
497 private void draw(VectorGraphics graphics, AttributeCache atts, HepRepInstance instance, boolean drawAsFrame) {
498
499
500
501
502
503 DrawAs handler = drawAsCache.lookup(atts.getDrawAs());
504 if (handler == null) {
505 handler = drawAsCache.lookup("point");
506 if (handler == null) {
507 System.err.println("WARNING: DrawAsPoint handler seems to be missing");
508 handler = new DrawAsPoint();
509 }
510 missingDrawAsValues.add(atts.getDrawAs());
511 }
512 if (drawAsFrame) {
513 handler.frame(graphics, instance, atts, usedMode, projection, viewPort);
514 } else {
515 handler.draw(graphics, instance, atts, usedMode, projection, viewPort);
516 }
517 }
518
519 public static interface InstanceListener {
520 public void currentHepRepInstance(HepRepInstance instance);
521 }
522
523 public void setBounds(int x, int y, int width, int height) {
524 super.setBounds(x, y, width, height);
525
526 if (height < width) {
527 viewPort = new RectangularViewPort((width - height)/2, 0, height, height, width, height);
528 } else {
529 viewPort = new RectangularViewPort(0, (height - width) / 2, width, width, width, height);
530 }
531 }
532
533 /***
534 * Returns the viewport for this panel.
535 */
536 public ViewPort getViewPort() {
537 return viewPort;
538 }
539
540 /***
541 * Returns the GraphicsMode
542 */
543 public GraphicsMode getGraphicsMode() {
544 return mode;
545 }
546
547 /***
548 * Returns a set of DrawAs values for which no DrawAs service was available. These were drawn as
549 * Points.
550 */
551 public Set
552 NullGraphics2D ng = new NullGraphics2D();
553 draw(ng, true, false);
554 return missingDrawAsValues;
555 }
556
557 public JPopupMenu modifyPopupMenu(JPopupMenu menu, final RecordPlot plot) {
558
559
560
561
562
563 menu.addSeparator();
564
565 final JMenu projectionMenu = new JMenu("Projection");
566 menu.add(projectionMenu);
567 projectionMenu.addMenuListener(new MenuListener() {
568
569 public void menuCanceled(MenuEvent event) {
570 }
571
572 public void menuSelected(MenuEvent event) {
573 ButtonGroup group = new ButtonGroup();
574
575
576 String[] names = AbstractProjection.defaultProjections.split(":");
577
578 Collection allProjections = WiredRegistry.allInstances(Projection.class);
579
580 for (int i=0; i<names.length; i++) {
581 addMenu(group, projectionMenu, names[i]);
582 }
583 }
584
585 public void menuDeselected(MenuEvent event) {
586 projectionMenu.removeAll();
587 }
588
589
590
591
592
593 private void addMenu(ButtonGroup group, JMenu menu, String name) {
594 int dollar = name.lastIndexOf('$');
595 int dot = name.indexOf('.', dollar+1);
596 String menuName = dot < 0 ? name.substring(dollar+1) : name.substring(dollar+1, dot);
597 if (dot >= 0) {
598
599 JMenu subMenu = null;
600 for (int i=0; i<menu.getItemCount(); i++) {
601 JMenuItem item = menu.getItem(i);
602 if ((item instanceof JMenu) && item.getText().equals(menuName)) {
603 subMenu = (JMenu)item;
604 break;
605 }
606 }
607 if (subMenu == null) {
608 subMenu = new JMenu(menuName);
609 menu.add(subMenu);
610 }
611 addMenu(group, subMenu, name.substring(0,dot)+"$"+name.substring(dot+1));
612 } else {
613
614 JMenuItem item = new JRadioButtonMenuItem(menuName);
615 menu.add(item);
616 group.add(item);
617 item.setEnabled(false);
618
619
620 final Projection projection = (Projection)WiredRegistry.lookup(Projection.class, name);
621 if (projection != null) {
622 item.setSelected(name.equals(getProjection().getID()));
623 item.setEnabled(true);
624 item.addActionListener(new ActionListener() {
625 public void actionPerformed(ActionEvent event) {
626 plot.postEdit(new SetProjection(projection));
627 }
628 });
629 }
630 }
631 }
632 });
633
634 return menu;
635 }
636
637
638
639
640 public void save(XMLIOManager xmlioManager,
641 org.jdom.Element nodeEl) {
642 nodeEl.addContent(xmlioManager.save(mode));
643 nodeEl.addContent(xmlioManager.save(projection));
644 nodeEl.addContent(xmlioManager.save(viewPort));
645 }
646
647 public void restore(XMLIOManager xmlioManager,
648 org.jdom.Element nodeEl) {
649 Iterator i = nodeEl.getChildren().iterator();
650 mode = (HepRepGraphicsMode)xmlioManager.restore((Element)i.next());
651 projection = (Projection)xmlioManager.restore((Element)i.next());
652 viewPort = (ViewPort)xmlioManager.restore((Element)i.next());
653 }
654 }