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

Last change on this file since 1336 was 1336, checked in by Maciej Komosinski, 2 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
Line 
1// This file is a part of Framsticks SDK.  http://www.framsticks.com/
2// Copyright (C) 1999-2025  Maciej Komosinski and Szymon Ulatowski.
3// See LICENSE.txt for details.
4
5#include "f9_conv.h"
6#include <frams/model/model.h>
7#include <string.h>
8
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.
10
11GenoConv_f90::GenoConv_f90()
12{
13        name = "Turtle3D-ortho encoding";
14        in_format = '9';
15        out_format = '0';
16        mapsupport = 1;
17}
18
19
20const char* turtle_commands_f9 = "LRBFDU";
21int turtle_commands_f9_count = 6; //keep in sync, must equal strlen(turtle_commands_f9)
22
23//const char* turtle_commandsX_f9="-+0000";
24//const char* turtle_commandsY_f9="00-+00";
25//const char* turtle_commandsZ_f9="0000-+";
26
27SString GenoConv_f90::convert(SString &in, MultiMap *map, bool using_checkpoints)
28{
29        vector<XYZ_LOC> vertices;
30        XYZ_LOC current;
31        Model m;
32        m.open(using_checkpoints);
33        int recently_added = addSegment(m, 0, vertices, current, 0xDead);
34        for (int i = 0; i < in.length(); i++)
35        {
36                char command = in[i];
37                char *ptr = strchr((char*)turtle_commands_f9, command);
38                if (ptr)
39                {
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
45                        current.add(delta);
46                        recently_added = addSegment(m, i, vertices, current, recently_added);
47                        m.checkpoint();
48                }
49        }
50#ifdef APPLY_DETERMINISTIC_BODY_NOISE
51        perturbPartLocations(m);
52#endif
53        setColors(m, recently_added);
54        m.close();
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
57        if (map != NULL)
58                m.getCurrentToF0Map(*map);
59        return m.getF0Geno().getGenes();
60}
61
62int GenoConv_f90::addSegment(Model &m, int genenr, vector<XYZ_LOC> &vertices, const XYZ_LOC &new_vertex, int recently_added)
63{
64        if (vertices.size() < 1) //empty model?
65        {
66                return addNewVertex(m, vertices, new_vertex);
67        }
68        else
69        {
70                int vertex_here = findVertexAt(vertices, new_vertex);
71                if (vertex_here < 0) //need to create a new Part
72                {
73                        vertex_here = addNewVertex(m, vertices, new_vertex);
74                } //else there already exists a Part in new_vertex; new Joint may or may not be needed
75                Part *p1 = m.getPart(recently_added);
76                Part *p2 = m.getPart(vertex_here);
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));
88                return vertex_here;
89        }
90}
91
92int GenoConv_f90::findVertexAt(vector<XYZ_LOC> &vertices, const XYZ_LOC &vertex)
93{
94        for (int i = 0; i < (int)vertices.size(); i++)
95                if (vertices[i].same_coordinates(vertex)) return i;
96        return -1;
97}
98
99
100int GenoConv_f90::addNewVertex(Model &m, vector<XYZ_LOC> &vertices, const XYZ_LOC &new_vertex)
101{
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;
106        m.addPart(p);
107
108        vertices.push_back(new_vertex);
109        return int(vertices.size()) - 1;
110}
111
112double mix(int *colortab, int maxind, double ind)
113{
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;
121        double v = indpre == indpost ? v1 : d2 * v1 + d1 * v2; //d1+d2==1
122        return v;
123}
124
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
126{
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;
135
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
148        {
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                }
172        }
173
174        int parts_count = m.getPartCount();
175        if (OLD)
176        {
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                }
188        }
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
202}
203
204void GenoConv_f90::perturbPartLocations(Model &m) //deterministic "body noise", see APPLY_DETERMINISTIC_BODY_NOISE
205{
206        for (int i = 0; i < m.getPartCount(); i++)
207        {
208                Part *p = m.getPart(i);
209                Pt3D noise(
210                        ((i + 1) % 10) - 4.5,
211                        ((3 * i + 5) % 10) - 4.5,
212                        ((7 * i + 2) % 10) - 4.5
213                ); //-4.5 .. 4.5 in each axis
214                p->p += noise / 1000;
215        }
216}
Note: See TracBrowser for help on using the repository browser.