001    /*
002     * SVGDiagram.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 February 18, 2004, 5:04 PM
026     */
027    
028    package com.kitfox.svg;
029    
030    import java.awt.Graphics2D;
031    import java.awt.Rectangle;
032    import java.awt.geom.AffineTransform;
033    import java.awt.geom.Point2D;
034    import java.awt.geom.Rectangle2D;
035    import java.io.Serializable;
036    import java.net.URI;
037    import java.util.ArrayList;
038    import java.util.HashMap;
039    import java.util.List;
040    
041    
042    /**
043     * Top level structure in an SVG tree.
044     *
045     * @author Mark McKay
046     * @author <a href="mailto:mark@kitfox.com">Mark McKay</a>
047     */
048    public class SVGDiagram implements Serializable
049    {
050        public static final long serialVersionUID = 0;
051        
052        //Indexes elements within this SVG diagram
053        final HashMap idMap = new HashMap();
054    
055        SVGRoot root;
056        final SVGUniverse universe;
057    
058        /**
059         * This is used by the SVGRoot to determine the width of the 
060         */
061        private Rectangle deviceViewport = new Rectangle(100, 100);
062    
063        /**
064         * If true, no attempt will be made to discard geometry based on it being
065         * out of bounds.  This trades potentially drawing many out of bounds
066         * shapes with having to recalculate bounding boxes every animation iteration.
067         */
068        protected boolean ignoreClipHeuristic = false;
069    
070        /**
071         * URL which uniquely identifies this document
072         */
073    //    final URI docRoot;
074    
075        /**
076         * URI that uniquely identifies this document.  Also used to resolve
077         * relative urls.  Default base for document.
078         */
079        final URI xmlBase;
080    
081        /** Creates a new instance of SVGDiagram */
082        public SVGDiagram(URI xmlBase, SVGUniverse universe)
083        {
084            this.universe = universe;
085    //        this.docRoot = docRoot;
086            this.xmlBase = xmlBase;
087        }
088    
089        /**
090         * Draws this diagram to the passed graphics context
091         */
092        public void render(Graphics2D g) throws SVGException
093        {
094            root.render(g);
095        }
096        
097        /**
098         * Searches thorough the scene graph for all RenderableElements that have
099         * shapes that contain the passed point.
100         * 
101         * For every shape which contains the pick point, a List containing the
102         * path to the node is added to the return list.  That is, the result of
103         * SVGElement.getPath() is added for each entry.
104         *
105         * @return the passed in list
106         */
107        public List pick(Point2D point, List retVec) throws SVGException
108        {
109            return pick(point, false, retVec);
110        }
111        
112        public List pick(Point2D point, boolean boundingBox, List retVec) throws SVGException
113        {
114            if (retVec == null)
115            {
116                retVec = new ArrayList();
117            }
118            
119            root.pick(point, boundingBox, retVec);
120            
121            return retVec;
122        }
123    
124        public List pick(Rectangle2D pickArea, List retVec) throws SVGException
125        {
126            return pick(pickArea, false, retVec);
127        }
128        
129        public List pick(Rectangle2D pickArea, boolean boundingBox, List retVec) throws SVGException
130        {
131            if (retVec == null)
132            {
133                retVec = new ArrayList();
134            }
135            
136            root.pick(pickArea, new AffineTransform(), boundingBox, retVec);
137            
138            return retVec;
139        }
140    
141        public SVGUniverse getUniverse()
142        {
143            return universe;
144        }
145    
146        public URI getXMLBase()
147        {
148            return xmlBase;
149        }
150    
151    //    public URL getDocRoot()
152    //    {
153    //        return docRoot;
154    //    }
155    
156        public float getWidth()
157        {
158            if (root == null) return 0;
159            return root.getDeviceWidth();
160        }
161        
162        public float getHeight()
163        {
164            if (root == null) return 0;
165            return root.getDeviceHeight();
166        }
167        
168        /**
169         * Returns the viewing rectangle of this diagram in device coordinates.
170         */
171        public Rectangle2D getViewRect(Rectangle2D rect)
172        {
173            if (root != null) return root.getDeviceRect(rect);
174            return rect;
175        }
176    
177        public Rectangle2D getViewRect()
178        {
179            return getViewRect(new Rectangle2D.Double());
180        }
181    
182        public SVGElement getElement(String name)
183        {
184            return (SVGElement)idMap.get(name);
185        }
186    
187        public void setElement(String name, SVGElement node)
188        {
189            idMap.put(name, node);
190        }
191    
192        public void removeElement(String name)
193        {
194            idMap.remove(name);
195        }
196    
197        public SVGRoot getRoot()
198        {
199            return root;
200        }
201    
202        public void setRoot(SVGRoot root)
203        {
204            this.root = root;
205            root.setDiagram(this);
206        }
207    
208        public boolean ignoringClipHeuristic() { return ignoreClipHeuristic; }
209    
210        public void setIgnoringClipHeuristic(boolean ignoreClipHeuristic) { this.ignoreClipHeuristic = ignoreClipHeuristic; }
211    
212        /**
213         * Updates all attributes in this diagram associated with a time event.
214         * Ie, all attributes with track information.
215         */
216        public void updateTime(double curTime) throws SVGException
217        {
218            if (root == null) return;
219            root.updateTime(curTime);
220        }
221    
222        public Rectangle getDeviceViewport()
223        {
224            return deviceViewport;
225        }
226    
227        /**
228         * Sets the dimensions of the device being rendered into.  This is used by
229         * SVGRoot when its x, y, width or height parameters are specified as
230         * percentages.
231         */
232        public void setDeviceViewport(Rectangle deviceViewport)
233        {
234            this.deviceViewport.setBounds(deviceViewport);
235            if (root != null)
236            {
237                try
238                {
239                    root.build();
240                } catch (SVGException ex)
241                {
242                    ex.printStackTrace();
243                }
244            }
245        }
246    }