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