001: /*
002:
003: Licensed to the Apache Software Foundation (ASF) under one or more
004: contributor license agreements. See the NOTICE file distributed with
005: this work for additional information regarding copyright ownership.
006: The ASF licenses this file to You under the Apache License, Version 2.0
007: (the "License"); you may not use this file except in compliance with
008: the License. You may obtain a copy of the License at
009:
010: http://www.apache.org/licenses/LICENSE-2.0
011:
012: Unless required by applicable law or agreed to in writing, software
013: distributed under the License is distributed on an "AS IS" BASIS,
014: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: See the License for the specific language governing permissions and
016: limitations under the License.
017:
018: */
019: package org.apache.batik.gvt;
020:
021: import java.awt.Graphics2D;
022: import java.awt.Shape;
023: import java.awt.geom.Point2D;
024: import java.awt.geom.Rectangle2D;
025:
026: import org.apache.batik.util.HaltingThread;
027:
028: /**
029: * A graphics node that represents a shape.
030: *
031: * @author <a href="mailto:Thierry.Kormann@sophia.inria.fr">Thierry Kormann</a>
032: * @version $Id: ShapeNode.java 475477 2006-11-15 22:44:28Z cam $
033: */
034: public class ShapeNode extends AbstractGraphicsNode {
035:
036: /**
037: * The shape that describes this <tt>ShapeNode</tt>.
038: */
039: protected Shape shape;
040:
041: /**
042: * The shape painter used to paint the shape of this shape node.
043: */
044: protected ShapePainter shapePainter;
045:
046: /**
047: * Internal Cache: Primitive bounds
048: */
049: private Rectangle2D primitiveBounds;
050:
051: /**
052: * Internal Cache: Geometry bounds
053: */
054: private Rectangle2D geometryBounds;
055:
056: /**
057: * Internal Cache: Sensitive bounds
058: */
059: private Rectangle2D sensitiveBounds;
060:
061: /**
062: * Internal Cache: The painted area.
063: */
064: private Shape paintedArea;
065:
066: /**
067: * Internal Cache: The sensitive area.
068: */
069: private Shape sensitiveArea;
070:
071: /**
072: * Constructs a new empty <tt>ShapeNode</tt>.
073: */
074: public ShapeNode() {
075: }
076:
077: //
078: // Properties methods
079: //
080:
081: /**
082: * Sets the shape of this <tt>ShapeNode</tt>.
083: *
084: * @param newShape the new shape of this shape node
085: */
086: public void setShape(Shape newShape) {
087: fireGraphicsNodeChangeStarted();
088: invalidateGeometryCache();
089: this .shape = newShape;
090: if (this .shapePainter != null) {
091: if (newShape != null) {
092: this .shapePainter.setShape(newShape);
093: } else {
094: this .shapePainter = null;
095: }
096: }
097: fireGraphicsNodeChangeCompleted();
098: }
099:
100: /**
101: * Returns the shape of this <tt>ShapeNode</tt>.
102: */
103: public Shape getShape() {
104: return shape;
105: }
106:
107: /**
108: * Sets the <tt>ShapePainter</tt> used by this shape node to render its
109: * shape.
110: *
111: * @param newShapePainter the new ShapePainter to use
112: */
113: public void setShapePainter(ShapePainter newShapePainter) {
114: if (shape == null) // Doesn't matter if we don't have a shape.
115: return;
116: fireGraphicsNodeChangeStarted();
117: invalidateGeometryCache();
118: this .shapePainter = newShapePainter;
119: if (shapePainter != null
120: && shape != this .shapePainter.getShape()) {
121: shapePainter.setShape(shape);
122: }
123: fireGraphicsNodeChangeCompleted();
124: }
125:
126: /**
127: * Returns the <tt>ShapePainter</tt> used by this shape node to render its
128: * shape.
129: */
130: public ShapePainter getShapePainter() {
131: return shapePainter;
132: }
133:
134: //
135: // Drawing methods
136: //
137:
138: /**
139: * Paints this node.
140: *
141: * @param g2d the Graphics2D to use
142: */
143: public void paint(Graphics2D g2d) {
144: if (isVisible)
145: super .paint(g2d);
146: }
147:
148: /**
149: * Paints this node without applying Filter, Mask, Composite, and clip.
150: *
151: * @param g2d the Graphics2D to use
152: */
153: public void primitivePaint(Graphics2D g2d) {
154: if (shapePainter != null) {
155: shapePainter.paint(g2d);
156: }
157: }
158:
159: //
160: // Geometric methods
161: //
162:
163: /**
164: * Invalidates this <tt>ShapeNode</tt>. This node and all its ancestors have
165: * been informed that all its cached values related to its bounds must be
166: * recomputed.
167: */
168: protected void invalidateGeometryCache() {
169: super .invalidateGeometryCache();
170: primitiveBounds = null;
171: geometryBounds = null;
172: sensitiveBounds = null;
173: paintedArea = null;
174: sensitiveArea = null;
175: }
176:
177: public void setPointerEventType(int pointerEventType) {
178: super .setPointerEventType(pointerEventType);
179: sensitiveBounds = null;
180: sensitiveArea = null;
181: }
182:
183: /**
184: * Returns true if the specified Point2D is inside the boundary of this
185: * node, false otherwise.
186: *
187: * @param p the specified Point2D in the user space
188: */
189: public boolean contains(Point2D p) {
190: switch (pointerEventType) {
191: case VISIBLE_PAINTED:
192: case VISIBLE_FILL:
193: case VISIBLE_STROKE:
194: case VISIBLE:
195: if (!isVisible)
196: return false;
197: // Fall Through
198: case PAINTED:
199: case FILL:
200: case STROKE:
201: case ALL: {
202: Rectangle2D b = getSensitiveBounds();
203: if (b == null || !b.contains(p))
204: return false;
205:
206: return inSensitiveArea(p);
207: }
208: case NONE:
209: default:
210: return false;
211: }
212: }
213:
214: /**
215: * Returns true if the interior of this node intersects the interior of a
216: * specified Rectangle2D, false otherwise.
217: *
218: * @param r the specified Rectangle2D in the user node space
219: */
220: public boolean intersects(Rectangle2D r) {
221: Rectangle2D b = getBounds();
222: if (b != null) {
223: return (b.intersects(r) && paintedArea != null && paintedArea
224: .intersects(r));
225: }
226: return false;
227: }
228:
229: /**
230: * Returns the bounds of the area covered by this node's primitive paint.
231: */
232: public Rectangle2D getPrimitiveBounds() {
233: if (!isVisible)
234: return null;
235: if (shape == null)
236: return null;
237: if (primitiveBounds != null)
238: return primitiveBounds;
239:
240: if (shapePainter == null)
241: primitiveBounds = shape.getBounds2D();
242: else
243: primitiveBounds = shapePainter.getPaintedBounds2D();
244:
245: // Check If we should halt early.
246: if (HaltingThread.hasBeenHalted()) {
247: // The Thread has been halted.
248: // Invalidate any cached values and proceed (this
249: // sets primitiveBounds to null).
250: invalidateGeometryCache();
251: }
252: return primitiveBounds;
253: }
254:
255: public boolean inSensitiveArea(Point2D pt) {
256: if (shapePainter == null)
257: return false;
258:
259: // <!> NOT REALLY NICE CODE BUT NO OTHER WAY
260: ShapePainter strokeShapePainter = null;
261: ShapePainter fillShapePainter = null;
262: if (shapePainter instanceof StrokeShapePainter) {
263: strokeShapePainter = shapePainter;
264: } else if (shapePainter instanceof FillShapePainter) {
265: fillShapePainter = shapePainter;
266: } else if (shapePainter instanceof CompositeShapePainter) {
267: CompositeShapePainter cp = (CompositeShapePainter) shapePainter;
268:
269: for (int i = 0; i < cp.getShapePainterCount(); ++i) {
270: ShapePainter sp = cp.getShapePainter(i);
271: if (sp instanceof StrokeShapePainter) {
272: strokeShapePainter = sp;
273: } else if (sp instanceof FillShapePainter) {
274: fillShapePainter = sp;
275: }
276: }
277: } else {
278: return false; // Don't know what we have...
279: }
280:
281: switch (pointerEventType) {
282: case VISIBLE_PAINTED:
283: case PAINTED:
284: return shapePainter.inPaintedArea(pt);
285: case VISIBLE:
286: case ALL:
287: return shapePainter.inSensitiveArea(pt);
288: case VISIBLE_FILL:
289: case FILL:
290: if (fillShapePainter != null)
291: return fillShapePainter.inSensitiveArea(pt);
292: break;
293: case VISIBLE_STROKE:
294: case STROKE:
295: if (strokeShapePainter != null)
296: return strokeShapePainter.inSensitiveArea(pt);
297: break;
298: case NONE:
299: default:
300: // nothing to tdo
301: }
302: return false;
303: }
304:
305: /**
306: * Returns the bounds of the sensitive area covered by this node,
307: * This includes the stroked area but does not include the effects
308: * of clipping, masking or filtering.
309: */
310: public Rectangle2D getSensitiveBounds() {
311: if (sensitiveBounds != null)
312: return sensitiveBounds;
313:
314: if (shapePainter == null)
315: return null;
316:
317: // <!> NOT REALLY NICE CODE BUT NO OTHER WAY
318: ShapePainter strokeShapePainter = null;
319: ShapePainter fillShapePainter = null;
320: if (shapePainter instanceof StrokeShapePainter) {
321: strokeShapePainter = shapePainter;
322: } else if (shapePainter instanceof FillShapePainter) {
323: fillShapePainter = shapePainter;
324: } else if (shapePainter instanceof CompositeShapePainter) {
325: CompositeShapePainter cp = (CompositeShapePainter) shapePainter;
326:
327: for (int i = 0; i < cp.getShapePainterCount(); ++i) {
328: ShapePainter sp = cp.getShapePainter(i);
329: if (sp instanceof StrokeShapePainter) {
330: strokeShapePainter = sp;
331: } else if (sp instanceof FillShapePainter) {
332: fillShapePainter = sp;
333: }
334: }
335: } else
336: return null; // Don't know what we have...
337:
338: switch (pointerEventType) {
339: case VISIBLE_PAINTED:
340: case PAINTED:
341: sensitiveBounds = shapePainter.getPaintedBounds2D();
342: break;
343: case VISIBLE_FILL:
344: case FILL:
345: if (fillShapePainter != null) {
346: sensitiveBounds = fillShapePainter
347: .getSensitiveBounds2D();
348: }
349: break;
350: case VISIBLE_STROKE:
351: case STROKE:
352: if (strokeShapePainter != null) {
353: sensitiveBounds = strokeShapePainter
354: .getSensitiveBounds2D();
355: }
356: break;
357: case VISIBLE:
358: case ALL:
359: sensitiveBounds = shapePainter.getSensitiveBounds2D();
360: break;
361: case NONE:
362: default:
363: // nothing to tdo
364: }
365: return sensitiveBounds;
366: }
367:
368: /**
369: * Returns the shape that represents the sensitive area of this graphics
370: * node.
371: */
372: public Shape getSensitiveArea() {
373: if (sensitiveArea != null)
374: return sensitiveArea;
375: if (shapePainter == null)
376: return null;
377:
378: // <!> NOT REALLY NICE CODE BUT NO OTHER WAY
379: ShapePainter strokeShapePainter = null;
380: ShapePainter fillShapePainter = null;
381: if (shapePainter instanceof StrokeShapePainter) {
382: strokeShapePainter = shapePainter;
383: } else if (shapePainter instanceof FillShapePainter) {
384: fillShapePainter = shapePainter;
385: } else if (shapePainter instanceof CompositeShapePainter) {
386: CompositeShapePainter cp = (CompositeShapePainter) shapePainter;
387:
388: for (int i = 0; i < cp.getShapePainterCount(); ++i) {
389: ShapePainter sp = cp.getShapePainter(i);
390: if (sp instanceof StrokeShapePainter) {
391: strokeShapePainter = sp;
392: } else if (sp instanceof FillShapePainter) {
393: fillShapePainter = sp;
394: }
395: }
396: } else
397: return null; // Don't know what we have...
398:
399: switch (pointerEventType) {
400: case VISIBLE_PAINTED:
401: case PAINTED:
402: sensitiveArea = shapePainter.getPaintedArea();
403: break;
404: case VISIBLE_FILL:
405: case FILL:
406: if (fillShapePainter != null) {
407: sensitiveArea = fillShapePainter.getSensitiveArea();
408: }
409: break;
410: case VISIBLE_STROKE:
411: case STROKE:
412: if (strokeShapePainter != null) {
413: sensitiveArea = strokeShapePainter.getSensitiveArea();
414: }
415: break;
416: case VISIBLE:
417: case ALL:
418: sensitiveArea = shapePainter.getSensitiveArea();
419: break;
420: case NONE:
421: default:
422: // nothing to tdo
423: }
424: return sensitiveArea;
425: }
426:
427: /**
428: * Returns the bounds of the area covered by this node, without
429: * taking any of its rendering attribute into account. That is,
430: * exclusive of any clipping, masking, filtering or stroking, for
431: * example.
432: */
433: public Rectangle2D getGeometryBounds() {
434: if (geometryBounds == null) {
435: if (shape == null) {
436: return null;
437: }
438: geometryBounds = normalizeRectangle(shape.getBounds2D());
439: }
440: return geometryBounds;
441: }
442:
443: /**
444: * Returns the outline of this node.
445: */
446: public Shape getOutline() {
447: return shape;
448: }
449: }
|