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}