001/*
002 * SVG Salamander
003 * Copyright (c) 2004, Mark McKay
004 * All rights reserved.
005 *
006 * Redistribution and use in source and binary forms, with or 
007 * without modification, are permitted provided that the following
008 * conditions are met:
009 *
010 *   - Redistributions of source code must retain the above 
011 *     copyright notice, this list of conditions and the following
012 *     disclaimer.
013 *   - Redistributions in binary form must reproduce the above
014 *     copyright notice, this list of conditions and the following
015 *     disclaimer in the documentation and/or other materials 
016 *     provided with the distribution.
017 *
018 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
019 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
020 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
021 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
022 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
023 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
025 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
026 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
027 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
028 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
029 * OF THE POSSIBILITY OF SUCH DAMAGE. 
030 * 
031 * Mark McKay can be contacted at mark@kitfox.com.  Salamander and other
032 * projects can be found at http://www.kitfox.com
033 *
034 * Created on February 18, 2004, 5:04 PM
035 */
036
037package com.kitfox.svg;
038
039import java.awt.Graphics2D;
040import java.awt.Rectangle;
041import java.awt.geom.AffineTransform;
042import java.awt.geom.Point2D;
043import java.awt.geom.Rectangle2D;
044import java.io.Serializable;
045import java.net.URI;
046import java.util.ArrayList;
047import java.util.HashMap;
048import java.util.List;
049import java.util.logging.Level;
050import java.util.logging.Logger;
051
052
053/**
054 * Top level structure in an SVG tree.
055 *
056 * @author Mark McKay
057 * @author <a href="mailto:mark@kitfox.com">Mark McKay</a>
058 */
059public class SVGDiagram implements Serializable
060{
061    public static final long serialVersionUID = 0;
062    
063    //Indexes elements within this SVG diagram
064    final HashMap idMap = new HashMap();
065
066    SVGRoot root;
067    final SVGUniverse universe;
068
069    /**
070     * This is used by the SVGRoot to determine the width of the 
071     */
072    private Rectangle deviceViewport = new Rectangle(100, 100);
073
074    /**
075     * If true, no attempt will be made to discard geometry based on it being
076     * out of bounds.  This trades potentially drawing many out of bounds
077     * shapes with having to recalculate bounding boxes every animation iteration.
078     */
079    protected boolean ignoreClipHeuristic = false;
080
081    /**
082     * URL which uniquely identifies this document
083     */
084//    final URI docRoot;
085
086    /**
087     * URI that uniquely identifies this document.  Also used to resolve
088     * relative urls.  Default base for document.
089     */
090    final URI xmlBase;
091
092    /** Creates a new instance of SVGDiagram */
093    public SVGDiagram(URI xmlBase, SVGUniverse universe)
094    {
095        this.universe = universe;
096//        this.docRoot = docRoot;
097        this.xmlBase = xmlBase;
098    }
099
100    /**
101     * Draws this diagram to the passed graphics context
102     */
103    public void render(Graphics2D g) throws SVGException
104    {
105        root.renderToViewport(g);
106    }
107    
108    /**
109     * Searches thorough the scene graph for all RenderableElements that have
110     * shapes that contain the passed point.
111     * 
112     * For every shape which contains the pick point, a List containing the
113     * path to the node is added to the return list.  That is, the result of
114     * SVGElement.getPath() is added for each entry.
115     *
116     * @return the passed in list
117     */
118    public List pick(Point2D point, List retVec) throws SVGException
119    {
120        return pick(point, false, retVec);
121    }
122    
123    public List pick(Point2D point, boolean boundingBox, List retVec) throws SVGException
124    {
125        if (retVec == null)
126        {
127            retVec = new ArrayList();
128        }
129        
130        root.pick(point, boundingBox, retVec);
131        
132        return retVec;
133    }
134
135    public List pick(Rectangle2D pickArea, List retVec) throws SVGException
136    {
137        return pick(pickArea, false, retVec);
138    }
139    
140    public List pick(Rectangle2D pickArea, boolean boundingBox, List retVec) throws SVGException
141    {
142        if (retVec == null)
143        {
144            retVec = new ArrayList();
145        }
146        
147        root.pick(pickArea, new AffineTransform(), boundingBox, retVec);
148        
149        return retVec;
150    }
151
152    public SVGUniverse getUniverse()
153    {
154        return universe;
155    }
156
157    public URI getXMLBase()
158    {
159        return xmlBase;
160    }
161
162//    public URL getDocRoot()
163//    {
164//        return docRoot;
165//    }
166
167    public float getWidth()
168    {
169        if (root == null) return 0;
170        return root.getDeviceWidth();
171    }
172    
173    public float getHeight()
174    {
175        if (root == null) return 0;
176        return root.getDeviceHeight();
177    }
178    
179    /**
180     * Returns the viewing rectangle of this diagram in device coordinates.
181     */
182    public Rectangle2D getViewRect(Rectangle2D rect)
183    {
184        if (root != null) return root.getDeviceRect(rect);
185        return rect;
186    }
187
188    public Rectangle2D getViewRect()
189    {
190        return getViewRect(new Rectangle2D.Double());
191    }
192
193    public SVGElement getElement(String name)
194    {
195        return (SVGElement)idMap.get(name);
196    }
197
198    public void setElement(String name, SVGElement node)
199    {
200        idMap.put(name, node);
201    }
202
203    public void removeElement(String name)
204    {
205        idMap.remove(name);
206    }
207
208    public SVGRoot getRoot()
209    {
210        return root;
211    }
212
213    public void setRoot(SVGRoot root)
214    {
215        this.root = root;
216        root.setDiagram(this);
217    }
218
219    public boolean ignoringClipHeuristic() { return ignoreClipHeuristic; }
220
221    public void setIgnoringClipHeuristic(boolean ignoreClipHeuristic) { this.ignoreClipHeuristic = ignoreClipHeuristic; }
222
223    /**
224     * Updates all attributes in this diagram associated with a time event.
225     * Ie, all attributes with track information.
226     */
227    public void updateTime(double curTime) throws SVGException
228    {
229        if (root == null) return;
230        root.updateTime(curTime);
231    }
232
233    public Rectangle getDeviceViewport()
234    {
235        return deviceViewport;
236    }
237
238    /**
239     * Sets the dimensions of the device being rendered into.  This is used by
240     * SVGRoot when its x, y, width or height parameters are specified as
241     * percentages.
242     */
243    public void setDeviceViewport(Rectangle deviceViewport)
244    {
245        this.deviceViewport.setBounds(deviceViewport);
246        if (root != null)
247        {
248            try
249            {
250                root.build();
251            } catch (SVGException ex)
252            {
253                Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING, 
254                    "Could not build document", ex);
255            }
256        }
257    }
258}