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