001/* Plot XY functions.
002
003 @Copyright (c) 1998-2014 The Regents of the University of California.
004 All rights reserved.
005
006 Permission is hereby granted, without written agreement and without
007 license or royalty fees, to use, copy, modify, and distribute this
008 software and its documentation for any purpose, provided that the
009 above copyright notice and the following two paragraphs appear in all
010 copies of this software.
011
012 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
013 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
014 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
015 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
016 SUCH DAMAGE.
017
018 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
019 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
020 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
021 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
022 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
023 ENHANCEMENTS, OR MODIFICATIONS.
024
025 PT_COPYRIGHT_VERSION 2
026 COPYRIGHTENDKEY
027 */
028package ptolemy.actor.lib.gui;
029
030import ptolemy.actor.TypedIOPort;
031import ptolemy.data.DoubleToken;
032import ptolemy.data.IntToken;
033import ptolemy.data.type.BaseType;
034import ptolemy.kernel.CompositeEntity;
035import ptolemy.kernel.util.IllegalActionException;
036import ptolemy.kernel.util.NameDuplicationException;
037import ptolemy.plot.Plot;
038
039///////////////////////////////////////////////////////////////////
040//// XYPlotter
041
042/**
043 An XY plotter.  This plotter contains an instance of the Plot class
044 from the Ptolemy plot package as a public member.
045 Data at <i>inputX</i> and <i>inputY</i> are plotted on this instance.
046 Both <i>inputX</i> and <i>inputY</i> are multiports with type DOUBLE.
047 When plotted, the first channel of <i>inputX</i> and the first channel
048 of <i>inputY</i> are together considered the first signal,
049 then the second channel of <i>inputX</i> and the second channel
050 of <i>inputY</i> are considered the second signal, and so on.
051 This requires that <i>inputX</i> and
052 <i>inputY</i> have the same width. The actor
053 assumes that there is at least one token available on each channel
054 when it fires. The horizontal axis is given by the value of the
055 input from <i>inputX</i> and vertical axis is given by <i>inputY</i>.
056
057 @author Jie Liu
058 @version $Id$
059 @since Ptolemy II 1.0
060 @Pt.ProposedRating Green (liuj)
061 @Pt.AcceptedRating Green (cxh)
062 */
063public class XYPlotter extends Plotter {
064    /** Construct an actor with the given container and name.
065     *  @param container The container.
066     *  @param name The name of this actor.
067     *  @exception IllegalActionException If the actor cannot be contained
068     *   by the proposed container.
069     *  @exception NameDuplicationException If the container already has an
070     *   actor with this name.
071     */
072    public XYPlotter(CompositeEntity container, String name)
073            throws IllegalActionException, NameDuplicationException {
074        super(container, name);
075
076        // Create the input ports and make them single ports.
077        inputX = new TypedIOPort(this, "inputX", true, false);
078        inputX.setMultiport(true);
079        inputX.setTypeEquals(BaseType.DOUBLE);
080
081        inputY = new TypedIOPort(this, "inputY", true, false);
082        inputY.setMultiport(true);
083        inputY.setTypeEquals(BaseType.DOUBLE);
084
085        _attachText("_iconDescription", "<svg>\n" + "<rect x=\"-20\" y=\"-20\" "
086                + "width=\"40\" height=\"40\" " + "style=\"fill:lightGrey\"/>\n"
087                + "<rect x=\"-12\" y=\"-12\" " + "width=\"24\" height=\"24\" "
088                + "style=\"fill:white\"/>\n" + "<rect x=\"2\" y=\"-18\" "
089                + "width=\"4\" height=\"4\" " + "style=\"fill:grey\"/>\n"
090                + "<rect x=\"8\" y=\"-18\" " + "width=\"4\" height=\"4\" "
091                + "style=\"fill:grey\"/>\n" + "<rect x=\"14\" y=\"-18\" "
092                + "width=\"4\" height=\"4\" " + "style=\"fill:grey\"/>\n"
093                + "<ellipse cx=\"-4\" cy=\"0\" " + "rx=\"4\" ry=\"8\" "
094                + "style=\"stroke:red\"/>\n" + "<ellipse cx=\"4\" cy=\"0\" "
095                + "rx=\"4\" ry=\"8\" " + "style=\"stroke:red\"/>\n"
096                + "</svg>\n");
097    }
098
099    ///////////////////////////////////////////////////////////////////
100    ////                     ports and parameters                  ////
101
102    /** Input port for the horizontal axis, with type DOUBLE. */
103    public TypedIOPort inputX;
104
105    /** Input port for the vertical axis, with type DOUBLE. */
106    public TypedIOPort inputY;
107
108    ///////////////////////////////////////////////////////////////////
109    ////                         public methods                    ////
110
111    /** Read at most one token from each channel of each input port
112     *  and plot it.
113     *  This is done in postfire to ensure that data has settled.
114     *  The width of the inputs should be the same, otherwise a
115     *  exception will be thrown. The channels from the two input
116     *  ports are matched to give the X and Y position of a single
117     *  data point.  Each matched channel pair must have
118     *  at least one token, or
119     *  a token will be consumed from the input channel that has
120     *  a token, but nothing will be plotted.
121     *  @exception IllegalActionException If there is no director,
122     *   the width of the ports are not the same, or
123     *   if the base class throws it.
124     *  @return True if it is OK to continue.
125     */
126    @Override
127    public boolean postfire() throws IllegalActionException {
128        int widthX = inputX.getWidth();
129        int widthY = inputY.getWidth();
130
131        if (widthX != widthY) {
132            throw new IllegalActionException(this,
133                    " The number of input channels mismatch.");
134        }
135
136        int offset = ((IntToken) startingDataset.getToken()).intValue();
137
138        for (int i = widthX - 1; i >= 0; i--) {
139            boolean hasX = false;
140            boolean hasY = false;
141            double xValue = 0.0;
142            double yValue = 0.0;
143
144            if (inputX.hasToken(i)) {
145                xValue = ((DoubleToken) inputX.get(i)).doubleValue();
146                hasX = true;
147            }
148
149            if (inputY.hasToken(i)) {
150                yValue = ((DoubleToken) inputY.get(i)).doubleValue();
151                hasY = true;
152            }
153
154            if (hasX && hasY) {
155                // NOTE: We assume the superclass ensures this cast is safe.
156                ((Plot) plot).addPoint(i + offset, xValue, yValue, true);
157            }
158        }
159
160        return super.postfire();
161    }
162}