1 /* 2 ------------------------------------------------------------------- 3 4 Copyright (C) 2014, Edwin van Leeuwen 5 6 This file is part of plotd plotting library. 7 8 Plotd is free software; you can redistribute it and/or modify 9 it under the terms of the GNU General Public License as published by 10 the Free Software Foundation; either version 3 of the License, or 11 (at your option) any later version. 12 13 Plotd is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU General Public License for more details. 17 18 You should have received a copy of the GNU General Public License 19 along with Plotd. If not, see <http://www.gnu.org/licenses/>. 20 21 ------------------------------------------------------------------- 22 */ 23 24 module cli.column; 25 26 import std.conv : to; 27 import std.regex : ctRegex, match; 28 import std.range; 29 import std.string; 30 31 version( unittest ) { 32 import std.stdio : writeln; 33 } 34 35 struct Format { 36 string mode; /// x,y,lx,ly,h 37 int dataID = -1; /// -1 is the default value 38 string plotID; /// plotName/id 39 } 40 41 Format parseColumnFormat( string mode ) { 42 Format colMode; 43 auto columnRegex = ctRegex!( r"(lx|ly|x|y|hx|hy|hz|h|..)(\d*)(.*)" ); 44 auto m = mode.match( columnRegex ); 45 colMode.mode = m.captures[1]; 46 if ( m.captures[2].length > 0 ) 47 colMode.dataID = m.captures[2].to!int; 48 colMode.plotID = m.captures[3]; 49 return colMode; 50 } 51 52 unittest { 53 auto col = parseColumnFormat( "lx1a" ); 54 assert( col.mode == "lx" ); 55 assert( col.dataID == 1 ); 56 assert( col.plotID == "a" ); 57 col = parseColumnFormat( "ly1a" ); 58 assert( col.mode == "ly" ); 59 col = parseColumnFormat( "xb" ); 60 assert( col.mode == "x" ); 61 assert( col.dataID == -1 ); 62 assert( col.plotID == "b" ); 63 64 col = parseColumnFormat( "y3" ); 65 assert( col.mode == "y" ); 66 assert( col.dataID == 3 ); 67 assert( col.plotID == "" ); 68 69 col = parseColumnFormat( "hx" ); 70 assert( col.mode == "hx" ); 71 col = parseColumnFormat( "hy" ); 72 assert( col.mode == "hy" ); 73 col = parseColumnFormat( "h" ); 74 assert( col.mode == "h" ); 75 } 76 /// Return true if Format represents an x value 77 bool xCoord( Format cm ) { 78 return cm.mode.back.to!string == "x"; 79 } 80 81 unittest { 82 auto cm = Format(); 83 cm.mode = "x"; 84 assert( cm.xCoord ); 85 cm.mode = "lx"; 86 assert( cm.xCoord ); 87 cm.mode = "yx"; 88 assert( cm.xCoord ); 89 cm.mode = "y"; 90 assert( !cm.xCoord ); 91 cm.mode = "ly"; 92 assert( !cm.xCoord ); 93 cm.mode = "xy"; 94 assert( !cm.xCoord ); 95 } 96 97 /// Return true if Format represents an y value 98 bool yCoord( Format cm ) { 99 return cm.mode.back.to!string == "y"; 100 } 101 102 unittest { 103 auto cm = Format(); 104 cm.mode = "y"; 105 assert( cm.yCoord ); 106 cm.mode = "ly"; 107 assert( cm.yCoord ); 108 cm.mode = "xy"; 109 assert( cm.yCoord ); 110 cm.mode = "x"; 111 assert( !cm.yCoord ); 112 cm.mode = "lx"; 113 assert( !cm.yCoord ); 114 cm.mode = "yx"; 115 assert( !cm.yCoord ); 116 } 117 118 /// 119 struct Formats { 120 121 this( size_t noColumns ) { 122 if (noColumns > 1) 123 _formats = parseDataFormat( "x,y,.." )._formats; 124 else 125 _formats = parseDataFormat( "h,.." )._formats; 126 } 127 128 @property Format front() { 129 auto fm = _formats.front; 130 if ( _formats.length == 1 && fm.mode == ".." ) { 131 auto pfm = prevFormats[dotdotID%prevFormats.length]; 132 133 // Increase plot ID if needed 134 if ( prevFormats.length == 2 135 && prevFormats[0].plotID.length > 0 136 && prevFormats[1].plotID.length > 0 ) { 137 pfm.plotID = pfm.plotID[0..$-1] ~ 138 (prevFormats[1].plotID.back.to!char + 139 (1+dotdotID)*(prevFormats[1].plotID.back.to!char - 140 prevFormats[0].plotID.back.to!char)).to!char.to!string; 141 } 142 return pfm; 143 } 144 return fm; 145 } 146 147 void popFront() { 148 if (_formats.length <= 3 && prevFormats.length == 0 149 && _formats.back.mode == "..") { 150 prevFormats = _formats[0..$-1].dup; 151 } 152 if (_formats.front.mode != ".." ) 153 _formats.popFront; 154 else 155 dotdotID++; 156 } 157 158 @property bool empty() { 159 return _formats.empty; 160 } 161 162 private: 163 Format[] _formats; 164 Format[] prevFormats; 165 size_t dotdotID = 0; 166 } 167 168 /// 169 Formats parseDataFormat( string dataFormat ) { 170 Formats formats; 171 foreach( fm; dataFormat.split( ',' ) ) { 172 formats._formats ~= parseColumnFormat( fm ); 173 } 174 return formats; 175 } 176 177 unittest { 178 auto fmts = parseDataFormat( "x,y" ); 179 assert( fmts.front.mode == "x" ); 180 fmts = parseDataFormat( "x,.." ); 181 fmts.popFront; 182 assert( fmts.front.mode == "x" ); 183 fmts.popFront; 184 assert( fmts.front.mode == "x" ); 185 fmts = parseDataFormat( "x,y,.." ); 186 fmts.popFront; 187 fmts.popFront; 188 assert( fmts.front.mode == "x" ); 189 fmts.popFront; 190 assert( fmts.front.mode == "y" ); 191 fmts = parseDataFormat( "haa,hab,.." ); 192 fmts.popFront; 193 fmts.popFront; 194 assert( fmts.front.plotID == "ac" ); 195 fmts.popFront; 196 assert( fmts.front.plotID == "ad" ); 197 fmts.popFront; 198 assert( fmts.front.plotID == "ae" ); 199 } 200 201 /// 202 bool validFormat( Formats formats, size_t noColumns ) { 203 if ( formats.empty ) 204 return false; 205 if ( formats._formats.length == noColumns || 206 formats._formats.back.mode == ".." ) { 207 return true; 208 } 209 return false; 210 } 211 212 unittest { 213 Formats fmts; 214 assert( !validFormat( fmts, 2 ) ); 215 } 216 217 /// Format and data of a specific column 218 struct ColumnData { 219 double value; 220 221 this( Format fm ) { 222 _format = fm; 223 } 224 225 this( string mode, int dataID, string plotID, double v ) { 226 _format.mode = mode; 227 _format.dataID = dataID; 228 _format.plotID = plotID; 229 value = v; 230 } 231 232 alias _format this; 233 234 Format _format; 235 } 236 237 unittest { 238 auto fm = Format( "lx", 1, "a" ); 239 ColumnData cm = ColumnData( fm ); 240 assert( cm.mode == "lx" ); 241 assert( cm.dataID == 1 ); 242 assert( cm.plotID == "a" ); 243 }