KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > batik > gvt > AbstractGraphicsNode


1 /*
2
3    Copyright 2001-2003 The Apache Software Foundation
4
5    Licensed under the Apache License, Version 2.0 (the "License");
6    you may not use this file except in compliance with the License.
7    You may obtain a copy of the License at
8
9        http://www.apache.org/licenses/LICENSE-2.0
10
11    Unless required by applicable law or agreed to in writing, software
12    distributed under the License is distributed on an "AS IS" BASIS,
13    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14    See the License for the specific language governing permissions and
15    limitations under the License.
16
17  */

18 package org.apache.batik.gvt;
19
20 import java.awt.AlphaComposite JavaDoc;
21 import java.awt.Composite JavaDoc;
22 import java.awt.Graphics2D JavaDoc;
23 import java.awt.RenderingHints JavaDoc;
24 import java.awt.Shape JavaDoc;
25 import java.awt.geom.AffineTransform JavaDoc;
26 import java.awt.geom.NoninvertibleTransformException JavaDoc;
27 import java.awt.geom.Point2D JavaDoc;
28 import java.awt.geom.Rectangle2D JavaDoc;
29 import java.lang.ref.WeakReference JavaDoc;
30 import java.util.Iterator JavaDoc;
31 import java.util.List JavaDoc;
32 import java.util.Map JavaDoc;
33
34 import javax.swing.event.EventListenerList JavaDoc;
35
36 import org.apache.batik.ext.awt.RenderingHintsKeyExt;
37 import org.apache.batik.ext.awt.image.renderable.ClipRable;
38 import org.apache.batik.ext.awt.image.renderable.Filter;
39 import org.apache.batik.gvt.event.GraphicsNodeChangeEvent;
40 import org.apache.batik.gvt.event.GraphicsNodeChangeListener;
41 import org.apache.batik.gvt.filter.GraphicsNodeRable;
42 import org.apache.batik.gvt.filter.GraphicsNodeRable8Bit;
43 import org.apache.batik.gvt.filter.Mask;
44 import org.apache.batik.util.HaltingThread;
45
46 /**
47  * A partial implementation of the <tt>GraphicsNode</tt> interface.
48  *
49  * @author <a HREF="mailto:Thierry.Kormann@sophia.inria.fr">Thierry Kormann</a>
50  * @author <a HREF="mailto:etissandier@ilog.fr">Emmanuel Tissandier</a>
51  * @author <a HREF="mailto:Thomas.DeWeeese@Kodak.com">Thomas DeWeese</a>
52  * @version $Id: AbstractGraphicsNode.java,v 1.58 2005/03/27 08:58:34 cam Exp $
53  */

54 public abstract class AbstractGraphicsNode implements GraphicsNode {
55
56     /**
57      * The listeners list.
58      */

59     protected EventListenerList JavaDoc listeners;
60
61     /**
62      * The transform of this graphics node.
63      */

64     protected AffineTransform JavaDoc transform;
65
66     /**
67      * The inverse transform for this node, i.e., from parent node
68      * to this node.
69      */

70     protected AffineTransform JavaDoc inverseTransform;
71
72     /**
73      * The compositing operation to be used when a graphics node is
74      * painted on top of another one.
75      */

76     protected Composite JavaDoc composite;
77
78     /**
79      * This flag bit indicates whether or not this graphics node is visible.
80      */

81     protected boolean isVisible = true;
82
83     /**
84      * The clipping filter for this graphics node.
85      */

86     protected ClipRable clip;
87
88     /**
89      * The rendering hints that control the quality to use when rendering
90      * this graphics node.
91      */

92     protected RenderingHints JavaDoc hints;
93
94     /**
95      * The parent of this graphics node.
96      */

97     protected CompositeGraphicsNode parent;
98
99     /**
100      * The root of the GVT tree.
101      */

102     protected RootGraphicsNode root;
103
104     /**
105      * The mask of this graphics node.
106      */

107     protected Mask mask;
108
109     /**
110      * The filter of this graphics node.
111      */

112     protected Filter filter;
113
114     /**
115      * Indicates how this graphics node reacts to events.
116      */

117     protected int pointerEventType = VISIBLE_PAINTED;
118
119     /**
120      * The GraphicsNodeRable for this node.
121      */

122     protected WeakReference JavaDoc graphicsNodeRable;
123
124     /**
125      * The GraphicsNodeRable for this node with all filtering applied
126      */

127     protected WeakReference JavaDoc enableBackgroundGraphicsNodeRable;
128
129     /**
130      * A Weak Reference to this.
131      */

132     protected WeakReference JavaDoc weakRef;
133
134     /**
135      * Internal Cache: node bounds
136      */

137     private Rectangle2D JavaDoc bounds;
138
139
140     protected GraphicsNodeChangeEvent changeStartedEvent = null;
141     protected GraphicsNodeChangeEvent changeCompletedEvent = null;
142
143     /**
144      * Constructs a new graphics node.
145      */

146     protected AbstractGraphicsNode() {}
147
148     /**
149      * Returns a canonical WeakReference to this GraphicsNode.
150      * This is suitable for use as a key value in a hash map
151      */

152     public WeakReference JavaDoc getWeakReference() {
153         if (weakRef == null)
154             weakRef = new WeakReference JavaDoc(this);
155         return weakRef;
156     }
157
158     //
159
// Properties methods
160
//
161

162     /**
163      * Returns the type that describes how this graphics node reacts to events.
164      *
165      * @return VISIBLE_PAINTED | VISIBLE_FILL | VISIBLE_STROKE | VISIBLE |
166      * PAINTED | FILL | STROKE | ALL | NONE
167      */

168     public int getPointerEventType() {
169         return pointerEventType;
170     }
171
172     /**
173      * Sets the type that describes how this graphics node reacts to events.
174      *
175      * @param pointerEventType VISIBLE_PAINTED | VISIBLE_FILL | VISIBLE_STROKE |
176      * VISIBLE | PAINTED | FILL | STROKE | ALL | NONE
177      */

178     public void setPointerEventType(int pointerEventType) {
179         this.pointerEventType = pointerEventType;
180     }
181
182     /**
183      * Sets the transform of this node.
184      *
185      * @param newTransform the new transform of this node
186      */

187     public void setTransform(AffineTransform JavaDoc newTransform) {
188         fireGraphicsNodeChangeStarted();
189         this.transform = newTransform;
190         if(transform.getDeterminant() != 0){
191             try{
192                 inverseTransform = transform.createInverse();
193             }catch(NoninvertibleTransformException JavaDoc e){
194                 // Should never happen.
195
throw new Error JavaDoc();
196             }
197         } else {
198             // The transform is not invertible. Use the same
199
// transform.
200
inverseTransform = transform;
201         }
202         if (parent != null)
203             parent.invalidateGeometryCache();
204         fireGraphicsNodeChangeCompleted();
205     }
206
207     /**
208      * Returns the transform of this node or null if any.
209      */

210     public AffineTransform JavaDoc getTransform() {
211         return transform;
212     }
213
214     /**
215      * Returns the inverse transform for this node.
216      */

217     public AffineTransform JavaDoc getInverseTransform(){
218         return inverseTransform;
219     }
220
221     /**
222      * Returns the concatenated transform of this node. That is, this
223      * node's transform preconcatenated with it's parent's transforms.
224      */

225     public AffineTransform JavaDoc getGlobalTransform(){
226         AffineTransform JavaDoc ctm = new AffineTransform JavaDoc();
227         GraphicsNode node = this;
228         while (node != null) {
229             if(node.getTransform() != null){
230                 ctm.preConcatenate(node.getTransform());
231             }
232             node = node.getParent();
233         }
234         return ctm;
235     }
236
237     /**
238      * Sets the composite of this node.
239      *
240      * @param newComposite the composite of this node
241      */

242     public void setComposite(Composite JavaDoc newComposite) {
243         fireGraphicsNodeChangeStarted();
244         invalidateGeometryCache();
245         this.composite = newComposite;
246         fireGraphicsNodeChangeCompleted();
247     }
248
249     /**
250      * Returns the composite of this node or null if any.
251      */

252     public Composite JavaDoc getComposite() {
253         return composite;
254     }
255
256     /**
257      * Sets if this node is visible or not depending on the specified value.
258      *
259      * @param isVisible If true this node is visible
260      */

261     public void setVisible(boolean isVisible) {
262         fireGraphicsNodeChangeStarted();
263         this.isVisible = isVisible;
264         invalidateGeometryCache();
265         fireGraphicsNodeChangeCompleted();
266     }
267
268     /**
269      * Returns true if this node is visible, false otherwise.
270      */

271     public boolean isVisible() {
272         return isVisible;
273     }
274
275     public void setClip(ClipRable newClipper) {
276         fireGraphicsNodeChangeStarted();
277         invalidateGeometryCache();
278         this.clip = newClipper;
279         fireGraphicsNodeChangeCompleted();
280     }
281
282     /**
283      * Returns the clipping filter of this node or null if any.
284      */

285     public ClipRable getClip() {
286         return clip;
287     }
288
289     /**
290      * Maps the specified key to the specified value in the rendering hints of
291      * this node.
292      *
293      * @param key the key of the hint to be set
294      * @param value the value indicating preferences for the specified
295      * hint category.
296      */

297     public void setRenderingHint(RenderingHints.Key JavaDoc key, Object JavaDoc value) {
298         fireGraphicsNodeChangeStarted();
299         if (this.hints == null) {
300             this.hints = new RenderingHints JavaDoc(key, value);
301         } else {
302             hints.put(key, value);
303         }
304         fireGraphicsNodeChangeCompleted();
305     }
306
307     /**
308      * Copies all of the mappings from the specified Map to the
309      * rendering hints of this node.
310      *
311      * @param hints the rendering hints to be set
312      */

313     public void setRenderingHints(Map JavaDoc hints) {
314         fireGraphicsNodeChangeStarted();
315         if (this.hints == null) {
316             this.hints = new RenderingHints JavaDoc(hints);
317         } else {
318             this.hints.putAll(hints);
319         }
320         fireGraphicsNodeChangeCompleted();
321     }
322
323     /**
324      * Sets the rendering hints of this node.
325      *
326      * @param newHints the new rendering hints of this node
327      */

328     public void setRenderingHints(RenderingHints JavaDoc newHints) {
329         fireGraphicsNodeChangeStarted();
330         this.hints = newHints;
331         fireGraphicsNodeChangeCompleted();
332     }
333
334     /**
335      * Returns the rendering hints of this node or null if any.
336      */

337     public RenderingHints JavaDoc getRenderingHints() {
338         return hints;
339     }
340
341     /**
342      * Sets the mask of this node.
343      *
344      * @param newMask the new mask of this node
345      */

346     public void setMask(Mask newMask) {
347         fireGraphicsNodeChangeStarted();
348         invalidateGeometryCache();
349         this.mask = newMask;
350         fireGraphicsNodeChangeCompleted();
351     }
352
353     /**
354      * Returns the mask of this node or null if any.
355      */

356     public Mask getMask() {
357         return mask;
358     }
359
360     /**
361      * Sets the filter of this node.
362      *
363      * @param newFilter the new filter of this node
364      */

365     public void setFilter(Filter newFilter) {
366         fireGraphicsNodeChangeStarted();
367         invalidateGeometryCache();
368         this.filter = newFilter;
369         fireGraphicsNodeChangeCompleted();
370     }
371
372     /**
373      * Returns the filter of this node or null if any.
374      */

375     public Filter getFilter() {
376         return filter;
377     }
378
379     /**
380      * Returns the GraphicsNodeRable for this node. This
381      * GraphicsNodeRable is the Renderable (Filter) before any of the
382      * filter operations have been applied.
383      */

384     public Filter getGraphicsNodeRable(boolean createIfNeeded) {
385         GraphicsNodeRable ret = null;
386         if (graphicsNodeRable != null) {
387             ret = (GraphicsNodeRable)graphicsNodeRable.get();
388             if (ret != null) return ret;
389         }
390         if (createIfNeeded) {
391         ret = new GraphicsNodeRable8Bit(this);
392         graphicsNodeRable = new WeakReference JavaDoc(ret);
393         }
394         return ret;
395     }
396
397     /**
398      * Returns the GraphicsNodeRable for this node. This
399      * GraphicsNodeRable is the Renderable (Filter) after all of the
400      * filter operations have been applied.
401      */

402     public Filter getEnableBackgroundGraphicsNodeRable
403         (boolean createIfNeeded) {
404         GraphicsNodeRable ret = null;
405         if (enableBackgroundGraphicsNodeRable != null) {
406             ret = (GraphicsNodeRable)enableBackgroundGraphicsNodeRable.get();
407             if (ret != null) return ret;
408         }
409         if (createIfNeeded) {
410             ret = new GraphicsNodeRable8Bit(this);
411             ret.setUsePrimitivePaint(false);
412             enableBackgroundGraphicsNodeRable = new WeakReference JavaDoc(ret);
413         }
414         return ret;
415     }
416
417     //
418
// Drawing methods
419
//
420

421     /**
422      * Paints this node.
423      *
424      * @param g2d the Graphics2D to use
425      */

426     public void paint(Graphics2D JavaDoc g2d){
427         if ((composite != null) &&
428             (composite instanceof AlphaComposite JavaDoc)) {
429             AlphaComposite JavaDoc ac = (AlphaComposite JavaDoc)composite;
430             if (ac.getAlpha() < 0.001)
431                 return; // No point in drawing
432
}
433         Rectangle2D JavaDoc bounds = getBounds();
434         if (bounds == null) return;
435
436         // Set up graphic context. It is important to setup the
437
// transform first, because the clip is defined in this node's
438
// user space.
439
Shape JavaDoc defaultClip = g2d.getClip();
440         Composite JavaDoc defaultComposite = g2d.getComposite();
441         AffineTransform JavaDoc defaultTransform = g2d.getTransform();
442         RenderingHints JavaDoc defaultHints = null;
443
444         if (hints != null) {
445             defaultHints = g2d.getRenderingHints();
446             g2d.addRenderingHints(hints);
447         }
448         if (transform != null) {
449             g2d.transform(transform);
450         }
451         if (composite != null) {
452             g2d.setComposite(composite);
453         }
454         if (clip != null){
455             g2d.clip(clip.getClipPath());
456         }
457
458         Shape JavaDoc curClip = g2d.getClip();
459         g2d.setRenderingHint(RenderingHintsKeyExt.KEY_AREA_OF_INTEREST,
460                              curClip);
461
462         // Check if any painting is needed at all. Get the clip (in user space)
463
// and see if it intersects with this node's bounds (in user space).
464
boolean paintNeeded = true;
465         Shape JavaDoc g2dClip = curClip; //g2d.getClip();
466
if (g2dClip != null) {
467             Rectangle2D JavaDoc cb = g2dClip.getBounds2D();
468             if(!bounds.intersects(cb.getX(), cb.getY(),
469                                   cb.getWidth(), cb.getHeight()))
470                 paintNeeded = false;
471         }
472
473         // Only paint if needed.
474
if (paintNeeded){
475             boolean antialiasedClip = false;
476             if ((clip != null) && clip.getUseAntialiasedClip()) {
477                 antialiasedClip = isAntialiasedClip(g2d.getTransform(),
478                                                     g2d.getRenderingHints(),
479                                                     clip.getClipPath());
480             }
481
482             boolean useOffscreen = isOffscreenBufferNeeded();
483
484             useOffscreen |= antialiasedClip;
485
486             if (!useOffscreen) {
487                 // Render on this canvas.
488
primitivePaint(g2d);
489             } else {
490                 Filter filteredImage = null;
491
492                 if(filter == null){
493                     filteredImage = getGraphicsNodeRable(true);
494                 }
495                 else {
496                     // traceFilter(filter, "=====>> ");
497
filteredImage = filter;
498                 }
499
500                 if (mask != null) {
501                     if (mask.getSource() != filteredImage){
502                         mask.setSource(filteredImage);
503                     }
504                     filteredImage = mask;
505                 }
506
507                 if (clip != null && antialiasedClip) {
508                     if (clip.getSource() != filteredImage){
509                         clip.setSource(filteredImage);
510                     }
511                     filteredImage = clip;
512                 }
513
514                 if(antialiasedClip){
515                     // Remove hard edged clip
516
g2d.setClip(null);
517                 }
518
519                 Rectangle2D JavaDoc filterBounds = filteredImage.getBounds2D();
520                 g2d.clip(filterBounds);
521
522                 org.apache.batik.ext.awt.image.GraphicsUtil.drawImage
523                     (g2d, filteredImage);
524             }
525         }
526
527         // Restore default rendering attributes
528
if (defaultHints != null) {
529             g2d.setRenderingHints(defaultHints);
530         }
531         g2d.setTransform(defaultTransform);
532         g2d.setClip(defaultClip);
533         if (composite != null) {
534             g2d.setComposite(defaultComposite);
535         }
536     }
537
538     /**
539      * DEBUG: Trace filter chain
540      */

541     private void traceFilter(Filter filter, String JavaDoc prefix){
542         System.out.println(prefix + filter.getClass().getName());
543         System.out.println(prefix + filter.getBounds2D());
544         java.util.Vector JavaDoc sources = filter.getSources();
545         int nSources = sources != null ? sources.size() : 0;
546         prefix += "\t";
547         for(int i=0; i<nSources; i++){
548             Filter source = (Filter)sources.elementAt(i);
549             traceFilter(source, prefix);
550         }
551
552         System.out.flush();
553     }
554
555     /**
556      * Returns true of an offscreen buffer is needed to render this node, false
557      * otherwise.
558      */

559     protected boolean isOffscreenBufferNeeded() {
560         return ((filter != null) ||
561                 (mask != null) ||
562                 (composite != null &&
563                  !AlphaComposite.SrcOver.equals(composite)));
564     }
565
566     /**
567      * Returns true if there is a clip and it should be antialiased
568      */

569     protected boolean isAntialiasedClip(AffineTransform JavaDoc usr2dev,
570                                         RenderingHints JavaDoc hints,
571                                         Shape JavaDoc clip){
572         // Antialias clip if:
573
// + The KEY_CLIP_ANTIALIASING is true.
574
// *and*
575
// + clip is not null
576
// *and*
577
// + clip is not a rectangle in device space.
578
//
579
// This leaves out the case where the node clip is a
580
// rectangle and the current clip (i.e., the intersection
581
// of the current Graphics2D's clip and this node's clip)
582
// is not a rectangle.
583
//
584
if (clip == null) return false;
585
586         Object JavaDoc val = hints.get(RenderingHintsKeyExt.KEY_TRANSCODING);
587         if ((val == RenderingHintsKeyExt.VALUE_TRANSCODING_PRINTING) ||
588             (val == RenderingHintsKeyExt.VALUE_TRANSCODING_VECTOR))
589             return false;
590
591         if(!(clip instanceof Rectangle2D JavaDoc &&
592              usr2dev.getShearX() == 0 &&
593              usr2dev.getShearY() == 0))
594             return true;
595
596         return false;
597     }
598
599     //
600
// Event support methods
601
//
602
public void fireGraphicsNodeChangeStarted(GraphicsNode changeSrc) {
603         if (changeStartedEvent == null)
604             changeStartedEvent = new GraphicsNodeChangeEvent
605                 (this, GraphicsNodeChangeEvent.CHANGE_STARTED);
606         changeStartedEvent.setChangeSrc(changeSrc);
607         fireGraphicsNodeChangeStarted(changeStartedEvent);
608         changeStartedEvent.setChangeSrc(null);
609     }
610
611     //
612
// Event support methods
613
//
614
public void fireGraphicsNodeChangeStarted() {
615         if (changeStartedEvent == null)
616             changeStartedEvent = new GraphicsNodeChangeEvent
617                 (this, GraphicsNodeChangeEvent.CHANGE_STARTED);
618         else {
619             changeStartedEvent.setChangeSrc(null);
620         }
621         fireGraphicsNodeChangeStarted(changeStartedEvent);
622     }
623
624     public void fireGraphicsNodeChangeStarted
625         (GraphicsNodeChangeEvent changeStartedEvent) {
626         // If we had per node listeners we would fire them here...
627

628         RootGraphicsNode rootGN = getRoot();
629         if (rootGN == null) return;
630
631         List JavaDoc l = rootGN.getTreeGraphicsNodeChangeListeners();
632         if (l == null) return;
633
634         Iterator JavaDoc i = l.iterator();
635         GraphicsNodeChangeListener gncl;
636         while (i.hasNext()) {
637             gncl = (GraphicsNodeChangeListener)i.next();
638             gncl.changeStarted(changeStartedEvent);
639         }
640     }
641
642     public void fireGraphicsNodeChangeCompleted() {
643         if (changeCompletedEvent == null) {
644             changeCompletedEvent = new GraphicsNodeChangeEvent
645                 (this, GraphicsNodeChangeEvent.CHANGE_COMPLETED);
646         }
647
648         // If we had per node listeners we would fire them here...
649

650         RootGraphicsNode rootGN = getRoot();
651         if (rootGN == null) return;
652
653         List JavaDoc l = rootGN.getTreeGraphicsNodeChangeListeners();
654         if (l == null) return;
655
656         Iterator JavaDoc i = l.iterator();
657         GraphicsNodeChangeListener gncl;
658         while (i.hasNext()) {
659             gncl = (GraphicsNodeChangeListener)i.next();
660             gncl.changeCompleted(changeCompletedEvent);
661         }
662     }
663
664
665     //
666
// Structural methods
667
//
668

669     /**
670      * Returns the parent of this node or null if any.
671      */

672     public CompositeGraphicsNode getParent() {
673         return parent;
674     }
675
676     /**
677      * Returns the root of the GVT tree or null if the node is not part of a GVT
678      * tree.
679      */

680     public RootGraphicsNode getRoot() {
681         return root;
682     }
683
684     /**
685      * Sets the root node of this graphics node.
686      *
687      * @param newRoot the new root node of this node
688      */

689     protected void setRoot(RootGraphicsNode newRoot) {
690         this.root = newRoot;
691     }
692
693     /**
694      * Sets the parent node of this graphics node.
695      *
696      * @param newParent the new parent node of this node
697      */

698     protected void setParent(CompositeGraphicsNode newParent) {
699         this. parent = newParent;
700     }
701
702     //
703
// Geometric methods
704
//
705

706     /**
707      * Invalidates the cached geometric bounds. This method is called
708      * each time an attribute that affects the bounds of this node
709      * changed.
710      */

711     protected void invalidateGeometryCache() {
712         // If our bounds are invalid then our parents bounds
713
// must be invalid also. So just return.
714
//if (bounds == null) return;
715

716         if (parent != null) {
717             parent.invalidateGeometryCache();
718         }
719         bounds = null;
720     }
721
722     /**
723      * Returns the bounds of this node in user space. This includes primitive
724      * paint, filtering, clipping and masking.
725      */

726     public Rectangle2D JavaDoc getBounds(){
727         // Get the primitive bounds
728
// Rectangle2D bounds = null;
729
if (bounds == null) {
730             // The painted region, before cliping, masking and compositing is
731
// either the area painted by the primitive paint or the area
732
// painted by the filter.
733
if(filter == null){
734                 bounds = getPrimitiveBounds();
735             } else {
736                 bounds = filter.getBounds2D();
737             }
738             // Factor in the clipping area, if any
739
if(bounds != null){
740                 if (clip != null) {
741                     Rectangle2D JavaDoc clipR = clip.getClipPath().getBounds2D();
742                     if (clipR.intersects(bounds))
743                         Rectangle2D.intersect(bounds, clipR, bounds);
744                 }
745                 // Factor in the mask, if any
746
if (mask != null) {
747                     Rectangle2D JavaDoc maskR = mask.getBounds2D();
748                     if (maskR.intersects(bounds))
749                         Rectangle2D.intersect(bounds, maskR, bounds);
750                 }
751             }
752
753             bounds = normalizeRectangle(bounds);
754
755             // Check If we should halt early.
756
if (HaltingThread.hasBeenHalted()) {
757                 // The Thread has been 'halted'.
758
// Invalidate any cached values and proceed.
759
invalidateGeometryCache();
760             }
761         }
762
763         return bounds;
764     }
765
766     /**
767      * Returns the bounds of this node after applying the input transform
768      * (if any), concatenated with this node's transform (if any).
769      *
770      * @param txf the affine transform with which this node's transform should
771      * be concatenated. Should not be null.
772      */

773     public Rectangle2D JavaDoc getTransformedBounds(AffineTransform JavaDoc txf){
774         AffineTransform JavaDoc t = txf;
775         if (transform != null) {
776             t = new AffineTransform JavaDoc(txf);
777             t.concatenate(transform);
778         }
779
780         // The painted region, before cliping, masking and compositing
781
// is either the area painted by the primitive paint or the
782
// area painted by the filter.
783
Rectangle2D JavaDoc tBounds = null;
784         if (filter == null) {
785             // Use txf, not t
786
tBounds = getTransformedPrimitiveBounds(txf);
787         } else {
788             tBounds = t.createTransformedShape
789                 (filter.getBounds2D()).getBounds2D();
790         }
791         // Factor in the clipping area, if any
792
if (tBounds != null) {
793             if (clip != null) {
794                 Rectangle2D.intersect
795                     (tBounds,
796                      t.createTransformedShape(clip.getClipPath()).getBounds2D(),
797                      tBounds);
798             }
799
800             // Factor in the mask, if any
801
if(mask != null) {
802                 Rectangle2D.intersect
803                     (tBounds,
804                      t.createTransformedShape(mask.getBounds2D()).getBounds2D(),
805                      tBounds);
806             }
807         }
808
809         return tBounds;
810     }
811
812     /**
813      * Returns the bounds of this node's primitivePaint after applying
814      * the input transform (if any), concatenated with this node's
815      * transform (if any).
816      *
817      * @param txf the affine transform with which this node's transform should
818      * be concatenated. Should not be null. */

819     public Rectangle2D JavaDoc getTransformedPrimitiveBounds(AffineTransform JavaDoc txf) {
820         Rectangle2D JavaDoc tpBounds = getPrimitiveBounds();
821         if (tpBounds == null) {
822             return null;
823         }
824         AffineTransform JavaDoc t = txf;
825         if (transform != null) {
826             t = new AffineTransform JavaDoc(txf);
827             t.concatenate(transform);
828         }
829
830         return t.createTransformedShape(tpBounds).getBounds2D();
831     }
832
833     /**
834      * Returns the bounds of the area covered by this node, without
835      * taking any of its rendering attribute into accoun. That is,
836      * exclusive of any clipping, masking, filtering or stroking, for
837      * example. The returned value is transformed by the concatenation
838      * of the input transform and this node's transform.
839      *
840      * @param txf the affine transform with which this node's transform should
841      * be concatenated. Should not be null.
842      */

843     public Rectangle2D JavaDoc getTransformedGeometryBounds(AffineTransform JavaDoc txf) {
844         Rectangle2D JavaDoc tpBounds = getGeometryBounds();
845         if (tpBounds == null) {
846             return null;
847         }
848         AffineTransform JavaDoc t = txf;
849         if (transform != null) {
850             t = new AffineTransform JavaDoc(txf);
851             t.concatenate(transform);
852         }
853
854         return t.createTransformedShape(tpBounds).getBounds2D();
855     }
856
857     /**
858      * Returns the bounds of the sensitive area covered by this node,
859      * This includes the stroked area but does not include the effects
860      * of clipping, masking or filtering. The returned value is
861      * transformed by the concatenation of the input transform and
862      * this node's transform.
863      *
864      * @param txf the affine transform with which this node's
865      * transform should be concatenated. Should not be null.
866      */

867     public Rectangle2D JavaDoc getTransformedSensitiveBounds(AffineTransform JavaDoc txf) {
868         Rectangle2D JavaDoc sBounds = getSensitiveBounds();
869         if (sBounds == null) {
870             return null;
871         }
872         AffineTransform JavaDoc t = txf;
873         if (transform != null) {
874             t = new AffineTransform JavaDoc(txf);
875             t.concatenate(transform);
876         }
877
878         return t.createTransformedShape(sBounds).getBounds2D();
879     }
880
881     /**
882      * Returns true if the specified Point2D is inside the boundary of this
883      * node, false otherwise.
884      *
885      * @param p the specified Point2D in the user space
886      */

887     public boolean contains(Point2D JavaDoc p) {
888         Rectangle2D JavaDoc b = getSensitiveBounds();
889         if (b == null || !b.contains(p)) {
890             return false;
891         }
892         switch(pointerEventType) {
893         case VISIBLE_PAINTED:
894         case VISIBLE_FILL:
895         case VISIBLE_STROKE:
896         case VISIBLE:
897             return isVisible;
898         case PAINTED:
899         case FILL:
900         case STROKE:
901         case ALL:
902             return true;
903         case NONE:
904         default:
905             return false;
906         }
907     }
908
909     /**
910      * Returns true if the interior of this node intersects the interior of a
911      * specified Rectangle2D, false otherwise.
912      *
913      * @param r the specified Rectangle2D in the user node space
914      */

915     public boolean intersects(Rectangle2D JavaDoc r) {
916         Rectangle2D JavaDoc b = getBounds();
917         if (b == null) return false;
918
919         return b.intersects(r);
920     }
921
922     /**
923      * Returns the GraphicsNode containing point p if this node or one of its
924      * children is sensitive to mouse events at p.
925      *
926      * @param p the specified Point2D in the user space
927      */

928     public GraphicsNode nodeHitAt(Point2D JavaDoc p) {
929         return (contains(p) ? this : null);
930     }
931
932     static double EPSILON = 1e-6;
933
934     /**
935      * This method makes sure that neither the width nor height of the
936      * rectangle is zero. But it tries to make them very small
937      * relatively speaking.
938      */

939     protected Rectangle2D JavaDoc normalizeRectangle(Rectangle2D JavaDoc bounds) {
940         if (bounds == null) return null;
941
942         if ((bounds.getWidth() < EPSILON)) {
943             if (bounds.getHeight() < EPSILON) {
944                 AffineTransform JavaDoc gt = getGlobalTransform();
945                 double det = Math.sqrt(gt.getDeterminant());
946                 return new Rectangle2D.Double JavaDoc
947                     (bounds.getX(), bounds.getY(), EPSILON/det, EPSILON/det);
948             } else {
949                 double tmpW = bounds.getHeight()*EPSILON;
950                 if (tmpW < bounds.getWidth())
951                     tmpW = bounds.getWidth();
952                 return new Rectangle2D.Double JavaDoc
953                     (bounds.getX(), bounds.getY(),
954                      tmpW, bounds.getHeight());
955             }
956         } else if (bounds.getHeight() < EPSILON) {
957             double tmpH = bounds.getWidth()*EPSILON;
958             if (tmpH < bounds.getHeight())
959                 tmpH = bounds.getHeight();
960             return new Rectangle2D.Double JavaDoc
961                 (bounds.getX(), bounds.getY(),
962                  bounds.getWidth(), tmpH);
963         }
964         return bounds;
965     }
966
967 }
968
Popular Tags