1 /* BurrTools
2  *
3  * BurrTools is the legal property of its developers, whose
4  * names are listed in the COPYRIGHT file, which is included
5  * within the source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11 
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16 
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20  */
21 #include "voxel_2.h"
22 
23 #include "puzzle.h"
24 
25 #include "../halfedge/polyhedron.h"
26 
27 #include "math.h"
28 
29 /* this file contains the mesh generation part of voxel_2. This is so big for the spheres
30  * that I didn't want to put it into the normal file
31  */
32 
33 #define Epsilon 1.0e-6
34 
35 /* makes a sphere */
36 
37 /* These are the points where the spheres touch (called touch point),
38  * corresponding to the faces of a rhombic dodecahedron.
39  * The order must be the same as in neighbor calculation.
40  */
41 static int connectionPoints[12][3] = {
42   {-1,-1, 0}, {-1,+1, 0}, {+1,-1, 0}, {+1,+1, 0},
43   {-1, 0,-1}, {-1, 0,+1}, {+1, 0,-1}, {+1, 0,+1},
44   { 0,-1,-1}, { 0,-1,+1}, { 0,+1,-1}, { 0,+1,+1}
45 };
46 
47 /* Six points surrounding each touch point that
48  * correspond (roughly) to the connecting cylinder.
49  * they are all on a circle around the touch point but not equally space, only roughly equally
50  *
51  * As the touch point surrounding circles do have their maximal radius the circles touch, that
52  * is where those point given below are.
53  *
54  * We do not really give the real point but only a vector that is a direction seen from the center
55  * of the sphere, when you scale that vector to the radius of the sphere you will end up at the points
56  *
57  * As those points are exactly at the middle between 2 touching points all we need to do is take the average
58  * of the corresponding 2 touching points, the comment behind a line shows which 2 touching points are taken
59  *
60  * For the remaining 2 of the 6 points the other point we take the main axis to do the averaging with, this
61  * is not really on the circle but only in the direction where we want the point to be. It works anyway because
62  * before using a point we shift coordinates until they are on the circle
63  *
64  * if you cut the sphere in half so that a touching point is on the cutting plane and then connect center of
65  * the sphere with the 2 points on the circle of the cut sphere where the touching circle lies you will get
66  * an equilateral triangle, to the angle of the circle is 60 degree
67  */
68 static int holeTouchPoints[12][6][3] = {
69   {
70     {-1+0, -1-1, 0},
71     {-1+0, -1-1, 0+1}, // 0+9
72     {-1-1, -1+0, 0+1}, // 0+5
73     {-1-1, -1+0, 0},
74     {-1-1, -1+0, 0-1}, // 0+4
75     {-1+0, -1-1, 0-1}, // 0+8
76   },{
77     {-1+0, +1+1, 0},
78     {-1+0, +1+1, 0-1}, // 1+10
79     {-1-1, +1+0, 0-1}, // 1+4
80     {-1-1, +1+0, 0},
81     {-1-1, +1+0, 0+1}, // 1+5
82     {-1+0, +1+1, 0+1}, // 1+11
83   },{
84     {+1+1, -1+0, 0+1}, // 2+7
85     {+1+0, -1-1, 0+1}, // 2+9
86     {+1+0, -1-1, 0},
87     {+1+0, -1-1, 0-1}, // 2+8
88     {+1+1, -1+0, 0-1}, // 2+6
89     {+1+1, -1+0, 0}
90   },{
91     {+1+1, +1+0, 0-1}, // 3+6
92     {+1+0, +1+1, 0-1}, // 3+10
93     {+1+0, +1+1, 0},
94     {+1+0, +1+1, 0+1}, // 3+11
95     {+1+1, +1+0, 0+1}, // 3+7
96     {+1+1, +1+0, 0}
97   },{
98     {-1+0, 0-1, -1-1}, // 4+8
99     {-1-1, 0-1, -1+0}, // 4+0
100     {-1-1, 0, -1+0},
101     {-1-1, 0+1, -1+0}, // 4+1
102     {-1+0, 0+1, -1-1}, // 4+10
103     {-1+0, 0, -1-1}
104   },{
105     {-1-1, 0-1, +1+0}, // 5+0
106     {-1+0, 0-1, +1+1}, // 5+9
107     {-1+0, 0, +1+1},
108     {-1+0, 0+1, +1+1}, // 5+11
109     {-1-1, 0+1, +1+0}, // 5+1
110     {-1-1, 0, +1+0}
111   },{
112     {+1+1, 0-1, -1+0}, // 6+2
113     {+1+0, 0-1, -1-1}, // 6+8
114     {+1+0, 0, -1-1},
115     {+1+0, 0+1, -1-1}, // 6+10
116     {+1+1, 0+1, -1+0}, // 6+3
117     {+1+1, 0, -1+0}
118   },{
119     {+1+1, 0, +1+0},
120     {+1+1, 0+1, +1+0}, // 7+3
121     {+1+0, 0+1, +1+1}, // 7+11
122     {+1+0, 0, +1+1},
123     {+1+0, 0-1, +1+1}, // 7+9
124     {+1+1, 0-1, +1+0} // 7+2
125   },{
126     {0, -1+0, -1-1},
127     {0+1, -1+0, -1-1}, // 8+6
128     {0+1, -1-1, -1+0}, // 8+2
129     {0, -1-1, -1+0},
130     {0-1, -1-1, -1+0}, // 8+0
131     {0-1, -1+0, -1-1}, // 8+4
132   },{
133     {0+1, -1-1, +1+0}, // 9+2
134     {0+1, -1+0, +1+1}, // 9+7
135     {0, -1+0, +1+1},
136     {0-1, -1+0, +1+1}, // 9+5
137     {0-1, -1-1, +1+0}, // 9+0
138     {0, -1-1, +1+0}
139   },{
140     {0, +1+0, -1-1},
141     {0-1, +1+0, -1-1}, // 10+4
142     {0-1, +1+1, -1+0}, // 10+1
143     {0, +1+1, -1+0},
144     {0+1, +1+1, -1+0}, // 10+3
145     {0+1, +1+0, -1-1} // 10+6
146   },{
147     {0-1, +1+1, +1+0}, // 11+1
148     {0-1, +1+0, +1+1}, // 11+5
149     {0, +1+0, +1+1},
150     {0+1, +1+0, +1+1}, // 11+7
151     {0+1, +1+1, +1+0}, // 11+3
152     {0, +1+1, +1+0},
153   }
154 };
155 
156 /* These correspond to 3 triangles around the eight obtuse
157  * vertices of a rhombic dodecahedron (3 faces meet).
158  *
159  * we have the point defined as above for the circles and one additional value
160  * that defines, what is the touching point that lies on the edge towards the next point in the list
161  * (or the first point for the last entry)
162  *
163  * important to note is that the point must be in counter clockwise order, because we need
164  * to generate all triangles in counter clockwise order so that we know where the normal points to
165  */
166 static int trianglePoints[8][3][3+1] = {
167   {
168     {+1+0, 0+1, +1+1,  7}, // 7+11
169     {+1+1, +1+0, 0+1,  3}, // 3+7
170     {0+1, +1+1, +1+0, 11} // 11+3
171   },{
172     {+1+1, +1+0, 0-1,  6}, // 3+6
173     {+1+0, 0+1, -1-1, 10}, // 6+10
174     {0+1, +1+1, -1+0,  3} // 10+3
175   },{
176     {+1+1, 0-1, -1+0,  2}, // 6+2
177     {+1+0, -1-1, 0-1,  8}, // 2+8
178     {0+1, -1+0, -1-1,  6} // 8+6
179   },{
180     {0-1, +1+0, -1-1,  4}, // 10+4
181     {-1-1, 0+1, -1+0,  1}, // 4+1
182     {-1+0, +1+1, 0-1, 10} // 1+10
183   },{
184     {-1+0, 0-1, -1-1,  8}, // 4+8
185     {0-1, -1-1, -1+0,  0}, // 8+0
186     {-1-1, -1+0, 0-1,  4} // 0+4
187   },{
188     {0-1, +1+0, +1+1, 11}, // 11+5
189     {-1+0, +1+1, 0+1,  1}, // 1+11
190     {-1-1, 0+1, +1+0,  5} // 5+1
191   },{
192     {-1+0, -1-1, 0+1,  9}, // 0+9
193     {0-1, -1+0, +1+1,  5}, // 9+5
194     {-1-1, 0-1, +1+0,  0} // 5+0
195   },{
196     {+1+1, -1+0, 0+1,  7}, // 2+7
197     {+1+0, 0-1, +1+1,  9}, // 7+9
198     {0+1, -1-1, +1+0,  2} // 9+2
199   }
200 };
201 
202 /* These correspond to 6 triangles around the six acute
203  * vertices of a rhombic dodecahedron (4 faces meet).
204  * Two of these triangles form a square, the other four
205  * share an edge with each side of the square.
206  * The shape of all 6 triangles form a star-shape.
207  *
208  * otherwise the same notes apply as for the trianglePoints array above
209  */
210 static int squarePoints[6][8][4] = {
211   {
212     {0, +1+0, -1-1,   10},
213     {0+1, +1+0, -1-1}, // 10+6
214     {+1+0, 0, -1-1,    6},
215     {+1+0, 0-1, -1-1}, // 6+8
216     {0, -1+0, -1-1,    8},
217     {0-1, -1+0, -1-1}, // 8+4
218     {-1+0, 0, -1-1,    4},
219     {-1+0, 0+1, -1-1}, // 4+10
220   },{
221     {-1-1, 0, -1+0,    4},
222     {-1-1, 0-1, -1+0}, // 4+0
223     {-1-1, -1+0, 0,    0},
224     {-1-1, -1+0, 0+1}, // 0+5
225     {-1-1, 0, +1+0,    5},
226     {-1-1, 0+1, +1+0}, // 5+1
227     {-1-1, +1+0, 0,    1},
228     {-1-1, +1+0, 0-1}, // 1+4
229   },{
230     {+1+0, -1-1, 0,    2},
231     {+1+0, -1-1, 0+1}, // 2+9
232     {0, -1-1, +1+0,    9},
233     {0-1, -1-1, +1+0}, // 9+0
234     {-1+0, -1-1, 0,    0},
235     {-1+0, -1-1, 0-1}, // 0+8
236     {0, -1-1, -1+0,    8},
237     {0+1, -1-1, -1+0}, // 8+2
238   },{
239     {+1+1, +1+0, 0,    3},
240     {+1+1, 0+1, +1+0}, // 7+3
241     {+1+1, 0, +1+0,    7},
242     {+1+1, -1+0, 0+1}, // 2+7
243     {+1+1, -1+0, 0,    2},
244     {+1+1, 0-1, -1+0}, // 6+2
245     {+1+1, 0, -1+0,    6},
246     {+1+1, +1+0, 0-1}, // 3+6
247   },{
248     {0, +1+0, +1+1,   11},
249     {0-1, +1+0, +1+1}, // 11+5
250     {-1+0, 0, +1+1,    5},
251     {-1+0, 0-1, +1+1}, // 5+9
252     {0, -1+0, +1+1,    9},
253     {0+1, -1+0, +1+1}, // 9+7
254     {+1+0, 0, +1+1,    7},
255     {+1+0, 0+1, +1+1}, // 7+11
256   },{
257     {0, +1+1, +1+0,   11},
258     {+1+0, +1+1, 0+1}, // 3+11
259     {+1+0, +1+1, 0,    3},
260     {0+1, +1+1, -1+0}, // 10+3
261     {0, +1+1, -1+0,   10},
262     {-1+0, +1+1, 0-1}, // 1+10
263     {-1+0, +1+1, 0,    1},
264     {0-1, +1+1, +1+0}, // 11+1
265   }
266 };
267 
268 // this structure contains all parameters that are used for
269 // the mesh generation, there are so many of them that it became
270 // unhandy to deliver them all as parameters to the different
271 // functions, instead we will just hand around a (const where possible) reference
272 typedef struct
273 {
274   float sphere_rad;    // Radius of the sphere (outer layer)
275   float inner_rad;     // Radius of the sphere (inner layer, radius of the hole)
276   float offset;        // offset by which the sphere radii are made smaller (subtraction)
277   bool outside;        // is currently drawn sphere the outer or inner sphere
278   float hole_diam;     // diameter of the hole between inside and outside > 0 round hole, <0 square hole
279   float connection_rad;// radius of the connection between spheres (1 = maximal possible radius)
280   float xc, yc, zc;    // center of the current sphere
281 
282   // some internal values used to calculate the curve that makes up the transition between
283   // the sphere surface and the connection cylinder
284   float curvX;
285   float curvY;
286   float curvRad;
287   float holeStart;
288   float lineEnd;
289   float curvEnd;
290 
291   // the vertex list, we add the triangles to
292   vertexList_c * vl;
293 
294   // the flags to use for the triangles
295   int flags;
296   int fb_index;
297   int fb_face;
298   int color;
299 
300 } genPar;
301 
302 
303 // add the triangle to the vertexList (and thus to the polyhedron)
304 //
outTriangle(float x1,float y1,float z1,float x2,float y2,float z2,float x3,float y3,float z3,genPar & par)305 static void outTriangle(
306         /* the 3 vertexes of the triangle */
307         float x1, float y1, float z1,
308         float x2, float y2, float z2,
309         float x3, float y3, float z3,
310         genPar & par)
311 {
312 
313   // when outputting the vertices, we round the values to
314   // a multiple of 1/256. This will help us properly align vertices up to a very high degree
315   // and the power of 2 factor will make exact division possible for the floats
316   const float roundfac = 256;
317 
318   x1 = roundf((par.xc+x1)*roundfac)/roundfac;
319   y1 = roundf((par.yc+y1)*roundfac)/roundfac;
320   z1 = roundf((par.zc+z1)*roundfac)/roundfac;
321 
322   x2 = roundf((par.xc+x2)*roundfac)/roundfac;
323   y2 = roundf((par.yc+y2)*roundfac)/roundfac;
324   z2 = roundf((par.zc+z2)*roundfac)/roundfac;
325 
326   x3 = roundf((par.xc+x3)*roundfac)/roundfac;
327   y3 = roundf((par.yc+y3)*roundfac)/roundfac;
328   z3 = roundf((par.zc+z3)*roundfac)/roundfac;
329 
330   std::vector<int> face3(3);
331   face3[0] = par.vl->get(x1, y1, z1);
332   face3[1] = par.vl->get(x2, y2, z2);
333   face3[2] = par.vl->get(x3, y3, z3);
334 
335   // don't add degenerate triangles
336   if (face3[0] == face3[1] || face3[0] == face3[2] || face3[1] == face3[2])
337     return;
338 
339   Face * f = par.vl->addFace(face3);
340 
341   f->_flags = par.flags;
342   f->_fb_index = par.fb_index;
343   f->_fb_face = par.fb_face;
344   f->_color = par.color;
345 }
346 
347 // normalize a vector
normalize(float * x,float * y,float * z)348 static void normalize(float *x, float *y, float *z)
349 {
350   float l = sqrt(*x * *x + *y * *y + *z * *z);
351   *x /= l;
352   *y /= l;
353   *z /= l;
354 }
355 
356 /* The given point is shifted away from the given touching
357  * point until it is on the 60 degree circle around that
358  * touching point.
359  * This gives the maximum size cylinder that can be used to
360  * connect the spheres. Such cylinders touch at the sphere
361  * surface, and have a diameter equal to the radius of the sphere.
362  */
shiftToHoleBorder(int nr,float * x,float * y,float * z)363 static void shiftToHoleBorder(int nr, float *x, float *y, float *z)
364 {
365   float l = sqrt(*x * *x + *y * *y + *z * *z);
366 
367   /* ok some names
368    * M is the middle of the sphere (which is at the origin btw.)
369    * T is out touching point
370    * P is the input point
371    */
372 
373   // calculate the angle at PMT
374   float a = acos((
375         connectionPoints[nr][0] * *x +
376         connectionPoints[nr][1] * *y +
377         connectionPoints[nr][2] * *z) / (l * sqrt(2)));
378 
379   // what the heck, I don't understand any more what I did in here... bahh next time
380   // I will need to comment it as soon as it works
381   float b = 105*M_PI/180 - a;
382 
383   float xl = sin(75*M_PI/180) / sin(b);
384 
385   *x *= xl/l;
386   *y *= xl/l;
387   *z *= xl/l;
388 
389   *x -= connectionPoints[nr][0]/sqrt(2);
390   *y -= connectionPoints[nr][1]/sqrt(2);
391   *z -= connectionPoints[nr][2]/sqrt(2);
392 
393   normalize(x, y, z);
394 
395   *x *= sin(30*M_PI/180)/sin(75*M_PI/180);
396   *y *= sin(30*M_PI/180)/sin(75*M_PI/180);
397   *z *= sin(30*M_PI/180)/sin(75*M_PI/180);
398 
399   *x += connectionPoints[nr][0]/sqrt(2);
400   *y += connectionPoints[nr][1]/sqrt(2);
401   *z += connectionPoints[nr][2]/sqrt(2);
402 }
403 
404 // recursively draw a spherical triangle on the perimeter of of
405 // a sphere, additionally it is possible to shift the edges of the
406 // triangle to that they are on the circle around the touching
407 // points of the sphere
drawTriangle(float x1,float y1,float z1,float x2,float y2,float z2,float x3,float y3,float z3,int edge12,int edge23,int edge31,int rec,genPar & par)408 static void drawTriangle(
409     // direction vectors for the 3 corners, the length is not important
410     // as the points will eventually be on the sphere
411     float x1, float y1, float z1,
412     float x2, float y2, float z2,
413     float x3, float y3, float z3,
414     // which touching circle to use for which edge, if negative no connection circle is used
415     // the recursion level tells how many times the triangle is subdivided multiplying
416     // the number of triangles by 4 each time
417     int edge12, int edge23, int edge31, int rec,
418     genPar & par)
419 {
420   // make the vectors unit length
421   normalize(&x1, &y1, &z1);
422   normalize(&x2, &y2, &z2);
423   normalize(&x3, &y3, &z3);
424 
425   if (rec > 0)
426   {
427     // find the middle of the 3 triangle sides
428     float x12 = (x1+x2)/2;
429     float y12 = (y1+y2)/2;
430     float z12 = (z1+z2)/2;
431 
432     float x23 = (x2+x3)/2;
433     float y23 = (y2+y3)/2;
434     float z23 = (z2+z3)/2;
435 
436     float x31 = (x3+x1)/2;
437     float y31 = (y3+y1)/2;
438     float z31 = (z3+z1)/2;
439 
440     // shift to the connection circles
441     if (edge12 >= 0) shiftToHoleBorder(edge12, &x12, &y12, &z12);
442     if (edge23 >= 0) shiftToHoleBorder(edge23, &x23, &y23, &z23);
443     if (edge31 >= 0) shiftToHoleBorder(edge31, &x31, &y31, &z31);
444 
445     // recursively output the triangles, we need to keep the edge flags only for
446     // the sides of the triangles that are still on the outside
447     drawTriangle(x1, y1, z1, x12, y12, z12, x31, y31, z31, edge12, -1, edge31, rec-1, par);
448     drawTriangle(x2, y2, z2, x23, y23, z23, x12, y12, z12, edge23, -1, edge12, rec-1, par);
449     drawTriangle(x3, y3, z3, x31, y31, z31, x23, y23, z23, edge31, -1, edge23, rec-1, par);
450     drawTriangle(x12, y12, z12, x23, y23, z23, x31, y31, z31, -1, -1, -1, rec-1, par);
451   }
452   else
453   {
454 
455     // final output, first find out the radius of
456     // the sphere we are on
457     float mult;
458 
459     if (par.outside)
460       mult = par.sphere_rad-par.offset;
461     else
462       mult = par.inner_rad-par.offset;
463 
464     // scale the triangle point onto the sphere
465     x1 *= mult;
466     y1 *= mult;
467     z1 *= mult;
468 
469     x2 *= mult;
470     y2 *= mult;
471     z2 *= mult;
472 
473     x3 *= mult;
474     y3 *= mult;
475     z3 *= mult;
476 
477     // if not outside reverse the order of points to flip surface
478     if (par.outside)
479       outTriangle(x1, y1, z1, x2, y2, z2, x3, y3, z3, par);
480     else
481       outTriangle(x1, y1, z1, x3, y3, z3, x2, y2, z2, par);
482   }
483 }
484 
485 
486 /* when drawing a connection this function calculates
487  * the radius (meaning how far is the surfae away from the center of the sphere
488  * for a certain angle from the touching point
489  *
490  * this value is the sphere radius for the outer rim and will increase as the
491  * angle gets smaller and we get closer to the point, where the cylinsers touch
492  * inside of the cylinder the value is not defined
493  */
radius(float a,genPar & par)494 static float radius(float a, genPar & par)
495 {
496   /* this is a bit like raytracing of the situation transformed to 2d
497    *   we are calculating 3 segments that construct the shape of the hole piece:
498    *     - line where 2 spheres tough
499    *     - circle of the curvature
500    *     - arc of the circle of the sphere
501    *   and then we intersect and find out on which piece we are
502    *   and calculate the distance...
503    */
504 
505   float m = tan(90*M_PI/180-a);
506 
507   float linex = par.connection_rad*(par.sphere_rad-par.offset)/2;
508   float liney = m*linex;
509 
510   float circlex = (par.sphere_rad-par.offset)*sin(a);
511   float circley = (par.sphere_rad-par.offset)*cos(a);
512 
513   float curvex, curvey;
514 
515   float ap = 1+m*m;
516   float bp = -2*par.curvX-2*m*par.curvY;
517   float cp = par.curvX*par.curvX+par.curvY*par.curvY-par.curvRad*par.curvRad;
518 
519   if (fabs(ap) < Epsilon)
520   {
521     curvex = curvey = 1000000;
522   }
523   else
524   {
525     float p = bp/ap;
526     float q = cp/ap;
527 
528     if (p*p/4-q >= 0)
529     {
530       curvex = -p/2-sqrt(p*p/4-q);
531       curvey = m*curvex;
532     }
533     else
534     {
535       curvex = curvey = 1000000;
536     }
537   }
538 
539   float px = linex;
540   float py = liney;
541 
542   if (liney < curvey && curvey < 10000)
543   {
544     px = curvex;
545     py = curvey;
546   }
547 
548   if (m*par.curvX < par.curvY)
549   {
550     px = circlex;
551     py = circley;
552   }
553 
554   return sqrt(px*px + py*py);
555 }
556 
557 
findPointOnArc(float xs,float ys,float zs,float xe,float ye,float ze,float arc,float * x,float * y,float * z)558 static void findPointOnArc(float xs, float ys, float zs, float xe, float ye, float ze, float arc,
559     float *x, float *y, float *z )
560 {
561   float ls = sqrt(xs*xs+ys*ys+zs*zs);
562   float le = sqrt(xe*xe+ye*ye+ze*ze);
563 
564   float a = (xs*xe+ys*ye+zs*ze)/(ls*le);
565 
566   if (arc >= a)
567   {
568     *x = xe;
569     *y = ye;
570     *z = ze;
571 
572     return;
573   }
574 
575   float ws = 0;
576   float we = 1;
577 
578   while (fabs(ws-we) > Epsilon)
579   {
580     float w = (ws+we)/2;
581 
582     *x = (1-w)*xs+w*xe;
583     *y = (1-w)*ys+w*ye;
584     *z = (1-w)*zs+w*ze;
585 
586     float l = sqrt(*x * *x + *y * *y + *z * *z);
587     a = acos((*x * xs + *y * ys + *z * zs)/(l*ls));
588 
589     if (arc < a)
590     {
591       we = w;
592     }
593     else
594     {
595       ws = w;
596     }
597   }
598 }
599 
drawHolePiece(int i,float start,float end,float x1,float y1,float z1,float x2,float y2,float z2,int rec,genPar & par)600 static void drawHolePiece(
601     int i,
602     float start, float end,
603     float x1, float y1, float z1,
604     float x2, float y2, float z2,
605     int rec,
606     genPar & par)
607 {
608   if (rec > 0 && fabs(start-end) > Epsilon)
609   {
610     drawHolePiece(i, start, (start+end)/2, x1, y1, z1, x2, y2, z2, rec-1, par);
611     drawHolePiece(i, (start+end)/2, end, x1, y1, z1, x2, y2, z2, rec-1, par);
612   }
613   else
614   {
615     float x1s, y1s, z1s, x2s, y2s, z2s, x1e, y1e, z1e, x2e, y2e, z2e;
616 
617     findPointOnArc(connectionPoints[i][0], connectionPoints[i][1], connectionPoints[i][2], x1, y1, z1, start, &x1s, &y1s, &z1s);
618     findPointOnArc(connectionPoints[i][0], connectionPoints[i][1], connectionPoints[i][2], x2, y2, z2, start, &x2s, &y2s, &z2s);
619     findPointOnArc(connectionPoints[i][0], connectionPoints[i][1], connectionPoints[i][2], x1, y1, z1, end, &x1e, &y1e, &z1e);
620     findPointOnArc(connectionPoints[i][0], connectionPoints[i][1], connectionPoints[i][2], x2, y2, z2, end, &x2e, &y2e, &z2e);
621 
622     float rs = radius(start, par);
623     float re = radius(end, par);
624 
625     float l;
626     l = sqrt(x1s*x1s+y1s*y1s+z1s*z1s); x1s *= rs/l; y1s *= rs/l; z1s *= rs/l;
627     l = sqrt(x2s*x2s+y2s*y2s+z2s*z2s); x2s *= rs/l; y2s *= rs/l; z2s *= rs/l;
628 
629     l = sqrt(x1e*x1e+y1e*y1e+z1e*z1e); x1e *= re/l; y1e *= re/l; z1e *= re/l;
630     l = sqrt(x2e*x2e+y2e*y2e+z2e*z2e); x2e *= re/l; y2e *= re/l; z2e *= re/l;
631 
632     outTriangle(x2s, y2s, z2s, x1s, y1s, z1s, x2e, y2e, z2e, par);
633     outTriangle(x1s, y1s, z1s, x1e, y1e, z1e, x2e, y2e, z2e, par);
634   }
635 }
636 
637 /* generate the connection between 2 spheres
638  * this connection starts on the sphere surface and slowly (via a rounding circle)
639  * warps into the connection cylinder
640  *
641  * the function really only draws a pie shaped piece of the connection that
642  * may be recursively split into smaller pie shaped pieces
643  */
drawHole(int i,float x1,float y1,float z1,float x2,float y2,float z2,int rec,int rec2,genPar & par)644 static void drawHole(
645     /* which touching point */
646     int i,
647     /* the 2 corners of the pie shaped piece where the hole is placed in between */
648     float x1, float y1, float z1, float x2, float y2, float z2,
649     /* number of recursion levels for this pie division, and number of recursion levels for
650      * when the pie piece is drawn
651      */
652     int rec, int rec2,
653     genPar & par)
654 {
655   if (rec > 0)
656   {
657     // find the middle between the 2 points
658     normalize(&x1, &y1, &z1);
659     normalize(&x2, &y2, &z2);
660 
661     float px = (x1+x2)/2;
662     float py = (y1+y2)/2;
663     float pz = (z1+z2)/2;
664 
665     // shift that point to the edge of the touching circle
666     shiftToHoleBorder(i, &px, &py, &pz);
667 
668     // draw the 2 pies to the left and the right
669     drawHole(i, x1, y1, z1, px, py, pz, rec-1, rec2, par);
670     drawHole(i, px, py, pz, x2, y2, z2, rec-1, rec2, par);
671   }
672   else
673   {
674     // Outermost section of the connection (farthest from center)
675     if (par.holeStart < par.lineEnd)
676       drawHolePiece(i, par.holeStart, par.lineEnd, x1, y1, z1, x2, y2, z2, 0, par);
677 
678     // Curvature transition
679     if (par.lineEnd < par.curvEnd)
680       drawHolePiece(i, par.lineEnd, par.curvEnd, x1, y1, z1, x2, y2, z2, rec2, par);
681 
682     // Innermost, transition to the connector
683     if (par.curvEnd < 30*M_PI/180)
684       drawHolePiece(i, par.curvEnd, 30*M_PI/180, x1, y1, z1, x2, y2, z2, rec2, par);
685   }
686 }
687 
688 
689 /* Shift the given point along the line connecting (x, y, z) to (px, py, pz) until
690  * it is a distance rad from this point (px, py, pz).
691  */
shiftToConnectingHole(float px,float py,float pz,float rad,float * x,float * y,float * z)692 static void shiftToConnectingHole(float px, float py, float pz, float rad,
693     float *x, float *y, float *z)
694 {
695   float diff = sqrt((px-*x)*(px-*x)+(py-*y)*(py-*y)+(pz-*z)*(pz-*z));
696 
697   if (diff < Epsilon) return;
698 
699   float s = 1.0 - rad/diff;
700 
701   *x = *x + s*(px-*x);
702   *y = *y + s*(py-*y);
703   *z = *z + s*(pz-*z);
704 
705   normalize(x, y, z);
706 }
707 
708 /* Draw the connecting faces between the inside and outside of the sphere.
709  * Simply draw two triangles between two pairs of inside and outside points.
710  */
drawConnectingTriangle(float x1,float y1,float z1,float x2,float y2,float z2,genPar & par)711 static void drawConnectingTriangle(
712     float x1, float y1, float z1,
713     float x2, float y2, float z2,
714     genPar & par)
715 {
716   normalize(&x1, &y1, &z1);
717   normalize(&x2, &y2, &z2);
718 
719   // calculate the points that are on the outer sphere
720   float x3 = x2*(par.sphere_rad-par.offset);
721   float y3 = y2*(par.sphere_rad-par.offset);
722   float z3 = z2*(par.sphere_rad-par.offset);
723 
724   float x4 = x1*(par.sphere_rad-par.offset);
725   float y4 = y1*(par.sphere_rad-par.offset);
726   float z4 = z1*(par.sphere_rad-par.offset);
727 
728   // the points for the inner sphere
729   float mult = par.inner_rad-par.offset;
730 
731   x1 *= mult;
732   y1 *= mult;
733   z1 *= mult;
734 
735   x2 *= mult;
736   y2 *= mult;
737   z2 *= mult;
738 
739   outTriangle(x1, y1, z1, x3, y3, z3, x2, y2, z2, par);
740   outTriangle(x1, y1, z1, x4, y4, z4, x3, y3, z3, par);
741 }
742 
743 /* The pattern of squares by variable name (x1,y1,z1), (x5,y5,z5), etc.
744    1  5  2
745    6  7  8
746    3  9  4
747 
748    edgeflag & 1 means this square includes the top edge
749    edgeflag & 2 means this square includes the bottom edge
750 */
drawConnectingHole(float hx,float hy,float hz,float x1,float y1,float z1,float x2,float y2,float z2,float x3,float y3,float z3,float x4,float y4,float z4,int edgeflag,int rec,genPar & par)751 static void drawConnectingHole(
752     float hx, float hy, float hz,
753     float x1, float y1, float z1, float x2, float y2, float z2,
754     float x3, float y3, float z3, float x4, float y4, float z4,
755     int edgeflag, int rec,
756     genPar & par)
757 {
758   normalize(&x1, &y1, &z1);
759   normalize(&x2, &y2, &z2);
760   normalize(&x3, &y3, &z3);
761   normalize(&x4, &y4, &z4);
762 
763   if (rec > 0)
764   {
765     float x9 = (x3 + x4)/2;
766     float y9 = (y3 + y4)/2;
767     float z9 = (z3 + z4)/2;
768 
769     // Do these next lines to make round (circular) holes
770     // otherwise they will be square.
771     if (edgeflag & 2 && par.hole_diam > 0)
772     {
773       float connectingHoleRadius = 0.5 * par.hole_diam / par.sphere_rad;
774       shiftToConnectingHole(hx, hy, hz, connectingHoleRadius, &x9, &y9, &z9);
775     }
776 
777     float x5 = (x1 + x2)/2;
778     float y5 = (y1 + y2)/2;
779     float z5 = (z1 + z2)/2;
780 
781     float x6 = (x1 + x3)/2;
782     float y6 = (y1 + y3)/2;
783     float z6 = (z1 + z3)/2;
784 
785     float x8 = (x2 + x4)/2;
786     float y8 = (y2 + y4)/2;
787     float z8 = (z2 + z4)/2;
788 
789     float x7 = (x5 + x9)/2;
790     float y7 = (y5 + y9)/2;
791     float z7 = (z5 + z9)/2;
792 
793     drawConnectingHole(hx, hy, hz,
794       x1, y1, z1, x5, y5, z5, x6, y6, z6, x7, y7, z7, edgeflag & 1, rec-1, par);
795     drawConnectingHole(hx, hy, hz,
796       x5, y5, z5, x2, y2, z2, x7, y7, z7, x8, y8, z8, edgeflag & 1, rec-1, par);
797     drawConnectingHole(hx, hy, hz,
798       x6, y6, z6, x7, y7, z7, x3, y3, z3, x9, y9, z9, edgeflag & 2, rec-1, par);
799     drawConnectingHole(hx, hy, hz,
800       x7, y7, z7, x8, y8, z8, x9, y9, z9, x4, y4, z4, edgeflag & 2, rec-1, par);
801 
802   }
803   else
804   {
805     float diag23 = sqrt((x2-x3)*(x2-x3)+(y2-y3)*(y2-y3)+(z2-z3)*(z2-z3));
806     float diag14 = sqrt((x1-x4)*(x1-x4)+(y1-y4)*(y1-y4)+(z1-z4)*(z1-z4));
807 
808     if (diag23 < diag14)
809     {
810       drawTriangle(x1, y1, z1, x2, y2, z2, x3, y3, z3, 0, 0, 0, 0, par);
811       drawTriangle(x2, y2, z2, x4, y4, z4, x3, y3, z3, 0, 0, 0, 0, par);
812     }
813     else
814     {
815       drawTriangle(x1, y1, z1, x2, y2, z2, x4, y4, z4, 0, 0, 0, 0, par);
816       drawTriangle(x1, y1, z1, x4, y4, z4, x3, y3, z3, 0, 0, 0, 0, par);
817     }
818 
819     if ((edgeflag & 2) && par.outside)
820     {
821       drawConnectingTriangle(x3, y3, z3, x4, y4, z4, par);
822     }
823   }
824 }
825 
826 /* create one sphere.
827  * neighbors shows, where we need connections to the neibors (each bit set means that
828  * there must be a connection cylinder, no bit set close the sphere) the bits are in
829  * the same order as the neighbors or connection points
830  *
831  * bollow specifies, whether the sphere is supposed to be hollow
832  * variable, shows, whether the sphere is supposed to be a variable sphere (use the bevel face as variable marker)
833  */
makeSphere(uint16_t neighbors,int recursion,bool hollow,bool variable,genPar & par)834 static void makeSphere(uint16_t neighbors, int recursion, bool hollow, bool variable, genPar & par)
835 {
836   /* first make the holes, or hole caps */
837   for (int i = 0; i < 12; i++)
838   {
839     float p1x = connectionPoints[i][0];
840     float p1y = connectionPoints[i][1];
841     float p1z = connectionPoints[i][2];
842 
843     normalize(&p1x, &p1y, &p1z);
844 
845     /* close the hole using 6 triangles */
846 
847     for (int t = 0; t < 6; t++)
848     {
849       float p2x = holeTouchPoints[i][(t+0)%6][0];
850       float p2y = holeTouchPoints[i][(t+0)%6][1];
851       float p2z = holeTouchPoints[i][(t+0)%6][2];
852 
853       float p3x = holeTouchPoints[i][(t+1)%6][0];
854       float p3y = holeTouchPoints[i][(t+1)%6][1];
855       float p3z = holeTouchPoints[i][(t+1)%6][2];
856 
857       shiftToHoleBorder(i, &p2x, &p2y, &p2z);
858       shiftToHoleBorder(i, &p3x, &p3y, &p3z);
859 
860       if ((neighbors & (1<<i)) && par.outside && (par.connection_rad > Epsilon))
861       {
862         /* make a proper hole (connection to next sphere) */
863         par.flags = FF_COLOR_LIGHT;
864         drawHole(i, p2x, p2y, p2z, p3x, p3y, p3z, (int)recursion, (int)recursion, par);
865       }
866       else
867       {
868         /* simply close the sphere with a sphere surface section
869          * those faces are the sensitive faces for appending another sphere
870          * they are drawn a bit darker
871          */
872         par.fb_face = i;
873         par.flags = 0;
874         drawTriangle(p1x, p1y, p1z, p2x, p2y, p2z, p3x, p3y, p3z, -1, i, -1, (int)recursion, par);
875       }
876     } // end loop over t
877   } // end loop over i
878 
879   // when the sphere is a variable sphere, we use the variable face marker for the filling faces, this will end up with
880   // black faces, otherwise we use the light marker to get lighter faces than the clickable faces
881   if (variable)
882     par.flags = FF_VARIABLE_FACE;
883   else
884     par.flags = FF_COLOR_LIGHT;
885 
886   // followind faces are supposed to stay, when in wire frame mode
887   par.flags |= FF_WIREFRAME;
888 
889   /* fill the 8 triangular gaps */
890   for (int i = 0; i < 8; i++)
891   {
892     float p1x = trianglePoints[i][0][0];
893     float p1y = trianglePoints[i][0][1];
894     float p1z = trianglePoints[i][0][2];
895 
896     float p2x = trianglePoints[i][1][0];
897     float p2y = trianglePoints[i][1][1];
898     float p2z = trianglePoints[i][1][2];
899 
900     float p3x = trianglePoints[i][2][0];
901     float p3y = trianglePoints[i][2][1];
902     float p3z = trianglePoints[i][2][2];
903 
904     shiftToHoleBorder(trianglePoints[i][0][3], &p1x, &p1y, &p1z);
905     shiftToHoleBorder(trianglePoints[i][1][3], &p2x, &p2y, &p2z);
906     shiftToHoleBorder(trianglePoints[i][2][3], &p3x, &p3y, &p3z);
907 
908     drawTriangle(p1x, p1y, p1z, p2x, p2y, p2z, p3x, p3y, p3z,
909         trianglePoints[i][0][3],
910         trianglePoints[i][1][3],
911         trianglePoints[i][2][3],
912         (int)recursion, par);
913   }
914 
915   /* finally the 6 square gaps */
916   for (int i = 0; i < 6; i++)
917   {
918     /* first fill the corners */
919     for (int k = 0; k < 4; k++) {
920       float p1x = squarePoints[i][(2*k+0)%8][0];
921       float p1y = squarePoints[i][(2*k+0)%8][1];
922       float p1z = squarePoints[i][(2*k+0)%8][2];
923 
924       float p2x = squarePoints[i][(2*k+1)%8][0];
925       float p2y = squarePoints[i][(2*k+1)%8][1];
926       float p2z = squarePoints[i][(2*k+1)%8][2];
927 
928       float p3x = squarePoints[i][(2*k+2)%8][0];
929       float p3y = squarePoints[i][(2*k+2)%8][1];
930       float p3z = squarePoints[i][(2*k+2)%8][2];
931 
932       shiftToHoleBorder(squarePoints[i][(2*k+0)%8][3], &p1x, &p1y, &p1z);
933       shiftToHoleBorder(squarePoints[i][(2*k+0)%8][3], &p2x, &p2y, &p2z);
934       shiftToHoleBorder(squarePoints[i][(2*k+2)%8][3], &p3x, &p3y, &p3z);
935 
936       drawTriangle(p1x, p1y, p1z, p2x, p2y, p2z, p3x, p3y, p3z,
937           squarePoints[i][(2*k+0)%8][3],
938           squarePoints[i][(2*k+2)%8][3],
939           -1,
940           (int)recursion, par);
941     }
942 
943     /* Then the center square */
944     if (!hollow || fabs(par.hole_diam) < Epsilon)
945     { // not hollow or no holes
946       for (int k = 0; k < 2; k++)
947       {
948         float p1x = squarePoints[i][(4*k+0)%8][0];
949         float p1y = squarePoints[i][(4*k+0)%8][1];
950         float p1z = squarePoints[i][(4*k+0)%8][2];
951 
952         float p2x = squarePoints[i][(4*k+2)%8][0];
953         float p2y = squarePoints[i][(4*k+2)%8][1];
954         float p2z = squarePoints[i][(4*k+2)%8][2];
955 
956         float p3x = squarePoints[i][(4*k+4)%8][0];
957         float p3y = squarePoints[i][(4*k+4)%8][1];
958         float p3z = squarePoints[i][(4*k+4)%8][2];
959 
960         shiftToHoleBorder(squarePoints[i][(4*k+0)%8][3], &p1x, &p1y, &p1z);
961         shiftToHoleBorder(squarePoints[i][(4*k+2)%8][3], &p2x, &p2y, &p2z);
962         shiftToHoleBorder(squarePoints[i][(4*k+4)%8][3], &p3x, &p3y, &p3z);
963 
964         drawTriangle(p1x, p1y, p1z, p2x, p2y, p2z, p3x, p3y, p3z,
965             -1, -1, -1, (int)recursion, par);
966       }
967     }
968     else
969     { //hollow with holes
970 
971       // hx,hy,hz, location of the center of the hole into the interior
972       float hx = 0.125*(squarePoints[i][0][0] + squarePoints[i][2][0] +
973                       squarePoints[i][4][0] + squarePoints[i][6][0]);
974       float hy = 0.125*(squarePoints[i][0][1] + squarePoints[i][2][1] +
975                       squarePoints[i][4][1] + squarePoints[i][6][1]);
976       float hz = 0.125*(squarePoints[i][0][2] + squarePoints[i][2][2] +
977                       squarePoints[i][4][2] + squarePoints[i][6][2]);
978 
979       /* Extend the inner square to the hole */
980       for (int k=0; k < 4; k++)
981       { //Once per side of the square
982         float p1x = squarePoints[i][(2*k+0)%8][0];
983         float p1y = squarePoints[i][(2*k+0)%8][1];
984         float p1z = squarePoints[i][(2*k+0)%8][2];
985         shiftToHoleBorder(squarePoints[i][(2*k+0)%8][3], &p1x, &p1y, &p1z);
986 
987         float p2x = squarePoints[i][(2*k+2)%8][0];
988         float p2y = squarePoints[i][(2*k+2)%8][1];
989         float p2z = squarePoints[i][(2*k+2)%8][2];
990         shiftToHoleBorder(squarePoints[i][(2*k+2)%8][3], &p2x, &p2y, &p2z);
991 
992         float p3x = p1x;
993         float p3y = p1y;
994         float p3z = p1z;
995 
996         float p4x = p2x;
997         float p4y = p2y;
998         float p4z = p2z;
999 
1000         float connectingHoleRadius = 0.5 / par.sphere_rad;
1001 
1002         // connectingHoleRadius is a diagonal measurement for square holes.
1003         // Thus, enlarge them by sqrt(2) for a side measurement.
1004         if (par.hole_diam < 0)
1005           connectingHoleRadius *= - (sqrt(2.0) * par.hole_diam);
1006         else
1007           connectingHoleRadius *= par.hole_diam;
1008 
1009         shiftToConnectingHole(hx, hy, hz, connectingHoleRadius, &p3x, &p3y, &p3z);
1010         shiftToConnectingHole(hx, hy, hz, connectingHoleRadius, &p4x, &p4y, &p4z);
1011 
1012         drawConnectingHole(hx, hy, hz, p1x, p1y, p1z, p2x, p2y, p2z, p3x, p3y, p3z, p4x, p4y, p4z, 3, (int)recursion, par);
1013       }
1014     } // End else
1015   } // End of loop over i
1016 }
1017 
curvOk(float curvrad,float cnrad,float sprad,float offset)1018 static bool curvOk(float curvrad, float cnrad, float sprad, float offset)
1019 {
1020   float curvx = cnrad*(sprad-offset)/2+curvrad;
1021   float curvy = sqrt((sprad-offset+curvrad)*(sprad-offset+curvrad)-curvx*curvx);
1022 
1023   if (curvy > sprad) return false;
1024   if (curvy/curvx < tan(60*M_PI/180)) return false;
1025 
1026   return true;
1027 }
1028 
getMeshInternal(float sphere_rad,float connection_rad,float round,float offset,int recursion,float inner_rad,float hole_diam,bool fast) const1029 Polyhedron * voxel_2_c::getMeshInternal(float sphere_rad, float connection_rad, float round, float offset, int recursion, float inner_rad, float hole_diam, bool fast) const
1030 {
1031   Polyhedron * poly = new Polyhedron;
1032   vertexList_c vl(poly);
1033 
1034   float maxcurv = 10;
1035   float maxcurv2 = 0;
1036 
1037   while (curvOk(maxcurv, connection_rad, sphere_rad, offset))
1038   {
1039     maxcurv2 = maxcurv;
1040     maxcurv *= 2;
1041   }
1042 
1043   while (fabs(maxcurv2-maxcurv) > Epsilon)
1044   {
1045     if (curvOk((maxcurv+maxcurv2)/2, connection_rad, sphere_rad, offset))
1046     {
1047       maxcurv2 = (maxcurv+maxcurv2)/2;
1048     }
1049     else
1050     {
1051       maxcurv = (maxcurv+maxcurv2)/2;
1052     }
1053   }
1054 
1055   genPar par;
1056 
1057   par.sphere_rad = sphere_rad;
1058   par.inner_rad = inner_rad;
1059   par.offset = offset;
1060   par.hole_diam = hole_diam;
1061   par.connection_rad = connection_rad;
1062   par.curvRad = maxcurv * round;
1063   par.curvX = connection_rad*(sphere_rad - offset)/2 + par.curvRad;
1064   par.curvY = sqrt((sphere_rad-offset+par.curvRad)*(sphere_rad-offset+par.curvRad)-par.curvX*par.curvX);
1065   par.vl = &vl;
1066   par.holeStart = atan((par.connection_rad*(par.sphere_rad-par.offset)/2)/par.sphere_rad);
1067   par.lineEnd = M_PI/2-atan2(par.curvY, par.curvX-par.curvRad);
1068   par.curvEnd = M_PI/2-atan2(par.curvY, par.curvX);
1069 
1070   if (par.lineEnd < par.holeStart) par.lineEnd = par.holeStart;
1071   if (par.curvEnd < par.lineEnd) par.curvEnd = par.lineEnd;
1072 
1073   for (unsigned int x = 0; x < getX(); x++)
1074     for (unsigned int y = 0; y < getY(); y++)
1075       for (unsigned int z = 0; z < getZ(); z++) {
1076         if (validCoordinate(x, y, z) && !isEmpty(x, y, z))
1077         {
1078           /* collect neighbors for a bitmask */
1079           uint16_t neighbors = 0;
1080 
1081           int nx, ny, nz;
1082           int idx = 0;
1083 
1084           while (getNeighbor(idx, 0, x, y, z, &nx, &ny, &nz))
1085           {
1086             if (validCoordinate(nx, ny, nz) && !isEmpty2(nx, ny, nz))
1087               neighbors |= 1<<idx;
1088             idx++;
1089           }
1090 
1091           bool hollow = false;
1092           if (inner_rad > Epsilon) hollow = true;
1093 
1094           par.fb_index = getIndex(x, y, z);
1095           bool variable = isVariable(x, y, z);
1096           par.color = getColor(x, y, z);
1097 
1098           par.xc = 2*sphere_rad*(x)*sqrt(0.5)+sphere_rad;
1099           par.yc = 2*sphere_rad*(y)*sqrt(0.5)+sphere_rad;
1100           par.zc = 2*sphere_rad*(z)*sqrt(0.5)+sphere_rad;
1101 
1102           // Draw the outside of the sphere.
1103           par.outside = true;
1104           makeSphere(neighbors, recursion, hollow, variable, par);
1105 
1106           // In the sphere is hollow, draw the inside of the sphere.
1107           // The connection between the inner and outer halves is done with the outside.
1108           if (hollow)
1109           {
1110             par.outside = false;
1111             par.fb_index = 0;
1112             par.flags = false;
1113             makeSphere(neighbors, recursion, hollow, variable, par);
1114           }
1115         }
1116       }
1117 
1118   if (!fast)
1119   {
1120     poly->finalize();
1121   }
1122 
1123   return poly;
1124 }
1125 
getMesh(float sphere_rad,float connection_rad,float round,float offset,int recursion,float inner_rad,float hole_diam) const1126 Polyhedron * voxel_2_c::getMesh(float sphere_rad, float connection_rad, float round, float offset, int recursion, float inner_rad, float hole_diam) const
1127 {
1128   return getMeshInternal(
1129       sphere_rad, connection_rad, round, offset, recursion, inner_rad, hole_diam, false);
1130 }
1131 
getDrawingMesh(void) const1132 Polyhedron * voxel_2_c::getDrawingMesh(void) const
1133 {
1134   return getMeshInternal(
1135       0.5,  // sphere radius
1136       0.7,  // connection radius
1137       1,    // round
1138       0.01, // offset
1139       1,    // recursion
1140       0,    // inner_rad
1141       0,    // hole_diam
1142       true  // fast generation
1143       );
1144 }
1145 
getWireframeMesh(void) const1146 Polyhedron * voxel_2_c::getWireframeMesh(void) const
1147 {
1148   return getDrawingMesh();
1149 }
1150 
getConnectionFace(int x,int y,int z,int n,double,double,std::vector<float> & faceCorners) const1151 void voxel_2_c::getConnectionFace(int x, int y, int z, int n, double /*bevel*/, double /*offset*/, std::vector<float> & faceCorners) const
1152 {
1153   static const float A = sqrt(0.5);
1154   static const float B = sqrt(0.125);
1155 
1156   /* array of
1157    * - of the 12 neighbors
1158    * - of the 4 points of a rhombus
1159    * - x, y, z
1160    */
1161   static const float faces[12][4][3] =
1162   {
1163     /* neighbor at -1, -1,  0 */ { {0, -A, 0}, {-B, -B,  B}, {-A, 0, 0}, {-B, -B, -B} },
1164     /* neighbor at -1,  1,  0 */ { {0,  A, 0}, {-B,  B, -B}, {-A, 0, 0}, {-B,  B,  B} },
1165     /* neighbor at  1, -1,  0 */ { {0, -A, 0}, { B, -B, -B}, { A, 0, 0}, { B, -B,  B} },
1166     /* neighbor at  1,  1,  0 */ { {0,  A, 0}, { B,  B,  B}, { A, 0, 0}, { B,  B, -B} },
1167 
1168     /* neighbor at -1,  0, -1 */ { {-A, 0, 0}, {-B,  B, -B}, {0, 0, -A}, {-B, -B, -B} },
1169     /* neighbor at -1,  0,  1 */ { {-A, 0, 0}, {-B, -B,  B}, {0, 0,  A}, {-B,  B,  B} },
1170     /* neighbor at  1,  0, -1 */ { { A, 0, 0}, { B, -B, -B}, {0, 0, -A}, { B,  B, -B} },
1171     /* neighbor at  1,  0,  1 */ { { A, 0, 0}, { B,  B,  B}, {0, 0,  A}, { B, -B,  B} },
1172 
1173     /* neighbor at  0, -1, -1 */ { {0, 0, -A}, { B, -B, -B}, {0, -A, 0}, {-B, -B, -B} },
1174     /* neighbor at  0, -1,  1 */ { {0, 0,  A}, {-B, -B,  B}, {0, -A, 0}, { B, -B,  B} },
1175     /* neighbor at  0,  1, -1 */ { {0, 0, -A}, {-B,  B, -B}, {0,  A, 0}, { B,  B, -B} },
1176     /* neighbor at  0,  1,  1 */ { {0, 0,  A}, { B,  B,  B}, {0,  A, 0}, {-B,  B,  B} },
1177   };
1178 
1179   bt_assert(n < 12);
1180 
1181   float xc = x*sqrt(0.5)+0.5;
1182   float yc = y*sqrt(0.5)+0.5;
1183   float zc = z*sqrt(0.5)+0.5;
1184 
1185   faceCorners.push_back(xc+faces[n][0][0]);
1186   faceCorners.push_back(yc+faces[n][0][1]);
1187   faceCorners.push_back(zc+faces[n][0][2]);
1188 
1189   faceCorners.push_back(xc+faces[n][1][0]);
1190   faceCorners.push_back(yc+faces[n][1][1]);
1191   faceCorners.push_back(zc+faces[n][1][2]);
1192 
1193   faceCorners.push_back(xc+faces[n][2][0]);
1194   faceCorners.push_back(yc+faces[n][2][1]);
1195   faceCorners.push_back(zc+faces[n][2][2]);
1196 
1197   faceCorners.push_back(xc+faces[n][3][0]);
1198   faceCorners.push_back(yc+faces[n][3][1]);
1199   faceCorners.push_back(zc+faces[n][3][2]);
1200 }
1201 
calculateSize(float * x,float * y,float * z) const1202 void voxel_2_c::calculateSize(float * x, float * y, float * z) const
1203 {
1204   *x = 1 + (getX()-1)*sqrt(0.5);
1205   *y = 1 + (getY()-1)*sqrt(0.5);
1206   *z = 1 + (getZ()-1)*sqrt(0.5);
1207 }
1208 
recalcSpaceCoordinates(float * x,float * y,float * z) const1209 void voxel_2_c::recalcSpaceCoordinates(float * x, float * y, float * z) const
1210 {
1211   *x = *x * sqrt(0.5);
1212   *y = *y * sqrt(0.5);
1213   *z = *z * sqrt(0.5);
1214 }
1215 
1216