osgEarth 2.1.1
|
00001 /* -*-c++-*- */ 00002 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph 00003 * Copyright 2008-2010 Pelican Mapping 00004 * http://osgearth.org 00005 * 00006 * osgEarth is free software; you can redistribute it and/or modify 00007 * it under the terms of the GNU Lesser General Public License as published by 00008 * the Free Software Foundation; either version 2 of the License, or 00009 * (at your option) any later version. 00010 * 00011 * This program is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 * GNU Lesser General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU Lesser General Public License 00017 * along with this program. If not, see <http://www.gnu.org/licenses/> 00018 */ 00019 #include <osgEarthSymbology/Expression> 00020 #include <osgEarth/StringUtils> 00021 #include <algorithm> 00022 00023 using namespace osgEarth; 00024 using namespace osgEarth::Symbology; 00025 00026 NumericExpression::NumericExpression( const std::string& expr ) : 00027 _src ( expr ), 00028 _value( 0.0 ), 00029 _dirty( true ) 00030 { 00031 init(); 00032 } 00033 00034 NumericExpression::NumericExpression( const NumericExpression& rhs ) : 00035 _src ( rhs._src ), 00036 _rpn ( rhs._rpn ), 00037 _vars ( rhs._vars ), 00038 _value( rhs._value ), 00039 _dirty( rhs._dirty ) 00040 { 00041 //nop 00042 } 00043 00044 NumericExpression::NumericExpression( double staticValue ) : 00045 _value( staticValue ), 00046 _dirty( false ) 00047 { 00048 _src = Stringify() << staticValue; 00049 init(); 00050 } 00051 00052 NumericExpression::NumericExpression( const Config& conf ) 00053 { 00054 mergeConfig( conf ); 00055 init(); 00056 } 00057 00058 void 00059 NumericExpression::mergeConfig( const Config& conf ) 00060 { 00061 _src = conf.value(); 00062 init(); 00063 _dirty = true; 00064 } 00065 00066 Config 00067 NumericExpression::getConfig() const 00068 { 00069 return Config( "numeric_expression", _src ); 00070 } 00071 00072 #define IS_OPERATOR(a) ( a .first == ADD || a .first == SUB || a .first == MULT || a .first == DIV || a .first == MOD ) 00073 00074 void 00075 NumericExpression::init() 00076 { 00077 _vars.clear(); 00078 _rpn.clear(); 00079 00080 StringTokenizer tokenizer( "", "'\"" ); 00081 tokenizer.addDelims( "[],()%*/+-", true ); 00082 tokenizer.keepEmpties() = false; 00083 00084 StringVector t; 00085 tokenizer.tokenize( _src, t ); 00086 //tokenize(_src, t, "[],()%*/+-", "'\"", false, true); 00087 00088 // identify tokens: 00089 AtomVector infix; 00090 bool invar = false; 00091 for( unsigned i=0; i<t.size(); ++i ) { 00092 if ( t[i] == "[" && !invar ) { 00093 invar = true; 00094 } 00095 else if ( t[i] == "]" && invar ) { 00096 invar = false; 00097 infix.push_back( Atom(VARIABLE,0.0) ); 00098 _vars.push_back( Variable(t[i-1],0) ); 00099 } 00100 else if ( t[i] == "(" ) infix.push_back( Atom(LPAREN,0.0) ); 00101 else if ( t[i] == ")" ) infix.push_back( Atom(RPAREN,0.0) ); 00102 else if ( t[i] == "," ) infix.push_back( Atom(COMMA,0.0) ); 00103 else if ( t[i] == "%" ) infix.push_back( Atom(MOD,0.0) ); 00104 else if ( t[i] == "*" ) infix.push_back( Atom(MULT,0.0) ); 00105 else if ( t[i] == "/" ) infix.push_back( Atom(DIV,0.0) ); 00106 else if ( t[i] == "+" ) infix.push_back( Atom(ADD,0.0) ); 00107 else if ( t[i] == "-" ) infix.push_back( Atom(SUB,0.0) ); 00108 else if ( t[i] == "min" ) infix.push_back( Atom(MIN,0.0) ); 00109 else if ( t[i] == "max" ) infix.push_back( Atom(MAX,0.0) ); 00110 else if ( (t[i][0] >= '0' && t[i][0] <= '9') || t[i][0] == '.' ) 00111 infix.push_back( Atom(OPERAND,as<double>(t[i],0.0)) ); 00112 00113 // note: do nothing for a comma 00114 } 00115 00116 // convert to RPN: 00117 // http://en.wikipedia.org/wiki/Shunting-yard_algorithm 00118 AtomStack s; 00119 unsigned var_i = 0; 00120 00121 for( unsigned i=0; i<infix.size(); ++i ) 00122 { 00123 Atom& a = infix[i]; 00124 00125 if ( a.first == LPAREN ) 00126 { 00127 s.push( a ); 00128 } 00129 else if ( a.first == RPAREN ) 00130 { 00131 while( s.size() > 0 ) 00132 { 00133 Atom top = s.top(); 00134 s.pop(); 00135 if ( top.first == LPAREN ) 00136 break; 00137 else 00138 _rpn.push_back( top ); 00139 } 00140 } 00141 else if ( a.first == COMMA ) 00142 { 00143 while( s.size() > 0 && s.top().first != LPAREN ) 00144 { 00145 _rpn.push_back( s.top() ); 00146 s.pop(); 00147 } 00148 } 00149 else if ( IS_OPERATOR(a) ) 00150 { 00151 if ( s.empty() || a.first > s.top().first ) 00152 { 00153 s.push( a ); 00154 } 00155 else 00156 { 00157 while( s.size() > 0 && a.first < s.top().first && IS_OPERATOR(s.top()) ) 00158 { 00159 _rpn.push_back( s.top() ); 00160 s.pop(); 00161 } 00162 s.push( a ); 00163 } 00164 } 00165 else if ( a.first == MIN || a.first == MAX ) 00166 { 00167 s.push( a ); 00168 } 00169 else if ( a.first == OPERAND ) 00170 { 00171 _rpn.push_back( a ); 00172 } 00173 else if ( a.first == VARIABLE ) 00174 { 00175 _rpn.push_back( a ); 00176 _vars[var_i++].second = _rpn.size()-1; // store the index 00177 } 00178 } 00179 00180 while( s.size() > 0 ) 00181 { 00182 _rpn.push_back( s.top() ); 00183 s.pop(); 00184 } 00185 } 00186 00187 void 00188 NumericExpression::set( const Variable& var, double value ) 00189 { 00190 Atom& a = _rpn[var.second]; 00191 if ( a.second != value ) 00192 { 00193 a.second = value; 00194 _dirty = true; 00195 } 00196 } 00197 00198 double 00199 NumericExpression::eval() const 00200 { 00201 if ( _dirty ) 00202 { 00203 std::stack<double> s; 00204 00205 for( unsigned i=0; i<_rpn.size(); ++i ) 00206 { 00207 const Atom& a = _rpn[i]; 00208 00209 if ( a.first == ADD ) 00210 { 00211 if ( s.size() >= 2 ) 00212 { 00213 double op2 = s.top(); s.pop(); 00214 double op1 = s.top(); s.pop(); 00215 s.push( op1 + op2 ); 00216 } 00217 } 00218 else if ( a.first == SUB ) 00219 { 00220 if ( s.size() >= 2 ) 00221 { 00222 double op2 = s.top(); s.pop(); 00223 double op1 = s.top(); s.pop(); 00224 s.push( op1 - op2 ); 00225 } 00226 } 00227 else if ( a.first == MULT ) 00228 { 00229 if ( s.size() >= 2 ) 00230 { 00231 double op2 = s.top(); s.pop(); 00232 double op1 = s.top(); s.pop(); 00233 s.push( op1 * op2 ); 00234 } 00235 } 00236 else if ( a.first == DIV ) 00237 { 00238 if ( s.size() >= 2 ) 00239 { 00240 double op2 = s.top(); s.pop(); 00241 double op1 = s.top(); s.pop(); 00242 s.push( op1 / op2 ); 00243 } 00244 } 00245 else if ( a.first == MOD ) 00246 { 00247 if ( s.size() >= 2 ) 00248 { 00249 double op2 = s.top(); s.pop(); 00250 double op1 = s.top(); s.pop(); 00251 s.push( fmod(op1, op2) ); 00252 } 00253 } 00254 else if ( a.first == MIN ) 00255 { 00256 if ( s.size() >= 2 ) 00257 { 00258 double op2 = s.top(); s.pop(); 00259 double op1 = s.top(); s.pop(); 00260 s.push( std::min(op1, op2) ); 00261 } 00262 } 00263 else if ( a.first == MAX ) 00264 { 00265 if ( s.size() >= 2 ) 00266 { 00267 double op2 = s.top(); s.pop(); 00268 double op1 = s.top(); s.pop(); 00269 s.push( std::max(op1, op2) ); 00270 } 00271 } 00272 else // OPERAND or VARIABLE 00273 { 00274 s.push( a.second ); 00275 } 00276 } 00277 00278 const_cast<NumericExpression*>(this)->_value = s.size() > 0 ? s.top() : 0.0; 00279 const_cast<NumericExpression*>(this)->_dirty = false; 00280 } 00281 00282 return !osg::isNaN( _value ) ? _value : 0.0; 00283 } 00284 00285 //------------------------------------------------------------------------ 00286 00287 StringExpression::StringExpression( const std::string& expr ) : 00288 _src( expr ), 00289 _dirty( true ) 00290 { 00291 init(); 00292 } 00293 00294 StringExpression::StringExpression( const StringExpression& rhs ) : 00295 _src( rhs._src ), 00296 _vars( rhs._vars ), 00297 _value( rhs._value ), 00298 _infix( rhs._infix ), 00299 _dirty( rhs._dirty ), 00300 _uriContext( rhs._uriContext ) 00301 { 00302 //nop 00303 } 00304 00305 StringExpression::StringExpression( const Config& conf ) 00306 { 00307 mergeConfig( conf ); 00308 init(); 00309 } 00310 00311 void 00312 StringExpression::mergeConfig( const Config& conf ) 00313 { 00314 _src = conf.value(); 00315 _dirty = true; 00316 } 00317 00318 Config 00319 StringExpression::getConfig() const 00320 { 00321 return Config( "string_expression", _src ); 00322 } 00323 00324 void 00325 StringExpression::init() 00326 { 00327 StringTokenizer izer("", ""); 00328 izer.addDelims( "[]", true ); 00329 izer.addQuotes( "'\"", false ); 00330 izer.keepEmpties() = false; 00331 izer.trimTokens() = false; 00332 00333 StringVector t; 00334 izer.tokenize( _src, t ); 00335 //tokenize(_src, t, "[]", "'\"", false, true, false); 00336 00337 // identify tokens: 00338 bool invar = false; 00339 for( unsigned i=0; i<t.size(); ++i ) 00340 { 00341 if ( t[i] == "[" && !invar ) 00342 { 00343 invar = true; 00344 } 00345 else if ( t[i] == "]" && invar ) 00346 { 00347 invar = false; 00348 _infix.push_back( Atom(VARIABLE,"") ); 00349 _vars.push_back( Variable(t[i-1],0) ); 00350 } 00351 else 00352 _infix.push_back( Atom(OPERAND,t[i]) ); 00353 } 00354 } 00355 00356 void 00357 StringExpression::set( const Variable& var, const std::string& value ) 00358 { 00359 Atom& a = _infix[var.second]; 00360 if ( a.second != value ) 00361 { 00362 a.second = value; 00363 _dirty = true; 00364 } 00365 } 00366 00367 const std::string& 00368 StringExpression::eval() const 00369 { 00370 if ( _dirty ) 00371 { 00372 std::stringstream buf; 00373 for( AtomVector::const_iterator i = _infix.begin(); i != _infix.end(); ++i ) 00374 buf << i->second; 00375 00376 const_cast<StringExpression*>(this)->_value = buf.str(); 00377 const_cast<StringExpression*>(this)->_dirty = false; 00378 } 00379 00380 return _value; 00381 }