Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

plistSpecialty.cc

Go to the documentation of this file.
00001 #include "plistSpecialty.h"
00002 #include "string_util.h"
00003 #include <libxml/tree.h>
00004 #include <cmath>
00005 #include <cstdlib>
00006 
00007 #ifdef PLATFORM_APERIOS
00008 // has strtod, but not strtof :(
00009 #  define strtof strtod
00010 #endif
00011 
00012 //better to put this here instead of the header
00013 using namespace std;
00014 
00015 namespace plist {
00016   
00017   void OutputSelector::bad_value::initMessage() {
00018     if(strValue.size()==0 && intValue==UNUSED) {
00019       message="plist specialty type OutputSelector was passed UNUSED, configured as invalid for this value";
00020     } else if(rangeError) {
00021       if(strValue.size()>0 && intValue!=UNUSED) {
00022         std::stringstream ss;
00023         ss << "plist specialty type OutputSelector was passed an out-of-range offset named " << strValue << " (" << intValue <<")";
00024         message=ss.str();
00025       } else if(strValue.size()>0) {
00026         message="plist specialty type OutputSelector was passed an out-of-range offset named '"+strValue+"'";
00027       } else {
00028         std::stringstream ss;
00029         ss << "plist specialty type OutputSelector was passed an out-of-range offset '" << intValue <<"'";
00030         message=ss.str();
00031       }
00032     } else {
00033       if(strValue.size()>0) {
00034         message="plist specialty type OutputSelector was passed '"+strValue+"', invalid for this robot model";
00035       } else {
00036         std::stringstream ss;
00037         ss << "plist specialty type OutputSelector was passed '" << intValue <<"', invalid for this robot model";
00038         message=ss.str();
00039       }
00040     }
00041   }
00042   
00043   OutputSelector& OutputSelector::operator=(const unsigned int& v) {
00044     if(&prevVal==&val) std::swap(val,prevVal); else { prevVal=val; val=v; }
00045     if(val!=UNUSED && (val<rangeBegin || val>=rangeEnd || val>=capabilities.getNumFrames()) ) {
00046       val=UNUSED;
00047       if(throwInvalid) {
00048         fireValueChanged(prevVal==val);
00049         if(capabilities.getFrameName(v)!=NULL)
00050           throw bad_value(capabilities.getFrameName(v),v);
00051         else
00052           throw bad_value(v,true);
00053       }
00054       cerr << "Error: plist specialty type OutputSelector assigned an out-of-range ("<<rangeBegin<<','<<std::min(rangeEnd,capabilities.getNumFrames())<<"] offset "<<v<<" for model " << capabilities.getRobotName() << ", marking 'UNUSED'" << endl;
00055     }
00056     if(throwUnused && val==UNUSED) {
00057       fireValueChanged(prevVal==val);
00058       throw bad_value(UNUSED);
00059     } else {
00060       fireValueChanged(prevVal==val); 
00061     }
00062     return *this;
00063   }
00064   
00065   void OutputSelector::loadXML(xmlNode* node) {
00066     if(node==NULL)
00067       return;
00068     xmlChar* cont=xmlNodeGetContent(node);
00069     try {
00070       if(xNodeHasName(node,"string")) {
00071         useNumeric=false;
00072         saveModel="";
00073         set((char*)cont);
00074       } else if(xNodeHasName(node,"integer") || xNodeHasName(node,"real")) {
00075         if(xNodeHasName(node,"real"))
00076           std::cerr << "Warning: plist specialty type OutputSelector should be either a string or integer, interpreting 'real' as 'integer'" << std::endl;
00077         useNumeric=true;
00078         saveModel="";
00079         prevVal=val;
00080         val=UNUSED;
00081         string strval = string_util::trim((const char*)cont);
00082         if(strval.size()>0) {
00083           char * endp=NULL;
00084           val = strtol(strval.c_str(),&endp,0);
00085           if(*endp!='\0') { // nope, found non-digit characters or unknown name
00086             throw bad_format(node,"Error: plist specialty type OutputSelector encountered malformed integer");
00087           } else if(defModel.size()!=0 && defModel!=capabilities.getRobotName()) {
00088             // need to map from this other model
00089             const Capabilities * cap = getCapabilities(defModel);
00090             if(cap==NULL) {
00091               unsigned int badval=val;
00092               val=UNUSED;
00093               if(throwInvalid) {
00094                 fireValueChanged(prevVal==val);
00095                 std::stringstream ss;
00096                 ss << defModel << '/' << badval;
00097                 throw bad_value(ss.str());
00098               }
00099               cerr << "Error: plist specialty type OutputSelector has an invalid default model specifier "<<defModel<<", marking 'UNUSED'" << endl;
00100             } else {
00101               const char * name = cap->getFrameName(val);
00102               if(name==NULL) { // unknown name for specified value
00103                 unsigned int badval=val;
00104                 val=UNUSED;
00105                 if(throwInvalid) {
00106                   fireValueChanged(prevVal==val);
00107                   throw bad_value(badval);
00108                 } else {
00109                   cerr << "Error: plist specialty type OutputSelector attempted to set an invalid offset "<<badval<<" for model " << cap->getRobotName() << ", marking 'UNUSED'" << endl;
00110                 }
00111               }
00112               // val currently in the other model's ordering, convert to current model
00113               if(val!=UNUSED) {
00114                 val=capabilities.findFrameOffset(name);
00115                 if(val==-1U) {
00116                   unsigned int badval=val;
00117                   val=UNUSED;
00118                   if(throwInvalid) {
00119                     fireValueChanged(prevVal==val);
00120                     throw bad_value(name);
00121                   } else {
00122                     cerr << "Error: plist specialty type OutputSelector could not map "<<name<<" ("<<defModel<<'/'<<badval<<") to model " << capabilities.getRobotName() << ", marking 'UNUSED'" << endl;
00123                   }
00124                 }
00125               }
00126             }
00127           }
00128           if(val!=UNUSED && (val<rangeBegin || val>=rangeEnd || val>=capabilities.getNumFrames()) ) {
00129             unsigned int badval=val;
00130             val=UNUSED;
00131             if(throwInvalid) {
00132               fireValueChanged(prevVal==val);
00133               throw bad_value(strval,badval);
00134             }
00135             if(defModel.size()!=0 && defModel!=capabilities.getRobotName())
00136               cerr << "Error: plist specialty type OutputSelector loading an out-of-range ("<<rangeBegin<<','<<std::min(rangeEnd,capabilities.getNumFrames())<<"] offset "<<defModel<<'/'<<strval<<" ("<<badval<<") for model " << capabilities.getRobotName() << ", marking 'UNUSED'" << endl;
00137             else
00138               cerr << "Error: plist specialty type OutputSelector loading an out-of-range ("<<rangeBegin<<','<<std::min(rangeEnd,capabilities.getNumFrames())<<"] offset "<<strval<<" for model " << capabilities.getRobotName() << ", marking 'UNUSED'" << endl;
00139           }
00140         }
00141         if(throwUnused && val==UNUSED) {
00142           fireValueChanged(prevVal==val);
00143           throw bad_value(UNUSED);
00144         } else {
00145           fireValueChanged(prevVal==val); 
00146         }
00147       } else {
00148         throw bad_format(node,"Error: plist specialty type OutputSelector must be either a string or integer");
00149       } 
00150     } catch(const bad_format& err) {
00151       xmlFree(cont);
00152       throw err;
00153     } catch(...) {
00154       xmlFree(cont);
00155       throw;
00156     }
00157     xmlFree(cont);
00158   }
00159   
00160   void OutputSelector::saveXML(xmlNode* node) const {
00161     if(node==NULL)
00162       return;
00163     if(val!=UNUSED && useNumeric && saveModel.size()==0) {
00164       xmlNodeSetName(node,(const xmlChar*)"integer");
00165       std::stringstream str;
00166       str << (int)val;
00167       xmlNodeSetContent(node,(const xmlChar*)str.str().c_str());
00168     } else {
00169       xmlNodeSetName(node,(const xmlChar*)"string");
00170       std::string str = get();
00171       if(str=="UNUSED")
00172         str.clear();
00173       xmlNodeSetContent(node,(const xmlChar*)str.c_str());
00174     }
00175   }
00176   
00177   void OutputSelector::set(const std::string& str) {
00178     prevVal=val;
00179     val=UNUSED;
00180     string strval = string_util::trim(str);
00181     string model = defModel;
00182     string::size_type modelpos = str.find('/');
00183     if(modelpos!=string::npos) {
00184       model = saveModel = str.substr(0,modelpos);
00185       strval = str.substr(modelpos+1);
00186     }
00187     if(strval.size()==0 || strval=="UNUSED") {
00188       if(throwUnused) {
00189         fireValueChanged(prevVal==val);
00190         throw bad_value(UNUSED);
00191       }
00192     } else {
00193       if(model.size()==0 || model==capabilities.getRobotName()) {
00194         // try interpreting as a number first...
00195         char * endp=NULL;
00196         val = strtol(strval.c_str(),&endp,0);
00197         if(*endp!='\0') { // nope, found non-digit characters, try interpreting as a name
00198           val = capabilities.findFrameOffset(strval.c_str());
00199           if(val==-1U) { // nope, invalid
00200             if(throwInvalid) {
00201               val=UNUSED;
00202               fireValueChanged(prevVal==val);
00203               throw bad_value(str);
00204             } else {
00205               val=UNUSED;
00206               cerr << "Error: plist specialty type OutputSelector attempted to set an invalid offset "<<strval<<" for model " << capabilities.getRobotName() << ", marking 'UNUSED'" << endl;
00207             }
00208           }
00209         }
00210       } else {
00211         const Capabilities * cap = getCapabilities(model);
00212         if(cap==NULL) {
00213           if(throwInvalid) {
00214             fireValueChanged(prevVal==val);
00215             throw bad_value(str);
00216           }
00217           cerr << "Error: plist specialty type OutputSelector attempted to set a bad model specifier "<<model<<", marking 'UNUSED'" << endl;
00218         } else {
00219           // try interpreting as a number first...
00220           char * endp=NULL;
00221           val = strtol(strval.c_str(),&endp,0);
00222           if(*endp!='\0') { // nope, found non-digit characters, try a name
00223             val = cap->findFrameOffset(strval.c_str());
00224             if(val!=-1U) {
00225               // recognized as a name, map it to current model
00226               val=capabilities.findFrameOffset(strval.c_str());
00227               if(val==-1U) {
00228                 if(throwInvalid) {
00229                   val=UNUSED;
00230                   fireValueChanged(prevVal==val);
00231                   throw bad_value(str);
00232                 } else {
00233                   val=UNUSED;
00234                   cerr << "Error: plist specialty type OutputSelector could not map "<<str<<" to model " << capabilities.getRobotName() << ", marking 'UNUSED'" << endl;
00235                 }
00236               }
00237             } else {
00238               // not recognized as a name on the other model, try the name on the current model (might've been a bad export, but we can recover)
00239               val=capabilities.findFrameOffset(strval.c_str());
00240               if(val!=-1U) {
00241                 cerr << "Warning: plist specialty type OutputSelector couldn't find "<<str<<" on model " << cap->getRobotName() << ", but it was found on current host model " << capabilities.getRobotName() << ", using that.." << endl;
00242               } else {
00243                 if(throwInvalid) {
00244                   val=UNUSED;
00245                   fireValueChanged(prevVal==val);
00246                   throw bad_value(str);
00247                 } else {
00248                   val=UNUSED;
00249                   cerr << "Error: plist specialty type OutputSelector attempted to set an invalid offset "<<str<<" for model " << cap->getRobotName() << ", marking 'UNUSED'" << endl;
00250                 }
00251               }
00252             }
00253           } else if(val!=UNUSED) { // got a value, what's it's name, and does it map to current model?
00254             const char * name = cap->getFrameName(val);
00255             if(name==NULL) { // unknown name for specified value
00256               val=UNUSED;
00257               if(throwInvalid) {
00258                 fireValueChanged(prevVal==val);
00259                 throw bad_value(str);
00260               } else {
00261                 cerr << "Error: plist specialty type OutputSelector attempted to set an invalid offset "<<str<<" for model " << cap->getRobotName() << ", marking 'UNUSED'" << endl;
00262               }
00263             }
00264             // val currently in the other model's ordering, convert to current model
00265             if(val!=UNUSED) {
00266               val=capabilities.findFrameOffset(name);
00267               if(val==-1U) {
00268                 unsigned int badval=val;
00269                 val=UNUSED;
00270                 if(throwInvalid) {
00271                   fireValueChanged(prevVal==val);
00272                   throw bad_value(name);
00273                 } else {
00274                   cerr << "Error: plist specialty type OutputSelector could not map "<<name<<" ("<<model<<'/'<<badval<<") to model " << capabilities.getRobotName() << ", marking 'UNUSED'" << endl;
00275                 }
00276               }
00277             }
00278           }
00279         }
00280       }
00281     }
00282     if(val!=UNUSED && (val<rangeBegin || val>=rangeEnd || val>=capabilities.getNumFrames())) {
00283       unsigned int badval=val;
00284       val=UNUSED;
00285       if(throwInvalid) {
00286         fireValueChanged(prevVal==val);
00287         throw bad_value(strval,badval);
00288       }
00289       cerr << "Error: plist specialty type OutputSelector loading an out-of-range ("<<rangeBegin<<','<<std::min(rangeEnd,capabilities.getNumFrames())<<"] offset "<<str<<" ("<<badval<<") for model " << capabilities.getRobotName() << ", marking 'UNUSED'" << endl;
00290     }
00291     if(throwUnused && val==UNUSED) {
00292       fireValueChanged(prevVal==val);
00293       throw bad_value(UNUSED);
00294     } else {
00295       fireValueChanged(prevVal==val);
00296     }
00297   }
00298   
00299   std::string OutputSelector::get() const {
00300     if(val==UNUSED)
00301       return "UNUSED";
00302     if(useNumeric) {
00303       unsigned int mappedValue=val;
00304       if(saveModel.size()==0) {
00305         std::stringstream sstr;
00306         sstr << (int)val;
00307         return sstr.str();
00308       } else {
00309         const Capabilities * cap = getCapabilities(saveModel);
00310         if(cap==NULL)
00311           throw bad_format(NULL,"Error: plist specialty type OutputSelector could not save due to bad saveModel "+saveModel);
00312         std::stringstream sstr;
00313         const char * name = capabilities.getFrameName(val);
00314         if(name==NULL) {
00315           // output isn't valid on the current model, can't map to others!
00316           cerr << "Warning: plist specialty type OutputSelector does not have an for offset " << (int)val << " can't map to model " << cap->getRobotName() << endl;
00317           sstr << capabilities.getRobotName()<<'/'<<(int)val;
00318           return sstr.str();
00319         }
00320         mappedValue = (int)cap->findFrameOffset(name);
00321         if(mappedValue==-1U) {
00322           // output can't be mapped to the saveModel, so we'll override the numeric and save the name
00323           cerr << "Warning: plist specialty type OutputSelector mapping to model " << saveModel << ", does not have " << name << endl;
00324           sstr << saveModel << '/' << name;
00325           return sstr.str();
00326         }
00327         sstr << saveModel << '/' << mappedValue;
00328         return sstr.str();
00329       }
00330     } else {
00331       const char * name = capabilities.getFrameName(val);
00332       if(name!=NULL) {
00333         return name;
00334       } else {
00335         std::stringstream sstr;
00336         if(saveModel.size()>0)
00337           sstr << saveModel << '/';
00338         else
00339           sstr << "INVALID" << '/';
00340         sstr << (int)val;
00341         return sstr.str();
00342       }
00343     }
00344   }
00345   
00346   void OutputSelector::setRange(unsigned int begin, unsigned int end) {
00347     rangeBegin=begin;
00348     rangeEnd=end;
00349     if(val!=UNUSED && (val<rangeBegin || val>=rangeEnd || val>=capabilities.getNumFrames()) ) {
00350       prevVal=val;
00351       val=UNUSED;
00352       fireValueChanged(prevVal==val);
00353       if(throwInvalid) {
00354         if(capabilities.getFrameName(val)!=NULL)
00355           throw bad_value(capabilities.getFrameName(val),val);
00356         else
00357           throw bad_value(val,true);
00358       }
00359       cerr << "Error: plist specialty type OutputSelector value " << prevVal << " was invalidated by change to range (" << rangeBegin <<','<< std::min(rangeEnd,capabilities.getNumFrames()) << ']' << endl;
00360     }
00361   }
00362   
00363   //! implements the clone function for Primitive<char>
00364   PLIST_CLONE_IMP(OutputSelector,new OutputSelector);
00365   
00366 
00367   
00368 
00369   
00370 
00371   Angle::Format Angle::defaultFormat=Angle::FORMAT_DEGREE;
00372   Angle::Pedantic Angle::pedantic=Angle::PEDANTIC_FULL;
00373   
00374   void Angle::loadXML(xmlNode* node) {
00375     if(node==NULL)
00376       return;
00377     xmlChar* cont=xmlNodeGetContent(node);
00378     xmlChar * att=NULL;
00379     try {
00380       if(xNodeHasName(node,"string") || xNodeHasName(node,"integer") || xNodeHasName(node,"real")) {
00381         att = xmlGetProp(node,(const xmlChar*)"unit");
00382         if(att==NULL) {
00383           set((char*)cont);
00384         } else {
00385           prevVal=val;
00386           if(xmlStrcasecmp(att,(const xmlChar*)"rad")==0 || xmlStrcasecmp(att,(const xmlChar*)"radian")==0 || xmlStrcasecmp(att,(const xmlChar*)"radians")==0) {
00387             char * endp=NULL;
00388             val=strtof((char*)cont,&endp);
00389             if(*endp!='\0') {
00390               val=prevVal;
00391               throw bad_format(node,"plist specialty type Angle expects a numeric value");
00392             }
00393             loadedFormat=FORMAT_RADIAN;
00394           } else if(xmlStrcasecmp(att,(const xmlChar*)"°")==0 || xmlStrcasecmp(att,(const xmlChar*)"deg")==0 || xmlStrcasecmp(att,(const xmlChar*)"degree")==0 || xmlStrcasecmp(att,(const xmlChar*)"degrees")==0) {
00395             char * endp=NULL;
00396             val=strtof((char*)cont,&endp);
00397             if(*endp!='\0') {
00398               val=prevVal;
00399               throw bad_format(node,"plist specialty type Angle expects a numeric value");
00400             }
00401             val=val*(decltype(val))M_PI/180;
00402             loadedFormat=FORMAT_DEGREE;
00403           } else if(xmlStrcasecmp(att,(const xmlChar*)"π")==0 || xmlStrcasecmp(att,(const xmlChar*)"pi")==0) {
00404             char * endp=NULL;
00405             val=strtof((char*)cont,&endp);
00406             if(*endp!='\0') {
00407               val=prevVal;
00408               throw bad_format(node,"plist specialty type Angle expects a numeric value");
00409             }
00410             val*=(decltype(val))M_PI;
00411             loadedFormat=FORMAT_PI;
00412           } else if(xmlStrcasecmp(att,(const xmlChar*)"%")==0 || xmlStrcasecmp(att,(const xmlChar*)"percent")==0) {
00413             char * endp=NULL;
00414             val=strtof((char*)cont,&endp);
00415             if(*endp!='\0') {
00416               val=prevVal;
00417               throw bad_format(node,"plist specialty type Angle expects a numeric value");
00418             }
00419             val=val*2*(decltype(val))M_PI/100;
00420             loadedFormat=FORMAT_PERCENT;
00421           } else {
00422             throw bad_format(node,"Error: plist specialty type Angle encountered unknown unit specification");
00423           }
00424           fireValueChanged(prevVal==val);
00425         }
00426       } else {
00427         throw bad_format(node,"Error: plist specialty type Angle must be either a string, real, or integer");
00428       } 
00429     } catch(const bad_format& err) {
00430       xmlFree(att);
00431       xmlFree(cont);
00432       throw (err.getNode()!=NULL) ? err : bad_format(node,err.what());
00433     } catch(...) {
00434       xmlFree(att);
00435       xmlFree(cont);
00436       throw;
00437     }
00438     xmlFree(att);
00439     xmlFree(cont);
00440   }
00441   
00442   void Angle::saveXML(xmlNode* node) const {
00443     if(node==NULL)
00444       return;
00445     Format fmt = saveFormat;
00446     if(fmt==FORMAT_SAME)
00447       fmt = (loadedFormat==FORMAT_AUTO) ? defaultFormat : loadedFormat;
00448     if(fmt==FORMAT_AUTO || fmt==FORMAT_SAME) {
00449       decltype(val) deg = val/(decltype(val))M_PI*180;
00450       if(std::abs(std::floor(deg)-deg)<1e-5)
00451         fmt=FORMAT_DEGREE;
00452     }
00453     switch(pedantic) {
00454       case PEDANTIC_FULL: { // use 'string' element for non-radian values
00455         xmlUnsetProp(node,(const xmlChar*)"unit");
00456         if(fmt==FORMAT_RADIAN || fmt==FORMAT_NONE) {
00457           xmlNodeSetName(node,(const xmlChar*)"real");
00458           std::stringstream str;
00459           str << val;
00460           xmlNodeSetContent(node,(const xmlChar*)str.str().c_str());
00461         } else {
00462           xmlNodeSetName(node,(const xmlChar*)"string");
00463           xmlNodeSetContent(node,(const xmlChar*)get().c_str());
00464         }
00465       } break;
00466       
00467       case PEDANTIC_VALID: { // use 'unit' attribute on 'real' element to specify units
00468         xmlNodeSetName(node,(const xmlChar*)"real");
00469         std::stringstream str;
00470         switch(fmt) {
00471           case FORMAT_NONE: {
00472             xmlUnsetProp(node,(const xmlChar*)"unit");
00473             str << val;
00474           } break;
00475           case FORMAT_RADIAN: {
00476             xmlSetProp(node,(const xmlChar*)"unit",(const xmlChar*)"rad");
00477             str << val;
00478           } break;
00479           case FORMAT_DEGREE: {
00480             xmlSetProp(node,(const xmlChar*)"unit",(const xmlChar*)"deg");
00481             str << (val/M_PI*180);
00482           } break;
00483           case FORMAT_PI: {
00484             xmlSetProp(node,(const xmlChar*)"unit",(const xmlChar*)"pi");
00485             str << (val/M_PI);
00486           } break;
00487           case FORMAT_PERCENT: {
00488             xmlSetProp(node,(const xmlChar*)"unit",(const xmlChar*)"%");
00489             str << (val/(2*M_PI)*100);
00490           } break;
00491           default: {
00492             throw std::logic_error("unhandled format");
00493           } break;
00494         }
00495         xmlNodeSetContent(node,(const xmlChar*)str.str().c_str());
00496       } break;
00497         
00498       case PEDANTIC_CUTE: { // use 'real' element for everything
00499         xmlUnsetProp(node,(const xmlChar*)"unit");
00500         xmlNodeSetName(node,(const xmlChar*)"real");
00501         xmlNodeSetContent(node,(const xmlChar*)get().c_str());
00502       } break;
00503         
00504     }
00505   }
00506   
00507   void Angle::set(const std::string& str) {
00508     prevVal=val;
00509     string strval = string_util::trim(str);
00510     size_t p;
00511     if((p=strval.find("%"))!=string::npos) {
00512       string strnum = strval.substr(0,p);
00513       char * endp=NULL;
00514       val=strtof(strnum.c_str(),&endp);
00515       if(endp==strnum.c_str()) {
00516         val=prevVal;
00517         throw bad_format(NULL,"plist specialty type Angle expects a numeric value");
00518       }
00519       if(*endp!='\0')
00520         std::cerr << "WARNING: plist specialty type Angle ignoring extra characters preceeding '%': " << endp << std::endl;
00521       if(p!=strval.size()-strlen("%"))
00522         std::cerr << "WARNING: plist specialty type Angle ignoring extra characters following '%': " << strval.substr(p+strlen("%")) << std::endl;
00523       val=val*2*(decltype(val))M_PI/100;
00524       loadedFormat=FORMAT_PERCENT;
00525       
00526     } else if((p=strval.find("π"))!=string::npos) {
00527       bool neg=false;
00528       if(strval[0]=='-') {
00529         neg=true;
00530         strval=strval.substr(1);
00531         p=strval.find("π");
00532       }
00533       float num,den=1;
00534       if(p==0)
00535         num=1;
00536       else {
00537         string strnum = strval.substr(0,p);
00538         char * endp=NULL;
00539         num=strtof(strnum.c_str(),&endp);
00540         if(endp==strnum.c_str())
00541           throw bad_format(NULL,"plist specialty type Angle expects a numeric value in π numerator");
00542         if(*endp!='\0')
00543           std::cerr << "WARNING: plist specialty type Angle ignoring extra characters preceeding 'π': " << endp << std::endl;
00544       }
00545       strval=string_util::trim(strval.substr(p+strlen("π")));
00546       if(strval[0]=='/') {
00547         strval=strval.substr(1);
00548         if(strval.size()==0) {
00549           std::cerr << "WARNING: plist specialty type Angle ignoring trailing '/' following 'π'" << std::endl;
00550         } else {
00551           char * endp=NULL;
00552           den=strtof(strval.c_str(),&endp);
00553           if(endp==strval.c_str())
00554             throw bad_format(NULL,"plist specialty type Angle expects a numeric value in π denominator");
00555           if(*endp!='\0')
00556             std::cerr << "WARNING: plist specialty type Angle ignoring extra characters following denominator: " << endp << std::endl;
00557         }
00558       } else if(strval.size()!=0) {
00559         std::cerr << "WARNING: plist specialty type Angle ignoring extra characters following 'π': " << strval.substr(p+strlen("π")) << std::endl;
00560       }
00561       val = neg ? -num*(decltype(val))M_PI/den : num*(decltype(val))M_PI/den;
00562       loadedFormat=FORMAT_PI;
00563       
00564     } else if((p=strval.find("°"))!=string::npos) {
00565       string strnum = strval.substr(0,p);
00566       char * endp=NULL;
00567       val=strtof(strnum.c_str(),&endp);
00568       if(endp==strnum.c_str()) {
00569         val=prevVal;
00570         throw bad_format(NULL,"plist specialty type Angle expects a numeric value");
00571       }
00572       if(*endp!='\0')
00573         std::cerr << "WARNING: plist specialty type Angle ignoring extra characters preceeding '°': " << endp << std::endl;
00574       if(p!=strval.size()-strlen("°"))
00575         std::cerr << "WARNING: plist specialty type Angle ignoring extra characters following '°': " << strval.substr(p+strlen("%")) << std::endl;
00576       val=val*(decltype(val))M_PI/180;
00577       loadedFormat=FORMAT_DEGREE;
00578       
00579     } else {
00580       if(strval=="∞" && std::numeric_limits<PLISTREAL>::has_infinity) {
00581         val=std::numeric_limits<PLISTREAL>::infinity();
00582       } else {
00583         char * endp=NULL;
00584         val=strtof(strval.c_str(),&endp);
00585         if(endp==strval.c_str()) {
00586           val=prevVal;
00587           throw bad_format(NULL,"plist specialty type Angle expects a numeric value");
00588         }
00589         if(*endp!='\0')
00590           std::cerr << "WARNING: plist specialty type Angle ignoring extra characters following value: " << endp << std::endl;
00591       }
00592       loadedFormat=FORMAT_RADIAN;
00593       
00594     }
00595     fireValueChanged(prevVal==val);
00596   }
00597   
00598   std::string Angle::get() const {
00599     Format fmt = saveFormat;
00600     if(fmt==FORMAT_SAME)
00601       fmt = (loadedFormat==FORMAT_AUTO) ? defaultFormat : loadedFormat;
00602     if(fmt==FORMAT_AUTO || fmt==FORMAT_SAME) {
00603       decltype(val) deg = val*180/(decltype(val))M_PI;
00604       if(std::abs(std::floor(deg)-deg)<1e-5)
00605         fmt=FORMAT_DEGREE;
00606     }
00607     std::stringstream str;
00608     switch(fmt) {
00609       case FORMAT_NONE:
00610       case FORMAT_RADIAN: {
00611         str << val;
00612       } break;
00613       case FORMAT_DEGREE: {
00614         str << (val/M_PI*180) << "°";
00615       } break;
00616       case FORMAT_PI: {
00617         float x = val/(decltype(val))M_PI;
00618         // is it an even multiple of pi?
00619         if(std::abs(rint(x)-x)<1e-5) {
00620           if(x==1) 
00621             return "π";
00622           else if(x==-1)
00623             return "-π";
00624           else
00625             str << x << "π";
00626         } else for(int i=2; i<16; ++i) {
00627           // nope, then test for good fractions
00628           x = val/(decltype(val))M_PI*i;
00629           if(std::abs(rint(x)-x)<1e-5 && std::abs(x)>.9) {
00630             if(std::abs(rint(x)-1)<1e-5) {
00631               str << "π/" << i;
00632             } else if(std::abs(rint(x)+1)<1e-5) {
00633               str << "-π/" << i;
00634             } else {
00635               str << x << "π/" << i;
00636             }
00637             break;
00638           }
00639         }
00640         if(str.str().size()==0) {
00641           // no good fraction, just dump it
00642           str << val/M_PI << "π";
00643         }
00644       } break;
00645       case FORMAT_PERCENT: {
00646         str << (val/(2*M_PI)*100) << "%";
00647       } break;
00648       default: {
00649         throw std::logic_error("unhandled format");
00650       } break;
00651     }
00652     return str.str();
00653   }
00654   
00655   //! implements the clone function for Primitive<char>
00656   PLIST_CLONE_IMP(Angle,new Angle);
00657   
00658 }
00659 
00660 
00661 /*! @file
00662  * @brief 
00663  * @author Ethan Tira-Thompson (ejt) (Creator)
00664  */

Tekkotsu v5.1CVS
Generated Mon May 9 04:58:48 2016 by Doxygen 1.6.3