001/*
002 * Copyright (c) 2002-2010 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: crawl $'
006 * '$Date: 2015-08-24 22:48:48 +0000 (Mon, 24 Aug 2015) $' 
007 * '$Revision: 33634 $'
008 * 
009 * Permission is hereby granted, without written agreement and without
010 * license or royalty fees, to use, copy, modify, and distribute this
011 * software and its documentation for any purpose, provided that the above
012 * copyright notice and the following two paragraphs appear in all copies
013 * of this software.
014 *
015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
019 * SUCH DAMAGE.
020 *
021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
026 * ENHANCEMENTS, OR MODIFICATIONS.
027 *
028 */
029
030package org.geon;
031
032import java.io.File;
033import java.io.FileInputStream;
034import java.io.InputStream;
035import java.util.Set;
036import java.util.Vector;
037
038import javax.xml.parsers.DocumentBuilder;
039import javax.xml.parsers.DocumentBuilderFactory;
040
041import org.w3c.dom.Document;
042import org.w3c.dom.Element;
043import org.w3c.dom.NodeList;
044
045import nl.skybound.awt.DoublePolygon;
046import ptolemy.actor.lib.Source;
047import ptolemy.data.DoubleToken;
048import ptolemy.data.ObjectToken;
049import ptolemy.data.StringToken;
050import ptolemy.data.Token;
051import ptolemy.data.expr.ASTPtRootNode;
052import ptolemy.data.expr.FileParameter;
053import ptolemy.data.expr.ModelScope;
054import ptolemy.data.expr.ParseTreeEvaluator;
055import ptolemy.data.expr.PtParser;
056import ptolemy.data.type.BaseType;
057import ptolemy.data.type.Type;
058import ptolemy.data.type.TypeConstant;
059import ptolemy.kernel.CompositeEntity;
060import ptolemy.kernel.util.IllegalActionException;
061import ptolemy.kernel.util.NameDuplicationException;
062
063//////////////////////////////////////////////////////////////////////////
064//// SVGToPolygon
065/**
066 * This actor converts an SVG file into polygon objects. The polygon coordinates
067 * are read using the polygon tag.
068 * 
069 * @author Efrat Jaeger
070 * @version $Id: SVGToPolygon.java 33634 2015-08-24 22:48:48Z crawl $
071 * @since Ptolemy II 3.0.2
072 */
073public class SVGToPolygon extends Source {
074
075        /**
076         * Construct an actor with the given container and name.
077         * 
078         * @param container
079         *            The container.
080         * @param name
081         *            The name of this actor.
082         * @exception IllegalActionException
083         *                If the actor cannot be contained by the proposed
084         *                container.
085         * @exception NameDuplicationException
086         *                If the container already has an actor with this name.
087         */
088        public SVGToPolygon(CompositeEntity container, String name)
089                        throws IllegalActionException, NameDuplicationException {
090                super(container, name);
091
092                fileOrURL = new FileParameter(this, "fileOrURL");
093
094                output.setTypeEquals(BaseType.GENERAL);
095                trigger.setTypeEquals(BaseType.STRING);
096
097                _attachText("_iconDescription", "<svg>\n"
098                                + "<rect x=\"-25\" y=\"-20\" " + "width=\"50\" height=\"40\" "
099                                + "style=\"fill:white\"/>\n"
100                                + "<polygon points=\"-15,-10 -12,-10 -8,-14 -1,-14 3,-10"
101                                + " 15,-10 15,10, -15,10\" " + "style=\"fill:red\"/>\n"
102                                + "</svg>\n");
103        }
104
105        // /////////////////////////////////////////////////////////////////
106        // // ports and parameters ////
107
108        /**
109         * The file name or URL from which to read. This is a string with any form
110         * accepted by FileParameter.
111         * 
112         * @see FileParameter
113         */
114        public FileParameter fileOrURL;
115
116        /**
117         * Output the SVG file into a Polygons and their region names.
118         * 
119         * @exception IllegalActionException
120         *                If there's no director.
121         */
122        public void fire() throws IllegalActionException {
123                if (trigger.getWidth() > 0) {
124                        String fileName = ((StringToken) trigger.get(0)).stringValue();
125                        int lineEndInd = fileName.indexOf("\n");
126                        if (lineEndInd != -1) { // The string contains a CR.
127                                fileName = fileName.substring(0, lineEndInd);
128                        }
129                        fileOrURL.setExpression(fileName.trim());
130                }
131                input = fileOrURL.asFile();
132
133                GeonXMLUtil parseXML = new GeonXMLUtil();
134
135                Vector polyPoints = parseXML.getAttrValue(input, "polygon", "points");
136                Vector regionsStr = parseXML.getElementsById(input, "polygon",
137                                "onmouseover");
138
139                // get conversion factors.
140                xRatio = "";
141                yRatio = ""; // reset factors first.
142                _getConversionFactors(input);
143
144                // calculate parse tree for ratios.
145                PtParser parser = new PtParser();
146                _parseTreeEvaluator = new ParseTreeEvaluator();
147                _scope = new VariableScope();
148
149                ASTPtRootNode _parseTreeX = null, _parseTreeY = null;
150
151                if (!xRatio.equals(""))
152                        _parseTreeX = parser.generateParseTree(xRatio);
153
154                if (!yRatio.equals(""))
155                        _parseTreeY = parser.generateParseTree(yRatio);
156
157                PolygonUtil polygonRegions[] = new PolygonUtil[polyPoints.size()];
158                int beginInd, endInd;
159                for (int i = 0; i < polyPoints.size(); i++) {
160
161                        // extract the polygon points.
162                        String points = ((String) polyPoints.get(i));
163                        int iter = 0, ind = -1, comma = -1;
164                        polygonRegions[i] = new PolygonUtil();
165                        polygonRegions[i].Poly = new DoublePolygon();
166                        while (iter < points.length()) {
167                                ind = points.indexOf(' ', iter);
168                                String point = points.substring(iter, ind);
169                                iter = ind + 1;
170                                comma = point.indexOf(',');
171
172                                // processing the point by the conversion ratios.
173                                double Px = Double.parseDouble(point.substring(0, comma));
174                                if (!xRatio.equals("")) {
175                                        param = Px;
176                                        Px = _ratioConvert(_parseTreeX);
177                                }
178                                double Py = Double.parseDouble(point.substring(comma + 1));
179                                if (!yRatio.equals("")) {
180                                        param = Py;
181                                        Py = _ratioConvert(_parseTreeY);
182                                }
183                                // System.out.println(Px+","+Py);
184                                polygonRegions[i].Poly.addPoint(Px, Py);
185                                if (ind == points.lastIndexOf(' ')) {
186                                        point = points.substring(ind + 1);
187                                        comma = point.indexOf(',');
188                                        Px = Double.parseDouble(point.substring(0, comma));
189                                        if (!xRatio.equals("")) {
190                                                param = Px;
191                                                Px = _ratioConvert(_parseTreeX);
192                                        }
193                                        Py = Double.parseDouble(point.substring(comma + 1));
194                                        if (!yRatio.equals("")) {
195                                                param = Py;
196                                                Py = _ratioConvert(_parseTreeY);
197                                        }
198                                        // System.out.println(Px+","+Py);
199                                        polygonRegions[i].Poly.addPoint(Px, Py);
200                                        iter = points.length();
201                                }
202                        }
203
204                        // extract the region.
205                        String reg = ((String) regionsStr.get(i));
206                        /*
207                         * beginInd = reg.indexOf('('); endInd = reg.indexOf(')'); reg =
208                         * reg.substring(beginInd + 2, endInd - 1);
209                         */
210                        polygonRegions[i].Region = reg;
211                }
212                output.broadcast(new ObjectToken(polygonRegions));
213        }
214
215        /**
216         * Post fire the actor. Return false to indicate that the process has
217         * finished. If it returns true, the process will continue indefinitely.
218         */
219        public boolean postfire() {
220                return true;
221        }
222
223        // /////////////////////////////////////////////////////////////////
224        // // private methods ////
225
226        private void _getConversionFactors(File input)
227                        throws IllegalActionException {
228                try {
229                        DocumentBuilderFactory factory = DocumentBuilderFactory
230                                        .newInstance();
231                        factory.setValidating(false);
232                        DocumentBuilder builder = factory.newDocumentBuilder();
233                        InputStream is = new FileInputStream(input);
234                        Document doc = builder.parse(is);
235
236                        NodeList nodes = doc.getElementsByTagName("conversion");
237                        for (int i = 0; i < nodes.getLength(); i++) {
238                                String _id = ((Element) nodes.item(i)).getAttribute("id");
239                                String value = nodes.item(i).getFirstChild().getNodeValue();
240                                // String value = ( (Element) nodes.item(i)).getAttribute(_id);
241                                if (_id.equals("toOriginalX"))
242                                        xRatio = value;
243                                else if (_id.equals("toOriginalY"))
244                                        yRatio = value;
245                        }
246                } catch (Exception ex) {
247                        throw new IllegalActionException(this, "Error parsing SVG file "
248                                        + input);
249                }
250        }
251
252        private double _ratioConvert(ASTPtRootNode _parseTree)
253                        throws IllegalActionException {
254                DoubleToken ratioToken = (DoubleToken) _parseTreeEvaluator
255                                .evaluateParseTree(_parseTree, _scope);
256                if (ratioToken == null) {
257                        throw new IllegalActionException(this,
258                                        "Expression yields a null result.");
259                }
260                return ratioToken.doubleValue();
261        }
262
263        private class VariableScope extends ModelScope {
264
265                /**
266                 * Look up and return the attribute with the specified name in the
267                 * scope. Return null if such an attribute does not exist.
268                 * 
269                 * @return The attribute with the specified name in the scope.
270                 */
271                public Token get(String name) throws IllegalActionException {
272                        if (name.equals("val")) {
273                                return new DoubleToken(param);
274                        } else
275                                return null;
276                }
277
278                /**
279                 * Look up and return the type of the attribute with the specified name
280                 * in the scope. Return null if such an attribute does not exist.
281                 * 
282                 * @return The attribute with the specified name in the scope.
283                 */
284                public Type getType(String name) throws IllegalActionException {
285                        if (name.equals("val")) {
286                                return BaseType.DOUBLE;
287                        } else
288                                return null;
289                }
290
291                /**
292                 * Look up and return the type term for the specified name in the scope.
293                 * Return null if the name is not defined in this scope, or is a
294                 * constant type.
295                 * 
296                 * @return The InequalityTerm associated with the given name in the
297                 *         scope.
298                 * @exception IllegalActionException
299                 *                If a value in the scope exists with the given name,
300                 *                but cannot be evaluated.
301                 */
302                public ptolemy.graph.InequalityTerm getTypeTerm(String name)
303                                throws IllegalActionException {
304                        if (name.equals("val")) {
305                                return new TypeConstant(BaseType.DOUBLE);
306                        } else
307                                return null;
308                }
309
310                /**
311                 * Return the list of identifiers within the scope.
312                 * 
313                 * @return The list of identifiers within the scope.
314                 */
315                public Set identifierSet() {
316                        return getAllScopedVariableNames(null, SVGToPolygon.this);
317                }
318        }
319
320        // /////////////////////////////////////////////////////////////////
321        // // private members ////
322
323        /** Previous value of fileOrURL parameter. */
324        private String _previousFileOrURL;
325        private File input;
326        private String xRatio = "", yRatio = "";
327        private double param;
328        // private ASTPtRootNode _parseTree = null;
329        private ParseTreeEvaluator _parseTreeEvaluator = null;
330        private VariableScope _scope = null;
331
332}