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

Last change on this file since 653 was 653, checked in by Maciej Komosinski, 3 years ago

Model::buildUsingSolidShapeTypes() can now handle single-Part models (affects SolidsShapeTypeModel?, ModelGeometryInfo?)

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