source: cpp/frams/genetics/f9/f9_conv.cpp @ 1336

Last change on this file since 1336 was 1336, checked in by Maciej Komosinski, 3 days ago

f9: the colors of the sticks now match the colors of letters for genes, making it easier to connect f9 genes with their phenotypic expressions

  • Property svn:eol-style set to native
File size: 7.0 KB
RevLine 
[286]1// This file is a part of Framsticks SDK.  http://www.framsticks.com/
[1336]2// Copyright (C) 1999-2025  Maciej Komosinski and Szymon Ulatowski.
[286]3// See LICENSE.txt for details.
[120]4
[779]5#include "f9_conv.h"
[120]6#include <frams/model/model.h>
7#include <string.h>
8
[259]9#define APPLY_DETERMINISTIC_BODY_NOISE //this genetic representation easily produces perfectly vertical sticks that would stay upright forever in simulation. In most cases such infinite perfection is not desired, so we make the construct less perfect by perturbing its coordinates.
[120]10
[139]11GenoConv_f90::GenoConv_f90()
[120]12{
13        name = "Turtle3D-ortho encoding";
[168]14        in_format = '9';
[120]15        out_format = '0';
[259]16        mapsupport = 1;
[120]17}
18
19
[643]20const char* turtle_commands_f9 = "LRBFDU";
[1157]21int turtle_commands_f9_count = 6; //keep in sync, must equal strlen(turtle_commands_f9)
[120]22
23//const char* turtle_commandsX_f9="-+0000";
24//const char* turtle_commandsY_f9="00-+00";
25//const char* turtle_commandsZ_f9="0000-+";
26
[736]27SString GenoConv_f90::convert(SString &in, MultiMap *map, bool using_checkpoints)
[120]28{
29        vector<XYZ_LOC> vertices;
30        XYZ_LOC current;
31        Model m;
[740]32        m.open(using_checkpoints);
[259]33        int recently_added = addSegment(m, 0, vertices, current, 0xDead);
[973]34        for (int i = 0; i < in.length(); i++)
[120]35        {
[168]36                char command = in[i];
37                char *ptr = strchr((char*)turtle_commands_f9, command);
[120]38                if (ptr)
39                {
[168]40                        int delta[] = { 0, 0, 0 };
41                        int pos = ptr - turtle_commands_f9;
42                        int axis = pos / 2;
43                        int dir = pos % 2;
44                        (*(delta + axis)) += dir * 2 - 1; //+1 or -1 in the given axis
[120]45                        current.add(delta);
[259]46                        recently_added = addSegment(m, i, vertices, current, recently_added);
[740]47                        m.checkpoint();
[120]48                }
49        }
50#ifdef APPLY_DETERMINISTIC_BODY_NOISE
51        perturbPartLocations(m);
52#endif
[664]53        setColors(m, recently_added);
[120]54        m.close();
[157]55        if (m.getPartCount() < 2) //only one part <=> there were no valid turtle commands in the input genotype
56                return ""; //so we return an invalid f0 genotype
[259]57        if (map != NULL)
58                m.getCurrentToF0Map(*map);
[534]59        return m.getF0Geno().getGenes();
[120]60}
61
[259]62int GenoConv_f90::addSegment(Model &m, int genenr, vector<XYZ_LOC> &vertices, const XYZ_LOC &new_vertex, int recently_added)
[120]63{
[168]64        if (vertices.size() < 1) //empty model?
[120]65        {
[168]66                return addNewVertex(m, vertices, new_vertex);
67        }
68        else
[120]69        {
[168]70                int vertex_here = findVertexAt(vertices, new_vertex);
71                if (vertex_here < 0) //need to create a new Part
[120]72                {
[168]73                        vertex_here = addNewVertex(m, vertices, new_vertex);
[120]74                } //else there already exists a Part in new_vertex; new Joint may or may not be needed
[168]75                Part *p1 = m.getPart(recently_added);
76                Part *p2 = m.getPart(vertex_here);
[259]77                p1->addMapping(MultiRange(genenr, genenr));
78                p2->addMapping(MultiRange(genenr, genenr));
79
80                int j12 = m.findJoint(p1, p2);
81                int j21 = m.findJoint(p2, p1);
82                if (j12 >= 0)
83                        m.getJoint(j12)->addMapping(MultiRange(genenr, genenr));
84                else if (j21 >= 0)
85                        m.getJoint(j21)->addMapping(MultiRange(genenr, genenr));
86                else //both j12<0 and j21<0. New Joint needed. Should always happen if we just created a new Part (vertex_here was <0)
87                        m.addNewJoint(p1, p2)->addMapping(MultiRange(genenr, genenr));
[120]88                return vertex_here;
89        }
90}
91
[168]92int GenoConv_f90::findVertexAt(vector<XYZ_LOC> &vertices, const XYZ_LOC &vertex)
[120]93{
[1287]94        for (int i = 0; i < (int)vertices.size(); i++)
[120]95                if (vertices[i].same_coordinates(vertex)) return i;
96        return -1;
97}
98
99
[168]100int GenoConv_f90::addNewVertex(Model &m, vector<XYZ_LOC> &vertices, const XYZ_LOC &new_vertex)
[120]101{
[168]102        Part *p = new Part;
103        p->p.x = new_vertex.x;
104        p->p.y = new_vertex.y;
105        p->p.z = new_vertex.z;
[120]106        m.addPart(p);
107
108        vertices.push_back(new_vertex);
[1108]109        return int(vertices.size()) - 1;
[120]110}
111
[168]112double mix(int *colortab, int maxind, double ind)
[120]113{
[168]114        int indpre = (int)ind;
115        int indpost = indpre + 1;
116        if (indpost > maxind) indpost = maxind;
117        int v1 = colortab[indpre];
118        int v2 = colortab[indpost];
119        double d1 = ind - indpre;
120        double d2 = indpost - ind;
[973]121        double v = indpre == indpost ? v1 : d2 * v1 + d1 * v2; //d1+d2==1
[120]122        return v;
123}
124
[664]125void GenoConv_f90::setColors(Model &m, int last_added_part) //sets fixed (independent from genes) colors and widths on a model, purely for aesthetic purposes
[120]126{
[1336]127        static const bool OLD = false; //old "rainbow" hue gradient
128        if (OLD)
129        {
130                //a rainbow on Joints: from the first one red, through middle green, to blue or violet - last
131                static int r[] = { 1, 1, 0, 0, 0, 1 };
132                static int g[] = { 0, 1, 1, 1, 0, 0 };
133                static int b[] = { 0, 0, 0, 1, 1, 1 };
134                int maxind = int(std::size(r)) - 1;
[120]135
[1336]136                int joints_count = m.getJointCount();
137                for (int i = 0; i < joints_count; i++)
138                {
139                        Joint *j = m.getJoint(i);
140                        double x = joints_count < 2 ? 0 : (double)i / (joints_count - 1); //0..1, position in the rainbow
141                        double ind = x * maxind;
142                        j->vcolor.x = mix(r, maxind, ind);
143                        j->vcolor.y = mix(g, maxind, ind);
144                        j->vcolor.z = mix(b, maxind, ind);
145                }
146        }
147        else
[120]148        {
[1336]149                int joints_count = m.getJointCount();
150                for (int i = 0; i < joints_count; i++)
151                {
152                        Joint *j = m.getJoint(i);
153                        Pt3D d = j->part2->p - j->part1->p; //dx,dy,dz
154                        double ax = fabs(d.x), ay = fabs(d.y), az = fabs(d.z);
155                        // Pairs of colors should be easy to associate as "one family" at a glance, but different so that we use all main six parts of the spectrum.
156                        // Colors are slightly brightened to make them more pastel/plastic; extreme saturation pure red=1,0,0 or blue 0,0,1 do not look good.
157                        // Find the longest axis (i.e., recover the information from the genotype: was this joint created by LR, BF, or DU?)
158                        if ((ax > ay) && (ax > az)) // x
159                        {
160                                j->vcolor = d.x < 0 ? Pt3D(1, 0.2, 0.2) : Pt3D(1, 0.2, 0.6); // red, purple red
161                        }
162                        else
163                                if ((ay > ax) && (ay > az)) // y
164                                {
165                                        j->vcolor = d.y < 0 ? Pt3D(0.2, 1, 0.2) : Pt3D(0.7, 1, 0.2); //green, yellowish green
166                                }
167                                else // z
168                                {
169                                        j->vcolor = d.z < 0 ? Pt3D(0.2, 0.2, 1) : Pt3D(0.4, 0.9, 1); //blue, cyanish blue
170                                }
171                }
[120]172        }
173
[168]174        int parts_count = m.getPartCount();
[1336]175        if (OLD)
[120]176        {
[1336]177                SList jlist;
178                for (int i = 0; i < parts_count; i++)
179                {
180                        Part *p = m.getPart(i);
181                        jlist.clear();
182                        int count = m.findJoints(jlist, p);
183                        Pt3D averagecolor(0, 0, 0); //Parts will get averaged colors from all attached Joints
184                        FOREACH(Joint*, j, jlist)
185                                averagecolor += j->vcolor;
186                        p->vcolor = averagecolor / count;
187                }
[120]188        }
[1336]189        else
190        {
191                //Parts will get gray colors from darker to brighter, according to their order of creation.
192                for (int i = 0; i < parts_count; i++)
193                {
194                        Part *p = m.getPart(i);
195                        p->vcolor.x = p->vcolor.y = p->vcolor.z = 0.3 + 0.4 * i / (parts_count - 1); //0.3..0.7, so first (black) and last (white) stand out more
196                }
197        }
198        //The first Part will be black, the last Part will be white - a visual aid for easier matching of the genotype and the corresponding phenotype.
199        if (!OLD)
200                m.getPart(0)->vcolor = Pt3D(0, 0, 0); //mark first Part black - not attractive in OLD sweet and positive color scheme
201        m.getPart(last_added_part)->vcolor = Pt3D(1, 1, 1); //mark last Part white
[120]202}
203
[139]204void GenoConv_f90::perturbPartLocations(Model &m) //deterministic "body noise", see APPLY_DETERMINISTIC_BODY_NOISE
[120]205{
[168]206        for (int i = 0; i < m.getPartCount(); i++)
[120]207        {
[168]208                Part *p = m.getPart(i);
[120]209                Pt3D noise(
[168]210                        ((i + 1) % 10) - 4.5,
211                        ((3 * i + 5) % 10) - 4.5,
212                        ((7 * i + 2) % 10) - 4.5
[973]213                ); //-4.5 .. 4.5 in each axis
[168]214                p->p += noise / 1000;
[120]215        }
216}
Note: See TracBrowser for help on using the repository browser.