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