1 /****************************************************************************
2 **
3 ** Copyright (c) 2008-2020 C.B. Barber. All rights reserved.
4 ** $Id: //main/2019/qhull/src/qhulltest/QhullFacet_test.cpp#6 $$Change: 3006 $
5 ** $DateTime: 2020/07/29 18:28:16 $$Author: bbarber $
6 **
7 ****************************************************************************/
8 
9 //pre-compiled headers
10 #include <iostream>
11 #include "qhulltest/RoadTest.h" // QT_VERSION
12 
13 #include "libqhullcpp/QhullFacet.h"
14 #include "libqhullcpp/QhullError.h"
15 #include "libqhullcpp/Coordinates.h"
16 #include "libqhullcpp/RboxPoints.h"
17 #include "libqhullcpp/QhullFacetList.h"
18 #include "libqhullcpp/QhullFacetSet.h"
19 #include "libqhullcpp/QhullPointSet.h"
20 #include "libqhullcpp/QhullRidge.h"
21 #include "libqhullcpp/Qhull.h"
22 
23 using std::cout;
24 using std::endl;
25 using std::ostringstream;
26 using std::ostream;
27 using std::string;
28 
29 namespace orgQhull {
30 
31 class QhullFacet_test : public RoadTest
32 {
33     Q_OBJECT
34 
35 #//!\name Test slots
36 private slots:
37     void cleanup();
38     void t_construct_qh();
39     void t_constructConvert();
40     void t_getSet();
41     void t_getSet2d();
42     void t_value();
43     void t_foreach();
44     void t_io();
45 };//QhullFacet_test
46 
47 void
add_QhullFacet_test()48 add_QhullFacet_test()
49 {
50     new QhullFacet_test();  // RoadTest::s_testcases
51 }
52 
53 //Executed after each testcase
54 void QhullFacet_test::
cleanup()55 cleanup()
56 {
57     RoadTest::cleanup();
58 }
59 
60 void QhullFacet_test::
t_construct_qh()61 t_construct_qh()
62 {
63     // Qhull.runQhull() constructs QhullFacets as facetT
64     QhullQh qh;
65     QhullFacet f(&qh);
66     QVERIFY(!f.isValid());
67     QCOMPARE(f.dimension(),0);
68 }//t_construct_qh
69 
70 void QhullFacet_test::
t_constructConvert()71 t_constructConvert()
72 {
73     // Qhull.runQhull() constructs QhullFacets as facetT
74     Qhull q2;
75     QhullFacet f(q2);
76     QVERIFY(!f.isValid());
77     QCOMPARE(f.dimension(),0);
78     RboxPoints rcube("c");
79     Qhull q(rcube,"Qt QR0");  // triangulation of rotated unit cube
80     QhullFacet f2(q.beginFacet());
81     QCOMPARE(f2.dimension(),3);
82     f= f2; // copy assignment
83     QVERIFY(f.isValid());
84     QCOMPARE(f.dimension(),3);
85     QhullFacet f5= f2;
86     QVERIFY(f5==f2);
87     QVERIFY(f5==f);
88     QhullFacet f3(q, f2.getFacetT());
89     QCOMPARE(f,f3);
90     QhullFacet f4(q, f2.getBaseT());
91     QCOMPARE(f,f4);
92 }//t_constructConvert
93 
94 void QhullFacet_test::
t_getSet()95 t_getSet()
96 {
97     RboxPoints rcube("c");
98     {
99         Qhull q(rcube,"Qt QR0");  // triangulation of rotated unit cube
100         cout << " rbox c | qhull Qt QR0 QR" << q.rotateRandom() << "   distanceEpsilon " << q.distanceEpsilon() << endl;
101         QCOMPARE(q.facetCount(), 12);
102         QCOMPARE(q.vertexCount(), 8);
103         QhullFacetListIterator i(q.facetList());
104         while(i.hasNext()){
105             const QhullFacet f= i.next();
106             cout << f.id() << endl;
107             QhullFacet f2;
108             f2.setFacetT(f.qh(), f.getFacetT());
109             QCOMPARE(f, f2);
110             QCOMPARE(f.dimension(),3);
111             QVERIFY(f.id()>0 && f.id()<=39);
112             QVERIFY(f.isValid());
113             if(i.hasNext()){
114                 QCOMPARE(f.next(), i.peekNext());
115                 QVERIFY(f.next()!=f);
116                 QCOMPARE(f.next().previous(), f);
117                 QVERIFY(f.hasNext());
118                 QVERIFY(f.next().hasPrevious());
119             }else
120               QVERIFY(!f.hasNext());
121             QVERIFY(i.hasPrevious());
122             QCOMPARE(f, i.peekPrevious());
123         }
124         while(i.hasPrevious()){
125           const QhullFacet f= i.previous();
126           cout << f.id() << endl;
127           QVERIFY(f.isValid());
128           if(i.hasPrevious()){
129             QVERIFY(f.hasPrevious());
130             QCOMPARE(f.previous(), i.peekPrevious());
131             QVERIFY(f.previous()!=f);
132             QVERIFY(f.previous().hasNext());
133             QCOMPARE(f.previous().next(), f);
134           }else
135             QVERIFY(!f.hasPrevious());
136           QVERIFY(i.hasNext());
137           QCOMPARE(f, i.peekNext());
138         }
139         // test tricoplanarOwner
140         QhullFacet facet= q.beginFacet();
141         QhullFacet tricoplanarOwner= facet.tricoplanarOwner();
142         int tricoplanarCount= 0;
143         i.toFront();
144         while(i.hasNext()){
145             const QhullFacet f= i.next();
146             if(f.tricoplanarOwner()==tricoplanarOwner){
147                 tricoplanarCount++;
148             }
149         }
150         QCOMPARE(tricoplanarCount, 2);
151         int tricoplanarCount2= 0;
152         foreach (QhullFacet f, q.facetList()){  // Qt only
153             QhullHyperplane hi= f.innerplane();
154             QCOMPARE(hi.count(), 3);
155             double innerOffset= hi.offset() + 0.5;
156             cout << "InnerPlane: " << hi << "   innerOffset+0.5 " << innerOffset << endl;
157             QhullHyperplane h= f.hyperplane();
158             double offset= h.offset() + 0.5;
159             cout << "Hyperplane: " << h << "   offset+0.5 " << offset << endl;
160             QCOMPARE(h.count(), 3);
161             QCOMPARE(h.offset(), -0.5);
162             double n= h.norm();
163             QCOMPARE(n, 1.0);
164             QhullHyperplane ho= f.outerplane();
165             QCOMPARE(ho.count(), 3);
166             double outerOffset= ho.offset()+0.5;
167             cout << "OuterPlane: " << ho << "   outerOffset+0.5 " << outerOffset << endl;
168             QVERIFY(offset < innerOffset); // the outerOffset is more negative than the innerOffset.  The expected values are -0.5 for the unit cube centered at the origin
169             QVERIFY(outerOffset < offset); // QR1595623290 was 8.4x,   innerOffset+0.5 9.71445e-15, 2.5e-16 max. distance of a new vertex to a facet
170             QVERIFY2(fabs(innerOffset) < (10 * q.distanceEpsilon()), "Guessed epsilon from -0.5 due to rotation");
171             QVERIFY2(fabs(outerOffset) < (10 * q.distanceEpsilon()), "Guessed epsilon from -0.5 due to rotation");
172 
173             for(int k= 0; k<3; k++){
174                 QVERIFY(ho[k]==hi[k]);
175                 QVERIFY(ho[k]==h[k]);
176             }
177             QhullPoint center= f.getCenter();
178             cout << "Center: " << center;
179             double d= f.distance(center);
180             QVERIFY(d < innerOffset-outerOffset);
181             QhullPoint center2= f.getCenter(qh_PRINTcentrums);
182             QCOMPARE(center, center2);
183             if(f.tricoplanarOwner()==tricoplanarOwner){
184                 tricoplanarCount2++;
185             }
186             cout << endl;
187         }
188         QCOMPARE(tricoplanarCount2, 2);
189         Qhull q2(rcube,"d Qz Qt QR0");  // 3-d triangulation of Delaunay triangulation (the cube)
190         cout << " rbox c | qhull d Qz Qt QR0 QR" << q2.rotateRandom() << "   distanceEpsilon " << q2.distanceEpsilon() << endl;
191         QhullFacet f2= q2.firstFacet();
192         QhullPoint center3= f2.getCenter(qh_PRINTtriangles);
193         QCOMPARE(center3.dimension(), 3);
194         QhullPoint center4= f2.getCenter();
195         QCOMPARE(center4.dimension(), 4);
196         for(int k= 0; k<3; k++){
197             QVERIFY(center4[k]==center3[k]);
198         }
199         Qhull q3(rcube,"v Qz QR0");  // Voronoi diagram of a cube (one vertex)
200         cout << " rbox c | qhull v Qz QR0 QR" << q3.rotateRandom() << "   distanceEpsilon " << q3.distanceEpsilon() << endl;
201 
202         q3.setFactorEpsilon(400); // Voronoi vertices are not necessarily within distance episilon
203         QhullPoint origin= q3.inputOrigin();
204         int voronoiCount= 0;
205         foreach(QhullFacet f, q3.facetList()){ //Qt only
206             if(f.isGood()){
207                 ++voronoiCount;
208                 QhullPoint p= f.voronoiVertex();
209                 cout << p.print("Voronoi vertex: ")
210                     << " Is it within " << q3.factorEpsilon() << " * distanceEpsilon (" << q3.distanceEpsilon() << ") of the origin?" << endl;
211                 QCOMPARE(p, origin);
212             }
213         }
214         QCOMPARE(voronoiCount, 1);
215     }
216 }//t_getSet
217 
218 void QhullFacet_test::
t_getSet2d()219 t_getSet2d()
220 {
221     RboxPoints rsquare("c D2");
222     Qhull q(rsquare, "o");  // convex hull of square
223     q.setOutputStream(&cout);
224     cout << "Points and facets.  Facet vertices in counter-clockwise order (option 'o')\n";
225     q.outputQhull();
226     int n= q.facetCount();
227     QhullFacet f= q.firstFacet();
228     QhullVertex v;
229     cout << "Facets and vertices in counter-clockwise order (f.nextFacet2d)\n";
230     for(int i= 0; i<n; ++i){
231         f= f.nextFacet2d(&v);
232         cout << "f" << f.id() << " v" << v.id() << " p" << v.point().id() << "\n";
233     }
234     cout << "Extreme points in counter-clockwise order (option 'Fx')\n";
235     q.outputQhull("Fx");
236 }//t_getSet2d
237 
238 void QhullFacet_test::
t_value()239 t_value()
240 {
241     RboxPoints rcube("c");
242     {
243         Qhull q(rcube, "");
244         coordT c[]= {0.0, 0.0, 0.0};
245         foreach (QhullFacet f, q.facetList()){  // Qt only
246             double d= f.distance(q.origin());
247             QCOMPARE(d, -0.5);
248             double d0= f.distance(c);
249             QCOMPARE(d0, -0.5);
250             double facetArea= f.facetArea();
251             QCOMPARE(facetArea, 1.0);
252             #if qh_MAXoutside
253                 double maxoutside= f.getFacetT()->maxoutside;
254                 QVERIFY(maxoutside<1e-7);
255             #endif
256         }
257     }
258 }//t_value
259 
260 void QhullFacet_test::
t_foreach()261 t_foreach()
262 {
263     RboxPoints rcube("c W0 300");  // cube plus 300 points on its surface
264     {
265         Qhull q(rcube, "QR0 Qc"); // keep coplanars, thick facet, and rotate the cube
266         int coplanarCount= 0;
267         foreach(const QhullFacet f, q.facetList()){
268             QhullPointSet coplanars= f.coplanarPoints();
269             coplanarCount += coplanars.count();
270             QhullFacetSet neighbors= f.neighborFacets();
271             QCOMPARE(neighbors.count(), 4);
272             QhullPointSet outsides= f.outsidePoints();
273             QCOMPARE(outsides.count(), 0);
274             QhullRidgeSet ridges= f.ridges();
275             QCOMPARE(ridges.count(), 4);
276             QhullVertexSet vertices= f.vertices();
277             QCOMPARE(vertices.count(), 4);
278             int ridgeCount= 0;
279             QhullRidge r= ridges.first();
280             for(int r0= r.id(); ridgeCount==0 || r.id()!=r0; r= r.nextRidge3d(f)){
281                 ++ridgeCount;
282                 if(!r.hasNextRidge3d(f)){
283                     QFAIL("Unexpected simplicial facet.  They only have ridges to non-simplicial neighbors.");
284                 }
285             }
286             QCOMPARE(ridgeCount, 4);
287         }
288         QCOMPARE(coplanarCount, 300);
289     }
290 }//t_foreach
291 
292 void QhullFacet_test::
t_io()293 t_io()
294 {
295     RboxPoints rcube("c");
296     {
297         Qhull q(rcube, "");
298         QhullFacet f= q.beginFacet();
299         cout << f;
300         ostringstream os;
301         os << f.print("\nWith a message\n");
302         os << "\nPrint header for the same facet\n";
303         os << f.printHeader();
304         os << "\nPrint each component\n";
305         os << f.printFlags("    - flags:");
306         os << f.printCenter(qh_PRINTfacets, "    - center: ");
307         os << f.printRidges();
308         cout << os.str();
309         ostringstream os2;
310         os2 << f;
311         QString facetString2= QString::fromStdString(os2.str());
312         facetString2.replace(QRegExp("\\s\\s+"), " ");
313         facetString2.replace(QRegExp("seen "), "");
314         ostringstream os3;
315         q.qh()->setOutputStream(&os3);
316         q.outputQhull("f");
317         QString facetsString= QString::fromStdString(os3.str());
318         QString facetString3= facetsString.mid(facetsString.indexOf("- f1\n"));
319         facetString3= facetString3.left(facetString3.indexOf("\n- f")+1);
320         facetString3.replace(QRegExp("\\s\\s+"), " ");
321         QCOMPARE(facetString2, facetString3);
322     }
323 }//t_io
324 
325 // toQhullFacet is static_cast only
326 
327 }//orgQhull
328 
329 #include "moc/QhullFacet_test.moc"
330