001/* Plot arrays of doubles in an XY plot.
002
003 @Copyright (c) 2007-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.actor.lib.SequenceActor;
032import ptolemy.data.ArrayToken;
033import ptolemy.data.BooleanToken;
034import ptolemy.data.DoubleToken;
035import ptolemy.data.IntToken;
036import ptolemy.data.Token;
037import ptolemy.data.expr.Parameter;
038import ptolemy.data.type.ArrayType;
039import ptolemy.data.type.BaseType;
040import ptolemy.kernel.CompositeEntity;
041import ptolemy.kernel.util.IllegalActionException;
042import ptolemy.kernel.util.NameDuplicationException;
043import ptolemy.plot.Plot;
044
045///////////////////////////////////////////////////////////////////
046//// ArrayPlotterXY
047
048/**
049 <p>A plotter that plots a sequence of pairs of arrays of doubles
050 as an XY plot.  This plotter contains an instance of the Plot
051 class from the Ptolemy plot package as a public member. Data at
052 the inputs, which can have any number of channels, are plotted
053 on this instance.  Each pair of input channels is plotted as a separate data set.
054 Each input token is an array of doubles.</p>
055 <p>
056 The <i>iterationsPerUpdate</i> parameter can be used to fine tune
057 the display.  It can be quite expensive to generate the display, and
058 by default, this actor generates it on every firing.  If
059 <i>iterationsPerUpdate</i> is set to some integer greater than
060 one, then it specifies how many iterations should be executed
061 between updates. Thus, if <i>iterationsPerUpdate</i> = 2, then every
062 second time this actor fires, it will update the display. That is,
063 it will update its display on the first firing, the third, the
064 fifth, etc. It will, however, consume its inputs on every firing.
065 The plot is always updated in the wrapup() method.</p>
066 <p>
067 Note that this can be used to generate live plots, like XYScope,
068 but it has fewer drawing artifacts than XYScope since it does
069 not use XOR drawing mode.</p>
070
071 @author  Edward A. Lee
072 @version $Id$
073 @since Ptolemy II 6.1
074 @Pt.ProposedRating Yellow (eal)
075 @Pt.AcceptedRating Red (cxh)
076 @see XYScope
077 */
078public class ArrayPlotterXY extends Plotter implements SequenceActor {
079    /** Construct an actor with the given container and name.
080     *  @param container The container.
081     *  @param name The name of this actor.
082     *  @exception IllegalActionException If the actor cannot be contained
083     *   by the proposed container.
084     *  @exception NameDuplicationException If the container already has an
085     *   actor with this name.
086     */
087    public ArrayPlotterXY(CompositeEntity container, String name)
088            throws IllegalActionException, NameDuplicationException {
089        super(container, name);
090
091        // Create the input port and make it a multiport.
092        x = new TypedIOPort(this, "x", true, false);
093        x.setMultiport(true);
094        x.setTypeEquals(BaseType.DOUBLE);
095        x.setTypeEquals(new ArrayType(BaseType.DOUBLE));
096        new Parameter(x, "_showName", BooleanToken.TRUE);
097
098        y = new TypedIOPort(this, "y", true, false);
099        y.setMultiport(true);
100        y.setTypeEquals(BaseType.DOUBLE);
101        y.setTypeEquals(new ArrayType(BaseType.DOUBLE));
102        new Parameter(y, "_showName", BooleanToken.TRUE);
103
104        iterationsPerUpdate = new Parameter(this, "iterationsPerUpdate");
105        iterationsPerUpdate.setExpression("1");
106    }
107
108    ///////////////////////////////////////////////////////////////////
109    ////                     ports and parameters                  ////
110
111    /** The number of iterations between updates of the display
112     *  on the screen.
113     *  This parameter has type IntToken, with default value 1.
114     *  Its value must be non-negative.
115     */
116    public Parameter iterationsPerUpdate;
117
118    /** Input port for the horizontal axis, which receives an array of doubles. */
119    public TypedIOPort x;
120
121    /** Input port for the vertical axis, which receives an array of doubles. */
122    public TypedIOPort y;
123
124    ///////////////////////////////////////////////////////////////////
125    ////                         public methods                    ////
126
127    /** If the plot has not already been created, create it.
128     *  If configurations specified by a call to configure() have not yet
129     *  been processed, process them.  This overrides the base class to
130     *  also start counting iterations, so that the
131     *  <i>iterationsPerUpdate</i> parameter works.
132     *  @exception IllegalActionException If the parent class throws it.
133     */
134    @Override
135    public void initialize() throws IllegalActionException {
136        super.initialize();
137        _iteration = 0;
138        _xtokens = null;
139        _ytokens = null;
140    }
141
142    /** Read at most one token from each input channel on <i>x</i> and
143     *  <i>y</i> inputs, and if there is a token on both, plot the data
144     *  as an XY plot. The input data are plotted in postfire() to
145     *  ensure that the data have settled.
146     *  @exception IllegalActionException If there is no director,
147     *   or if the base class throws it.
148     *  @return True if it is OK to continue.
149     */
150    @Override
151    public boolean postfire() throws IllegalActionException {
152        int xwidth = x.getWidth();
153        int ywidth = y.getWidth();
154        _offset = ((IntToken) startingDataset.getToken()).intValue();
155
156        int jointWidth = xwidth;
157        if (jointWidth > ywidth) {
158            jointWidth = ywidth;
159        }
160        if (_xtokens == null || _xtokens.length != jointWidth) {
161            _xtokens = new ArrayToken[jointWidth];
162        }
163        if (_ytokens == null || _ytokens.length != jointWidth) {
164            _ytokens = new ArrayToken[jointWidth];
165        }
166
167        for (int i = xwidth - 1; i >= 0; i--) {
168            if (x.hasToken(i)) {
169                _xtokens[i] = (ArrayToken) x.get(i);
170                if (ywidth > i && y.hasToken(i)) {
171                    _ytokens[i] = (ArrayToken) y.get(i);
172                    if (_iteration == 0) {
173                        Token[] xArray = _xtokens[i].arrayValue();
174                        Token[] yArray = _ytokens[i].arrayValue();
175
176                        // NOTE: We assume the superclass ensures this cast is safe.
177                        ((Plot) plot).clear(i + _offset);
178
179                        for (int j = 0; j < xArray.length; j++) {
180                            double xValue = ((DoubleToken) xArray[j])
181                                    .doubleValue();
182                            double yValue = ((DoubleToken) yArray[j])
183                                    .doubleValue();
184                            ((Plot) plot).addPoint(i + _offset, xValue, yValue,
185                                    true);
186                        }
187                    }
188                }
189            }
190        }
191        // If y is wider than x, read its inputs and discard them.
192        for (int j = ywidth - 1; j >= xwidth; j--) {
193            y.get(j);
194        }
195
196        _iteration++;
197
198        if (_iteration == ((IntToken) iterationsPerUpdate.getToken())
199                .intValue()) {
200            _iteration = 0;
201        }
202        return super.postfire();
203    }
204
205    /** Update the plot with the most recently read data.
206     *  If the <i>fillOnWrapup</i> parameter is true, rescale the
207     *  plot so that all the data is visible.
208     *  @exception IllegalActionException If the superclass throws it.
209     */
210    @Override
211    public void wrapup() throws IllegalActionException {
212        if (_xtokens != null) {
213            for (int i = _xtokens.length - 1; i >= 0; i--) {
214                if (_xtokens[i] != null && _ytokens[i] != null) {
215                    Token[] xArray = _xtokens[i].arrayValue();
216                    Token[] yArray = _ytokens[i].arrayValue();
217
218                    // NOTE: We assume the superclass ensures this cast is safe.
219                    ((Plot) plot).clear(i + _offset);
220
221                    for (int j = 0; j < xArray.length; j++) {
222                        double xValue = ((DoubleToken) xArray[j]).doubleValue();
223                        double yValue = ((DoubleToken) yArray[j]).doubleValue();
224                        ((Plot) plot).addPoint(i + _offset, xValue, yValue,
225                                true);
226                    }
227                }
228            }
229        }
230        super.wrapup();
231    }
232
233    ///////////////////////////////////////////////////////////////////
234    ////                         private variables                 ////
235    // Iteration count, modulo the iterationsPerUpdate.
236    private int _iteration = 0;
237
238    // The value of the startingDataset parameter.
239    private int _offset;
240
241    // The most recently read tokens from the x input in the fire() method.
242    private ArrayToken[] _xtokens;
243
244    // The most recently read tokens from the y input in the fire() method.
245    private ArrayToken[] _ytokens;
246}