Pre-generating SVG point coordinates with C++

We could use C++ to pre-generate SVG point coordinates for multiple simple n-gons, where n is a small number and then subsequently use JavaScript to animate them.

The following code describes how this can be done:

#include <iostream> #include <cmath> #include <vector> #include <cstring> using namespace std; int main() { int center_offset = 20, points_radius = 3, subdivisions = 3; float line_stroke = 1.0, center_x = 300, center_y = 200; string color = "#000"; vector<string> colors { "#AEA0C1", "#EAE20E", "#5B99DB", "#8CC45B", "#21C1CE", "#29DBDD", "#EAE20E", "#6DD1E3", "#C1D5FF" }; for(int n = 3; n < 12; n++) { int z = n - 3; color = colors[z]; vector<vector<float>> points; vector<float> v(2); // x, y cout << "<g class='group'>" << endl; cout << "\t<g id='points_group" << (z+1) << "' fill='" << color << "'>" << endl; for (float angle = 0, i = 0; angle < 360; angle += 360.0f / subdivisions, i++) { const float radians = angle * M_PI / 180; float xpos = center_x + center_offset * sin(radians); float ypos = center_y + center_offset * cos(radians); cout << "\t\t<circle cx='" << xpos << "' cy='" << ypos << "' r='" << points_radius << "' />" << endl; v[0] = xpos; v[1] = ypos; points.push_back(v); } // Insert the starting point at the end points.push_back(points[0]); cout << "\t</g>" << endl; cout << "\t<g id='lines_group" << (z+1) << "' fill='transparent' stroke='" << color << "' stroke-width='" << line_stroke << "'>" << endl; int sz =points.size(); for(int k = 1; k < sz + 1; k++) { if(k < sz) { vector<float> prev = points[k-1]; vector<float> next = points[k]; cout << "\t\t<line x1='" << prev[0] << "' y1='" << prev[1] << "' x2='" << next[0] << "' y2='" << next[1] << "' />" << endl; } } cout << "\t</g>" << endl; cout << "</g>" << endl; center_offset += 20; subdivisions += 1; line_stroke += 0.4; } return 0; }

For the largest n-gon, we obtain the following fragment:

<g class='group'> <g id='points_group9' fill='#C1D5FF'> <circle cx='300' cy='380' r='3' /> <circle cx='397.315' cy='351.426' r='3' /> <circle cx='463.734' cy='274.775' r='3' /> <circle cx='478.168' cy='174.383' r='3' /> <circle cx='436.035' cy='82.1251' r='3' /> <circle cx='350.712' cy='27.2913' r='3' /> <circle cx='249.288' cy='27.2913' r='3' /> <circle cx='163.965' cy='82.125' r='3' /> <circle cx='121.832' cy='174.383' r='3' /> <circle cx='136.266' cy='274.775' r='3' /> <circle cx='202.684' cy='351.426' r='3' /> <circle cx='300' cy='380' r='3' /> </g> <g id='lines_group9' fill='transparent' stroke='#C1D5FF' stroke-width='4.2'> <line x1='300' y1='380' x2='397.315' y2='351.426' /> <line x1='397.315' y1='351.426' x2='463.734' y2='274.775' /> <line x1='463.734' y1='274.775' x2='478.168' y2='174.383' /> <line x1='478.168' y1='174.383' x2='436.035' y2='82.1251' /> <line x1='436.035' y1='82.1251' x2='350.712' y2='27.2913' /> <line x1='350.712' y1='27.2913' x2='249.288' y2='27.2913' /> <line x1='249.288' y1='27.2913' x2='163.965' y2='82.125' /> <line x1='163.965' y1='82.125' x2='121.832' y2='174.383' /> <line x1='121.832' y1='174.383' x2='136.266' y2='274.775' /> <line x1='136.266' y1='274.775' x2='202.684' y2='351.426' /> <line x1='202.684' y1='351.426' x2='300' y2='380' /> <line x1='300' y1='380' x2='300' y2='380' /> </g> </g>

Having clearly labeled groups allows us to find things more easily, especially when the SVG output becomes large. Another purpose groups serve is to reduce our code, since we can define some properties (like fill, stroke etc.) directly on them, without having to do this for each child element individually. If you want to see the full SVG output, view the source code of this page.

Once all fragments for all n-gons are in place, we can adress the individual groups with JavaScript:

function t(what) {console.log(what);} function id(what) {return document.getElementById(what);} var button = id('button'), group = null, angle = 0, angle_diffs = [0,0,0,0,0,0,0,0,0], paused = false, anim_id = null, log = Math.log, sqrt = Math.sqrt, groups = document.querySelectorAll('.group'); function transform_string(angle) { var center_x = 300, center_y = 200; return 'translate({center_x} {center_y}) rotate({angle}) translate(-{center_x} -{center_y})' .replace(/{center_x}/g, center_x) .replace(/{center_y}/g, center_y) .replace('{angle}', angle); } for(var i = 0; i < 9; i++) { angle_diffs[i] = i*log(i+1); } function update() { if(paused) {return;} for(var i = 0; i < 9; i++) { group = groups[i]; group.setAttribute('transform', transform_string(angle)); angle += angle_diffs[i]; } angle %= 360; requestAnimationFrame(update); } button.onclick = function() { if(this.innerHTML == 'Play') { this.innerHTML = 'Pause'; paused = false; } else { this.innerHTML = 'Play'; paused = true; } requestAnimationFrame(update); };

We select all elements having a class name "group" and ensure that when the mouse is clicked they are rotated with varying angles (defined in the angle_diffs array). For the rotation to work properly, we use the move-rotate-move idea, where the second move is in direction opposite to the first. With requestAnimationFrame we repeatedly execute the update function, so that we get a smooth animation. Here is the result: