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}