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

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

Added support for "checkpoints" (intermediate phases of development of the Model when converting between genetic encodings). See Model.checkpoint() and conv_f1.cpp for an example.

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