KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > thoughtriver > open > vectorvisuals > VisualObject


1 /*
2  * VisualObject.java
3  *
4  * Created on 3 June 2003, 01:55
5  */

6
7 package com.thoughtriver.open.vectorvisuals;
8
9 import java.awt.*;
10 import java.awt.geom.*;
11 import java.util.*;
12 import java.util.List JavaDoc;
13
14 /**
15  * The "visual object" is the basic rendering unit of the Vector Visuals system.
16  * The vector world to be rendered is made up of some set of these objects, and
17  * when the repaint cycle is triggered, all of the <CODE>VisualObject</CODE>s
18  * that are within the viewable area are told to render.
19  * <P>
20  * <CODE>VisualObject</CODE>s can be embedded inside one another to create
21  * compound objects, much like the way <CODE>Container</CODE>s work in the
22  * familiar AWT system. An object that is embedded in its parent will always
23  * paint on top of the parent, and will be inherit its parent's transforms.
24  * <P>
25  * Every <CODE>VisualObject</CODE> has an associated "layer", which is simply
26  * an integer value indicated the depth order in which the object will be
27  * rendered. Objects with lower depth values will be rendered first.
28  *
29  * @author Brandon Franklin
30  * @version $Date: 2006/11/25 09:18:36 $
31  */

32 public class VisualObject {
33
34     /** The layer on which to render this object */
35     private int layer;
36
37     /** The <CODE>VVDisplay</CODE> in which this object is shown */
38     private VVDisplay vvDisplay = null;
39
40     /** The manager that handles all embedded objects for this instance */
41     private VisualObjectManager objManager = null;
42
43     /** The Level of Detail (LOD) manager for this instance */
44     private LODManager lodManager = null;
45
46     /**
47      * The <CODE>VisualObject</CODE>, if any, in which this instance is
48      * embedded
49      */

50     private VisualObject parent = null;
51
52     /** The transformation attributes of this object */
53     private AffineTransform transform = null;
54
55     /** The <CODE>Shape</CODE> of this object */
56     private Shape shape = null;
57
58     /** The <CODE>Brush</CODE> to use to draw the outline of the object */
59     private Brush lineBrush = null;
60
61     /** The <CODE>Brush</CODE> to use to fill the object */
62     private Brush fillBrush = null;
63
64     /** The current level of detail being used to render the object */
65     transient private int renderingDetail = Integer.MAX_VALUE;
66
67     /**
68      * Creates a new instance of <CODE>VisualObject</CODE> with the supplied
69      * <CODE>Shape</CODE> and <CODE>Brush</CODE>es. All new <CODE>VisualObject</CODE>s
70      * are on layer 1 by default.
71      *
72      * @param shape the <CODE>Shape</CODE> of the object
73      * @param lineBrush the <CODE>Brush</CODE> to use to draw the outline of
74      * the object
75      * @param fillBrush the <CODE>Brush</CODE> to use to fill the object
76      */

77     public VisualObject(final Shape shape, final Brush lineBrush, final Brush fillBrush) {
78         layer = 1;
79         setShape(shape);
80         setLineBrush(lineBrush);
81         setFillBrush(fillBrush);
82         transform = new AffineTransform();
83     }
84
85     /**
86      * Sets the <CODE>VVDisplay</CODE> upon which this object is being shown.
87      *
88      * @param vvDisplay the <CODE>VVDisplay</CODE> upon which this object is
89      * being shown
90      */

91     synchronized void setDisplay(final VVDisplay vvDisplay) {
92         this.vvDisplay = vvDisplay;
93     }
94
95     /**
96      * Returns the <CODE>VVDisplay</CODE> upon which this object is being
97      * shown.
98      *
99      * @return the <CODE>VVDisplay</CODE> upon which this object is being
100      * shown
101      */

102     synchronized public VVDisplay getDisplay() {
103
104         if (vvDisplay == null) { // doesn't know its display
105

106             if (getParent() != null) { // has a parent
107
return getParent().getDisplay();
108             }
109
110             // doesn't have a parent
111
return null;
112
113         }
114
115         // knows its display
116
return vvDisplay;
117     }
118
119     /**
120      * Returns the <CODE>Shape</CODE> of the object.
121      *
122      * @return the <CODE>Shape</CODE> of the object
123      */

124     synchronized public Shape getShape() {
125         return shape;
126     }
127
128     /**
129      * Sets the <CODE>Shape</CODE> of the object.
130      *
131      * @param shape the <CODE>Shape</CODE> of the object
132      */

133     synchronized public void setShape(final Shape shape) {
134         this.shape = shape;
135     }
136
137     /**
138      * Returns the <CODE>Brush</CODE> used to draw the outline of the object.
139      *
140      * @return the <CODE>Brush</CODE> used to draw the outline of the object
141      */

142     synchronized public Brush getLineBrush() {
143         return lineBrush;
144     }
145
146     /**
147      * Sets the <CODE>Brush</CODE> used to draw the outline of the object.
148      *
149      * @param lineBrush the <CODE>Brush</CODE> used to draw the outline of the
150      * object
151      */

152     synchronized public void setLineBrush(final Brush lineBrush) {
153         this.lineBrush = lineBrush;
154     }
155
156     /**
157      * Returns the <CODE>Brush</CODE> used to fill in the object.
158      *
159      * @return the <CODE>Brush</CODE> used to fill in the object
160      */

161     synchronized public Brush getFillBrush() {
162         return fillBrush;
163     }
164
165     /**
166      * Sets the <CODE>Brush</CODE> used to fill in the object.
167      *
168      * @param fillBrush the <CODE>Brush</CODE> used to fill in the object
169      */

170     synchronized public void setFillBrush(final Brush fillBrush) {
171         this.fillBrush = fillBrush;
172     }
173
174     /**
175      * Adds a new "level of detail" (LOD) setting to this object. LOD is used to
176      * help make large, complicated scenes render faster by not rendering things
177      * that are too small to see, or by reducing their rendering quality. Each
178      * <CODE>VisualObject</CODE> can interpret its detail values differently
179      * in the <CODE>render</CODE> methods, but the default behavior for the
180      * base <CODE>VisualObject</CODE> is as follows:
181      * <UL>
182      * <LI>Detail level 2 or above means render at full quality.
183      * <LI>Detail level 1 means render as a simple rectangle.
184      * <LI>Detail level 0 or below means do not render.
185      * </UL>
186      * Subclasses are free to change these values and interpretations in any way
187      * desired.
188      *
189      * @param scale the scale at and below which the specified level should come
190      * into effect
191      * @param detail the level of detail that should be active below the
192      * specified scale
193      */

194     synchronized public void addLevelOfDetail(final double scale, final int detail) {
195         if (lodManager == null) {
196             lodManager = new LODManager();
197         }
198         lodManager.addLevel(scale, detail);
199     }
200
201     /**
202      * Returns the current "level of detail" (LOD) being used when rendering
203      * this object, due to its current scale and the scale of the <CODE>Graphics2D</CODE>
204      * context.
205      *
206      * @param g2d the <CODE>Graphics2D</CODE> context being rendered on
207      * @return the current "level of detail" (LOD) being used when rendering
208      * this object
209      * @see #addLevelOfDetail(double, int)
210      */

211     synchronized public int getLevelOfDetail(final Graphics2D g2d) {
212         if (lodManager != null) {
213             AffineTransform absTransform = getTransformRelativeTo(null);
214             AffineTransform gTransform = g2d.getTransform();
215
216             double scale = Math.max(Math.abs(absTransform.getScaleX()), Math.abs(absTransform.getScaleY()));
217             scale *= Math.max(Math.abs(gTransform.getScaleX()), Math.abs(gTransform.getScaleY()));
218             return lodManager.getLevel(scale);
219         }
220
221         return Integer.MAX_VALUE;
222     }
223
224     /**
225      * Embeds the supplied <CODE>VisualObject</CODE> in this one. The embedded
226      * object will now inherit the transformations of this object, and will
227      * always be rendered after this object is. If an <CODE>Arranger</CODE>
228      * has been set on this object, the embedded object will be added to it, but
229      * the arrangement will be updated until <CODE>arrange()</CODE> is called
230      * on it.
231      *
232      * @param obj the <CODE>VisualObject</CODE> to embed in this one
233      */

234     synchronized public void add(final VisualObject obj) {
235         if (objManager == null) {
236             objManager = new VisualObjectManager();
237         }
238         objManager.addObject(obj);
239
240         obj.setParent(this);
241     }
242
243     /**
244      * Removes the supplied <CODE>VisualObject</CODE> from this one, so that
245      * it will no longer be embedded. The object will no longer inherit the
246      * transformations of this object, and will no longer be rendered by this
247      * object.
248      *
249      * @param obj the <CODE>VisualObject</CODE> to remove from this one
250      */

251     synchronized public void remove(final VisualObject obj) {
252         if (objManager == null) {
253             return;
254         }
255         objManager.removeObject(obj);
256
257         obj.setParent(null);
258     }
259
260     /**
261      * Returns a <CODE>List</CODE> containing references to all of the <CODE>VisualObject</CODE>s
262      * that are embedded in this one.
263      *
264      * @return a <CODE>List</CODE> containing references to all of the <CODE>VisualObject</CODE>s
265      * that are embedded in this one.
266      */

267     synchronized public List JavaDoc<VisualObject> getAllEmbedded() {
268         if (objManager == null) {
269             return new LinkedList<VisualObject>();
270         }
271         return objManager.getAllObjects();
272     }
273
274     /**
275      * Returns the depth layer of this <CODE>VisualObject</CODE>. Objects are
276      * rendered in order of layer depth. If an object is given a negative layer
277      * depth, then it is "down inside" its containing parent, and will be
278      * clipped to the <CODE>Shape</CODE> of the parent when rendered.
279      *
280      * @return the depth layer of this <CODE>VisualObject</CODE>
281      */

282     synchronized public int getLayer() {
283         return layer;
284     }
285
286     /**
287      * Sets the depth layer of this <CODE>VisualObject</CODE>. Objects are
288      * rendered in order of layer depth. If an object is given a negative layer
289      * depth, then it is "down inside" its containing parent, and will be
290      * clipped to the <CODE>Shape</CODE> of the parent when rendered.
291      *
292      * @param layer the new depth layer of this <CODE>VisualObject</CODE>
293      */

294     synchronized public void setLayer(final int layer) {
295         this.layer = layer;
296     }
297
298     /**
299      * Returns a reference to the <CODE>VisualObject</CODE> in which this one
300      * is embedded, or null if this instance is not embedded.
301      *
302      * @return a reference to the <CODE>VisualObject</CODE> in which this one
303      * is embedded, or null if this instance is not embedded
304      */

305     synchronized public VisualObject getParent() {
306         return parent;
307     }
308
309     /**
310      * Sets the <CODE>VisualObject</CODE> in which this one is embedded, or
311      * null if this instance is not embedded.
312      *
313      * @param parent the <CODE>VisualObject</CODE> in which this one is
314      * embedded, or null if this instance is not embedded
315      */

316     synchronized void setParent(final VisualObject parent) {
317         this.parent = parent;
318     }
319
320     /**
321      * Returns a copy of the <CODE>AffineTransform</CODE> that is currently
322      * applied to this object and its embedded objects during renders.
323      *
324      * @return a copy of the <CODE>AffineTransform</CODE> that is currently
325      * applied to this object and its embedded objects during renders
326      */

327     synchronized public AffineTransform getTransform() {
328         return new AffineTransform(transform);
329     }
330
331     /**
332      * Sets the <CODE>AffineTransform</CODE> that will be applied to this
333      * object and its embedded objects during renders.
334      *
335      * @param transform the <CODE>AffineTransform</CODE> that will be applied
336      * to this object and its embedded objects during renders
337      */

338     synchronized public void setTransform(final AffineTransform transform) {
339         this.transform = transform;
340     }
341
342     /**
343      * Returns the <CODE>AffineTransform</CODE> representing this object's
344      * position relative to the supplied parent <CODE>VisualObject</CODE>.
345      * The object must be embedded either in the supplied parent or in some
346      * other <CODE>VisualObject</CODE> that is ultimately embedded in the
347      * parent. Supplying "null" will return the object's transform relative to
348      * the top level parent, meaning it will typically be an absolute transform
349      * for the <CODE>VisualObject</CODE>.
350      *
351      * @param parent the <CODE>VisualObject</CODE> relative to which the
352      * transform should be determined
353      * @return the <CODE>AffineTransform</CODE> representing this object's
354      * position relative to the supplied parent <CODE>VisualObject</CODE>
355      */

356     synchronized public AffineTransform getTransformRelativeTo(final VisualObject parent) {
357
358         // Check to see if this is the desired parent
359
if (parent == this) {
360             return new AffineTransform();
361
362             // Check to see if this object is a root object
363
}
364         else if (getParent() == null) {
365             return getTransform();
366
367             // Return this object's transform concatenated to those it inherits
368
}
369         else {
370             AffineTransform transform = getParent().getTransformRelativeTo(parent);
371             transform.concatenate(getTransform());
372             return transform;
373         }
374
375     }
376
377     /**
378      * Returns the point about which this object should rotate in the event that
379      * rotation should need to occur. This can be overridden to return any valid
380      * value desired, but left unchanged it will return the point at the center
381      * of the object's <CODE>Shape</CODE>'s bounding box.
382      *
383      * @return the point about which this object should rotate
384      */

385     synchronized public Point2D getRotationPoint() {
386         Rectangle2D bounds = shape.getBounds2D();
387         return new Point2D.Double(bounds.getCenterX(), bounds.getCenterY());
388     }
389
390     /**
391      * Returns whether or not this object's <CODE>Shape</CODE> contains the
392      * provided absolute vector world point. This does <I>not</I> consider the
393      * bounds of any embedded or attached objects.
394      *
395      * @param point the <CODE>Point2D</CODE> to check for containment in this
396      * object's <CODE>Shape</CODE>
397      * @return true if the point is contained, false otherwise
398      */

399     synchronized public boolean contains(final Point2D point) {
400
401         Point2D transPoint = (Point2D) point.clone();
402         try {
403             transPoint = getTransformRelativeTo(null).createInverse().transform(transPoint, transPoint);
404         }
405         catch (NoninvertibleTransformException e) {
406             return false;
407         }
408         return getShape().contains(transPoint);
409     }
410
411     /**
412      * Returns the <CODE>VisualObject</CODE>, if any, that is currently being
413      * displayed at the provided absolute vector world point.
414      *
415      * @param point the point of interest
416      * @return the <CODE>VisualObject</CODE>, if any, that is currently being
417      * displayed at the provided location, or null if there is no object
418      * at that location
419      */

420     synchronized public VisualObject getVisualObjectAt(final Point2D point) {
421
422         // Check to see if an embedded object is there first
423
if (objManager != null) {
424             for (VisualObject obj : objManager.getAllObjects()) {
425                 VisualObject found = obj.getVisualObjectAt(point);
426                 if (found != null) {
427                     return found;
428                 }
429             }
430         }
431
432         // Check to see if this object contains the point
433
if (contains(point)) {
434             return this;
435         }
436
437         return null;
438     }
439
440     /**
441      * This method is called by the <CODE>VisualObjectManager</CODE>
442      * immediately prior to rendering. It gives the object an opportunity to do
443      * any special calculation or preparation that should be completed before
444      * the rendering stage can reliably execute. Notably, if a parent object
445      * never becomes available for rendering, then none of the <CODE>prepare</CODE>
446      * methods of its children will ever be called.
447      */

448     synchronized public void prepare() {
449         // Does nothing by default
450
}
451
452     /**
453      * This method is called by the <CODE>VisualObjectManager</CODE> when this
454      * object and its embedded objects need to be rendered. If an implementation
455      * desires to prevent the rendering of child objects, this method can be
456      * overridden. Otherwise, it first calls the <CODE>renderObject</CODE>
457      * method on this object, then causes the <CODE>render</CODE> method on
458      * all embedded objects to be called. Finally, it calls the <CODE>renderOutline</CODE>
459      * method on this object.
460      *
461      * @param g the graphics context on which to render
462      */

463     synchronized public void render(final Graphics2D g) {
464
465         renderingDetail = getLevelOfDetail(g);
466
467         Graphics2D transformedG = (Graphics2D) g.create();
468         transformedG.transform(transform);
469         renderObject(transformedG);
470
471         if (objManager != null) {
472             // Only render children if the detail level is at least 2
473
if (renderingDetail >= 2) {
474                 objManager.renderNegativeLayers(transformedG);
475             }
476         }
477
478         renderOutline(transformedG);
479
480         if (objManager != null) {
481             // Only render children if the detail level is at least 2
482
if (renderingDetail >= 2) {
483                 objManager.renderNonNegativeLayers(transformedG);
484             }
485         }
486
487         transformedG.dispose();
488     }
489
490     /**
491      * Renders this object onto the supplied graphics context. Embedded objects
492      * will be rendered later, followed by the object's outline.
493      *
494      * @param g the graphics context on which to render
495      */

496     synchronized public void renderObject(final Graphics2D g) {
497         if (fillBrush != null) {
498             fillBrush.useOn(g);
499
500             if (renderingDetail >= 2) {
501                 g.fill(shape);
502             }
503             else if (renderingDetail == 1) {
504                 Rectangle rect = shape.getBounds();
505                 g.fillRect(rect.x, rect.y, rect.width, rect.height);
506             }
507         }
508     }
509
510     /**
511      * Renders this object's outline onto the supplied graphics context.
512      *
513      * @param g the graphics context on which to render
514      */

515     synchronized public void renderOutline(final Graphics2D g) {
516         if (renderingDetail >= 2) {
517             if (lineBrush != null) {
518                 lineBrush.useOn(g);
519                 g.draw(shape);
520             }
521         }
522     }
523
524 }
525
Popular Tags