////////////////////////////////////////////////////////////////////// // JExpressionEvaluatorObj.cpp - an expression evaluator for NeatTools // ver. 99 Jul 16 // // // REVISIONS: ver. 99 Jul 15 (buggy: destructor) // ver. 99 Jul 14 (buggy: memory allocation in ee_MakeImage) // ver. 99 Mar 28 (buggy: copy constructor) // // Language: Microsoft Visual C++, ver 6.0 // Authors: Rob Salgado (salgado@physics.syr.edu) // Tav Hawkins (taviare@physics.syr.edu) // // Credits: This external module was based on // Laptev Eugene's (ztv@imp.kiev.ua) // Fast Expression Evaluator. // // Special thanks to Echeruo Ifeanyi // for helping us witht the copy constructor. // ////////////////////////////////////////////////////////////////////// #include "JExpressionEvaluatorObj.h" #include "JInteger.h" #include "JIntegerData.h" #include "JIntegerProperty.h" #include "JRealData.h" #include "JRealProperty.h" #include "JString.h" #include "JStringData.h" #include "JStringProperty.h" #include "JIntegerListProperty.h" #include "JLinkObj.h" #include "JLabel.h" //for drawing fancier labels #include #include #include /*------------------------------------------------------------------------*/ // // Register() registers the module with NeatTools // char* theJExpressionEvaluatorObj = JExpressionEvaluatorObj().Register(); /*------------------------------------------------------------------------*/ // // className() returns the name of the module // const char* JExpressionEvaluatorObj::className() const { return "JExpressionEvaluatorObj";} /*------------------------------------------------------------------------*/ // // clone() is used to instantiate copies of the module // JObject* JExpressionEvaluatorObj::clone() const { return new JExpressionEvaluatorObj(*this);} /*------------------------------------------------------------------------*/ // // Constructor method // JExpressionEvaluatorObj::JExpressionEvaluatorObj() { doRepaint = false; moduleColor=JColor::green.darker(); //see JColor.h for definitions value_errorColor=JColor::yellow.darker(); image_errorColor=JColor::red.brighter(); labelColor=JColor::black; labelShadowColor=JColor::green.brighter(); numvar=2; igm = numvar+1; // tell NeatTools the number of inputs ogm = 2; // tell NeatTools the number of outputs var=new double[numvar]; //this holds the input variables varlabel=new JString[numvar]; //this holds their labels varlabellist=JString(""); //this holds all of their labels image_errorflag=value_errorflag=0; //reset error flags //** default **// label="f(x,y)"; //label on module face label_scale=1; var[0]=0.0; varlabel[0]="x"; var[1]=0.0; varlabel[1]="y"; algebraicExpression="x^2+y^2"; //**// //first, build string of variable labels for (int i=0; inumvar) {label_scale=numvar;} obj = getObject(dict, "algebraicExpression"); if (obj) { algebraicExpression = *(JString*)obj;} for (int i=0;i0) { //this seems to work well, until the font gets too big... //then it mysteriously vanishes //label=g.getJFont().getName()+JString("+")+JInteger::toJString( g.getJFont().getStyle() )+JString("-")+JInteger::toJString( g.getJFont().getSize()); JFontMetrics FM(g); int fontHeight=(int)((double)rect.height/(double)(label_scale+1)); //JRect srect = JLabel::formatText(g, label, rect, color, bkgnd, raise, align); JRect textrect(rect.x,rect.y+(rect.height-fontHeight)/2-FM.getDescent(),rect.width, fontHeight); //textrect.y=rect.yheight-fontHeight)/2; //JFont gfont(fontTag[fontId], fontStyleId, rect.height-depth); JFont gfont("times", 3, fontHeight); g.setJFont(gfont); JRect srect = JLabel::formatText(g, JString(" ")+label, textrect, labelColor, labelShadowColor, 0, 0); } else { ////standard way to write the label on the module face g.setJColor(labelColor); drawText(g,label,rect); } //some examples of graphics // //int xx[] = { rect.x, rect.x+rect.width-6, rect.x, rect.x}; //int yy[] = { rect.y, rect.y+(rect.height-1)/2, rect.y+rect.height-1, rect.y}; //g.drawPolygon(xx, yy, 4); //g.drawOval(rect.x+rect.width-5, rect.y+(rect.height-1)/2-2, 5, 5); //g.drawLine(rect.x+rect.width, rect.y, rect.x, rect.y+rect.height); } /*------------------------------------------------------------------------*/ // // int JExpressionEvaluatorObj::outputType(int n){ if (n==1) return JIntegerData::id; //output port-1 is the errorflag return JRealData::id; //output port-0 is the value } int JExpressionEvaluatorObj::inputType(int n){ if (n==0) return JStringData::id; //input port-1 is the expression return JRealData::id; //other input ports are variable inputs } /*------------------------------------------------------------------------*/ // // boolean JExpressionEvaluatorObj::inputAllowed(int n){ //return true if you want to say "vacancy" //return false if you want to say "no vacancy" return !inputSet(n).last(); //accept only one link per input port //inputSet(n) returns the link list of port-n // if empty, last() returns NULL } boolean JExpressionEvaluatorObj::outputAllowed(int n){ return true; //accept as many links as needed } /*------------------------------------------------------------------------*/ // // JString JExpressionEvaluatorObj::inputTag(int n) { char* tag[] = { "formula","input_"}; if (n==0) return tag[0]; return JString(tag[1])+JInteger::toJString(n)+JString(" (")+varlabel[n-1]+JString(")"); } /*------------------------------------------------------------------------*/ JString JExpressionEvaluatorObj::outputTag(int n) { char* tag[] = { "output", "errorflag" }; if (n==1) return tag[1]; return tag[n]; } /*------------------------------------------------------------------------*/ // inputArea, outputArea defines the area that a port will accept connections // leftArea(int portNumber, // int whichPortSlotInThisArea, // int howManyPortSlotsInThisArea, // double whereToStartAreaFraction (default 0.25), // double widthAreaFraction (default is 0.50) // double perpendicularReciprocalFactor (default is 6) ) //ex: // return leftArea(n, 0, 1); //port-0 is alone on LEFT // JFRect JExpressionEvaluatorObj::inputArea(int n) { if (n==0) return topArea(n,0,1); //input port-0 is alone on TOP // return leftArea(n, 1, numvar); //input ports 1 through n on LEFT return leftArea(n, 1, numvar, 0.05, 0.90, 8); //one output on the bottomArea, starting with 1 //starting at a position (1/20) of the module HEIGHT // use a region of size (18/20) of the module HEIGHT // with a depth of size 1/(8) of the module WIDTH } /*------------------------------------------------------------------------*/ JFRect JExpressionEvaluatorObj::outputArea(int n) { if (n==1) return bottomArea(n,0,1); //output port-1 is alone on BOTTOM return rightArea(n, 0, 1); //output port-0 is alone on RIGHT } /*------------------------------------------------------------------------*/ // // int JExpressionEvaluatorObj::inputFace(int n) { if (n==0) return TOP; return LEFT; } /*------------------------------------------------------------------------*/ int JExpressionEvaluatorObj::outputFace(int n) { if (n==1) return BOTTOM; return RIGHT; } /*------------------------------------------------------------------------*/ // // JFPoint JExpressionEvaluatorObj::inputPoint(int n, JLinkObj& link) { //return leftPoint(n, link, 0, 1); if (n==0) return topPoint(n, link, 0, 1); return leftPoint(n, link, 1, numvar, 0.05, .90); } /*------------------------------------------------------------------------*/ JFPoint JExpressionEvaluatorObj::outputPoint(int n, JLinkObj& link) { if (n==1) return bottomPoint(n, link, 0, 1); return rightPoint(n, link, 0, 1); } /*------------------------------------------------------------------------*/ // // engine() is called when pinged through an input-port // void JExpressionEvaluatorObj::engine(int n, JLinkObj& link) { // int nv; // link.access(JIntegerData(nv)); //call the input-link's access() method if (n==0) { //received a ping from input port-0 JString nstr; link.access(JStringData(nstr)); //ask module at port-0 for JString if (algebraicExpression!=nstr) //update algebraicExpression? { //UPDATE algebraicExpression=nstr; ee_MakeImage(); //reprocess algebraicExpression repaint(); broadcast(0); //ping output port-0 (access yields ee_value) } } else { //must have received a ping from another input port double ndbl; link.access(JRealData(ndbl)); //ask module at port-n for double var[n-1]=ndbl; //update the corresponding variable repaint(); broadcast(0); //ping output port-0 (access yields ee_value) } } /*------------------------------------------------------------------------*/ // void JExpressionEvaluatorObj::ee_MakeImage() { //call MakeImage() in EE-src.cpp, which parses algebraicExpression and creates //an image of the expression for fast evaluation // //first, do preliminary check if(MakeImage(&ee_size, sizeof(int), algebraicExpression, ee_error, &ee_errorposition, varlabellist) == EE_SMALLBUFFER) { //SEEMS OKAY //important! if (ee_buff != NULL) {delete ee_buff;} ee_buff = new char[ee_size]; if(ee_buff == NULL) { // WE HAVE A SERIOUS PROBLEM errormessage=JString("Error 2"); image_errorflag=2; //printf("error 2.\n"); //remnant of EE.CPP (front-end example) } else if(MakeImage(ee_buff, ee_size, algebraicExpression, NULL, NULL, varlabellist) == EE_OK) { //ALL IS WELL Simplify(ee_buff, NULL); //perform algebraic simplification errormessage=errormessage2=JString(""); image_errorflag=0; } else { //WE HAVE A SERIOUS PROBLEM errormessage=JString("BAD"); image_errorflag=1; //printf("ee.dll is a bad program.\n"); } } else { //NOT OKAY image_errorflag=1; errormessage2=JString(""); if(ee_errorposition != -1) { //SYNTAX ERROR errormessage2=algebraicExpression(0,ee_errorposition+1)+"[?]"; //printf("%s\n", algebraicExpression); //printf("%*s\n", ee_errorposition+1, "^"); } //printf("%s\n\n", ee_error); /* //kill the last image (then value will evaluate to zero) if(MakeImage(&ee_size, sizeof(int), JString("0"), ee_error, &ee_errorposition, varlabellist) == EE_SMALLBUFFER) { //SEEMS OKAY for errorExpression //important! if (ee_buff != NULL) {delete ee_buff;} ee_buff = new char[ee_size]; if(MakeImage(ee_buff, ee_size, JString("0"), NULL, NULL, varlabellist) == EE_OK) { //errormessage=JString(ee_error); } else { //SERIOUS PROBLEM errormessage=JString("VERY BAD"); image_errorflag=1; //printf("ee.dll is a bad program.\n"); } } else { //SERIOUS PROBLEM errormessage=JString("VERY VERY BAD"); image_errorflag=1; //printf("ee.dll is a bad program.\n"); } */ } broadcast(1); //ping output port-1 (access yields error_state) } /*------------------------------------------------------------------------*/ // // access() may be called by an output-link engine() method // when we do a broadcast() // void JExpressionEvaluatorObj::access(int n, JLinkObj& link, const JDataType& data) { double ee_value=0.0; if (n==0 && image_errorflag==0) { ////repaint and broadcast on errors or after repair-of-errors only ////wasteful to repaint if nothing is wrong if (value_errorflag==0) { //ee_value will have the new value, if there are no problems // if (ValueFromArgarray(ee_buff, &ee_value, ee_error, numvar, var)!=EE_OK) { //we have a new value_problem ee_value=0.0; //yield zero errormessage=JString(ee_error); errormessage2=JString("value error"); value_errorflag=1; //set value_error repaint(); broadcast(1); //ping the error_output //when access is called, // we'll say all is NOT well } } else { //we are having a value_error that we will try to fix // if (ValueFromArgarray(ee_buff, &ee_value, ee_error, numvar, var)!=EE_OK) { //unresolved value_problem ee_value=0.0; errormessage=JString(ee_error); errormessage2=JString("value error"); value_errorflag=1; //set value_error } else { //value_error resolved errormessage=JString(""); errormessage2=JString(""); value_errorflag=0; } repaint(); broadcast(1); //ping the error_output } DOUBLE(data) = ee_value; //give it up } if (n==1) { //return the error code 0=no errors, 1=value_error, 2=image_error INT(data)=image_errorflag*2 + value_errorflag; } ////How to return a JString //// ////JSTRING(data)=algebraicExpression; //// ////in general, ////give value (suitably casted) in response to my output ping } /*------------------------------------------------------------------------*/ // // getProperties() produces a list of properties // JArray JExpressionEvaluatorObj::getProperties() { JArray properties; // format: // J_x_Property(VARIABLE_TAG, PERSISTENT_VARIABLE, MIN_LABEL, MAX_LABEL) if (value_errorflag | image_errorflag) { //If there's an error, display errorstrings in the property page. //This is a good example of how to vary the appearance od this page // properties.append( JStringProperty("---BAD--- AlgebraicExpression", algebraicExpression) ); properties.append( JStringProperty("[ERROR", errormessage) ); properties.append( JStringProperty("[ERROR", errormessage2) ); //I think it's okay to use repeated labels as long as one does not //want to edit its property field ///ROB } else { properties.append( JStringProperty("AlgebraicExpression", algebraicExpression) ); } properties.append( JStringProperty("[varlabel_list]", varlabellist) ); properties.append( JStringProperty("label", label) ); properties.append( JIntegerProperty("label_scale", label_scale,0,numvar) ); //IntegerProperties need a range to be specified // properties.append( JIntegerProperty("NumberOfVariables", numvar,1,16) ); int i; for (i=0; ivalue; //do update repaint(); return true; //leave and announce that the property was found } if (prop.getName() == JString("label_scale")) { label_scale = ((JIntegerProperty*)&prop)->value; //do update if (label_scale<0) {label_scale=0;} if (label_scale>numvar) {label_scale=numvar;} repaint(); return true; } if ((prop.getName() == JString("AlgebraicExpression")) || (prop.getName() == JString("---BAD--- AlgebraicExpression")) ) { algebraicExpression = ((JStringProperty*)&prop)->value; //do update ee_MakeImage(); repaint(); broadcast(0); return true; //leave and announce that the property was found } if (prop.getName() == JString("NumberOfVariables")) { int oldnumvar=numvar; numvar = ((JIntegerProperty*)&prop)->value; //do update if (numvar<1) {numvar=1;} if (numvar>16) {numvar=16;} igm=numvar+1; // tell NeatTools the new number of inputs resizeVariableList(oldnumvar,numvar); ee_MakeImage(); repaint(); broadcast(0); return true; //leave and announce that the property was found } if ( prop.getName()(0,8) == JString("varlabel")) { int i; for (i=0; ivalue; varlabellist=JString(""); for (int i=0; i