1 /****************************************************************************
2 **
3 ** Copyright (c) 2008-2020 C.B. Barber. All rights reserved.
4 ** $Id: //main/2019/qhull/src/libqhullcpp/QhullUser.cpp#10 $$Change: 3008 $
5 ** $DateTime: 2020/07/30 13:54:27 $$Author: bbarber $
6 **
7 ****************************************************************************/
8 
9 #include "libqhullcpp/QhullUser.h"
10 
11 #include "libqhullcpp/QhullError.h"
12 
13 #include <iostream>
14 #include <stdint.h>
15 
16 using std::cerr;
17 using std::endl;
18 using std::istream;
19 using std::ostream;
20 using std::ostringstream;
21 using std::string;
22 using std::vector;
23 using std::ws;
24 
25 #ifdef _MSC_VER  // Microsoft Visual C++ -- warning level 4
26 #pragma warning( disable : 4996)  // function was declared deprecated(strcpy, localtime, etc.)
27 #endif
28 
29 namespace orgQhull{
30 
31 #//! QhullUser -- user-defined interface to qhull via qh_fprintf
32 
33 
34 #//!\name Constructors
35 QhullUser::
36 QhullUser(QhullQh *qqh)
37     : qh_qh(qqh)
38     , previous_user(NULL)
39     , doubles_vector()
40     , ints_vector()
41     , fprintf_ints()
42     , fprintf_doubles()
43     , fprintf_codes()
44     , fprintf_strings()
45     , num_facets(0)
46     , num_neighbors(0)
47     , num_numbers(0)
48     , num_points(0)
49     , num_results(0)
50     , num_ridges(0)
51     , num_vectors(0)
52     , num_vertices(0)
53     , qhull_dim(0)
54     , delaunay_dim(0)
55 {
56     previous_user= qh()->cpp_user;
57     qh()->cpp_user= NULL;
58     captureOn();
59 }//constructor
60 
61 QhullUser::
62 ~QhullUser()
63 {
64     captureOff();
65     qh()->cpp_user= previous_user;
66 }//destructor
67 
68 #//!\name Get/Set
69 
70 //! Clear working fields of QhullUser
71 //! Retains qh_qh and previous_user as initialized
72 //! Updates qhull_dim from qh_qh (does not change)
73 //! Updates delaunay_dim if qh_qh.ISdelaunay
74 void QhullUser::
75 clear()
76 {
77     doubles_vector.clear();
78     ints_vector.clear();
79     fprintf_ints.clear();
80     fprintf_doubles.clear();
81     fprintf_codes.clear();
82     fprintf_strings.clear();
83     num_facets= 0;
84     num_neighbors= 0;
85     num_numbers= 0;
86     num_points= 0;
87     num_results= 0;
88     num_ridges= 0;
89     num_vectors= 0;
90     num_vertices= 0;
91 }//clear
92 
93 #//!\name Methods
94 
95 void QhullUser::
96 captureOff()
97 {
98     if(qh()->cpp_user==NULL){
99         throw QhullError(10080, "Qhull error: QhullUser::captureOn not call before QhullUser::captureOff for QhullUser 0x%llx", 0, 0, 0.0, this);
100     }
101     if(qh()->cpp_user!=this){
102         throw QhullError(10081, "Qhull error: conflicting QhullUser (0x%llx) for QhullUser::captureOff().  Does not match 'this' (0x...%X)", int(0xffff&(intptr_t)this), 0, 0.0, qh()->cpp_user);
103     }
104     qh()->cpp_user= NULL;
105 }//captureOff
106 
107 void QhullUser::
108 captureOn()
109 {
110     if(qh()->cpp_user){
111         throw QhullError(10079, "Qhull error: conflicting user of cpp_user for QhullUser::captureOn() or corrupted qh_qh 0x%llx", 0, 0, 0.0, qh());
112     }
113     qh()->cpp_user= this;
114 }//captureOn
115 
116 }//namespace orgQhull
117 
118 #//!\name Global functions
119 
120 /*-<a                             href="qh-user.htm#TOC"
121 >-------------------------------</a><a name="qh_fprintf">-</a>
122 
123   qh_fprintf(qh, fp, msgcode, format, list of args )
124     replacement for qh_fprintf in userprintf_r.c, which replaces fprintf for Qhull
125     qh.ISqhullQh must be true, indicating that qh a subclass of QhullQh
126     qh.cpp_user is an optional QhullUser for trapped MSG_OUTPUT calls
127     fp may be NULL
128     otherwise behaves the same as qh_fprintf in userprintf_r.c
129 
130 returns:
131     sets qhullQh->qhull_status if msgcode is error 6000..6999
132 ....sets qh.last_errcode if error reported
133 ....if qh.cpp_user defined, records results of 'qhull v Fi Fo'
134         See "qvoronoi-fifo" in user_eg3_r.cpp for an example
135 
136 
137 notes:
138     Only called from libqhull_r
139     A similar technique is used by RboxPoints and qh_fprintf_rbox
140     Do not throw errors from here.  Use qh_errexit;
141     fgets() is not trapped like fprintf(), QH11008 FIX: how do users handle input?  A callback?
142 */
143 extern "C"
144 
145 //! Custom qh_fprintf for transferring Qhull output from io_r.c to QhullUser and QhullQh
146 //! Identify msgcodes with Qhull option 'Ta'
147 //! A similar technique is used by RboxPoints with a custom qh_fprintf_rbox
148 void qh_fprintf(qhT *qh, FILE* fp, int msgcode, const char *fmt, ... ){
149     va_list args;
150     int last_errcode;
151 
152     using namespace orgQhull;
153 
154     if(qh==NULL || !qh->ISqhullQh){
155         qh_fprintf_stderr(10025, "Qhull error: qh_fprintf in QhullUser.cpp called from a Qhull instance without QhullQh defined\n");
156         last_errcode= 10025;
157         qh_exit(last_errcode);
158     }
159     va_start(args, fmt);
160     if(msgcode>=MSG_OUTPUT && qh->cpp_user){
161         QhullUser *out= reinterpret_cast<QhullUser *>(qh->cpp_user);
162         bool isOut= false;
163         switch (msgcode){
164         case 9231:        /* printvdiagram, totcount (ignored) */
165             out->setNumResults(va_arg(args, int));
166             isOut= true;
167             break;
168         case 9271:        /* qh_printvnorm, count, pointId, pointId, hyperplane */
169             out->appendInt(va_arg(args, int));
170             out->appendInt(va_arg(args, int));
171             out->appendInt(va_arg(args, int));
172             out->appendAndClearInts();
173             isOut= true;
174             break;
175         case 9272:
176         case 9273:
177             out->appendDouble(va_arg(args, double));
178             isOut= true;
179             break;
180         case 9274:
181             out->appendAndClearDoubles();
182             isOut= true;
183             break;
184         default:
185             // do nothing
186             break;
187         }
188         if(isOut){
189             out->appendCode(msgcode);
190             va_end(args);
191             return;
192         }
193     }/*MSG_OUTPUT, cpp_user*/
194 
195     QhullQh *qhullQh= static_cast<QhullQh *>(qh);
196     char newMessage[MSG_MAXLEN];
197     int msgLen= 0;
198     if((qh && qh->ANNOTATEoutput) || msgcode < MSG_TRACE4){
199         msgLen= snprintf(newMessage, sizeof(newMessage), "[QH%.4d]", msgcode);
200     }else if(msgcode>=MSG_ERROR && msgcode < MSG_STDERR){
201         msgLen= snprintf(newMessage, sizeof(newMessage), "QH%.4d ", msgcode);
202     }
203     if(msgLen>=0 && msgLen < (int)sizeof(newMessage)){
204         vsnprintf(newMessage + msgLen, sizeof(newMessage) - msgLen, fmt, args);
205     }
206     if(msgcode < MSG_OUTPUT || fp == qh_FILEstderr){
207         if(msgcode>=MSG_ERROR && msgcode < MSG_WARNING){
208             qh->last_errcode= msgcode;
209             if(qhullQh->qhull_status < MSG_ERROR || qhullQh->qhull_status>=MSG_WARNING){
210                 qhullQh->qhull_status= msgcode;
211             }
212         }
213         qhullQh->appendQhullMessage(newMessage);
214     }else if(qhullQh->output_stream && qhullQh->use_output_stream){
215         *qhullQh->output_stream << newMessage;
216         if(qh->FLUSHprint){
217             qhullQh->output_stream->flush();
218         }
219     }else{
220         qhullQh->appendQhullMessage(newMessage);
221     }
222     va_end(args);
223 
224     /* Place debugging traps here. Use with trace option 'Tn'
225          Set qh.tracefacet_id, qh.traceridge_id, and/or qh.tracevertex_id in global_r.c
226     */
227     if(False){ /* in production skip test for debugging traps */
228         facetT *neighbor, **neighborp;
229         if(qh->tracefacet && qh->tracefacet->tested){
230             if(qh_setsize(qh, qh->tracefacet->neighbors) < qh->hull_dim)
231                 qh_errexit(qh, qh_ERRdebug, qh->tracefacet, qh->traceridge);
232             FOREACHneighbor_(qh->tracefacet){
233                 if(neighbor != qh_DUPLICATEridge && neighbor != qh_MERGEridge && neighbor->visible)
234                     qh_errexit2(qh, qh_ERRdebug, qh->tracefacet, neighbor);
235             }
236         }
237         if(qh->traceridge && qh->traceridge->top->id == 234342223){
238             qh_errexit(qh, qh_ERRdebug, qh->tracefacet, qh->traceridge);
239         }
240         if(qh->tracevertex && qh_setsize(qh, qh->tracevertex->neighbors) > 3434334){
241             qh_errexit(qh, qh_ERRdebug, qh->tracefacet, qh->traceridge);
242         }
243     }
244 } /* qh_fprintf */
245 
246