001/* Plot arrays of doubles.
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.actor.lib.SequenceActor;
032import ptolemy.data.ArrayToken;
033import ptolemy.data.DoubleToken;
034import ptolemy.data.IntToken;
035import ptolemy.data.Token;
036import ptolemy.data.expr.Parameter;
037import ptolemy.data.type.ArrayType;
038import ptolemy.data.type.BaseType;
039import ptolemy.kernel.CompositeEntity;
040import ptolemy.kernel.util.Attribute;
041import ptolemy.kernel.util.IllegalActionException;
042import ptolemy.kernel.util.NameDuplicationException;
043import ptolemy.plot.PlotInterface;
044
045///////////////////////////////////////////////////////////////////
046//// ArrayPlotter
047
048/**
049 <p>A plotter that plots a sequence of arrays of doubles.
050 This plotter contains an instance of the Plot
051 class from the Ptolemy plot package as a public member. Data at
052 the input, which can consist of any number of channels, are plotted
053 on this instance.  Each input channel 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 SequenceScope,
068 but it has fewer drawing artifacts than SequenceScope since it does
069 not use XOR drawing mode.</p>
070
071 @author  Edward A. Lee
072 @version $Id$
073 @since Ptolemy II 3.0
074 @Pt.ProposedRating Yellow (eal)
075 @Pt.AcceptedRating Red (cxh)
076 @see SequenceScope
077 */
078public class ArrayPlotter 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 ArrayPlotter(CompositeEntity container, String name)
088            throws IllegalActionException, NameDuplicationException {
089        super(container, name);
090
091        // Create the input port and make it a multiport.
092        input = new TypedIOPort(this, "input", true, false);
093        input.setMultiport(true);
094        input.setTypeEquals(BaseType.DOUBLE);
095        input.setTypeEquals(new ArrayType(BaseType.DOUBLE));
096
097        iterationsPerUpdate = new Parameter(this, "iterationsPerUpdate");
098        iterationsPerUpdate.setExpression("1");
099
100        // set the parameters
101        xInit = new Parameter(this, "xInit", new DoubleToken(0.0));
102        xInit.setTypeEquals(BaseType.DOUBLE);
103        xUnit = new Parameter(this, "xUnit", new DoubleToken(1.0));
104        xUnit.setTypeEquals(BaseType.DOUBLE);
105
106        // initialize the parameters
107        attributeChanged(xInit);
108        attributeChanged(xUnit);
109    }
110
111    ///////////////////////////////////////////////////////////////////
112    ////                     ports and parameters                  ////
113
114    /** Input port, which receives an array of doubles. */
115    public TypedIOPort input;
116
117    /** The number of iterations between updates of the display
118     *  on the screen.
119     *  This parameter has type IntToken, with default value 1.
120     *  Its value must be non-negative.
121     */
122    public Parameter iterationsPerUpdate;
123
124    /** The increment of the X axis. */
125    public Parameter xUnit;
126
127    /** The start point of the X axis. */
128    public Parameter xInit;
129
130    ///////////////////////////////////////////////////////////////////
131    ////                         public methods                    ////
132
133    /** Notification that an attribute has changed.
134     *  @param attribute The attribute that changed.
135     *  @exception IllegalActionException If the expression of the
136     *   attribute cannot be parsed or cannot be evaluated.
137     */
138    @Override
139    public void attributeChanged(Attribute attribute)
140            throws IllegalActionException {
141        if (attribute == xInit) {
142            _xInit = ((DoubleToken) xInit.getToken()).doubleValue();
143        } else {
144            if (attribute == xUnit) {
145                _xUnit = ((DoubleToken) xUnit.getToken()).doubleValue();
146            } else {
147                super.attributeChanged(attribute);
148            }
149        }
150    }
151
152    /** If the plot has not already been created, create it.
153     *  If configurations specified by a call to configure() have not yet
154     *  been processed, process them.  This overrides the base class to
155     *  also start counting iterations, so that the
156     *  <i>iterationsPerUpdate</i> parameter works.
157     *  @exception IllegalActionException If the parent class throws it.
158     */
159    @Override
160    public void initialize() throws IllegalActionException {
161        super.initialize();
162        _iteration = 0;
163        // If the model is run, changed and run again but this actor is
164        // not fired, then be sure not to plot the old data
165        _tokens = null;
166    }
167
168    /** Read at most one token from each input channel and plot it as
169     *  a function of the iteration number, scaled by <i>xUnit</i>.
170     *  The first point is plotted at the horizontal position given by
171     *  <i>xInit</i>. The increments on the position are given by
172     *  <i>xUnit</i>. The input data are plotted in postfire() to
173     *  ensure that the data have settled.
174     *  @exception IllegalActionException If there is no director,
175     *   or if the base class throws it.
176     *  @return True if it is OK to continue.
177     */
178    @Override
179    public boolean postfire() throws IllegalActionException {
180        int width = input.getWidth();
181        _offset = ((IntToken) startingDataset.getToken()).intValue();
182
183        if (_tokens == null || _tokens.length != width) {
184            _tokens = new ArrayToken[width];
185        }
186
187        for (int i = width - 1; i >= 0; i--) {
188            double xValue = _xInit;
189
190            if (input.hasToken(i)) {
191                _tokens[i] = (ArrayToken) input.get(i);
192
193                if (_iteration == 0) {
194                    Token[] currentArray = _tokens[i].arrayValue();
195
196                    // NOTE: We assume the superclass ensures this cast is safe.
197                    ((PlotInterface) plot).clear(i + _offset);
198
199                    for (Token element : currentArray) {
200                        double currentValue = ((DoubleToken) element)
201                                .doubleValue();
202                        ((PlotInterface) plot).addPoint(i + _offset, xValue,
203                                currentValue, true);
204                        xValue += _xUnit;
205                    }
206                }
207            }
208        }
209
210        _iteration++;
211
212        if (_iteration == ((IntToken) iterationsPerUpdate.getToken())
213                .intValue()) {
214            _iteration = 0;
215        }
216
217        return super.postfire();
218    }
219
220    /** Update the plot with the most recently read data.
221     *  If the <i>fillOnWrapup</i> parameter is true, rescale the
222     *  plot so that all the data is visible.
223     *  @exception IllegalActionException If the superclass throws it.
224     */
225    @Override
226    public void wrapup() throws IllegalActionException {
227        if (_tokens != null) {
228            for (int i = _tokens.length - 1; i >= 0; i--) {
229                double xValue = _xInit;
230
231                if (_tokens[i] != null) {
232                    Token[] currentArray = _tokens[i].arrayValue();
233
234                    // NOTE: We assume the superclass ensures this cast is safe.
235                    ((PlotInterface) plot).clear(i + _offset);
236
237                    for (Token element : currentArray) {
238                        double currentValue = ((DoubleToken) element)
239                                .doubleValue();
240                        ((PlotInterface) plot).addPoint(i + _offset, xValue,
241                                currentValue, true);
242                        xValue += _xUnit;
243                    }
244                }
245            }
246        }
247
248        super.wrapup();
249    }
250
251    ///////////////////////////////////////////////////////////////////
252    ////                         protected members                 ////
253
254    /** Start of the X axis counter. */
255    protected double _xInit;
256
257    /** Increment of the X axis counter. */
258    protected double _xUnit;
259
260    ///////////////////////////////////////////////////////////////////
261    ////                         private variables                 ////
262    // Iteration count, modulo the iterationsPerUpdate.
263    private int _iteration = 0;
264
265    // The value of the startingDataset parameter.
266    private int _offset;
267
268    // The most recently read tokens in the fire() method.
269    private ArrayToken[] _tokens;
270}