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