// This file is a part of the Framsticks GDK. // Copyright (C) 2002-2014 Maciej Komosinski and Szymon Ulatowski. See LICENSE.txt for details. // Refer to http://www.framsticks.com/ for further information. #include "conv_f1.h" #include #include #include #include #include //#define v1f1COMPATIBLE F1Props stdprops={1, 0, 1, 0.4, 0.25, 0.25, 0.25, 0.25, 0.0, 1.0, 1.0, 1, 0.2, 0.5,0.5,0.5 }; class Builder { public: Builder(const char*g,int mapping=0):invalid(0),genbegin(g),usemapping(mapping),energ(0),energ_div(0),first_part_mapping(NULL) {} ~Builder() {SAFEDELETE(first_part_mapping);} char tmp[222]; bool invalid; Model model; const char *genbegin; SList neuro_f1_to_f0; // neuro_f1_to_f0(f1_refno) = actual neuro pointer Neuro *last_f1_neuro; SyntParam *neuro_cls_param; struct Connection { int n1,n2; double w; Connection(int _n1,int _n2, double _w):n1(_n1),n2(_n2),w(_w) {} }; SListTempl connections; int usemapping; MultiRange range; MultiRange *first_part_mapping; double lastjoint_muscle_power; double energ,energ_div; void grow(int part1,const char*g,Pt3D k,F1Props c); void setPartMapping(int p,const char* g); int growJoint(int part1,int part2,Pt3D &angle,F1Props &c,const char *g); int growPart(F1Props &c,const char *g); const char *skipNeuro(const char *z); const char* growNeuro(const char* t,F1Props &c,int&); void growConnection(const char* begin,const char* colon,const char* end,F1Props& props); int countBranches(const char*g,SList &out); SyntParam* lastNeuroClassParam(); void addClassParam(const char* name,double value); void addClassParam(const char* name,const char* value); const MultiRange* makeRange(const char*g) {return makeRange(g,g);} const MultiRange* makeRange(const char*g,const char*g2); Part *getLastPart() {return getLastJoint()->part2;} Neuro *getLastNeuro() {return model.getNeuro(model.getNeuroCount()-1);} Joint *getLastJoint() {return model.getJoint(model.getJointCount()-1);} void addOrRememberInput(int n1,int n2,double w) { //if (!addInput(n1,n2,w,false)) connections+=Connection(n1,n2,w); } bool addInput(int n1,int n2,double w,bool final) { if ((n1<0) || (n2<0) || (n1>=neuro_f1_to_f0.size()) || (n2>=neuro_f1_to_f0.size())) { if (final) FMprintf("GenoConvF1","addInput",FMLV_WARN, "illegal neuron connection %d <- %d (ignored)",n1,n2); return 0; } Neuro *neuro=(Neuro*)neuro_f1_to_f0(n1); Neuro *input=(Neuro*)neuro_f1_to_f0(n2); neuro->addInput(input,w); return 1; } void addPendingInputs() { for(int i=0;in1,c->n2,c->w,true); } } }; const MultiRange* Builder::makeRange(const char*g,const char*g2) { if (!usemapping) return 0; range.clear(); range.add(g-genbegin,g2-genbegin); return ⦥ } void F1Props::wykluczanie() { double s=ruch+asym+odpor+wchl; ruch=ruch/s; asym=asym/s; odpor=odpor/s; wchl=wchl/s; } /** main conversion function - with conversion map support */ SString GenoConv_f1::convert(SString &i,MultiMap *map) { const char* g=(const char*)i; Builder builder(g,map?1:0); builder.model.open(); builder.grow(-1,g,Pt3D_0,stdprops); // uses Model::singleStepBuild to create model elements if (builder.invalid) return SString(); builder.addPendingInputs(); builder.model.startenergy=(builder.energ_div>0)?(builder.energ/builder.energ_div):1.0; builder.model.close(); // model is ready to use now if (map) builder.model.getCurrentToF0Map(*map); // generate f1-to-f0 conversion map return builder.model.getF0Geno().getGene(); } void Builder::setPartMapping(int p,const char* g) { if (!usemapping) return; const MultiRange *r=makeRange(g); if (p<0) { //special case: mapping the part which is not yet created if (first_part_mapping) first_part_mapping->add(*r); else first_part_mapping=new MultiRange(*r); } else model.getPart(p)->addMapping(*r); } void Builder::grow(int part1,const char*g,Pt3D k,F1Props c) { int hasmuscles=0; k+=Pt3D(c.rot,0,c.skr); while(1) { switch(*g) { case 0: case ',': case ')': return; case 'R': k.x+=0.7853; setPartMapping(part1,g); break; case 'r': k.x-=0.7853; setPartMapping(part1,g); break; case 'Q': c.rot+=(1.58-c.rot)*0.3; setPartMapping(part1,g); break; case 'q': c.rot+=(-1.58-c.rot)*0.3; setPartMapping(part1,g); break; #ifdef v1f1COMPATIBLE case 'L': c.dlug+=(3.0-c.dlug)*0.3; setPartMapping(part1,g); break; #else case 'L': c.dlug+=(2.0-c.dlug)*0.3; setPartMapping(part1,g); break; #endif case 'l': c.dlug+=(0.33-c.dlug)*0.3; setPartMapping(part1,g); break; case 'A': c.asym+=(1-c.asym)*0.8; c.wykluczanie(); setPartMapping(part1,g); break; case 'a': c.asym-=c.asym*0.4; c.wykluczanie(); setPartMapping(part1,g); break; case 'I': c.wchl+=(1-c.wchl)*0.8; c.wykluczanie(); setPartMapping(part1,g); break; case 'i': c.wchl-=c.wchl*0.4; c.wykluczanie(); setPartMapping(part1,g); break; case 'S': c.odpor+=(1-c.odpor)*0.8; c.wykluczanie(); setPartMapping(part1,g); break; case 's': c.odpor-=c.odpor*0.4; c.wykluczanie(); setPartMapping(part1,g); break; case 'M': c.ruch+=(1-c.ruch)*0.8; c.wykluczanie(); setPartMapping(part1,g); break; case 'm': c.ruch-=c.ruch*0.4; c.wykluczanie(); setPartMapping(part1,g); break; case 'C': c.skr+=(2.0-c.skr)*0.25; setPartMapping(part1,g); break; case 'c': c.skr+=(-2.0-c.skr)*0.25; setPartMapping(part1,g); break; case 'F': c.tarcie+=(4-c.tarcie)*0.2; setPartMapping(part1,g); break; case 'f': c.tarcie-=c.tarcie*0.2; setPartMapping(part1,g); break; case 'W': c.masa+=(2.0-c.masa)*0.3; setPartMapping(part1,g); break; case 'w': c.masa+=(0.5-c.masa)*0.3; setPartMapping(part1,g); break; case 'E': c.energ+=(10.0-c.energ)*0.1; setPartMapping(part1,g); break; case 'e': c.energ-=c.energ*0.1; setPartMapping(part1,g); break; case 'D': c.cred+=(1.0-c.cred)*0.25; setPartMapping(part1,g); break; case 'd': c.cred+=(0.0-c.cred)*0.25; setPartMapping(part1,g); break; case 'G': c.cgreen+=(1.0-c.cgreen)*0.25; setPartMapping(part1,g); break; case 'g': c.cgreen+=(0.0-c.cgreen)*0.25; setPartMapping(part1,g); break; case 'B': c.cblue+=(1.0-c.cblue)*0.25; setPartMapping(part1,g); break; case 'b': c.cblue+=(0.0-c.cblue)*0.25; setPartMapping(part1,g); break; case 'H': c.grub+=(0.7-c.grub)*0.25; setPartMapping(part1,g); break; case 'h': c.grub+=(0.05-c.grub)*0.25; setPartMapping(part1,g); break; case '[': //neuron // setdebug(g-(char*)geny,DEBUGNEURO | !l_neu); if (model.getJointCount()) g=growNeuro(g+1,c,hasmuscles); else { FramMessage("GenoConv_F1","grow","Illegal neuron position (ignored)",1); g=skipNeuro(g+1); } break; case 'X': { int freshpart=0; //setdebug(g-(char*)geny,DEBUGEST | !l_est); if (part1<0) //initial grow { if (model.getPartCount()>0) part1=0; else { part1=growPart(c,g); freshpart=1; if (first_part_mapping) model.getPart(part1)->setMapping(*first_part_mapping); } } if (!freshpart) { Part *part=model.getPart(part1); part->density=((part->mass*part->density)+1.0/c.masa)/(part->mass+1.0); // v=m*d // part->volume+=1.0/c.masa; part->mass+=1.0; } energ+=0.9*c.energ+0.1; energ_div+=1.0; int part2 = growPart(c,g); growJoint(part1,part2,k,c,g); // est* e = new est(*s,*s2,k,c,zz,this); // oslabianie cech wzdluz struktury c.dlug=0.5*c.dlug+0.5*stdprops.dlug; c.grub=0.5*c.grub+0.5*stdprops.grub; c.skr=0.66*c.skr; c.rot=0.66*c.rot; c.tarcie=0.8*c.tarcie+0.2*stdprops.tarcie; c.asym=0.8*c.asym+0.2*stdprops.asym; c.odpor=0.8*c.odpor+0.2*stdprops.odpor; c.ruch=0.8*c.ruch+0.2*stdprops.ruch; c.wchl=0.8*c.wchl+0.2*stdprops.wchl; c.masa+=(stdprops.masa-c.masa)*0.5; c.wykluczanie(); if (c.resetrange) c.bendrange=1.0; else c.resetrange=1; grow(part2,g+1,Pt3D_0,c); return; } case '(': { setPartMapping(part1,g); SList ga; int i,ile; ile=countBranches(g+1,ga); c.resetrange=0; c.bendrange=1.0/ile; for (i=0;igetClass(); if (cls) { neuro_cls_param=new SyntParam(last_f1_neuro->classProperties()); // this is equivalent to: // SyntParam tmp=last_f1_neuro->classProperties(); // neuro_cls_param=new SyntParam(tmp); // interestingly, some compilers eliminate the call to new SyntParam, // realizing that a copy constructor is redundant when the original object is // temporary. there are no side effect of such optimization, as long as the // copy-constructed object is exact equivalent of the original. } } return neuro_cls_param; } void Builder::addClassParam(const char* name,double value) { lastNeuroClassParam(); if (neuro_cls_param) neuro_cls_param->setDoubleById(name,value); } void Builder::addClassParam(const char* name,const char* value) { lastNeuroClassParam(); if (neuro_cls_param) { ExtValue e(value); const ExtValue &re(e); neuro_cls_param->setById(name,re); } } int Builder::countBranches(const char*g,SList &out) { int gl=0; out+=(void*)g; while (gl>=0) { switch(*g) { case 0: gl=-1; break; case '(': case '[': ++gl; break; case ')': case ']': --gl; break; case ',': if (!gl) out+=(void*)(g+1); } g++; } return !out; } int Builder::growJoint(int part1,int part2,Pt3D &angle,F1Props &c,const char *g) { double len=min(2.0,c.dlug); sprintf(tmp,"j:p1=%ld,p2=%ld,dx=%lg,rx=%lg,ry=%lg,rz=%lg,stam=%lg,vr=%g,vg=%g,vb=%g", part1,part2,len,angle.x,angle.y,angle.z,c.odpor, c.cred,c.cgreen,c.cblue); lastjoint_muscle_power=c.ruch; return model.singleStepBuild(tmp,makeRange(g)); } int Builder::growPart(F1Props &c,const char *g) { sprintf(tmp,"p:m=1,dn=%lg,fr=%lg,ing=%lg,as=%lg,vs=%g,vr=%g,vg=%g,vb=%g", 1.0/c.masa,c.tarcie,c.wchl,c.asym, c.grub, c.cred,c.cgreen,c.cblue); return model.singleStepBuild(tmp,makeRange(g)); } const char *Builder::skipNeuro(const char *z) { for (;*z;z++) if ((*z==']')||(*z==')')) break; return z-1; } const char* Builder::growNeuro(const char* t, F1Props& props,int &hasmuscles) { const char*neuroend=skipNeuro(t); last_f1_neuro=model.addNewNeuro(); neuro_cls_param=NULL; last_f1_neuro->attachToPart(getLastPart()); const MultiRange *mr=makeRange(t-1,neuroend+1); if (mr) last_f1_neuro->addMapping(*mr); neuro_f1_to_f0+=last_f1_neuro; SString clsname; bool haveclass=0; while(*t && *t<=' ') t++; const char* next=(*t)?(t+1):t; while(*next && *next<=' ') next++; if (*t && *next!=',' && *next!=']') // old style muscles [|rest] or [@rest] switch(*t) { case '@': if (t[1]==':') break; haveclass=1; // if (!(hasmuscles&1)) { hasmuscles|=1; Neuro *muscle=model.addNewNeuro(); sprintf(tmp,"@:p=%lg",lastjoint_muscle_power); muscle->addInput(last_f1_neuro); muscle->setDetails(tmp); muscle->attachToJoint(getLastJoint()); if (usemapping) muscle->addMapping(*makeRange(t)); } t++; break; case '|': if (t[1]==':') break; haveclass=1; // if (!(hasmuscles&2)) { hasmuscles|=2; Neuro *muscle=model.addNewNeuro(); sprintf(tmp,"|:p=%lg,r=%lg",lastjoint_muscle_power,props.bendrange); muscle->addInput(last_f1_neuro); muscle->setDetails(tmp); muscle->attachToJoint(getLastJoint()); if (usemapping) muscle->addMapping(*makeRange(t)); } t++; break; } while(*t && *t<=' ') t++; bool finished=0; const char *begin=t; const char* colon=0; SString classparams; while(!finished) { switch (*t) { case ':': colon=t; break; case 0: case ']': case ')': finished=1; // NO break! case ',': if ( !haveclass && !colon && t>begin ) { haveclass=1; SString clsname(begin,t-begin); clsname=trim(clsname); last_f1_neuro->setClassName(clsname); NeuroClass *cls=last_f1_neuro->getClass(); if (cls) { if (cls->getPreferredLocation()==2) last_f1_neuro->attachToJoint(getLastJoint()); else if (cls->getPreferredLocation()==1) last_f1_neuro->attachToPart(getLastPart()); lastNeuroClassParam(); //special handling: muscle properties (can be overwritten by subsequent property assignments) if (!strcmp(cls->getName(),"|")) { neuro_cls_param->setDoubleById("p",lastjoint_muscle_power); neuro_cls_param->setDoubleById("r",props.bendrange); } else if (!strcmp(cls->getName(),"@")) { neuro_cls_param->setDoubleById("p",lastjoint_muscle_power); } } } else if (colon && (colon>begin) && (t>colon)) growConnection(begin,colon,t,props); if (t[0]!=',') t--; begin=t+1; colon=0; break; } t++; } SAFEDELETE(neuro_cls_param); return t; } void Builder::growConnection(const char* begin, const char* colon,const char* end,F1Props& props) { while(*begin && *begin<=' ') begin++; int i; if (isdigit(begin[0]) || (begin[0]=='-')) { double weight=atof(colon+1); int relative=atoi(begin); int this_refno=neuro_f1_to_f0.size()-1; addOrRememberInput(this_refno,this_refno+relative,weight); } else if ((i=last_f1_neuro->extraProperties().findIdn(begin,colon-begin))>=0) { last_f1_neuro->extraProperties().set(i,colon+1); } else if (isupper(begin[0]) || strchr("*|@",begin[0])) { SString clsname(begin,colon-begin); trim(clsname); Neuro *receptor=model.addNewNeuro(); receptor->setClassName(clsname); NeuroClass *cls=receptor->getClass(); if (cls) { if (cls->getPreferredLocation()==2) receptor->attachToJoint(getLastJoint()); else if (cls->getPreferredLocation()==1) receptor->attachToPart(getLastPart()); } last_f1_neuro->addInput(receptor,atof(colon+1)); if (usemapping) receptor->addMapping(*makeRange(begin,end-1)); } else if ((begin[0]=='>')&&(begin[1])) { Neuro *out=model.addNewNeuro(); out->addInput(last_f1_neuro,atof(colon+1)); out->setClassName(SString(begin+1,end-colon-1)); if (begin[1]=='@') { sprintf(tmp,"p=%lg",lastjoint_muscle_power); out->setClassParams(tmp); } else if (begin[1]=='|') { sprintf(tmp,"p=%lg,r=%lg",lastjoint_muscle_power,props.bendrange); out->setClassParams(tmp); } NeuroClass *cls=out->getClass(); if (cls) { if (cls->getPreferredLocation()==2) out->attachToJoint(getLastJoint()); else if (cls->getPreferredLocation()==1) out->attachToPart(getLastPart()); } if (usemapping) out->addMapping(*makeRange(begin,end-1)); } else if (*begin=='!') addClassParam("fo",atof(colon+1)); else if (*begin=='=') addClassParam("in",atof(colon+1)); else if (*begin=='/') addClassParam("si",atof(colon+1)); else if (islower(begin[0])) { SString name(begin,colon-begin); SString value(colon+1,end-(colon+1)); addClassParam(name,value); } }