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

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