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

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