source: cpp/frams/genetics/fH/fH_general.cpp @ 780

Last change on this file since 780 was 780, checked in by Maciej Komosinski, 6 years ago

Added sources for genetic encodings fB, fH, fL

File size: 27.4 KB
Line 
1#include <string>
2#include <limits>
3#include <algorithm>
4#include <frams/util/multirange.h>
5#include <utility>
6#include "fH_general.h"
7
8using namespace std;
9#undef max //this macro is not needed here and it clashes with numeric_limits<>::max()
10
11
12
13// Methods for loading handles
14
15const char *fH_part_names[PART_PROPS_COUNT] = { "dn", "fr", "ing", "as" };
16
17const char *fH_joint_names[JOINT_PROPS_COUNT] = { "stif", "rotstif", "stam" };
18
19void fH_Handle::loadProperties(Param par)
20{
21        // loading values for vectors
22        for (int i = 0; i < dimensions; i++)
23        {
24                first[i] = par.getDouble(i);
25                second[i] = par.getDouble(dimensions + i);
26        }
27        obj = par.getSelected();
28}
29
30void fH_Builder::addHandle(fH_Handle *handle)
31{
32        switch (handle->type)
33        {
34        case fHBodyType::JOINT:
35                sticks.push_back((fH_StickHandle*)handle);
36                break;
37        case fHBodyType::NEURON:
38                neurons.push_back((fH_NeuronHandle*)handle);
39                break;
40        case fHBodyType::CONNECTION:
41                connections.push_back((fH_ConnectionHandle*)handle);
42                break;
43        }
44}
45
46// Methods for saving properties of handles in params
47
48void fH_Handle::saveProperties(Param &par)
49{
50        par.select(obj);
51        for (int i = 0; i < dimensions; i++)
52        {
53                par.setDouble(i, first[i]);
54                par.setDouble(dimensions + i, second[i]);
55        }
56}
57
58// Destructor of Builder
59
60fH_Builder::~fH_Builder()
61{
62        for (fH_StickHandle *obj : sticks)
63        {
64                delete obj;
65        }
66        sticks.clear();
67        for (fH_NeuronHandle *obj : neurons)
68        {
69                delete obj;
70        }
71        neurons.clear();
72        for (fH_ConnectionHandle *obj : connections)
73        {
74                delete obj;
75        }
76        connections.clear();
77
78        if (stickparamtab) ParamObject::freeParamTab(stickparamtab);
79        if (neuronparamtab) ParamObject::freeParamTab(neuronparamtab);
80        if (connectionparamtab) ParamObject::freeParamTab(connectionparamtab);
81
82}
83
84// Methods for parsing genotype
85
86void fH_Builder::prepareParams()
87{
88        for (int i = 0; i < dimensions; i++) // preparing first vector fields
89        {
90                string x = "x";
91                x += to_string(i);
92                stickmut.addProperty(NULL, x.c_str(), HANDLE_VECTOR_TYPE, x.c_str(), "", PARAM_CANOMITNAME, 0, -1);
93                neuronmut.addProperty(NULL, x.c_str(), HANDLE_VECTOR_TYPE, x.c_str(), "", PARAM_CANOMITNAME, 0, -1);
94                connectionmut.addProperty(NULL, x.c_str(), HANDLE_VECTOR_TYPE, x.c_str(), "", PARAM_CANOMITNAME, 0, -1);
95
96        }
97        for (int i = 0; i < dimensions; i++) // preparing second vector fields
98        {
99                string y = "y";
100                y += to_string(i);
101                stickmut.addProperty(NULL, y.c_str(), HANDLE_VECTOR_TYPE, y.c_str(), "", PARAM_CANOMITNAME, 0, -1);
102                neuronmut.addProperty(NULL, y.c_str(), HANDLE_VECTOR_TYPE, y.c_str(), "", PARAM_CANOMITNAME, 0, -1);
103                connectionmut.addProperty(NULL, y.c_str(), HANDLE_VECTOR_TYPE, y.c_str(), "", PARAM_CANOMITNAME, 0, -1);
104
105        }
106
107        Part p;
108        for (int i = 0; i < PART_PROPS_COUNT; i++)
109        {
110                stickmut.addProperty(&p.properties().getParamTab()[p.properties().findId(fH_part_names[i]) + p.properties().getGroupCount()], -1);
111        }
112
113        Joint j;
114        for (int i = 0; i < JOINT_PROPS_COUNT; i++)
115        {
116                stickmut.addProperty(&j.properties().getParamTab()[j.properties().findId(fH_joint_names[i]) + j.properties().getGroupCount()], -1);
117        }
118        stickmut.addProperty(NULL, "l", STICKH_LENGTH_TYPE, "length", "", 0, 0, -1);
119
120        Neuro n;
121        neuronmut.addProperty(&n.properties().getParamTab()[n.properties().findId(PE_NEURO_DET) + n.properties().getGroupCount()], -1);
122
123        Param tmp(f0_neuroconn_paramtab, NULL);
124        connectionmut.addProperty(&tmp.getParamTab()[tmp.findId(PE_CONN_WEIGHT) + tmp.getGroupCount()], -1);
125
126        stickparamtab = ParamObject::makeParamTab((ParamInterface *)&stickmut, 0, 0, stickmut.firstMutableIndex());
127        neuronparamtab = ParamObject::makeParamTab((ParamInterface *)&neuronmut, 0, 0, neuronmut.firstMutableIndex());
128        connectionparamtab = ParamObject::makeParamTab((ParamInterface *)&connectionmut, 0, 0, connectionmut.firstMutableIndex());
129}
130
131int fH_Builder::processLine(SString line, int linenumber, int begin, int end)
132{
133        // Firstly, method determines if line describes joint, neuron or neural connection
134        // and prepares corresponding ParamTab
135        fH_Handle *handle = NULL;
136        ParamEntry *tab = NULL;
137        if (line.startsWith("j:")) //joint
138        {
139                handle = new fH_StickHandle(dimensions, begin, end);
140                tab = stickparamtab;
141        }
142        else if (line.startsWith("n:")) //neuron
143        {
144                handle = new fH_NeuronHandle(dimensions, begin, end);
145                tab = neuronparamtab;
146        }
147        else if (line.startsWith("c:")) //connection
148        {
149                handle = new fH_ConnectionHandle(dimensions, begin, end);
150                tab = connectionparamtab;
151        }
152        else // could not determine type of a handle
153        {
154                string message = "Cannot determine handle type at line:  " + to_string(linenumber);
155                logMessage("fH_Builder", "processLine", LOG_ERROR, message.c_str());
156                return begin;
157        }
158        line = line.substr(2); // skip of "j:", "c:" or "n:"
159
160        // Secondly, ParamObject for holding handle properties is created
161        void *obj = ParamObject::makeObject(tab);
162        Param par(tab, obj);
163        par.setDefault();
164        ParamInterface::LoadOptions opts;
165
166        // After preparing Param objects, vector values and body properties are parsed
167        par.load(ParamInterface::FormatSingleLine, line, &opts);
168
169        // If parsing failed, method writes error message and ends processing
170        if (opts.parse_failed)
171        {
172                string message = "Error in parsing handle parameters at line:  " + to_string(linenumber);
173                logMessage("fH_Builder", "processLine", LOG_ERROR, message.c_str());
174                delete handle;
175                ParamObject::freeObject(obj);
176                return begin;
177        }
178
179        // If parsing ended successfully, parsed properties are loaded into handle fields
180        handle->loadProperties(par);
181
182        // In the end, ready handle is stored in an appropriate vector
183        addHandle(handle);
184        return 0;
185}
186
187int fH_Builder::parseGenotype(SString genotype)
188{
189        // Firstly, number of dimensions is parsed
190        int pos = 0;
191        SString numdimensions;
192        genotype.getNextToken(pos, numdimensions, '\n');
193        if (!ExtValue::parseInt(numdimensions.c_str(), dimensions, true, false))
194        {
195                logMessage("fH_Builder", "parseGenotype", LOG_ERROR, "Could not parse number of dimensions");
196                return 1;
197        }
198        if (dimensions < 1)
199        {
200                logMessage("fH_Builder", "parseGenotype", LOG_ERROR, "Number of dimensions cannot be lower than 1");
201                return 1;
202        }
203        SString line;
204        int linenumber = 2;
205
206        // With known number of dimensions ParamTabs for handles are prepared
207        prepareParams();
208
209        // After preparing Builder for parsing, each line is processed with processLine
210        int lastpos = pos;
211        while (genotype.getNextToken(pos, line, '\n'))
212        {
213                if (line.len() > 0)
214                {
215                        int res = processLine(line, linenumber, lastpos, pos - 1);
216                        if (res != 0)
217                        {
218                                return res;
219                        }
220                }
221                lastpos = pos;
222                linenumber++;
223        }
224        if (sticks.size() == 0)
225        {
226                logMessage("fH_Builder", "parseGenotype", LOG_ERROR, "Genotype does not contain any stick");
227                return 1;
228        }
229        return 0;
230}
231
232// Distance calculations
233
234double fH_Handle::dist(vector<double> left, vector<double> right)
235{
236        double sum = 0;
237        for (unsigned int i = 0; i < left.size(); i++)
238        {
239                sum += (left[i] - right[i]) * (left[i] - right[i]);
240        }
241        return sqrt(sum);
242}
243
244vector<double> fH_Handle::getVectorsAverage()
245{
246        vector<double> result(dimensions, 0);
247        for (int i = 0; i < dimensions; i++)
248        {
249                result[i] = (first[i] + second[i]) / 2;
250        }
251        return result;
252}
253
254double fH_StickHandle::distance(fH_Handle *right)
255{
256        double distance = 0;
257        switch (right->type)
258        {
259        case fHBodyType::JOINT:
260                // distance is computed between second vector of current handle and first
261                // vector of second handle
262                distance = dist(second, right->first);
263                break;
264        case fHBodyType::NEURON:
265        {
266                // if neuron has to be connected to joint, then distance is calculated
267                // between averages of both handles
268                vector<double> avgs = getVectorsAverage();
269                vector<double> avgn = right->getVectorsAverage();
270                distance = dist(avgs, avgn);
271                break;
272        }
273        case fHBodyType::CONNECTION:
274                // it is impossible to calculate distance between Joint and Connection
275                return numeric_limits<double>::quiet_NaN();
276        }
277        return distance;
278}
279
280double fH_NeuronHandle::distance(fH_Handle *right)
281{
282        double distance = 0;
283        switch (right->type)
284        {
285        case fHBodyType::JOINT:
286        {
287                // if neuron has to be connected to joint, then distance is calculated
288                // between averages of both handles
289                vector<double> avgs = right->getVectorsAverage();
290                vector<double> avgn = getVectorsAverage();
291                distance = dist(avgs, avgn);
292                break;
293        }
294        case fHBodyType::CONNECTION:
295                // this calculation is meant for input neuron - it compares second vector
296                // of neuron and first vector of connection
297                distance = dist(second, right->first);
298                break;
299        case fHBodyType::NEURON:
300                // it is impossible to calculate distance between two Neurons
301                return numeric_limits<double>::quiet_NaN();
302        }
303        return distance;
304}
305
306double fH_NeuronHandle::distance(fH_StickHandle *right, bool first)
307{
308        vector<double> avgn = getVectorsAverage();
309        double distance = 0;
310        if (first)
311        {
312                distance = dist(avgn, right->firstparthandle);
313        }
314        else
315        {
316                distance = dist(avgn, right->secondparthandle);
317        }
318        return distance;
319}
320
321double fH_ConnectionHandle::distance(fH_Handle *right)
322{
323        double distance = 0;
324        switch (right->type)
325        {
326        case fHBodyType::NEURON:
327                // this calculation is meant for output neuron - it compares second vector
328                // of connection and first vector of neuron
329                distance = dist(second, right->first);
330                break;
331        case fHBodyType::JOINT:
332        case fHBodyType::CONNECTION:
333                // it is impossible to calculate distance between Connection and other
334                // Connection or Joint
335                return numeric_limits<double>::quiet_NaN();
336        }
337        return distance;
338}
339
340// Creature build functions
341
342Part * fH_StickHandle::createPart(ParamEntry *tab, std::vector<fH_StickHandle *> children, Model *model, bool createmapping)
343{
344        Param par(tab, obj);
345        double partprops[PART_PROPS_COUNT];
346        for (int i = 0; i < PART_PROPS_COUNT; i++)
347        {
348                partprops[i] = par.getDouble(2 * getDimensions() + i);
349        }
350
351        unsigned int stickscount = children.size() + 1;
352
353        MultiRange ranges;
354        ranges.add(begin, end);
355
356        for (fH_StickHandle *child : children)
357        {
358                par.select(child->obj);
359                for (int i = 0; i < PART_PROPS_COUNT; i++)
360                {
361                        partprops[i] += par.getDouble(2 * getDimensions() + i);
362                }
363                ranges.add(child->begin, child->end);
364        }
365
366        for (int i = 0; i < PART_PROPS_COUNT; i++)
367        {
368                partprops[i] /= stickscount;
369        }
370
371        Part *newpart = new Part();
372
373        model->addPart(newpart);
374
375        newpart->density = partprops[0];
376        newpart->friction = partprops[1];
377        newpart->ingest = partprops[2];
378        newpart->assim = partprops[3];
379
380        if (createmapping) newpart->addMapping(ranges);
381
382        return newpart;
383}
384
385Joint* fH_StickHandle::createJoint(ParamEntry *tab, Model *model, bool createmapping)
386{
387        Param par(tab, obj);
388        if (firstpart == NULL || secondpart == NULL)
389        {
390                return NULL;
391        }
392        Joint *newjoint = new Joint();
393
394        model->addJoint(newjoint);
395
396        newjoint->stif = par.getDoubleById("stif");
397        newjoint->rotstif = par.getDoubleById("rotstif");
398        newjoint->stamina = par.getDoubleById("stam");
399        newjoint->attachToParts(firstpart, secondpart);
400        if (createmapping) newjoint->addMapping(IRange(begin, end));
401        return newjoint;
402}
403
404void fH_Builder::buildBody()
405{
406        // stickconnections vector holds information about connections between sticks.
407        // Left side of pair should hold pointer to stick that is connected with second
408        // vector, and right side of pair should hold pointer to stick that is connected
409        // with first vector
410        stickconnections.clear();
411
412        // if body consists of single stick, just add it to body
413        if (sticks.size() == 1)
414        {
415                stickconnections.push_back(pair<fH_StickHandle *, fH_StickHandle *>(NULL, sticks[0]));
416                sticksorder.push_back(0);
417                return;
418        }
419
420        vector<bool> remainingsticks(sticks.size(), true);
421
422        // first we find two handles that have minimal distances between their second
423        // and first vector
424        fH_StickHandle *left = sticks[0];
425        fH_StickHandle *right = sticks[1];
426        double mindist = left->distance(right);
427        int leftid = 0;
428        int rightid = 1;
429        for (unsigned int i = 0; i < sticks.size(); i++)
430        {
431                for (unsigned int j = i + 1; j < sticks.size(); j++)
432                {
433                        double distance = sticks[i]->distance(sticks[j]);
434                        if (distance < mindist)
435                        {
436                                mindist = distance;
437                                left = sticks[i];
438                                right = sticks[j];
439                                leftid = i;
440                                rightid = j;
441                        }
442                        distance = sticks[j]->distance(sticks[i]);
443                        if (distance < mindist)
444                        {
445                                mindist = distance;
446                                left = sticks[j];
447                                right = sticks[i];
448                                leftid = j;
449                                rightid = i;
450                        }
451                }
452        }
453
454        // two found handles are the beginning of creature body
455        stickconnections.push_back(pair<fH_StickHandle *, fH_StickHandle *>(NULL, left));
456        stickconnections.push_back(pair<fH_StickHandle *, fH_StickHandle *>(left, right));
457
458        // after selecting two handles as beginning of body, they are marked as used
459        // in the list of remaining sticks
460        remainingsticks[leftid] = false;
461        remainingsticks[rightid] = false;
462
463        sticksorder.push_back(leftid);
464        sticksorder.push_back(rightid);
465
466        // next stick is selected by minimum distance between first vector of its handle
467        // and second vector of any existing StickHandle in body
468        int remaining = sticks.size() - 2;
469        while (remaining > 0)
470        {
471                leftid = -1;
472                rightid = -1;
473                mindist = numeric_limits<double>::max();
474                for (unsigned int i = 0; i < sticks.size(); i++)
475                {
476                        // if stick is not already in
477                        if (remainingsticks[i])
478                        {
479                                for (int stickid : sticksorder)
480                                {
481                                        double distance = sticks[stickid]->distance(sticks[i]);
482                                        if (distance < mindist)
483                                        {
484                                                mindist = distance;
485                                                leftid = stickid;
486                                                rightid = i;
487                                        }
488                                }
489                        }
490                }
491                stickconnections.push_back(pair<fH_StickHandle *, fH_StickHandle *>(sticks[leftid], sticks[rightid]));
492                remainingsticks[rightid] = false;
493                sticksorder.push_back(rightid);
494                remaining--;
495        }
496}
497
498int fH_Builder::developBrain(Model *model, bool createmapping)
499{
500        Param par(neuronparamtab, NULL);
501        // First of all, neurons are attached to body
502        for (fH_NeuronHandle *currneu : neurons)
503        {
504                par.select(currneu->obj);
505                // create Neuro object and set details
506                currneu->neuron = new Neuro();
507                SString det = par.getStringById("d");
508                if (det != "")
509                {
510                        currneu->neuron->setDetails(det);
511                }
512                else
513                {
514                        currneu->neuron->setDetails("N");
515                }
516
517                // get class of neuron. If class with given name does not exist - return error
518                NeuroClass *nclass = currneu->neuron->getClass();
519                if (!nclass)
520                {
521                        SString msg = "NeuroClass given in details \"";
522                        msg += det + "\" does not exist";
523                        logMessage("fH_Builder", "developBrain", LOG_ERROR, msg.c_str());
524                        return -1;
525                }
526                // add neuron to model -> required before attaching to body part
527                model->addNeuro(currneu->neuron);
528                if (nclass->getPreferredLocation() == 2) // attach to Joint
529                {
530                        // find stick that has closest average handle to average handle of
531                        // neuron
532                        double mindist = currneu->distance(sticks[0]);
533                        fH_StickHandle *minstick = sticks[0];
534                        for (unsigned int i = 1; i < sticks.size(); i++)
535                        {
536                                double distance = currneu->distance(sticks[i]);
537                                if (distance < mindist)
538                                {
539                                        mindist = distance;
540                                        minstick = sticks[i];
541                                }
542                        }
543                        currneu->neuron->attachToJoint(minstick->joint);
544                }
545                else if (nclass->getPreferredLocation() == 1) // attach to Part
546                {
547                        // in the beginning we take first part of first stick to calculate
548                        // distance between them as initial minimal distance
549                        double mindist = currneu->distance(sticks[0], true);
550                        Part *minpart = sticks[0]->firstpart;
551                        for (unsigned int i = 0; i < sticks.size(); i++)
552                        {
553                                // after this we take only second parts of following sticks to
554                                // avoid repetition (thats why we start from i = 0)
555                                double distance = currneu->distance(sticks[i], false);
556                                if (distance < mindist)
557                                {
558                                        mindist = distance;
559                                        minpart = sticks[i]->secondpart;
560                                }
561                        }
562                        currneu->neuron->attachToPart(minpart);
563                }
564                if (createmapping) currneu->neuron->addMapping(IRange(currneu->begin, currneu->end));
565                model->checkpoint();
566        }
567
568        par.setParamTab(connectionparamtab);
569        // Secondly, connections are created
570        for (fH_ConnectionHandle *currcon : connections)
571        {
572                par.select(currcon->obj);
573                // Connection is created as follows:
574                //   beginneu ---> endneu
575                // distance between beginneu and connection is calculated as distance
576                // between second handle of beginneu and first handle of connection.
577                // This is why calculation is written as beginneu->distance(currcon).
578                // In case of connection and endneu distance between them is calculated
579                // as distance between second handle of connection and first handle of
580                // endneu. This is why calculation is written as currcon->distance(endneu).
581
582                fH_NeuronHandle *beginneu = NULL;
583                double mindist = numeric_limits<double>::max();
584                // find beginning of connection
585                for (fH_NeuronHandle *neuron : neurons)
586                {
587                        // These method checked earlier if all neurons have valid classes.
588                        // If a neuron does not have output, then it's skipped from comparison.
589                        // Otherwise:
590                        if (neuron->neuron->getClass()->getPreferredOutput() > 0)
591                        {
592                                double distance = neuron->distance(currcon);
593                                if (distance < mindist)
594                                {
595                                        mindist = distance;
596                                        beginneu = neuron;
597                                }
598                        }
599                }
600                // if there was no neuron that could begin connection, then return warning
601                if (!beginneu)
602                {
603                        logMessage("fH_Builder", "developBrain", LOG_WARN, "There are no available neurons with outputs, connection could not be established");
604                        continue;
605                }
606
607                fH_NeuronHandle *endneu = NULL;
608                mindist = numeric_limits<double>::max();
609                // find ending of connection
610                for (fH_NeuronHandle *neuron : neurons)
611                {
612                        // Method checked earlier if all neurons have valid classes.
613                        // If neuron does not accept input or all inputs are already connected,
614                        // then it's skipped from comparison.
615                        // Otherwise:
616                        if (neuron->neuron->getClass()->getPreferredInputs() == -1 ||
617                                neuron->neuron->getClass()->getPreferredInputs() > neuron->neuron->getInputCount())
618                        {
619                                double distance = currcon->distance(neuron);
620                                if (distance < mindist)
621                                {
622                                        mindist = distance;
623                                        endneu = neuron;
624                                }
625                        }
626                }
627                // if there was no neuron that could end connection, then return warning
628                if (!endneu)
629                {
630                        logMessage("fH_Builder", "developBrain", LOG_WARN, "There are no available neurons with free inputs, connection could not be established");
631                        continue;
632                }
633                endneu->neuron->addInput(beginneu->neuron, par.getDoubleById("w"));
634                if (createmapping) endneu->neuron->addMapping(IRange(currcon->begin, currcon->end));
635                model->checkpoint();
636        }
637        return 0;
638}
639
640Pt3D fH_Builder::getNextDirection(int count, int number)
641{
642        // In order to get evenly distributed sticks coming from the same Part method
643        // uses algorithm for even distribution of points on a sphere. There are several
644        // methods to perform this, usually they are iterative. This method introduced
645        // below offers not fully accurate, yet quite satisfying results. This is
646        // RSZ method (Rakhmanov, Saff and Zhou method), with use of the golden angle.
647        // This method is based on distribution of points along spiral that covers sphere
648        // surface.
649
650        // Following method works partially on spherical coordinates (r and theta is used).
651        // The Z coordinate is from Cartesian coordinate system. The golden angle is used
652        // to "iterate" along spiral, while Z coordinate is used to move down the
653        // sphere.
654
655        double golden_angle = M_PI * (3.0 - sqrt(5));
656        double dz = 2.0 / (double)count;
657        double z = 1 - ((double)number + 0.5) * dz;
658        double r = sqrt(1 - z * z);
659        double theta = golden_angle * number;
660        Pt3D vec;
661        // In the end X and Y coordinates are calculated with current values of
662        // r and theta. Value z is already calculated
663        vec.x = r * cos(theta);
664        vec.y = r * sin(theta);
665        vec.z = z;
666        vec.normalize();
667        return vec;
668}
669
670Orient fH_Builder::getRotationMatrixToFitVector(Pt3D currdir, Pt3D expecteddir)
671{
672        Orient res;
673        // first method normalizes vectors for easy calculations
674        currdir.normalize();
675        expecteddir.normalize();
676        double c = currdir.dotProduct(expecteddir); // dot product of both vectors
677        // if the dot product of both vectors equals 0
678        if (c == 0)
679        {
680                res.x.x = -1;
681                res.x.y = 0;
682                res.x.z = 0;
683
684                res.y.x = 0;
685                res.y.y = -1;
686                res.y.z = 0;
687
688                res.z.x = 0;
689                res.z.y = 0;
690                res.z.z = -1;
691        }
692        Pt3D v = Pt3D(0); // cross product of both vectors
693        v.x = currdir.y * expecteddir.z - currdir.z * expecteddir.y;
694        v.y = currdir.z * expecteddir.x - currdir.x * expecteddir.z;
695        v.z = currdir.x * expecteddir.y - currdir.y * expecteddir.x;
696
697        // Rotation matrix that enables aligning currdir to expecteddir comes from
698        // following calculation
699        // R = I + [v]_x + ([v]_x)^2 / (1+c)
700        // where [v]_x is the skew-symmetric cross-product matrix of v
701        res.x.x = 1 - (v.y * v.y + v.z * v.z) / (1 + c);
702        res.x.y = v.z + (v.x * v.y) / (1 + c);
703        res.x.z = -v.y + (v.x * v.z) / (1 + c);
704        res.y.x = -v.z + (v.x * v.y) / (1 + c);
705        res.y.y = 1 - (v.x * v.x + v.z * v.z) / (1 + c);
706        res.y.z = v.x + (v.y * v.z) / (1 + c);
707        res.z.x = v.y + (v.x * v.z) / (1 + c);
708        res.z.y = -v.x + (v.y * v.z) / (1 + c);
709        res.z.z = 1 - (v.x * v.x + v.y * v.y) / (1 + c);
710
711        return res;
712}
713
714Model* fH_Builder::buildModel(bool using_checkpoints)
715{
716        Model *model = new Model();
717
718        // At first, floating sticks are connected
719        buildBody();
720
721        model->open(using_checkpoints);
722
723        // Secondly, parts and joints are created
724        // For every stick in body, starting with initial
725        Param par(stickparamtab, NULL);
726        for (int currid : sticksorder)
727        {
728                fH_StickHandle *currstick = sticks[currid];
729                fH_StickHandle *parent = NULL;
730                // find parent of current stick - it is first element of pair, in which
731                // current stick is second
732                for (pair<fH_StickHandle *, fH_StickHandle *> conn : stickconnections)
733                {
734                        if (conn.second == currstick)
735                        {
736                                parent = conn.first;
737                                break;
738                        }
739                }
740
741                // if parent is NULL, then create Part with current stick properties and
742                // location at (0,0,0)
743                if (!parent)
744                {
745                        vector<fH_StickHandle *> emptylist;
746                        Part *firstpart = currstick->createPart(stickparamtab, emptylist, model, createmapping);
747                        firstpart->p = Pt3D(0);
748                        currstick->firstpart = firstpart;
749                        currstick->firstparthandle = currstick->first; // this is used to calculate later distance between
750                        model->checkpoint();
751                }
752                else //otherwise first part of current stick is the second part of previous stick
753                {
754                        currstick->firstpart = parent->secondpart;
755                        currstick->firstparthandle = parent->secondparthandle;
756                }
757                // position of second part depends on two things
758                //  1. direction of previous joint
759                //  2. how many sticks are connected to the same parent
760                // default direction of growth (without parent) is (1,0,0)
761                Pt3D direction(1, 0, 0);
762                Pt3D secondposition(currstick->firstpart->p);
763                // if parent does exist, then determine how many sticks are connected to
764                // parent and distribute them evenly on a sphere surrounding second part
765                if (parent)
766                {
767                        // improved RSZ method creates vectors that starts in
768                        // center of sphere (which will act as shared part), so direction
769                        // calculated below should point from shared part to previous part
770                        // in order to perform proper aligning
771                        direction = parent->secondpart->p - parent->firstpart->p;
772                        direction.normalize();
773                        // determine how many sticks are connected to parent and when connection
774                        // between parent and current stick appear
775                        int count = 0;
776                        int id = -1;
777                        for (unsigned int i = 0; i < stickconnections.size(); i++)
778                        {
779                                if (stickconnections[i].first == parent)
780                                {
781                                        if (stickconnections[i].second == currstick)
782                                        {
783                                                id = count;
784                                        }
785                                        count++;
786                                }
787                        }
788                        if (id == -1)
789                        {
790                                logMessage("fH_Builder", "buildModel", LOG_ERROR, "Invalid behaviour");
791                                delete model;
792                                return NULL;
793                        }
794
795                        // if there is only one child, then don't change direction - continue
796                        // along axis of parent. Otherwise calculate direction of id-th stick
797                        // (that is currstick) with use of RSZ/Vogel method of distributing points
798                        // evenly on a sphere
799                        if (count > 1)
800                        {
801                                direction = parent->firstpart->p - parent->secondpart->p;
802                                direction.normalize();
803                                // there has to be count+1 directions, so method needs to generate
804                                // count+1 evenly distributed points on a sphere to make vectors
805                                // from point (0,0,0) to those points. First generated vector
806                                // will act as parent joint direction vector
807                                Pt3D sphere0direction = getNextDirection(count + 1, 0);
808
809                                // First generated vector needs to be aligned to parent vector
810                                Orient rotmatrix = getRotationMatrixToFitVector(sphere0direction, direction);
811
812                                // Calculation of direction from sphere for currstick
813                                direction = getNextDirection(count + 1, id + 1);
814                                // Rotation matrix aligning
815                                direction = rotmatrix.transform(direction);
816                                direction.normalize();
817                        }
818                }
819
820                // calculate second position
821                par.select(currstick->obj);
822                secondposition += direction * par.getDoubleById("l");
823
824                // find every stick connected to current stick in order to calculate second
825                // part properties
826                vector<fH_StickHandle *> children;
827                currstick->secondparthandle = currstick->second;
828                for (pair<fH_StickHandle *, fH_StickHandle *> conn : stickconnections)
829                {
830                        if (conn.first == currstick)
831                        {
832                                children.push_back(conn.second);
833                                for (int i = 0; i < dimensions; i++)
834                                {
835                                        currstick->secondparthandle[i] += conn.second->first[i];
836                                }
837                        }
838                }
839                // create part from current stick and other sticks connected to this part
840                Part *secondpart = currstick->createPart(stickparamtab, children, model, createmapping);
841                secondpart->p = secondposition;
842                currstick->secondpart = secondpart;
843                double count = (double)children.size() + 1;
844                for (int i = 0; i < dimensions; i++)
845                {
846                        currstick->secondparthandle[i] /= count;
847                }
848
849                //after creating second part connect two parts with joint
850                Joint * joint = currstick->createJoint(stickparamtab, model, createmapping);
851                if (!joint)
852                {
853                        logMessage("fH_Builder", "buildModel", LOG_ERROR, "Joint cannot be created");
854                        delete model;
855                        return NULL;
856
857                }
858                currstick->joint = joint;
859                model->checkpoint();
860        }
861        // after creating a body, attach neurons to body and link them according to
862        // connections
863        if (developBrain(model, createmapping) == -1)
864        {
865                delete model;
866                return NULL;
867        }
868        model->close();
869        return model;
870}
871
872int fH_Builder::removeNeuronsWithInvalidClasses()
873{
874        int count = neurons.size();
875        if (count == 0)
876        {
877                return 0;
878        }
879        vector<fH_NeuronHandle *>::iterator it = neurons.begin();
880        Param par(neuronparamtab, NULL);
881        while (it != neurons.end())
882        {
883                par.select((*it)->obj);
884                SString det = par.getStringById("d");
885                if (det == "")
886                {
887                        it++;
888                }
889                else
890                {
891                        Neuro *neu = new Neuro();
892                        neu->setDetails(det);
893                        if (neu->getClass())
894                        {
895                                it++;
896                        }
897                        else
898                        {
899                                fH_NeuronHandle *tmp = (*it);
900                                it = neurons.erase(it);
901                                delete tmp;
902                        }
903                        delete neu;
904                }
905
906        }
907        return count - neurons.size();
908}
909
910SString fH_Builder::toString()
911{
912        SString result = "";
913        result += to_string(dimensions).c_str();
914        result += "\n";
915        // first method stringifies parts
916        Param par(stickparamtab, NULL);
917        void *def = ParamObject::makeObject(stickparamtab);
918        par.select(def);
919        par.setDefault();
920        for (fH_StickHandle *currstick : sticks)
921        {
922                currstick->saveProperties(par);
923                SString props;
924                par.saveSingleLine(props, def, true, false);
925                result += "j:";
926                result += props;
927        }
928        ParamObject::freeObject(def);
929        par.setParamTab(neuronparamtab);
930        def = ParamObject::makeObject(neuronparamtab);
931        par.select(def);
932        par.setDefault();
933        for (fH_NeuronHandle *currneuron : neurons)
934        {
935                currneuron->saveProperties(par);
936                SString props;
937                par.saveSingleLine(props, def, true, false);
938                result += "n:";
939                result += props;
940        }
941        ParamObject::freeObject(def);
942        par.setParamTab(connectionparamtab);
943        def = ParamObject::makeObject(connectionparamtab);
944        par.select(def);
945        par.setDefault();
946        for (fH_ConnectionHandle *currconnection : connections)
947        {
948                currconnection->saveProperties(par);
949                SString props;
950                par.saveSingleLine(props, def, true, false);
951                result += "c:";
952                result += props;
953        }
954        ParamObject::freeObject(def);
955        return result;
956}
957
958ParamEntry* fH_Builder::getParamTab(fHBodyType type)
959{
960        switch (type)
961        {
962        case fHBodyType::JOINT:
963                return stickparamtab;
964                break;
965        case fHBodyType::NEURON:
966                return neuronparamtab;
967                break;
968        default:
969                return connectionparamtab;
970                break;
971        }
972}
Note: See TracBrowser for help on using the repository browser.