001    /*
002     * BoundedElement.java
003     *
004     *
005     *  The Salamander Project - 2D and 3D graphics libraries in Java
006     *  Copyright (C) 2004 Mark McKay
007     *
008     *  This library is free software; you can redistribute it and/or
009     *  modify it under the terms of the GNU Lesser General Public
010     *  License as published by the Free Software Foundation; either
011     *  version 2.1 of the License, or (at your option) any later version.
012     *
013     *  This library is distributed in the hope that it will be useful,
014     *  but WITHOUT ANY WARRANTY; without even the implied warranty of
015     *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
016     *  Lesser General Public License for more details.
017     *
018     *  You should have received a copy of the GNU Lesser General Public
019     *  License along with this library; if not, write to the Free Software
020     *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
021     *
022     *  Mark McKay can be contacted at mark@kitfox.com.  Salamander and other
023     *  projects can be found at http://www.kitfox.com
024     *
025     * Created on January 26, 2004, 9:00 AM
026     */
027    
028    package com.kitfox.svg;
029    
030    import com.kitfox.svg.xml.StyleAttribute;
031    import java.awt.Graphics2D;
032    import java.awt.Shape;
033    import java.awt.geom.AffineTransform;
034    import java.awt.geom.Area;
035    import java.awt.geom.Point2D;
036    import java.awt.geom.Rectangle2D;
037    import java.net.URI;
038    import java.util.List;
039    
040    
041    
042    /**
043     * Maintains bounding box for this element
044     *
045     * @author Mark McKay
046     * @author <a href="mailto:mark@kitfox.com">Mark McKay</a>
047     */
048    abstract public class RenderableElement extends TransformableElement 
049    {
050    
051        AffineTransform cachedXform = null;
052        Shape cachedClip = null;
053        
054        public static final int VECTOR_EFFECT_NONE = 0;
055        public static final int VECTOR_EFFECT_NON_SCALING_STROKE = 1;
056        int vectorEffect;
057    
058        /** Creates a new instance of BoundedElement */
059        public RenderableElement() {
060        }
061    
062        public RenderableElement(String id, SVGElement parent)
063        {
064            super(id, parent);
065        }
066    
067        protected void build() throws SVGException
068        {
069            super.build();
070            
071            StyleAttribute sty = new StyleAttribute();
072            
073            if (getPres(sty.setName("vector-effect")))
074            {
075                if ("non-scaling-stroke".equals(sty.getStringValue()))
076                {
077                    vectorEffect = VECTOR_EFFECT_NON_SCALING_STROKE;
078                }
079                else
080                {
081                    vectorEffect = VECTOR_EFFECT_NONE;
082                }
083            }
084            else
085            {
086                vectorEffect = VECTOR_EFFECT_NONE;
087            }
088        }
089        
090        abstract public void render(Graphics2D g) throws SVGException;
091        
092        abstract void pick(Point2D point, boolean boundingBox, List retVec) throws SVGException;
093        
094        abstract void pick(Rectangle2D pickArea, AffineTransform ltw, boolean boundingBox, List retVec) throws SVGException;
095        
096        abstract public Rectangle2D getBoundingBox() throws SVGException;
097    /*
098        public void loaderStartElement(SVGLoaderHelper helper, Attributes attrs, SVGElement parent)
099        {
100            super.loaderStartElement(helper, attrs, parent);
101        }
102    */
103        /**
104         * Pushes transform stack, transforms to local coordinates and sets up
105         * clipping mask.
106         */
107        protected void beginLayer(Graphics2D g) throws SVGException
108        {
109            if (xform != null)
110            {
111                cachedXform = g.getTransform();
112                g.transform(xform);
113            }
114    
115            StyleAttribute styleAttrib = new StyleAttribute();
116            
117            //Get clipping path
118    //        StyleAttribute styleAttrib = getStyle("clip-path", false);
119            Shape clipPath = null;
120            int clipPathUnits = ClipPath.CP_USER_SPACE_ON_USE;
121            if (getStyle(styleAttrib.setName("clip-path")))
122            {
123                URI uri = styleAttrib.getURIValue(getXMLBase());
124                if (uri != null)
125                {
126                    ClipPath ele = (ClipPath)diagram.getUniverse().getElement(uri);
127                    clipPath = ele.getClipPathShape();
128                    clipPathUnits = ele.getClipPathUnits();
129                }
130            }
131    
132            //Return if we're out of clipping range
133            if (clipPath != null)
134            {
135                if (clipPathUnits == ClipPath.CP_OBJECT_BOUNDING_BOX && (this instanceof ShapeElement))
136                {
137                    Rectangle2D rect = ((ShapeElement)this).getBoundingBox();
138                    AffineTransform at = new AffineTransform();
139                    at.scale(rect.getWidth(), rect.getHeight());
140                    clipPath = at.createTransformedShape(clipPath);
141                }
142    
143                cachedClip = g.getClip();
144                if (cachedClip == null)
145                {
146                    g.setClip(clipPath);
147                }
148                else
149                {
150                    Area newClip = new Area(cachedClip);
151                    newClip.intersect(new Area(clipPath));
152                    g.setClip(newClip);
153                }
154            }
155        }
156    
157        /**
158         * Restores transform and clipping values to the way they were before
159         * this layer was drawn.
160         */
161        protected void finishLayer(Graphics2D g)
162        {
163            if (cachedClip != null)
164            {
165                g.setClip(cachedClip);
166            }
167    
168            if (cachedXform != null)
169            {
170                g.setTransform(cachedXform);
171            }
172        }
173    
174    }