1 /****************************************************************************
2 **
3 ** Copyright (c) 2008-2020 C.B. Barber. All rights reserved.
4 ** $Id: //main/2019/qhull/src/libqhullcpp/QhullFacet.cpp#5 $$Change: 3009 $
5 ** $DateTime: 2020/07/30 19:25:22 $$Author: bbarber $
6 **
7 ****************************************************************************/
8
9 #//! QhullFacet -- Qhull's facet structure, facetT, as a C++ class
10
11 #include "libqhullcpp/QhullFacet.h"
12
13 #include "libqhullcpp/QhullError.h"
14 #include "libqhullcpp/Qhull.h"
15 #include "libqhullcpp/QhullSet.h"
16 #include "libqhullcpp/QhullPoint.h"
17 #include "libqhullcpp/QhullPointSet.h"
18 #include "libqhullcpp/QhullRidge.h"
19 #include "libqhullcpp/QhullFacetSet.h"
20 #include "libqhullcpp/QhullVertex.h"
21
22 #include <ostream>
23
24 using std::endl;
25 using std::ostream;
26
27 #ifdef _MSC_VER // Microsoft Visual C++ -- warning level 4
28 #pragma warning( disable : 4611) // interaction between '_setjmp' and C++ object destruction is non-portable
29 #pragma warning( disable : 4996) // function was declared deprecated(strcpy, localtime, etc.)
30 #endif
31
32 namespace orgQhull {
33
34 #//!\name Class objects
35 facetT QhullFacet::
36 s_empty_facet= {
37 #if !qh_COMPUTEfurthest // must match facetT -Wmissing-field-initializers
38 0.0,
39 #endif
40 #if qh_MAXoutside
41 0.0,
42 #endif
43 0.0,NULL,{0.0},
44 NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0,0,0,
45 false,false,false,false,false,
46 false,false,false,false,false,
47 false,false,false,false,false,
48 false,false,false,false,false,
49 false,false,false};
50
51 #//!\name Constructors
52
53 QhullFacet::
QhullFacet(const Qhull & q)54 QhullFacet(const Qhull &q)
55 : qh_facet(&s_empty_facet)
56 , qh_qh(q.qh())
57 {
58 }
59
60 QhullFacet::
QhullFacet(const Qhull & q,facetT * f)61 QhullFacet(const Qhull &q, facetT *f)
62 : qh_facet(f ? f : &s_empty_facet)
63 , qh_qh(q.qh())
64 {
65 }
66
67 #//!\name GetSet
68
69 //! Return voronoi center or facet centrum. Derived from qh_printcenter [io_r.c]
70 //! if printFormat=qh_PRINTtriangles and qh.DELAUNAY, returns centrum of a Delaunay facet
71 //! Sets center if needed
72 //! Code duplicated for PrintCenter and getCenter
73 //! Returns QhullPoint() if none or qh_INFINITE
74 QhullPoint QhullFacet::
getCenter(qh_PRINT printFormat)75 getCenter(qh_PRINT printFormat)
76 {
77 if(!qh_qh){
78 // returns QhullPoint()
79 }else if(qh_qh->CENTERtype==qh_ASvoronoi){
80 if(!qh_facet->normal || !qh_facet->upperdelaunay || !qh_qh->ATinfinity){
81 if(!qh_facet->center){
82 QH_TRY_(qh_qh){ // no object creation -- destructors skipped on longjmp()
83 qh_facet->center= qh_facetcenter(qh_qh, qh_facet->vertices);
84 }
85 qh_qh->NOerrexit= true;
86 qh_qh->maybeThrowQhullMessage(QH_TRY_status);
87 }
88 return QhullPoint(qh_qh, qh_qh->hull_dim-1, qh_facet->center);
89 }
90 }else if(qh_qh->CENTERtype==qh_AScentrum){
91 volatile int numCoords= qh_qh->hull_dim;
92 if(printFormat==qh_PRINTtriangles && qh_qh->DELAUNAY){
93 numCoords--;
94 }
95 if(!qh_facet->center){
96 QH_TRY_(qh_qh){ // no object creation -- destructors skipped on longjmp()
97 qh_facet->center= qh_getcentrum(qh_qh, getFacetT());
98 }
99 qh_qh->NOerrexit= true;
100 qh_qh->maybeThrowQhullMessage(QH_TRY_status);
101 }
102 return QhullPoint(qh_qh, numCoords, qh_facet->center);
103 }
104 return QhullPoint();
105 }//getCenter
106
107 //! Return innerplane clearly below the vertices
108 //! from io_r.c[qh_PRINTinner]
109 QhullHyperplane QhullFacet::
innerplane() const110 innerplane() const
111 {
112 QhullHyperplane h;
113 if(qh_qh){
114 realT inner;
115 // Does not error, TRY_QHULL_ not needed
116 qh_outerinner(qh_qh, const_cast<facetT *>(getFacetT()), NULL, &inner);
117 h= hyperplane();
118 h.setOffset(h.offset()-inner); //inner is negative
119 }
120 return h;
121 }//innerplane
122
123 QhullFacet QhullFacet::
nextFacet2d(QhullVertex * nextVertex) const124 nextFacet2d(QhullVertex *nextVertex) const
125 {
126 QhullFacet f;
127 if(qh_qh && qh_facet){
128 vertexT *vertex;
129 // Does not error, TRY_QHULL_ not needed
130 facetT *facet= qh_nextfacet2d(getFacetT(), &vertex);
131 f.setFacetT(qh_qh, facet);
132 nextVertex->setVertexT(qh_qh, vertex);
133 }
134 return f;
135 }//nextFacet2d
136
137 //! Return outerplane clearly above all points
138 //! from io_r.c[qh_PRINTouter]
139 QhullHyperplane QhullFacet::
outerplane() const140 outerplane() const
141 {
142 QhullHyperplane h;
143 if(qh_qh){
144 realT outer;
145 // Does not error, TRY_QHULL_ not needed
146 qh_outerinner(qh_qh, const_cast<facetT *>(getFacetT()), &outer, NULL);
147 h= hyperplane();
148 h.setOffset(h.offset()-outer); //outer is positive
149 }
150 return h;
151 }//outerplane
152
153 //! Set by qh_triangulate for option 'Qt'.
154 //! Errors if tricoplanar and facetArea() or qh_getarea() called first.
155 QhullFacet QhullFacet::
tricoplanarOwner() const156 tricoplanarOwner() const
157 {
158 if(qh_facet->tricoplanar){
159 if(qh_facet->isarea){
160 throw QhullError(10018, "Qhull error: facetArea() or qh_getarea() previously called. triCoplanarOwner() is not available.");
161 }
162 return QhullFacet(qh_qh, qh_facet->f.triowner);
163 }
164 return QhullFacet(qh_qh);
165 }//tricoplanarOwner
166
167 QhullPoint QhullFacet::
voronoiVertex()168 voronoiVertex()
169 {
170 if(qh_qh && qh_qh->CENTERtype!=qh_ASvoronoi){
171 throw QhullError(10052, "Error: QhullFacet.voronoiVertex() requires option 'v' (qh_ASvoronoi)");
172 }
173 return getCenter();
174 }//voronoiVertex
175
176 #//!\name Value
177
178 //! Disables tricoplanarOwner()
179 double QhullFacet::
facetArea()180 facetArea()
181 {
182 if(qh_qh && !qh_facet->isarea){
183 QH_TRY_(qh_qh){ // no object creation -- destructors skipped on longjmp()
184 qh_facet->f.area= qh_facetarea(qh_qh, qh_facet);
185 qh_facet->isarea= True;
186 }
187 qh_qh->NOerrexit= true;
188 qh_qh->maybeThrowQhullMessage(QH_TRY_status);
189 }
190 return qh_facet->f.area;
191 }//facetArea
192
193 #//!\name Foreach
194
195 QhullPointSet QhullFacet::
coplanarPoints() const196 coplanarPoints() const
197 {
198 return QhullPointSet(qh_qh, qh_facet->coplanarset);
199 }//coplanarPoints
200
201 QhullFacetSet QhullFacet::
neighborFacets() const202 neighborFacets() const
203 {
204 return QhullFacetSet(qh_qh, qh_facet->neighbors);
205 }//neighborFacets
206
207 QhullPointSet QhullFacet::
outsidePoints() const208 outsidePoints() const
209 {
210 return QhullPointSet(qh_qh, qh_facet->outsideset);
211 }//outsidePoints
212
213 QhullRidgeSet QhullFacet::
ridges() const214 ridges() const
215 {
216 return QhullRidgeSet(qh_qh, qh_facet->ridges);
217 }//ridges
218
219 QhullVertexSet QhullFacet::
vertices() const220 vertices() const
221 {
222 return QhullVertexSet(qh_qh, qh_facet->vertices);
223 }//vertices
224
225 }//namespace orgQhull
226
227 #//!\name operator<<
228
229 using std::ostream;
230
231 using orgQhull::QhullFacet;
232 using orgQhull::QhullFacetSet;
233 using orgQhull::QhullPoint;
234 using orgQhull::QhullPointSet;
235 using orgQhull::QhullRidge;
236 using orgQhull::QhullRidgeSet;
237 using orgQhull::QhullSetBase;
238 using orgQhull::QhullVertexSet;
239
240 ostream &
operator <<(ostream & os,const QhullFacet::PrintFacet & pr)241 operator<<(ostream &os, const QhullFacet::PrintFacet &pr)
242 {
243 os << pr.message;
244 QhullFacet f= *pr.facet;
245 if(f.getFacetT()==0){ // Special values from set iterator
246 os << " NULLfacet" << endl;
247 return os;
248 }
249 if(f.getFacetT()==qh_MERGEridge){
250 os << " MERGEridge" << endl;
251 return os;
252 }
253 if(f.getFacetT()==qh_DUPLICATEridge){
254 os << " DUPLICATEridge" << endl;
255 return os;
256 }
257 os << f.printHeader();
258 if(!f.ridges().isEmpty()){
259 os << f.printRidges();
260 }
261 return os;
262 }//operator<< PrintFacet
263
264 //! Print Voronoi center or facet centrum to stream. Same as qh_printcenter [_r.]
265 //! Code duplicated for PrintCenter and getCenter
266 //! Sets center if needed
267 ostream &
operator <<(ostream & os,const QhullFacet::PrintCenter & pr)268 operator<<(ostream &os, const QhullFacet::PrintCenter &pr)
269 {
270 facetT *f= pr.facet->getFacetT();
271 if(pr.facet->qh()->CENTERtype!=qh_ASvoronoi && pr.facet->qh()->CENTERtype!=qh_AScentrum){
272 return os;
273 }
274 if(pr.message){
275 os << pr.message;
276 }
277 int numCoords;
278 if(pr.facet->qh()->CENTERtype==qh_ASvoronoi){
279 numCoords= pr.facet->qh()->hull_dim-1;
280 if(!f->normal || !f->upperdelaunay || !pr.facet->qh()->ATinfinity){
281 if(!f->center){
282 f->center= qh_facetcenter(pr.facet->qh(), f->vertices);
283 }
284 for(int k=0; k<numCoords; k++){
285 os << f->center[k] << " "; // QH11010 FIX: qh_REAL_1
286 }
287 }else{
288 for(int k=0; k<numCoords; k++){
289 os << qh_INFINITE << " "; // QH11010 FIX: qh_REAL_1
290 }
291 }
292 }else{ // qh CENTERtype==qh_AScentrum
293 numCoords= pr.facet->qh()->hull_dim;
294 if(pr.print_format==qh_PRINTtriangles && pr.facet->qh()->DELAUNAY){
295 numCoords--;
296 }
297 if(!f->center){
298 f->center= qh_getcentrum(pr.facet->qh(), f);
299 }
300 for(int k=0; k<numCoords; k++){
301 os << f->center[k] << " "; // QH11010 FIX: qh_REAL_1
302 }
303 }
304 if(pr.print_format==qh_PRINTgeom && numCoords==2){
305 os << " 0";
306 }
307 os << endl;
308 return os;
309 }//operator<< PrintCenter
310
311 //! Print flags for facet to stream. Space prefix. From qh_printfacetheader [io_r.c]
312 ostream &
operator <<(ostream & os,const QhullFacet::PrintFlags & p)313 operator<<(ostream &os, const QhullFacet::PrintFlags &p)
314 {
315 const facetT *f= p.facet->getFacetT();
316 if(p.message){
317 os << p.message;
318 }
319
320 os << (p.facet->isTopOrient() ? " top" : " bottom");
321 if(p.facet->isSimplicial()){
322 os << " simplicial";
323 }
324 if(p.facet->isTriCoplanar()){
325 os << " tricoplanar";
326 }
327 if(p.facet->isUpperDelaunay()){
328 os << " upperDelaunay";
329 }
330 if(f->visible){
331 os << " visible";
332 }
333 if(f->newfacet){
334 os << " new";
335 }
336 if(f->tested){
337 os << " tested";
338 }
339 if(!f->good){
340 os << " notG";
341 }
342 if(f->seen && p.facet->qh()->IStracing){
343 os << " seen";
344 }
345 if(f->seen2 && p.facet->qh()->IStracing){
346 os << " seen";
347 }
348 if(f->isarea){
349 os << " isarea";
350 }
351 if(f->coplanarhorizon){
352 os << " coplanarhorizon";
353 }
354 if(f->mergehorizon){
355 os << " mergehorizon";
356 }
357 if(f->cycledone){
358 os << " cycledone";
359 }
360 if(f->keepcentrum){
361 os << " keepcentrum";
362 }
363 if(f->dupridge){
364 os << " dupridge";
365 }
366 if(f->mergeridge && !f->mergeridge2){
367 os << " mergeridge1";
368 }
369 if(f->mergeridge2){
370 os << " mergeridge2";
371 }
372 if(f->newmerge){
373 os << " newmerge";
374 }
375 if(f->flipped){
376 os << " flipped";
377 }
378 if(f->notfurthest){
379 os << " notfurthest";
380 }
381 if(f->degenerate){
382 os << " degenerate";
383 }
384 if(f->redundant){
385 os << " redundant";
386 }
387 os << endl;
388 return os;
389 }//operator<< PrintFlags
390
391 //! Print header for facet to stream. Space prefix. From qh_printfacetheader [io_r.c]
392 ostream &
operator <<(ostream & os,const QhullFacet::PrintHeader & pr)393 operator<<(ostream &os, const QhullFacet::PrintHeader &pr)
394 {
395 QhullFacet facet= *pr.facet;
396 facetT *f= facet.getFacetT();
397 os << "- f" << facet.id() << endl;
398 os << facet.printFlags(" - flags:");
399 if(f->isarea){
400 os << " - area: " << f->f.area << endl; //QH11010 FIX: 2.2g
401 }else if(pr.facet->qh()->NEWfacets && f->visible && f->f.replace){
402 os << " - replacement: f" << f->f.replace->id << endl;
403 }else if(f->newfacet){
404 if(f->f.samecycle && f->f.samecycle != f){
405 os << " - shares same visible/horizon as f" << f->f.samecycle->id << endl;
406 }
407 }else if(f->tricoplanar /* !isarea */){
408 if(f->f.triowner){
409 os << " - owner of normal & centrum is facet f" << f->f.triowner->id << endl;
410 }
411 }else if(f->f.newcycle){
412 os << " - was horizon to f" << f->f.newcycle->id << endl;
413 }
414 if(f->nummerge){
415 os << " - merges: " << f->nummerge << endl;
416 }
417 os << facet.hyperplane().print(" - normal: ", "\n - offset: "); // QH11010 FIX: %10.7g
418 if(pr.facet->qh()->CENTERtype==qh_ASvoronoi || f->center){
419 os << facet.printCenter(qh_PRINTfacets, " - center: ");
420 }
421 #if qh_MAXoutside
422 if(f->maxoutside > pr.facet->qh()->DISTround){
423 os << " - maxoutside: " << f->maxoutside << endl; //QH11010 FIX: %10.7g
424 }
425 #endif
426 QhullPointSet ps= facet.outsidePoints();
427 if(!ps.isEmpty()){
428 QhullPoint furthest= ps.last();
429 if(ps.size() < 6){
430 os << " - outside set(furthest p" << furthest.id() << "):" << endl;
431 for(QhullPointSet::iterator i=ps.begin(); i!=ps.end(); ++i){
432 QhullPoint p= *i;
433 os << p.print(" ");
434 }
435 }else if(ps.size()<21){
436 os << ps.print(" - outside set:");
437 }else{
438 os << " - outside set: " << ps.size() << " points.";
439 os << furthest.print(" Furthest");
440 }
441 #if !qh_COMPUTEfurthest
442 os << " - furthest distance= " << f->furthestdist << endl; //QH11010 FIX: %2.2g
443 #endif
444 }
445 QhullPointSet cs= facet.coplanarPoints();
446 if(!cs.isEmpty()){
447 QhullPoint furthest= cs.last();
448 if(cs.size() < 6){
449 os << " - coplanar set(furthest p" << furthest.id() << "):" << endl;
450 for(QhullPointSet::iterator i=cs.begin(); i!=cs.end(); ++i){
451 QhullPoint p= *i;
452 os << p.print(" ");
453 }
454 }else if(cs.size()<21){
455 os << cs.print(" - coplanar set:");
456 }else{
457 os << " - coplanar set: " << cs.size() << " points.";
458 os << furthest.print(" Furthest");
459 }
460 // QH11027 FIX: Can/should zinc_(Zdistio) be called from C++ interface
461 double d= facet.distance(furthest);
462 os << " furthest distance= " << d << endl; //QH11010 FIX: %2.2g
463 }
464 QhullVertexSet vs= facet.vertices();
465 if(!vs.isEmpty()){
466 os << vs.print(" - vertices:");
467 }
468 QhullFacetSet fs= facet.neighborFacets();
469 fs.selectAll();
470 if(!fs.isEmpty()){
471 os << fs.printIdentifiers(" - neighboring facets:");
472 }
473 return os;
474 }//operator<< PrintHeader
475
476
477 //! Print ridges of facet to stream. Same as qh_printfacetridges [io_r.c]
478 ostream &
operator <<(ostream & os,const QhullFacet::PrintRidges & pr)479 operator<<(ostream &os, const QhullFacet::PrintRidges &pr)
480 {
481 const QhullFacet facet= *pr.facet;
482 facetT *f= facet.getFacetT();
483 QhullRidgeSet rs= facet.ridges();
484 if(!rs.isEmpty()){
485 if(f->visible && pr.facet->qh()->NEWfacets){
486 os << " - ridges(ids may be garbage):";
487 for(QhullRidgeSet::iterator i=rs.begin(); i!=rs.end(); ++i){
488 QhullRidge r= *i;
489 os << " r" << r.id();
490 }
491 os << endl;
492 }else{
493 os << " - ridges:" << endl;
494 }
495
496 // Keep track of printed ridges
497 for(QhullRidgeSet::iterator i=rs.begin(); i!=rs.end(); ++i){
498 QhullRidge r= *i;
499 r.getRidgeT()->seen= false;
500 }
501 int ridgeCount= 0;
502 if(facet.dimension()==3){
503 for(QhullRidge r= rs.first(); !r.getRidgeT()->seen; r= r.nextRidge3d(facet)){
504 r.getRidgeT()->seen= true;
505 os << r.print("");
506 ++ridgeCount;
507 if(!r.hasNextRidge3d(facet)){
508 break;
509 }
510 }
511 }else{
512 QhullFacetSet ns(facet.neighborFacets());
513 for(QhullFacetSet::iterator i=ns.begin(); i!=ns.end(); ++i){
514 QhullFacet neighbor= *i;
515 QhullRidgeSet nrs(neighbor.ridges());
516 for(QhullRidgeSet::iterator j=nrs.begin(); j!=nrs.end(); ++j){
517 QhullRidge r= *j;
518 if(r.otherFacet(neighbor)==facet){
519 r.getRidgeT()->seen= true;
520 os << r.print("");
521 ridgeCount++;
522 }
523 }
524 }
525 }
526 if(ridgeCount!=rs.count()){
527 os << " - all ridges:";
528 for(QhullRidgeSet::iterator i=rs.begin(); i!=rs.end(); ++i){
529 QhullRidge r= *i;
530 os << " r" << r.id();
531 }
532 os << endl;
533 }
534 for(QhullRidgeSet::iterator i=rs.begin(); i!=rs.end(); ++i){
535 QhullRidge r= *i;
536 if(!r.getRidgeT()->seen){
537 os << r.print("");
538 }
539 }
540 }
541 return os;
542 }//operator<< PrintRidges
543
544 // "No conversion" error if defined inline
545 ostream &
operator <<(ostream & os,QhullFacet & f)546 operator<<(ostream &os, QhullFacet &f)
547 {
548 os << f.print("");
549 return os;
550 }//<< QhullFacet
551