1 /****************************************************************************
2 **
3 ** Copyright (c) 2008-2019 C.B. Barber. All rights reserved.
4 ** $Id: //main/2019/qhull/src/qhulltest/Qhull_test.cpp#3 $$Change: 2695 $
5 ** $DateTime: 2019/06/21 16:40:16 $$Author: bbarber $
6 **
7 ****************************************************************************/
8 
9 //pre-compiled headers
10 #include <iostream>
11 #include "qhulltest/RoadTest.h" // QT_VERSION
12 
13 #include "libqhullcpp/Qhull.h"
14 #include "libqhullcpp/QhullError.h"
15 #include "libqhullcpp/RboxPoints.h"
16 #include "libqhullcpp/QhullFacetList.h"
17 
18 using std::cout;
19 using std::endl;
20 using std::string;
21 
22 namespace orgQhull {
23 
24 //! Test C++ interface to Qhull
25 //! See eg/q_test for tests of Qhull commands
26 class Qhull_test : public RoadTest
27 {
28     Q_OBJECT
29 
30 #//!\name Test slots
31 private slots:
32     void cleanup();
33     void t_construct();
34     void t_attribute();
35     void t_message();
36     void t_getSet();
37     void t_getQh();
38     void t_getValue();
39     void t_foreach();
40     void t_modify();
41 };//Qhull_test
42 
43 void
add_Qhull_test()44 add_Qhull_test()
45 {
46     new Qhull_test();  // RoadTest::s_testcases
47 }
48 
49 //Executed after each testcase
50 void Qhull_test::
cleanup()51 cleanup()
52 {
53     RoadTest::cleanup();
54 }
55 
56 void Qhull_test::
t_construct()57 t_construct()
58 {
59     {
60         Qhull q;
61         QCOMPARE(q.dimension(),0);
62         QVERIFY(q.qh()!=0);
63         QCOMPARE(QString(q.qhullCommand()),QString(""));
64         QCOMPARE(QString(q.rboxCommand()),QString(""));
65         try{
66             QCOMPARE(q.area(),0.0);
67             QFAIL("area() did not fail.");
68         }catch (const std::exception &e) {
69             cout << "INFO   : Caught " << e.what();
70         }
71     }
72     {
73         RboxPoints rbox("10000");
74         Qhull q(rbox, "QR0"); // Random points in a randomly rotated cube.
75         QCOMPARE(q.dimension(),3);
76         QVERIFY(q.volume() < 1.0);
77         QVERIFY(q.volume() > 0.99);
78     }
79     {
80         double points[] = {
81             0, 0,
82             1, 0,
83             1, 1
84         };
85         Qhull q("triangle", 2, 3, points, "");
86         QCOMPARE(q.dimension(),2);
87         QCOMPARE(q.facetCount(),3);
88         QCOMPARE(q.vertexCount(),3);
89         QCOMPARE(q.dimension(),2);
90         double area= (2.0 + sqrt(2.0));
91         double delta= fabs(q.area() - area);
92         double qtDelta= 0.000000000001 * area;
93         cout << "area delta " << delta << ". qFuzzyCompare delta " << qtDelta << endl;
94         QCOMPARE(q.area(), 2.0+sqrt(2.0)); // length of boundary
95         QCOMPARE(q.volume(), 0.5);        // the 2-d area
96     }
97 }//t_construct
98 
99 void Qhull_test::
t_attribute()100 t_attribute()
101 {
102     RboxPoints rcube("c");
103     {
104         double normals[] = {
105             0,  -1, -0.5,
106            -1,   0, -0.5,
107             1,   0, -0.5,
108             0,   1, -0.5
109         };
110         Qhull q;
111         Coordinates feasible;
112         feasible << 0.0 << 0.0;
113         q.setFeasiblePoint(feasible);
114         Coordinates c(std::vector<double>(2, 0.0));
115         QVERIFY(q.feasiblePoint()==c);
116         q.setOutputStream(&cout);
117         q.runQhull("normals of square", 3, 4, normals, "H"); // halfspace intersect
118         QVERIFY(q.feasiblePoint()==c); // from qh.feasible_point after runQhull()
119         QCOMPARE(q.facetList().count(), 4); // Vertices of square
120         cout << "Expecting summary of halfspace intersection\n";
121         q.outputQhull();
122         q.qh()->disableOutputStream();  // Same as q.disableOutputStream()
123         cout << "Expecting no output from qh_fprintf() in Qhull.cpp\n";
124         q.outputQhull();
125     }
126 }//t_attribute
127 
128 //! No QhullMessage for errors outside of qhull
129 void Qhull_test::
t_message()130 t_message()
131 {
132     RboxPoints rcube("c");
133     {
134         Qhull q;
135         QCOMPARE(q.qhullMessage(), string(""));
136         QCOMPARE(q.qhullStatus(), qh_ERRnone);
137         QVERIFY(!q.hasQhullMessage());
138         try{
139             q.runQhull(rcube, "Fd");
140             QFAIL("runQhull Fd did not fail.");
141         }catch (const std::exception &e) {
142             const char *s= e.what();
143             cout << "INFO   : Caught " << s;
144             QCOMPARE(QString::fromStdString(s).left(6), QString("QH6029"));
145             // QH11025 FIX: review decision to clearQhullMessage at QhullError()            // Cleared when copied to QhullError
146             QVERIFY(!q.hasQhullMessage());
147             // QCOMPARE(q.qhullMessage(), QString::fromStdString(s).remove(0, 7));
148             // QCOMPARE(q.qhullStatus(), 6029);
149             q.clearQhullMessage();
150             QVERIFY(!q.hasQhullMessage());
151         }
152         q.appendQhullMessage("Append 1");
153         QVERIFY(q.hasQhullMessage());
154         QCOMPARE(QString::fromStdString(q.qhullMessage()), QString("Append 1"));
155         q.appendQhullMessage("\nAppend 2\n");
156         QCOMPARE(QString::fromStdString(q.qhullMessage()), QString("Append 1\nAppend 2\n"));
157         q.clearQhullMessage();
158         QVERIFY(!q.hasQhullMessage());
159         QCOMPARE(QString::fromStdString(q.qhullMessage()), QString(""));
160     }
161     {
162         cout << "INFO   : Error stream without output stream\n";
163         Qhull q;
164         q.setErrorStream(&cout);
165         q.setOutputStream(0);
166         try{
167             q.runQhull(rcube, "Fd");
168             QFAIL("runQhull Fd did not fail.");
169         }catch (const QhullError &e) {
170             cout << "INFO   : Caught " << e;
171             QCOMPARE(e.errorCode(), 6029);
172         }
173         //QH11025 FIX: Qhullmessage cleared when QhullError thrown.  Switched to e
174         //QVERIFY(q.hasQhullMessage());
175         //QCOMPARE(QString::fromStdString(q.qhullMessage()).left(6), QString("QH6029"));
176         q.clearQhullMessage();
177         QVERIFY(!q.hasQhullMessage());
178     }
179     {
180         cout << "INFO   : Error output sent to output stream without error stream\n";
181         Qhull q;
182         q.setErrorStream(0);
183         q.setOutputStream(&cout);
184         try{
185             q.runQhull(rcube, "Tz H0");
186             QFAIL("runQhull TZ did not fail.");
187         }catch (const std::exception &e) {
188             const char *s= e.what();
189             cout << "INFO   : Caught " << s;
190             QCOMPARE(QString::fromLatin1(s).left(6), QString("QH6023"));
191         }
192         //QH11025 FIX: Qhullmessage cleared when QhullError thrown.  Switched to e
193         //QVERIFY(q.hasQhullMessage());
194         //QCOMPARE(QString::fromStdString(q.qhullMessage()).left(17), QString("qhull: no message"));
195         //QCOMPARE(q.qhullStatus(), 6023);
196         q.clearQhullMessage();
197         QVERIFY(!q.hasQhullMessage());
198     }
199     {
200         cout << "INFO   : No error stream or output stream\n";
201         Qhull q;
202         q.setErrorStream(0);
203         q.setOutputStream(0);
204         try{
205             q.runQhull(rcube, "Fd");
206             QFAIL("outputQhull did not fail.");
207         }catch (const std::exception &e) {
208             const char *s= e.what();
209             cout << "INFO   : Caught " << s;
210             QCOMPARE(QString::fromLatin1(s).left(6), QString("QH6029"));
211         }
212         //QH11025 FIX: QhullMessage cleared when QhullError thrown.  Switched to e
213         //QVERIFY(q.hasQhullMessage());
214         //QCOMPARE(QString::fromStdString(q.qhullMessage()).left(9), QString("qhull err"));
215         //QCOMPARE(q.qhullStatus(), 6029);
216         q.clearQhullMessage();
217         QVERIFY(!q.hasQhullMessage());
218     }
219 }//t_message
220 
221 void Qhull_test::
t_getSet()222 t_getSet()
223 {
224     RboxPoints rcube("c");
225     {
226         Qhull q;
227         QVERIFY(!q.initialized());
228         q.runQhull(rcube, "s");
229         QVERIFY(q.initialized());
230         QCOMPARE(q.dimension(), 3);
231         QhullPoint p= q.origin();
232         QCOMPARE(p.dimension(), 3);
233         QCOMPARE(p[0]+p[1]+p[2], 0.0);
234         q.setErrorStream(&cout);
235         q.outputQhull();
236     }
237     {
238         Qhull q;
239         q.runQhull(rcube, "");
240         q.setOutputStream(&cout);
241         q.outputQhull();
242     }
243 }//t_getSet
244 
245 void Qhull_test::
t_getQh()246 t_getQh()
247 {
248     RboxPoints rcube("c");
249     {
250         Qhull q;
251         q.runQhull(rcube, "s");
252         QCOMPARE(QString(q.qhullCommand()), QString("qhull s"));
253         QCOMPARE(QString(q.rboxCommand()), QString("rbox \"c\""));
254         QCOMPARE(q.facetCount(), 6);
255         QCOMPARE(q.vertexCount(), 8);
256         // Sample fields from Qhull's qhT [libqhull.h]
257         QCOMPARE(q.qh()->ALLpoints, 0u);
258         QCOMPARE(q.qh()->GOODpoint, 0);
259         QCOMPARE(q.qh()->IStracing, 0);
260         QCOMPARE(q.qh()->MAXcoplanar+1.0, 1.0); // fuzzy compare
261         QCOMPARE(q.qh()->MERGING, 1u);
262         QCOMPARE(q.qh()->input_dim, 3);
263         QCOMPARE(QString(q.qh()->qhull_options).left(8), QString("  run-id"));
264         QCOMPARE(q.qh()->num_facets, 6);
265         QCOMPARE(q.qh()->hasTriangulation, 0u);
266         QCOMPARE(q.qh()->max_outside - q.qh()->min_vertex + 1.0, 1.0); // fuzzy compare
267         QCOMPARE(*q.qh()->gm_matrix+1.0, 1.0); // fuzzy compare
268     }
269 }//t_getQh
270 
271 void Qhull_test::
t_getValue()272 t_getValue()
273 {
274     RboxPoints rcube("c");
275     {
276         Qhull q;
277         q.runQhull(rcube, "");
278         QCOMPARE(q.area(), 6.0);
279         QCOMPARE(q.volume(), 1.0);
280     }
281 }//t_getValue
282 
283 void Qhull_test::
t_foreach()284 t_foreach()
285 {
286     RboxPoints rcube("c");
287     {
288         Qhull q;
289         QCOMPARE(q.beginFacet(),q.endFacet());
290         QCOMPARE(q.beginVertex(),q.endVertex());
291         q.runQhull(rcube, "");
292         QCOMPARE(q.facetList().count(), 6);
293 
294         // defineVertexNeighborFacets() tested in QhullVertex_test::t_io()
295 
296         QhullFacetList facets(q.beginFacet(), q.endFacet());
297         QCOMPARE(facets.count(), 6);
298         QCOMPARE(q.firstFacet(), q.beginFacet());
299         QhullVertexList vertices(q.beginVertex(), q.endVertex());
300         QCOMPARE(vertices.count(), 8);
301         QCOMPARE(q.firstVertex(), q.beginVertex());
302         QhullPoints ps= q.points();
303         QCOMPARE(ps.count(), 8);
304         QhullPointSet ps2= q.otherPoints();
305         QCOMPARE(ps2.count(), 0);
306         // ps2= q.otherPoints(); //disabled, would not copy the points
307         QCOMPARE(q.facetCount(), 6);
308         QCOMPARE(q.vertexCount(), 8);
309         coordT *c= q.pointCoordinateBegin(); // of q.points()
310         QVERIFY(*c==0.5 || *c==-0.5);
311         coordT *c3= q.pointCoordinateEnd();
312         QVERIFY(c3[-1]==0.5 || c3[-1]==-0.5);
313         QCOMPARE(c3-c, 8*3);
314         QCOMPARE(q.vertexList().count(), 8);
315     }
316 }//t_foreach
317 
318 void Qhull_test::
t_modify()319 t_modify()
320 {
321     //addPoint() tested in t_foreach
322     RboxPoints diamond("d");
323     Qhull q(diamond, "o");
324     q.setOutputStream(&cout);
325     cout << "Expecting vertexList and facetList of a 3-d diamond.\n";
326     q.outputQhull();
327     cout << "Expecting normals of a 3-d diamond.\n";
328     q.outputQhull("n");
329     // runQhull tested in t_attribute(), t_message(), etc.
330 }//t_modify
331 
332 }//orgQhull
333 
334 // Redefine Qhull's usermem_r.c in order to report erroneous calls to qh_exit
qh_exit(int exitcode)335 void qh_exit(int exitcode) {
336     cout << "FAIL!  : Qhull called qh_exit().  Qhull's error handling not available.\n.. See the corresponding Qhull:qhull_message or setErrorStream().\n";
337     exit(exitcode);
338 }
qh_fprintf_stderr(int msgcode,const char * fmt,...)339 void qh_fprintf_stderr(int msgcode, const char *fmt, ... ) {
340     va_list args;
341 
342     va_start(args, fmt);
343     if(msgcode)
344         fprintf(stderr, "QH%.4d ", msgcode);
345     vfprintf(stderr, fmt, args);
346     va_end(args);
347 } /* fprintf_stderr */
qh_free(void * mem)348 void qh_free(void *mem) {
349     free(mem);
350 }
qh_malloc(size_t size)351 void *qh_malloc(size_t size) {
352     return malloc(size);
353 }
354 
355 #if 0
356 template<> char * QTest::
357 toString(const std::string &s)
358 {
359     QByteArray ba = s.c_str();
360     return qstrdup(ba.data());
361 }
362 #endif
363 
364 #include "moc/Qhull_test.moc"
365