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