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

Last change on this file since 1296 was 1286, checked in by Maciej Komosinski, 11 months ago

Improved Model-building error messages; for some cases changed ERROR to WARNING because the Model is still considered invalid (and Simulator.creatwarnfail will still be effective), but ERROR messages would by default stop the simulation

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