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 
22 #include "voxel.h"
23 
24 #include "../tools/xml.h"
25 
26 #include "../halfedge/polyhedron.h"
27 #include "../halfedge/vector3.h"
28 #include "../halfedge/modifiers.h"
29 
30 #include <string.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 
34 /** \mainpage The BurrTools Library documentation
35  *
36  * \section Introduction
37  *
38  * This documentation tries to achieve two goals: it tries to explain how to
39  * use the BurrTools Library and it also explains the algorithms used and generally
40  * how things are implemented and done inside the library.
41  *
42  * I hope this will help everyone to understand the inner workings of this
43  * complex piece of software.
44  *
45  * To get started it is best to begin reading the userguide. This document will explain
46  * basic concepts of the whole software, even though this is done in a GUI centric way.
47  * Next read the information for the voxel voxel_c classes.
48  * Then continue to the assembly assembly_c. The assemblers assembler_c are probably not
49  * necessary to read. They contain too much complicated stuff. Then read the disassembly
50  * disassembly_c class documentation. Finally the grid type gridType_c class will
51  * glue things together.
52  *
53  * You should now be able to use the library.
54  *
55  * If you want to understand how things work you will need to read the documentation for the
56  * assembler \ref assembler_c and disassembler \ref disassembler_c classses.
57  */
58 
59 /** \file voxel.cpp
60  * Contains the implementation for the voxel base class
61  */
62 
63 /** \class voxel_c
64  *
65  * For the transformation of a voxel space all the grid types use transformation matrices
66  * that are in the corresponding tabs_x directory in the file rotmatrix.inc
67  *
68  * These tables contain all rotation matrices for all orientations of the grid. More
69  * in \ref transformationDetails
70  *
71  * The base class of the voxel space is not suitable for any grid because it misses
72  * functions. The derived classes implement those missing functions.
73  *
74  * The glasses for the tetrahedral-octahedral and for the rhombic grid are derrived
75  * from the cube grid (voxel_0_c) because they just superimpose another larger grid
76  * onto the standard cube grid to achieve their goals
77  */
78 
79 /// the value tha signifies uninitialized values for the hotspot and bounding box cache
80 #define BBHSCACHE_UNINIT -30000
81 /// the value means that this entry for the bounding box hotspot cache is not defines
82 /// due to an undefined orientation of the piece
83 #define BBHSCACHE_NOT_DEF -30001
84 
voxel_c(unsigned int x,unsigned int y,unsigned int z,const gridType_c * g,voxel_type init)85 voxel_c::voxel_c(unsigned int x, unsigned int y, unsigned int z, const gridType_c * g, voxel_type init) : gt(g), sx(x), sy(y), sz(z), voxels(x*y*z), hx(0), hy(0), hz(0), weight(1) {
86 
87   space = new voxel_type[voxels];
88   bt_assert(space);
89   bt_assert(gt);
90 
91   memset(space, init, voxels);
92 
93   if (init == 0) {
94     bx2 = by2 = bz2 = 0;
95     bx1 = x-1;
96     by1 = y-1;
97     bz1 = z-1;
98   } else {
99     bx1 = by1 = bz1 = 0;
100     bx2 = x-1;
101     by2 = y-1;
102     bz2 = z-1;
103   }
104 
105   doRecalc = true;
106 
107   symmetries = symmetryInvalid();
108 
109   BbHsCache = new int[9*gt->getSymmetries()->getNumTransformationsMirror()];
110 
111   for (unsigned int i = 0; i < gt->getSymmetries()->getNumTransformationsMirror(); i++)
112     BbHsCache[9*i+0] = BbHsCache[9*i+3] = BBHSCACHE_UNINIT;
113 }
114 
voxel_c(const voxel_c & orig)115 voxel_c::voxel_c(const voxel_c & orig) : gt(orig.gt), sx(orig.sx), sy(orig.sy), sz(orig.sz),
116 voxels(orig.voxels), hx(orig.hx), hy(orig.hy), hz(orig.hz), weight(orig.weight) {
117 
118   space = new voxel_type[voxels];
119   bt_assert(space);
120 
121   memcpy(space, orig.space, voxels);
122 
123   bx1 = orig.bx1;
124   bx2 = orig.bx2;
125   by1 = orig.by1;
126   by2 = orig.by2;
127   bz1 = orig.bz1;
128   bz2 = orig.bz2;
129 
130   doRecalc = true;
131 
132   symmetries = symmetryInvalid();
133 
134   BbHsCache = new int[9*gt->getSymmetries()->getNumTransformationsMirror()];
135 
136   for (unsigned int i = 0; i < gt->getSymmetries()->getNumTransformationsMirror(); i++)
137     BbHsCache[9*i+0] = BbHsCache[9*i+3] = BBHSCACHE_UNINIT;
138 }
139 
voxel_c(const voxel_c * orig)140 voxel_c::voxel_c(const voxel_c * orig) : gt(orig->gt), sx(orig->sx), sy(orig->sy), sz(orig->sz),
141 voxels(orig->voxels), hx(orig->hx), hy(orig->hy), hz(orig->hz), weight(orig->weight) {
142 
143   space = new voxel_type[voxels];
144   bt_assert(space);
145 
146   memcpy(space, orig->space, voxels);
147 
148   bx1 = orig->bx1;
149   bx2 = orig->bx2;
150   by1 = orig->by1;
151   by2 = orig->by2;
152   bz1 = orig->bz1;
153   bz2 = orig->bz2;
154 
155   doRecalc = true;
156 
157   symmetries = symmetryInvalid();
158 
159   BbHsCache = new int[9*gt->getSymmetries()->getNumTransformationsMirror()];
160 
161   for (unsigned int i = 0; i < gt->getSymmetries()->getNumTransformationsMirror(); i++)
162     BbHsCache[9*i+0] = BbHsCache[9*i+3] = BBHSCACHE_UNINIT;
163 }
164 
~voxel_c()165 voxel_c::~voxel_c() {
166   delete [] space;
167   delete [] BbHsCache;
168 }
169 
recalcBoundingBox(void)170 void voxel_c::recalcBoundingBox(void) {
171 
172   if (!doRecalc)
173     return;
174 
175   bx1 = by1 = bz1 = sx+sy+sz;
176   bx2 = by2 = bz2 = 0;
177 
178   bool empty = true;
179 
180   unsigned int index = 0;
181 
182   for (unsigned int z = 0; z < sz; z++)
183     for (unsigned int y = 0; y < sy; y++)
184       for (unsigned int x = 0; x < sx; x++) {
185         if ((space[index] & 3) != VX_EMPTY) {
186           if (x < bx1) bx1 = x;
187           if (x > bx2) bx2 = x;
188 
189           if (y < by1) by1 = y;
190           if (y > by2) by2 = y;
191 
192           if (z < bz1) bz1 = z;
193           if (z > bz2) bz2 = z;
194 
195           empty = false;
196         } else {
197           space[index] = 0;  // clear away all colors that might be left
198         }
199         index++;
200       }
201 
202   if (empty)
203     bx1 = by1 = bz1 = bx2 = by2 = bz2 = 0;
204 
205   /* we also clear the bounding box and hotspot cache */
206   for (unsigned int i = 0; i < gt->getSymmetries()->getNumTransformationsMirror(); i++)
207     BbHsCache[9*i+0] = BbHsCache[9*i+3] = BBHSCACHE_UNINIT;
208 }
209 
operator ==(const voxel_c & op) const210 bool voxel_c::operator ==(const voxel_c & op) const {
211 
212   if (sx != op.sx) return false;
213   if (sy != op.sy) return false;
214   if (sz != op.sz) return false;
215 
216   for (unsigned int i = 0; i < voxels; i++)
217     if (space[i] != op.space[i])
218       return false;
219 
220   return true;
221 }
222 
identicalInBB(const voxel_c * op,bool includeColors) const223 bool voxel_c::identicalInBB(const voxel_c * op, bool includeColors) const {
224 
225   if (bx2-bx1 != op->bx2-op->bx1) return false;
226   if (by2-by1 != op->by2-op->by1) return false;
227   if (bz2-bz1 != op->bz2-op->bz1) return false;
228 
229   for (unsigned int x = bx1; x <= bx2; x++)
230     for (unsigned int y = by1; y <= by2; y++)
231       for (unsigned int z = bz1; z <= bz2; z++)
232         if (includeColors) {
233           if (get(x, y, z) != op->get(x-bx1+op->bx1, y-by1+op->by1, z-bz1+op->bz1))
234             return false;
235         } else {
236           if (getState(x, y, z) != op->getState(x-bx1+op->bx1, y-by1+op->by1, z-bz1+op->bz1))
237             return false;
238         }
239 
240 
241   return true;
242 }
243 
identicalWithRots(const voxel_c * op,bool includeMirror,bool includeColors) const244 bool voxel_c::identicalWithRots(const voxel_c * op, bool includeMirror, bool includeColors) const {
245 
246   const symmetries_c * sym = gt->getSymmetries();
247 
248   unsigned int maxTrans = includeMirror ? sym->getNumTransformationsMirror() : sym->getNumTransformations();
249 
250   for (unsigned int t = 0; t < maxTrans; t++) {
251     voxel_c * v = gt->getVoxel(op);
252 
253     if (v->transform(t) && identicalInBB(v, includeColors)) {
254       delete v;
255       return true;
256     }
257 
258     delete v;
259   }
260 
261   return false;
262 }
263 
getMirrorTransform(const voxel_c * op) const264 unsigned char voxel_c::getMirrorTransform(const voxel_c * op) const {
265 
266   const symmetries_c * sym = gt->getSymmetries();
267 
268   for (unsigned int t = sym->getNumTransformations(); t < sym->getNumTransformationsMirror(); t++) {
269     voxel_c * v = gt->getVoxel(this);
270 
271     if (v->transform(t) && v->identicalInBB(op, true)) {
272       delete v;
273       return t;
274     }
275 
276     delete v;
277   }
278 
279   return 0;
280 }
281 
getHotspot(unsigned char trans,int * x,int * y,int * z) const282 bool voxel_c::getHotspot(unsigned char trans, int * x, int * y, int * z) const {
283 
284   bt_assert(trans < gt->getSymmetries()->getNumTransformationsMirror());
285   bt_assert(x && y && z);
286 
287   /* if the cache values don't exist calculate them */
288   if (BbHsCache[9*trans] == BBHSCACHE_UNINIT) {
289 
290     /* this version always works, but also is quite slow
291     */
292     voxel_c * tmp = gt->getVoxel(this);
293 
294     if (!tmp->transform(trans))
295     {
296       BbHsCache[9*trans+0] = BBHSCACHE_NOT_DEF;
297       BbHsCache[9*trans+3] = BBHSCACHE_NOT_DEF;
298     }
299 
300     BbHsCache[9*trans+0] = tmp->getHx();
301     BbHsCache[9*trans+1] = tmp->getHy();
302     BbHsCache[9*trans+2] = tmp->getHz();
303 
304     delete tmp;
305   }
306 
307   if (BbHsCache[9*trans] == BBHSCACHE_NOT_DEF)
308   {
309      return false;
310   }
311   else
312   {
313     *x = BbHsCache[9*trans+0];
314     *y = BbHsCache[9*trans+1];
315     *z = BbHsCache[9*trans+2];
316     return true;
317   }
318 }
319 
getBoundingBox(unsigned char trans,int * x1,int * y1,int * z1,int * x2,int * y2,int * z2) const320 bool voxel_c::getBoundingBox(unsigned char trans, int * x1, int * y1, int * z1, int * x2, int * y2, int * z2) const {
321 
322   bt_assert(trans < gt->getSymmetries()->getNumTransformationsMirror());
323 
324   /* if the cache values don't exist calculate them */
325   if (BbHsCache[9*trans+3] == BBHSCACHE_UNINIT) {
326 
327     /* this version always works, but it is quite slow */
328     voxel_c * tmp = gt->getVoxel(this);
329 
330     if (!tmp->transform(trans))
331     {
332       BbHsCache[9*trans+3] = BBHSCACHE_NOT_DEF;
333       BbHsCache[9*trans+0] = BBHSCACHE_NOT_DEF;
334     }
335     else
336     {
337       BbHsCache[9*trans+3] = tmp->boundX1();
338       BbHsCache[9*trans+4] = tmp->boundX2();
339       BbHsCache[9*trans+5] = tmp->boundY1();
340       BbHsCache[9*trans+6] = tmp->boundY2();
341       BbHsCache[9*trans+7] = tmp->boundZ1();
342       BbHsCache[9*trans+8] = tmp->boundZ2();
343     }
344 
345     delete tmp;
346   }
347 
348   if (BbHsCache[9*trans+3] == BBHSCACHE_NOT_DEF)
349   {
350     return false;
351   }
352   else
353   {
354     if (x1) *x1 = BbHsCache[9*trans+3];
355     if (x2) *x2 = BbHsCache[9*trans+4];
356     if (y1) *y1 = BbHsCache[9*trans+5];
357     if (y2) *y2 = BbHsCache[9*trans+6];
358     if (z1) *z1 = BbHsCache[9*trans+7];
359     if (z2) *z2 = BbHsCache[9*trans+8];
360     return true;
361   }
362 }
363 
resize(unsigned int nsx,unsigned int nsy,unsigned int nsz,voxel_type filler)364 void voxel_c::resize(unsigned int nsx, unsigned int nsy, unsigned int nsz, voxel_type filler) {
365 
366   // if size doesn't change, do nothing
367   if (nsx == sx && nsy == sy && nsz == sz) return;
368 
369   voxel_type * s2 = new voxel_type[nsx*nsy*nsz];
370   memset(s2, filler, nsx*nsy*nsz);
371 
372   unsigned int mx = (sx < nsx) ? sx : nsx;
373   unsigned int my = (sy < nsy) ? sy : nsy;
374   unsigned int mz = (sz < nsz) ? sz : nsz;
375 
376   for (unsigned int x = 0; x < mx; x++)
377     for (unsigned int y = 0; y < my; y++)
378       for (unsigned int z = 0; z < mz; z++)
379         s2[x + nsx * (y + nsy * z)] = get(x, y, z);
380 
381   delete [] space;
382   space = s2;
383 
384   sx = nsx;
385   sy = nsy;
386   sz = nsz;
387   voxels = sx*sy*sz;
388 
389   recalcBoundingBox();
390 }
391 
scale(unsigned int,bool)392 void voxel_c::scale(unsigned int /*amount*/, bool /*grid*/) {
393   // do nothing by default
394 }
395 
scaleDown(unsigned char,bool)396 bool voxel_c::scaleDown(unsigned char /*by*/, bool /*action*/) {
397   return false;
398 }
399 
400 
count(voxel_type val) const401 unsigned int voxel_c::count(voxel_type val) const {
402   unsigned int count = 0;
403   for (unsigned int i = 0; i < getXYZ(); i++)
404     if (get(i) == val)
405       count ++;
406   return count;
407 }
408 
countState(int state) const409 unsigned int voxel_c::countState(int state) const {
410   unsigned int count = 0;
411   for (unsigned int i = 0; i < getXYZ(); i++)
412     if ((get(i) & 3) == state)
413       count ++;
414   return count;
415 }
416 
translate(int dx,int dy,int dz,voxel_type filler)417 void voxel_c::translate(int dx, int dy, int dz, voxel_type filler) {
418   voxel_type * s2 = new voxel_type[sx*sy*sz];
419   memset(s2, filler, sx*sy*sz);
420 
421   for (unsigned int x = 0; x < sx; x++)
422     for (unsigned int y = 0; y < sy; y++)
423       for (unsigned int z = 0; z < sz; z++)
424         if (((int)x+dx >= 0) && ((int)x+dx < (int)sx) &&
425             ((int)y+dy >= 0) && ((int)y+dy < (int)sy) &&
426             ((int)z+dz >= 0) && ((int)z+dz < (int)sz))
427           s2[(x+dx)+sx*((y+dy)+sy*(z+dz))] = get(x, y, z);
428 
429   delete [] space;
430   space = s2;
431 
432   // initially I thought I could just shift the bounding box, but this doesn't work
433   // as off piece voxels might have been shifted out making the shape smaller
434   // and thus requiring a recalculation...
435   recalcBoundingBox();
436 
437   hx += dx;
438   hy += dy;
439   hz += dz;
440 }
441 
442 /** \page unionfinddetails Union Find Details
443  *
444  * Please read http://en.wikipedia.org/wiki/Union_find first.
445  *
446  * Now the method in the unionFind function is similar.
447  * Each node in the tree corresponds to one voxel of the voxel space.
448  * The tree array contains
449  * all indices to the perent node of the tree. The tree is initialied with NULL
450  * pinter equivalents (-1 in this case) then we loop over all voxels and all neighbors
451  * of a voxel ar found and the corresponding trees are unified.
452  *
453  * In the end all nodes that are somehow connected are within a single tree within the forrest.
454  *
455  * The 2 functions that use this function now evaluate the tree forrest in 2 ways:
456  *
457  * The \ref voxel_c::connected function will check if all non empty voxels in the space
458  * are within the same tree
459  *
460  * The \ref voxel_c::fillHoles function uses the unionFind function differently. It lets
461  * the function connect the empty voxels instead of the filled. Now we will find isolated
462  * voids  because they are in separate trees.
463  */
464 
465 /** \ref unionfinddetails */
unionFind(int * tree,char type,bool inverse,voxel_type value,bool outsideZ) const466 void voxel_c::unionFind(int * tree, char type, bool inverse, voxel_type value, bool outsideZ) const {
467 
468   /* union find algorithm:
469    * 1. put all voxels that matter in an own set
470    * 2. unify all sets whose voxels are neighbours
471    * 3. check if all voxels are in the same set
472    */
473 
474   /* initialize tree */
475   for (unsigned int i = 0; i < voxels+1; i++)
476     tree[i] = -1;
477 
478   bool merge_outside = ((inverse && (value != 0)) || (!inverse && (value == 0)));
479 
480   /* merge all neighbouring voxels */
481   for (unsigned int x = 0; x < sx; x++)
482     for (unsigned int y = 0; y < sy; y++)
483       for (unsigned int z = 0; z < sz; z++)
484         if (validCoordinate(x, y, z) &&
485 
486             ((inverse && (get(x, y, z) != value)) ||
487              (!inverse && (get(x, y, z) == value)))) {
488 
489           int root1 = getIndex(x, y, z);
490           while (tree[root1] >= 0) root1 = tree[root1];
491 
492           int curTyp = 0;
493 
494           while (curTyp <= type) {
495 
496             int x2, y2, z2;
497             int idx = 0;
498             while (getNeighbor(idx, curTyp, x, y, z, &x2, &y2, &z2)) {
499               if ((x2 >= 0) && (x2 < (int)sx) && (y2 >= 0) && (y2 < (int)sy) && (z2 >= 0) && (z2 < (int)sz)) {
500                 if ((x2 < (int)x) || (y2 < (int)y) || (z2 < (int)z)) {
501                   if ((inverse && (get(x2, y2, z2) != value)) ||
502                       (!inverse && (get(x2, y2, z2) == value))) {
503 
504                     int root2 = getIndex(x2, y2, z2);
505                     while (tree[root2] >= 0) root2 = tree[root2];
506 
507                     if (root1 != root2)
508                       tree[root2] = root1;
509                   }
510                 }
511               } else if (merge_outside) {
512 
513                 if (outsideZ || ((z2 >= 0) && (z2 < (int)sz))) {
514 
515                   int root2 = voxels;
516                   while (tree[root2] >= 0) root2 = tree[root2];
517 
518                   if (root1 != root2)
519                     tree[root2] = root1;
520                 }
521               }
522               idx++;
523             }
524             curTyp++;
525           }
526         }
527 }
528 
connected(char type,bool inverse,voxel_type value,bool outsideZ) const529 bool voxel_c::connected(char type, bool inverse, voxel_type value, bool outsideZ) const {
530 
531   /* allocate enough space for all voxels plus one for the outside */
532   int * tree = new int[voxels+1];
533 
534   unionFind(tree, type, inverse, value, outsideZ);
535 
536   int root = -1;
537 
538   bool merge_outside = ((inverse && (value != 0)) || (!inverse && (value == 0)));
539 
540   /* finally check, if all voxels are in the same set */
541   { for (unsigned int x = 0; x < sx; x++)
542       for (unsigned int y = 0; y < sy; y++)
543         for (unsigned int z = 0; z < sz; z++)
544           if (validCoordinate(x, y, z) &&
545               ((inverse && (get(x, y, z) != value)) ||
546                (!inverse && (get(x, y, z) == value)))) {
547             if (root == -1) {
548               root = getIndex(x, y, z);
549               while (tree[root] >= 0) root = tree[root];
550 
551             } else {
552 
553               int root2 = getIndex(x, y, z);
554               while (tree[root2] >= 0) root2 = tree[root2];
555 
556               if (root2 != root) {
557                 delete [] tree;
558                 return false;
559               }
560             }
561           }
562 
563     if ((root != -1) && merge_outside) {
564       int root2 = voxels;
565       while (tree[root2] >= 0) root2 = tree[root2];
566 
567       if (root2 != root) {
568         delete [] tree;
569         return false;
570       }
571     }
572   }
573 
574   delete [] tree;
575   return true;
576 }
577 
fillHoles(char type)578 void voxel_c::fillHoles(char type) {
579 
580   /* allocate enough space for all voxels plus one for the outside */
581   int * tree = new int[voxels+1];
582 
583   unionFind(tree, type, true, VX_FILLED, true);
584 
585   int root = -1;
586 
587   root = tree[voxels];
588   while (tree[root] >= 0) root = tree[root];
589 
590   for (unsigned int x = 0; x < sx; x++)
591     for (unsigned int y = 0; y < sy; y++)
592       for (unsigned int z = 0; z < sz; z++)
593         if (validCoordinate(x, y, z) &&
594             get(x, y, z) != VX_FILLED)  {
595           int root2 = getIndex(x, y, z);
596           while (tree[root2] >= 0) root2 = tree[root2];
597 
598           if (root2 != root) {
599             set(x, y, z, VX_FILLED);
600           }
601         }
602 
603 
604   delete [] tree;
605 }
606 
copy(const voxel_c * orig)607 void voxel_c::copy(const voxel_c * orig) {
608 
609   delete [] space;
610 
611   space = new voxel_type [orig->getXYZ()];
612 
613   memcpy(space, orig->space, orig->getXYZ());
614 
615   sx = orig->sx;
616   sy = orig->sy;
617   sz = orig->sz;
618 
619   voxels = orig->voxels;
620 
621   bx1 = orig->bx1;
622   bx2 = orig->bx2;
623   by1 = orig->by1;
624   by2 = orig->by2;
625   bz1 = orig->bz1;
626   bz2 = orig->bz2;
627 
628   symmetries = orig->symmetries;
629 
630   // we don't copy the name intentionally because the name is supposed to
631   // be unique
632   name = "";
633 
634   weight = orig->weight;
635 }
636 
neighbour(unsigned int p,voxel_type val) const637 bool voxel_c::neighbour(unsigned int p, voxel_type val) const {
638 
639   unsigned int x = p % sx;
640   unsigned int y = ((p - x) / sx) % sy;
641   unsigned int z = (((p - x) / sx) - y) / sy;
642 
643   bt_assert(x + sx * (y + sy * z) == p);
644 
645   if ((x > 0   ) && (space[p-1] == val)) return true;
646   if ((x < sx-1) && (space[p+1] == val)) return true;
647 
648   if ((y > 0   ) && (space[p-sx] == val)) return true;
649   if ((y < sy-1) && (space[p+sx] == val)) return true;
650 
651   if ((z > 0   ) && (space[p-sx*sy] == val)) return true;
652   if ((z < sz-1) && (space[p+sx*sy] == val)) return true;
653 
654   return false;
655 }
656 
selfSymmetries(void) const657 symmetries_t voxel_c::selfSymmetries(void) const {
658 
659   // if we have not calculated the symmetries, yet we calclate it
660   if (isSymmetryInvalid(symmetries))
661   {
662     symmetries = gt->getSymmetries()->calculateSymmetry(this);
663   }
664 
665   return symmetries;
666 }
667 
minimizePiece(void)668 void voxel_c::minimizePiece(void) {
669 
670   unsigned int x1, x2, y1, y2, z1, z2;
671 
672   x1 = y1 = z1 = getXYZ();
673   x2 = y2 = z2 = 0;
674 
675   for (unsigned int x = 0; x < getX(); x++)
676     for (unsigned int y = 0; y < getY(); y++)
677       for (unsigned int z = 0; z < getZ(); z++)
678         if (getState(x, y, z) != VX_EMPTY) {
679           if (x < x1) x1 = x;
680           if (x > x2) x2 = x;
681 
682           if (y < y1) y1 = y;
683           if (y > y2) y2 = y;
684 
685           if (z < z1) z1 = z;
686           if (z > z2) z2 = z;
687         }
688 
689   // check for empty cube and do nothing in that case
690   if (x1 > x2)
691     return;
692 
693   if ((x1 != 0) || (y1 != 0) || (z1 != 0) || (x2 != getX()-1) || (y2 != getY()-1) || (z2 != getZ()-1)) {
694 
695     translate(-x1, -y1, -z1, 0);
696     resize(x2-x1+1, y2-y1+1, z2-z1+1, 0);
697   }
698 }
699 
actionOnSpace(VoxelAction action,bool inside)700 void voxel_c::actionOnSpace(VoxelAction action, bool inside) {
701 
702   if (!getX() || !getY() ||!getZ())
703     return;
704 
705   for (unsigned int x = 0; x < getX(); x++)
706     for (unsigned int y = 0; y < getY(); y++)
707       for (unsigned int z = 0; z < getZ(); z++)
708         if (getState(x, y, z) != VX_EMPTY) {
709           bool neighborEmpty = false;
710 
711           int idx = 0;
712           int nx, ny, nz;
713 
714           while (!neighborEmpty && getNeighbor(idx, 0, x, y, z, &nx, &ny, &nz)) {
715             neighborEmpty |= (getState2(nx, ny, nz) == VX_EMPTY);
716             idx++;
717           }
718 
719           if (inside ^ neighborEmpty)
720             switch(action) {
721               case ACT_FIXED: setState(x, y, z, VX_FILLED); break;
722               case ACT_VARIABLE: setState(x, y, z, VX_VARIABLE); break;
723               case ACT_DECOLOR: setColor(x, y, z, 0); break;
724             }
725         }
726 }
727 
save(xmlWriter_c & xml) const728 void voxel_c::save(xmlWriter_c & xml) const {
729 
730   xml.newTag("voxel");
731 
732   xml.newAttrib("x", sx);
733   xml.newAttrib("y", sy);
734   xml.newAttrib("z", sz);
735 
736   if (hx) xml.newAttrib("hx", hx);
737   if (hy) xml.newAttrib("hy", hy);
738   if (hz) xml.newAttrib("hz", hz);
739 
740   if (weight != 1)
741     xml.newAttrib("weight", weight);
742 
743   if (name.length())
744     xml.newAttrib("name", name);
745 
746   // this might allow us to later add another format
747   xml.newAttrib("type", 0);
748 
749   std::ostream & str = xml.addContent();
750 
751   for (unsigned int i = 0; i < getXYZ(); i++)
752   {
753     // output state
754     switch(getState(i))
755     {
756       case VX_EMPTY:    str << "_"; break;
757       case VX_FILLED:   str << "#"; break;
758       case VX_VARIABLE: str << "+"; break;
759     }
760 
761     // output color postfix, but only vor nonempty voxels
762     switch(getState(i))
763     {
764       case VX_VARIABLE:
765       case VX_FILLED:
766         // output color, only when color is not zero
767         if (getColor(i))
768           str << getColor(i);
769         break;
770 
771       default:
772         break;
773     }
774   }
775 
776   xml.endTag("voxel");
777 }
778 
voxel_c(xmlParser_c & pars,const gridType_c * g)779 voxel_c::voxel_c(xmlParser_c & pars, const gridType_c * g) : gt(g), hx(0), hy(0), hz(0), weight(1)
780 {
781   pars.require(xmlParser_c::START_TAG, "voxel");
782 
783   skipRecalcBoundingBox(true);
784 
785   std::string szStr;
786 
787   szStr = pars.getAttributeValue("x");
788   if (!szStr.length())
789     pars.exception("voxel space requires 'x' attribute");
790   sx = atoi(szStr.c_str());
791 
792   szStr = pars.getAttributeValue("y");
793   if (!szStr.length())
794     pars.exception("voxel space requires 'y' attribute");
795   sy = atoi(szStr.c_str());
796 
797   szStr = pars.getAttributeValue("z");
798   if (!szStr.length())
799     pars.exception("voxel space requires 'z' attribute");
800   sz = atoi(szStr.c_str());
801 
802   szStr = pars.getAttributeValue("type");
803   if (!szStr.length())
804     pars.exception("voxel space requires 'type' attribute");
805 
806   unsigned int type = atoi(szStr.c_str());
807 
808   // set to the correct size
809   voxels = sx*sy*sz;
810 
811   szStr = pars.getAttributeValue("hx");
812   hx = atoi(szStr.c_str());
813   szStr = pars.getAttributeValue("hy");
814   hy = atoi(szStr.c_str());
815   szStr = pars.getAttributeValue("hz");
816   hz = atoi(szStr.c_str());
817 
818   name = pars.getAttributeValue("name");
819 
820   szStr = pars.getAttributeValue("weight");
821   if (szStr != "")
822     weight = atoi(szStr.c_str());
823 
824   space = new voxel_type[voxels];
825 
826   if (pars.next() != xmlParser_c::TEXT)
827     pars.exception("voxel space requires content");
828 
829   std::string c = pars.getText();
830 
831   unsigned int idx = 0;
832   unsigned int color = 0;
833 
834   if (c.length())
835   {
836     if (type != 0)
837       pars.exception("unknown voxel type");
838 
839     for (unsigned int pos = 0; pos < c.length(); pos++)
840     {
841       switch (c[pos])
842       {
843         case '#':
844           setState(idx++, VX_FILLED);
845           color = 0;
846           break;
847         case '+':
848           setState(idx++, VX_VARIABLE);
849           color = 0;
850           break;
851         case '_':
852           setState(idx++, VX_EMPTY);
853           color = 0;
854           break;
855         case '0': color = color * 10 + 0; break;
856         case '1': color = color * 10 + 1; break;
857         case '2': color = color * 10 + 2; break;
858         case '3': color = color * 10 + 3; break;
859         case '4': color = color * 10 + 4; break;
860         case '5': color = color * 10 + 5; break;
861         case '6': color = color * 10 + 6; break;
862         case '7': color = color * 10 + 7; break;
863         case '8': color = color * 10 + 8; break;
864         case '9': color = color * 10 + 9; break;
865         default : pars.exception("unrecognised character in piece voxel space");
866       }
867 
868       if (color > 63)
869         pars.exception("constraint color too big > 63");
870 
871       if (idx > 0)
872         setColor(idx-1, color);
873 
874       if (idx > getXYZ())
875         pars.exception("too many voxels defined for voxelspace");
876     }
877     if (idx < getXYZ())
878       pars.exception("not enough voxels defined for voxelspace");
879   }
880 
881   symmetries = symmetryInvalid();
882   BbHsCache = new int[9*gt->getSymmetries()->getNumTransformationsMirror()];
883 
884   skipRecalcBoundingBox(false);
885 
886   pars.next();
887   pars.require(xmlParser_c::END_TAG, "voxel");
888 }
889 
setHotspot(int x,int y,int z)890 void voxel_c::setHotspot(int x, int y, int z) {
891   hx = x; hy = y; hz = z;
892 
893   for (unsigned int i = 0; i < gt->getSymmetries()->getNumTransformationsMirror(); i++)
894     BbHsCache[9*i+0] = BBHSCACHE_UNINIT;
895 }
896 
initHotspot(void)897 void voxel_c::initHotspot(void) {
898   setHotspot(0, 0, 0);
899 }
900 
indexToXYZ(unsigned int index,unsigned int * x,unsigned int * y,unsigned int * z) const901 bool voxel_c::indexToXYZ(unsigned int index, unsigned int *x, unsigned int *y, unsigned int *z) const {
902 
903   *x = index % sx;
904   index -= *x;
905   index /= sx;
906 
907   *y = index % sy;
908   index -= *y;
909   index /= sy;
910 
911   *z = index;
912 
913   return *z < sz;
914 }
915 
unionintersect(const voxel_c * va,int xa,int ya,int za,const voxel_c * vb,int xb,int yb,int zb)916 bool voxel_c::unionintersect(
917       const voxel_c * va, int xa, int ya, int za,
918       const voxel_c * vb, int xb, int yb, int zb
919       ) {
920 
921   /* first make sure that the 2 given voxels bounding boxes do overlap
922    * if they don't we don't need to to anything
923    */
924   if (va->bx1+xa > vb->bx2+xb || va->bx2+xa < vb->bx1+xb) return false;
925   if (va->by1+ya > vb->by2+yb || va->by2+ya < vb->by1+yb) return false;
926   if (va->bz1+za > vb->bz2+zb || va->bz2+za < vb->bz1+zb) return false;
927 
928   /* first make sure wa can accommodate the complete 2nd voxel space */
929   bool do_resize = false;
930   int nx = sx;
931   int ny = sy;
932   int nz = sz;
933 
934   if (xa+(int)va->sx > nx) { nx = xa+(int)va->sx; do_resize = true; }
935   if (ya+(int)va->sy > ny) { ny = ya+(int)va->sy; do_resize = true; }
936   if (za+(int)va->sz > nz) { nz = za+(int)va->sz; do_resize = true; }
937 
938   if (xb+(int)vb->sx > nx) { nx = xb+(int)vb->sx; do_resize = true; }
939   if (yb+(int)vb->sy > ny) { ny = yb+(int)vb->sy; do_resize = true; }
940   if (zb+(int)vb->sz > nz) { nz = zb+(int)vb->sz; do_resize = true; }
941 
942   if (do_resize)
943     resize(nx, ny, nz, VX_EMPTY);
944 
945   bool result = false;
946 
947   for (unsigned int x = 0; x < sx; x++)
948     for (unsigned int y = 0; y < sy; y++)
949       for (unsigned int z = 0; z < sz; z++)
950         if (va->getState2(x-xa, y-ya, z-za) == VX_FILLED &&
951             vb->getState2(x-xb, y-yb, z-zb) == VX_FILLED) {
952           set(x, y, z, VX_FILLED);
953           result = true;
954         }
955 
956   return result;
957 }
958 
getMeshInternal(double bevel,double offset,bool fast) const959 Polyhedron * voxel_c::getMeshInternal(double bevel, double offset, bool fast) const
960 {
961   Polyhedron * res = new Polyhedron();
962 
963   vertexList_c vl(res);
964   std::vector<int> face3(3);
965   std::vector<int> face4(4);
966   std::vector<float> faceCorners;
967   std::vector<float> faceCorners2;
968 
969   // make shure the 2 parameters are positive or zero and have a valid minimum size
970   if (bevel < 1e-5) bevel = 0;
971   if (offset < 1e-5) offset = 0;
972 
973   for (unsigned int z = 0; z < getZ(); z++)
974     for (unsigned int y = 0; y < getY(); y++)
975       for (unsigned int x = 0; x < getX(); x++)
976         if (!isEmpty(x, y, z))
977         {
978           int n;
979           int nx, ny, nz;
980 
981           // we skip generating this voxel for the fast case,
982           // when the voxel to generate has no empty neighbors
983           if (fast)
984           {
985             bool hasEmptyN = false;
986 
987             n = 0;
988 
989             while (getNeighbor(n, 0, x, y, z, &nx, &ny, &nz))
990             {
991               if (isEmpty2(nx, ny, nz))
992               {
993                 hasEmptyN = true;
994                 break;
995               }
996               n++;
997             }
998 
999             if (!hasEmptyN)
1000             {
1001               continue;
1002             }
1003           }
1004 
1005           uint32_t type = (((x+y+z) & 1) == 0) ? FF_COLOR_LIGHT : 0;
1006           uint32_t idx = getIndex(x, y, z);
1007 
1008           // first t the voxel polyhedron that is fixed
1009 
1010           n = 1;
1011 
1012           if (bevel > 0)
1013           {
1014             do {
1015               faceCorners.clear();
1016               getConnectionFace(x, y, z, -n, bevel, offset, faceCorners);
1017 
1018               Face * f = 0;
1019 
1020               if (faceCorners.size() == 9)
1021               {
1022                 face3[0] = vl.get(faceCorners[0], faceCorners[1], faceCorners[2]);
1023                 face3[1] = vl.get(faceCorners[3], faceCorners[4], faceCorners[5]);
1024                 face3[2] = vl.get(faceCorners[6], faceCorners[7], faceCorners[8]);
1025                 f = res->addFace(face3);
1026               }
1027               else if (faceCorners.size() == 12)
1028               {
1029                 face4[0] = vl.get(faceCorners[0], faceCorners[1], faceCorners[2]);
1030                 face4[1] = vl.get(faceCorners[3], faceCorners[4], faceCorners[5]);
1031                 face4[2] = vl.get(faceCorners[6], faceCorners[7], faceCorners[8]);
1032                 face4[3] = vl.get(faceCorners[9], faceCorners[10], faceCorners[11]);
1033                 f = res->addFace(face4);
1034               }
1035               else if (faceCorners.size() == 0)
1036               {
1037               }
1038               else
1039               {
1040                 bt_assert(0);
1041               }
1042 
1043               n++;
1044 
1045               if (f)
1046               {
1047                 f->_flags = FF_WIREFRAME | type | FF_BEVEL_FACE;
1048                 f->_fb_index = idx;
1049                 f->_fb_face = -1;
1050                 f->_color = 0;
1051               f->_color = getColor(x, y, z);
1052               }
1053 
1054             } while (faceCorners.size() > 0);
1055           }
1056 
1057           n = 0;
1058 
1059           while (getNeighbor(n, 0, x, y, z, &nx, &ny, &nz))
1060           {
1061             if (isEmpty2(nx, ny, nz))
1062             {
1063               faceCorners.clear();
1064               getConnectionFace(x, y, z, n, bevel, offset, faceCorners);
1065 
1066               Face * f = 0;
1067 
1068               if (faceCorners.size() == 9)
1069               {
1070                 face3[0] = vl.get(faceCorners[0], faceCorners[1], faceCorners[2]);
1071                 face3[1] = vl.get(faceCorners[3], faceCorners[4], faceCorners[5]);
1072                 face3[2] = vl.get(faceCorners[6], faceCorners[7], faceCorners[8]);
1073                 f = res->addFace(face3);
1074               }
1075               else if (faceCorners.size() == 12)
1076               {
1077                 face4[0] = vl.get(faceCorners[0], faceCorners[1], faceCorners[2]);
1078                 face4[1] = vl.get(faceCorners[3], faceCorners[4], faceCorners[5]);
1079                 face4[2] = vl.get(faceCorners[6], faceCorners[7], faceCorners[8]);
1080                 face4[3] = vl.get(faceCorners[9], faceCorners[10], faceCorners[11]);
1081                 f = res->addFace(face4);
1082               }
1083               else
1084               {
1085                 bt_assert(0);
1086               }
1087 
1088               f->_flags = type;
1089               if (isVariable(x, y, z)) f->_flags |= FF_VARIABLE_MARK;
1090               f->_fb_index = idx;
1091               f->_fb_face = n;
1092               f->_color = getColor(x, y, z);
1093             }
1094             else
1095             {
1096               if (!fast & (offset > 0) && ((nx > x) || (nx == x && ny > y) || (nx == x && ny == y && nz > z)))
1097               {
1098                 // add connection prisms
1099 
1100                 // first find out which neighbor we are relative to our neighbor n
1101 
1102                 int n2 = 0;
1103                 bool found = false;
1104                 int mx, my, mz;
1105 
1106                 while (getNeighbor(n2, 0, nx, ny, nz, &mx, &my, &mz))
1107                 {
1108                   if (mx == x && my == y && mz == z)
1109                   {
1110                     found = true;
1111                     break;
1112                   }
1113                   n2++;
1114                 }
1115 
1116                 bt_assert(found);
1117 
1118                 faceCorners.clear();
1119                 getConnectionFace(x, y, z, n, bevel, offset, faceCorners);
1120 
1121                 faceCorners2.clear();
1122                 getConnectionFace(nx, ny, nz, n2, bevel, offset, faceCorners2);
1123 
1124                 bt_assert(faceCorners.size() == faceCorners2.size());
1125                 bt_assert(faceCorners.size() % 3 == 0);
1126 
1127                 int corners = faceCorners.size() / 3;
1128 
1129                 for (int i = 0; i < corners; i++)
1130                 {
1131                   int f1c1 = 3*i;
1132                   int f1c2 = 3*((i+1) % corners);
1133                   int f2c1 = 3*((-i+corners) % corners);
1134                   int f2c2 = 3*((-i+corners-1) % corners);
1135 
1136                   face4[0] = vl.get(faceCorners[f1c1+0],  faceCorners[f1c1+1],  faceCorners[f1c1+2]);
1137                   face4[1] = vl.get(faceCorners[f1c2+0],  faceCorners[f1c2+1],  faceCorners[f1c2+2]);
1138                   face4[2] = vl.get(faceCorners2[f2c2+0], faceCorners2[f2c2+1], faceCorners2[f2c2+2]);
1139                   face4[3] = vl.get(faceCorners2[f2c1+0], faceCorners2[f2c1+1], faceCorners2[f2c1+2]);
1140 
1141                   Face * f = res->addFace(face4);
1142 
1143                   f->_flags = FF_WIREFRAME | type | FF_OFFSET_FACE;
1144                   f->_fb_index = idx;
1145                   f->_fb_face = -1;
1146                   f->_color = 0;
1147               f->_color = getColor(x, y, z);
1148                 }
1149               }
1150             }
1151             n++;
1152           }
1153         }
1154 
1155   if (!fast)
1156   {
1157     res->finalize();
1158   }
1159 
1160   return res;
1161 }
1162 
getMesh(double bevel,double offset) const1163 Polyhedron * voxel_c::getMesh(double bevel, double offset) const
1164 {
1165   return getMeshInternal(bevel, offset, false);
1166 }
1167 
getDrawingMesh(void) const1168 Polyhedron * voxel_c::getDrawingMesh(void) const
1169 {
1170   return getMeshInternal(0.03, 0.005, true);
1171 }
1172 
getWireframeMesh(void) const1173 Polyhedron * voxel_c::getWireframeMesh(void) const
1174 {
1175   Polyhedron * p = getMeshInternal(0.03, 0.005, false);
1176   fillPolyhedronHoles(*p, 1);
1177   return p;
1178 }
1179 
1180 
1181