1 | // This file is a part of Framsticks SDK. http://www.framsticks.com/ |
---|
2 | // Copyright (C) 1999-2023 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 | |
---|
11 | GenoConv_f90::GenoConv_f90() |
---|
12 | { |
---|
13 | name = "Turtle3D-ortho encoding"; |
---|
14 | in_format = '9'; |
---|
15 | out_format = '0'; |
---|
16 | mapsupport = 1; |
---|
17 | } |
---|
18 | |
---|
19 | |
---|
20 | const char* turtle_commands_f9 = "LRBFDU"; |
---|
21 | int 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 | |
---|
27 | SString 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 | |
---|
62 | int 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 | |
---|
92 | int 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 | |
---|
100 | int 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 | |
---|
112 | double 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 | |
---|
125 | void 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 | //a rainbow on Joints: from the first one red, through middle green, to blue or violet - last |
---|
128 | static int r[] = { 1, 1, 0, 0, 0, 1 }; |
---|
129 | static int g[] = { 0, 1, 1, 1, 0, 0 }; |
---|
130 | static int b[] = { 0, 0, 0, 1, 1, 1 }; |
---|
131 | int maxind = int(std::size(r)) - 1; |
---|
132 | |
---|
133 | int joints_count = m.getJointCount(); |
---|
134 | for (int i = 0; i < joints_count; i++) |
---|
135 | { |
---|
136 | Joint *j = m.getJoint(i); |
---|
137 | double x = joints_count < 2 ? 0 : (double)i / (joints_count - 1); //0..1, postion in the rainbow |
---|
138 | double ind = x * maxind; |
---|
139 | j->vcolor.x = mix(r, maxind, ind); |
---|
140 | j->vcolor.y = mix(g, maxind, ind); |
---|
141 | j->vcolor.z = mix(b, maxind, ind); |
---|
142 | } |
---|
143 | |
---|
144 | int parts_count = m.getPartCount(); |
---|
145 | SList jlist; |
---|
146 | for (int i = 0; i < parts_count; i++) |
---|
147 | { |
---|
148 | Part *p = m.getPart(i); |
---|
149 | jlist.clear(); |
---|
150 | int count = m.findJoints(jlist, p); |
---|
151 | Pt3D averagecolor(0, 0, 0); //Parts will get averaged colors from all attached Joints |
---|
152 | FOREACH(Joint*, j, jlist) |
---|
153 | averagecolor += j->vcolor; |
---|
154 | p->vcolor = averagecolor / count; |
---|
155 | } |
---|
156 | //m.getPart(0)->vcolor = Pt3D(0, 0, 0); //mark first Part black - a visual aid for easier editing |
---|
157 | m.getPart(last_added_part)->vcolor = Pt3D(1, 1, 1); //mark last Part white - a visual aid for easier editing |
---|
158 | } |
---|
159 | |
---|
160 | void GenoConv_f90::perturbPartLocations(Model &m) //deterministic "body noise", see APPLY_DETERMINISTIC_BODY_NOISE |
---|
161 | { |
---|
162 | for (int i = 0; i < m.getPartCount(); i++) |
---|
163 | { |
---|
164 | Part *p = m.getPart(i); |
---|
165 | Pt3D noise( |
---|
166 | ((i + 1) % 10) - 4.5, |
---|
167 | ((3 * i + 5) % 10) - 4.5, |
---|
168 | ((7 * i + 2) % 10) - 4.5 |
---|
169 | ); //-4.5 .. 4.5 in each axis |
---|
170 | p->p += noise / 1000; |
---|
171 | } |
---|
172 | } |
---|