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

Last change on this file since 726 was 726, checked in by Maciej Komosinski, 3 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
Line 
1// This file is a part of Framsticks SDK.  http://www.framsticks.com/
2// Copyright (C) 1999-2017  Maciej Komosinski and Szymon Ulatowski.
3// See LICENSE.txt for details.
4
5#include <common/nonstd_math.h>
6#include "model.h"
7#include <common/log.h>
8#include <frams/util/multimap.h>
9#include <common/loggers/loggers.h>
10
11Model::Model()
12{
13        autobuildmaps = false;
14        init();
15}
16
17void Model::init()
18{
19        partmappingchanged = 0;
20        buildstatus = empty;
21        modelfromgenotype = 0;
22        startenergy = 1.0;
23        checklevel = 1;
24        map = 0;
25        f0map = 0;
26        f0genoknown = 1;
27        shape = SHAPE_UNKNOWN;
28}
29
30void Model::moveElementsFrom(Model &source)
31{
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();
42}
43
44void Model::internalCopy(const Model &mod)
45{
46        geno = mod.geno;
47        f0genoknown = 0;
48        startenergy = mod.startenergy;
49        if (mod.getStatus() == valid)
50        {
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);
58                addJoint(j);
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);
65                addNeuro(n);
66                if (oldn->part_refno >= 0) n->attachToPart(oldn->part_refno);
67                else n->attachToJoint(oldn->joint_refno);
68        }}
69                for (int i = 0; i < mod.getNeuroCount(); i++)
70                {
71                        Neuro *oldn = mod.getNeuro(i);
72                        Neuro *n = getNeuro(i);
73                        for (int ni = 0; ni < oldn->getInputCount(); ni++)
74                        {
75                                double w;
76                                Neuro *oldinput = oldn->getInput(ni, w);
77                                SString info = n->getInputInfo(ni);
78                                n->addInput(getNeuro(oldinput->refno), w, &info);
79                        }
80                }
81        }
82}
83
84
85Model::Model(const Geno &src, bool buildmaps)
86        :autobuildmaps(buildmaps)
87{
88        init(src);
89}
90
91void Model::operator=(const Model &mod)
92{
93        clear();
94        open();
95        internalCopy(mod);
96        buildstatus = mod.buildstatus;
97}
98
99Model::Model(const Model &mod, bool buildmaps)
100        :autobuildmaps(buildmaps)
101{
102        init();
103        open();
104        internalCopy(mod);
105        buildstatus = mod.buildstatus;
106}
107
108void Model::init(const Geno &src)
109{
110        init();
111        modelfromgenotype = 1;
112        geno = src;
113        build();
114}
115
116void Model::resetAllDelta()
117{
118        for (int i = 0; i < getJointCount(); i++)
119                getJoint(i)->resetDelta();
120}
121
122void Model::useAllDelta(bool yesno)
123{
124        for (int i = 0; i < getJointCount(); i++)
125                getJoint(i)->useDelta(yesno);
126}
127
128Model::~Model()
129{
130        delmodel_list.action((intptr_t)this);
131        clear();
132}
133
134void Model::clear()
135{
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();
148}
149
150Part *Model::addPart(Part *p)
151{
152        p->owner = this;
153        p->refno = parts.size();
154        parts += p;
155        return p;
156}
157
158Joint *Model::addJoint(Joint *j)
159{
160        j->owner = this;
161        j->refno = joints.size();
162        joints += j;
163        return j;
164}
165
166Neuro *Model::addNeuro(Neuro *n)
167{
168        n->owner = this;
169        n->refno = neurons.size();
170        neurons += n;
171        return n;
172}
173
174void Model::removeNeuros(SList &nlist)
175{
176        FOREACH(Neuro*, nu, nlist)
177        {
178                int i = findNeuro(nu);
179                if (i >= 0) removeNeuro(i);
180        }
181}
182
183void Model::removePart(int partindex, int removeattachedjoints, int removeattachedneurons)
184{
185        Part *p = getPart(partindex);
186        if (removeattachedjoints)
187        {
188                SList jlist;
189                findJoints(jlist, p);
190                FOREACH(Joint*, j, jlist)
191                {
192                        int i = findJoint(j);
193                        if (i >= 0) removeJoint(i, removeattachedneurons);
194                }
195        }
196        if (removeattachedneurons)
197        {
198                SList nlist;
199                findNeuros(nlist, 0, p);
200                removeNeuros(nlist);
201        }
202        parts -= partindex;
203        delete p;
204}
205
206void Model::removeJoint(int jointindex, int removeattachedneurons)
207{
208        Joint *j = getJoint(jointindex);
209        if (removeattachedneurons)
210        {
211                SList nlist;
212                findNeuros(nlist, 0, 0, j);
213                removeNeuros(nlist);
214        }
215        joints -= jointindex;
216        delete j;
217}
218
219void Model::removeNeuro(int neuroindex, bool removereferences)
220{
221        Neuro* thisN = getNeuro(neuroindex);
222
223        if (removereferences)
224        {
225                Neuro* n;
226                // remove all references to thisN
227                for (int i = 0; n = (Neuro*)neurons(i); i++)
228                {
229                        Neuro *inp;
230                        for (int j = 0; inp = n->getInput(j); j++)
231                                if (inp == thisN)
232                                {
233                                n->removeInput(j);
234                                j--;
235                                }
236                }
237        }
238
239        neurons -= neuroindex;
240        delete thisN;
241}
242
243MultiMap& Model::getMap()
244{
245        if (!map) map = new MultiMap();
246        return *map;
247}
248
249void Model::delMap()
250{
251        if (map) { delete map; map = 0; }
252}
253void Model::delF0Map()
254{
255        if (f0map) { delete f0map; f0map = 0; }
256}
257
258void Model::makeGenToGenMap(MultiMap& result, const MultiMap& gen1tomodel, const MultiMap& gen2tomodel)
259{
260        result.clear();
261        MultiMap m;
262        m.addReversed(gen2tomodel);
263        result.addCombined(gen1tomodel, m);
264}
265
266void Model::getCurrentToF0Map(MultiMap& result)
267{
268        result.clear();
269        if (!map) return;
270        const MultiMap& f0m = getF0Map();
271        makeGenToGenMap(result, *map, f0m);
272}
273
274void Model::rebuild(bool buildm)
275{
276        autobuildmaps = buildm;
277        clear();
278        build();
279}
280
281void Model::initMap()
282{
283        if (!map) map = new MultiMap();
284        else map->clear();
285}
286
287void Model::initF0Map()
288{
289        if (!f0map) f0map = new MultiMap();
290        else f0map->clear();
291}
292
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
308void Model::build()
309{
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())
316        {
317                buildstatus = invalid;
318                if (convmap) delete convmap;
319                return;
320        }
321        SString f0txt = f0geno.getGenes();
322        buildstatus = building; // was: open();
323        if (autobuildmaps)
324        {
325                partmappingchanged = 0;
326                initMap();
327                initF0Map();
328        }
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++)
334        {
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               
353                if (autobuildmaps)
354                {
355                        frommap.clear();
356                        frommap.add(lastpos, pos - 1);
357                }
358                mh.reset();
359                if (addFromString(type, excluding_prefix, lnum, autobuildmaps ? (&frommap) : 0) == -1)
360                {
361                        buildstatus = invalid;
362                        f0errorposition = lastpos;
363                        if (convmap) delete convmap;
364                        return;
365                }
366                if (mh.getWarningCount())
367                {
368                        if (f0warnposition < 0) f0warnposition = lastpos;
369                }
370                lastpos = pos;
371        }
372        mh.disable();
373        close();
374        if (convmap)
375        {
376                *f0map = *map;
377                if (geno.getFormat() != '0')
378                {
379                        MultiMap tmp;
380                        tmp.addCombined(*convmap, getMap());
381                        *map = tmp;
382                }
383                delete convmap;
384        }
385}
386
387const MultiMap& Model::getF0Map()
388{
389        if (!f0map)
390        {
391                f0map = new MultiMap();
392                makeGeno(f0geno, f0map);
393                f0genoknown = 1;
394        }
395        return *f0map;
396}
397
398Geno Model::rawGeno()
399{
400        Geno tmpgen;
401        makeGeno(tmpgen);
402        return tmpgen;
403}
404
405void Model::makeGeno(Geno &g, MultiMap *map, bool handle_defaults)
406{
407        if ((buildstatus != valid) && (buildstatus != building))
408        {
409                g = Geno(0, 0, 0, "invalid model");
410                return;
411        }
412
413        SString gen;
414
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);
420
421        static Part defaultpart;
422        static Joint defaultjoint;
423        static Neuro defaultneuro;
424        static Model defaultmodel;
425        static NeuroConn defaultconn;
426        //static NeuroItem defaultneuroitem;
427
428        Part *p;
429        Joint *j;
430        Neuro *n;
431        int i;
432        int len;
433        int a, b;
434        //NeuroItem *ni;
435
436        SString mod_props;
437        modelparam.select(this);
438        modelparam.saveSingleLine(mod_props, handle_defaults ? &defaultmodel : NULL, true, !handle_defaults);
439        if (mod_props.len() > 1) //are there any non-default values? ("\n" is empty)
440        {
441                gen += "m:";
442                gen += mod_props;
443        }
444
445        for (i = 0; p = (Part*)parts(i); i++)
446        {
447                partparam.select(p);
448                len = gen.len();
449                gen += "p:";
450                partparam.saveSingleLine(gen, handle_defaults ? &defaultpart : NULL, true, !handle_defaults);
451                if (map)
452                        map->add(len, gen.len() - 1, partToMap(i));
453        }
454        for (i = 0; j = (Joint*)joints(i); i++)
455        {
456                jointparam.select(j);
457                len = gen.len();
458                jointparam.setParamTab(j->usedelta ? f0_joint_paramtab : f0_nodeltajoint_paramtab);
459                gen += "j:";
460                jointparam.saveSingleLine(gen, handle_defaults ? &defaultjoint : NULL, true, !handle_defaults);
461                if (map)
462                        map->add(len, gen.len() - 1, jointToMap(i));
463        }
464        for (i = 0; n = (Neuro*)neurons(i); i++)
465        {
466                neuroparam.select(n);
467                len = gen.len();
468                gen += "n:";
469                neuroparam.saveSingleLine(gen, handle_defaults ? &defaultneuro : NULL, true, !handle_defaults);
470                if (map)
471                        map->add(len, gen.len() - 1, neuroToMap(i));
472        }
473        for (a = 0; a < neurons.size(); a++)
474        { // inputs
475                n = (Neuro*)neurons(a);
476                //      if ((n->getInputCount()==1)&&(n->getInput(0).refno <= n->refno))
477                //              continue; // already done with Neuro::conn_refno
478
479                for (b = 0; b < n->getInputCount(); b++)
480                {
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");
487
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
491
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:";
500                        connparam.saveSingleLine(gen, handle_defaults ? &defaultconn : NULL, true, !handle_defaults);
501                        if (map)
502                                map->add(len, gen.len() - 1, neuroToMap(n->refno));
503                }
504        }
505        g = Geno(gen.c_str(), '0');
506}
507
508//////////////
509
510void Model::open()
511{
512        if (buildstatus == building) return;
513        buildstatus = building;
514        modelfromgenotype = 0;
515        partmappingchanged = 0;
516        f0genoknown = 0;
517        delMap();
518}
519
520void Model::checkpoint()
521{}
522
523void Model::setGeno(const Geno& newgeno)
524{
525        geno = newgeno;
526}
527
528void Model::clearMap()
529{
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();
540}
541
542int Model::close(bool building_live_model)
543{
544        if (buildstatus != building)
545                logPrintf("Model", "close", LOG_WARN, "Unexpected close() - no open()");
546        if (internalcheck(building_live_model ? LIVE_CHECK : FINAL_CHECK) > 0)
547        {
548                buildstatus = valid;
549
550                if (partmappingchanged)
551                {
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));
564                }
565        }
566        else
567                buildstatus = invalid;
568
569        return (buildstatus == valid);
570}
571
572int Model::validate()
573{
574        return internalcheck(EDITING_CHECK);
575}
576
577Pt3D Model::whereDelta(const Part& start, const Pt3D& rot, const Pt3D& delta)
578{
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;
587}
588
589int Model::addFromString(ItemType item_type, const SString &singleline, const MultiRange* srcrange)
590{
591        return addFromString(item_type,singleline, 0, srcrange);
592}
593
594int Model::addFromString(ItemType item_type, const SString &singleline, int line_num, const MultiRange* srcrange)
595{
596        SString error_message;
597        int result = addFromStringNoLog(item_type, singleline, error_message, srcrange);
598        if (result < 0)
599        {
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());
606        }
607        return result;
608}
609
610int Model::addFromStringNoLog(ItemType item_type, const SString &line, SString& error_message, const MultiRange* srcrange)
611{
612        error_message = SString::empty();
613        ParamInterface::LoadOptions opts;
614        switch(item_type)
615                {
616                case PartType:
617                {
618                Param partparam(f0_part_paramtab);
619                Part *p = new Part();
620                partparam.select(p);
621                partparam.load(ParamInterface::FormatSingleLine, line, &opts);
622                if (opts.parse_failed) { delete p; error_message = "Invalid 'p:'"; return -1; }
623                p->o.rotate(p->rot);
624                parts += p;
625                p->owner = this;
626                if (srcrange) p->setMapping(*srcrange);
627                return getPartCount() - 1;
628                }
629
630                case ModelType:
631                {
632                Param modelparam(f0_model_paramtab);
633                modelparam.select(this);
634                modelparam.load(ParamInterface::FormatSingleLine, line, &opts);
635                if (opts.parse_failed) { error_message = "Invalid 'm:'"; return -1; }
636                return 0;
637                }
638
639                case JointType:
640                {
641                Param jointparam(f0_joint_paramtab);
642                Joint *j = new Joint();
643                jointparam.select(j);
644                j->owner = this;
645                jointparam.load(ParamInterface::FormatSingleLine, line, &opts);
646                if (opts.parse_failed) { delete j; error_message = "Invalid 'j:'"; return -1; }
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()))))
650                {
651                        addJoint(j);
652                        if ((j->d.x != JOINT_DELTA_MARKER) || (j->d.y != JOINT_DELTA_MARKER) || (j->d.z != JOINT_DELTA_MARKER))
653                        {
654                                j->useDelta(1);
655                                j->resetDeltaMarkers();
656                        }
657                        j->attachToParts(j->p1_refno, j->p2_refno);
658                        if (srcrange) j->setMapping(*srcrange);
659                        return j->refno;
660                }
661                else
662                {
663                        error_message = SString::sprintf("Invalid reference to Part #%d", p1_ok ? j->p1_refno : j->p2_refno);
664                        delete j;
665                        return -1;
666                }
667                }
668
669                case NeuronType:
670                {
671                Param neuroparam(f0_neuro_paramtab);
672                Neuro *nu = new Neuro();
673                neuroparam.select(nu);
674                neuroparam.load(ParamInterface::FormatSingleLine, line, &opts);
675                if (opts.parse_failed) { delete nu; error_message = "Invalid 'n:'"; return -1; }
676                {
677                        // default class for unparented units: standard neuron
678                        if (nu->getClassName().len() == 0) nu->setClassName("N");
679                }
680                /*
681                        if (nu->conn_refno>=0) // input specified...
682                        {
683                        if (nu->conn_refno >= getNeuroCount()) // and it's illegal
684                        {
685                        delete nu;
686                        return -1;
687                        }
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)
695                {
696                        nu->attachToPart(nu->part_refno);
697                        if (nu->part == NULL)
698                        {
699                                error_message = SString::sprintf("Invalid reference to Part #%d", nu->part_refno); delete nu; return -1;
700                        }
701                }
702                if (nu->joint_refno >= 0)
703                {
704                        nu->attachToJoint(nu->joint_refno);
705                        if (nu->joint == NULL)
706                        {
707                                error_message = SString::sprintf("Invalid reference to Joint #%d", nu->joint_refno); delete nu; return -1;
708                        }
709                }
710                if (srcrange) nu->setMapping(*srcrange);
711                // todo: check part/joint ref#
712                {
713                        neurons += nu;
714                        return neurons.size() - 1;
715                }
716                }
717
718                case NeuronConnectionType:                     
719                {
720                Param ncparam(f0_neuroconn_paramtab);
721                NeuroConn c;
722                ncparam.select(&c);
723                ncparam.load(ParamInterface::FormatSingleLine, line, &opts);
724                if (opts.parse_failed) { error_message = "Invalid 'c:'"; return -1; }
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()))))
728                {
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;
735                }
736                error_message = SString::sprintf("Invalid reference to Neuro #%d", n1_ok ? c.n2_refno : c.n1_refno);
737                return -1;
738                }
739
740                case UnknownType: //handled by addFromString for uniform error handling
741                        return -1;
742                }
743        return -1;
744}
745
746
747/////////////
748
749/** change the sequence of neuro units
750        and fix references in "-" objects (n-n connections)  */
751void Model::moveNeuro(int oldpos, int newpos)
752{
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
758}
759
760////////////
761
762void Model::updateNeuroRefno()
763{
764        for (int i = 0; i < neurons.size(); i++)
765        {
766                Neuro* n = (Neuro*)neurons(i);
767                n->refno = i;
768        }
769}
770
771#define VALIDMINMAX(var,template,field) \
772if (var -> field < getMin ## template () . field) \
773        { var->field= getMin ## template () . field; \
774        logPrintf("Model","internalCheck",LOG_WARN,# field " too small in " # template " #%d (adjusted)",i);} \
775else if (var -> field > getMax ## template () . field) \
776        { var->field= getMax ## template ()  . field; \
777        logPrintf("Model","internalCheck",LOG_WARN,# field " too big in " # template " #%d (adjusted)",i);}
778
779#define LINKFLAG 0x8000000
780
781void Model::updateRefno()
782{
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;
787}
788
789SString Model::nameForErrors() const
790{
791        if (geno.getName().len()>0)
792                return SString::sprintf(" in '%s'", geno.getName().c_str());
793        return SString::empty();
794}
795
796int Model::internalcheck(CheckType check)
797{
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
808        {
809                Pt3D bbmin = ((Part*)parts(0))->p, bbmax = bbmin;
810                for (i = 0; i < parts.size(); i++)
811                {
812                        p = (Part*)parts(i);
813                        p->owner = this;
814                        p->refno = i;
815                        if (checklevel > 0)
816                                p->mass = 0.0;
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)
820                        VALIDMINMAX(p, Part, size);
821                        VALIDMINMAX(p, Part, scale.x);
822                        VALIDMINMAX(p, Part, scale.y);
823                        VALIDMINMAX(p, Part, scale.z);
824                        VALIDMINMAX(p, Part, hollow);
825                        VALIDMINMAX(p, Part, density);
826                        VALIDMINMAX(p, Part, friction);
827                        VALIDMINMAX(p, Part, ingest);
828                        VALIDMINMAX(p, Part, assim);
829                        VALIDMINMAX(p, Part, vsize);
830                        VALIDMINMAX(p, Part, vcolor.x);
831                        VALIDMINMAX(p, Part, vcolor.y);
832                        VALIDMINMAX(p, Part, vcolor.z);
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)
841                                shape = (p->shape == Part::SHAPE_BALL_AND_STICK) ? SHAPE_BALL_AND_STICK : SHAPE_SOLIDS;
842                        else if (shape != SHAPE_ILLEGAL)
843                        {
844                                if ((p->shape == Part::SHAPE_BALL_AND_STICK) ^ (shape == SHAPE_BALL_AND_STICK))
845                                {
846                                        shape = SHAPE_ILLEGAL;
847                                        logPrintf("Model", "internalCheck", LOG_WARN, "Inconsistent part shapes (mixed ball-and-stick and solids shape types)%s", nameForErrors().c_str());
848                                }
849                        }
850                }
851                size = bbmax - bbmin;
852                for (i = 0; i < joints.size(); i++)
853                {
854                        j = (Joint*)joints(i);
855                        // VALIDMINMAX are managed manually when adding joint properties in f0-def!
856                        // (could be made dynamic but not really worth the effort)
857                        VALIDMINMAX(j, Joint, stamina);
858                        VALIDMINMAX(j, Joint, stif);
859                        VALIDMINMAX(j, Joint, rotstif);
860                        VALIDMINMAX(p, Part, vcolor.x);
861                        VALIDMINMAX(p, Part, vcolor.y);
862                        VALIDMINMAX(p, Part, vcolor.z);
863                        j->refno = i;
864                        j->owner = this;
865                        if (j->part1 && j->part2 && (j->part1 != j->part2))
866                        {
867                                j->p1_refno = j->part1->refno;
868                                j->p2_refno = j->part2->refno;
869                                if (checklevel > 0)
870                                {
871                                        j->part1->mass += 1.0;
872                                        j->part2->mass += 1.0;
873                                }
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
894                                        if (check != EDITING_CHECK)
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                                }
909                                if (check != LIVE_CHECK)
910                                {
911                                        if (j->shape != Joint::SHAPE_FIXED)
912                                        {
913                                                if (j->d() > getMaxJoint().d.x)
914                                                {
915                                                        ret = 0;
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());
917                                                }
918                                        }
919                                }
920                        }
921                        else
922                        {
923                                logPrintf("Model", "internalCheck", LOG_ERROR, "Illegal part references in Joint #%d%s", i, nameForErrors().c_str());
924                                ret = 0;
925                        }
926                        if (shape != SHAPE_ILLEGAL)
927                        {
928                                if ((j->shape == Joint::SHAPE_BALL_AND_STICK) ^ (shape == SHAPE_BALL_AND_STICK))
929                                {
930                                        shape = SHAPE_ILLEGAL;
931                                        logPrintf("Model", "internalCheck", LOG_WARN, "Inconsistent joint shapes (mixed old and new shapes)%s", nameForErrors().c_str());
932                                }
933                        }
934                }
935        }
936
937        updateNeuroRefno(); // valid refno is important for n-n connections check (later)
938
939        for (i = 0; i < neurons.size(); i++)
940        {
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;
944        }
945
946        if (parts.size() && (checklevel > 0))
947        {
948                for (i = 0; i < parts.size(); i++)
949                {
950                        p = (Part*)parts(i);
951                        if (p->mass <= 0.001)
952                                p->mass = 1.0;
953                        p->flags &= ~LINKFLAG;
954                }
955                getPart(0)->flags |= LINKFLAG;
956                int change = 1;
957                while (change)
958                {
959                        change = 0;
960                        for (i = 0; i < joints.size(); i++)
961                        {
962                                j = (Joint*)joints(i);
963                                if (j->part1->flags&LINKFLAG)
964                                {
965                                        if (!(j->part2->flags&LINKFLAG))
966                                        {
967                                                change = 1;
968                                                j->part2->flags |= LINKFLAG;
969                                        }
970                                }
971                                else
972                                        if (j->part2->flags&LINKFLAG)
973                                        {
974                                        if (!(j->part1->flags&LINKFLAG))
975                                        {
976                                                change = 1;
977                                                j->part1->flags |= LINKFLAG;
978                                        }
979                                        }
980                        }
981                }
982                for (i = 0; i < parts.size(); i++)
983                {
984                        p = (Part*)parts(i);
985                        if (!(p->flags&LINKFLAG))
986                        {
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;
990                        }
991                }
992        }
993
994        for (i = 0; i < joints.size(); i++)
995        {
996                j = (Joint*)joints(i);
997                if (j->p1_refno == j->p2_refno)
998                {
999                        logPrintf("Model", "internalCheck", LOG_ERROR, "Illegal self connection, Joint #%d%s", i, nameForErrors().c_str());
1000                        ret = 0;
1001                        break;
1002                }
1003                for (k = i + 1; k < joints.size(); k++)
1004                {
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)))
1008                        {
1009                                logPrintf("Model", "internalCheck", LOG_ERROR, "Illegal duplicate Joint #%d and Joint #%d%s", i, k, nameForErrors().c_str());
1010                                ret = 0;
1011                                break;
1012                        }
1013                }
1014        }
1015        if (shape == SHAPE_ILLEGAL)
1016                ret = 0;
1017        return ret;
1018}
1019
1020/////////////
1021
1022int Model::getErrorPosition(bool includingwarnings)
1023{
1024        return includingwarnings ?
1025                ((f0errorposition >= 0) ? f0errorposition : f0warnposition)
1026                :
1027                f0errorposition;
1028}
1029
1030const Geno& Model::getGeno() const
1031{
1032        return geno;
1033}
1034
1035const Geno Model::getF0Geno()
1036{
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)
1042        {
1043                if (autobuildmaps)
1044                {
1045                        initF0Map();
1046                        makeGeno(f0geno, f0map);
1047                }
1048                else
1049                {
1050                        delF0Map();
1051                        makeGeno(f0geno);
1052                }
1053                f0genoknown = 1;
1054        }
1055        return f0geno;
1056}
1057
1058int Model::getPartCount() const
1059{
1060        return parts.size();
1061}
1062
1063Part* Model::getPart(int i) const
1064{
1065        return ((Part*)parts(i));
1066}
1067
1068int Model::getJointCount() const
1069{
1070        return joints.size();
1071}
1072
1073Joint* Model::getJoint(int i) const
1074{
1075        return ((Joint*)joints(i));
1076}
1077
1078int Model::findJoints(SList& result, const Part* part)
1079{
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;
1086}
1087
1088int Model::findNeuro(Neuro* n)
1089{
1090        return neurons.find(n);
1091}
1092
1093int Model::findPart(Part* p)
1094{
1095        return parts.find(p);
1096}
1097
1098int Model::findJoint(Joint* j)
1099{
1100        return joints.find(j);
1101}
1102
1103int Model::findJoint(Part *p1, Part *p2)
1104{
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;
1109}
1110
1111///////////////////////
1112
1113int Model::getNeuroCount() const
1114{
1115        return neurons.size();
1116}
1117
1118Neuro* Model::getNeuro(int i) const
1119{
1120        return (Neuro*)neurons(i);
1121}
1122
1123int Model::getConnectionCount() const
1124{
1125        int n = 0;
1126        for (int i = 0; i < getNeuroCount(); i++)
1127                n += getNeuro(i)->getInputCount();
1128        return n;
1129}
1130
1131int Model::findNeuros(SList& result,
1132        const char* classname, const Part* part, const Joint* joint)
1133{
1134        Neuro *nu;
1135        SString cn(classname);
1136        int n0 = result.size();
1137        for (int i = 0; nu = (Neuro*)neurons(i); i++)
1138        {
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;
1146        }
1147        return result.size() - n0;
1148}
1149
1150///////////////////
1151
1152void Model::disturb(double amount)
1153{
1154        int i;
1155        if (amount <= 0) return;
1156        for (i = 0; i < parts.size(); i++)
1157        {
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;
1162        }
1163        for (i = 0; i < joints.size(); i++)
1164        {
1165                Joint *j = getJoint(i);
1166                Pt3D tmpp(j->part2->p);
1167                tmpp -= j->part1->p;
1168                j->d = j->part2->o.revTransform(tmpp);
1169        }
1170}
1171
1172void Model::move(const Pt3D& shift)
1173{
1174        FOREACH(Part*, p, parts)
1175                p->p += shift;
1176}
1177
1178void Model::rotate(const Orient& rotation)
1179{
1180        FOREACH(Part*, p, parts)
1181        {
1182                p->p = rotation.transform(p->p);
1183                p->setOrient(rotation.transform(p->o));
1184        }
1185}
1186
1187void Model::buildUsingSolidShapeTypes(const Model& src_ballandstick_shapes, Part::Shape use_shape, float thickness)
1188{
1189        for (int i = 0; i < src_ballandstick_shapes.getJointCount(); i++)
1190        {
1191                Joint *oj = src_ballandstick_shapes.getJoint(i);
1192                Part *p = addNewPart(use_shape);
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;
1200        }
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                }
1210        for (int i = 0; i < src_ballandstick_shapes.getPartCount(); i++)
1211        {
1212                Part *op = src_ballandstick_shapes.getPart(i);
1213                for (int j = 0; j < src_ballandstick_shapes.getJointCount(); j++)
1214                {
1215                        Joint *oj = src_ballandstick_shapes.getJoint(j);
1216                        if ((oj->part1 == op) || (oj->part2 == op))
1217                        {
1218                                for (int j2 = j + 1; j2 < src_ballandstick_shapes.getJointCount(); j2++)
1219                                {
1220                                        Joint *oj2 = src_ballandstick_shapes.getJoint(j2);
1221                                        if ((oj2->part1 == op) || (oj2->part2 == op))
1222                                        {
1223                                                addNewJoint(getPart(j), getPart(j2), Joint::SHAPE_FIXED);
1224                                        }
1225                                }
1226                                break;
1227                        }
1228                }
1229        }
1230}
1231
1232SolidsShapeTypeModel::SolidsShapeTypeModel(Model& m, Part::Shape use_shape, float thickness)
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
1250//////////////////////
1251
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(); } };
1258
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.