Free cookie consent management tool by TermsFeed Policy Generator

source: branches/HeuristicLab.Analysis.AlgorithmBehavior/qhull-2012.1/src/libqhullcpp/Qhull.cpp @ 10207

Last change on this file since 10207 was 10207, checked in by ascheibe, 11 years ago

#1886 added a unit test for volume calculation and the qhull library

File size: 16.1 KB
Line 
1/****************************************************************************
2**
3** Copyright (c) 2008-2012 C.B. Barber. All rights reserved.
4** $Id: //main/2011/qhull/src/libqhullcpp/Qhull.cpp#6 $$Change: 1464 $
5** $DateTime: 2012/01/25 22:58:41 $$Author: bbarber $
6**
7****************************************************************************/
8
9#//! Qhull -- invoke qhull from C++
10#//! Compile libqhull and Qhull together due to use of setjmp/longjmp()
11
12#include "QhullError.h"
13#include "UsingLibQhull.h"
14#include "RboxPoints.h"
15#include "QhullQh.h"
16#include "QhullFacet.h"
17#include "QhullFacetList.h"
18#include "Qhull.h"
19extern "C" {
20    #include "libqhull/qhull_a.h"
21}
22
23#include <iostream>
24
25using std::cerr;
26using std::string;
27using std::vector;
28using std::ostream;
29
30#ifdef _MSC_VER  // Microsoft Visual C++ -- warning level 4
31#pragma warning( disable : 4611)  // interaction between '_setjmp' and C++ object destruction is non-portable
32#pragma warning( disable : 4996)  // function was declared deprecated(strcpy, localtime, etc.)
33#endif
34
35namespace orgQhull {
36
37#//Global variables
38
39char s_unsupported_options[]=" Fd TI ";
40char s_not_output_options[]= " Fd TI A C d E H P Qb QbB Qbb Qc Qf Qg Qi Qm QJ Qr QR Qs Qt Qv Qx Qz Q0 Q1 Q2 Q3 Q4 Q5 Q6 Q7 Q8 Q9 Q10 Q11 R Tc TC TM TP TR Tv TV TW U v V W ";
41
42#//Constructor, destructor, etc.
43Qhull::
44Qhull()
45: qhull_qh(0)
46, qhull_run_id(UsingLibQhull::NOqhRunId)
47, origin_point()
48, qhull_status(qh_ERRnone)
49, qhull_dimension(0)
50, run_called(false)
51, qh_active(false)
52, qhull_message()
53, error_stream(0)
54, output_stream(0)
55, feasiblePoint()
56, useOutputStream(false)
57{
58    initializeQhull();
59}//Qhull
60
61Qhull::
62Qhull(const RboxPoints &rboxPoints, const char *qhullCommand2)
63: qhull_qh(0)
64, qhull_run_id(UsingLibQhull::NOqhRunId)
65, origin_point()
66, qhull_status(qh_ERRnone)
67, qhull_dimension(0)
68, run_called(false)
69, qh_active(false)
70, qhull_message()
71, error_stream(0)
72, output_stream(0)
73, feasiblePoint()
74, useOutputStream(false)
75{
76    initializeQhull();
77    runQhull(rboxPoints, qhullCommand2);
78}//Qhull rbox
79
80Qhull::
81Qhull(const char *rboxCommand2, int pointDimension, int pointCount, const realT *pointCoordinates, const char *qhullCommand2)
82: qhull_qh(0)
83, qhull_run_id(UsingLibQhull::NOqhRunId)
84, origin_point()
85, qhull_status(qh_ERRnone)
86, qhull_dimension(0)
87, run_called(false)
88, qh_active(false)
89, qhull_message()
90, error_stream(0)
91, output_stream(0)
92, feasiblePoint()
93, useOutputStream(false)
94{
95    initializeQhull();
96    runQhull(rboxCommand2, pointDimension, pointCount, pointCoordinates, qhullCommand2);
97}//Qhull points
98
99Qhull::
100Qhull(const Qhull &other)
101: qhull_qh(0)
102, qhull_run_id(UsingLibQhull::NOqhRunId)
103, origin_point()
104, qhull_status(qh_ERRnone)
105, qhull_dimension(0)
106, run_called(false)
107, qh_active(false)
108, qhull_message(other.qhull_message)
109, error_stream(other.error_stream)
110, output_stream(other.output_stream)
111, feasiblePoint(other.feasiblePoint)
112, useOutputStream(other.useOutputStream)
113{
114    if(other.initialized()){
115        throw QhullError(10069, "Qhull error: can not use Qhull copy constructor if initialized() is true");
116    }
117    initializeQhull();
118}//copy constructor
119
120Qhull & Qhull::
121operator=(const Qhull &other)
122{
123    if(other.initialized() || initialized()){
124        throw QhullError(10070, "Qhull error: can not use Qhull copy assignment if initialized() is true");
125    }
126    qhull_message= other.qhull_message;
127    error_stream= other.error_stream;
128    output_stream= other.output_stream;
129    feasiblePoint= other.feasiblePoint;
130    useOutputStream= other.useOutputStream;
131    return *this;
132}//copy constructor
133
134void Qhull::
135initializeQhull()
136{
137    #if qh_QHpointer
138        qhull_qh= new QhullQh;
139        qhull_qh->old_qhstat= qh_qhstat;
140        qhull_qh->old_tempstack= qhmem.tempstack;
141        qh_qh= 0;
142        qh_qhstat= 0;
143    #else
144        qhull_qh= new (&qh_qh) QhullQh;
145        qhull_qh->old_qhstat= &qh_qhstat;
146        qhull_qh->old_tempstack= qhmem.tempstack;
147    #endif
148    qhmem.tempstack= 0;
149    qhull_run_id= qhull_qh->run_id;
150}//initializeQhull
151
152Qhull::
153~Qhull() throw()
154{
155    //! UsingLibQhull is required by ~QhullQh
156    UsingLibQhull q(this, QhullError::NOthrow);
157    if(q.defined()){
158        int exitCode = setjmp(qh errexit);
159        if(!exitCode){ // no object creation -- destructors skipped on longjmp()
160#if qh_QHpointer
161            delete qhull_qh;
162            // clears qhull_qh and qh_qh
163            qh_qh= 0;
164#else
165            qhull_qh->~QhullQh();
166            qhull_qh= 0;
167#endif
168            qhull_run_id= UsingLibQhull::NOqhRunId;
169            // Except for cerr, does not throw errors
170            if(hasQhullMessage()){
171                cerr<< "\nQhull output at end\n"; //FIXUP QH11005: where should error and log messages go on ~Qhull?
172                cerr<<qhullMessage();
173                clearQhullMessage();
174            }
175        }
176        maybeThrowQhullMessage(exitCode, QhullError::NOthrow);
177    }
178    s_qhull_output= 0; // Set by UsingLibQhull
179}//~Qhull
180
181#//Messaging
182
183void Qhull::
184appendQhullMessage(const string &s)
185{
186    if(output_stream && useOutputStream && qh USEstdout){   // threading errors caught elsewhere
187        *output_stream << s;
188    }else if(error_stream){
189        *error_stream << s;
190    }else{
191        qhull_message += s;
192    }
193}//appendQhullMessage
194
195//! clearQhullMessage does not throw errors (~Qhull)
196void Qhull::
197clearQhullMessage()
198{
199    qhull_status= qh_ERRnone;
200    qhull_message.clear();
201    RoadError::clearGlobalLog();
202}//clearQhullMessage
203
204//! hasQhullMessage does not throw errors (~Qhull)
205bool Qhull::
206hasQhullMessage() const
207{
208    return (!qhull_message.empty() || qhull_status!=qh_ERRnone);
209    //FIXUP QH11006 -- inconsistent usage with Rbox.  hasRboxMessage just tests rbox_status.  No appendRboxMessage()
210}
211
212//! qhullMessage does not throw errors (~Qhull)
213std::string Qhull::
214qhullMessage() const
215{
216    if(qhull_message.empty() && qhull_status!=qh_ERRnone){
217        return "qhull: no message for error.  Check cerr or error stream\n";
218    }else{
219        return qhull_message;
220    }
221}//qhullMessage
222
223int Qhull::
224qhullStatus() const
225{
226    return qhull_status;
227}//qhullStatus
228
229void Qhull::
230setErrorStream(ostream *os)
231{
232    error_stream= os;
233}//setErrorStream
234
235//! Updates useOutputStream
236void Qhull::
237setOutputStream(ostream *os)
238{
239    output_stream= os;
240    useOutputStream= (os!=0);
241}//setOutputStream
242
243#//GetSet
244
245void Qhull::
246checkIfQhullInitialized()
247{
248    if(!initialized()){ // qh_initqhull_buffers() not called
249        throw QhullError(10023, "Qhull error: checkIfQhullInitialized failed.  Call runQhull() first.");
250    }
251}//checkIfQhullInitialized
252
253//! Setup global state (qh_qh, qh_qhstat, qhmem.tempstack)
254int Qhull::
255runId()
256{
257    UsingLibQhull u(this);
258    QHULL_UNUSED(u);
259
260    return qhull_run_id;
261}//runId
262
263
264#//GetValue
265
266double Qhull::
267area(){
268    checkIfQhullInitialized();
269    UsingLibQhull q(this);
270    if(!qh hasAreaVolume){
271        int exitCode = setjmp(qh errexit);
272        if(!exitCode){ // no object creation -- destructors skipped on longjmp()
273            qh_getarea(qh facet_list);
274        }
275        maybeThrowQhullMessage(exitCode);
276    }
277    return qh totarea;
278}//area
279
280double Qhull::
281volume(){
282    checkIfQhullInitialized();
283    UsingLibQhull q(this);
284    if(!qh hasAreaVolume){
285        int exitCode = setjmp(qh errexit);
286        if(!exitCode){ // no object creation -- destructors skipped on longjmp()
287            qh_getarea(qh facet_list);
288        }
289        maybeThrowQhullMessage(exitCode);
290    }
291    return qh totvol;
292}//volume
293
294#//ForEach
295
296//! Define QhullVertex::neighborFacets().
297//! Automatically called if merging facets or computing the Voronoi diagram.
298//! Noop if called multiple times.
299void Qhull::
300defineVertexNeighborFacets(){
301    checkIfQhullInitialized();
302    UsingLibQhull q(this);
303    if(!qh hasAreaVolume){
304        int exitCode = setjmp(qh errexit);
305        if(!exitCode){ // no object creation -- destructors skipped on longjmp()
306            qh_vertexneighbors();
307        }
308        maybeThrowQhullMessage(exitCode);
309    }
310}//defineVertexNeighborFacets
311
312QhullFacetList Qhull::
313facetList() const{
314    return QhullFacetList(beginFacet(), endFacet());
315}//facetList
316
317QhullPoints Qhull::
318points() const
319{
320    return QhullPoints(hullDimension(), qhull_qh->num_points*hullDimension(), qhull_qh->first_point);
321}//points
322
323QhullPointSet Qhull::
324otherPoints() const
325{
326    return QhullPointSet(hullDimension(), qhull_qh->other_points);
327}//otherPoints
328
329//! Return vertices of the convex hull.
330QhullVertexList Qhull::
331vertexList() const{
332    return QhullVertexList(beginVertex(), endVertex());
333}//vertexList
334
335#//Modify
336
337void Qhull::
338outputQhull()
339{
340    checkIfQhullInitialized();
341    UsingLibQhull q(this);
342    int exitCode = setjmp(qh errexit);
343    if(!exitCode){ // no object creation -- destructors skipped on longjmp()
344        qh_produce_output2();
345    }
346    maybeThrowQhullMessage(exitCode);
347}//outputQhull
348
349void Qhull::
350outputQhull(const char *outputflags)
351{
352    checkIfQhullInitialized();
353    UsingLibQhull q(this);
354    string cmd(" "); // qh_checkflags skips first word
355    cmd += outputflags;
356    char *command= const_cast<char*>(cmd.c_str());
357    int exitCode = setjmp(qh errexit);
358    if(!exitCode){ // no object creation -- destructors skipped on longjmp()
359        qh_clear_outputflags();
360        char *s = qh qhull_command + strlen(qh qhull_command) + 1; //space
361        strncat(qh qhull_command, command, sizeof(qh qhull_command)-strlen(qh qhull_command)-1);
362        qh_checkflags(command, s_not_output_options);
363        qh_initflags(s);
364        qh_initqhull_outputflags();
365        if(qh KEEPminArea < REALmax/2
366           || (0 != qh KEEParea + qh KEEPmerge + qh GOODvertex
367                    + qh GOODthreshold + qh GOODpoint + qh SPLITthresholds)){
368            facetT *facet;
369            qh ONLYgood= False;
370            FORALLfacet_(qh facet_list) {
371                facet->good= True;
372            }
373            qh_prepare_output();
374        }
375        qh_produce_output2();
376        if(qh VERIFYoutput && !qh STOPpoint && !qh STOPcone){
377            qh_check_points();
378        }
379    }
380    maybeThrowQhullMessage(exitCode);
381}//outputQhull
382
383void Qhull::
384runQhull(const RboxPoints &rboxPoints, const char *qhullCommand2)
385{
386    runQhull(rboxPoints.comment().c_str(), rboxPoints.dimension(), rboxPoints.count(), &*rboxPoints.coordinates(), qhullCommand2);
387}//runQhull, RboxPoints
388
389//! points is a array of points, input sites ('d' or 'v'), or halfspaces with offset last ('H')
390//! Derived from qh_new_qhull [user.c]
391void Qhull::
392runQhull(const char *rboxCommand2, int pointDimension, int pointCount, const realT *rboxPoints, const char *qhullCommand2)
393{
394    if(run_called){
395        throw QhullError(10027, "Qhull error: runQhull called twice.  Only one call allowed.");
396    }
397    run_called= true;
398    string s("qhull ");
399    s += qhullCommand2;
400    char *command= const_cast<char*>(s.c_str());
401    UsingLibQhull q(this);
402    int exitCode = setjmp(qh errexit);
403    if(!exitCode){ // no object creation -- destructors skipped on longjmp()
404        qh_checkflags(command, s_unsupported_options);
405        qh_initflags(command);
406        *qh rbox_command= '\0';
407        strncat( qh rbox_command, rboxCommand2, sizeof(qh rbox_command)-1);
408        if(qh DELAUNAY){
409            qh PROJECTdelaunay= True;   // qh_init_B() calls qh_projectinput()
410        }
411        pointT *newPoints= const_cast<pointT*>(rboxPoints);
412        int newDimension= pointDimension;
413        int newIsMalloc= False;
414        if(qh HALFspace){
415            --newDimension;
416            initializeFeasiblePoint(newDimension);
417            newPoints= qh_sethalfspace_all(pointDimension, pointCount, newPoints, qh feasible_point);
418            newIsMalloc= True;
419        }
420        qh_init_B(newPoints, pointCount, newDimension, newIsMalloc);
421        qhull_dimension= (qh DELAUNAY ? qh hull_dim - 1 : qh hull_dim);
422        qh_qhull();
423        qh_check_output();
424        qh_prepare_output();
425        if(qh VERIFYoutput && !qh STOPpoint && !qh STOPcone){
426            qh_check_points();
427        }
428    }
429    for(int k= qhull_dimension; k--; ){  // Do not move up (may throw)
430        origin_point << 0.0;
431    }
432    maybeThrowQhullMessage(exitCode);
433}//runQhull
434
435#//Helpers -- be careful of allocating C++ objects due to setjmp/longjmp() error handling by qh_... routines
436
437void Qhull::
438initializeFeasiblePoint(int hulldim)
439{
440    if(qh feasible_string){
441        qh_setfeasible(hulldim);
442    }else{
443        if(feasiblePoint.empty()){
444            qh_fprintf(qh ferr, 6209, "qhull error: missing feasible point for halfspace intersection.  Use option 'Hn,n' or set qh.feasiblePoint\n");
445            qh_errexit(qh_ERRmem, NULL, NULL);
446        }
447        if(feasiblePoint.size()!=(size_t)hulldim){
448            qh_fprintf(qh ferr, 6210, "qhull error: dimension of feasiblePoint should be %d.  It is %u", hulldim, feasiblePoint.size());
449            qh_errexit(qh_ERRmem, NULL, NULL);
450        }
451        if (!(qh feasible_point= (coordT*)qh_malloc(hulldim * sizeof(coordT)))) {
452            qh_fprintf(qh ferr, 6202, "qhull error: insufficient memory for feasible point\n");
453            qh_errexit(qh_ERRmem, NULL, NULL);
454        }
455        coordT *t= qh feasible_point;
456        // No qh_... routines after here -- longjmp() ignores destructor
457        for(Coordinates::ConstIterator p=feasiblePoint.begin(); p<feasiblePoint.end(); p++){
458            *t++= *p;
459        }
460    }
461}//initializeFeasiblePoint
462
463void Qhull::
464maybeThrowQhullMessage(int exitCode)
465{
466    if(qhull_status==qh_ERRnone){
467        qhull_status= exitCode;
468    }
469    if(qhull_status!=qh_ERRnone){
470        QhullError e(qhull_status, qhull_message);
471        clearQhullMessage();
472        throw e; // FIXUP QH11007: copy constructor is expensive if logging
473    }
474}//maybeThrowQhullMessage
475
476void Qhull::
477maybeThrowQhullMessage(int exitCode, int noThrow)  throw()
478{
479    QHULL_UNUSED(noThrow);
480
481    if(qhull_status==qh_ERRnone){
482        qhull_status= exitCode;
483    }
484    if(qhull_status!=qh_ERRnone){
485        QhullError e(qhull_status, qhull_message);
486        e.logError();
487    }
488}//maybeThrowQhullMessage
489
490}//namespace orgQhull
491
492/*-<a                             href="qh-user.htm#TOC"
493 >-------------------------------</a><a name="qh_fprintf">-</a>
494
495  qh_fprintf(fp, msgcode, format, list of args )
496    fp is ignored (replaces qh_fprintf() in userprintf.c)
497    s_qhull_output == Qhull
498
499notes:
500    only called from libqhull
501    same as fprintf() and RboxPoints::qh_fprintf_rbox()
502    fgets() is not trapped like fprintf()
503    Do not throw errors from here.  Use qh_errexit;
504*/
505extern "C"
506void qh_fprintf(FILE *fp, int msgcode, const char *fmt, ... ) {
507    va_list args;
508
509    using namespace orgQhull;
510
511    if(!s_qhull_output){
512        fprintf(stderr, "QH10025 Qhull error: UsingLibQhull not declared prior to calling qh_...().  s_qhull_output==NULL.\n");
513        qh_exit(10025);
514    }
515    Qhull *out= s_qhull_output;
516    va_start(args, fmt);
517    if(msgcode<MSG_OUTPUT || fp == qh_FILEstderr){
518        if(msgcode>=MSG_ERROR && msgcode<MSG_WARNING){
519            if(out->qhull_status<MSG_ERROR || out->qhull_status>=MSG_WARNING){
520                out->qhull_status= msgcode;
521            }
522        }
523        char newMessage[MSG_MAXLEN];
524        // RoadError will add the message tag
525        vsnprintf(newMessage, sizeof(newMessage), fmt, args);
526        out->appendQhullMessage(newMessage);
527        va_end(args);
528        return;
529    }
530    if(out->output_stream && out->useOutputStream){
531        char newMessage[MSG_MAXLEN];
532        vsnprintf(newMessage, sizeof(newMessage), fmt, args);
533        *out->output_stream << newMessage;
534        va_end(args);
535        return;
536    }
537    // FIXUP QH11008: how do users trap messages and handle input?  A callback?
538    char newMessage[MSG_MAXLEN];
539    vsnprintf(newMessage, sizeof(newMessage), fmt, args);
540    out->appendQhullMessage(newMessage);
541    va_end(args);
542} /* qh_fprintf */
543
Note: See TracBrowser for help on using the repository browser.