source: cpp/frams/model/model.cpp @ 720

Last change on this file since 720 was 720, checked in by Maciej Komosinski, 7 years ago

Param::save2() renamed to saveSingleLine(); unified Param::load() so that it gets a single-line/multi-line format selector

  • Property svn:eol-style set to native
File size: 34.3 KB
RevLine 
[286]1// This file is a part of Framsticks SDK.  http://www.framsticks.com/
[653]2// Copyright (C) 1999-2017  Maciej Komosinski and Szymon Ulatowski.
[286]3// See LICENSE.txt for details.
[109]4
5#include <common/nonstd_math.h>
6#include "model.h"
[375]7#include <common/log.h>
[109]8#include <frams/util/multimap.h>
[391]9#include <common/loggers/loggers.h>
[109]10
11Model::Model()
12{
[522]13        autobuildmaps = false;
14        init();
[109]15}
16
17void Model::init()
18{
[522]19        partmappingchanged = 0;
20        buildstatus = empty;
21        modelfromgenotype = 0;
22        startenergy = 1.0;
23        checklevel = 1;
[109]24#ifdef MODEL_V1_COMPATIBLE
[522]25        oldneurocount=-1; // == unknown
26        oldconnections=0;
[109]27#endif
[522]28        map = 0;
29        f0map = 0;
30        f0genoknown = 1;
31        shape = SHAPE_UNKNOWN;
[109]32}
33
34void Model::moveElementsFrom(Model &source)
35{
[522]36        int i;
37        open();
38        for (i = 0; i < source.getPartCount(); i++)
39                addPart(source.getPart(i));
40        for (i = 0; i < source.getJointCount(); i++)
41                addJoint(source.getJoint(i));
42        for (i = 0; i < source.getNeuroCount(); i++)
43                addNeuro(source.getNeuro(i));
44        source.parts.clear(); source.joints.clear(); source.neurons.clear();
45        source.clear();
[109]46}
47
48void Model::internalCopy(const Model &mod)
49{
[522]50        geno = mod.geno;
51        f0genoknown = 0;
52        startenergy = mod.startenergy;
53        if (mod.getStatus() == valid)
[109]54        {
[522]55                modelfromgenotype = mod.modelfromgenotype;
56                {for (int i = 0; i < mod.getPartCount(); i++)
57                        addPart(new Part(*mod.getPart(i))); }
58        {for (int i = 0; i < mod.getJointCount(); i++)
59        {
60                Joint *oldj = mod.getJoint(i);
61                Joint *j = new Joint(*oldj);
[109]62                addJoint(j);
[522]63                j->attachToParts(oldj->part1->refno, oldj->part2->refno);
64        }}
65        {for (int i = 0; i < mod.getNeuroCount(); i++)
66        {
67                Neuro *oldn = mod.getNeuro(i);
68                Neuro *n = new Neuro(*oldn);
[109]69                addNeuro(n);
[522]70                if (oldn->part_refno >= 0) n->attachToPart(oldn->part_refno);
[109]71                else n->attachToJoint(oldn->joint_refno);
[522]72        }}
73                for (int i = 0; i < mod.getNeuroCount(); i++)
[109]74                {
[522]75                        Neuro *oldn = mod.getNeuro(i);
76                        Neuro *n = getNeuro(i);
77                        for (int ni = 0; ni < oldn->getInputCount(); ni++)
[109]78                        {
[522]79                                double w;
80                                Neuro *oldinput = oldn->getInput(ni, w);
81                                SString info = n->getInputInfo(ni);
82                                n->addInput(getNeuro(oldinput->refno), w, &info);
[109]83                        }
84                }
85        }
86}
87
88
[522]89Model::Model(const Geno &src, bool buildmaps)
[109]90        :autobuildmaps(buildmaps)
[522]91{
92        init(src);
93}
[109]94
95void Model::operator=(const Model &mod)
96{
[522]97        clear();
98        open();
99        internalCopy(mod);
100        buildstatus = mod.buildstatus;
[109]101}
102
[522]103Model::Model(const Model &mod, bool buildmaps)
[109]104        :autobuildmaps(buildmaps)
105{
[522]106        init();
107        open();
108        internalCopy(mod);
109        buildstatus = mod.buildstatus;
[109]110}
111
112void Model::init(const Geno &src)
113{
[522]114        init();
115        modelfromgenotype = 1;
116        geno = src;
117        build();
[109]118}
119
120void Model::resetAllDelta()
121{
[522]122        for (int i = 0; i < getJointCount(); i++)
123                getJoint(i)->resetDelta();
[109]124}
125
126void Model::useAllDelta(bool yesno)
127{
[522]128        for (int i = 0; i < getJointCount(); i++)
129                getJoint(i)->useDelta(yesno);
[109]130}
131
132Model::~Model()
133{
[522]134        delmodel_list.action((intptr_t)this);
135        clear();
[109]136}
137
138void Model::clear()
139{
[522]140        FOREACH(Part*, p, parts)
141                delete p;
142        FOREACH(Joint*, j, joints)
143                delete j;
144        FOREACH(Neuro*, n, neurons)
145                delete n;
146        parts.clear(); joints.clear(); neurons.clear();
147        delMap();
148        delF0Map();
149        init();
150        geno = Geno();
151        f0geno = Geno();
[109]152}
153
154Part *Model::addPart(Part *p)
155{
[522]156        p->owner = this;
157        p->refno = parts.size();
158        parts += p;
159        return p;
[109]160}
161
162Joint *Model::addJoint(Joint *j)
163{
[522]164        j->owner = this;
165        j->refno = joints.size();
166        joints += j;
167        return j;
[109]168}
169
170Neuro *Model::addNeuro(Neuro *n)
171{
[522]172        n->owner = this;
173        n->refno = neurons.size();
174        neurons += n;
175        return n;
[109]176}
177
178void Model::removeNeuros(SList &nlist)
179{
[522]180        FOREACH(Neuro*, nu, nlist)
[109]181        {
[522]182                int i = findNeuro(nu);
183                if (i >= 0) removeNeuro(i);
[109]184        }
185}
186
[522]187void Model::removePart(int partindex, int removeattachedjoints, int removeattachedneurons)
[109]188{
[522]189        Part *p = getPart(partindex);
190        if (removeattachedjoints)
[109]191        {
[522]192                SList jlist;
193                findJoints(jlist, p);
194                FOREACH(Joint*, j, jlist)
[109]195                {
[522]196                        int i = findJoint(j);
197                        if (i >= 0) removeJoint(i, removeattachedneurons);
[109]198                }
199        }
[522]200        if (removeattachedneurons)
[109]201        {
[522]202                SList nlist;
203                findNeuros(nlist, 0, p);
204                removeNeuros(nlist);
[109]205        }
[522]206        parts -= partindex;
207        delete p;
[109]208}
209
[522]210void Model::removeJoint(int jointindex, int removeattachedneurons)
[109]211{
[522]212        Joint *j = getJoint(jointindex);
213        if (removeattachedneurons)
[109]214        {
[522]215                SList nlist;
216                findNeuros(nlist, 0, 0, j);
217                removeNeuros(nlist);
[109]218        }
[522]219        joints -= jointindex;
220        delete j;
[109]221}
222
[522]223void Model::removeNeuro(int neuroindex, bool removereferences)
[109]224{
[522]225        Neuro* thisN = getNeuro(neuroindex);
[109]226
[522]227        if (removereferences)
[109]228        {
[522]229                Neuro* n;
230                // remove all references to thisN
231                for (int i = 0; n = (Neuro*)neurons(i); i++)
[109]232                {
[522]233                        Neuro *inp;
234                        for (int j = 0; inp = n->getInput(j); j++)
235                                if (inp == thisN)
[109]236                                {
237                                n->removeInput(j);
238                                j--;
239                                }
240                }
241        }
242
[522]243        neurons -= neuroindex;
244        delete thisN;
[109]245}
246
247MultiMap& Model::getMap()
248{
[522]249        if (!map) map = new MultiMap();
250        return *map;
[109]251}
252
253void Model::delMap()
254{
[522]255        if (map) { delete map; map = 0; }
[109]256}
257void Model::delF0Map()
258{
[522]259        if (f0map) { delete f0map; f0map = 0; }
[109]260}
261
[522]262void Model::makeGenToGenMap(MultiMap& result, const MultiMap& gen1tomodel, const MultiMap& gen2tomodel)
[109]263{
[522]264        result.clear();
265        MultiMap m;
266        m.addReversed(gen2tomodel);
267        result.addCombined(gen1tomodel, m);
[109]268}
269
270void Model::getCurrentToF0Map(MultiMap& result)
271{
[522]272        result.clear();
273        if (!map) return;
274        const MultiMap& f0m = getF0Map();
275        makeGenToGenMap(result, *map, f0m);
[109]276}
277
278void Model::rebuild(bool buildm)
279{
[522]280        autobuildmaps = buildm;
281        clear();
282        build();
[109]283}
284
285void Model::initMap()
286{
[522]287        if (!map) map = new MultiMap();
288        else map->clear();
[109]289}
290
291void Model::initF0Map()
292{
[522]293        if (!f0map) f0map = new MultiMap();
294        else f0map->clear();
[109]295}
296
297void Model::build()
298{
[522]299        f0errorposition = -1;
300        f0warnposition = -1;
301        MultiMap *convmap = autobuildmaps ? new MultiMap() : NULL;
302        f0geno = (geno.getFormat() == '0') ? geno : geno.getConverted('0', convmap);
303        f0genoknown = 1;
304        if (f0geno.isInvalid())
[109]305        {
[522]306                buildstatus = invalid;
307                if (convmap) delete convmap;
308                return;
[109]309        }
[534]310        SString f0txt = f0geno.getGenes();
[522]311        buildstatus = building; // was: open();
312        if (autobuildmaps)
[109]313        {
[522]314                partmappingchanged = 0;
315                initMap();
316                initF0Map();
[109]317        }
[522]318        int pos = 0, lnum = 1, lastpos = 0;
319        SString line;
320        MultiRange frommap;
321        LoggerToMemory mh(LoggerBase::Enable | LoggerBase::DontBlock);
322        for (; f0txt.getNextToken(pos, line, '\n'); lnum++)
[109]323        {
[522]324                if (autobuildmaps)
[109]325                {
[522]326                        frommap.clear();
327                        frommap.add(lastpos, pos - 1);
[109]328                }
[522]329                mh.reset();
330                if (singleStepBuild(line, lnum, autobuildmaps ? (&frommap) : 0) == -1)
[109]331                {
[522]332                        buildstatus = invalid;
333                        f0errorposition = lastpos;
334                        if (convmap) delete convmap;
335                        return;
[109]336                }
[522]337                if (mh.getWarningCount())
338                {
339                        if (f0warnposition < 0) f0warnposition = lastpos;
340                }
341                lastpos = pos;
[109]342        }
[522]343        mh.disable();
344        close();
345        if (convmap)
[109]346        {
[522]347                *f0map = *map;
348                if (geno.getFormat() != '0')
[109]349                {
[522]350                        MultiMap tmp;
351                        tmp.addCombined(*convmap, getMap());
352                        *map = tmp;
[109]353                }
[522]354                delete convmap;
[109]355        }
356}
357
358const MultiMap& Model::getF0Map()
359{
[522]360        if (!f0map)
[109]361        {
[522]362                f0map = new MultiMap();
363                makeGeno(f0geno, f0map);
364                f0genoknown = 1;
[109]365        }
[522]366        return *f0map;
[109]367}
368
369Geno Model::rawGeno()
370{
[522]371        Geno tmpgen;
372        makeGeno(tmpgen);
373        return tmpgen;
[109]374}
375
[522]376void Model::makeGeno(Geno &g, MultiMap *map, bool handle_defaults)
[109]377{
[522]378        if ((buildstatus != valid) && (buildstatus != building))
[109]379        {
[522]380                g = Geno(0, 0, 0, "invalid model");
381                return;
[109]382        }
383
[522]384        SString gen;
[109]385
[522]386        Param modelparam(f0_model_paramtab);
387        Param partparam(f0_part_paramtab);
388        Param jointparam(f0_joint_paramtab);
389        Param neuroparam(f0_neuro_paramtab);
390        Param connparam(f0_neuroconn_paramtab);
[109]391
[522]392        static Part defaultpart;
393        static Joint defaultjoint;
394        static Neuro defaultneuro;
395        static Model defaultmodel;
396        static NeuroConn defaultconn;
397        //static NeuroItem defaultneuroitem;
[109]398
[522]399        Part *p;
400        Joint *j;
401        Neuro *n;
402        int i;
403        int len;
404        int a, b;
405        //NeuroItem *ni;
[109]406
[522]407        SString mod_props;
408        modelparam.select(this);
[720]409        modelparam.saveSingleLine(mod_props, handle_defaults ? &defaultmodel : NULL, true, !handle_defaults);
[522]410        if (mod_props.len() > 1) //are there any non-default values? ("\n" is empty)
[109]411        {
[522]412                gen += "m:";
413                gen += mod_props;
[109]414        }
415
[522]416        for (i = 0; p = (Part*)parts(i); i++)
[109]417        {
[522]418                partparam.select(p);
419                len = gen.len();
420                gen += "p:";
[720]421                partparam.saveSingleLine(gen, handle_defaults ? &defaultpart : NULL, true, !handle_defaults);
[522]422                if (map)
423                        map->add(len, gen.len() - 1, partToMap(i));
[109]424        }
[522]425        for (i = 0; j = (Joint*)joints(i); i++)
[109]426        {
[522]427                jointparam.select(j);
428                len = gen.len();
429                jointparam.setParamTab(j->usedelta ? f0_joint_paramtab : f0_nodeltajoint_paramtab);
430                gen += "j:";
[720]431                jointparam.saveSingleLine(gen, handle_defaults ? &defaultjoint : NULL, true, !handle_defaults);
[522]432                if (map)
433                        map->add(len, gen.len() - 1, jointToMap(i));
[109]434        }
[522]435        for (i = 0; n = (Neuro*)neurons(i); i++)
[109]436        {
[522]437                neuroparam.select(n);
438                len = gen.len();
439                gen += "n:";
[720]440                neuroparam.saveSingleLine(gen, handle_defaults ? &defaultneuro : NULL, true, !handle_defaults);
[522]441                if (map)
442                        map->add(len, gen.len() - 1, neuroToMap(i));
[109]443        }
[522]444        for (a = 0; a < neurons.size(); a++)
[109]445        { // inputs
[522]446                n = (Neuro*)neurons(a);
447                //      if ((n->getInputCount()==1)&&(n->getInput(0).refno <= n->refno))
448                //              continue; // already done with Neuro::conn_refno
[109]449
[522]450                for (b = 0; b < n->getInputCount(); b++)
[109]451                {
[522]452                        double w;
453                        NeuroConn nc;
454                        Neuro* n2 = n->getInput(b, w);
455                        //              if (((n2.parentcount==1)&&(n2.parent)&&(n2.parent->refno < n2.refno)) ^
456                        //                  (n2.neuro_refno>=0))
457                        //                      printf("!!!! bad Neuro::neuro_refno ?!\n");
[109]458
[522]459                        //              if ((n2.parentcount==1)&&(n2.parent)&&(n2.parent->refno < n2.refno))
460                        //              if (n2.neuro_refno>=0)
461                        //                      continue; // already done with Neuro::neuro_refno
[109]462
[522]463                        nc.n1_refno = n->refno; nc.n2_refno = n2->refno;
464                        nc.weight = w;
465                        SString **s = n->inputInfo(b);
466                        if ((s) && (*s))
467                                nc.info = **s;
468                        connparam.select(&nc);
469                        len = gen.len();
470                        gen += "c:";
[720]471                        connparam.saveSingleLine(gen, handle_defaults ? &defaultconn : NULL, true, !handle_defaults);
[522]472                        if (map)
473                                map->add(len, gen.len() - 1, neuroToMap(n->refno));
[109]474                }
475        }
[522]476        g = Geno(gen.c_str(), '0');
[109]477}
478
479//////////////
480
481void Model::open()
482{
[522]483        if (buildstatus == building) return;
484        buildstatus = building;
485        modelfromgenotype = 0;
486        partmappingchanged = 0;
487        f0genoknown = 0;
488        delMap();
[109]489}
490
491void Model::checkpoint()
492{}
493
494void Model::setGeno(const Geno& newgeno)
495{
[522]496        geno = newgeno;
[109]497}
498
499void Model::clearMap()
500{
[522]501        Part *p; Joint *j; Neuro *n;
502        int i;
503        delMap();
504        delF0Map();
505        for (i = 0; p = (Part*)parts(i); i++)
506                p->clearMapping();
507        for (i = 0; j = (Joint*)joints(i); i++)
508                j->clearMapping();
509        for (i = 0; n = (Neuro*)neurons(i); i++)
510                n->clearMapping();
[109]511}
512
[610]513int Model::close(bool building_live_model)
[109]514{
[522]515        if (buildstatus != building)
516                logPrintf("Model", "close", LOG_WARN, "Unexpected close() - no open()");
[610]517        if (internalcheck(building_live_model ? LIVE_CHECK : FINAL_CHECK) > 0)
[109]518        {
[522]519                buildstatus = valid;
[109]520
[522]521                if (partmappingchanged)
[109]522                {
[522]523                        getMap();
524                        Part *p; Joint *j; Neuro *n;
525                        int i;
526                        for (i = 0; p = (Part*)parts(i); i++)
527                                if (p->getMapping())
528                                        map->add(*p->getMapping(), partToMap(i));
529                        for (i = 0; j = (Joint*)joints(i); i++)
530                                if (j->getMapping())
531                                        map->add(*j->getMapping(), jointToMap(i));
532                        for (i = 0; n = (Neuro*)neurons(i); i++)
533                                if (n->getMapping())
534                                        map->add(*n->getMapping(), neuroToMap(i));
[109]535                }
536        }
[522]537        else
538                buildstatus = invalid;
[109]539
[522]540        return (buildstatus == valid);
[109]541}
542
543int Model::validate()
544{
[610]545        return internalcheck(EDITING_CHECK);
[109]546}
547
[522]548Pt3D Model::whereDelta(const Part& start, const Pt3D& rot, const Pt3D& delta)
[109]549{
[522]550        Orient roto;
551        roto = rot;
552        Orient o;
553        roto.transform(o, start.o);
554        //o.x=start.o/roto.x;
555        //o.y=start.o/roto.y;
556        //o.z=start.o/roto.z;
557        return o.transform(delta) + start.p;
[109]558}
559
[522]560int Model::singleStepBuild(const SString &singleline, const MultiRange* srcrange)
[109]561{
[522]562        return singleStepBuild(singleline, 0, srcrange);
[495]563}
564
[522]565int Model::singleStepBuild(const SString &singleline, int line_num, const MultiRange* srcrange)
[495]566{
[522]567        SString error_message;
568        int result = singleStepBuildNoLog(singleline, error_message, srcrange);
569        if (result < 0)
[495]570        {
[522]571                if (error_message.len() == 0) // generic error when no detailed message is available
572                        error_message = "Invalid f0 code";
573                if (line_num>0)
574                        error_message += SString::sprintf(", line #%d", line_num);
575                error_message += nameForErrors();
576                logPrintf("Model", "build", LOG_ERROR, "%s", error_message.c_str());
[495]577        }
[522]578        return result;
[495]579}
580
[522]581int Model::singleStepBuildNoLog(const SString &line, SString& error_message, const MultiRange* srcrange)
[495]582{
[522]583        error_message = SString::empty();
[720]584        const char*t = line.c_str();
585        ParamInterface::LoadOptions opts;
586        for (; *t; t++, opts.offset++)
[522]587                if (!strchr(" \r\t", *t)) break;
588        if (*t == '#') return 0;
589        if (!*t) return 0;
590        if (!strncmp(t, "p:", 2))
[109]591        {
[522]592                Param partparam(f0_part_paramtab);
593                Part *p = new Part();
594                partparam.select(p);
[720]595                opts.offset += 2;
596                partparam.load(ParamInterface::FormatSingleLine, line, &opts);
597                if (opts.parse_failed) { delete p; error_message = "Invalid 'p:'"; return -1; }
[522]598                p->o.rotate(p->rot);
599                parts += p;
600                p->owner = this;
601                if (srcrange) p->setMapping(*srcrange);
602                return getPartCount() - 1;
[109]603        }
[522]604        if (!strncmp(t, "m:", 2))
[109]605        {
[522]606                Param modelparam(f0_model_paramtab);
607                modelparam.select(this);
[720]608                opts.offset += 2;
609                modelparam.load(ParamInterface::FormatSingleLine, line, &opts);
610                if (opts.parse_failed) { error_message = "Invalid 'm:'"; return -1; }
[522]611                return 0;
[109]612        }
[522]613        else if (!strncmp(t, "j:", 2))
[109]614        {
[522]615                Param jointparam(f0_joint_paramtab);
616                Joint *j = new Joint();
617                jointparam.select(j);
[720]618                opts.offset += 2;
[522]619                j->owner = this;
[720]620                jointparam.load(ParamInterface::FormatSingleLine, line, &opts);
621                if (opts.parse_failed) { delete j; error_message = "Invalid 'j:'"; return -1; }
[522]622                bool p1_ok = false, p2_ok = false;
623                if ((p1_ok = ((j->p1_refno >= 0) && (j->p1_refno < getPartCount()))) &&
624                        (p2_ok = ((j->p2_refno >= 0) && (j->p2_refno < getPartCount()))))
[109]625                {
[522]626                        addJoint(j);
627                        if ((j->d.x != JOINT_DELTA_MARKER) || (j->d.y != JOINT_DELTA_MARKER) || (j->d.z != JOINT_DELTA_MARKER))
[114]628                        {
[522]629                                j->useDelta(1);
630                                j->resetDeltaMarkers();
[114]631                        }
[522]632                        j->attachToParts(j->p1_refno, j->p2_refno);
633                        if (srcrange) j->setMapping(*srcrange);
634                        return j->refno;
[109]635                }
[522]636                else
[109]637                {
[522]638                        error_message = SString::sprintf("Invalid reference to Part #%d", p1_ok ? j->p1_refno : j->p2_refno);
639                        delete j;
640                        return -1;
[109]641                }
642        }
[522]643        else if (!strncmp(t, "n:", 2)) // neuro (or the old neuro object as the special case)
[109]644        {
[522]645                Param neuroparam(f0_neuro_paramtab);
646                Neuro *nu = new Neuro();
647                neuroparam.select(nu);
[720]648                opts.offset += 2;
649                neuroparam.load(ParamInterface::FormatSingleLine, line, &opts);
650                if (opts.parse_failed) { delete nu; error_message = "Invalid 'n:'"; return -1; }
[109]651#ifdef MODEL_V1_COMPATIBLE
[522]652                if (nu->neuro_refno>=0) // parent specified...
[109]653                {
[522]654                        if (nu->neuro_refno >= getNeuroCount()) // and it's illegal
[109]655                        {
[522]656                                delete nu;
657                                return -1;
[109]658                        }
[522]659                        Neuro *parentNU=getNeuro(nu->neuro_refno);
660                        parentNU->addInput(nu,nu->weight);
661                        // default class for parented units: n-n link
662                        //if (nu->moredata.len()==0) nu->moredata="-";
[109]663                }
[522]664                else
[109]665#endif
666                {
[522]667                        // default class for unparented units: standard neuron
668                        if (nu->getClassName().len() == 0) nu->setClassName("N");
[109]669                }
[522]670                /*
671                        if (nu->conn_refno>=0) // input specified...
[109]672                        {
[522]673                        if (nu->conn_refno >= getNeuroCount()) // and it's illegal
674                        {
[109]675                        delete nu;
676                        return -1;
677                        }
[522]678                        Neuro *inputNU=getNeuro(nu->conn_refno);
679                        nu->addInput(inputNU,nu->weight);
680                        }
681                        */
[109]682#ifdef MODEL_V1_COMPATIBLE
[522]683                nu->weight=1.0;
[109]684#endif
[522]685                nu->owner = this;
686                // attach to part/joint
687                if (nu->part_refno >= 0)
[653]688                {
689                        nu->attachToPart(nu->part_refno);
690                        if (nu->part == NULL)
[644]691                        {
[653]692                                error_message = SString::sprintf("Invalid reference to Part #%d", nu->part_refno); delete nu; return -1;
[644]693                        }
[653]694                }
[522]695                if (nu->joint_refno >= 0)
[653]696                {
697                        nu->attachToJoint(nu->joint_refno);
698                        if (nu->joint == NULL)
[644]699                        {
[653]700                                error_message = SString::sprintf("Invalid reference to Joint #%d", nu->joint_refno); delete nu; return -1;
[644]701                        }
[653]702                }
[522]703                if (srcrange) nu->setMapping(*srcrange);
704                // todo: check part/joint ref#
[109]705#ifdef MODEL_V1_COMPATIBLE
[522]706                if (hasOldNeuroLayout())
[109]707                {
[522]708                        int count=old_getNeuroCount();
709                        neurons.insert(count,nu);
710                        oldneurocount=count+1;
711                        return oldneurocount-1;
[109]712                }
[522]713                else
[109]714#endif
715                {
[522]716                        neurons += nu;
717                        return neurons.size() - 1;
[109]718                }
719        }
[522]720        else if (!strncmp(t, "c:", 2)) // add input
[109]721        {
[522]722                Param ncparam(f0_neuroconn_paramtab);
723                NeuroConn c;
724                ncparam.select(&c);
[720]725                opts.offset += 2;
726                ncparam.load(ParamInterface::FormatSingleLine, line, &opts);
727                if (opts.parse_failed) { error_message = "Invalid 'c:'"; return -1; }
[522]728                bool n1_ok = false, n2_ok = false;
729                if ((n1_ok = ((c.n1_refno >= 0) && (c.n1_refno < getNeuroCount())))
730                        && (n2_ok = ((c.n2_refno >= 0) && (c.n2_refno < getNeuroCount()))))
[109]731                {
[522]732                        Neuro *na = getNeuro(c.n1_refno);
733                        Neuro *nb = getNeuro(c.n2_refno);
734                        na->addInput(nb, c.weight, &c.info);
735                        if (srcrange)
736                                na->addMapping(*srcrange);
737                        return 0;
[109]738                }
[522]739                error_message = SString::sprintf("Invalid reference to Neuro #%d", n1_ok ? c.n2_refno : c.n1_refno);
740                return -1;
[109]741        }
742#ifdef MODEL_V1_COMPATIBLE
[522]743        else if (!strncmp(t,"ni:",3)) // old neuroitem
[109]744        {
[522]745                // we always use old layout for "ni:"
746                Param neuroitemparam(f0_neuroitem_paramtab);
747                Neuro *nu=new Neuro();
748                neuroitemparam.select(nu);
[720]749                opts.offset+=3;
750                neuroitemparam.loadSingleLine(line,opts);
751                if (opts.parse_failed) {delete nu; return -1;}
[522]752                // illegal parent?
753                if ((nu->neuro_refno<0)||(nu->neuro_refno>=old_getNeuroCount()))
[109]754                {
[522]755                        delete nu;
756                        return -1;
[109]757                }
[522]758                Neuro *parentN=getNeuro(nu->neuro_refno);
759                // copy part/joint refno from parent, if possible
760                if ((nu->part_refno<0)&&(parentN->part_refno>=0))
761                        nu->part_refno=parentN->part_refno;
762                if ((nu->joint_refno<0)&&(parentN->joint_refno>=0))
763                        nu->joint_refno=parentN->joint_refno;
764                nu->owner=this;
765                // attach to part/joint
766                if (nu->part_refno>=0)
767                        nu->attachToPart(nu->part_refno);
768                if (nu->joint_refno>=0)
769                        nu->attachToJoint(nu->joint_refno);
770                if (srcrange)
771                        nu->setMapping(*srcrange);
772                // special case: old muscles
773                // PARENT neuron will be set up to be the CHILD of the current one (!)
774                if (nu->isOldEffector())
[109]775                {
[522]776                        nu->neuro_refno=parentN->refno;
777                        neurons+=nu;
778                        nu->owner=this;
779                        nu->addInput(parentN,nu->weight); // (!)
780                        nu->weight=1.0;
781                        parentN->invalidateOldItems();
782                        return 0; // !!! -> ...getItemCount()-1;
783                }
784                parentN->addInput(nu,nu->weight);
[109]785                neurons+=nu;
786                parentN->invalidateOldItems();
[522]787                if (nu->getClassName().len()==0)
[109]788                {
[522]789                        nu->setClassName("-");
790                        // for compatibility, "ni" can define a n-n connection
791                        // referring to non-existent neuron (which will be hopefully defined later)
792                        // internal check will add the proper input to this unit
793                        // if conn_refno >=0 and input count==0
794                        oldconnections=1;
795                        if (srcrange)
796                                parentN->addMapping(*srcrange);
[109]797                }
[522]798                else
799                        nu->weight=1.0;
800                return 0; // !!! -> ...getItemCount()-1;
[109]801        }
802#endif
[522]803        else return -1;
[109]804}
805
806#ifdef MODEL_V1_COMPATIBLE
807int Model::addOldConnectionsInputs()
808{
[522]809        if (!oldconnections) return 1;
810        Neuro* n;
811        for (int i=0;i<neurons.size();i++)
[109]812        {
[522]813                n=(Neuro*)neurons(i);
814                if (n->conn_refno>=0)
815                        if (n->isNNConnection())
816                                if (n->conn_refno < old_getNeuroCount())
[109]817                                { // good reference
[522]818                        Neuro* parent=n->parent; // nn connection has exactly one parent
819                        int inp=parent->findInput(n);
820                        Neuro* target=getNeuro(n->conn_refno);
821                        parent->setInput(inp,target,n->weight);
822                        removeNeuro(i,0); // no need to remove references
823                        i--;
[109]824                                }
[522]825                                else
[109]826                                {
[522]827                                        logPrintf("Model","internalCheck",LOG_ERROR,
828                                                "illegal N-N connection #%d (reference to #%d)%s",
829                                                i,n->conn_refno,nameForErrors().c_str());
830                                        return 0;
[109]831                                }
832        }
[522]833        oldconnections=0;
834        return 1;
[109]835}
836#endif
837
838/////////////
839
[522]840/** change the sequence of neuro units
841        and fix references in "-" objects (n-n connections)  */
842void Model::moveNeuro(int oldpos, int newpos)
[109]843{
[522]844        if (oldpos == newpos) return; // nop!
845        Neuro *n = getNeuro(oldpos);
846        neurons -= oldpos;
847        neurons.insert(newpos, n);
848        // conn_refno could be broken -> fix it
[109]849#ifdef MODEL_V1_COMPATIBLE
[522]850        for (int i=0;i<neurons.size();i++)
[109]851        {
[522]852                Neuro *n2=getNeuro(i);
853                if (n2->isNNConnection())
854                        if (n2->conn_refno == oldpos) n2->conn_refno=newpos;
855                        else
856                        { if (n2->conn_refno > oldpos) n2->conn_refno--;
857                if (n2->conn_refno >= newpos) n2->conn_refno++; }
[109]858        }
[522]859        invalidateOldNeuroCount();
[109]860#endif
861}
862
863#ifdef MODEL_V1_COMPATIBLE
864/** move all old neurons (class "N") to the front of the neurons list.
[522]865        @return number of old neurons
866        */
[109]867int Model::reorderToOldLayout()
868{
[522]869        Neuro *n;
870        int neurocount=0;
871        for (int i=0;i<neurons.size();i++)
[109]872        {
[522]873                n=(Neuro*)neurons(i);
874                if (n->isOldNeuron())
[109]875                {
[522]876                        moveNeuro(i,neurocount);
877                        neurocount++;
878                        i=neurocount-1;
[109]879                }
880        }
[522]881        return neurocount;
[109]882}
883#endif
884////////////
885
886void Model::updateNeuroRefno()
887{
[522]888        for (int i = 0; i < neurons.size(); i++)
[109]889        {
[522]890                Neuro* n = (Neuro*)neurons(i);
891                n->refno = i;
[109]892        }
893}
894
895#define VALIDMINMAX(var,template,field) \
896if (var -> field < getMin ## template () . field) \
897        { var->field= getMin ## template () . field; \
[495]898        logPrintf("Model","internalCheck",LOG_WARN,# field " too small in " # template " #%d (adjusted)",i);} \
[109]899else if (var -> field > getMax ## template () . field) \
900        { var->field= getMax ## template ()  . field; \
[495]901        logPrintf("Model","internalCheck",LOG_WARN,# field " too big in " # template " #%d (adjusted)",i);}
[109]902
903#define LINKFLAG 0x8000000
904
905void Model::updateRefno()
906{
[522]907        int i;
908        for (i = 0; i < getPartCount(); i++) getPart(i)->refno = i;
909        for (i = 0; i < getJointCount(); i++) getJoint(i)->refno = i;
910        for (i = 0; i < getNeuroCount(); i++) getNeuro(i)->refno = i;
[109]911}
912
[495]913SString Model::nameForErrors() const
914{
[522]915        if (geno.getName().len()>0)
916                return SString::sprintf(" in '%s'", geno.getName().c_str());
917        return SString::empty();
[495]918}
919
[610]920int Model::internalcheck(CheckType check)
[109]921{
[522]922        Part *p;
923        Joint *j;
924        Neuro *n;
925        int i, k;
926        int ret = 1;
927        shape = SHAPE_UNKNOWN;
928        if ((parts.size() == 0) && (neurons.size() == 0)) return 0;
929        if (parts.size() == 0)
930                size = Pt3D_0;
931        else
[109]932        {
[522]933                Pt3D bbmin = ((Part*)parts(0))->p, bbmax = bbmin;
934                for (i = 0; i < parts.size(); i++)
[165]935                {
[522]936                        p = (Part*)parts(i);
937                        p->owner = this;
938                        p->refno = i;
[536]939                        if (checklevel > 0)
940                                p->mass = 0.0;
[528]941                        //VALIDMINMAX(p,part,mass);//mass is very special
942                        // VALIDMINMAX are managed manually when adding part properties in f0-def!
943                        // (could be made dynamic but not really worth the effort)
[522]944                        VALIDMINMAX(p, Part, size);
[528]945                        VALIDMINMAX(p, Part, scale.x);
946                        VALIDMINMAX(p, Part, scale.y);
947                        VALIDMINMAX(p, Part, scale.z);
948                        VALIDMINMAX(p, Part, hollow);
[522]949                        VALIDMINMAX(p, Part, density);
950                        VALIDMINMAX(p, Part, friction);
951                        VALIDMINMAX(p, Part, ingest);
952                        VALIDMINMAX(p, Part, assim);
[528]953                        VALIDMINMAX(p, Part, vsize);
954                        VALIDMINMAX(p, Part, vcolor.x);
955                        VALIDMINMAX(p, Part, vcolor.y);
956                        VALIDMINMAX(p, Part, vcolor.z);
[522]957                        p->flags &= ~LINKFLAG; // for delta joint cycle detection
958                        if (p->p.x - p->size < bbmin.x) bbmin.x = p->p.x - p->size;
959                        if (p->p.y - p->size < bbmin.y) bbmin.y = p->p.y - p->size;
960                        if (p->p.z - p->size < bbmin.z) bbmin.z = p->p.z - p->size;
961                        if (p->p.x + p->size > bbmax.x) bbmax.x = p->p.x + p->size;
962                        if (p->p.y + p->size > bbmax.y) bbmax.y = p->p.y + p->size;
963                        if (p->p.z + p->size > bbmax.z) bbmax.z = p->p.z + p->size;
964                        if (shape == SHAPE_UNKNOWN)
[544]965                                shape = (p->shape == Part::SHAPE_BALL_AND_STICK) ? SHAPE_BALL_AND_STICK : SHAPE_SOLIDS;
[522]966                        else if (shape != SHAPE_ILLEGAL)
967                        {
[544]968                                if ((p->shape == Part::SHAPE_BALL_AND_STICK) ^ (shape == SHAPE_BALL_AND_STICK))
[522]969                                {
970                                        shape = SHAPE_ILLEGAL;
[544]971                                        logPrintf("Model", "internalCheck", LOG_WARN, "Inconsistent part shapes (mixed ball-and-stick and solids shape types)%s", nameForErrors().c_str());
[522]972                                }
973                        }
[269]974                }
[522]975                size = bbmax - bbmin;
976                for (i = 0; i < joints.size(); i++)
[109]977                {
[522]978                        j = (Joint*)joints(i);
[528]979                        // VALIDMINMAX are managed manually when adding joint properties in f0-def!
980                        // (could be made dynamic but not really worth the effort)
[522]981                        VALIDMINMAX(j, Joint, stamina);
982                        VALIDMINMAX(j, Joint, stif);
983                        VALIDMINMAX(j, Joint, rotstif);
[528]984                        VALIDMINMAX(p, Part, vcolor.x);
985                        VALIDMINMAX(p, Part, vcolor.y);
986                        VALIDMINMAX(p, Part, vcolor.z);
[522]987                        j->refno = i;
988                        j->owner = this;
989                        if (j->part1 && j->part2 && (j->part1 != j->part2))
[109]990                        {
[522]991                                j->p1_refno = j->part1->refno;
992                                j->p2_refno = j->part2->refno;
993                                if (checklevel > 0)
[546]994                                {
[536]995                                        j->part1->mass += 1.0;
996                                        j->part2->mass += 1.0;
[546]997                                }
[522]998                                if ((j->usedelta) && ((j->d.x != JOINT_DELTA_MARKER) || (j->d.y != JOINT_DELTA_MARKER) || (j->d.z != JOINT_DELTA_MARKER)))
999                                { // delta positioning -> calc. orient.
1000                                        if (j->part2->flags & LINKFLAG)
1001                                        {
1002                                                ret = 0;
1003                                                logPrintf("Model", "internalCheck", LOG_ERROR,
1004                                                        "Delta joint cycle detected at Joint #%d%s",
1005                                                        i, nameForErrors().c_str());
1006                                        }
1007                                        j->resetDeltaMarkers();
1008                                        j->o = j->rot;
1009                                        j->part1->o.transform(j->part2->o, j->o);
1010                                        //                      j->part2->o.x=j->part1->o/j->o.x;
1011                                        //                      j->part2->o.y=j->part1->o/j->o.y;
1012                                        //                      j->part2->o.z=j->part1->o/j->o.z;
1013                                        j->part2->p = j->part2->o.transform(j->d) + j->part1->p;
1014                                        j->part2->flags |= LINKFLAG; j->part1->flags |= LINKFLAG; // for delta joint cycle detection
1015                                }
1016                                else
1017                                { // abs.positioning -> calc. delta
[611]1018                                        if (check != EDITING_CHECK)
[522]1019                                        {
1020                                                // calc orient delta
1021                                                //                      Orient tmpo(j->part2->o);
1022                                                //                      tmpo*=j->part1->o;
1023                                                Orient tmpo;
1024                                                j->part1->o.revTransform(tmpo, j->part2->o);
1025                                                tmpo.getAngles(j->rot);
1026                                                j->o = j->rot;
1027                                                // calc position delta
1028                                                Pt3D tmpp(j->part2->p);
1029                                                tmpp -= j->part1->p;
1030                                                j->d = j->part2->o.revTransform(tmpp);
1031                                        }
1032                                }
[611]1033                                if (check != LIVE_CHECK)
[522]1034                                {
[544]1035                                        if (j->shape != Joint::SHAPE_FIXED)
[522]1036                                        {
1037                                                if (j->d() > getMaxJoint().d.x)
1038                                                {
1039                                                        ret = 0;
[610]1040                                                        logPrintf("Model", "internalCheck", LOG_ERROR, "Joint #%d too long (its length %g exceeds allowed %g)%s", i, j->d(), getMaxJoint().d.x, nameForErrors().c_str());
[522]1041                                                }
1042                                        }
1043                                }
[109]1044                        }
[522]1045                        else
[109]1046                        {
[522]1047                                logPrintf("Model", "internalCheck", LOG_ERROR, "Illegal part references in Joint #%d%s", i, nameForErrors().c_str());
1048                                ret = 0;
[109]1049                        }
[522]1050                        if (shape != SHAPE_ILLEGAL)
[109]1051                        {
[544]1052                                if ((j->shape == Joint::SHAPE_BALL_AND_STICK) ^ (shape == SHAPE_BALL_AND_STICK))
[109]1053                                {
[522]1054                                        shape = SHAPE_ILLEGAL;
1055                                        logPrintf("Model", "internalCheck", LOG_WARN, "Inconsistent joint shapes (mixed old and new shapes)%s", nameForErrors().c_str());
[109]1056                                }
1057                        }
1058                }
1059        }
1060#ifdef MODEL_V1_COMPATIBLE
[522]1061        if (!addOldConnectionsInputs())
1062                return 0;
[109]1063#endif
1064
[522]1065        updateNeuroRefno(); // valid refno is important for n-n connections check (later)
[109]1066
[522]1067        for (i = 0; i < neurons.size(); i++)
[109]1068        {
[522]1069                n = (Neuro*)neurons(i);
[109]1070#ifdef MODEL_V1_COMPATIBLE
[522]1071                VALIDMINMAX(n,Neuro,inertia);
1072                VALIDMINMAX(n,Neuro,force);
1073                VALIDMINMAX(n,Neuro,sigmo);
1074                n->conn_refno=-1;
1075                n->weight=1.0;
1076                n->neuro_refno=-1;
[109]1077#endif
[522]1078                n->part_refno = (n->part) ? n->part->refno : -1;
1079                n->joint_refno = (n->joint) ? n->joint->refno : -1;
[109]1080        }
1081
[522]1082        if (parts.size() && (checklevel > 0))
[109]1083        {
[522]1084                for (i = 0; i < parts.size(); i++)
[109]1085                {
[522]1086                        p = (Part*)parts(i);
[536]1087                        if (p->mass <= 0.001)
1088                                p->mass = 1.0;
[522]1089                        p->flags &= ~LINKFLAG;
[109]1090                }
[522]1091                getPart(0)->flags |= LINKFLAG;
1092                int change = 1;
1093                while (change)
[109]1094                {
[522]1095                        change = 0;
1096                        for (i = 0; i < joints.size(); i++)
[109]1097                        {
[522]1098                                j = (Joint*)joints(i);
1099                                if (j->part1->flags&LINKFLAG)
[109]1100                                {
[522]1101                                        if (!(j->part2->flags&LINKFLAG))
[109]1102                                        {
[522]1103                                                change = 1;
1104                                                j->part2->flags |= LINKFLAG;
[109]1105                                        }
1106                                }
[522]1107                                else
1108                                        if (j->part2->flags&LINKFLAG)
[109]1109                                        {
[522]1110                                        if (!(j->part1->flags&LINKFLAG))
1111                                        {
1112                                                change = 1;
1113                                                j->part1->flags |= LINKFLAG;
[109]1114                                        }
[522]1115                                        }
[109]1116                        }
1117                }
[522]1118                for (i = 0; i < parts.size(); i++)
[109]1119                {
[522]1120                        p = (Part*)parts(i);
1121                        if (!(p->flags&LINKFLAG))
[109]1122                        {
[522]1123                                logPrintf("Model", "internalCheck", LOG_ERROR, "Not all parts connected (eg. Part #0 and Part #%d)%s", i, nameForErrors().c_str());
1124                                ret = 0;
1125                                break;
[109]1126                        }
1127                }
1128        }
1129
[522]1130        for (i = 0; i < joints.size(); i++)
[109]1131        {
[522]1132                j = (Joint*)joints(i);
1133                if (j->p1_refno == j->p2_refno)
[109]1134                {
[522]1135                        logPrintf("Model", "internalCheck", LOG_ERROR, "Illegal self connection, Joint #%d%s", i, nameForErrors().c_str());
1136                        ret = 0;
1137                        break;
[109]1138                }
[522]1139                for (k = i + 1; k < joints.size(); k++)
[109]1140                {
[522]1141                        Joint* j2 = (Joint*)joints(k);
1142                        if (((j->p1_refno == j2->p1_refno) && (j->p2_refno == j2->p2_refno))
1143                                || ((j->p1_refno == j2->p2_refno) && (j->p2_refno == j2->p1_refno)))
[109]1144                        {
[522]1145                                logPrintf("Model", "internalCheck", LOG_ERROR, "Illegal duplicate Joint #%d and Joint #%d%s", i, k, nameForErrors().c_str());
1146                                ret = 0;
1147                                break;
[109]1148                        }
1149                }
1150        }
[522]1151        if (shape == SHAPE_ILLEGAL)
1152                ret = 0;
1153        return ret;
[109]1154}
1155
1156/////////////
1157
1158int Model::getErrorPosition(bool includingwarnings)
1159{
[522]1160        return includingwarnings ?
1161                ((f0errorposition >= 0) ? f0errorposition : f0warnposition)
1162                :
1163                f0errorposition;
[109]1164}
1165
1166const Geno& Model::getGeno() const
1167{
[522]1168        return geno;
[109]1169}
1170
1171const Geno Model::getF0Geno()
1172{
[522]1173        if (buildstatus == building)
1174                logPrintf("Model", "getGeno", LOG_WARN, "Model was not completed - missing close()");
1175        if (buildstatus != valid)
1176                return Geno("", '0', "", "invalid");
1177        if (!f0genoknown)
[109]1178        {
[522]1179                if (autobuildmaps)
[109]1180                {
[522]1181                        initF0Map();
1182                        makeGeno(f0geno, f0map);
[109]1183                }
[522]1184                else
[109]1185                {
[522]1186                        delF0Map();
1187                        makeGeno(f0geno);
[109]1188                }
[522]1189                f0genoknown = 1;
[109]1190        }
[522]1191        return f0geno;
[109]1192}
1193
1194int Model::getPartCount() const
1195{
[522]1196        return parts.size();
[109]1197}
1198
1199Part* Model::getPart(int i) const
1200{
[522]1201        return ((Part*)parts(i));
[109]1202}
1203
1204int Model::getJointCount() const
1205{
[522]1206        return joints.size();
[109]1207}
1208
1209Joint* Model::getJoint(int i) const
1210{
[522]1211        return ((Joint*)joints(i));
[109]1212}
1213
[522]1214int Model::findJoints(SList& result, const Part* part)
[109]1215{
[522]1216        Joint *j;
1217        int n0 = result.size();
1218        if (part)
1219                for (int i = 0; j = (Joint*)joints(i); i++)
1220                        if ((j->part1 == part) || (j->part2 == part)) result += (void*)j;
1221        return result.size() - n0;
[109]1222}
1223
1224int Model::findNeuro(Neuro* n)
[522]1225{
1226        return neurons.find(n);
1227}
[109]1228
1229int Model::findPart(Part* p)
[522]1230{
1231        return parts.find(p);
1232}
[109]1233
1234int Model::findJoint(Joint* j)
[522]1235{
1236        return joints.find(j);
1237}
[109]1238
1239int Model::findJoint(Part *p1, Part *p2)
1240{
[522]1241        Joint* j;
1242        for (int i = 0; j = getJoint(i); i++)
1243                if ((j->part1 == p1) && (j->part2 == p2)) return i;
1244        return -1;
[109]1245}
1246
1247
1248////////////////////
1249
1250#ifdef MODEL_V1_COMPATIBLE
1251void Model::calcOldNeuroCount()
1252{
[522]1253        if (oldneurocount>=0) return;
1254        oldneurocount=reorderToOldLayout();
[109]1255}
1256
1257int Model::old_getNeuroCount()
1258{ calcOldNeuroCount();
[522]1259return oldneurocount;}
[109]1260
1261Neuro* Model::old_getNeuro(int i)
1262{calcOldNeuroCount();
1263return (i<oldneurocount)? (Neuro*)getNeuro(i) : (Neuro*)0;
1264}
1265
1266int Model::old_findNeuro(Neuro* n)
1267{calcOldNeuroCount();
1268return findNeuro(n);}
1269
1270Neuro *Model::old_addNewNeuro()
1271{
[522]1272        int count=old_getNeuroCount();
1273        Neuro *nu=addNewNeuro();
1274        nu->setClassName("N");
1275        moveNeuro(nu->refno,oldneurocount);
1276        oldneurocount=count+1;
1277        return (Neuro*)nu;
[109]1278}
1279#endif
1280
1281///////////////////////
1282
1283int Model::getNeuroCount() const
[522]1284{
1285        return neurons.size();
1286}
[109]1287
1288Neuro* Model::getNeuro(int i) const
[522]1289{
1290        return (Neuro*)neurons(i);
1291}
[109]1292
1293int Model::getConnectionCount() const
1294{
[522]1295        int n = 0;
1296        for (int i = 0; i < getNeuroCount(); i++)
1297                n += getNeuro(i)->getInputCount();
1298        return n;
[109]1299}
1300
1301int Model::findNeuros(SList& result,
[522]1302        const char* classname, const Part* part, const Joint* joint)
[109]1303{
[522]1304        Neuro *nu;
1305        SString cn(classname);
1306        int n0 = result.size();
1307        for (int i = 0; nu = (Neuro*)neurons(i); i++)
[109]1308        {
[522]1309                if (part)
1310                        if (nu->part != part) continue;
1311                if (joint)
1312                        if (nu->joint != joint) continue;
1313                if (classname)
1314                        if (nu->getClassName() != cn) continue;
1315                result += (void*)nu;
[109]1316        }
[522]1317        return result.size() - n0;
[109]1318}
1319
1320///////////////////
1321
1322void Model::disturb(double amount)
1323{
[522]1324        int i;
1325        if (amount <= 0) return;
1326        for (i = 0; i < parts.size(); i++)
[109]1327        {
[522]1328                Part *p = getPart(i);
1329                p->p.x += (rnd01 - 0.5)*amount;
1330                p->p.y += (rnd01 - 0.5)*amount;
1331                p->p.z += (rnd01 - 0.5)*amount;
[109]1332        }
[522]1333        for (i = 0; i < joints.size(); i++)
[109]1334        {
[522]1335                Joint *j = getJoint(i);
1336                Pt3D tmpp(j->part2->p);
1337                tmpp -= j->part1->p;
1338                j->d = j->part2->o.revTransform(tmpp);
[109]1339        }
1340}
1341
[274]1342void Model::move(const Pt3D& shift)
1343{
[522]1344        FOREACH(Part*, p, parts)
1345                p->p += shift;
[274]1346}
1347
1348void Model::rotate(const Orient& rotation)
1349{
[522]1350        FOREACH(Part*, p, parts)
[274]1351        {
[522]1352                p->p = rotation.transform(p->p);
1353                p->setOrient(rotation.transform(p->o));
[274]1354        }
1355}
1356
[546]1357void Model::buildUsingSolidShapeTypes(const Model& src_ballandstick_shapes, Part::Shape use_shape, float thickness)
[269]1358{
[546]1359        for (int i = 0; i < src_ballandstick_shapes.getJointCount(); i++)
[269]1360        {
[546]1361                Joint *oj = src_ballandstick_shapes.getJoint(i);
1362                Part *p = addNewPart(use_shape);
[522]1363                p->p = (oj->part1->p + oj->part2->p) / 2;
1364                Orient o;
1365                o.lookAt(oj->part1->p - oj->part2->p);
1366                p->rot = o.getAngles();
1367                p->scale.x = oj->part1->p.distanceTo(oj->part2->p) / 2;
1368                p->scale.y = thickness;
1369                p->scale.z = thickness;
[269]1370        }
[653]1371        if (src_ballandstick_shapes.getJointCount() == 0) //single part "ball-and-stick" models are valid so let's make a valid solid shape model
1372                for (int i = 0; i < src_ballandstick_shapes.getPartCount(); i++)
1373                {
1374                Part *op = src_ballandstick_shapes.getPart(i);
1375                Part *p = addNewPart(Part::SHAPE_ELLIPSOID); //always using spherical shape regardless of the 'use_shape' parameter - 'use shape' is meant for sticks!
1376                p->p = op->p;
1377                p->rot = op->rot;
1378                p->scale.x = p->scale.y = p->scale.z = thickness;
1379                }
[546]1380        for (int i = 0; i < src_ballandstick_shapes.getPartCount(); i++)
[269]1381        {
[546]1382                Part *op = src_ballandstick_shapes.getPart(i);
1383                for (int j = 0; j < src_ballandstick_shapes.getJointCount(); j++)
[269]1384                {
[546]1385                        Joint *oj = src_ballandstick_shapes.getJoint(j);
[522]1386                        if ((oj->part1 == op) || (oj->part2 == op))
[269]1387                        {
[546]1388                                for (int j2 = j + 1; j2 < src_ballandstick_shapes.getJointCount(); j2++)
[269]1389                                {
[546]1390                                        Joint *oj2 = src_ballandstick_shapes.getJoint(j2);
[522]1391                                        if ((oj2->part1 == op) || (oj2->part2 == op))
[269]1392                                        {
[544]1393                                                addNewJoint(getPart(j), getPart(j2), Joint::SHAPE_FIXED);
[269]1394                                        }
1395                                }
[522]1396                                break;
[269]1397                        }
1398                }
1399        }
1400}
1401
[660]1402SolidsShapeTypeModel::SolidsShapeTypeModel(Model& m, Part::Shape use_shape, float thickness)
[546]1403{
1404        using_model = converted_model = NULL;
1405        if (m.getShapeType() == Model::SHAPE_BALL_AND_STICK)
1406        {
1407                converted_model = new Model;
1408                converted_model->open();
1409                converted_model->buildUsingSolidShapeTypes(m, use_shape, thickness);
1410                converted_model->close();
1411                using_model = converted_model;
1412        }
1413        else
1414        {
1415                converted_model = NULL;
1416                using_model = &m;
1417        }
1418}
1419
[109]1420//////////////////////
1421
[522]1422class MinPart : public Part { public: MinPart() { Param par(f0_part_paramtab, this); par.setMin(); } };
1423class MaxPart : public Part { public: MaxPart() { Param par(f0_part_paramtab, this); par.setMax(); } };
1424class MinJoint : public Joint { public: MinJoint() { Param par(f0_joint_paramtab, this); par.setMin(); } };
1425class MaxJoint : public Joint { public: MaxJoint() { Param par(f0_joint_paramtab, this); par.setMax(); } };
1426class MinNeuro : public Neuro { public: MinNeuro() { Param par(f0_neuro_paramtab, this); par.setMin(); } };
1427class MaxNeuro : public Neuro { public: MaxNeuro() { Param par(f0_neuro_paramtab, this); par.setMax(); } };
[109]1428
[522]1429Part& Model::getMinPart() { static MinPart part; return part; }
1430Part& Model::getMaxPart() { static MaxPart part; return part; }
1431Part& Model::getDefPart() { static Part part; return part; }
1432Joint& Model::getMinJoint() { static MinJoint joint; return joint; }
1433Joint& Model::getMaxJoint() { static MaxJoint joint; return joint; }
1434Joint& Model::getDefJoint() { static Joint joint; return joint; }
1435Neuro& Model::getMinNeuro() { static MinNeuro neuro; return neuro; }
1436Neuro& Model::getMaxNeuro() { static MaxNeuro neuro; return neuro; }
1437Neuro& Model::getDefNeuro() { static Neuro neuro; return neuro; }
Note: See TracBrowser for help on using the repository browser.