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<String, SVGElement> idMap = new HashMap<String, SVGElement>();
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    /**
093     * Creates a new instance of SVGDiagram
094     * @param xmlBase
095     * @param universe
096     */
097    public SVGDiagram(URI xmlBase, SVGUniverse universe)
098    {
099        this.universe = universe;
100//        this.docRoot = docRoot;
101        this.xmlBase = xmlBase;
102    }
103
104    /**
105     * Draws this diagram to the passed graphics context
106     * @param g
107     * @throws com.kitfox.svg.SVGException
108     */
109    public void render(Graphics2D g) throws SVGException
110    {
111        root.renderToViewport(g);
112    }
113    
114    /**
115     * Searches thorough the scene graph for all RenderableElements that have
116     * shapes that contain the passed point.
117     * 
118     * For every shape which contains the pick point, a List containing the
119     * path to the node is added to the return list.  That is, the result of
120     * SVGElement.getPath() is added for each entry.
121     *
122     * @param point
123     * @param retVec
124     * @return the passed in list
125     * @throws com.kitfox.svg.SVGException
126     */
127    public List<List<SVGElement>> pick(Point2D point, List<List<SVGElement>> retVec) throws SVGException
128    {
129        return pick(point, false, retVec);
130    }
131    
132    public List<List<SVGElement>> pick(Point2D point, boolean boundingBox, List<List<SVGElement>> retVec) throws SVGException
133    {
134        if (retVec == null)
135        {
136            retVec = new ArrayList<List<SVGElement>>();
137        }
138        
139        root.pick(point, boundingBox, retVec);
140        
141        return retVec;
142    }
143
144    public List<List<SVGElement>> pick(Rectangle2D pickArea, List<List<SVGElement>> retVec) throws SVGException
145    {
146        return pick(pickArea, false, retVec);
147    }
148    
149    public List<List<SVGElement>> pick(Rectangle2D pickArea, boolean boundingBox, List<List<SVGElement>> retVec) throws SVGException
150    {
151        if (retVec == null)
152        {
153            retVec = new ArrayList<List<SVGElement>>();
154        }
155        
156        root.pick(pickArea, new AffineTransform(), boundingBox, retVec);
157        
158        return retVec;
159    }
160
161    public SVGUniverse getUniverse()
162    {
163        return universe;
164    }
165
166    public URI getXMLBase()
167    {
168        return xmlBase;
169    }
170
171//    public URL getDocRoot()
172//    {
173//        return docRoot;
174//    }
175
176    public float getWidth()
177    {
178        if (root == null) return 0;
179        return root.getDeviceWidth();
180    }
181    
182    public float getHeight()
183    {
184        if (root == null) return 0;
185        return root.getDeviceHeight();
186    }
187    
188    /**
189     * Returns the viewing rectangle of this diagram in device coordinates.
190     * @param rect
191     * @return 
192     */
193    public Rectangle2D getViewRect(Rectangle2D rect)
194    {
195        if (root != null) return root.getDeviceRect(rect);
196        return rect;
197    }
198
199    public Rectangle2D getViewRect()
200    {
201        return getViewRect(new Rectangle2D.Double());
202    }
203
204    public SVGElement getElement(String name)
205    {
206        return (SVGElement)idMap.get(name);
207    }
208
209    public void setElement(String name, SVGElement node)
210    {
211        idMap.put(name, node);
212    }
213
214    public void removeElement(String name)
215    {
216        idMap.remove(name);
217    }
218
219    public SVGRoot getRoot()
220    {
221        return root;
222    }
223
224    public void setRoot(SVGRoot root)
225    {
226        this.root = root;
227        root.setDiagram(this);
228    }
229
230    public boolean ignoringClipHeuristic() { return ignoreClipHeuristic; }
231
232    public void setIgnoringClipHeuristic(boolean ignoreClipHeuristic) { this.ignoreClipHeuristic = ignoreClipHeuristic; }
233
234    /**
235     * Updates all attributes in this diagram associated with a time event.
236     * Ie, all attributes with track information.
237     * @param curTime
238     * @throws com.kitfox.svg.SVGException
239     */
240    public void updateTime(double curTime) throws SVGException
241    {
242        if (root == null) return;
243        root.updateTime(curTime);
244    }
245
246    public Rectangle getDeviceViewport()
247    {
248        return deviceViewport;
249    }
250
251    /**
252     * Sets the dimensions of the device being rendered into.  This is used by
253     * SVGRoot when its x, y, width or height parameters are specified as
254     * percentages.
255     * @param deviceViewport
256     */
257    public void setDeviceViewport(Rectangle deviceViewport)
258    {
259        this.deviceViewport.setBounds(deviceViewport);
260        if (root != null)
261        {
262            try
263            {
264                root.build();
265            } catch (SVGException ex)
266            {
267                Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING, 
268                    "Could not build document", ex);
269            }
270        }
271    }
272}