Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/supermerill/SuperSlicer.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Build.PL1
-rw-r--r--CMakeLists.txt16
-rw-r--r--lib/Slic3r.pm1
-rw-r--r--lib/Slic3r/GUI/Plater.pm117
-rw-r--r--lib/Slic3r/GUI/Plater/ObjectCutDialog.pm2
-rw-r--r--lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm14
-rw-r--r--lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm2
-rw-r--r--resources/icons/overlay/layflat_hover.pngbin0 -> 2410 bytes
-rw-r--r--resources/icons/overlay/layflat_off.pngbin0 -> 2864 bytes
-rw-r--r--resources/icons/overlay/layflat_on.pngbin0 -> 3816 bytes
-rw-r--r--resources/profiles/PrusaResearch.idx10
-rw-r--r--resources/profiles/PrusaResearch.ini119
-rw-r--r--xs/CMakeLists.txt31
-rw-r--r--xs/src/admesh/stlinit.cpp2
-rw-r--r--xs/src/avrdude/avrdude-slic3r.cpp101
-rw-r--r--xs/src/avrdude/avrdude-slic3r.hpp15
-rw-r--r--xs/src/avrdude/avrdude.h6
-rw-r--r--xs/src/avrdude/avrpart.c34
-rw-r--r--xs/src/avrdude/buspirate.c7
-rw-r--r--xs/src/avrdude/butterfly.c7
-rw-r--r--xs/src/avrdude/lexer.c8
-rw-r--r--xs/src/avrdude/main.c27
-rw-r--r--xs/src/avrdude/pgm.c7
-rw-r--r--xs/src/avrdude/ser_win32.c9
-rw-r--r--xs/src/avrdude/stk500v2.c7
-rw-r--r--xs/src/avrdude/update.c25
-rw-r--r--xs/src/avrdude/wiring.c7
-rw-r--r--xs/src/libnest2d/CMakeLists.txt30
-rw-r--r--xs/src/libnest2d/README.md18
-rw-r--r--xs/src/libnest2d/cmake_modules/FindTBB.cmake322
-rw-r--r--xs/src/libnest2d/examples/main.cpp668
-rw-r--r--xs/src/libnest2d/libnest2d.h14
-rw-r--r--xs/src/libnest2d/libnest2d/boost_alg.hpp133
-rw-r--r--xs/src/libnest2d/libnest2d/clipper_backend/clipper_backend.hpp142
-rw-r--r--xs/src/libnest2d/libnest2d/geometry_traits.hpp288
-rw-r--r--xs/src/libnest2d/libnest2d/geometry_traits_nfp.hpp127
-rw-r--r--xs/src/libnest2d/libnest2d/libnest2d.hpp216
-rw-r--r--xs/src/libnest2d/libnest2d/metaloop.hpp8
-rw-r--r--xs/src/libnest2d/libnest2d/optimizer.hpp3
-rw-r--r--xs/src/libnest2d/libnest2d/optimizers/nlopt_boilerplate.hpp6
-rw-r--r--xs/src/libnest2d/libnest2d/placers/bottomleftplacer.hpp92
-rw-r--r--xs/src/libnest2d/libnest2d/placers/nfpplacer.hpp920
-rw-r--r--xs/src/libnest2d/libnest2d/placers/placer_boilerplate.hpp39
-rw-r--r--xs/src/libnest2d/libnest2d/rotfinder.hpp41
-rw-r--r--xs/src/libnest2d/libnest2d/selections/djd_heuristic.hpp59
-rw-r--r--xs/src/libnest2d/libnest2d/selections/filler.hpp12
-rw-r--r--xs/src/libnest2d/libnest2d/selections/firstfit.hpp17
-rw-r--r--xs/src/libnest2d/libnest2d/selections/selection_boilerplate.hpp3
-rw-r--r--xs/src/libnest2d/tests/test.cpp120
-rw-r--r--xs/src/libnest2d/tools/libnfpglue.cpp16
-rw-r--r--xs/src/libnest2d/tools/libnfpglue.hpp10
-rw-r--r--xs/src/libnest2d/tools/nfp_svgnest.hpp1018
-rw-r--r--xs/src/libnest2d/tools/nfp_svgnest_glue.hpp75
-rw-r--r--xs/src/libnest2d/tools/svgtools.hpp6
-rw-r--r--xs/src/libslic3r/Format/3mf.cpp1
-rw-r--r--xs/src/libslic3r/Format/AMF.cpp1
-rw-r--r--xs/src/libslic3r/GCode.cpp31
-rw-r--r--xs/src/libslic3r/GCode.hpp1
-rw-r--r--xs/src/libslic3r/GCode/WipeTower.hpp6
-rw-r--r--xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp69
-rw-r--r--xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp20
-rw-r--r--xs/src/libslic3r/GCodeTimeEstimator.cpp13
-rw-r--r--xs/src/libslic3r/GCodeTimeEstimator.hpp3
-rw-r--r--xs/src/libslic3r/Geometry.cpp53
-rw-r--r--xs/src/libslic3r/Geometry.hpp2
-rw-r--r--xs/src/libslic3r/Model.cpp142
-rw-r--r--xs/src/libslic3r/Model.hpp38
-rw-r--r--xs/src/libslic3r/ModelArrange.hpp469
-rw-r--r--xs/src/libslic3r/Point.cpp6
-rw-r--r--xs/src/libslic3r/Point.hpp1
-rw-r--r--xs/src/libslic3r/Print.cpp7
-rw-r--r--xs/src/libslic3r/Print.hpp4
-rw-r--r--xs/src/libslic3r/PrintConfig.cpp77
-rw-r--r--xs/src/libslic3r/PrintConfig.hpp31
-rw-r--r--xs/src/libslic3r/TriangleMesh.cpp151
-rw-r--r--xs/src/libslic3r/TriangleMesh.hpp8
-rw-r--r--xs/src/libslic3r/libslic3r.h2
-rw-r--r--xs/src/perlglue.cpp2
-rw-r--r--xs/src/qhull/Announce.txt47
-rw-r--r--xs/src/qhull/CMakeLists.txt128
-rw-r--r--xs/src/qhull/COPYING.txt38
-rw-r--r--xs/src/qhull/README.txt623
-rw-r--r--xs/src/qhull/REGISTER.txt32
-rw-r--r--xs/src/qhull/html/index.htm935
-rw-r--r--xs/src/qhull/html/normal_voronoi_knauss_oesterle.jpgbin0 -> 23924 bytes
-rw-r--r--xs/src/qhull/html/qconvex.htm630
-rw-r--r--xs/src/qhull/html/qdelau_f.htm416
-rw-r--r--xs/src/qhull/html/qdelaun.htm628
-rw-r--r--xs/src/qhull/html/qh--4d.gifbin0 -> 4372 bytes
-rw-r--r--xs/src/qhull/html/qh--cone.gifbin0 -> 2946 bytes
-rw-r--r--xs/src/qhull/html/qh--dt.gifbin0 -> 3772 bytes
-rw-r--r--xs/src/qhull/html/qh--geom.gifbin0 -> 318 bytes
-rw-r--r--xs/src/qhull/html/qh--half.gifbin0 -> 2537 bytes
-rw-r--r--xs/src/qhull/html/qh--rand.gifbin0 -> 3875 bytes
-rw-r--r--xs/src/qhull/html/qh-code.htm1062
-rw-r--r--xs/src/qhull/html/qh-eg.htm693
-rw-r--r--xs/src/qhull/html/qh-faq.htm1547
-rw-r--r--xs/src/qhull/html/qh-get.htm106
-rw-r--r--xs/src/qhull/html/qh-impre.htm826
-rw-r--r--xs/src/qhull/html/qh-optc.htm292
-rw-r--r--xs/src/qhull/html/qh-optf.htm736
-rw-r--r--xs/src/qhull/html/qh-optg.htm274
-rw-r--r--xs/src/qhull/html/qh-opto.htm353
-rw-r--r--xs/src/qhull/html/qh-optp.htm253
-rw-r--r--xs/src/qhull/html/qh-optq.htm731
-rw-r--r--xs/src/qhull/html/qh-optt.htm278
-rw-r--r--xs/src/qhull/html/qh-quick.htm495
-rw-r--r--xs/src/qhull/html/qhalf.htm626
-rw-r--r--xs/src/qhull/html/qhull-cpp.xml214
-rw-r--r--xs/src/qhull/html/qhull.htm473
-rw-r--r--xs/src/qhull/html/qhull.man1008
-rw-r--r--xs/src/qhull/html/qhull.txt1263
-rw-r--r--xs/src/qhull/html/qvoron_f.htm396
-rw-r--r--xs/src/qhull/html/qvoronoi.htm667
-rw-r--r--xs/src/qhull/html/rbox.htm277
-rw-r--r--xs/src/qhull/html/rbox.man176
-rw-r--r--xs/src/qhull/html/rbox.txt195
-rw-r--r--xs/src/qhull/index.htm284
-rw-r--r--xs/src/qhull/origCMakeLists.txt426
-rw-r--r--xs/src/qhull/src/Changes.txt2129
-rw-r--r--xs/src/qhull/src/libqhull/DEPRECATED.txt29
-rw-r--r--xs/src/qhull/src/libqhull/Makefile240
-rw-r--r--xs/src/qhull/src/libqhull/Mborland206
-rw-r--r--xs/src/qhull/src/libqhull/geom.c1234
-rw-r--r--xs/src/qhull/src/libqhull/geom.h176
-rw-r--r--xs/src/qhull/src/libqhull/geom2.c2094
-rw-r--r--xs/src/qhull/src/libqhull/global.c2217
-rw-r--r--xs/src/qhull/src/libqhull/index.htm264
-rw-r--r--xs/src/qhull/src/libqhull/io.c4062
-rw-r--r--xs/src/qhull/src/libqhull/io.h159
-rw-r--r--xs/src/qhull/src/libqhull/libqhull.c1403
-rw-r--r--xs/src/qhull/src/libqhull/libqhull.h1140
-rw-r--r--xs/src/qhull/src/libqhull/libqhull.pro67
-rw-r--r--xs/src/qhull/src/libqhull/mem.c576
-rw-r--r--xs/src/qhull/src/libqhull/mem.h222
-rw-r--r--xs/src/qhull/src/libqhull/merge.c3628
-rw-r--r--xs/src/qhull/src/libqhull/merge.h178
-rw-r--r--xs/src/qhull/src/libqhull/poly.c1205
-rw-r--r--xs/src/qhull/src/libqhull/poly.h296
-rw-r--r--xs/src/qhull/src/libqhull/poly2.c3222
-rw-r--r--xs/src/qhull/src/libqhull/qh-geom.htm295
-rw-r--r--xs/src/qhull/src/libqhull/qh-globa.htm165
-rw-r--r--xs/src/qhull/src/libqhull/qh-io.htm305
-rw-r--r--xs/src/qhull/src/libqhull/qh-mem.htm145
-rw-r--r--xs/src/qhull/src/libqhull/qh-merge.htm366
-rw-r--r--xs/src/qhull/src/libqhull/qh-poly.htm485
-rw-r--r--xs/src/qhull/src/libqhull/qh-qhull.htm279
-rw-r--r--xs/src/qhull/src/libqhull/qh-set.htm308
-rw-r--r--xs/src/qhull/src/libqhull/qh-stat.htm163
-rw-r--r--xs/src/qhull/src/libqhull/qh-user.htm271
-rw-r--r--xs/src/qhull/src/libqhull/qhull-exports.def417
-rw-r--r--xs/src/qhull/src/libqhull/qhull_a.h150
-rw-r--r--xs/src/qhull/src/libqhull/qhull_p-exports.def418
-rw-r--r--xs/src/qhull/src/libqhull/qset.c1340
-rw-r--r--xs/src/qhull/src/libqhull/qset.h490
-rw-r--r--xs/src/qhull/src/libqhull/random.c245
-rw-r--r--xs/src/qhull/src/libqhull/random.h34
-rw-r--r--xs/src/qhull/src/libqhull/rboxlib.c870
-rw-r--r--xs/src/qhull/src/libqhull/stat.c717
-rw-r--r--xs/src/qhull/src/libqhull/stat.h543
-rw-r--r--xs/src/qhull/src/libqhull/user.c538
-rw-r--r--xs/src/qhull/src/libqhull/user.h909
-rw-r--r--xs/src/qhull/src/libqhull/usermem.c94
-rw-r--r--xs/src/qhull/src/libqhull/userprintf.c66
-rw-r--r--xs/src/qhull/src/libqhull/userprintf_rbox.c53
-rw-r--r--xs/src/qhull/src/libqhull_r/Makefile240
-rw-r--r--xs/src/qhull/src/libqhull_r/geom2_r.c2096
-rw-r--r--xs/src/qhull/src/libqhull_r/geom_r.c1234
-rw-r--r--xs/src/qhull/src/libqhull_r/geom_r.h184
-rw-r--r--xs/src/qhull/src/libqhull_r/global_r.c2100
-rw-r--r--xs/src/qhull/src/libqhull_r/index.htm266
-rw-r--r--xs/src/qhull/src/libqhull_r/io_r.c4062
-rw-r--r--xs/src/qhull/src/libqhull_r/io_r.h167
-rw-r--r--xs/src/qhull/src/libqhull_r/libqhull_r.c1403
-rw-r--r--xs/src/qhull/src/libqhull_r/libqhull_r.h1134
-rw-r--r--xs/src/qhull/src/libqhull_r/libqhull_r.pro67
-rw-r--r--xs/src/qhull/src/libqhull_r/mem_r.c562
-rw-r--r--xs/src/qhull/src/libqhull_r/mem_r.h234
-rw-r--r--xs/src/qhull/src/libqhull_r/merge_r.c3627
-rw-r--r--xs/src/qhull/src/libqhull_r/merge_r.h186
-rw-r--r--xs/src/qhull/src/libqhull_r/poly2_r.c3222
-rw-r--r--xs/src/qhull/src/libqhull_r/poly_r.c1205
-rw-r--r--xs/src/qhull/src/libqhull_r/poly_r.h303
-rw-r--r--xs/src/qhull/src/libqhull_r/qh-geom_r.htm295
-rw-r--r--xs/src/qhull/src/libqhull_r/qh-globa_r.htm163
-rw-r--r--xs/src/qhull/src/libqhull_r/qh-io_r.htm305
-rw-r--r--xs/src/qhull/src/libqhull_r/qh-mem_r.htm145
-rw-r--r--xs/src/qhull/src/libqhull_r/qh-merge_r.htm366
-rw-r--r--xs/src/qhull/src/libqhull_r/qh-poly_r.htm485
-rw-r--r--xs/src/qhull/src/libqhull_r/qh-qhull_r.htm279
-rw-r--r--xs/src/qhull/src/libqhull_r/qh-set_r.htm308
-rw-r--r--xs/src/qhull/src/libqhull_r/qh-stat_r.htm161
-rw-r--r--xs/src/qhull/src/libqhull_r/qh-user_r.htm271
-rw-r--r--xs/src/qhull/src/libqhull_r/qhull_r-exports.def404
-rw-r--r--xs/src/qhull/src/libqhull_r/qhull_ra.h158
-rw-r--r--xs/src/qhull/src/libqhull_r/qset_r.c1340
-rw-r--r--xs/src/qhull/src/libqhull_r/qset_r.h502
-rw-r--r--xs/src/qhull/src/libqhull_r/random_r.c247
-rw-r--r--xs/src/qhull/src/libqhull_r/random_r.h41
-rw-r--r--xs/src/qhull/src/libqhull_r/rboxlib_r.c842
-rw-r--r--xs/src/qhull/src/libqhull_r/stat_r.c682
-rw-r--r--xs/src/qhull/src/libqhull_r/stat_r.h533
-rw-r--r--xs/src/qhull/src/libqhull_r/user_r.c527
-rw-r--r--xs/src/qhull/src/libqhull_r/user_r.h882
-rw-r--r--xs/src/qhull/src/libqhull_r/usermem_r.c94
-rw-r--r--xs/src/qhull/src/libqhull_r/userprintf_r.c65
-rw-r--r--xs/src/qhull/src/libqhull_r/userprintf_rbox_r.c53
-rw-r--r--xs/src/qhull/src/libqhullcpp/Coordinates.cpp198
-rw-r--r--xs/src/qhull/src/libqhullcpp/Coordinates.h303
-rw-r--r--xs/src/qhull/src/libqhullcpp/PointCoordinates.cpp348
-rw-r--r--xs/src/qhull/src/libqhullcpp/PointCoordinates.h161
-rw-r--r--xs/src/qhull/src/libqhullcpp/Qhull.cpp352
-rw-r--r--xs/src/qhull/src/libqhullcpp/Qhull.h132
-rw-r--r--xs/src/qhull/src/libqhullcpp/QhullError.h62
-rw-r--r--xs/src/qhull/src/libqhullcpp/QhullFacet.cpp519
-rw-r--r--xs/src/qhull/src/libqhullcpp/QhullFacet.h151
-rw-r--r--xs/src/qhull/src/libqhullcpp/QhullFacetList.cpp174
-rw-r--r--xs/src/qhull/src/libqhullcpp/QhullFacetList.h106
-rw-r--r--xs/src/qhull/src/libqhullcpp/QhullFacetSet.cpp147
-rw-r--r--xs/src/qhull/src/libqhullcpp/QhullFacetSet.h97
-rw-r--r--xs/src/qhull/src/libqhullcpp/QhullHyperplane.cpp187
-rw-r--r--xs/src/qhull/src/libqhullcpp/QhullHyperplane.h123
-rw-r--r--xs/src/qhull/src/libqhullcpp/QhullIterator.h173
-rw-r--r--xs/src/qhull/src/libqhullcpp/QhullLinkedList.h388
-rw-r--r--xs/src/qhull/src/libqhullcpp/QhullPoint.cpp203
-rw-r--r--xs/src/qhull/src/libqhullcpp/QhullPoint.h136
-rw-r--r--xs/src/qhull/src/libqhullcpp/QhullPointSet.cpp62
-rw-r--r--xs/src/qhull/src/libqhullcpp/QhullPointSet.h77
-rw-r--r--xs/src/qhull/src/libqhullcpp/QhullPoints.cpp320
-rw-r--r--xs/src/qhull/src/libqhullcpp/QhullPoints.h266
-rw-r--r--xs/src/qhull/src/libqhullcpp/QhullQh.cpp237
-rw-r--r--xs/src/qhull/src/libqhullcpp/QhullQh.h110
-rw-r--r--xs/src/qhull/src/libqhullcpp/QhullRidge.cpp124
-rw-r--r--xs/src/qhull/src/libqhullcpp/QhullRidge.h112
-rw-r--r--xs/src/qhull/src/libqhullcpp/QhullSet.cpp62
-rw-r--r--xs/src/qhull/src/libqhullcpp/QhullSet.h462
-rw-r--r--xs/src/qhull/src/libqhullcpp/QhullSets.h27
-rw-r--r--xs/src/qhull/src/libqhullcpp/QhullStat.cpp42
-rw-r--r--xs/src/qhull/src/libqhullcpp/QhullStat.h49
-rw-r--r--xs/src/qhull/src/libqhullcpp/QhullVertex.cpp112
-rw-r--r--xs/src/qhull/src/libqhullcpp/QhullVertex.h104
-rw-r--r--xs/src/qhull/src/libqhullcpp/QhullVertexSet.cpp161
-rw-r--r--xs/src/qhull/src/libqhullcpp/QhullVertexSet.h86
-rw-r--r--xs/src/qhull/src/libqhullcpp/RboxPoints.cpp224
-rw-r--r--xs/src/qhull/src/libqhullcpp/RboxPoints.h69
-rw-r--r--xs/src/qhull/src/libqhullcpp/RoadError.cpp158
-rw-r--r--xs/src/qhull/src/libqhullcpp/RoadError.h88
-rw-r--r--xs/src/qhull/src/libqhullcpp/RoadLogEvent.cpp122
-rw-r--r--xs/src/qhull/src/libqhullcpp/RoadLogEvent.h77
-rw-r--r--xs/src/qhull/src/libqhullcpp/functionObjects.h67
-rw-r--r--xs/src/qhull/src/libqhullcpp/libqhullcpp.pro71
-rw-r--r--xs/src/qhull/src/libqhullcpp/qt-qhull.cpp130
-rw-r--r--xs/src/qhull/src/libqhullcpp/usermem_r-cpp.cpp93
-rw-r--r--xs/src/qhull/src/libqhullstatic/libqhullstatic.pro19
-rw-r--r--xs/src/qhull/src/libqhullstatic_r/libqhullstatic_r.pro21
-rw-r--r--xs/src/qhull/src/qconvex/qconvex.c326
-rw-r--r--xs/src/qhull/src/qconvex/qconvex.pro9
-rw-r--r--xs/src/qhull/src/qconvex/qconvex_r.c328
-rw-r--r--xs/src/qhull/src/qdelaunay/qdelaun.c315
-rw-r--r--xs/src/qhull/src/qdelaunay/qdelaun_r.c317
-rw-r--r--xs/src/qhull/src/qdelaunay/qdelaunay.pro9
-rw-r--r--xs/src/qhull/src/qhalf/qhalf.c316
-rw-r--r--xs/src/qhull/src/qhalf/qhalf.pro9
-rw-r--r--xs/src/qhull/src/qhalf/qhalf_r.c318
-rw-r--r--xs/src/qhull/src/qhull-all.pro94
-rw-r--r--xs/src/qhull/src/qhull-app-c.pri24
-rw-r--r--xs/src/qhull/src/qhull-app-c_r.pri26
-rw-r--r--xs/src/qhull/src/qhull-app-cpp.pri23
-rw-r--r--xs/src/qhull/src/qhull-app-shared.pri27
-rw-r--r--xs/src/qhull/src/qhull-app-shared_r.pri29
-rw-r--r--xs/src/qhull/src/qhull-libqhull-src.pri39
-rw-r--r--xs/src/qhull/src/qhull-libqhull-src_r.pri39
-rw-r--r--xs/src/qhull/src/qhull-warn.pri57
-rw-r--r--xs/src/qhull/src/qhull/qhull.pro9
-rw-r--r--xs/src/qhull/src/qhull/unix.c372
-rw-r--r--xs/src/qhull/src/qhull/unix_r.c374
-rw-r--r--xs/src/qhull/src/qhulltest/Coordinates_test.cpp539
-rw-r--r--xs/src/qhull/src/qhulltest/PointCoordinates_test.cpp478
-rw-r--r--xs/src/qhull/src/qhulltest/QhullFacetList_test.cpp196
-rw-r--r--xs/src/qhull/src/qhulltest/QhullFacetSet_test.cpp153
-rw-r--r--xs/src/qhull/src/qhulltest/QhullFacet_test.cpp283
-rw-r--r--xs/src/qhull/src/qhulltest/QhullHyperplane_test.cpp429
-rw-r--r--xs/src/qhull/src/qhulltest/QhullLinkedList_test.cpp330
-rw-r--r--xs/src/qhull/src/qhulltest/QhullPointSet_test.cpp378
-rw-r--r--xs/src/qhull/src/qhulltest/QhullPoint_test.cpp437
-rw-r--r--xs/src/qhull/src/qhulltest/QhullPoints_test.cpp561
-rw-r--r--xs/src/qhull/src/qhulltest/QhullRidge_test.cpp159
-rw-r--r--xs/src/qhull/src/qhulltest/QhullSet_test.cpp434
-rw-r--r--xs/src/qhull/src/qhulltest/QhullVertexSet_test.cpp152
-rw-r--r--xs/src/qhull/src/qhulltest/QhullVertex_test.cpp184
-rw-r--r--xs/src/qhull/src/qhulltest/Qhull_test.cpp360
-rw-r--r--xs/src/qhull/src/qhulltest/RboxPoints_test.cpp215
-rw-r--r--xs/src/qhull/src/qhulltest/RoadTest.cpp94
-rw-r--r--xs/src/qhull/src/qhulltest/RoadTest.h102
-rw-r--r--xs/src/qhull/src/qhulltest/qhulltest.cpp94
-rw-r--r--xs/src/qhull/src/qhulltest/qhulltest.pro36
-rw-r--r--xs/src/qhull/src/qvoronoi/qvoronoi.c303
-rw-r--r--xs/src/qhull/src/qvoronoi/qvoronoi.pro9
-rw-r--r--xs/src/qhull/src/qvoronoi/qvoronoi_r.c305
-rw-r--r--xs/src/qhull/src/rbox/rbox.c88
-rw-r--r--xs/src/qhull/src/rbox/rbox.pro9
-rw-r--r--xs/src/qhull/src/rbox/rbox_r.c78
-rw-r--r--xs/src/qhull/src/testqset/testqset.c891
-rw-r--r--xs/src/qhull/src/testqset/testqset.pro30
-rw-r--r--xs/src/qhull/src/testqset_r/testqset_r.c890
-rw-r--r--xs/src/qhull/src/testqset_r/testqset_r.pro30
-rw-r--r--xs/src/qhull/src/user_eg/user_eg.c330
-rw-r--r--xs/src/qhull/src/user_eg/user_eg.pro11
-rw-r--r--xs/src/qhull/src/user_eg/user_eg_r.c326
-rw-r--r--xs/src/qhull/src/user_eg2/user_eg2.c746
-rw-r--r--xs/src/qhull/src/user_eg2/user_eg2.pro11
-rw-r--r--xs/src/qhull/src/user_eg2/user_eg2_r.c742
-rw-r--r--xs/src/qhull/src/user_eg3/user_eg3.pro12
-rw-r--r--xs/src/qhull/src/user_eg3/user_eg3_r.cpp162
-rw-r--r--xs/src/slic3r/AppController.cpp216
-rw-r--r--xs/src/slic3r/AppController.hpp52
-rw-r--r--xs/src/slic3r/AppControllerWx.cpp73
-rw-r--r--xs/src/slic3r/GUI/3DScene.cpp84
-rw-r--r--xs/src/slic3r/GUI/3DScene.hpp16
-rw-r--r--xs/src/slic3r/GUI/Field.cpp31
-rw-r--r--xs/src/slic3r/GUI/Field.hpp3
-rw-r--r--xs/src/slic3r/GUI/FirmwareDialog.cpp6
-rw-r--r--xs/src/slic3r/GUI/GLCanvas3D.cpp74
-rw-r--r--xs/src/slic3r/GUI/GLCanvas3D.hpp12
-rw-r--r--xs/src/slic3r/GUI/GLGizmo.cpp340
-rw-r--r--xs/src/slic3r/GUI/GLGizmo.hpp53
-rw-r--r--xs/src/slic3r/GUI/GUI.cpp2
-rw-r--r--xs/src/slic3r/GUI/OptionsGroup.cpp6
-rw-r--r--xs/src/slic3r/GUI/Preset.cpp17
-rw-r--r--xs/src/slic3r/GUI/Tab.cpp65
-rw-r--r--xs/src/slic3r/GUI/Tab.hpp3
-rw-r--r--xs/src/slic3r/ProgressIndicator.hpp (renamed from xs/src/slic3r/IProgressIndicator.hpp)33
-rw-r--r--xs/src/slic3r/Strings.hpp10
-rw-r--r--xs/src/slic3r/Utils/Duet.cpp279
-rw-r--r--xs/src/slic3r/Utils/Duet.hpp47
-rw-r--r--xs/src/slic3r/Utils/Http.cpp36
-rw-r--r--xs/src/slic3r/Utils/Http.hpp8
-rw-r--r--xs/src/slic3r/Utils/OctoPrint.cpp80
-rw-r--r--xs/src/slic3r/Utils/OctoPrint.hpp9
-rw-r--r--xs/src/slic3r/Utils/PrintHost.cpp23
-rw-r--r--xs/src/slic3r/Utils/PrintHost.hpp35
-rw-r--r--xs/src/slic3r/Utils/PrintHostSendDialog.cpp52
-rw-r--r--xs/src/slic3r/Utils/PrintHostSendDialog.hpp38
-rw-r--r--xs/xsp/AppController.xsp3
-rw-r--r--xs/xsp/GUI_3DScene.xsp1
-rw-r--r--xs/xsp/Model.xsp3
-rw-r--r--xs/xsp/Print.xsp30
-rw-r--r--xs/xsp/Utils_OctoPrint.xsp13
-rw-r--r--xs/xsp/Utils_PrintHost.xsp12
-rw-r--r--xs/xsp/my.map4
-rw-r--r--xs/xsp/typemap.xspt1
351 files changed, 109541 insertions, 2485 deletions
diff --git a/Build.PL b/Build.PL
index 8f882fc4b..16e37986a 100644
--- a/Build.PL
+++ b/Build.PL
@@ -38,7 +38,6 @@ if ($gui) {
%prereqs = qw(
Class::Accessor 0
Wx 0.9918
- Socket 2.016
);
%recommends = qw(
Wx::GLCanvas 0
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 89e6369e0..8e3d8ad4d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,5 +1,4 @@
-# Boost 1.63 requires CMake 3.7 or newer
-cmake_minimum_required(VERSION 2.8)
+cmake_minimum_required(VERSION 3.2)
project(Slic3r)
@@ -23,6 +22,7 @@ option(SLIC3R_GUI "Compile Slic3r with GUI components (OpenGL, wxWidgets)"
option(SLIC3R_PRUSACONTROL "Compile Slic3r with the PrusaControl prject file format (requires wxWidgets base library)" 1)
option(SLIC3R_PROFILE "Compile Slic3r with an invasive Shiny profiler" 0)
option(SLIC3R_MSVC_COMPILE_PARALLEL "Compile on Visual Studio in parallel" 1)
+option(SLIC3R_MSVC_PDB "Generate PDB files on MSVC in Release mode" 1)
if (MSVC AND SLIC3R_MSVC_COMPILE_PARALLEL)
add_compile_options(/MP)
@@ -36,10 +36,18 @@ else()
set(ENV_PATH_SEPARATOR ":")
endif()
set(ENV{PATH} "${PROJECT_SOURCE_DIR}/local-lib/bin${ENV_PATH_SEPARATOR}$ENV{PATH}")
-set(ENV{PERL5LIB} "${PROJECT_SOURCE_DIR}/local-lib/lib/perl${ENV_PATH_SEPARATOR}$ENV{PERL5LIB}")
+set(PERL_INCLUDE "${PROJECT_SOURCE_DIR}/local-lib/lib/perl5${ENV_PATH_SEPARATOR}$ENV{PERL5LIB}")
message("PATH: $ENV{PATH}")
-message("PERL5LIB: $ENV{PERL5LIB}")
+message("PERL_INCLUDE: ${PERL_INCLUDE}")
find_package(Perl REQUIRED)
+if (WIN32)
+ # On Windows passing the PERL5LIB variable causes various problems (such as with MAX_PATH and others),
+ # basically I've found no good way to do it on Windows.
+ set(PERL5LIB_ENV_CMD "")
+else()
+ set(PERL5LIB_ENV_CMD ${CMAKE_COMMAND} -E env PERL5LIB=${PERL_INCLUDE})
+endif()
+
# CMAKE_PREFIX_PATH is used to point CMake to the remaining dependencies (Boost, TBB, ...)
# We pick it from environment if it is not defined in another way
diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm
index 0c6c81bb5..46627311f 100644
--- a/lib/Slic3r.pm
+++ b/lib/Slic3r.pm
@@ -167,6 +167,7 @@ sub thread_cleanup {
*Slic3r::GUI::PresetHints::DESTROY = sub {};
*Slic3r::GUI::TabIface::DESTROY = sub {};
*Slic3r::OctoPrint::DESTROY = sub {};
+ *Slic3r::Duet::DESTROY = sub {};
*Slic3r::PresetUpdater::DESTROY = sub {};
return undef; # this prevents a "Scalars leaked" warning
}
diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm
index a0eef72fe..48b82237f 100644
--- a/lib/Slic3r/GUI/Plater.pm
+++ b/lib/Slic3r/GUI/Plater.pm
@@ -53,7 +53,7 @@ sub new {
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
$self->{config} = Slic3r::Config::new_from_defaults_keys([qw(
bed_shape complete_objects extruder_clearance_radius skirts skirt_distance brim_width variable_layer_height
- serial_port serial_speed octoprint_host octoprint_apikey octoprint_cafile
+ serial_port serial_speed host_type print_host printhost_apikey printhost_cafile
nozzle_diameter single_extruder_multi_material wipe_tower wipe_tower_x wipe_tower_y wipe_tower_width
wipe_tower_rotation_angle extruder_colour filament_colour max_print_height printer_model
)]);
@@ -140,9 +140,16 @@ sub new {
};
# callback to react to gizmo rotate
+ # omitting last three parameters means rotation around Z
+ # otherwise they are the components of the rotation axis vector
my $on_gizmo_rotate = sub {
- my ($angle_z) = @_;
- $self->rotate(rad2deg($angle_z), Z, 'absolute');
+ my ($angle, $axis_x, $axis_y, $axis_z) = @_;
+ if (!defined $axis_x) {
+ $self->rotate(rad2deg($angle), Z, 'absolute');
+ }
+ else {
+ $self->rotate(rad2deg($angle), undef, 'absolute', $axis_x, $axis_y, $axis_z) if $angle != 0;
+ }
};
# callback to update object's geometry info while using gizmos
@@ -760,6 +767,15 @@ sub load_files {
$model->convert_multipart_object(scalar(@$nozzle_dmrs)) if $dialog->ShowModal() == wxID_YES;
}
+ # objects imported from 3mf require a call to center_around_origin to have gizmos working properly and this call
+ # need to be done after looks_like_multipart_object detection
+ if ($input_file =~ /.3[mM][fF]$/)
+ {
+ foreach my $model_object (@{$model->objects}) {
+ $model_object->center_around_origin; # also aligns object to Z = 0
+ }
+ }
+
if ($one_by_one) {
push @obj_idx, $self->load_model_objects(@{$model->objects});
} else {
@@ -995,9 +1011,10 @@ sub set_number_of_copies {
my $model_object = $self->{model}->objects->[$obj_idx];
# prompt user
- my $copies = Wx::GetNumberFromUser("", L("Enter the number of copies of the selected object:"), L("Copies"), $model_object->instances_count, 0, 1000, $self);
+ my $copies = -1;
+ $copies = Wx::GetNumberFromUser("", L("Enter the number of copies of the selected object:"), L("Copies"), $model_object->instances_count, 0, 1000, $self);
my $diff = $copies - $model_object->instances_count;
- if ($diff == 0) {
+ if ($diff == 0 || $copies == -1) {
# no variation
$self->resume_background_process;
} elsif ($diff > 0) {
@@ -1030,28 +1047,40 @@ sub _get_number_from_user {
}
sub rotate {
- my ($self, $angle, $axis, $relative_key) = @_;
+ my ($self, $angle, $axis, $relative_key, $axis_x, $axis_y, $axis_z) = @_;
$relative_key //= 'absolute'; # relative or absolute coordinates
- $axis //= Z; # angle is in degrees
-
+ $axis_x //= 0;
+ $axis_y //= 0;
+ $axis_z //= 0;
my $relative = $relative_key eq 'relative';
-
+
my ($obj_idx, $object) = $self->selected_object;
return if !defined $obj_idx;
-
+
my $model_object = $self->{model}->objects->[$obj_idx];
my $model_instance = $model_object->instances->[0];
-
+
if (!defined $angle) {
my $axis_name = $axis == X ? 'X' : $axis == Y ? 'Y' : 'Z';
my $default = $axis == Z ? rad2deg($model_instance->rotation) : 0;
$angle = $self->_get_number_from_user(L("Enter the rotation angle:"), L("Rotate around ").$axis_name.(" axis"), L("Invalid rotation angle entered"), $default);
return if $angle eq '';
}
+
+ # Let's calculate vector of rotation axis (if we don't have it already)
+ # The minus is there so that the direction is the same as was established
+ if (defined $axis) {
+ if ($axis == X) {
+ $axis_x = -1;
+ }
+ if ($axis == Y) {
+ $axis_y = -1;
+ }
+ }
$self->stop_background_process;
- if ($axis == Z) {
+ if (defined $axis && $axis == Z) {
my $new_angle = deg2rad($angle);
foreach my $inst (@{ $model_object->instances }) {
my $rotation = ($relative ? $inst->rotation : 0.) + $new_angle;
@@ -1066,13 +1095,15 @@ sub rotate {
}
# $object->transform_thumbnail($self->{model}, $obj_idx);
} else {
- # rotation around X and Y needs to be performed on mesh
- # so we first apply any Z rotation
- if ($model_instance->rotation != 0) {
- $model_object->rotate($model_instance->rotation, Z);
- $_->set_rotation(0) for @{ $model_object->instances };
+ if (defined $axis) {
+ # rotation around X and Y needs to be performed on mesh
+ # so we first apply any Z rotation
+ if ($model_instance->rotation != 0) {
+ $model_object->rotate($model_instance->rotation, Slic3r::Pointf3->new(0, 0, -1));
+ $_->set_rotation(0) for @{ $model_object->instances };
+ }
}
- $model_object->rotate(deg2rad($angle), $axis);
+ $model_object->rotate(deg2rad($angle), Slic3r::Pointf3->new($axis_x, $axis_y, $axis_z));
# # realign object to Z = 0
# $model_object->center_around_origin;
@@ -1098,7 +1129,7 @@ sub mirror {
# apply Z rotation before mirroring
if ($model_instance->rotation != 0) {
- $model_object->rotate($model_instance->rotation, Z);
+ $model_object->rotate($model_instance->rotation, Slic3r::Pointf3->new(0, 0, 1));
$_->set_rotation(0) for @{ $model_object->instances };
}
@@ -1126,8 +1157,7 @@ sub changescale {
my $model_object = $self->{model}->objects->[$obj_idx];
my $model_instance = $model_object->instances->[0];
- my $object_size = $model_object->bounding_box->size;
- my $bed_size = Slic3r::Polygon->new_scale(@{$self->{config}->bed_shape})->bounding_box->size;
+ my $object_size = $model_object->instance_bounding_box(0)->size;
if (defined $axis) {
my $axis_name = $axis == X ? 'X' : $axis == Y ? 'Y' : 'Z';
@@ -1135,7 +1165,7 @@ sub changescale {
if ($tosize) {
my $cursize = $object_size->[$axis];
my $newsize = $self->_get_number_from_user(
- sprintf(L('Enter the new size for the selected object (print bed: %smm):'), unscale($bed_size->[$axis])),
+ L('Enter the new size for the selected object:'),
L("Scale along ").$axis_name, L('Invalid scaling value entered'), $cursize, 1);
return if $newsize eq '';
$scale = $newsize / $cursize * 100;
@@ -1146,7 +1176,7 @@ sub changescale {
# apply Z rotation before scaling
if ($model_instance->rotation != 0) {
- $model_object->rotate($model_instance->rotation, Z);
+ $model_object->rotate($model_instance->rotation, Slic3r::Pointf3->new(0, 0, 1));
$_->set_rotation(0) for @{ $model_object->instances };
}
@@ -1291,7 +1321,9 @@ sub async_apply_config {
# We also need to reload 3D scene because of the wipe tower preview box
if ($self->{config}->wipe_tower) {
- Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 1) if $self->{canvas3D}
+ my $selections = $self->collect_selections;
+ Slic3r::GUI::_3DScene::set_objects_selections($self->{canvas3D}, \@$selections);
+ Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 1) if $self->{canvas3D}
}
}
}
@@ -1507,6 +1539,8 @@ sub on_process_completed {
$self->{preview3D}->reload_print if $self->{preview3D};
# in case this was MM print, wipe tower bounding box on 3D tab might need redrawing with exact depth:
+ my $selections = $self->collect_selections;
+ Slic3r::GUI::_3DScene::set_objects_selections($self->{canvas3D}, \@$selections);
Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 1);
# if we have an export filename, start a new thread for exporting G-code
@@ -1569,7 +1603,7 @@ sub on_export_completed {
$message = L("File added to print queue");
$do_print = 1;
} elsif ($self->{send_gcode_file}) {
- $message = L("Sending G-code file to the OctoPrint server...");
+ $message = L("Sending G-code file to the Printer Host ...");
$send_gcode = 1;
} else {
$message = L("G-code file exported to ") . $self->{export_gcode_output_file};
@@ -1585,9 +1619,10 @@ sub on_export_completed {
# Send $self->{send_gcode_file} to OctoPrint.
if ($send_gcode) {
- my $op = Slic3r::OctoPrint->new($self->{config});
- if ($op->send_gcode($self->{send_gcode_file})) {
- $self->statusbar->SetStatusText(L("OctoPrint upload finished."));
+ my $host = Slic3r::PrintHost::get_print_host($self->{config});
+
+ if ($host->send_gcode($self->{send_gcode_file})) {
+ $self->statusbar->SetStatusText(L("Upload to host finished."));
} else {
$self->statusbar->SetStatusText("");
}
@@ -1620,20 +1655,37 @@ sub print_info_box_show {
$grid_sizer->AddGrowableCol(1, 1);
$grid_sizer->AddGrowableCol(3, 1);
$print_info_sizer->Add($grid_sizer, 0, wxEXPAND);
+ my $is_wipe_tower = $self->{print}->total_wipe_tower_filament > 0;
my @info = (
L("Used Filament (m)")
- => sprintf("%.2f" , $self->{print}->total_used_filament / 1000),
+ => $is_wipe_tower ?
+ sprintf("%.2f (%.2f %s + %.2f %s)" , $self->{print}->total_used_filament / 1000,
+ ($self->{print}->total_used_filament - $self->{print}->total_wipe_tower_filament) / 1000,
+ L("objects"),
+ $self->{print}->total_wipe_tower_filament / 1000,
+ L("wipe tower")) :
+ sprintf("%.2f" , $self->{print}->total_used_filament / 1000),
+
L("Used Filament (mm³)")
=> sprintf("%.2f" , $self->{print}->total_extruded_volume),
L("Used Filament (g)"),
=> sprintf("%.2f" , $self->{print}->total_weight),
L("Cost"),
- => sprintf("%.2f" , $self->{print}->total_cost),
+ => $is_wipe_tower ?
+ sprintf("%.2f (%.2f %s + %.2f %s)" , $self->{print}->total_cost,
+ ($self->{print}->total_cost - $self->{print}->total_wipe_tower_cost),
+ L("objects"),
+ $self->{print}->total_wipe_tower_cost,
+ L("wipe tower")) :
+ sprintf("%.2f" , $self->{print}->total_cost),
L("Estimated printing time (normal mode)")
=> $self->{print}->estimated_normal_print_time,
L("Estimated printing time (silent mode)")
=> $self->{print}->estimated_silent_print_time
);
+ # if there is a wipe tower, insert number of toolchanges info into the array:
+ splice (@info, 8, 0, L("Number of tool changes") => sprintf("%.d", $self->{print}->m_wipe_tower_number_of_toolchanges)) if ($is_wipe_tower);
+
while ( my $label = shift @info) {
my $value = shift @info;
next if $value eq "N/A";
@@ -1648,6 +1700,7 @@ sub print_info_box_show {
$scrolled_window_sizer->Show(2, $show);
$scrolled_window_panel->Layout;
+ $self->Layout;
}
sub do_print {
@@ -1914,8 +1967,8 @@ sub on_config_change {
} elsif ($opt_key eq 'serial_port') {
$self->{btn_print}->Show($config->get('serial_port'));
$self->Layout;
- } elsif ($opt_key eq 'octoprint_host') {
- $self->{btn_send_gcode}->Show($config->get('octoprint_host'));
+ } elsif ($opt_key eq 'print_host') {
+ $self->{btn_send_gcode}->Show($config->get('print_host'));
$self->Layout;
} elsif ($opt_key eq 'variable_layer_height') {
if ($config->get('variable_layer_height') != 1) {
diff --git a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm
index 35aa28818..26a6fdec3 100644
--- a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm
+++ b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm
@@ -137,7 +137,7 @@ sub new {
# Adjust position / orientation of the split object halves.
if ($self->{new_model_objects}{lower}) {
if ($self->{cut_options}{rotate_lower}) {
- $self->{new_model_objects}{lower}->rotate(PI, X);
+ $self->{new_model_objects}{lower}->rotate(PI, Slic3r::Pointf3->new(1,0,0));
$self->{new_model_objects}{lower}->center_around_origin; # align to Z = 0
}
}
diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm
index a20a241f7..4032886f3 100644
--- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm
+++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm
@@ -157,7 +157,7 @@ sub new {
my ($volume_idx) = @_;
$self->reload_tree($volume_idx);
});
- Slic3r::GUI::_3DScene::load_model_object($canvas, $self->{model_object}, 0, [0]);
+ Slic3r::GUI::_3DScene::load_model_object($canvas, $self->{model_object}, 0, [0]);
Slic3r::GUI::_3DScene::set_auto_bed_shape($canvas);
Slic3r::GUI::_3DScene::set_axes_length($canvas, 2.0 * max(@{ Slic3r::GUI::_3DScene::get_volumes_bounding_box($canvas)->size }));
$canvas->SetSize([500,700]);
@@ -362,13 +362,17 @@ sub on_btn_load {
}
foreach my $object (@{$model->objects}) {
+ $object->center_around_origin;
foreach my $volume (@{$object->volumes}) {
my $new_volume = $self->{model_object}->add_volume($volume);
$new_volume->set_modifier($is_modifier);
$new_volume->set_name(basename($input_file));
# apply the same translation we applied to the object
- $new_volume->mesh->translate(@{$self->{model_object}->origin_translation});
+ my $delta_x = $self->{model_object}->origin_translation->x - $object->origin_translation->x;
+ my $delta_y = $self->{model_object}->origin_translation->y - $object->origin_translation->y;
+ my $delta_z = $self->{model_object}->origin_translation->z - $object->origin_translation->z;
+ $new_volume->mesh->translate($delta_x, $delta_y, $delta_z);
# set a default extruder value, since user can't add it manually
$new_volume->config->set_ifndef('extruder', 0);
@@ -377,7 +381,8 @@ sub on_btn_load {
}
}
}
-
+
+ $self->{model_object}->center_around_origin if $self->{parts_changed};
$self->_parts_changed;
}
@@ -479,7 +484,8 @@ sub on_btn_delete {
$self->{model_object}->delete_volume($itemData->{volume_id});
$self->{parts_changed} = 1;
}
-
+
+ $self->{model_object}->center_around_origin if $self->{parts_changed};
$self->_parts_changed;
}
diff --git a/lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm b/lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm
index 3b10ed99f..ea4ce7132 100644
--- a/lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm
+++ b/lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm
@@ -136,7 +136,7 @@ sub update_optgroup {
full_labels => 1,
label_font => $Slic3r::GUI::small_font,
sidetext_font => $Slic3r::GUI::small_font,
- label_width => 120,
+ label_width => 150,
on_change => sub { $self->{on_change}->() if $self->{on_change} },
extra_column => sub {
my ($line) = @_;
diff --git a/resources/icons/overlay/layflat_hover.png b/resources/icons/overlay/layflat_hover.png
new file mode 100644
index 000000000..afce81d19
--- /dev/null
+++ b/resources/icons/overlay/layflat_hover.png
Binary files differ
diff --git a/resources/icons/overlay/layflat_off.png b/resources/icons/overlay/layflat_off.png
new file mode 100644
index 000000000..70d198112
--- /dev/null
+++ b/resources/icons/overlay/layflat_off.png
Binary files differ
diff --git a/resources/icons/overlay/layflat_on.png b/resources/icons/overlay/layflat_on.png
new file mode 100644
index 000000000..3c1891b3c
--- /dev/null
+++ b/resources/icons/overlay/layflat_on.png
Binary files differ
diff --git a/resources/profiles/PrusaResearch.idx b/resources/profiles/PrusaResearch.idx
index ba4123588..63a81db49 100644
--- a/resources/profiles/PrusaResearch.idx
+++ b/resources/profiles/PrusaResearch.idx
@@ -1,8 +1,14 @@
min_slic3r_version = 1.41.0-alpha
+0.2.2 Edited MMU2 Single mode purge line
+0.2.1 Added PET and BVOH settings for MMU2
+0.2.0-beta5 Fixed MMU1 ramming parameters
+0.2.0-beta4 Added filament loading speed at start, increased minimal purge on wipe tower
+0.2.0-beta3 Edited ramming parameters and filament cooling moves for MMU2
+0.2.0-beta2 Edited first layer speed and wipe tower position
0.2.0-beta Removed limit on the MK3MMU2 height, added legacy M204 S T format to the MK2 profiles
0.2.0-alpha8 Added filament_load/unload_time for the PLA/ABS MMU2 filament presets.
-0.2.0-alpha7 Fixed the *MK3* references
-0.2.0-alpha6
+0.2.0-alpha7 Vojtech's fix the incorrect *MK3* references
+0.2.0-alpha6 Jindra's way to fix the 0.2.0-alpha5 version
0.2.0-alpha5 Bumped up firmware versions for MK2.5/MK3 to 3.3.1, disabled priming areas for MK3MMU2
0.2.0-alpha4 Extended the custom start/end G-codes of the MMU2.0 printers for no priming towers.
0.2.0-alpha3 Adjusted machine limits for time estimates, added filament density and cost
diff --git a/resources/profiles/PrusaResearch.ini b/resources/profiles/PrusaResearch.ini
index 32ec800e7..3b44a4bc7 100644
--- a/resources/profiles/PrusaResearch.ini
+++ b/resources/profiles/PrusaResearch.ini
@@ -5,7 +5,7 @@
name = Prusa Research
# Configuration version of this file. Config file will only be installed, if the config_version differs.
# This means, the server may force the Slic3r configuration to be downgraded.
-config_version = 0.2.0-beta
+config_version = 0.2.2
# Where to get the updates from?
config_update_url = https://raw.githubusercontent.com/prusa3d/Slic3r-settings/master/live/PrusaResearch/
@@ -67,7 +67,7 @@ fill_pattern = cubic
first_layer_acceleration = 1000
first_layer_extrusion_width = 0.42
first_layer_height = 0.2
-first_layer_speed = 30
+first_layer_speed = 20
gap_fill_speed = 40
gcode_comments = 0
infill_every_layers = 1
@@ -113,7 +113,7 @@ support_material_interface_extruder = 0
support_material_angle = 0
support_material_buildplate_only = 0
support_material_enforce_layers = 0
-support_material_contact_distance = 0.15
+support_material_contact_distance = 0.1
support_material_interface_contact_loops = 0
support_material_interface_layers = 2
support_material_interface_spacing = 0.2
@@ -122,9 +122,9 @@ support_material_pattern = rectilinear
support_material_spacing = 2
support_material_speed = 50
support_material_synchronize_layers = 0
-support_material_threshold = 45
+support_material_threshold = 55
support_material_with_sheath = 0
-support_material_xy_spacing = 60%
+support_material_xy_spacing = 50%
thin_walls = 0
top_infill_extrusion_width = 0.45
top_solid_infill_speed = 40
@@ -133,13 +133,15 @@ wipe_tower = 1
wipe_tower_bridging = 10
wipe_tower_rotation_angle = 0
wipe_tower_width = 60
-wipe_tower_x = 180
-wipe_tower_y = 135
+wipe_tower_x = 170
+wipe_tower_y = 140
xy_size_compensation = 0
[print:*MK3*]
fill_pattern = grid
single_extruder_multi_material_priming = 0
+wipe_tower_x = 170
+wipe_tower_y = 125
# Print parameters common to a 0.25mm diameter nozzle.
[print:*0.25nozzle*]
@@ -557,13 +559,17 @@ compatible_printers_condition = ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ a
end_filament_gcode = "; Filament-specific end gcode"
extrusion_multiplier = 1
filament_loading_speed = 28
+filament_loading_speed_start = 3
filament_unloading_speed = 90
+filament_unloading_speed_start = 100
filament_toolchange_delay = 0
filament_cooling_moves = 4
filament_cooling_initial_speed = 2.2
filament_cooling_final_speed = 3.4
+filament_load_time = 0
+filament_unload_time = 0
filament_ramming_parameters = "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6"
-filament_minimal_purge_on_wipe_tower = 5
+filament_minimal_purge_on_wipe_tower = 15
filament_cost = 0
filament_density = 0
filament_diameter = 1.75
@@ -664,6 +670,8 @@ cooling = 1
disable_fan_first_layers = 3
fan_always_on = 0
fan_below_layer_time = 10
+filament_cost = 58.66
+filament_density = 1.18
first_layer_bed_temperature = 105
first_layer_temperature = 270
max_fan_speed = 20
@@ -741,7 +749,7 @@ temperature = 260
[filament:E3D Edge]
inherits = *PET*
-filament_cost = 0
+filament_cost = 56.9
filament_density = 1.26
filament_notes = "List of manufacturers tested with standart PET print settings for MK2:\n\nE3D Edge\nFillamentum CPE GH100\nPlasty Mladeč PETG"
@@ -754,14 +762,14 @@ temperature = 270
[filament:Fillamentum ABS]
inherits = *ABS*
-filament_cost = 0
+filament_cost = 32.4
filament_density = 1.04
first_layer_temperature = 240
temperature = 240
[filament:Fillamentum ASA]
inherits = *ABS*
-filament_cost = 0
+filament_cost = 38.7
filament_density = 1.04
fan_always_on = 1
first_layer_temperature = 265
@@ -769,7 +777,7 @@ temperature = 265
[filament:Fillamentum CPE HG100 HM100]
inherits = *PET*
-filament_cost = 0
+filament_cost = 54.1
filament_density = 1.25
filament_notes = "CPE HG100 , CPE HM100"
first_layer_bed_temperature = 90
@@ -783,7 +791,7 @@ inherits = *PLA*
# For now, all but selected filaments are disabled for the MMU 2.0
compatible_printers_condition = nozzle_diameter[0]>0.35 and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and single_extruder_multi_material)
extrusion_multiplier = 1.2
-filament_cost = 0
+filament_cost = 68
filament_density = 1.15
filament_colour = #804040
filament_max_volumetric_speed = 10
@@ -793,24 +801,26 @@ temperature = 190
[filament:Generic ABS]
inherits = *ABS*
-filament_cost = 0
+filament_cost = 27.82
filament_density = 1.04
filament_notes = "List of materials tested with standart ABS print settings for MK2:\n\nEsun ABS\nFil-A-Gehr ABS\nHatchboxABS\nPlasty Mladeč ABS"
[filament:Generic PET]
inherits = *PET*
-filament_cost = 0
+filament_cost = 27.82
filament_density = 1.24
filament_notes = "List of manufacturers tested with standart PET print settings for MK2:\n\nE3D Edge\nFillamentum CPE GH100\nPlasty Mladeč PETG"
[filament:Generic PLA]
inherits = *PLA*
-filament_cost = 0
+filament_cost = 25.4
filament_density = 1.27
filament_notes = "List of materials tested with standart PLA print settings for MK2:\n\nDas Filament\nEsun PLA\nEUMAKERS PLA\nFiberlogy HD-PLA\nFillamentum PLA\nFloreon3D\nHatchbox PLA\nPlasty Mladeč PLA\nPrimavalue PLA\nProto pasta Matte Fiber\nVerbatim PLA\nVerbatim BVOH"
[filament:Polymaker PC-Max]
inherits = *ABS*
+filament_cost = 77.3
+filament_density = 1.20
bed_temperature = 115
filament_colour = #3A80CA
first_layer_bed_temperature = 100
@@ -819,6 +829,8 @@ temperature = 270
[filament:Primavalue PVA]
inherits = *PLA*
+filament_cost = 108
+filament_density = 1.23
cooling = 0
fan_always_on = 0
filament_colour = #FFFFD7
@@ -843,10 +855,7 @@ compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and
filament_cooling_final_speed = 50
filament_cooling_initial_speed = 10
filament_cooling_moves = 5
-filament_loading_speed = 14
filament_ramming_parameters = "120 110 5.32258 5.45161 5.67742 6 6.48387 7.12903 7.90323 8.70968 9.3871 9.83871 10.0968 10.2258| 0.05 5.30967 0.45 5.50967 0.95 6.1871 1.45 7.39677 1.95 9.05484 2.45 10 2.95 10.3098 3.45 13.0839 3.95 7.6 4.45 7.6 4.95 7.6";
-filament_load_time = 12
-filament_unload_time = 11
[filament:Generic ABS MMU2]
inherits = *ABS MMU2*
@@ -856,6 +865,8 @@ inherits = *ABS MMU2*
[filament:Prusa HIPS]
inherits = *ABS*
+filament_cost = 27.3
+filament_density = 1.04
bridge_fan_speed = 50
cooling = 1
extrusion_multiplier = 0.9
@@ -876,6 +887,28 @@ filament_cost = 27.82
filament_density = 1.27
filament_notes = "List of manufacturers tested with standart PET print settings for MK2:\n\nE3D Edge\nFillamentum CPE GH100\nPlasty Mladeč PETG"
+[filament:*PET MMU2*]
+inherits = Prusa PET
+compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and single_extruder_multi_material
+temperature = 230
+first_layer_temperature = 230
+filament_cooling_final_speed = 1
+filament_cooling_initial_speed = 2
+filament_cooling_moves = 1
+filament_load_time = 12
+filament_loading_speed = 14
+filament_notes = PET
+filament_ramming_parameters = "120 140 4.70968 4.74194 4.77419 4.80645 4.83871 4.87097 4.90323 5 5.25806 5.67742 6.29032 7.06452 7.83871 8.3871| 0.05 4.72901 0.45 4.73545 0.95 4.83226 1.45 4.88067 1.95 5.05483 2.45 5.93553 2.95 7.53556 3.45 8.6323 3.95 7.6 4.45 7.6 4.95 7.6"
+filament_unload_time = 11
+filament_unloading_speed = 20
+filament_unloading_speed_start = 120
+
+[filament:Generic PET MMU2]
+inherits = *PET MMU2*
+
+[filament:Prusa PET MMU2]
+inherits = *PET MMU2*
+
[filament:Prusa PLA]
inherits = *PLA*
filament_cost = 25.4
@@ -885,13 +918,15 @@ filament_notes = "List of materials tested with standart PLA print settings for
[filament:*PLA MMU2*]
inherits = Prusa PLA
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and single_extruder_multi_material
-filament_cooling_final_speed = 50
-filament_cooling_initial_speed = 10
-filament_cooling_moves = 7
-filament_loading_speed = 14
-filament_ramming_parameters = "120 110 4.03226 4.12903 4.25806 4.41935 4.58065 4.80645 5.35484 6.29032 7.58065 9.09677 10.5806 11.8387 12.6452 12.9677| 0.05 4.01935 0.45 4.15483 0.95 4.50968 1.45 4.94516 1.95 6.79677 2.45 9.87102 2.95 12.4388 3.45 13.0839 3.95 7.6 4.45 7.6 4.95 7.6"
+temperature = 205
+filament_cooling_final_speed = 1
+filament_cooling_initial_speed = 2
+filament_cooling_moves = 1
filament_load_time = 12
+filament_loading_speed = 14
+filament_ramming_parameters = "120 110 2.70968 2.93548 3.32258 3.83871 4.58065 5.54839 6.51613 7.35484 7.93548 8.16129| 0.05 2.66451 0.45 3.05805 0.95 4.05807 1.45 5.97742 1.95 7.69999 2.45 8.1936 2.95 11.342 3.45 11.4065 3.95 7.6 4.45 7.6 4.95 7.6"
filament_unload_time = 11
+filament_unloading_speed = 20
[filament:Generic PLA MMU2]
inherits = *PLA MMU2*
@@ -901,11 +936,13 @@ inherits = *PLA MMU2*
[filament:SemiFlex or Flexfill 98A]
inherits = *FLEX*
-filament_cost = 0
+filament_cost = 82
filament_density = 1.22
[filament:Taulman Bridge]
inherits = *common*
+filament_cost = 40
+filament_density = 1.13
bed_temperature = 90
bridge_fan_speed = 40
cooling = 0
@@ -925,6 +962,8 @@ temperature = 250
[filament:Taulman T-Glase]
inherits = *PET*
+filament_cost = 40
+filament_density = 1.27
bridge_fan_speed = 40
cooling = 0
fan_always_on = 0
@@ -936,6 +975,8 @@ start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{el
[filament:Verbatim BVOH]
inherits = *common*
+filament_cost = 218
+filament_density = 1.23
bed_temperature = 60
bridge_fan_speed = 100
cooling = 0
@@ -944,7 +985,7 @@ extrusion_multiplier = 1
fan_always_on = 0
fan_below_layer_time = 100
filament_colour = #FFFFD7
-filament_max_volumetric_speed = 10
+filament_max_volumetric_speed = 4
filament_notes = "List of materials tested with standart PLA print settings for MK2:\n\nDas Filament\nEsun PLA\nEUMAKERS PLA\nFiberlogy HD-PLA\nFillamentum PLA\nFloreon3D\nHatchbox PLA\nPlasty Mladeč PLA\nPrimavalue PLA\nProto pasta Matte Fiber\nVerbatim PLA\nVerbatim BVOH"
filament_soluble = 1
filament_type = PLA
@@ -955,8 +996,29 @@ min_fan_speed = 100
start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}10{endif}; Filament gcode"
temperature = 210
+[filament:Verbatim BVOH MMU2]
+inherits = Verbatim BVOH
+compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and single_extruder_multi_material
+temperature = 195
+filament_notes = BVOH
+fan_always_on = 1
+first_layer_temperature = 200
+filament_cooling_final_speed = 1
+filament_cooling_initial_speed = 2
+filament_max_volumetric_speed = 4
+filament_type = PVA
+filament_cooling_moves = 1
+filament_load_time = 12
+filament_loading_speed = 14
+filament_ramming_parameters = "120 110 1.74194 1.90323 2.16129 2.48387 2.83871 3.25806 3.83871 4.6129 5.41935 5.96774| 0.05 1.69677 0.45 1.96128 0.95 2.63872 1.45 3.46129 1.95 4.99031 2.45 6.12908 2.95 8.30974 3.45 11.4065 3.95 7.6 4.45 7.6 4.95 7.6"
+filament_unload_time = 11
+filament_unloading_speed = 20
+filament_unloading_speed_start = 100
+
[filament:Verbatim PP]
inherits = *common*
+filament_cost = 72
+filament_density = 0.89
bed_temperature = 100
bridge_fan_speed = 100
cooling = 1
@@ -1207,7 +1269,7 @@ default_filament_profile = Prusa PLA MMU2
[printer:Original Prusa i3 MK3 MMU2 Single]
inherits = *mm2*
-start_gcode = M107\nM115 U3.3.1 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\nG21 ; set units to millimeters\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT?\n; initial load\nG1 X55.0 E32.0 F1073.0\nG1 X5.0 E32.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG92 E0.0\n
+start_gcode = M107\nM115 U3.4.0-RC2 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\nG21 ; set units to millimeters\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT?\n; purge line\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG92 E0.0\n
end_gcode = G1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15.0000 F5800\nG1 E-20.0000 F5500\nG1 E10.0000 F3000\nG1 E-10.0000 F3100\nG1 E10.0000 F3150\nG1 E-10.0000 F3250\nG1 E10.0000 F3300\n\nM702 C\n\nG4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nG1 X0 Y200; home X axis\nM84 ; disable motors
[printer:Original Prusa i3 MK3 MMU2]
@@ -1215,9 +1277,10 @@ inherits = *mm2*
# The 5x nozzle diameter defines the number of extruders. Other extruder parameters
# (for example the retract values) are duplicaed from the first value, so they do not need
# to be defined explicitely.
+machine_max_acceleration_e = 8000,8000
nozzle_diameter = 0.4,0.4,0.4,0.4,0.4
extruder_colour = #FF8000;#0080FF;#00FFFF;#FF4F4F;#9FFF9F
-start_gcode = M107\nM115 U3.3.1 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG21 ; set units to millimeters\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E32.0 F1073.0\nG1 X5.0 E32.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\n\n;M221 S{if layer_height<0.075}100{else}95{endif}\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG92 E0.0\n
+start_gcode = M107\nM115 U3.4.0-RC2 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG21 ; set units to millimeters\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E32.0 F1073.0\nG1 X5.0 E32.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG92 E0.0\n
end_gcode = {if has_wipe_tower}\nG1 E-15.0000 F3000\n{else}\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15.0000 F5800\nG1 E-20.0000 F5500\nG1 E10.0000 F3000\nG1 E-10.0000 F3100\nG1 E10.0000 F3150\nG1 E-10.0000 F3250\nG1 E10.0000 F3300\n{endif}\n\n; Unload filament\nM702 C\n\nG4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n; Lift print head a bit\n{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+30, max_print_height)}{endif} ; Move print head up\nG1 X0 Y200; home X axis\nM84 ; disable motors\n
# The obsolete presets will be removed when upgrading from the legacy configuration structure (up to Slic3r 1.39.2) to 1.40.0 and newer.
diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt
index d41b4c13a..efd52ac13 100644
--- a/xs/CMakeLists.txt
+++ b/xs/CMakeLists.txt
@@ -251,8 +251,14 @@ add_library(libslic3r_gui STATIC
${LIBDIR}/slic3r/Utils/Http.hpp
${LIBDIR}/slic3r/Utils/FixModelByWin10.cpp
${LIBDIR}/slic3r/Utils/FixModelByWin10.hpp
+ ${LIBDIR}/slic3r/Utils/PrintHostSendDialog.cpp
+ ${LIBDIR}/slic3r/Utils/PrintHostSendDialog.hpp
${LIBDIR}/slic3r/Utils/OctoPrint.cpp
${LIBDIR}/slic3r/Utils/OctoPrint.hpp
+ ${LIBDIR}/slic3r/Utils/Duet.cpp
+ ${LIBDIR}/slic3r/Utils/Duet.hpp
+ ${LIBDIR}/slic3r/Utils/PrintHost.cpp
+ ${LIBDIR}/slic3r/Utils/PrintHost.hpp
${LIBDIR}/slic3r/Utils/Bonjour.cpp
${LIBDIR}/slic3r/Utils/Bonjour.hpp
${LIBDIR}/slic3r/Utils/PresetUpdater.cpp
@@ -261,11 +267,10 @@ add_library(libslic3r_gui STATIC
${LIBDIR}/slic3r/Utils/Time.hpp
${LIBDIR}/slic3r/Utils/HexFile.cpp
${LIBDIR}/slic3r/Utils/HexFile.hpp
- ${LIBDIR}/slic3r/IProgressIndicator.hpp
+ ${LIBDIR}/slic3r/ProgressIndicator.hpp
${LIBDIR}/slic3r/AppController.hpp
${LIBDIR}/slic3r/AppController.cpp
${LIBDIR}/slic3r/AppControllerWx.cpp
- ${LIBDIR}/slic3r/Strings.hpp
)
add_library(admesh STATIC
@@ -367,7 +372,7 @@ set(MyTypemap ${CMAKE_CURRENT_BINARY_DIR}/typemap)
add_custom_command(
OUTPUT ${MyTypemap}
DEPENDS ${CMAKE_CURRENT_LIST_DIR}/xsp/my.map
- COMMAND ${PERL_EXECUTABLE} -MExtUtils::Typemaps -MExtUtils::Typemaps::Basic -e "$typemap = ExtUtils::Typemaps->new(file => \"${CMAKE_CURRENT_LIST_DIR}/xsp/my.map\"); $typemap->merge(typemap => ExtUtils::Typemaps::Basic->new); $typemap->write(file => \"${MyTypemap}\")"
+ COMMAND ${PERL5LIB_ENV_CMD} ${PERL_EXECUTABLE} -MExtUtils::Typemaps -MExtUtils::Typemaps::Basic -e "$typemap = ExtUtils::Typemaps->new(file => \"${CMAKE_CURRENT_LIST_DIR}/xsp/my.map\"); $typemap->merge(typemap => ExtUtils::Typemaps::Basic->new); $typemap->write(file => \"${MyTypemap}\")"
VERBATIM
)
@@ -411,7 +416,7 @@ set(XS_XSP_FILES
${XSP_DIR}/Surface.xsp
${XSP_DIR}/SurfaceCollection.xsp
${XSP_DIR}/TriangleMesh.xsp
- ${XSP_DIR}/Utils_OctoPrint.xsp
+ ${XSP_DIR}/Utils_PrintHost.xsp
${XSP_DIR}/Utils_PresetUpdater.xsp
${XSP_DIR}/AppController.xsp
${XSP_DIR}/XS.xsp
@@ -432,7 +437,8 @@ set(XS_MAIN_CPP ${CMAKE_CURRENT_BINARY_DIR}/XS.cpp)
add_custom_command(
OUTPUT ${XS_MAIN_CPP}
DEPENDS ${MyTypemap} ${XS_XSP_FILES} ${CMAKE_CURRENT_LIST_DIR}/xsp/typemap.xspt
- COMMAND COMMAND xsubpp -typemap typemap -output ${XS_MAIN_CPP} -hiertype ${XS_MAIN_XS}
+ COMMAND ${PERL5LIB_ENV_CMD} xsubpp -typemap typemap -output ${XS_MAIN_CPP} -hiertype ${XS_MAIN_XS}
+ VERBATIM
)
# Define the Perl XS shared library.
@@ -459,7 +465,7 @@ if(APPLE)
# Ignore undefined symbols of the perl interpreter, they will be found in the caller image.
target_link_libraries(XS "-undefined dynamic_lookup")
endif()
-target_link_libraries(XS libslic3r libslic3r_gui admesh miniz clipper nowide polypartition poly2tri semver avrdude)
+target_link_libraries(XS libslic3r libslic3r_gui admesh miniz clipper nowide polypartition poly2tri semver avrdude qhull)
if(SLIC3R_PROFILE)
target_link_libraries(XS Shiny)
endif()
@@ -496,6 +502,14 @@ if (WIN32)
target_compile_definitions(XS PRIVATE -DNOGDI -DNOMINMAX -DHAS_BOOL)
endif ()
+# SLIC3R_MSVC_PDB
+if (MSVC AND SLIC3R_MSVC_PDB AND ${CMAKE_BUILD_TYPE} STREQUAL "Release")
+ set_target_properties(XS PROPERTIES
+ COMPILE_FLAGS "/Zi"
+ LINK_FLAGS "/DEBUG /OPT:REF /OPT:ICF"
+ )
+endif()
+
## Configuration flags
if (SLIC3R_GUI)
message("Slic3r will be built with GUI support")
@@ -540,6 +554,10 @@ endif()
add_subdirectory(src/avrdude)
+add_subdirectory(src/qhull)
+include_directories(${LIBDIR}/qhull/src)
+message(STATUS ${LIBDIR}/qhull/src)
+
## REQUIRED packages
# Find and configure boost
@@ -729,6 +747,7 @@ add_custom_target(pot
set(LIBNEST2D_UNITTESTS ON CACHE BOOL "Force generating unittests for libnest2d")
add_subdirectory(${LIBDIR}/libnest2d)
+target_compile_definitions(libslic3r PUBLIC -DUSE_TBB)
target_include_directories(libslic3r PUBLIC BEFORE ${LIBNEST2D_INCLUDES})
target_include_directories(libslic3r_gui PUBLIC BEFORE ${LIBNEST2D_INCLUDES})
diff --git a/xs/src/admesh/stlinit.cpp b/xs/src/admesh/stlinit.cpp
index e572ce930..ed8beb9d1 100644
--- a/xs/src/admesh/stlinit.cpp
+++ b/xs/src/admesh/stlinit.cpp
@@ -287,7 +287,7 @@ stl_read(stl_file *stl, int first_facet, int first) {
{
// skip solid/endsolid
// (in this order, otherwise it won't work when they are paired in the middle of a file)
- fscanf(stl->fp, "endsolid\n");
+ fscanf(stl->fp, "endsolid%*[^\n]\n");
fscanf(stl->fp, "solid%*[^\n]\n"); // name might contain spaces so %*s doesn't work and it also can be empty (just "solid")
// Leading space in the fscanf format skips all leading white spaces including numerous new lines and tabs.
int res_normal = fscanf(stl->fp, " facet normal %31s %31s %31s", normal_buf[0], normal_buf[1], normal_buf[2]);
diff --git a/xs/src/avrdude/avrdude-slic3r.cpp b/xs/src/avrdude/avrdude-slic3r.cpp
index 0577fe6d0..3037f5284 100644
--- a/xs/src/avrdude/avrdude-slic3r.cpp
+++ b/xs/src/avrdude/avrdude-slic3r.cpp
@@ -2,6 +2,10 @@
#include <deque>
#include <thread>
+#include <cstring>
+#include <cstdlib>
+#include <new>
+#include <exception>
extern "C" {
#include "ac_cfg.h"
@@ -28,6 +32,11 @@ static void avrdude_progress_handler_closure(const char *task, unsigned progress
(*progress_fn)(task, progress);
}
+static void avrdude_oom_handler(const char *context, void *user_p)
+{
+ throw std::bad_alloc();
+}
+
// Private
@@ -47,16 +56,22 @@ struct AvrDude::priv
priv(std::string &&sys_config) : sys_config(sys_config) {}
+ void set_handlers();
+ void unset_handlers();
int run_one(const std::vector<std::string> &args);
int run();
-};
-int AvrDude::priv::run_one(const std::vector<std::string> &args) {
- std::vector<char*> c_args {{ const_cast<char*>(PACKAGE_NAME) }};
- for (const auto &arg : args) {
- c_args.push_back(const_cast<char*>(arg.data()));
- }
+ struct HandlerGuard
+ {
+ priv &p;
+
+ HandlerGuard(priv &p) : p(p) { p.set_handlers(); }
+ ~HandlerGuard() { p.unset_handlers(); }
+ };
+};
+void AvrDude::priv::set_handlers()
+{
if (message_fn) {
::avrdude_message_handler_set(avrdude_message_handler_closure, reinterpret_cast<void*>(&message_fn));
} else {
@@ -69,10 +84,27 @@ int AvrDude::priv::run_one(const std::vector<std::string> &args) {
::avrdude_progress_handler_set(nullptr, nullptr);
}
- const auto res = ::avrdude_main(static_cast<int>(c_args.size()), c_args.data(), sys_config.c_str());
+ ::avrdude_oom_handler_set(avrdude_oom_handler, nullptr);
+}
+void AvrDude::priv::unset_handlers()
+{
::avrdude_message_handler_set(nullptr, nullptr);
::avrdude_progress_handler_set(nullptr, nullptr);
+ ::avrdude_oom_handler_set(nullptr, nullptr);
+}
+
+
+int AvrDude::priv::run_one(const std::vector<std::string> &args) {
+ std::vector<char*> c_args {{ const_cast<char*>(PACKAGE_NAME) }};
+ for (const auto &arg : args) {
+ c_args.push_back(const_cast<char*>(arg.data()));
+ }
+
+ HandlerGuard guard(*this);
+
+ const auto res = ::avrdude_main(static_cast<int>(c_args.size()), c_args.data(), sys_config.c_str());
+
return res;
}
@@ -134,7 +166,7 @@ AvrDude& AvrDude::on_complete(CompleteFn fn)
int AvrDude::run_sync()
{
- return p->run();
+ return p ? p->run() : -1;
}
AvrDude::Ptr AvrDude::run()
@@ -143,19 +175,46 @@ AvrDude::Ptr AvrDude::run()
if (self->p) {
auto avrdude_thread = std::thread([self]() {
- bool cancel = false;
- int res = -1;
-
- if (self->p->run_fn) {
- self->p->run_fn();
- }
-
- if (! self->p->cancelled) {
- self->p->exit_code = self->p->run();
- }
-
- if (self->p->complete_fn) {
- self->p->complete_fn();
+ try {
+ if (self->p->run_fn) {
+ self->p->run_fn(self);
+ }
+
+ if (! self->p->cancelled) {
+ self->p->exit_code = self->p->run();
+ }
+
+ if (self->p->complete_fn) {
+ self->p->complete_fn();
+ }
+ } catch (const std::exception &ex) {
+ self->p->exit_code = EXIT_EXCEPTION;
+
+ static const char *msg = "An exception was thrown in the background thread:\n";
+
+ const char *what = ex.what();
+ auto &message_fn = self->p->message_fn;
+ if (message_fn) {
+ message_fn(msg, sizeof(msg));
+ message_fn(what, std::strlen(what));
+ message_fn("\n", 1);
+ }
+
+ if (self->p->complete_fn) {
+ self->p->complete_fn();
+ }
+ } catch (...) {
+ self->p->exit_code = EXIT_EXCEPTION;
+
+ static const char *msg = "An unkown exception was thrown in the background thread.\n";
+
+ if (self->p->message_fn) {
+ self->p->message_fn(msg, sizeof(msg));
+ }
+
+ if (self->p->complete_fn) {
+ self->p->complete_fn();
+ }
}
});
diff --git a/xs/src/avrdude/avrdude-slic3r.hpp b/xs/src/avrdude/avrdude-slic3r.hpp
index 86e097034..754e1e345 100644
--- a/xs/src/avrdude/avrdude-slic3r.hpp
+++ b/xs/src/avrdude/avrdude-slic3r.hpp
@@ -11,8 +11,13 @@ namespace Slic3r {
class AvrDude
{
public:
+ enum {
+ EXIT_SUCCEESS = 0,
+ EXIT_EXCEPTION = -1000,
+ };
+
typedef std::shared_ptr<AvrDude> Ptr;
- typedef std::function<void()> RunFn;
+ typedef std::function<void(Ptr /* avrdude */)> RunFn;
typedef std::function<void(const char * /* msg */, unsigned /* size */)> MessageFn;
typedef std::function<void(const char * /* task */, unsigned /* progress */)> ProgressFn;
typedef std::function<void()> CompleteFn;
@@ -49,10 +54,18 @@ public:
// This has no effect when using run_sync().
AvrDude& on_complete(CompleteFn fn);
+ // Perform AvrDude invocation(s) synchronously on the current thread
int run_sync();
+
+ // Perform AvrDude invocation(s) on a background thread.
+ // Current instance is moved into a shared_ptr which is returned (and also passed in on_run, if any).
Ptr run();
+ // Cancel current operation
void cancel();
+
+ // If there is a background thread and it is joinable, join() it,
+ // that is, wait for it to finish.
void join();
bool cancelled(); // Whether avrdude run was cancelled
diff --git a/xs/src/avrdude/avrdude.h b/xs/src/avrdude/avrdude.h
index 9f724433f..f4c92a75d 100644
--- a/xs/src/avrdude/avrdude.h
+++ b/xs/src/avrdude/avrdude.h
@@ -39,6 +39,12 @@ typedef void (*avrdude_progress_handler_t)(const char *task, unsigned progress,
void avrdude_progress_handler_set(avrdude_progress_handler_t newhandler, void *user_p);
void avrdude_progress_external(const char *task, unsigned progress);
+// OOM handler
+typedef void (*avrdude_oom_handler_t)(const char *context, void *user_p);
+void avrdude_oom_handler_set(avrdude_oom_handler_t newhandler, void *user_p);
+void avrdude_oom(const char *context);
+
+
// Cancellation
void avrdude_cancel();
diff --git a/xs/src/avrdude/avrpart.c b/xs/src/avrdude/avrpart.c
index b04851ac1..d0bb951ee 100644
--- a/xs/src/avrdude/avrpart.c
+++ b/xs/src/avrdude/avrpart.c
@@ -36,8 +36,9 @@ OPCODE * avr_new_opcode(void)
m = (OPCODE *)malloc(sizeof(*m));
if (m == NULL) {
- avrdude_message(MSG_INFO, "avr_new_opcode(): out of memory\n");
- exit(1);
+ // avrdude_message(MSG_INFO, "avr_new_opcode(): out of memory\n");
+ // exit(1);
+ avrdude_oom("avr_new_opcode(): out of memory\n");
}
memset(m, 0, sizeof(*m));
@@ -56,8 +57,9 @@ static OPCODE * avr_dup_opcode(OPCODE * op)
m = (OPCODE *)malloc(sizeof(*m));
if (m == NULL) {
- avrdude_message(MSG_INFO, "avr_dup_opcode(): out of memory\n");
- exit(1);
+ // avrdude_message(MSG_INFO, "avr_dup_opcode(): out of memory\n");
+ // exit(1);
+ avrdude_oom("avr_dup_opcode(): out of memory\n");
}
memcpy(m, op, sizeof(*m));
@@ -249,8 +251,9 @@ AVRMEM * avr_new_memtype(void)
m = (AVRMEM *)malloc(sizeof(*m));
if (m == NULL) {
- avrdude_message(MSG_INFO, "avr_new_memtype(): out of memory\n");
- exit(1);
+ // avrdude_message(MSG_INFO, "avr_new_memtype(): out of memory\n");
+ // exit(1);
+ avrdude_oom("avr_new_memtype(): out of memory\n");
}
memset(m, 0, sizeof(*m));
@@ -300,9 +303,10 @@ AVRMEM * avr_dup_mem(AVRMEM * m)
if (m->buf != NULL) {
n->buf = (unsigned char *)malloc(n->size);
if (n->buf == NULL) {
- avrdude_message(MSG_INFO, "avr_dup_mem(): out of memory (memsize=%d)\n",
- n->size);
- exit(1);
+ // avrdude_message(MSG_INFO, "avr_dup_mem(): out of memory (memsize=%d)\n",
+ // n->size);
+ // exit(1);
+ avrdude_oom("avr_dup_mem(): out of memory");
}
memcpy(n->buf, m->buf, n->size);
}
@@ -310,9 +314,10 @@ AVRMEM * avr_dup_mem(AVRMEM * m)
if (m->tags != NULL) {
n->tags = (unsigned char *)malloc(n->size);
if (n->tags == NULL) {
- avrdude_message(MSG_INFO, "avr_dup_mem(): out of memory (memsize=%d)\n",
- n->size);
- exit(1);
+ // avrdude_message(MSG_INFO, "avr_dup_mem(): out of memory (memsize=%d)\n",
+ // n->size);
+ // exit(1);
+ avrdude_oom("avr_dup_mem(): out of memory");
}
memcpy(n->tags, m->tags, n->size);
}
@@ -441,8 +446,9 @@ AVRPART * avr_new_part(void)
p = (AVRPART *)malloc(sizeof(AVRPART));
if (p == NULL) {
- avrdude_message(MSG_INFO, "new_part(): out of memory\n");
- exit(1);
+ // avrdude_message(MSG_INFO, "new_part(): out of memory\n");
+ // exit(1);
+ avrdude_oom("new_part(): out of memory\n");
}
memset(p, 0, sizeof(*p));
diff --git a/xs/src/avrdude/buspirate.c b/xs/src/avrdude/buspirate.c
index 435c4ce53..5875d4283 100644
--- a/xs/src/avrdude/buspirate.c
+++ b/xs/src/avrdude/buspirate.c
@@ -1135,9 +1135,10 @@ static void buspirate_setup(struct programmer_t *pgm)
{
/* Allocate private data */
if ((pgm->cookie = calloc(1, sizeof(struct pdata))) == 0) {
- avrdude_message(MSG_INFO, "%s: buspirate_initpgm(): Out of memory allocating private data\n",
- progname);
- exit(1);
+ // avrdude_message(MSG_INFO, "%s: buspirate_initpgm(): Out of memory allocating private data\n",
+ // progname);
+ // exit(1);
+ avrdude_oom("buspirate_initpgm(): Out of memory allocating private data\n");
}
PDATA(pgm)->serial_recv_timeout = 100;
}
diff --git a/xs/src/avrdude/butterfly.c b/xs/src/avrdude/butterfly.c
index de9a3175f..beb5e04de 100644
--- a/xs/src/avrdude/butterfly.c
+++ b/xs/src/avrdude/butterfly.c
@@ -63,9 +63,10 @@ struct pdata
static void butterfly_setup(PROGRAMMER * pgm)
{
if ((pgm->cookie = malloc(sizeof(struct pdata))) == 0) {
- avrdude_message(MSG_INFO, "%s: butterfly_setup(): Out of memory allocating private data\n",
- progname);
- exit(1);
+ // avrdude_message(MSG_INFO, "%s: butterfly_setup(): Out of memory allocating private data\n",
+ // progname);
+ // exit(1);
+ avrdude_oom("butterfly_setup(): Out of memory allocating private data\n");
}
memset(pgm->cookie, 0, sizeof(struct pdata));
}
diff --git a/xs/src/avrdude/lexer.c b/xs/src/avrdude/lexer.c
index 93249a9ab..f2d8adb4b 100644
--- a/xs/src/avrdude/lexer.c
+++ b/xs/src/avrdude/lexer.c
@@ -2834,7 +2834,8 @@ YY_BUFFER_STATE yy_scan_bytes (const char * yybytes, int _yybytes_len )
n = (yy_size_t) (_yybytes_len + 2);
buf = (char *) yyalloc( n );
if ( ! buf )
- YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" );
+ // YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" );
+ avrdude_oom("out of dynamic memory in yy_scan_bytes()");
for ( i = 0; i < _yybytes_len; ++i )
buf[i] = yybytes[i];
@@ -2859,8 +2860,9 @@ YY_BUFFER_STATE yy_scan_bytes (const char * yybytes, int _yybytes_len )
static void yynoreturn yy_fatal_error (const char* msg )
{
- fprintf( stderr, "%s\n", msg );
- exit( YY_EXIT_FAILURE );
+ fprintf( stderr, "%s\n", msg );
+ // exit( YY_EXIT_FAILURE );
+ abort();
}
/* Redefine yyless() so it works in section 3 code. */
diff --git a/xs/src/avrdude/main.c b/xs/src/avrdude/main.c
index 5d73403b0..ebda0ba19 100644
--- a/xs/src/avrdude/main.c
+++ b/xs/src/avrdude/main.c
@@ -144,6 +144,33 @@ void avrdude_progress_external(const char *task, unsigned progress)
avrdude_progress_handler(task, progress, avrdude_progress_handler_user_p);
}
+static void avrdude_oom_handler_null(const char *context, void *user_p)
+{
+ // Output a message and just exit
+ fputs("avrdude: Out of memory: ", stderr);
+ fputs(context, stderr);
+ exit(99);
+}
+
+static void *avrdude_oom_handler_user_p = NULL;
+static avrdude_oom_handler_t avrdude_oom_handler = avrdude_oom_handler_null;
+
+void avrdude_oom_handler_set(avrdude_oom_handler_t newhandler, void *user_p)
+{
+ if (newhandler != NULL) {
+ avrdude_oom_handler = newhandler;
+ avrdude_oom_handler_user_p = user_p;
+ } else {
+ avrdude_oom_handler = avrdude_oom_handler_null;
+ avrdude_oom_handler_user_p = NULL;
+ }
+}
+
+void avrdude_oom(const char *context)
+{
+ avrdude_oom_handler(context, avrdude_oom_handler_user_p);
+}
+
void avrdude_cancel()
{
cancel_flag = true;
diff --git a/xs/src/avrdude/pgm.c b/xs/src/avrdude/pgm.c
index 851ac5a87..b8a93f104 100644
--- a/xs/src/avrdude/pgm.c
+++ b/xs/src/avrdude/pgm.c
@@ -172,9 +172,10 @@ PROGRAMMER * pgm_dup(const PROGRAMMER * const src)
for (ln = lfirst(src->usbpid); ln; ln = lnext(ln)) {
int *ip = malloc(sizeof(int));
if (ip == NULL) {
- avrdude_message(MSG_INFO, "%s: out of memory allocating programmer structure\n",
- progname);
- exit(1);
+ // avrdude_message(MSG_INFO, "%s: out of memory allocating programmer structure\n",
+ // progname);
+ // exit(1);
+ avrdude_oom("out of memory allocating programmer structure\n");
}
*ip = *(int *) ldata(ln);
ladd(pgm->usbpid, ip);
diff --git a/xs/src/avrdude/ser_win32.c b/xs/src/avrdude/ser_win32.c
index 3a05cfa90..4e1713128 100644
--- a/xs/src/avrdude/ser_win32.c
+++ b/xs/src/avrdude/ser_win32.c
@@ -246,10 +246,11 @@ static int ser_open(char * port, union pinfo pinfo, union filedescriptor *fdp)
newname = malloc(strlen("\\\\.\\") + strlen(port) + 1);
if (newname == 0) {
- avrdude_message(MSG_INFO, "%s: ser_open(): out of memory\n",
- progname);
- exit(1);
- }
+ // avrdude_message(MSG_INFO, "%s: ser_open(): out of memory\n",
+ // progname);
+ // exit(1);
+ avrdude_oom("ser_open(): out of memory\n");
+ }
strcpy(newname, "\\\\.\\");
strcat(newname, port);
diff --git a/xs/src/avrdude/stk500v2.c b/xs/src/avrdude/stk500v2.c
index 4d62640c0..691152b46 100644
--- a/xs/src/avrdude/stk500v2.c
+++ b/xs/src/avrdude/stk500v2.c
@@ -295,9 +295,10 @@ static int stk600_xprog_program_enable(PROGRAMMER * pgm, AVRPART * p);
void stk500v2_setup(PROGRAMMER * pgm)
{
if ((pgm->cookie = malloc(sizeof(struct pdata))) == 0) {
- avrdude_message(MSG_INFO, "%s: stk500v2_setup(): Out of memory allocating private data\n",
- progname);
- exit(1);
+ // avrdude_message(MSG_INFO, "%s: stk500v2_setup(): Out of memory allocating private data\n",
+ // progname);
+ // exit(1);
+ avrdude_oom("stk500v2_setup(): Out of memory allocating private data\n");
}
memset(pgm->cookie, 0, sizeof(struct pdata));
PDATA(pgm)->command_sequence = 1;
diff --git a/xs/src/avrdude/update.c b/xs/src/avrdude/update.c
index 417cbf71d..a255ab4f9 100644
--- a/xs/src/avrdude/update.c
+++ b/xs/src/avrdude/update.c
@@ -38,8 +38,9 @@ UPDATE * parse_op(char * s)
upd = (UPDATE *)malloc(sizeof(UPDATE));
if (upd == NULL) {
- avrdude_message(MSG_INFO, "%s: out of memory\n", progname);
- exit(1);
+ // avrdude_message(MSG_INFO, "%s: out of memory\n", progname);
+ // exit(1);
+ avrdude_oom("parse_op: out of memory\n");
}
i = 0;
@@ -53,8 +54,9 @@ UPDATE * parse_op(char * s)
upd->op = DEVICE_WRITE;
upd->filename = (char *)malloc(strlen(buf) + 1);
if (upd->filename == NULL) {
- avrdude_message(MSG_INFO, "%s: out of memory\n", progname);
- exit(1);
+ // avrdude_message(MSG_INFO, "%s: out of memory\n", progname);
+ // exit(1);
+ avrdude_oom("parse_op: out of memory\n");
}
strcpy(upd->filename, buf);
upd->format = FMT_AUTO;
@@ -63,8 +65,9 @@ UPDATE * parse_op(char * s)
upd->memtype = (char *)malloc(strlen(buf)+1);
if (upd->memtype == NULL) {
- avrdude_message(MSG_INFO, "%s: out of memory\n", progname);
- exit(1);
+ // avrdude_message(MSG_INFO, "%s: out of memory\n", progname);
+ // exit(1);
+ avrdude_oom("parse_op: out of memory\n");
}
strcpy(upd->memtype, buf);
@@ -179,8 +182,9 @@ UPDATE * dup_update(UPDATE * upd)
u = (UPDATE *)malloc(sizeof(UPDATE));
if (u == NULL) {
- avrdude_message(MSG_INFO, "%s: out of memory\n", progname);
- exit(1);
+ // avrdude_message(MSG_INFO, "%s: out of memory\n", progname);
+ // exit(1);
+ avrdude_oom("dup_update: out of memory\n");
}
memcpy(u, upd, sizeof(UPDATE));
@@ -200,8 +204,9 @@ UPDATE * new_update(int op, char * memtype, int filefmt, char * filename, unsign
u = (UPDATE *)malloc(sizeof(UPDATE));
if (u == NULL) {
- avrdude_message(MSG_INFO, "%s: out of memory\n", progname);
- exit(1);
+ // avrdude_message(MSG_INFO, "%s: out of memory\n", progname);
+ // exit(1);
+ avrdude_oom("new_update: out of memory\n");
}
u->memtype = strdup(memtype);
diff --git a/xs/src/avrdude/wiring.c b/xs/src/avrdude/wiring.c
index 395459762..562a3f17c 100644
--- a/xs/src/avrdude/wiring.c
+++ b/xs/src/avrdude/wiring.c
@@ -85,9 +85,10 @@ static void wiring_setup(PROGRAMMER * pgm)
* Now prepare our data
*/
if ((mycookie = malloc(sizeof(struct wiringpdata))) == 0) {
- avrdude_message(MSG_INFO, "%s: wiring_setup(): Out of memory allocating private data\n",
- progname);
- exit(1);
+ // avrdude_message(MSG_INFO, "%s: wiring_setup(): Out of memory allocating private data\n",
+ // progname);
+ // exit(1);
+ avrdude_oom("wiring_setup(): Out of memory allocating private data\n");
}
memset(mycookie, 0, sizeof(struct wiringpdata));
WIRINGPDATA(mycookie)->snoozetime = 0;
diff --git a/xs/src/libnest2d/CMakeLists.txt b/xs/src/libnest2d/CMakeLists.txt
index 835e8311d..f81355012 100644
--- a/xs/src/libnest2d/CMakeLists.txt
+++ b/xs/src/libnest2d/CMakeLists.txt
@@ -31,6 +31,7 @@ set(LIBNEST2D_SRCFILES
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/common.hpp
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/optimizer.hpp
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/metaloop.hpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/rotfinder.hpp
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/placers/placer_boilerplate.hpp
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/placers/bottomleftplacer.hpp
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/placers/nfpplacer.hpp
@@ -89,14 +90,39 @@ if(LIBNEST2D_UNITTESTS)
endif()
if(LIBNEST2D_BUILD_EXAMPLES)
+
add_executable(example examples/main.cpp
# tools/libnfpglue.hpp
# tools/libnfpglue.cpp
+ tools/nfp_svgnest.hpp
+ tools/nfp_svgnest_glue.hpp
tools/svgtools.hpp
tests/printer_parts.cpp
tests/printer_parts.h
- ${LIBNEST2D_SRCFILES})
-
+ ${LIBNEST2D_SRCFILES}
+ )
+ set(TBB_STATIC ON)
+ find_package(TBB QUIET)
+ if(TBB_FOUND)
+ message(STATUS "Parallelization with Intel TBB")
+ target_include_directories(example PUBLIC ${TBB_INCLUDE_DIRS})
+ target_compile_definitions(example PUBLIC ${TBB_DEFINITIONS} -DUSE_TBB)
+ if(MSVC)
+ # Suppress implicit linking of the TBB libraries by the Visual Studio compiler.
+ target_compile_definitions(example PUBLIC -D__TBB_NO_IMPLICIT_LINKAGE)
+ endif()
+ # The Intel TBB library will use the std::exception_ptr feature of C++11.
+ target_compile_definitions(example PUBLIC -DTBB_USE_CAPTURED_EXCEPTION=1)
+
+ target_link_libraries(example ${TBB_LIBRARIES})
+ else()
+ find_package(OpenMP QUIET)
+ if(OpenMP_CXX_FOUND)
+ message(STATUS "Parallelization with OpenMP")
+ target_include_directories(example PUBLIC OpenMP::OpenMP_CXX)
+ target_link_libraries(example OpenMP::OpenMP_CXX)
+ endif()
+ endif()
target_link_libraries(example ${LIBNEST2D_LIBRARIES})
target_include_directories(example PUBLIC ${LIBNEST2D_HEADERS})
diff --git a/xs/src/libnest2d/README.md b/xs/src/libnest2d/README.md
index 3508801a8..61a7ac7d0 100644
--- a/xs/src/libnest2d/README.md
+++ b/xs/src/libnest2d/README.md
@@ -9,18 +9,28 @@ with templated geometry types. These geometries can have custom or already
existing implementation to avoid copying or having unnecessary dependencies.
A default backend is provided if the user of the library just wants to use it
-out of the box without additional integration. The default backend is reasonably
+out of the box without additional integration. This backend is reasonably
fast and robust, being built on top of boost geometry and the
[polyclipping](http://www.angusj.com/delphi/clipper.php) library. Usage of
-this default backend implies the dependency on these packages as well as the
-compilation of the backend itself (The default backend is not yet header only).
+this default backend implies the dependency on these packages but its header
+only as well.
This software is currently under construction and lacks a throughout
documentation and some essential algorithms as well. At this stage it works well
for rectangles and convex closed polygons without considering holes and
concavities.
-Holes and non-convex polygons will be usable in the near future as well.
+Holes and non-convex polygons will be usable in the near future as well. The
+no fit polygon based placer module combined with the first fit selection
+strategy is now used in the [Slic3r](https://github.com/prusa3d/Slic3r)
+application's arrangement feature. It uses local optimization techniques to find
+the best placement of each new item based on some features of the arrangement.
+
+In the near future I would like to use machine learning to evaluate the
+placements and (or) the order if items in which they are placed and see what
+results can be obtained. This is a different approach than that of SVGnest which
+uses genetic algorithms to find better and better selection orders. Maybe the
+two approaches can be combined as well.
# References
- [SVGNest](https://github.com/Jack000/SVGnest)
diff --git a/xs/src/libnest2d/cmake_modules/FindTBB.cmake b/xs/src/libnest2d/cmake_modules/FindTBB.cmake
new file mode 100644
index 000000000..8b498d3ab
--- /dev/null
+++ b/xs/src/libnest2d/cmake_modules/FindTBB.cmake
@@ -0,0 +1,322 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2015 Justus Calvin
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+#
+# FindTBB
+# -------
+#
+# Find TBB include directories and libraries.
+#
+# Usage:
+#
+# find_package(TBB [major[.minor]] [EXACT]
+# [QUIET] [REQUIRED]
+# [[COMPONENTS] [components...]]
+# [OPTIONAL_COMPONENTS components...])
+#
+# where the allowed components are tbbmalloc and tbb_preview. Users may modify
+# the behavior of this module with the following variables:
+#
+# * TBB_ROOT_DIR - The base directory the of TBB installation.
+# * TBB_INCLUDE_DIR - The directory that contains the TBB headers files.
+# * TBB_LIBRARY - The directory that contains the TBB library files.
+# * TBB_<library>_LIBRARY - The path of the TBB the corresponding TBB library.
+# These libraries, if specified, override the
+# corresponding library search results, where <library>
+# may be tbb, tbb_debug, tbbmalloc, tbbmalloc_debug,
+# tbb_preview, or tbb_preview_debug.
+# * TBB_USE_DEBUG_BUILD - The debug version of tbb libraries, if present, will
+# be used instead of the release version.
+# * TBB_STATIC - Static linking of libraries with a _static suffix.
+# For example, on Windows a tbb_static.lib will be searched for
+# instead of tbb.lib.
+#
+# Users may modify the behavior of this module with the following environment
+# variables:
+#
+# * TBB_INSTALL_DIR
+# * TBBROOT
+# * LIBRARY_PATH
+#
+# This module will set the following variables:
+#
+# * TBB_FOUND - Set to false, or undefined, if we haven’t found, or
+# don’t want to use TBB.
+# * TBB_<component>_FOUND - If False, optional <component> part of TBB sytem is
+# not available.
+# * TBB_VERSION - The full version string
+# * TBB_VERSION_MAJOR - The major version
+# * TBB_VERSION_MINOR - The minor version
+# * TBB_INTERFACE_VERSION - The interface version number defined in
+# tbb/tbb_stddef.h.
+# * TBB_<library>_LIBRARY_RELEASE - The path of the TBB release version of
+# <library>, where <library> may be tbb, tbb_debug,
+# tbbmalloc, tbbmalloc_debug, tbb_preview, or
+# tbb_preview_debug.
+# * TBB_<library>_LIBRARY_DEGUG - The path of the TBB release version of
+# <library>, where <library> may be tbb, tbb_debug,
+# tbbmalloc, tbbmalloc_debug, tbb_preview, or
+# tbb_preview_debug.
+#
+# The following varibles should be used to build and link with TBB:
+#
+# * TBB_INCLUDE_DIRS - The include directory for TBB.
+# * TBB_LIBRARIES - The libraries to link against to use TBB.
+# * TBB_LIBRARIES_RELEASE - The release libraries to link against to use TBB.
+# * TBB_LIBRARIES_DEBUG - The debug libraries to link against to use TBB.
+# * TBB_DEFINITIONS - Definitions to use when compiling code that uses
+# TBB.
+# * TBB_DEFINITIONS_RELEASE - Definitions to use when compiling release code that
+# uses TBB.
+# * TBB_DEFINITIONS_DEBUG - Definitions to use when compiling debug code that
+# uses TBB.
+#
+# This module will also create the "tbb" target that may be used when building
+# executables and libraries.
+
+include(FindPackageHandleStandardArgs)
+
+if(NOT TBB_FOUND)
+
+ ##################################
+ # Check the build type
+ ##################################
+
+ if(NOT DEFINED TBB_USE_DEBUG_BUILD)
+ if(CMAKE_BUILD_TYPE MATCHES "(Debug|DEBUG|debug)")
+ set(TBB_BUILD_TYPE DEBUG)
+ else()
+ set(TBB_BUILD_TYPE RELEASE)
+ endif()
+ elseif(TBB_USE_DEBUG_BUILD)
+ set(TBB_BUILD_TYPE DEBUG)
+ else()
+ set(TBB_BUILD_TYPE RELEASE)
+ endif()
+
+ ##################################
+ # Set the TBB search directories
+ ##################################
+
+ # Define search paths based on user input and environment variables
+ set(TBB_SEARCH_DIR ${TBB_ROOT_DIR} $ENV{TBB_INSTALL_DIR} $ENV{TBBROOT})
+
+ # Define the search directories based on the current platform
+ if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
+ set(TBB_DEFAULT_SEARCH_DIR "C:/Program Files/Intel/TBB"
+ "C:/Program Files (x86)/Intel/TBB")
+
+ # Set the target architecture
+ if(CMAKE_SIZEOF_VOID_P EQUAL 8)
+ set(TBB_ARCHITECTURE "intel64")
+ else()
+ set(TBB_ARCHITECTURE "ia32")
+ endif()
+
+ # Set the TBB search library path search suffix based on the version of VC
+ if(WINDOWS_STORE)
+ set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc11_ui")
+ elseif(MSVC14)
+ set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc14")
+ elseif(MSVC12)
+ set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc12")
+ elseif(MSVC11)
+ set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc11")
+ elseif(MSVC10)
+ set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc10")
+ endif()
+
+ # Add the library path search suffix for the VC independent version of TBB
+ list(APPEND TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc_mt")
+
+ elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
+ # OS X
+ set(TBB_DEFAULT_SEARCH_DIR "/opt/intel/tbb")
+
+ # TODO: Check to see which C++ library is being used by the compiler.
+ if(NOT ${CMAKE_SYSTEM_VERSION} VERSION_LESS 13.0)
+ # The default C++ library on OS X 10.9 and later is libc++
+ set(TBB_LIB_PATH_SUFFIX "lib/libc++" "lib")
+ else()
+ set(TBB_LIB_PATH_SUFFIX "lib")
+ endif()
+ elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
+ # Linux
+ set(TBB_DEFAULT_SEARCH_DIR "/opt/intel/tbb")
+
+ # TODO: Check compiler version to see the suffix should be <arch>/gcc4.1 or
+ # <arch>/gcc4.1. For now, assume that the compiler is more recent than
+ # gcc 4.4.x or later.
+ if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
+ set(TBB_LIB_PATH_SUFFIX "lib/intel64/gcc4.4")
+ elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^i.86$")
+ set(TBB_LIB_PATH_SUFFIX "lib/ia32/gcc4.4")
+ endif()
+ endif()
+
+ ##################################
+ # Find the TBB include dir
+ ##################################
+
+ find_path(TBB_INCLUDE_DIRS tbb/tbb.h
+ HINTS ${TBB_INCLUDE_DIR} ${TBB_SEARCH_DIR}
+ PATHS ${TBB_DEFAULT_SEARCH_DIR}
+ PATH_SUFFIXES include)
+
+ ##################################
+ # Set version strings
+ ##################################
+
+ if(TBB_INCLUDE_DIRS)
+ file(READ "${TBB_INCLUDE_DIRS}/tbb/tbb_stddef.h" _tbb_version_file)
+ string(REGEX REPLACE ".*#define TBB_VERSION_MAJOR ([0-9]+).*" "\\1"
+ TBB_VERSION_MAJOR "${_tbb_version_file}")
+ string(REGEX REPLACE ".*#define TBB_VERSION_MINOR ([0-9]+).*" "\\1"
+ TBB_VERSION_MINOR "${_tbb_version_file}")
+ string(REGEX REPLACE ".*#define TBB_INTERFACE_VERSION ([0-9]+).*" "\\1"
+ TBB_INTERFACE_VERSION "${_tbb_version_file}")
+ set(TBB_VERSION "${TBB_VERSION_MAJOR}.${TBB_VERSION_MINOR}")
+ endif()
+
+ ##################################
+ # Find TBB components
+ ##################################
+
+ if(TBB_VERSION VERSION_LESS 4.3)
+ set(TBB_SEARCH_COMPOMPONENTS tbb_preview tbbmalloc tbb)
+ else()
+ set(TBB_SEARCH_COMPOMPONENTS tbb_preview tbbmalloc_proxy tbbmalloc tbb)
+ endif()
+
+ if(TBB_STATIC)
+ set(TBB_STATIC_SUFFIX "_static")
+ endif()
+
+ # Find each component
+ foreach(_comp ${TBB_SEARCH_COMPOMPONENTS})
+ if(";${TBB_FIND_COMPONENTS};tbb;" MATCHES ";${_comp};")
+
+ # Search for the libraries
+ find_library(TBB_${_comp}_LIBRARY_RELEASE ${_comp}${TBB_STATIC_SUFFIX}
+ HINTS ${TBB_LIBRARY} ${TBB_SEARCH_DIR}
+ PATHS ${TBB_DEFAULT_SEARCH_DIR} ENV LIBRARY_PATH
+ PATH_SUFFIXES ${TBB_LIB_PATH_SUFFIX})
+
+ find_library(TBB_${_comp}_LIBRARY_DEBUG ${_comp}${TBB_STATIC_SUFFIX}_debug
+ HINTS ${TBB_LIBRARY} ${TBB_SEARCH_DIR}
+ PATHS ${TBB_DEFAULT_SEARCH_DIR} ENV LIBRARY_PATH
+ PATH_SUFFIXES ${TBB_LIB_PATH_SUFFIX})
+
+ if(TBB_${_comp}_LIBRARY_DEBUG)
+ list(APPEND TBB_LIBRARIES_DEBUG "${TBB_${_comp}_LIBRARY_DEBUG}")
+ endif()
+ if(TBB_${_comp}_LIBRARY_RELEASE)
+ list(APPEND TBB_LIBRARIES_RELEASE "${TBB_${_comp}_LIBRARY_RELEASE}")
+ endif()
+ if(TBB_${_comp}_LIBRARY_${TBB_BUILD_TYPE} AND NOT TBB_${_comp}_LIBRARY)
+ set(TBB_${_comp}_LIBRARY "${TBB_${_comp}_LIBRARY_${TBB_BUILD_TYPE}}")
+ endif()
+
+ if(TBB_${_comp}_LIBRARY AND EXISTS "${TBB_${_comp}_LIBRARY}")
+ set(TBB_${_comp}_FOUND TRUE)
+ else()
+ set(TBB_${_comp}_FOUND FALSE)
+ endif()
+
+ # Mark internal variables as advanced
+ mark_as_advanced(TBB_${_comp}_LIBRARY_RELEASE)
+ mark_as_advanced(TBB_${_comp}_LIBRARY_DEBUG)
+ mark_as_advanced(TBB_${_comp}_LIBRARY)
+
+ endif()
+ endforeach()
+
+ unset(TBB_STATIC_SUFFIX)
+
+ ##################################
+ # Set compile flags and libraries
+ ##################################
+
+ set(TBB_DEFINITIONS_RELEASE "")
+ set(TBB_DEFINITIONS_DEBUG "-DTBB_USE_DEBUG=1")
+
+ if(TBB_LIBRARIES_${TBB_BUILD_TYPE})
+ set(TBB_DEFINITIONS "${TBB_DEFINITIONS_${TBB_BUILD_TYPE}}")
+ set(TBB_LIBRARIES "${TBB_LIBRARIES_${TBB_BUILD_TYPE}}")
+ elseif(TBB_LIBRARIES_RELEASE)
+ set(TBB_DEFINITIONS "${TBB_DEFINITIONS_RELEASE}")
+ set(TBB_LIBRARIES "${TBB_LIBRARIES_RELEASE}")
+ elseif(TBB_LIBRARIES_DEBUG)
+ set(TBB_DEFINITIONS "${TBB_DEFINITIONS_DEBUG}")
+ set(TBB_LIBRARIES "${TBB_LIBRARIES_DEBUG}")
+ endif()
+
+ find_package_handle_standard_args(TBB
+ REQUIRED_VARS TBB_INCLUDE_DIRS TBB_LIBRARIES
+ HANDLE_COMPONENTS
+ VERSION_VAR TBB_VERSION)
+
+ ##################################
+ # Create targets
+ ##################################
+
+ if(NOT CMAKE_VERSION VERSION_LESS 3.0 AND TBB_FOUND)
+ add_library(tbb SHARED IMPORTED)
+ set_target_properties(tbb PROPERTIES
+ INTERFACE_INCLUDE_DIRECTORIES ${TBB_INCLUDE_DIRS}
+ IMPORTED_LOCATION ${TBB_LIBRARIES})
+ if(TBB_LIBRARIES_RELEASE AND TBB_LIBRARIES_DEBUG)
+ set_target_properties(tbb PROPERTIES
+ INTERFACE_COMPILE_DEFINITIONS "$<$<OR:$<CONFIG:Debug>,$<CONFIG:RelWithDebInfo>>:TBB_USE_DEBUG=1>"
+ IMPORTED_LOCATION_DEBUG ${TBB_LIBRARIES_DEBUG}
+ IMPORTED_LOCATION_RELWITHDEBINFO ${TBB_LIBRARIES_DEBUG}
+ IMPORTED_LOCATION_RELEASE ${TBB_LIBRARIES_RELEASE}
+ IMPORTED_LOCATION_MINSIZEREL ${TBB_LIBRARIES_RELEASE}
+ )
+ elseif(TBB_LIBRARIES_RELEASE)
+ set_target_properties(tbb PROPERTIES IMPORTED_LOCATION ${TBB_LIBRARIES_RELEASE})
+ else()
+ set_target_properties(tbb PROPERTIES
+ INTERFACE_COMPILE_DEFINITIONS "${TBB_DEFINITIONS_DEBUG}"
+ IMPORTED_LOCATION ${TBB_LIBRARIES_DEBUG}
+ )
+ endif()
+ endif()
+
+ mark_as_advanced(TBB_INCLUDE_DIRS TBB_LIBRARIES)
+
+ unset(TBB_ARCHITECTURE)
+ unset(TBB_BUILD_TYPE)
+ unset(TBB_LIB_PATH_SUFFIX)
+ unset(TBB_DEFAULT_SEARCH_DIR)
+
+ if(TBB_DEBUG)
+ message(STATUS " TBB_INCLUDE_DIRS = ${TBB_INCLUDE_DIRS}")
+ message(STATUS " TBB_DEFINITIONS = ${TBB_DEFINITIONS}")
+ message(STATUS " TBB_LIBRARIES = ${TBB_LIBRARIES}")
+ message(STATUS " TBB_DEFINITIONS_DEBUG = ${TBB_DEFINITIONS_DEBUG}")
+ message(STATUS " TBB_LIBRARIES_DEBUG = ${TBB_LIBRARIES_DEBUG}")
+ message(STATUS " TBB_DEFINITIONS_RELEASE = ${TBB_DEFINITIONS_RELEASE}")
+ message(STATUS " TBB_LIBRARIES_RELEASE = ${TBB_LIBRARIES_RELEASE}")
+ endif()
+
+endif()
diff --git a/xs/src/libnest2d/examples/main.cpp b/xs/src/libnest2d/examples/main.cpp
index d6b2ccc34..ebc3fb15c 100644
--- a/xs/src/libnest2d/examples/main.cpp
+++ b/xs/src/libnest2d/examples/main.cpp
@@ -1,7 +1,6 @@
#include <iostream>
#include <string>
#include <fstream>
-
//#define DEBUG_EXPORT_NFP
#include <libnest2d.h>
@@ -9,7 +8,11 @@
#include "tests/printer_parts.h"
#include "tools/benchmark.h"
#include "tools/svgtools.hpp"
+#include "libnest2d/rotfinder.hpp"
+
//#include "tools/libnfpglue.hpp"
+//#include "tools/nfp_svgnest_glue.hpp"
+
using namespace libnest2d;
using ItemGroup = std::vector<std::reference_wrapper<Item>>;
@@ -50,499 +53,57 @@ void arrangeRectangles() {
using namespace libnest2d;
const int SCALE = 1000000;
-// const int SCALE = 1;
- std::vector<Rectangle> rects = {
- {80*SCALE, 80*SCALE},
- {60*SCALE, 90*SCALE},
- {70*SCALE, 30*SCALE},
- {80*SCALE, 60*SCALE},
- {60*SCALE, 60*SCALE},
- {60*SCALE, 40*SCALE},
- {40*SCALE, 40*SCALE},
- {10*SCALE, 10*SCALE},
- {10*SCALE, 10*SCALE},
- {10*SCALE, 10*SCALE},
- {10*SCALE, 10*SCALE},
- {10*SCALE, 10*SCALE},
- {5*SCALE, 5*SCALE},
- {5*SCALE, 5*SCALE},
- {5*SCALE, 5*SCALE},
- {5*SCALE, 5*SCALE},
- {5*SCALE, 5*SCALE},
- {5*SCALE, 5*SCALE},
- {5*SCALE, 5*SCALE},
- {20*SCALE, 20*SCALE}
- };
-
-// std::vector<Rectangle> rects = {
-// {20*SCALE, 10*SCALE},
-// {20*SCALE, 10*SCALE},
-// {20*SCALE, 20*SCALE},
-// };
-// std::vector<Item> input {
-// {{0, 0}, {0, 20*SCALE}, {10*SCALE, 0}, {0, 0}}
-// };
-
- std::vector<Item> crasher =
- {
- {
- {-5000000, 8954050},
- {5000000, 8954050},
- {5000000, -45949},
- {4972609, -568549},
- {3500000, -8954050},
- {-3500000, -8954050},
- {-4972609, -568549},
- {-5000000, -45949},
- {-5000000, 8954050},
- },
- {
- {-5000000, 8954050},
- {5000000, 8954050},
- {5000000, -45949},
- {4972609, -568549},
- {3500000, -8954050},
- {-3500000, -8954050},
- {-4972609, -568549},
- {-5000000, -45949},
- {-5000000, 8954050},
- },
- {
- {-5000000, 8954050},
- {5000000, 8954050},
- {5000000, -45949},
- {4972609, -568549},
- {3500000, -8954050},
- {-3500000, -8954050},
- {-4972609, -568549},
- {-5000000, -45949},
- {-5000000, 8954050},
- },
- {
- {-5000000, 8954050},
- {5000000, 8954050},
- {5000000, -45949},
- {4972609, -568549},
- {3500000, -8954050},
- {-3500000, -8954050},
- {-4972609, -568549},
- {-5000000, -45949},
- {-5000000, 8954050},
- },
- {
- {-5000000, 8954050},
- {5000000, 8954050},
- {5000000, -45949},
- {4972609, -568549},
- {3500000, -8954050},
- {-3500000, -8954050},
- {-4972609, -568549},
- {-5000000, -45949},
- {-5000000, 8954050},
- },
- {
- {-5000000, 8954050},
- {5000000, 8954050},
- {5000000, -45949},
- {4972609, -568549},
- {3500000, -8954050},
- {-3500000, -8954050},
- {-4972609, -568549},
- {-5000000, -45949},
- {-5000000, 8954050},
- },
- {
- {-9945219, -3065619},
- {-9781479, -2031780},
- {-9510560, -1020730},
- {-9135450, -43529},
- {-2099999, 14110899},
- {2099999, 14110899},
- {9135450, -43529},
- {9510560, -1020730},
- {9781479, -2031780},
- {9945219, -3065619},
- {10000000, -4110899},
- {9945219, -5156179},
- {9781479, -6190020},
- {9510560, -7201069},
- {9135450, -8178270},
- {8660249, -9110899},
- {8090169, -9988750},
- {7431449, -10802200},
- {6691309, -11542300},
- {5877850, -12201100},
- {5000000, -12771100},
- {4067369, -13246399},
- {3090169, -13621500},
- {2079119, -13892399},
- {1045279, -14056099},
- {0, -14110899},
- {-1045279, -14056099},
- {-2079119, -13892399},
- {-3090169, -13621500},
- {-4067369, -13246399},
- {-5000000, -12771100},
- {-5877850, -12201100},
- {-6691309, -11542300},
- {-7431449, -10802200},
- {-8090169, -9988750},
- {-8660249, -9110899},
- {-9135450, -8178270},
- {-9510560, -7201069},
- {-9781479, -6190020},
- {-9945219, -5156179},
- {-10000000, -4110899},
- {-9945219, -3065619},
- },
- {
- {-9945219, -3065619},
- {-9781479, -2031780},
- {-9510560, -1020730},
- {-9135450, -43529},
- {-2099999, 14110899},
- {2099999, 14110899},
- {9135450, -43529},
- {9510560, -1020730},
- {9781479, -2031780},
- {9945219, -3065619},
- {10000000, -4110899},
- {9945219, -5156179},
- {9781479, -6190020},
- {9510560, -7201069},
- {9135450, -8178270},
- {8660249, -9110899},
- {8090169, -9988750},
- {7431449, -10802200},
- {6691309, -11542300},
- {5877850, -12201100},
- {5000000, -12771100},
- {4067369, -13246399},
- {3090169, -13621500},
- {2079119, -13892399},
- {1045279, -14056099},
- {0, -14110899},
- {-1045279, -14056099},
- {-2079119, -13892399},
- {-3090169, -13621500},
- {-4067369, -13246399},
- {-5000000, -12771100},
- {-5877850, -12201100},
- {-6691309, -11542300},
- {-7431449, -10802200},
- {-8090169, -9988750},
- {-8660249, -9110899},
- {-9135450, -8178270},
- {-9510560, -7201069},
- {-9781479, -6190020},
- {-9945219, -5156179},
- {-10000000, -4110899},
- {-9945219, -3065619},
- },
- {
- {-9945219, -3065619},
- {-9781479, -2031780},
- {-9510560, -1020730},
- {-9135450, -43529},
- {-2099999, 14110899},
- {2099999, 14110899},
- {9135450, -43529},
- {9510560, -1020730},
- {9781479, -2031780},
- {9945219, -3065619},
- {10000000, -4110899},
- {9945219, -5156179},
- {9781479, -6190020},
- {9510560, -7201069},
- {9135450, -8178270},
- {8660249, -9110899},
- {8090169, -9988750},
- {7431449, -10802200},
- {6691309, -11542300},
- {5877850, -12201100},
- {5000000, -12771100},
- {4067369, -13246399},
- {3090169, -13621500},
- {2079119, -13892399},
- {1045279, -14056099},
- {0, -14110899},
- {-1045279, -14056099},
- {-2079119, -13892399},
- {-3090169, -13621500},
- {-4067369, -13246399},
- {-5000000, -12771100},
- {-5877850, -12201100},
- {-6691309, -11542300},
- {-7431449, -10802200},
- {-8090169, -9988750},
- {-8660249, -9110899},
- {-9135450, -8178270},
- {-9510560, -7201069},
- {-9781479, -6190020},
- {-9945219, -5156179},
- {-10000000, -4110899},
- {-9945219, -3065619},
- },
- {
- {-9945219, -3065619},
- {-9781479, -2031780},
- {-9510560, -1020730},
- {-9135450, -43529},
- {-2099999, 14110899},
- {2099999, 14110899},
- {9135450, -43529},
- {9510560, -1020730},
- {9781479, -2031780},
- {9945219, -3065619},
- {10000000, -4110899},
- {9945219, -5156179},
- {9781479, -6190020},
- {9510560, -7201069},
- {9135450, -8178270},
- {8660249, -9110899},
- {8090169, -9988750},
- {7431449, -10802200},
- {6691309, -11542300},
- {5877850, -12201100},
- {5000000, -12771100},
- {4067369, -13246399},
- {3090169, -13621500},
- {2079119, -13892399},
- {1045279, -14056099},
- {0, -14110899},
- {-1045279, -14056099},
- {-2079119, -13892399},
- {-3090169, -13621500},
- {-4067369, -13246399},
- {-5000000, -12771100},
- {-5877850, -12201100},
- {-6691309, -11542300},
- {-7431449, -10802200},
- {-8090169, -9988750},
- {-8660249, -9110899},
- {-9135450, -8178270},
- {-9510560, -7201069},
- {-9781479, -6190020},
- {-9945219, -5156179},
- {-10000000, -4110899},
- {-9945219, -3065619},
- },
- {
- {-9945219, -3065619},
- {-9781479, -2031780},
- {-9510560, -1020730},
- {-9135450, -43529},
- {-2099999, 14110899},
- {2099999, 14110899},
- {9135450, -43529},
- {9510560, -1020730},
- {9781479, -2031780},
- {9945219, -3065619},
- {10000000, -4110899},
- {9945219, -5156179},
- {9781479, -6190020},
- {9510560, -7201069},
- {9135450, -8178270},
- {8660249, -9110899},
- {8090169, -9988750},
- {7431449, -10802200},
- {6691309, -11542300},
- {5877850, -12201100},
- {5000000, -12771100},
- {4067369, -13246399},
- {3090169, -13621500},
- {2079119, -13892399},
- {1045279, -14056099},
- {0, -14110899},
- {-1045279, -14056099},
- {-2079119, -13892399},
- {-3090169, -13621500},
- {-4067369, -13246399},
- {-5000000, -12771100},
- {-5877850, -12201100},
- {-6691309, -11542300},
- {-7431449, -10802200},
- {-8090169, -9988750},
- {-8660249, -9110899},
- {-9135450, -8178270},
- {-9510560, -7201069},
- {-9781479, -6190020},
- {-9945219, -5156179},
- {-10000000, -4110899},
- {-9945219, -3065619},
- },
- {
- {-9945219, -3065619},
- {-9781479, -2031780},
- {-9510560, -1020730},
- {-9135450, -43529},
- {-2099999, 14110899},
- {2099999, 14110899},
- {9135450, -43529},
- {9510560, -1020730},
- {9781479, -2031780},
- {9945219, -3065619},
- {10000000, -4110899},
- {9945219, -5156179},
- {9781479, -6190020},
- {9510560, -7201069},
- {9135450, -8178270},
- {8660249, -9110899},
- {8090169, -9988750},
- {7431449, -10802200},
- {6691309, -11542300},
- {5877850, -12201100},
- {5000000, -12771100},
- {4067369, -13246399},
- {3090169, -13621500},
- {2079119, -13892399},
- {1045279, -14056099},
- {0, -14110899},
- {-1045279, -14056099},
- {-2079119, -13892399},
- {-3090169, -13621500},
- {-4067369, -13246399},
- {-5000000, -12771100},
- {-5877850, -12201100},
- {-6691309, -11542300},
- {-7431449, -10802200},
- {-8090169, -9988750},
- {-8660249, -9110899},
- {-9135450, -8178270},
- {-9510560, -7201069},
- {-9781479, -6190020},
- {-9945219, -5156179},
- {-10000000, -4110899},
- {-9945219, -3065619},
- },
- {
- {-9945219, -3065619},
- {-9781479, -2031780},
- {-9510560, -1020730},
- {-9135450, -43529},
- {-2099999, 14110899},
- {2099999, 14110899},
- {9135450, -43529},
- {9510560, -1020730},
- {9781479, -2031780},
- {9945219, -3065619},
- {10000000, -4110899},
- {9945219, -5156179},
- {9781479, -6190020},
- {9510560, -7201069},
- {9135450, -8178270},
- {8660249, -9110899},
- {8090169, -9988750},
- {7431449, -10802200},
- {6691309, -11542300},
- {5877850, -12201100},
- {5000000, -12771100},
- {4067369, -13246399},
- {3090169, -13621500},
- {2079119, -13892399},
- {1045279, -14056099},
- {0, -14110899},
- {-1045279, -14056099},
- {-2079119, -13892399},
- {-3090169, -13621500},
- {-4067369, -13246399},
- {-5000000, -12771100},
- {-5877850, -12201100},
- {-6691309, -11542300},
- {-7431449, -10802200},
- {-8090169, -9988750},
- {-8660249, -9110899},
- {-9135450, -8178270},
- {-9510560, -7201069},
- {-9781479, -6190020},
- {-9945219, -5156179},
- {-10000000, -4110899},
- {-9945219, -3065619},
- },
- {
- {-9945219, -3065619},
- {-9781479, -2031780},
- {-9510560, -1020730},
- {-9135450, -43529},
- {-2099999, 14110899},
- {2099999, 14110899},
- {9135450, -43529},
- {9510560, -1020730},
- {9781479, -2031780},
- {9945219, -3065619},
- {10000000, -4110899},
- {9945219, -5156179},
- {9781479, -6190020},
- {9510560, -7201069},
- {9135450, -8178270},
- {8660249, -9110899},
- {8090169, -9988750},
- {7431449, -10802200},
- {6691309, -11542300},
- {5877850, -12201100},
- {5000000, -12771100},
- {4067369, -13246399},
- {3090169, -13621500},
- {2079119, -13892399},
- {1045279, -14056099},
- {0, -14110899},
- {-1045279, -14056099},
- {-2079119, -13892399},
- {-3090169, -13621500},
- {-4067369, -13246399},
- {-5000000, -12771100},
- {-5877850, -12201100},
- {-6691309, -11542300},
- {-7431449, -10802200},
- {-8090169, -9988750},
- {-8660249, -9110899},
- {-9135450, -8178270},
- {-9510560, -7201069},
- {-9781479, -6190020},
- {-9945219, -5156179},
- {-10000000, -4110899},
- {-9945219, -3065619},
- },
- {
- {-18000000, -1000000},
- {-15000000, 22000000},
- {-11000000, 26000000},
- {11000000, 26000000},
- {15000000, 22000000},
- {18000000, -1000000},
- {18000000, -26000000},
- {-18000000, -26000000},
- {-18000000, -1000000},
- },
- };
+ std::vector<Item> rects(202, {
+ {-9945219, -3065619},
+ {-9781479, -2031780},
+ {-9510560, -1020730},
+ {-9135450, -43529},
+ {-2099999, 14110899},
+ {2099999, 14110899},
+ {9135450, -43529},
+ {9510560, -1020730},
+ {9781479, -2031780},
+ {9945219, -3065619},
+ {10000000, -4110899},
+ {9945219, -5156179},
+ {9781479, -6190019},
+ {9510560, -7201069},
+ {9135450, -8178270},
+ {8660249, -9110899},
+ {8090169, -9988750},
+ {7431449, -10802209},
+ {6691309, -11542349},
+ {5877850, -12201069},
+ {5000000, -12771149},
+ {4067369, -13246350},
+ {3090169, -13621459},
+ {2079119, -13892379},
+ {1045279, -14056119},
+ {0, -14110899},
+ {-1045279, -14056119},
+ {-2079119, -13892379},
+ {-3090169, -13621459},
+ {-4067369, -13246350},
+ {-5000000, -12771149},
+ {-5877850, -12201069},
+ {-6691309, -11542349},
+ {-7431449, -10802209},
+ {-8090169, -9988750},
+ {-8660249, -9110899},
+ {-9135450, -8178270},
+ {-9510560, -7201069},
+ {-9781479, -6190019},
+ {-9945219, -5156179},
+ {-10000000, -4110899},
+ {-9945219, -3065619},
+ });
- std::vector<Item> proba = {
- {
- Rectangle(100, 2)
- },
- {
- Rectangle(100, 2)
- },
- {
- Rectangle(100, 2)
- },
- {
- Rectangle(10, 10)
- },
- };
-
- proba[0].rotate(Pi/3);
- proba[1].rotate(Pi-Pi/3);
-
-// std::vector<Item> input(25, Rectangle(70*SCALE, 10*SCALE));
std::vector<Item> input;
input.insert(input.end(), prusaParts().begin(), prusaParts().end());
// input.insert(input.end(), prusaExParts().begin(), prusaExParts().end());
// input.insert(input.end(), stegoParts().begin(), stegoParts().end());
// input.insert(input.end(), rects.begin(), rects.end());
-// input.insert(input.end(), proba.begin(), proba.end());
-// input.insert(input.end(), crasher.begin(), crasher.end());
Box bin(250*SCALE, 210*SCALE);
// PolygonImpl bin = {
@@ -560,10 +121,12 @@ void arrangeRectangles() {
// {}
// };
- auto min_obj_distance = static_cast<Coord>(0*SCALE);
+// Circle bin({0, 0}, 125*SCALE);
- using Placer = strategies::_NofitPolyPlacer<PolygonImpl, Box>;
- using Packer = Arranger<Placer, FirstFitSelection>;
+ auto min_obj_distance = static_cast<Coord>(6*SCALE);
+
+ using Placer = placers::_NofitPolyPlacer<PolygonImpl, decltype(bin)>;
+ using Packer = Nester<Placer, FirstFitSelection>;
Packer arrange(bin, min_obj_distance);
@@ -571,121 +134,23 @@ void arrangeRectangles() {
pconf.alignment = Placer::Config::Alignment::CENTER;
pconf.starting_point = Placer::Config::Alignment::CENTER;
pconf.rotations = {0.0/*, Pi/2.0, Pi, 3*Pi/2*/};
- pconf.accuracy = 0.5f;
-
-// auto bincenter = ShapeLike::boundingBox(bin).center();
-// pconf.object_function = [&bin, bincenter](
-// Placer::Pile pile, const Item& item,
-// double /*area*/, double norm, double penality) {
-
-// using pl = PointLike;
-
-// static const double BIG_ITEM_TRESHOLD = 0.2;
-// static const double GRAVITY_RATIO = 0.5;
-// static const double DENSITY_RATIO = 1.0 - GRAVITY_RATIO;
-
-// // We will treat big items (compared to the print bed) differently
-// NfpPlacer::Pile bigs;
-// bigs.reserve(pile.size());
-// for(auto& p : pile) {
-// auto pbb = ShapeLike::boundingBox(p);
-// auto na = std::sqrt(pbb.width()*pbb.height())/norm;
-// if(na > BIG_ITEM_TRESHOLD) bigs.emplace_back(p);
-// }
-
-// // Candidate item bounding box
-// auto ibb = item.boundingBox();
-
-// // Calculate the full bounding box of the pile with the candidate item
-// pile.emplace_back(item.transformedShape());
-// auto fullbb = ShapeLike::boundingBox(pile);
-// pile.pop_back();
-
-// // The bounding box of the big items (they will accumulate in the center
-// // of the pile
-// auto bigbb = bigs.empty()? fullbb : ShapeLike::boundingBox(bigs);
-
-// // The size indicator of the candidate item. This is not the area,
-// // but almost...
-// auto itemnormarea = std::sqrt(ibb.width()*ibb.height())/norm;
-
-// // Will hold the resulting score
-// double score = 0;
-
-// if(itemnormarea > BIG_ITEM_TRESHOLD) {
-// // This branch is for the bigger items..
-// // Here we will use the closest point of the item bounding box to
-// // the already arranged pile. So not the bb center nor the a choosen
-// // corner but whichever is the closest to the center. This will
-// // prevent unwanted strange arrangements.
-
-// auto minc = ibb.minCorner(); // bottom left corner
-// auto maxc = ibb.maxCorner(); // top right corner
-
-// // top left and bottom right corners
-// auto top_left = PointImpl{getX(minc), getY(maxc)};
-// auto bottom_right = PointImpl{getX(maxc), getY(minc)};
-
-// auto cc = fullbb.center(); // The gravity center
-
-// // Now the distnce of the gravity center will be calculated to the
-// // five anchor points and the smallest will be chosen.
-// std::array<double, 5> dists;
-// dists[0] = pl::distance(minc, cc);
-// dists[1] = pl::distance(maxc, cc);
-// dists[2] = pl::distance(ibb.center(), cc);
-// dists[3] = pl::distance(top_left, cc);
-// dists[4] = pl::distance(bottom_right, cc);
-
-// auto dist = *(std::min_element(dists.begin(), dists.end())) / norm;
-
-// // Density is the pack density: how big is the arranged pile
-// auto density = std::sqrt(fullbb.width()*fullbb.height()) / norm;
-
-// // The score is a weighted sum of the distance from pile center
-// // and the pile size
-// score = GRAVITY_RATIO * dist + DENSITY_RATIO * density;
-
-// } else if(itemnormarea < BIG_ITEM_TRESHOLD && bigs.empty()) {
-// // If there are no big items, only small, we should consider the
-// // density here as well to not get silly results
-// auto bindist = pl::distance(ibb.center(), bincenter) / norm;
-// auto density = std::sqrt(fullbb.width()*fullbb.height()) / norm;
-// score = GRAVITY_RATIO * bindist + DENSITY_RATIO * density;
-// } else {
-// // Here there are the small items that should be placed around the
-// // already processed bigger items.
-// // No need to play around with the anchor points, the center will be
-// // just fine for small items
-// score = pl::distance(ibb.center(), bigbb.center()) / norm;
-// }
-
-// // If it does not fit into the print bed we will beat it
-// // with a large penality. If we would not do this, there would be only
-// // one big pile that doesn't care whether it fits onto the print bed.
-// if(!NfpPlacer::wouldFit(fullbb, bin)) score = 2*penality - score;
-
-// return score;
-// };
+ pconf.accuracy = 0.65f;
+ pconf.parallel = true;
Packer::SelectionConfig sconf;
// sconf.allow_parallel = false;
// sconf.force_parallel = false;
// sconf.try_triplets = true;
// sconf.try_reverse_order = true;
-// sconf.waste_increment = 0.005;
+// sconf.waste_increment = 0.01;
arrange.configure(pconf, sconf);
arrange.progressIndicator([&](unsigned r){
-// svg::SVGWriter::Config conf;
-// conf.mm_in_coord_units = SCALE;
-// svg::SVGWriter svgw(conf);
-// svgw.setSize(bin);
-// svgw.writePackGroup(arrange.lastResult());
-// svgw.save("debout");
std::cout << "Remaining items: " << r << std::endl;
- })/*.useMinimumBoundigBoxRotation()*/;
+ });
+
+// findMinimumBoundingBoxRotations(input.begin(), input.end());
Benchmark bench;
@@ -693,7 +158,7 @@ void arrangeRectangles() {
Packer::ResultType result;
try {
- result = arrange.arrange(input.begin(), input.end());
+ result = arrange.execute(input.begin(), input.end());
} catch(GeometryException& ge) {
std::cerr << "Geometry error: " << ge.what() << std::endl;
return ;
@@ -707,7 +172,7 @@ void arrangeRectangles() {
std::vector<double> eff;
eff.reserve(result.size());
- auto bin_area = ShapeLike::area<PolygonImpl>(bin);
+ auto bin_area = sl::area(bin);
for(auto& r : result) {
double a = 0;
std::for_each(r.begin(), r.end(), [&a] (Item& e ){ a += e.area(); });
@@ -728,10 +193,10 @@ void arrangeRectangles() {
for(auto& r : result) { std::cout << r.size() << " "; total += r.size(); }
std::cout << ") Total: " << total << std::endl;
- for(auto& it : input) {
- auto ret = ShapeLike::isValid(it.transformedShape());
- std::cout << ret.second << std::endl;
- }
+// for(auto& it : input) {
+// auto ret = sl::isValid(it.transformedShape());
+// std::cout << ret.second << std::endl;
+// }
if(total != input.size()) std::cout << "ERROR " << "could not pack "
<< input.size() - total << " elements!"
@@ -744,13 +209,10 @@ void arrangeRectangles() {
SVGWriter svgw(conf);
svgw.setSize(Box(250*SCALE, 210*SCALE));
svgw.writePackGroup(result);
-// std::for_each(input.begin(), input.end(), [&svgw](Item& item){ svgw.writeItem(item);});
svgw.save("out");
}
int main(void /*int argc, char **argv*/) {
arrangeRectangles();
-// findDegenerateCase();
-
return EXIT_SUCCESS;
}
diff --git a/xs/src/libnest2d/libnest2d.h b/xs/src/libnest2d/libnest2d.h
index c9e21ecfb..bfd88f4f5 100644
--- a/xs/src/libnest2d/libnest2d.h
+++ b/xs/src/libnest2d/libnest2d.h
@@ -22,6 +22,7 @@ using Point = PointImpl;
using Coord = TCoord<PointImpl>;
using Box = _Box<PointImpl>;
using Segment = _Segment<PointImpl>;
+using Circle = _Circle<PointImpl>;
using Item = _Item<PolygonImpl>;
using Rectangle = _Rectangle<PolygonImpl>;
@@ -29,15 +30,12 @@ using Rectangle = _Rectangle<PolygonImpl>;
using PackGroup = _PackGroup<PolygonImpl>;
using IndexedPackGroup = _IndexedPackGroup<PolygonImpl>;
-using FillerSelection = strategies::_FillerSelection<PolygonImpl>;
-using FirstFitSelection = strategies::_FirstFitSelection<PolygonImpl>;
-using DJDHeuristic = strategies::_DJDHeuristic<PolygonImpl>;
+using FillerSelection = selections::_FillerSelection<PolygonImpl>;
+using FirstFitSelection = selections::_FirstFitSelection<PolygonImpl>;
+using DJDHeuristic = selections::_DJDHeuristic<PolygonImpl>;
-using NfpPlacer = strategies::_NofitPolyPlacer<PolygonImpl>;
-using BottomLeftPlacer = strategies::_BottomLeftPlacer<PolygonImpl>;
-
-//template<NfpLevel lvl = NfpLevel::BOTH_CONCAVE_WITH_HOLES>
-//using NofitPolyPlacer = strategies::_NofitPolyPlacer<PolygonImpl, lvl>;
+using NfpPlacer = placers::_NofitPolyPlacer<PolygonImpl>;
+using BottomLeftPlacer = placers::_BottomLeftPlacer<PolygonImpl>;
}
diff --git a/xs/src/libnest2d/libnest2d/boost_alg.hpp b/xs/src/libnest2d/libnest2d/boost_alg.hpp
index 67e19fcbd..bb0403b06 100644
--- a/xs/src/libnest2d/libnest2d/boost_alg.hpp
+++ b/xs/src/libnest2d/libnest2d/boost_alg.hpp
@@ -36,7 +36,7 @@ using libnest2d::setX;
using libnest2d::setY;
using Box = libnest2d::_Box<PointImpl>;
using Segment = libnest2d::_Segment<PointImpl>;
-using Shapes = libnest2d::Nfp::Shapes<PolygonImpl>;
+using Shapes = libnest2d::nfp::Shapes<PolygonImpl>;
}
@@ -241,11 +241,11 @@ template<> struct tag<bp2d::PolygonImpl> {
template<> struct exterior_ring<bp2d::PolygonImpl> {
static inline bp2d::PathImpl& get(bp2d::PolygonImpl& p) {
- return libnest2d::ShapeLike::getContour(p);
+ return libnest2d::shapelike::getContour(p);
}
static inline bp2d::PathImpl const& get(bp2d::PolygonImpl const& p) {
- return libnest2d::ShapeLike::getContour(p);
+ return libnest2d::shapelike::getContour(p);
}
};
@@ -271,13 +271,13 @@ struct interior_rings<bp2d::PolygonImpl> {
static inline libnest2d::THolesContainer<bp2d::PolygonImpl>& get(
bp2d::PolygonImpl& p)
{
- return libnest2d::ShapeLike::holes(p);
+ return libnest2d::shapelike::holes(p);
}
static inline const libnest2d::THolesContainer<bp2d::PolygonImpl>& get(
bp2d::PolygonImpl const& p)
{
- return libnest2d::ShapeLike::holes(p);
+ return libnest2d::shapelike::holes(p);
}
};
@@ -311,83 +311,77 @@ struct range_value<bp2d::Shapes> {
namespace libnest2d { // Now the algorithms that boost can provide...
+namespace pointlike {
template<>
-inline double PointLike::distance(const PointImpl& p1,
- const PointImpl& p2 )
+inline double distance(const PointImpl& p1, const PointImpl& p2 )
{
return boost::geometry::distance(p1, p2);
}
template<>
-inline double PointLike::distance(const PointImpl& p,
- const bp2d::Segment& seg )
+inline double distance(const PointImpl& p, const bp2d::Segment& seg )
{
return boost::geometry::distance(p, seg);
}
+}
+namespace shapelike {
// Tell libnest2d how to make string out of a ClipperPolygon object
template<>
-inline bool ShapeLike::intersects(const PathImpl& sh1,
- const PathImpl& sh2)
+inline bool intersects(const PathImpl& sh1, const PathImpl& sh2)
{
return boost::geometry::intersects(sh1, sh2);
}
// Tell libnest2d how to make string out of a ClipperPolygon object
template<>
-inline bool ShapeLike::intersects(const PolygonImpl& sh1,
- const PolygonImpl& sh2)
+inline bool intersects(const PolygonImpl& sh1, const PolygonImpl& sh2)
{
return boost::geometry::intersects(sh1, sh2);
}
// Tell libnest2d how to make string out of a ClipperPolygon object
template<>
-inline bool ShapeLike::intersects(const bp2d::Segment& s1,
- const bp2d::Segment& s2)
+inline bool intersects(const bp2d::Segment& s1, const bp2d::Segment& s2)
{
return boost::geometry::intersects(s1, s2);
}
#ifndef DISABLE_BOOST_AREA
template<>
-inline double ShapeLike::area(const PolygonImpl& shape)
+inline double area(const PolygonImpl& shape, const PolygonTag&)
{
return boost::geometry::area(shape);
}
#endif
template<>
-inline bool ShapeLike::isInside<PolygonImpl>(const PointImpl& point,
- const PolygonImpl& shape)
+inline bool isInside(const PointImpl& point, const PolygonImpl& shape)
{
return boost::geometry::within(point, shape);
}
template<>
-inline bool ShapeLike::isInside(const PolygonImpl& sh1,
- const PolygonImpl& sh2)
+inline bool isInside(const PolygonImpl& sh1, const PolygonImpl& sh2)
{
return boost::geometry::within(sh1, sh2);
}
template<>
-inline bool ShapeLike::touches( const PolygonImpl& sh1,
- const PolygonImpl& sh2)
+inline bool touches(const PolygonImpl& sh1, const PolygonImpl& sh2)
{
return boost::geometry::touches(sh1, sh2);
}
template<>
-inline bool ShapeLike::touches( const PointImpl& point,
- const PolygonImpl& shape)
+inline bool touches( const PointImpl& point, const PolygonImpl& shape)
{
return boost::geometry::touches(point, shape);
}
#ifndef DISABLE_BOOST_BOUNDING_BOX
template<>
-inline bp2d::Box ShapeLike::boundingBox(const PolygonImpl& sh)
+inline bp2d::Box boundingBox(const PolygonImpl& sh, const PolygonTag&)
{
bp2d::Box b;
boost::geometry::envelope(sh, b);
@@ -395,7 +389,8 @@ inline bp2d::Box ShapeLike::boundingBox(const PolygonImpl& sh)
}
template<>
-inline bp2d::Box ShapeLike::boundingBox<PolygonImpl>(const bp2d::Shapes& shapes)
+inline bp2d::Box boundingBox<bp2d::Shapes>(const bp2d::Shapes& shapes,
+ const MultiPolygonTag&)
{
bp2d::Box b;
boost::geometry::envelope(shapes, b);
@@ -405,7 +400,7 @@ inline bp2d::Box ShapeLike::boundingBox<PolygonImpl>(const bp2d::Shapes& shapes)
#ifndef DISABLE_BOOST_CONVEX_HULL
template<>
-inline PolygonImpl ShapeLike::convexHull(const PolygonImpl& sh)
+inline PolygonImpl convexHull(const PolygonImpl& sh, const PolygonTag&)
{
PolygonImpl ret;
boost::geometry::convex_hull(sh, ret);
@@ -413,7 +408,8 @@ inline PolygonImpl ShapeLike::convexHull(const PolygonImpl& sh)
}
template<>
-inline PolygonImpl ShapeLike::convexHull(const bp2d::Shapes& shapes)
+inline PolygonImpl convexHull(const TMultiShape<PolygonImpl>& shapes,
+ const MultiPolygonTag&)
{
PolygonImpl ret;
boost::geometry::convex_hull(shapes, ret);
@@ -421,56 +417,17 @@ inline PolygonImpl ShapeLike::convexHull(const bp2d::Shapes& shapes)
}
#endif
-#ifndef DISABLE_BOOST_ROTATE
-template<>
-inline void ShapeLike::rotate(PolygonImpl& sh, const Radians& rads)
-{
- namespace trans = boost::geometry::strategy::transform;
-
- PolygonImpl cpy = sh;
- trans::rotate_transformer<boost::geometry::radian, Radians, 2, 2>
- rotate(rads);
-
- boost::geometry::transform(cpy, sh, rotate);
-}
-#endif
-
-#ifndef DISABLE_BOOST_TRANSLATE
-template<>
-inline void ShapeLike::translate(PolygonImpl& sh, const PointImpl& offs)
-{
- namespace trans = boost::geometry::strategy::transform;
-
- PolygonImpl cpy = sh;
- trans::translate_transformer<bp2d::Coord, 2, 2> translate(
- bp2d::getX(offs), bp2d::getY(offs));
-
- boost::geometry::transform(cpy, sh, translate);
-}
-#endif
-
#ifndef DISABLE_BOOST_OFFSET
template<>
-inline void ShapeLike::offset(PolygonImpl& sh, bp2d::Coord distance)
+inline void offset(PolygonImpl& sh, bp2d::Coord distance)
{
PolygonImpl cpy = sh;
boost::geometry::buffer(cpy, sh, distance);
}
#endif
-#ifndef DISABLE_BOOST_NFP_MERGE
-template<>
-inline bp2d::Shapes Nfp::merge(const bp2d::Shapes& shapes,
- const PolygonImpl& sh)
-{
- bp2d::Shapes retv;
- boost::geometry::union_(shapes, sh, retv);
- return retv;
-}
-#endif
-
#ifndef DISABLE_BOOST_SERIALIZE
-template<> inline std::string ShapeLike::serialize<libnest2d::Formats::SVG>(
+template<> inline std::string serialize<libnest2d::Formats::SVG>(
const PolygonImpl& sh, double scale)
{
std::stringstream ss;
@@ -482,14 +439,14 @@ template<> inline std::string ShapeLike::serialize<libnest2d::Formats::SVG>(
Polygonf::ring_type ring;
Polygonf::inner_container_type holes;
- ring.reserve(ShapeLike::contourVertexCount(sh));
+ ring.reserve(shapelike::contourVertexCount(sh));
- for(auto it = ShapeLike::cbegin(sh); it != ShapeLike::cend(sh); it++) {
+ for(auto it = shapelike::cbegin(sh); it != shapelike::cend(sh); it++) {
auto& v = *it;
ring.emplace_back(getX(v)*scale, getY(v)*scale);
};
- auto H = ShapeLike::holes(sh);
+ auto H = shapelike::holes(sh);
for(PathImpl& h : H ) {
Polygonf::ring_type hf;
for(auto it = h.begin(); it != h.end(); it++) {
@@ -512,21 +469,47 @@ template<> inline std::string ShapeLike::serialize<libnest2d::Formats::SVG>(
#ifndef DISABLE_BOOST_UNSERIALIZE
template<>
-inline void ShapeLike::unserialize<libnest2d::Formats::SVG>(
+inline void unserialize<libnest2d::Formats::SVG>(
PolygonImpl& sh,
const std::string& str)
{
}
#endif
-template<> inline std::pair<bool, std::string>
-ShapeLike::isValid(const PolygonImpl& sh)
+template<> inline std::pair<bool, std::string> isValid(const PolygonImpl& sh)
{
std::string message;
bool ret = boost::geometry::is_valid(sh, message);
return {ret, message};
}
+}
+
+namespace nfp {
+
+#ifndef DISABLE_BOOST_NFP_MERGE
+
+// Warning: I could not get boost union_ to work. Geometries will overlap.
+template<>
+inline bp2d::Shapes nfp::merge(const bp2d::Shapes& shapes,
+ const PolygonImpl& sh)
+{
+ bp2d::Shapes retv;
+ boost::geometry::union_(shapes, sh, retv);
+ return retv;
+}
+
+template<>
+inline bp2d::Shapes nfp::merge(const bp2d::Shapes& shapes)
+{
+ bp2d::Shapes retv;
+ boost::geometry::union_(shapes, shapes.back(), retv);
+ return retv;
+}
+#endif
+
+}
+
}
diff --git a/xs/src/libnest2d/libnest2d/clipper_backend/clipper_backend.hpp b/xs/src/libnest2d/libnest2d/clipper_backend/clipper_backend.hpp
index 15ceb1576..745fd2108 100644
--- a/xs/src/libnest2d/libnest2d/clipper_backend/clipper_backend.hpp
+++ b/xs/src/libnest2d/libnest2d/clipper_backend/clipper_backend.hpp
@@ -99,49 +99,54 @@ template<> struct PointType<PolygonImpl> {
using Type = PointImpl;
};
-// Type of vertex iterator used by Clipper
-template<> struct VertexIteratorType<PolygonImpl> {
- using Type = ClipperLib::Path::iterator;
-};
-
-// Type of vertex iterator used by Clipper
-template<> struct VertexConstIteratorType<PolygonImpl> {
- using Type = ClipperLib::Path::const_iterator;
+template<> struct PointType<PointImpl> {
+ using Type = PointImpl;
};
template<> struct CountourType<PolygonImpl> {
using Type = PathImpl;
};
-// Tell binpack2d how to extract the X coord from a ClipperPoint object
-template<> inline TCoord<PointImpl> PointLike::x(const PointImpl& p)
+template<> struct ShapeTag<PolygonImpl> { using Type = PolygonTag; };
+
+template<> struct ShapeTag<TMultiShape<PolygonImpl>> {
+ using Type = MultiPolygonTag;
+};
+
+template<> struct PointType<TMultiShape<PolygonImpl>> {
+ using Type = PointImpl;
+};
+
+template<> struct HolesContainer<PolygonImpl> {
+ using Type = ClipperLib::Paths;
+};
+
+namespace pointlike {
+
+// Tell libnest2d how to extract the X coord from a ClipperPoint object
+template<> inline TCoord<PointImpl> x(const PointImpl& p)
{
return p.X;
}
-// Tell binpack2d how to extract the Y coord from a ClipperPoint object
-template<> inline TCoord<PointImpl> PointLike::y(const PointImpl& p)
+// Tell libnest2d how to extract the Y coord from a ClipperPoint object
+template<> inline TCoord<PointImpl> y(const PointImpl& p)
{
return p.Y;
}
-// Tell binpack2d how to extract the X coord from a ClipperPoint object
-template<> inline TCoord<PointImpl>& PointLike::x(PointImpl& p)
+// Tell libnest2d how to extract the X coord from a ClipperPoint object
+template<> inline TCoord<PointImpl>& x(PointImpl& p)
{
return p.X;
}
-// Tell binpack2d how to extract the Y coord from a ClipperPoint object
-template<>
-inline TCoord<PointImpl>& PointLike::y(PointImpl& p)
+// Tell libnest2d how to extract the Y coord from a ClipperPoint object
+template<> inline TCoord<PointImpl>& y(PointImpl& p)
{
return p.Y;
}
-template<>
-inline void ShapeLike::reserve(PolygonImpl& sh, size_t vertex_capacity)
-{
- return sh.Contour.reserve(vertex_capacity);
}
#define DISABLE_BOOST_AREA
@@ -175,16 +180,24 @@ inline double area<Orientation::COUNTER_CLOCKWISE>(const PolygonImpl& sh) {
return ClipperLib::Area(sh.Contour) + a;
}
+
}
-// Tell binpack2d how to make string out of a ClipperPolygon object
-template<>
-inline double ShapeLike::area(const PolygonImpl& sh) {
+namespace shapelike {
+
+template<> inline void reserve(PolygonImpl& sh, size_t vertex_capacity)
+{
+ return sh.Contour.reserve(vertex_capacity);
+}
+
+// Tell libnest2d how to make string out of a ClipperPolygon object
+template<> inline double area(const PolygonImpl& sh, const PolygonTag&)
+{
return _smartarea::area<OrientationType<PolygonImpl>::Value>(sh);
}
-template<>
-inline void ShapeLike::offset(PolygonImpl& sh, TCoord<PointImpl> distance) {
+template<> inline void offset(PolygonImpl& sh, TCoord<PointImpl> distance)
+{
#define DISABLE_BOOST_OFFSET
using ClipperLib::ClipperOffset;
@@ -234,7 +247,8 @@ inline void ShapeLike::offset(PolygonImpl& sh, TCoord<PointImpl> distance) {
}
// Tell libnest2d how to make string out of a ClipperPolygon object
-template<> inline std::string ShapeLike::toString(const PolygonImpl& sh) {
+template<> inline std::string toString(const PolygonImpl& sh)
+{
std::stringstream ss;
ss << "Contour {\n";
@@ -257,37 +271,8 @@ template<> inline std::string ShapeLike::toString(const PolygonImpl& sh) {
}
template<>
-inline TVertexIterator<PolygonImpl> ShapeLike::begin(PolygonImpl& sh)
-{
- return sh.Contour.begin();
-}
-
-template<>
-inline TVertexIterator<PolygonImpl> ShapeLike::end(PolygonImpl& sh)
-{
- return sh.Contour.end();
-}
-
-template<>
-inline TVertexConstIterator<PolygonImpl> ShapeLike::cbegin(
- const PolygonImpl& sh)
-{
- return sh.Contour.cbegin();
-}
-
-template<>
-inline TVertexConstIterator<PolygonImpl> ShapeLike::cend(
- const PolygonImpl& sh)
+inline PolygonImpl create(const PathImpl& path, const HoleStore& holes)
{
- return sh.Contour.cend();
-}
-
-template<> struct HolesContainer<PolygonImpl> {
- using Type = ClipperLib::Paths;
-};
-
-template<> inline PolygonImpl ShapeLike::create(const PathImpl& path,
- const HoleStore& holes) {
PolygonImpl p;
p.Contour = path;
@@ -308,8 +293,7 @@ template<> inline PolygonImpl ShapeLike::create(const PathImpl& path,
return p;
}
-template<> inline PolygonImpl ShapeLike::create( PathImpl&& path,
- HoleStore&& holes) {
+template<> inline PolygonImpl create( PathImpl&& path, HoleStore&& holes) {
PolygonImpl p;
p.Contour.swap(path);
@@ -331,49 +315,49 @@ template<> inline PolygonImpl ShapeLike::create( PathImpl&& path,
return p;
}
-template<> inline const THolesContainer<PolygonImpl>&
-ShapeLike::holes(const PolygonImpl& sh)
+template<>
+inline const THolesContainer<PolygonImpl>& holes(const PolygonImpl& sh)
{
return sh.Holes;
}
-template<> inline THolesContainer<PolygonImpl>&
-ShapeLike::holes(PolygonImpl& sh)
+template<> inline THolesContainer<PolygonImpl>& holes(PolygonImpl& sh)
{
return sh.Holes;
}
-template<> inline TContour<PolygonImpl>&
-ShapeLike::getHole(PolygonImpl& sh, unsigned long idx)
+template<>
+inline TContour<PolygonImpl>& getHole(PolygonImpl& sh, unsigned long idx)
{
return sh.Holes[idx];
}
-template<> inline const TContour<PolygonImpl>&
-ShapeLike::getHole(const PolygonImpl& sh, unsigned long idx)
+template<>
+inline const TContour<PolygonImpl>& getHole(const PolygonImpl& sh,
+ unsigned long idx)
{
return sh.Holes[idx];
}
-template<> inline size_t ShapeLike::holeCount(const PolygonImpl& sh)
+template<> inline size_t holeCount(const PolygonImpl& sh)
{
return sh.Holes.size();
}
-template<> inline PathImpl& ShapeLike::getContour(PolygonImpl& sh)
+template<> inline PathImpl& getContour(PolygonImpl& sh)
{
return sh.Contour;
}
template<>
-inline const PathImpl& ShapeLike::getContour(const PolygonImpl& sh)
+inline const PathImpl& getContour(const PolygonImpl& sh)
{
return sh.Contour;
}
#define DISABLE_BOOST_TRANSLATE
template<>
-inline void ShapeLike::translate(PolygonImpl& sh, const PointImpl& offs)
+inline void translate(PolygonImpl& sh, const PointImpl& offs)
{
for(auto& p : sh.Contour) { p += offs; }
for(auto& hole : sh.Holes) for(auto& p : hole) { p += offs; }
@@ -381,7 +365,7 @@ inline void ShapeLike::translate(PolygonImpl& sh, const PointImpl& offs)
#define DISABLE_BOOST_ROTATE
template<>
-inline void ShapeLike::rotate(PolygonImpl& sh, const Radians& rads)
+inline void rotate(PolygonImpl& sh, const Radians& rads)
{
using Coord = TCoord<PointImpl>;
@@ -402,9 +386,11 @@ inline void ShapeLike::rotate(PolygonImpl& sh, const Radians& rads)
}
}
+} // namespace shapelike
+
#define DISABLE_BOOST_NFP_MERGE
-inline Nfp::Shapes<PolygonImpl> _merge(ClipperLib::Clipper& clipper) {
- Nfp::Shapes<PolygonImpl> retv;
+inline std::vector<PolygonImpl> _merge(ClipperLib::Clipper& clipper) {
+ shapelike::Shapes<PolygonImpl> retv;
ClipperLib::PolyTree result;
clipper.Execute(ClipperLib::ctUnion, result, ClipperLib::pftNegative);
@@ -438,8 +424,10 @@ inline Nfp::Shapes<PolygonImpl> _merge(ClipperLib::Clipper& clipper) {
return retv;
}
-template<> inline Nfp::Shapes<PolygonImpl>
-Nfp::merge(const Nfp::Shapes<PolygonImpl>& shapes)
+namespace nfp {
+
+template<> inline std::vector<PolygonImpl>
+merge(const std::vector<PolygonImpl>& shapes)
{
ClipperLib::Clipper clipper(ClipperLib::ioReverseSolution);
@@ -461,6 +449,8 @@ Nfp::merge(const Nfp::Shapes<PolygonImpl>& shapes)
}
+}
+
//#define DISABLE_BOOST_SERIALIZE
//#define DISABLE_BOOST_UNSERIALIZE
diff --git a/xs/src/libnest2d/libnest2d/geometry_traits.hpp b/xs/src/libnest2d/libnest2d/geometry_traits.hpp
index 058c47cd4..d16257731 100644
--- a/xs/src/libnest2d/libnest2d/geometry_traits.hpp
+++ b/xs/src/libnest2d/libnest2d/geometry_traits.hpp
@@ -22,34 +22,12 @@ template<class GeomType>
using TCoord = typename CoordType<remove_cvref_t<GeomType>>::Type;
/// Getting the type of point structure used by a shape.
-template<class Shape> struct PointType { /*using Type = void;*/ };
+template<class Sh> struct PointType { using Type = typename Sh::PointType; };
/// TPoint<ShapeClass> as shorthand for `typename PointType<ShapeClass>::Type`.
template<class Shape>
using TPoint = typename PointType<remove_cvref_t<Shape>>::Type;
-/// Getting the VertexIterator type of a shape class.
-template<class Shape> struct VertexIteratorType { /*using Type = void;*/ };
-
-/// Getting the const vertex iterator for a shape class.
-template<class Shape> struct VertexConstIteratorType {/* using Type = void;*/ };
-
-/**
- * TVertexIterator<Shape> as shorthand for
- * `typename VertexIteratorType<Shape>::Type`
- */
-template<class Shape>
-using TVertexIterator =
-typename VertexIteratorType<remove_cvref_t<Shape>>::Type;
-
-/**
- * \brief TVertexConstIterator<Shape> as shorthand for
- * `typename VertexConstIteratorType<Shape>::Type`
- */
-template<class ShapeClass>
-using TVertexConstIterator =
-typename VertexConstIteratorType<remove_cvref_t<ShapeClass>>::Type;
-
/**
* \brief A point pair base class for other point pairs (segment, box, ...).
* \tparam RawPoint The actual point type to use.
@@ -60,6 +38,17 @@ struct PointPair {
RawPoint p2;
};
+struct PolygonTag {};
+struct MultiPolygonTag {};
+struct BoxTag {};
+struct CircleTag {};
+
+template<class Shape> struct ShapeTag { using Type = typename Shape::Tag; };
+template<class S> using Tag = typename ShapeTag<S>::Type;
+
+template<class S> struct MultiShape { using Type = std::vector<S>; };
+template<class S> using TMultiShape = typename MultiShape<S>::Type;
+
/**
* \brief An abstraction of a box;
*/
@@ -69,6 +58,9 @@ class _Box: PointPair<RawPoint> {
using PointPair<RawPoint>::p2;
public:
+ using Tag = BoxTag;
+ using PointType = RawPoint;
+
inline _Box() = default;
inline _Box(const RawPoint& p, const RawPoint& pp):
PointPair<RawPoint>({p, pp}) {}
@@ -98,6 +90,9 @@ class _Circle {
double radius_ = 0;
public:
+ using Tag = CircleTag;
+ using PointType = RawPoint;
+
_Circle() = default;
_Circle(const RawPoint& center, double r): center_(center), radius_(r) {}
@@ -109,7 +104,7 @@ public:
inline void radius(double r) { radius_ = r; }
inline double area() const BP2D_NOEXCEPT {
- return 2.0*Pi*radius_;
+ return 2.0*Pi*radius_*radius_;
}
};
@@ -123,6 +118,8 @@ class _Segment: PointPair<RawPoint> {
mutable Radians angletox_ = std::nan("");
public:
+ using PointType = RawPoint;
+
inline _Segment() = default;
inline _Segment(const RawPoint& p, const RawPoint& pp):
@@ -156,36 +153,36 @@ public:
inline double length();
};
-// This struct serves as a namespace. The only difference is that is can be
+// This struct serves almost as a namespace. The only difference is that is can
// used in friend declarations.
-struct PointLike {
+namespace pointlike {
template<class RawPoint>
- static TCoord<RawPoint> x(const RawPoint& p)
+ inline TCoord<RawPoint> x(const RawPoint& p)
{
return p.x();
}
template<class RawPoint>
- static TCoord<RawPoint> y(const RawPoint& p)
+ inline TCoord<RawPoint> y(const RawPoint& p)
{
return p.y();
}
template<class RawPoint>
- static TCoord<RawPoint>& x(RawPoint& p)
+ inline TCoord<RawPoint>& x(RawPoint& p)
{
return p.x();
}
template<class RawPoint>
- static TCoord<RawPoint>& y(RawPoint& p)
+ inline TCoord<RawPoint>& y(RawPoint& p)
{
return p.y();
}
template<class RawPoint>
- static double distance(const RawPoint& /*p1*/, const RawPoint& /*p2*/)
+ inline double distance(const RawPoint& /*p1*/, const RawPoint& /*p2*/)
{
static_assert(always_false<RawPoint>::value,
"PointLike::distance(point, point) unimplemented!");
@@ -193,7 +190,7 @@ struct PointLike {
}
template<class RawPoint>
- static double distance(const RawPoint& /*p1*/,
+ inline double distance(const RawPoint& /*p1*/,
const _Segment<RawPoint>& /*s*/)
{
static_assert(always_false<RawPoint>::value,
@@ -202,13 +199,13 @@ struct PointLike {
}
template<class RawPoint>
- static std::pair<TCoord<RawPoint>, bool> horizontalDistance(
+ inline std::pair<TCoord<RawPoint>, bool> horizontalDistance(
const RawPoint& p, const _Segment<RawPoint>& s)
{
using Unit = TCoord<RawPoint>;
- auto x = PointLike::x(p), y = PointLike::y(p);
- auto x1 = PointLike::x(s.first()), y1 = PointLike::y(s.first());
- auto x2 = PointLike::x(s.second()), y2 = PointLike::y(s.second());
+ auto x = pointlike::x(p), y = pointlike::y(p);
+ auto x1 = pointlike::x(s.first()), y1 = pointlike::y(s.first());
+ auto x2 = pointlike::x(s.second()), y2 = pointlike::y(s.second());
TCoord<RawPoint> ret;
@@ -228,13 +225,13 @@ struct PointLike {
}
template<class RawPoint>
- static std::pair<TCoord<RawPoint>, bool> verticalDistance(
+ inline std::pair<TCoord<RawPoint>, bool> verticalDistance(
const RawPoint& p, const _Segment<RawPoint>& s)
{
using Unit = TCoord<RawPoint>;
- auto x = PointLike::x(p), y = PointLike::y(p);
- auto x1 = PointLike::x(s.first()), y1 = PointLike::y(s.first());
- auto x2 = PointLike::x(s.second()), y2 = PointLike::y(s.second());
+ auto x = pointlike::x(p), y = pointlike::y(p);
+ auto x1 = pointlike::x(s.first()), y1 = pointlike::y(s.first());
+ auto x2 = pointlike::x(s.second()), y2 = pointlike::y(s.second());
TCoord<RawPoint> ret;
@@ -252,36 +249,36 @@ struct PointLike {
return {ret, true};
}
-};
+}
template<class RawPoint>
TCoord<RawPoint> _Box<RawPoint>::width() const BP2D_NOEXCEPT
{
- return PointLike::x(maxCorner()) - PointLike::x(minCorner());
+ return pointlike::x(maxCorner()) - pointlike::x(minCorner());
}
template<class RawPoint>
TCoord<RawPoint> _Box<RawPoint>::height() const BP2D_NOEXCEPT
{
- return PointLike::y(maxCorner()) - PointLike::y(minCorner());
+ return pointlike::y(maxCorner()) - pointlike::y(minCorner());
}
template<class RawPoint>
-TCoord<RawPoint> getX(const RawPoint& p) { return PointLike::x<RawPoint>(p); }
+TCoord<RawPoint> getX(const RawPoint& p) { return pointlike::x<RawPoint>(p); }
template<class RawPoint>
-TCoord<RawPoint> getY(const RawPoint& p) { return PointLike::y<RawPoint>(p); }
+TCoord<RawPoint> getY(const RawPoint& p) { return pointlike::y<RawPoint>(p); }
template<class RawPoint>
void setX(RawPoint& p, const TCoord<RawPoint>& val)
{
- PointLike::x<RawPoint>(p) = val;
+ pointlike::x<RawPoint>(p) = val;
}
template<class RawPoint>
void setY(RawPoint& p, const TCoord<RawPoint>& val)
{
- PointLike::y<RawPoint>(p) = val;
+ pointlike::y<RawPoint>(p) = val;
}
template<class RawPoint>
@@ -303,7 +300,7 @@ inline Radians _Segment<RawPoint>::angleToXaxis() const
template<class RawPoint>
inline double _Segment<RawPoint>::length()
{
- return PointLike::distance(first(), second());
+ return pointlike::distance(first(), second());
}
template<class RawPoint>
@@ -313,9 +310,9 @@ inline RawPoint _Box<RawPoint>::center() const BP2D_NOEXCEPT {
using Coord = TCoord<RawPoint>;
- RawPoint ret = {
- static_cast<Coord>( std::round((getX(minc) + getX(maxc))/2.0) ),
- static_cast<Coord>( std::round((getY(minc) + getY(maxc))/2.0) )
+ RawPoint ret = { // No rounding here, we dont know if these are int coords
+ static_cast<Coord>( (getX(minc) + getX(maxc))/2.0 ),
+ static_cast<Coord>( (getY(minc) + getY(maxc))/2.0 )
};
return ret;
@@ -356,124 +353,125 @@ enum class Formats {
// This struct serves as a namespace. The only difference is that it can be
// used in friend declarations and can be aliased at class scope.
-struct ShapeLike {
+namespace shapelike {
template<class RawShape>
- using Shapes = std::vector<RawShape>;
+ using Shapes = TMultiShape<RawShape>;
template<class RawShape>
- static RawShape create(const TContour<RawShape>& contour,
+ inline RawShape create(const TContour<RawShape>& contour,
const THolesContainer<RawShape>& holes)
{
return RawShape(contour, holes);
}
template<class RawShape>
- static RawShape create(TContour<RawShape>&& contour,
+ inline RawShape create(TContour<RawShape>&& contour,
THolesContainer<RawShape>&& holes)
{
return RawShape(contour, holes);
}
template<class RawShape>
- static RawShape create(const TContour<RawShape>& contour)
+ inline RawShape create(const TContour<RawShape>& contour)
{
return create<RawShape>(contour, {});
}
template<class RawShape>
- static RawShape create(TContour<RawShape>&& contour)
+ inline RawShape create(TContour<RawShape>&& contour)
{
return create<RawShape>(contour, {});
}
template<class RawShape>
- static THolesContainer<RawShape>& holes(RawShape& /*sh*/)
+ inline THolesContainer<RawShape>& holes(RawShape& /*sh*/)
{
static THolesContainer<RawShape> empty;
return empty;
}
template<class RawShape>
- static const THolesContainer<RawShape>& holes(const RawShape& /*sh*/)
+ inline const THolesContainer<RawShape>& holes(const RawShape& /*sh*/)
{
static THolesContainer<RawShape> empty;
return empty;
}
template<class RawShape>
- static TContour<RawShape>& getHole(RawShape& sh, unsigned long idx)
+ inline TContour<RawShape>& getHole(RawShape& sh, unsigned long idx)
{
return holes(sh)[idx];
}
template<class RawShape>
- static const TContour<RawShape>& getHole(const RawShape& sh,
+ inline const TContour<RawShape>& getHole(const RawShape& sh,
unsigned long idx)
{
return holes(sh)[idx];
}
template<class RawShape>
- static size_t holeCount(const RawShape& sh)
+ inline size_t holeCount(const RawShape& sh)
{
return holes(sh).size();
}
template<class RawShape>
- static TContour<RawShape>& getContour(RawShape& sh)
+ inline TContour<RawShape>& getContour(RawShape& sh)
{
return sh;
}
template<class RawShape>
- static const TContour<RawShape>& getContour(const RawShape& sh)
+ inline const TContour<RawShape>& getContour(const RawShape& sh)
{
return sh;
}
// Optional, does nothing by default
template<class RawShape>
- static void reserve(RawShape& /*sh*/, size_t /*vertex_capacity*/) {}
+ inline void reserve(RawShape& /*sh*/, size_t /*vertex_capacity*/) {}
template<class RawShape, class...Args>
- static void addVertex(RawShape& sh, Args...args)
+ inline void addVertex(RawShape& sh, Args...args)
{
return getContour(sh).emplace_back(std::forward<Args>(args)...);
}
template<class RawShape>
- static TVertexIterator<RawShape> begin(RawShape& sh)
+ inline typename TContour<RawShape>::iterator begin(RawShape& sh)
{
- return sh.begin();
+ return getContour(sh).begin();
}
template<class RawShape>
- static TVertexIterator<RawShape> end(RawShape& sh)
+ inline typename TContour<RawShape>::iterator end(RawShape& sh)
{
- return sh.end();
+ return getContour(sh).end();
}
template<class RawShape>
- static TVertexConstIterator<RawShape> cbegin(const RawShape& sh)
+ inline typename TContour<RawShape>::const_iterator
+ cbegin(const RawShape& sh)
{
- return sh.cbegin();
+ return getContour(sh).cbegin();
}
template<class RawShape>
- static TVertexConstIterator<RawShape> cend(const RawShape& sh)
+ inline typename TContour<RawShape>::const_iterator cend(const RawShape& sh)
{
- return sh.cend();
+ return getContour(sh).cend();
}
template<class RawShape>
- static std::string toString(const RawShape& /*sh*/)
+ inline std::string toString(const RawShape& /*sh*/)
{
return "";
}
template<Formats, class RawShape>
- static std::string serialize(const RawShape& /*sh*/, double /*scale*/=1)
+ inline std::string serialize(const RawShape& /*sh*/, double /*scale*/=1)
{
static_assert(always_false<RawShape>::value,
"ShapeLike::serialize() unimplemented!");
@@ -481,14 +479,14 @@ struct ShapeLike {
}
template<Formats, class RawShape>
- static void unserialize(RawShape& /*sh*/, const std::string& /*str*/)
+ inline void unserialize(RawShape& /*sh*/, const std::string& /*str*/)
{
static_assert(always_false<RawShape>::value,
"ShapeLike::unserialize() unimplemented!");
}
template<class RawShape>
- static double area(const RawShape& /*sh*/)
+ inline double area(const RawShape& /*sh*/, const PolygonTag&)
{
static_assert(always_false<RawShape>::value,
"ShapeLike::area() unimplemented!");
@@ -496,7 +494,7 @@ struct ShapeLike {
}
template<class RawShape>
- static bool intersects(const RawShape& /*sh*/, const RawShape& /*sh*/)
+ inline bool intersects(const RawShape& /*sh*/, const RawShape& /*sh*/)
{
static_assert(always_false<RawShape>::value,
"ShapeLike::intersects() unimplemented!");
@@ -504,7 +502,7 @@ struct ShapeLike {
}
template<class RawShape>
- static bool isInside(const TPoint<RawShape>& /*point*/,
+ inline bool isInside(const TPoint<RawShape>& /*point*/,
const RawShape& /*shape*/)
{
static_assert(always_false<RawShape>::value,
@@ -513,7 +511,7 @@ struct ShapeLike {
}
template<class RawShape>
- static bool isInside(const RawShape& /*shape*/,
+ inline bool isInside(const RawShape& /*shape*/,
const RawShape& /*shape*/)
{
static_assert(always_false<RawShape>::value,
@@ -522,7 +520,7 @@ struct ShapeLike {
}
template<class RawShape>
- static bool touches( const RawShape& /*shape*/,
+ inline bool touches( const RawShape& /*shape*/,
const RawShape& /*shape*/)
{
static_assert(always_false<RawShape>::value,
@@ -531,7 +529,7 @@ struct ShapeLike {
}
template<class RawShape>
- static bool touches( const TPoint<RawShape>& /*point*/,
+ inline bool touches( const TPoint<RawShape>& /*point*/,
const RawShape& /*shape*/)
{
static_assert(always_false<RawShape>::value,
@@ -540,64 +538,66 @@ struct ShapeLike {
}
template<class RawShape>
- static _Box<TPoint<RawShape>> boundingBox(const RawShape& /*sh*/)
+ inline _Box<TPoint<RawShape>> boundingBox(const RawShape& /*sh*/,
+ const PolygonTag&)
{
static_assert(always_false<RawShape>::value,
"ShapeLike::boundingBox(shape) unimplemented!");
}
- template<class RawShape>
- static _Box<TPoint<RawShape>> boundingBox(const Shapes<RawShape>& /*sh*/)
+ template<class RawShapes>
+ inline _Box<TPoint<typename RawShapes::value_type>>
+ boundingBox(const RawShapes& /*sh*/, const MultiPolygonTag&)
{
- static_assert(always_false<RawShape>::value,
+ static_assert(always_false<RawShapes>::value,
"ShapeLike::boundingBox(shapes) unimplemented!");
}
template<class RawShape>
- static RawShape convexHull(const RawShape& /*sh*/)
+ inline RawShape convexHull(const RawShape& /*sh*/, const PolygonTag&)
{
static_assert(always_false<RawShape>::value,
"ShapeLike::convexHull(shape) unimplemented!");
return RawShape();
}
- template<class RawShape>
- static RawShape convexHull(const Shapes<RawShape>& /*sh*/)
+ template<class RawShapes>
+ inline typename RawShapes::value_type
+ convexHull(const RawShapes& /*sh*/, const MultiPolygonTag&)
{
- static_assert(always_false<RawShape>::value,
+ static_assert(always_false<RawShapes>::value,
"ShapeLike::convexHull(shapes) unimplemented!");
- return RawShape();
+ return typename RawShapes::value_type();
}
template<class RawShape>
- static void rotate(RawShape& /*sh*/, const Radians& /*rads*/)
+ inline void rotate(RawShape& /*sh*/, const Radians& /*rads*/)
{
static_assert(always_false<RawShape>::value,
"ShapeLike::rotate() unimplemented!");
}
template<class RawShape, class RawPoint>
- static void translate(RawShape& /*sh*/, const RawPoint& /*offs*/)
+ inline void translate(RawShape& /*sh*/, const RawPoint& /*offs*/)
{
static_assert(always_false<RawShape>::value,
"ShapeLike::translate() unimplemented!");
}
template<class RawShape>
- static void offset(RawShape& /*sh*/, TCoord<TPoint<RawShape>> /*distance*/)
+ inline void offset(RawShape& /*sh*/, TCoord<TPoint<RawShape>> /*distance*/)
{
- static_assert(always_false<RawShape>::value,
- "ShapeLike::offset() unimplemented!");
+ dout() << "The current geometry backend does not support offsetting!\n";
}
template<class RawShape>
- static std::pair<bool, std::string> isValid(const RawShape& /*sh*/)
+ inline std::pair<bool, std::string> isValid(const RawShape& /*sh*/)
{
return {false, "ShapeLike::isValid() unimplemented!"};
}
template<class RawShape>
- static inline bool isConvex(const TContour<RawShape>& sh)
+ inline bool isConvex(const TContour<RawShape>& sh)
{
using Vertex = TPoint<RawShape>;
auto first = sh.begin();
@@ -633,43 +633,55 @@ struct ShapeLike {
// No need to implement these
// *************************************************************************
- template<class RawShape>
- static inline _Box<TPoint<RawShape>> boundingBox(
- const _Box<TPoint<RawShape>>& box)
+ template<class Box>
+ inline Box boundingBox(const Box& box, const BoxTag& )
{
return box;
}
- template<class RawShape>
- static inline _Box<TPoint<RawShape>> boundingBox(
- const _Circle<TPoint<RawShape>>& circ)
+ template<class Circle>
+ inline _Box<typename Circle::PointType> boundingBox(
+ const Circle& circ, const CircleTag&)
{
- using Coord = TCoord<TPoint<RawShape>>;
- TPoint<RawShape> pmin = {
+ using Point = typename Circle::PointType;
+ using Coord = TCoord<Point>;
+ Point pmin = {
static_cast<Coord>(getX(circ.center()) - circ.radius()),
static_cast<Coord>(getY(circ.center()) - circ.radius()) };
- TPoint<RawShape> pmax = {
+ Point pmax = {
static_cast<Coord>(getX(circ.center()) + circ.radius()),
static_cast<Coord>(getY(circ.center()) + circ.radius()) };
return {pmin, pmax};
}
- template<class RawShape>
- static inline double area(const _Box<TPoint<RawShape>>& box)
+ template<class S> // Dispatch function
+ inline _Box<TPoint<S>> boundingBox(const S& sh)
{
- return static_cast<double>(box.width() * box.height());
+ return boundingBox(sh, Tag<S>() );
}
- template<class RawShape>
- static inline double area(const _Circle<TPoint<RawShape>>& circ)
+ template<class Box>
+ inline double area(const Box& box, const BoxTag& )
+ {
+ return box.area();
+ }
+
+ template<class Circle>
+ inline double area(const Circle& circ, const CircleTag& )
{
return circ.area();
}
+ template<class RawShape> // Dispatching function
+ inline double area(const RawShape& sh)
+ {
+ return area(sh, Tag<RawShape>());
+ }
+
template<class RawShape>
- static inline double area(const Shapes<RawShape>& shapes)
+ inline double area(const Shapes<RawShape>& shapes)
{
return std::accumulate(shapes.begin(), shapes.end(), 0.0,
[](double a, const RawShape& b) {
@@ -678,14 +690,21 @@ struct ShapeLike {
}
template<class RawShape>
- static bool isInside(const TPoint<RawShape>& point,
+ inline auto convexHull(const RawShape& sh)
+ -> decltype(convexHull(sh, Tag<RawShape>())) // TODO: C++14 could deduce
+ {
+ return convexHull(sh, Tag<RawShape>());
+ }
+
+ template<class RawShape>
+ inline bool isInside(const TPoint<RawShape>& point,
const _Circle<TPoint<RawShape>>& circ)
{
- return PointLike::distance(point, circ.center()) < circ.radius();
+ return pointlike::distance(point, circ.center()) < circ.radius();
}
template<class RawShape>
- static bool isInside(const TPoint<RawShape>& point,
+ inline bool isInside(const TPoint<RawShape>& point,
const _Box<TPoint<RawShape>>& box)
{
auto px = getX(point);
@@ -699,7 +718,7 @@ struct ShapeLike {
}
template<class RawShape>
- static bool isInside(const RawShape& sh,
+ inline bool isInside(const RawShape& sh,
const _Circle<TPoint<RawShape>>& circ)
{
return std::all_of(cbegin(sh), cend(sh),
@@ -709,7 +728,7 @@ struct ShapeLike {
}
template<class RawShape>
- static bool isInside(const _Box<TPoint<RawShape>>& box,
+ inline bool isInside(const _Box<TPoint<RawShape>>& box,
const _Circle<TPoint<RawShape>>& circ)
{
return isInside<RawShape>(box.minCorner(), circ) &&
@@ -717,7 +736,7 @@ struct ShapeLike {
}
template<class RawShape>
- static bool isInside(const _Box<TPoint<RawShape>>& ibb,
+ inline bool isInside(const _Box<TPoint<RawShape>>& ibb,
const _Box<TPoint<RawShape>>& box)
{
auto iminX = getX(ibb.minCorner());
@@ -734,31 +753,31 @@ struct ShapeLike {
}
template<class RawShape> // Potential O(1) implementation may exist
- static inline TPoint<RawShape>& vertex(RawShape& sh, unsigned long idx)
+ inline TPoint<RawShape>& vertex(RawShape& sh, unsigned long idx)
{
return *(begin(sh) + idx);
}
template<class RawShape> // Potential O(1) implementation may exist
- static inline const TPoint<RawShape>& vertex(const RawShape& sh,
+ inline const TPoint<RawShape>& vertex(const RawShape& sh,
unsigned long idx)
{
return *(cbegin(sh) + idx);
}
template<class RawShape>
- static inline size_t contourVertexCount(const RawShape& sh)
+ inline size_t contourVertexCount(const RawShape& sh)
{
return cend(sh) - cbegin(sh);
}
template<class RawShape, class Fn>
- static inline void foreachContourVertex(RawShape& sh, Fn fn) {
+ inline void foreachContourVertex(RawShape& sh, Fn fn) {
for(auto it = begin(sh); it != end(sh); ++it) fn(*it);
}
template<class RawShape, class Fn>
- static inline void foreachHoleVertex(RawShape& sh, Fn fn) {
+ inline void foreachHoleVertex(RawShape& sh, Fn fn) {
for(int i = 0; i < holeCount(sh); ++i) {
auto& h = getHole(sh, i);
for(auto it = begin(h); it != end(h); ++it) fn(*it);
@@ -766,12 +785,12 @@ struct ShapeLike {
}
template<class RawShape, class Fn>
- static inline void foreachContourVertex(const RawShape& sh, Fn fn) {
+ inline void foreachContourVertex(const RawShape& sh, Fn fn) {
for(auto it = cbegin(sh); it != cend(sh); ++it) fn(*it);
}
template<class RawShape, class Fn>
- static inline void foreachHoleVertex(const RawShape& sh, Fn fn) {
+ inline void foreachHoleVertex(const RawShape& sh, Fn fn) {
for(int i = 0; i < holeCount(sh); ++i) {
auto& h = getHole(sh, i);
for(auto it = cbegin(h); it != cend(h); ++it) fn(*it);
@@ -779,18 +798,27 @@ struct ShapeLike {
}
template<class RawShape, class Fn>
- static inline void foreachVertex(RawShape& sh, Fn fn) {
+ inline void foreachVertex(RawShape& sh, Fn fn) {
foreachContourVertex(sh, fn);
foreachHoleVertex(sh, fn);
}
template<class RawShape, class Fn>
- static inline void foreachVertex(const RawShape& sh, Fn fn) {
+ inline void foreachVertex(const RawShape& sh, Fn fn) {
foreachContourVertex(sh, fn);
foreachHoleVertex(sh, fn);
}
+}
-};
+#define DECLARE_MAIN_TYPES(T) \
+ using Polygon = T; \
+ using Point = TPoint<T>; \
+ using Coord = TCoord<Point>; \
+ using Contour = TContour<T>; \
+ using Box = _Box<Point>; \
+ using Circle = _Circle<Point>; \
+ using Segment = _Segment<Point>; \
+ using Polygons = TMultiShape<T>
}
diff --git a/xs/src/libnest2d/libnest2d/geometry_traits_nfp.hpp b/xs/src/libnest2d/libnest2d/geometry_traits_nfp.hpp
index 90cf21be5..2982454cd 100644
--- a/xs/src/libnest2d/libnest2d/geometry_traits_nfp.hpp
+++ b/xs/src/libnest2d/libnest2d/geometry_traits_nfp.hpp
@@ -9,6 +9,27 @@
namespace libnest2d {
+namespace __nfp {
+// Do not specialize this...
+template<class RawShape>
+inline bool _vsort(const TPoint<RawShape>& v1, const TPoint<RawShape>& v2)
+{
+ using Coord = TCoord<TPoint<RawShape>>;
+ Coord &&x1 = getX(v1), &&x2 = getX(v2), &&y1 = getY(v1), &&y2 = getY(v2);
+ auto diff = y1 - y2;
+ if(std::abs(diff) <= std::numeric_limits<Coord>::epsilon())
+ return x1 < x2;
+
+ return diff < 0;
+}
+}
+
+/// A collection of static methods for handling the no fit polygon creation.
+namespace nfp {
+
+//namespace sl = shapelike;
+//namespace pl = pointlike;
+
/// The complexity level of a polygon that an NFP implementation can handle.
enum class NfpLevel: unsigned {
CONVEX_ONLY,
@@ -18,12 +39,17 @@ enum class NfpLevel: unsigned {
BOTH_CONCAVE_WITH_HOLES
};
-/// A collection of static methods for handling the no fit polygon creation.
-struct Nfp {
+template<class RawShape>
+using NfpResult = std::pair<RawShape, TPoint<RawShape>>;
+
+template<class RawShape> struct MaxNfpLevel {
+ static const BP2D_CONSTEXPR NfpLevel value = NfpLevel::CONVEX_ONLY;
+};
+
// Shorthand for a pile of polygons
template<class RawShape>
-using Shapes = typename ShapeLike::Shapes<RawShape>;
+using Shapes = TMultiShape<RawShape>;
/**
* Merge a bunch of polygons with the specified additional polygon.
@@ -36,10 +62,10 @@ using Shapes = typename ShapeLike::Shapes<RawShape>;
* mostly it will be a set containing only one big polygon but if the input
* polygons are disjuct than the resulting set will contain more polygons.
*/
-template<class RawShape>
-static Shapes<RawShape> merge(const Shapes<RawShape>& /*shc*/)
+template<class RawShapes>
+inline RawShapes merge(const RawShapes& /*shc*/)
{
- static_assert(always_false<RawShape>::value,
+ static_assert(always_false<RawShapes>::value,
"Nfp::merge(shapes, shape) unimplemented!");
}
@@ -55,25 +81,12 @@ static Shapes<RawShape> merge(const Shapes<RawShape>& /*shc*/)
* polygons are disjuct than the resulting set will contain more polygons.
*/
template<class RawShape>
-static Shapes<RawShape> merge(const Shapes<RawShape>& shc,
- const RawShape& sh)
+inline TMultiShape<RawShape> merge(const TMultiShape<RawShape>& shc,
+ const RawShape& sh)
{
- auto m = merge(shc);
+ auto m = nfp::merge(shc);
m.push_back(sh);
- return merge(m);
-}
-
-/**
- * A method to get a vertex from a polygon that always maintains a relative
- * position to the coordinate system: It is always the rightmost top vertex.
- *
- * This way it does not matter in what order the vertices are stored, the
- * reference will be always the same for the same polygon.
- */
-template<class RawShape>
-inline static TPoint<RawShape> referenceVertex(const RawShape& sh)
-{
- return rightmostUpVertex(sh);
+ return nfp::merge(m);
}
/**
@@ -82,14 +95,14 @@ inline static TPoint<RawShape> referenceVertex(const RawShape& sh)
* the result will be the leftmost (with the highest X coordinate).
*/
template<class RawShape>
-static TPoint<RawShape> leftmostDownVertex(const RawShape& sh)
+inline TPoint<RawShape> leftmostDownVertex(const RawShape& sh)
{
// find min x and min y vertex
- auto it = std::min_element(ShapeLike::cbegin(sh), ShapeLike::cend(sh),
- _vsort<RawShape>);
+ auto it = std::min_element(shapelike::cbegin(sh), shapelike::cend(sh),
+ __nfp::_vsort<RawShape>);
- return *it;
+ return it == shapelike::cend(sh) ? TPoint<RawShape>() : *it;;
}
/**
@@ -98,26 +111,27 @@ static TPoint<RawShape> leftmostDownVertex(const RawShape& sh)
* the result will be the rightmost (with the lowest X coordinate).
*/
template<class RawShape>
-static TPoint<RawShape> rightmostUpVertex(const RawShape& sh)
+TPoint<RawShape> rightmostUpVertex(const RawShape& sh)
{
// find max x and max y vertex
- auto it = std::max_element(ShapeLike::cbegin(sh), ShapeLike::cend(sh),
- _vsort<RawShape>);
+ auto it = std::max_element(shapelike::cbegin(sh), shapelike::cend(sh),
+ __nfp::_vsort<RawShape>);
- return *it;
+ return it == shapelike::cend(sh) ? TPoint<RawShape>() : *it;
}
+/**
+ * A method to get a vertex from a polygon that always maintains a relative
+ * position to the coordinate system: It is always the rightmost top vertex.
+ *
+ * This way it does not matter in what order the vertices are stored, the
+ * reference will be always the same for the same polygon.
+ */
template<class RawShape>
-using NfpResult = std::pair<RawShape, TPoint<RawShape>>;
-
-/// Helper function to get the NFP
-template<NfpLevel nfptype, class RawShape>
-static NfpResult<RawShape> noFitPolygon(const RawShape& sh,
- const RawShape& other)
+inline TPoint<RawShape> referenceVertex(const RawShape& sh)
{
- NfpImpl<RawShape, nfptype> nfp;
- return nfp(sh, other);
+ return rightmostUpVertex(sh);
}
/**
@@ -139,11 +153,11 @@ static NfpResult<RawShape> noFitPolygon(const RawShape& sh,
*
*/
template<class RawShape>
-static NfpResult<RawShape> nfpConvexOnly(const RawShape& sh,
+inline NfpResult<RawShape> nfpConvexOnly(const RawShape& sh,
const RawShape& other)
{
using Vertex = TPoint<RawShape>; using Edge = _Segment<Vertex>;
- using sl = ShapeLike;
+ namespace sl = shapelike;
RawShape rsh; // Final nfp placeholder
Vertex top_nfp;
@@ -187,7 +201,7 @@ static NfpResult<RawShape> nfpConvexOnly(const RawShape& sh,
sl::addVertex(rsh, edgelist.front().second());
// Sorting function for the nfp reference vertex search
- auto& cmp = _vsort<RawShape>;
+ auto& cmp = __nfp::_vsort<RawShape>;
// the reference (rightmost top) vertex so far
top_nfp = *std::max_element(sl::cbegin(rsh), sl::cend(rsh), cmp );
@@ -214,7 +228,7 @@ static NfpResult<RawShape> nfpConvexOnly(const RawShape& sh,
}
template<class RawShape>
-static NfpResult<RawShape> nfpSimpleSimple(const RawShape& cstationary,
+NfpResult<RawShape> nfpSimpleSimple(const RawShape& cstationary,
const RawShape& cother)
{
@@ -233,7 +247,7 @@ static NfpResult<RawShape> nfpSimpleSimple(const RawShape& cstationary,
using Vertex = TPoint<RawShape>;
using Coord = TCoord<Vertex>;
using Edge = _Segment<Vertex>;
- using sl = ShapeLike;
+ namespace sl = shapelike;
using std::signbit;
using std::sort;
using std::vector;
@@ -528,27 +542,16 @@ struct NfpImpl {
}
};
-template<class RawShape> struct MaxNfpLevel {
- static const BP2D_CONSTEXPR NfpLevel value = NfpLevel::CONVEX_ONLY;
-};
-
-private:
-
-// Do not specialize this...
-template<class RawShape>
-static inline bool _vsort(const TPoint<RawShape>& v1,
- const TPoint<RawShape>& v2)
+/// Helper function to get the NFP
+template<NfpLevel nfptype, class RawShape>
+inline NfpResult<RawShape> noFitPolygon(const RawShape& sh,
+ const RawShape& other)
{
- using Coord = TCoord<TPoint<RawShape>>;
- Coord &&x1 = getX(v1), &&x2 = getX(v2), &&y1 = getY(v1), &&y2 = getY(v2);
- auto diff = y1 - y2;
- if(std::abs(diff) <= std::numeric_limits<Coord>::epsilon())
- return x1 < x2;
-
- return diff < 0;
+ NfpImpl<RawShape, nfptype> nfps;
+ return nfps(sh, other);
}
-};
+}
}
diff --git a/xs/src/libnest2d/libnest2d/libnest2d.hpp b/xs/src/libnest2d/libnest2d/libnest2d.hpp
index 7f23de358..4d1e62f99 100644
--- a/xs/src/libnest2d/libnest2d/libnest2d.hpp
+++ b/xs/src/libnest2d/libnest2d/libnest2d.hpp
@@ -9,10 +9,12 @@
#include <functional>
#include "geometry_traits.hpp"
-#include "optimizer.hpp"
namespace libnest2d {
+namespace sl = shapelike;
+namespace pl = pointlike;
+
/**
* \brief An item to be placed on a bin.
*
@@ -28,7 +30,8 @@ class _Item {
using Coord = TCoord<TPoint<RawShape>>;
using Vertex = TPoint<RawShape>;
using Box = _Box<Vertex>;
- using sl = ShapeLike;
+
+ using VertexConstIterator = typename TContour<RawShape>::const_iterator;
// The original shape that gets encapsulated.
RawShape sh_;
@@ -38,7 +41,7 @@ class _Item {
Radians rotation_;
Coord offset_distance_;
- // Info about whether the tranformations will have to take place
+ // Info about whether the transformations will have to take place
// This is needed because if floating point is used, it is hard to say
// that a zero angle is not a rotation because of testing for equality.
bool has_rotation_ = false, has_translation_ = false, has_offset_ = false;
@@ -58,12 +61,12 @@ class _Item {
};
mutable Convexity convexity_ = Convexity::UNCHECKED;
- mutable TVertexConstIterator<RawShape> rmt_; // rightmost top vertex
- mutable TVertexConstIterator<RawShape> lmb_; // leftmost bottom vertex
+ mutable VertexConstIterator rmt_; // rightmost top vertex
+ mutable VertexConstIterator lmb_; // leftmost bottom vertex
mutable bool rmt_valid_ = false, lmb_valid_ = false;
mutable struct BBCache {
- Box bb; bool valid; Vertex tr;
- BBCache(): valid(false), tr(0, 0) {}
+ Box bb; bool valid;
+ BBCache(): valid(false) {}
} bb_cache_;
public:
@@ -80,7 +83,7 @@ public:
* supports. Giving out a non const iterator would make it impossible to
* perform correct cache invalidation.
*/
- using Iterator = TVertexConstIterator<RawShape>;
+ using Iterator = VertexConstIterator;
/**
* @brief Get the orientation of the polygon.
@@ -109,7 +112,7 @@ public:
explicit inline _Item(RawShape&& sh): sh_(std::move(sh)) {}
/**
- * @brief Create an item from an initilizer list.
+ * @brief Create an item from an initializer list.
* @param il The initializer list of vertices.
*/
inline _Item(const std::initializer_list< Vertex >& il):
@@ -159,7 +162,7 @@ public:
}
/**
- * @brief Get a copy of an outer vertex whithin the carried shape.
+ * @brief Get a copy of an outer vertex within the carried shape.
*
* Note that the vertex considered here is taken from the original shape
* that this item is constructed from. This means that no transformation is
@@ -244,7 +247,7 @@ public:
* @param p
* @return
*/
- inline bool isPointInside(const Vertex& p) const
+ inline bool isInside(const Vertex& p) const
{
return sl::isInside(p, transformedShape());
}
@@ -307,7 +310,7 @@ public:
{
if(translation_ != tr) {
translation_ = tr; has_translation_ = true; tr_cache_valid_ = false;
- bb_cache_.valid = false;
+ //bb_cache_.valid = false;
}
}
@@ -342,13 +345,19 @@ public:
inline Box boundingBox() const {
if(!bb_cache_.valid) {
- bb_cache_.bb = sl::boundingBox(transformedShape());
- bb_cache_.tr = {0, 0};
+ if(!has_rotation_)
+ bb_cache_.bb = sl::boundingBox(offsettedShape());
+ else {
+ // TODO make sure this works
+ auto rotsh = offsettedShape();
+ sl::rotate(rotsh, rotation_);
+ bb_cache_.bb = sl::boundingBox(rotsh);
+ }
bb_cache_.valid = true;
}
- auto &bb = bb_cache_.bb; auto &tr = bb_cache_.tr;
- return {bb.minCorner() + tr, bb.maxCorner() + tr};
+ auto &bb = bb_cache_.bb; auto &tr = translation_;
+ return {bb.minCorner() + tr, bb.maxCorner() + tr };
}
inline Vertex referenceVertex() const {
@@ -438,7 +447,7 @@ public:
inline _Rectangle(Unit width, Unit height,
// disable this ctor if o != CLOCKWISE
enable_if_t< o == TO::CLOCKWISE, int> = 0 ):
- _Item<RawShape>( ShapeLike::create<RawShape>( {
+ _Item<RawShape>( sl::create<RawShape>( {
{0, 0},
{0, height},
{width, height},
@@ -452,7 +461,7 @@ public:
inline _Rectangle(Unit width, Unit height,
// disable this ctor if o != COUNTER_CLOCKWISE
enable_if_t< o == TO::COUNTER_CLOCKWISE, int> = 0 ):
- _Item<RawShape>( ShapeLike::create<RawShape>( {
+ _Item<RawShape>( sl::create<RawShape>( {
{0, 0},
{width, 0},
{width, height},
@@ -473,18 +482,38 @@ public:
template<class RawShape>
inline bool _Item<RawShape>::isInside(const _Box<TPoint<RawShape>>& box) const {
- return ShapeLike::isInside<RawShape>(boundingBox(), box);
+ return sl::isInside<RawShape>(boundingBox(), box);
}
template<class RawShape> inline bool
_Item<RawShape>::isInside(const _Circle<TPoint<RawShape>>& circ) const {
- return ShapeLike::isInside<RawShape>(transformedShape(), circ);
+ return sl::isInside<RawShape>(transformedShape(), circ);
+}
+
+
+template<class I> using _ItemRef = std::reference_wrapper<I>;
+template<class I> using _ItemGroup = std::vector<_ItemRef<I>>;
+
+template<class Iterator>
+struct ConstItemRange {
+ Iterator from;
+ Iterator to;
+ bool valid = false;
+
+ ConstItemRange() = default;
+ ConstItemRange(Iterator f, Iterator t): from(f), to(t), valid(true) {}
+};
+
+template<class Container>
+inline ConstItemRange<typename Container::const_iterator>
+rem(typename Container::const_iterator it, const Container& cont) {
+ return {std::next(it), cont.end()};
}
/**
* \brief A wrapper interface (trait) class for any placement strategy provider.
*
- * If a client want's to use its own placement algorithm, all it has to do is to
+ * If a client wants to use its own placement algorithm, all it has to do is to
* specialize this class template and define all the ten methods it has. It can
* use the strategies::PlacerBoilerplace class for creating a new placement
* strategy where only the constructor and the trypack method has to be provided
@@ -515,8 +544,9 @@ public:
*/
using PackResult = typename PlacementStrategy::PackResult;
- using ItemRef = std::reference_wrapper<Item>;
- using ItemGroup = std::vector<ItemRef>;
+ using ItemRef = _ItemRef<Item>;
+ using ItemGroup = _ItemGroup<Item>;
+ using DefaultIterator = typename ItemGroup::const_iterator;
/**
* @brief Constructor taking the bin and an optional configuration.
@@ -536,29 +566,32 @@ public:
* Note that it depends on the particular placer implementation how it
* reacts to config changes in the middle of a calculation.
*
- * @param config The configuration object defined by the placement startegy.
+ * @param config The configuration object defined by the placement strategy.
*/
inline void configure(const Config& config) { impl_.configure(config); }
/**
- * @brief A method that tries to pack an item and returns an object
- * describing the pack result.
- *
- * The result can be casted to bool and used as an argument to the accept
- * method to accept a succesfully packed item. This way the next packing
- * will consider the accepted item as well. The PackResult should carry the
- * transformation info so that if the tried item is later modified or tried
- * multiple times, the result object should set it to the originally
- * determied position. An implementation can be found in the
- * strategies::PlacerBoilerplate::PackResult class.
+ * Try to pack an item with a result object that contains the packing
+ * information for later accepting it.
*
- * @param item Ithe item to be packed.
- * @return The PackResult object that can be implicitly casted to bool.
+ * \param item_store A container of items that are intended to be packed
+ * later. Can be used by the placer to switch tactics. When it's knows that
+ * many items will come a greedy strategy may not be the best.
+ * \param from The iterator to the item from which the packing should start,
+ * including the pointed item
+ * \param count How many items should be packed. If the value is 1, than
+ * just the item pointed to by "from" argument should be packed.
*/
- inline PackResult trypack(Item& item) { return impl_.trypack(item); }
+ template<class Iter = DefaultIterator>
+ inline PackResult trypack(
+ Item& item,
+ const ConstItemRange<Iter>& remaining = ConstItemRange<Iter>())
+ {
+ return impl_.trypack(item, remaining);
+ }
/**
- * @brief A method to accept a previously tried item.
+ * @brief A method to accept a previously tried item (or items).
*
* If the pack result is a failure the method should ignore it.
* @param r The result of a previous trypack call.
@@ -566,19 +599,25 @@ public:
inline void accept(PackResult& r) { impl_.accept(r); }
/**
- * @brief pack Try to pack an item and immediately accept it on success.
+ * @brief pack Try to pack and immediately accept it on success.
*
* A default implementation would be to call
- * { auto&& r = trypack(item); accept(r); return r; } but we should let the
+ * { auto&& r = trypack(...); accept(r); return r; } but we should let the
* implementor of the placement strategy to harvest any optimizations from
- * the absence of an intermadiate step. The above version can still be used
+ * the absence of an intermediate step. The above version can still be used
* in the implementation.
*
* @param item The item to pack.
* @return Returns true if the item was packed or false if it could not be
* packed.
*/
- inline bool pack(Item& item) { return impl_.pack(item); }
+ template<class Range = ConstItemRange<DefaultIterator>>
+ inline bool pack(
+ Item& item,
+ const Range& remaining = Range())
+ {
+ return impl_.pack(item, remaining);
+ }
/// Unpack the last element (remove it from the list of packed items).
inline void unpackLast() { impl_.unpackLast(); }
@@ -597,13 +636,6 @@ public:
inline double filledArea() const { return impl_.filledArea(); }
-#ifndef NDEBUG
- inline auto getDebugItems() -> decltype(impl_.debug_items_)&
- {
- return impl_.debug_items_;
- }
-#endif
-
};
// The progress function will be called with the number of placed items
@@ -628,15 +660,15 @@ public:
* Note that it depends on the particular placer implementation how it
* reacts to config changes in the middle of a calculation.
*
- * @param config The configuration object defined by the selection startegy.
+ * @param config The configuration object defined by the selection strategy.
*/
inline void configure(const Config& config) {
impl_.configure(config);
}
/**
- * @brief A function callback which should be called whenewer an item or
- * a group of items where succesfully packed.
+ * @brief A function callback which should be called whenever an item or
+ * a group of items where successfully packed.
* @param fn A function callback object taking one unsigned integer as the
* number of the remaining items to pack.
*/
@@ -649,7 +681,7 @@ public:
* placer compatible with the PlacementStrategyLike interface.
*
* \param first, last The first and last iterator if the input sequence. It
- * can be only an iterator of a type converitible to Item.
+ * can be only an iterator of a type convertible to Item.
* \param bin. The shape of the bin. It has to be supported by the placement
* strategy.
* \param An optional config object for the placer.
@@ -681,7 +713,7 @@ public:
/**
* @brief Get the items for a particular bin.
* @param binIndex The index of the requested bin.
- * @return Returns a list of allitems packed into the requested bin.
+ * @return Returns a list of all items packed into the requested bin.
*/
inline ItemGroup itemsForBin(size_t binIndex) {
return impl_.itemsForBin(binIndex);
@@ -723,15 +755,14 @@ using _IndexedPackGroup = std::vector<
>;
/**
- * The Arranger is the frontend class for the binpack2d library. It takes the
+ * The Arranger is the front-end class for the libnest2d library. It takes the
* input items and outputs the items with the proper transformations to be
* inside the provided bin.
*/
template<class PlacementStrategy, class SelectionStrategy >
-class Arranger {
+class Nester {
using TSel = SelectionStrategyLike<SelectionStrategy>;
TSel selector_;
- bool use_min_bb_rotation_ = false;
public:
using Item = typename PlacementStrategy::Item;
using ItemRef = std::reference_wrapper<Item>;
@@ -769,7 +800,7 @@ public:
template<class TBinType = BinType,
class PConf = PlacementConfig,
class SConf = SelectionConfig>
- Arranger( TBinType&& bin,
+ Nester( TBinType&& bin,
Unit min_obj_distance = 0,
PConf&& pconfig = PConf(),
SConf&& sconfig = SConf()):
@@ -802,9 +833,9 @@ public:
* the selection algorithm.
*/
template<class TIterator>
- inline PackGroup arrange(TIterator from, TIterator to)
+ inline PackGroup execute(TIterator from, TIterator to)
{
- return _arrange(from, to);
+ return _execute(from, to);
}
/**
@@ -815,20 +846,20 @@ public:
* input sequence size.
*/
template<class TIterator>
- inline IndexedPackGroup arrangeIndexed(TIterator from, TIterator to)
+ inline IndexedPackGroup executeIndexed(TIterator from, TIterator to)
{
- return _arrangeIndexed(from, to);
+ return _executeIndexed(from, to);
}
/// Shorthand to normal arrange method.
template<class TIterator>
inline PackGroup operator() (TIterator from, TIterator to)
{
- return _arrange(from, to);
+ return _execute(from, to);
}
- /// Set a progress indicatior function object for the selector.
- inline Arranger& progressIndicator(ProgressFunction func)
+ /// Set a progress indicator function object for the selector.
+ inline Nester& progressIndicator(ProgressFunction func)
{
selector_.progressIndicator(func); return *this;
}
@@ -842,24 +873,20 @@ public:
return ret;
}
- inline Arranger& useMinimumBoundigBoxRotation(bool s = true) {
- use_min_bb_rotation_ = s; return *this;
- }
-
private:
template<class TIterator,
class IT = remove_cvref_t<typename TIterator::value_type>,
- // This funtion will be used only if the iterators are pointing to
- // a type compatible with the binpack2d::_Item template.
+ // This function will be used only if the iterators are pointing to
+ // a type compatible with the libnets2d::_Item template.
// This way we can use references to input elements as they will
// have to exist for the lifetime of this call.
class T = enable_if_t< std::is_convertible<IT, TPItem>::value, IT>
>
- inline PackGroup _arrange(TIterator from, TIterator to, bool = false)
+ inline PackGroup _execute(TIterator from, TIterator to, bool = false)
{
- __arrange(from, to);
+ __execute(from, to);
return lastResult();
}
@@ -867,28 +894,28 @@ private:
class IT = remove_cvref_t<typename TIterator::value_type>,
class T = enable_if_t<!std::is_convertible<IT, TPItem>::value, IT>
>
- inline PackGroup _arrange(TIterator from, TIterator to, int = false)
+ inline PackGroup _execute(TIterator from, TIterator to, int = false)
{
item_cache_ = {from, to};
- __arrange(item_cache_.begin(), item_cache_.end());
+ __execute(item_cache_.begin(), item_cache_.end());
return lastResult();
}
template<class TIterator,
class IT = remove_cvref_t<typename TIterator::value_type>,
- // This funtion will be used only if the iterators are pointing to
- // a type compatible with the binpack2d::_Item template.
+ // This function will be used only if the iterators are pointing to
+ // a type compatible with the libnest2d::_Item template.
// This way we can use references to input elements as they will
// have to exist for the lifetime of this call.
class T = enable_if_t< std::is_convertible<IT, TPItem>::value, IT>
>
- inline IndexedPackGroup _arrangeIndexed(TIterator from,
+ inline IndexedPackGroup _executeIndexed(TIterator from,
TIterator to,
bool = false)
{
- __arrange(from, to);
+ __execute(from, to);
return createIndexedPackGroup(from, to, selector_);
}
@@ -896,12 +923,12 @@ private:
class IT = remove_cvref_t<typename TIterator::value_type>,
class T = enable_if_t<!std::is_convertible<IT, TPItem>::value, IT>
>
- inline IndexedPackGroup _arrangeIndexed(TIterator from,
+ inline IndexedPackGroup _executeIndexed(TIterator from,
TIterator to,
int = false)
{
item_cache_ = {from, to};
- __arrange(item_cache_.begin(), item_cache_.end());
+ __execute(item_cache_.begin(), item_cache_.end());
return createIndexedPackGroup(from, to, selector_);
}
@@ -933,37 +960,12 @@ private:
return pg;
}
- Radians findBestRotation(Item& item) {
- opt::StopCriteria stopcr;
- stopcr.absolute_score_difference = 0.01;
- stopcr.max_iterations = 10000;
- opt::TOptimizer<opt::Method::G_GENETIC> solver(stopcr);
-
- auto orig_rot = item.rotation();
-
- auto result = solver.optimize_min([&item, &orig_rot](Radians rot){
- item.rotation(orig_rot + rot);
- auto bb = item.boundingBox();
- return std::sqrt(bb.height()*bb.width());
- }, opt::initvals(Radians(0)), opt::bound<Radians>(-Pi/2, Pi/2));
-
- item.rotation(orig_rot);
-
- return std::get<0>(result.optimum);
- }
-
- template<class TIter> inline void __arrange(TIter from, TIter to)
+ template<class TIter> inline void __execute(TIter from, TIter to)
{
if(min_obj_distance_ > 0) std::for_each(from, to, [this](Item& item) {
item.addOffset(static_cast<Unit>(std::ceil(min_obj_distance_/2.0)));
});
- if(use_min_bb_rotation_)
- std::for_each(from, to, [this](Item& item){
- Radians rot = findBestRotation(item);
- item.rotate(rot);
- });
-
selector_.template packItems<PlacementStrategy>(
from, to, bin_, pconfig_);
diff --git a/xs/src/libnest2d/libnest2d/metaloop.hpp b/xs/src/libnest2d/libnest2d/metaloop.hpp
index 18755525c..d88988ba1 100644
--- a/xs/src/libnest2d/libnest2d/metaloop.hpp
+++ b/xs/src/libnest2d/libnest2d/metaloop.hpp
@@ -67,11 +67,11 @@ class metaloop {
// need to wrap that in a type (see metaloop::Int).
/*
- * A helper alias to create integer values wrapped as a type. It is nessecary
+ * A helper alias to create integer values wrapped as a type. It is necessary
* because a non type template parameter (such as int) would be prohibited in
* a partial specialization. Also for the same reason we have to use a class
* _Metaloop instead of a simple function as a functor. A function cannot be
- * partially specialized in a way that is neccesary for this trick.
+ * partially specialized in a way that is necessary for this trick.
*/
template<int N> using Int = std::integral_constant<int, N>;
@@ -88,7 +88,7 @@ public:
// It takes the real functor that can be specified in-place but only
// with C++14 because the second parameter's type will depend on the
// type of the parameter pack element that is processed. In C++14 we can
- // specify this second parameter type as auto in the lamda parameter list.
+ // specify this second parameter type as auto in the lambda parameter list.
inline MapFn(Fn&& fn): fn_(forward<Fn>(fn)) {}
template<class T> void operator ()(T&& pack_element) {
@@ -146,7 +146,7 @@ public:
* version of run is called which does not call itself anymore.
*
* If you are utterly annoyed, at least you have learned a super crazy
- * functional metaprogramming pattern.
+ * functional meta-programming pattern.
*/
template<class...Args>
using MetaLoop = _MetaLoop<Int<sizeof...(Args)-1>, Args...>;
diff --git a/xs/src/libnest2d/libnest2d/optimizer.hpp b/xs/src/libnest2d/libnest2d/optimizer.hpp
index c8ed2e378..90d2f2ff9 100644
--- a/xs/src/libnest2d/libnest2d/optimizer.hpp
+++ b/xs/src/libnest2d/libnest2d/optimizer.hpp
@@ -102,6 +102,9 @@ struct StopCriteria {
/// If the relative value difference between two scores.
double relative_score_difference = std::nan("");
+ /// Stop if this value or better is found.
+ double stop_score = std::nan("");
+
unsigned max_iterations = 0;
};
diff --git a/xs/src/libnest2d/libnest2d/optimizers/nlopt_boilerplate.hpp b/xs/src/libnest2d/libnest2d/optimizers/nlopt_boilerplate.hpp
index 737ca6e3c..1a0f06e02 100644
--- a/xs/src/libnest2d/libnest2d/optimizers/nlopt_boilerplate.hpp
+++ b/xs/src/libnest2d/libnest2d/optimizers/nlopt_boilerplate.hpp
@@ -142,10 +142,12 @@ protected:
default: ;
}
- auto abs_diff = stopcr_.absolute_score_difference;
- auto rel_diff = stopcr_.relative_score_difference;
+ double abs_diff = stopcr_.absolute_score_difference;
+ double rel_diff = stopcr_.relative_score_difference;
+ double stopval = stopcr_.stop_score;
if(!std::isnan(abs_diff)) opt_.set_ftol_abs(abs_diff);
if(!std::isnan(rel_diff)) opt_.set_ftol_rel(rel_diff);
+ if(!std::isnan(stopval)) opt_.set_stopval(stopval);
if(this->stopcr_.max_iterations > 0)
opt_.set_maxeval(this->stopcr_.max_iterations );
diff --git a/xs/src/libnest2d/libnest2d/placers/bottomleftplacer.hpp b/xs/src/libnest2d/libnest2d/placers/bottomleftplacer.hpp
index 775e44e09..18c27c40c 100644
--- a/xs/src/libnest2d/libnest2d/placers/bottomleftplacer.hpp
+++ b/xs/src/libnest2d/libnest2d/placers/bottomleftplacer.hpp
@@ -5,11 +5,26 @@
#include "placer_boilerplate.hpp"
-namespace libnest2d { namespace strategies {
+namespace libnest2d { namespace placers {
+
+template<class T, class = T> struct Epsilon {};
+
+template<class T>
+struct Epsilon<T, enable_if_t<std::is_integral<T>::value, T> > {
+ static const T Value = 1;
+};
+
+template<class T>
+struct Epsilon<T, enable_if_t<std::is_floating_point<T>::value, T> > {
+ static const T Value = 1e-3;
+};
template<class RawShape>
struct BLConfig {
- TCoord<TPoint<RawShape>> min_obj_distance = 0;
+ DECLARE_MAIN_TYPES(RawShape);
+
+ Coord min_obj_distance = 0;
+ Coord epsilon = Epsilon<Coord>::Value;
bool allow_rotations = false;
};
@@ -27,9 +42,13 @@ public:
explicit _BottomLeftPlacer(const BinType& bin): Base(bin) {}
- PackResult trypack(Item& item) {
+ template<class Range = ConstItemRange<typename Base::DefaultIter>>
+ PackResult trypack(Item& item,
+ const Range& = Range())
+ {
auto r = _trypack(item);
if(!r && Base::config_.allow_rotations) {
+
item.rotate(Degrees(90));
r =_trypack(item);
}
@@ -65,20 +84,21 @@ protected:
setInitialPosition(item);
Unit d = availableSpaceDown(item);
- bool can_move = d > 1 /*std::numeric_limits<Unit>::epsilon()*/;
+ auto eps = config_.epsilon;
+ bool can_move = d > eps;
bool can_be_packed = can_move;
bool left = true;
while(can_move) {
if(left) { // write previous down move and go down
- item.translate({0, -d+1});
+ item.translate({0, -d+eps});
d = availableSpaceLeft(item);
- can_move = d > 1/*std::numeric_limits<Unit>::epsilon()*/;
+ can_move = d > eps;
left = false;
} else { // write previous left move and go down
- item.translate({-d+1, 0});
+ item.translate({-d+eps, 0});
d = availableSpaceDown(item);
- can_move = d > 1/*std::numeric_limits<Unit>::epsilon()*/;
+ can_move = d > eps;
left = true;
}
}
@@ -112,10 +132,10 @@ protected:
const RawShape& scanpoly)
{
auto tsh = other.transformedShape();
- return ( ShapeLike::intersects(tsh, scanpoly) ||
- ShapeLike::isInside(tsh, scanpoly) ) &&
- ( !ShapeLike::intersects(tsh, item.rawShape()) &&
- !ShapeLike::isInside(tsh, item.rawShape()) );
+ return ( sl::intersects(tsh, scanpoly) ||
+ sl::isInside(tsh, scanpoly) ) &&
+ ( !sl::intersects(tsh, item.rawShape()) &&
+ !sl::isInside(tsh, item.rawShape()) );
}
template<class C = Coord>
@@ -126,25 +146,25 @@ protected:
{
auto tsh = other.transformedShape();
- bool inters_scanpoly = ShapeLike::intersects(tsh, scanpoly) &&
- !ShapeLike::touches(tsh, scanpoly);
- bool inters_item = ShapeLike::intersects(tsh, item.rawShape()) &&
- !ShapeLike::touches(tsh, item.rawShape());
+ bool inters_scanpoly = sl::intersects(tsh, scanpoly) &&
+ !sl::touches(tsh, scanpoly);
+ bool inters_item = sl::intersects(tsh, item.rawShape()) &&
+ !sl::touches(tsh, item.rawShape());
return ( inters_scanpoly ||
- ShapeLike::isInside(tsh, scanpoly)) &&
+ sl::isInside(tsh, scanpoly)) &&
( !inters_item &&
- !ShapeLike::isInside(tsh, item.rawShape())
+ !sl::isInside(tsh, item.rawShape())
);
}
- Container itemsInTheWayOf(const Item& item, const Dir dir) {
+ ItemGroup itemsInTheWayOf(const Item& item, const Dir dir) {
// Get the left or down polygon, that has the same area as the shadow
// of input item reflected to the left or downwards
auto&& scanpoly = dir == Dir::LEFT? leftPoly(item) :
downPoly(item);
- Container ret; // packed items 'in the way' of item
+ ItemGroup ret; // packed items 'in the way' of item
ret.reserve(items_.size());
// Predicate to find items that are 'in the way' for left (down) move
@@ -173,18 +193,18 @@ protected:
if(dir == Dir::LEFT) {
getCoord = [](const Vertex& v) { return getX(v); };
- availableDistance = PointLike::horizontalDistance<Vertex>;
+ availableDistance = pointlike::horizontalDistance<Vertex>;
availableDistanceSV = [](const Segment& s, const Vertex& v) {
- auto ret = PointLike::horizontalDistance<Vertex>(v, s);
+ auto ret = pointlike::horizontalDistance<Vertex>(v, s);
if(ret.second) ret.first = -ret.first;
return ret;
};
}
else {
getCoord = [](const Vertex& v) { return getY(v); };
- availableDistance = PointLike::verticalDistance<Vertex>;
+ availableDistance = pointlike::verticalDistance<Vertex>;
availableDistanceSV = [](const Segment& s, const Vertex& v) {
- auto ret = PointLike::verticalDistance<Vertex>(v, s);
+ auto ret = pointlike::verticalDistance<Vertex>(v, s);
if(ret.second) ret.first = -ret.first;
return ret;
};
@@ -214,9 +234,9 @@ protected:
assert(pleft.vertexCount() > 0);
auto trpleft = pleft.transformedShape();
- auto first = ShapeLike::begin(trpleft);
+ auto first = sl::begin(trpleft);
auto next = first + 1;
- auto endit = ShapeLike::end(trpleft);
+ auto endit = sl::end(trpleft);
while(next != endit) {
Segment seg(*(first++), *(next++));
@@ -340,16 +360,16 @@ protected:
// reserve for all vertices plus 2 for the left horizontal wall, 2 for
// the additional vertices for maintaning min object distance
- ShapeLike::reserve(rsh, finish-start+4);
+ sl::reserve(rsh, finish-start+4);
/*auto addOthers = [&rsh, finish, start, &item](){
for(size_t i = start+1; i < finish; i++)
- ShapeLike::addVertex(rsh, item.vertex(i));
+ sl::addVertex(rsh, item.vertex(i));
};*/
auto reverseAddOthers = [&rsh, finish, start, &item](){
for(auto i = finish-1; i > start; i--)
- ShapeLike::addVertex(rsh, item.vertex(
+ sl::addVertex(rsh, item.vertex(
static_cast<unsigned long>(i)));
};
@@ -361,25 +381,25 @@ protected:
// Clockwise polygon construction
- ShapeLike::addVertex(rsh, topleft_vertex);
+ sl::addVertex(rsh, topleft_vertex);
if(dir == Dir::LEFT) reverseAddOthers();
else {
- ShapeLike::addVertex(rsh, getX(topleft_vertex), 0);
- ShapeLike::addVertex(rsh, getX(bottomleft_vertex), 0);
+ sl::addVertex(rsh, getX(topleft_vertex), 0);
+ sl::addVertex(rsh, getX(bottomleft_vertex), 0);
}
- ShapeLike::addVertex(rsh, bottomleft_vertex);
+ sl::addVertex(rsh, bottomleft_vertex);
if(dir == Dir::LEFT) {
- ShapeLike::addVertex(rsh, 0, getY(bottomleft_vertex));
- ShapeLike::addVertex(rsh, 0, getY(topleft_vertex));
+ sl::addVertex(rsh, 0, getY(bottomleft_vertex));
+ sl::addVertex(rsh, 0, getY(topleft_vertex));
}
else reverseAddOthers();
// Close the polygon
- ShapeLike::addVertex(rsh, topleft_vertex);
+ sl::addVertex(rsh, topleft_vertex);
return rsh;
}
diff --git a/xs/src/libnest2d/libnest2d/placers/nfpplacer.hpp b/xs/src/libnest2d/libnest2d/placers/nfpplacer.hpp
index 5d09a61fc..c86fb507e 100644
--- a/xs/src/libnest2d/libnest2d/placers/nfpplacer.hpp
+++ b/xs/src/libnest2d/libnest2d/placers/nfpplacer.hpp
@@ -1,21 +1,148 @@
#ifndef NOFITPOLY_HPP
#define NOFITPOLY_HPP
+#include <cassert>
+
+// For caching nfps
+#include <unordered_map>
+
+// For parallel for
+#include <functional>
+#include <iterator>
+#include <future>
+#include <atomic>
+
#ifndef NDEBUG
#include <iostream>
#endif
#include "placer_boilerplate.hpp"
#include "../geometry_traits_nfp.hpp"
#include "libnest2d/optimizer.hpp"
-#include <cassert>
#include "tools/svgtools.hpp"
-namespace libnest2d { namespace strategies {
+#ifdef USE_TBB
+#include <tbb/parallel_for.h>
+#elif defined(_OPENMP)
+#include <omp.h>
+#endif
+
+namespace libnest2d {
+
+namespace __parallel {
+
+using std::function;
+using std::iterator_traits;
+template<class It>
+using TIteratorValue = typename iterator_traits<It>::value_type;
+
+template<class Iterator>
+inline void enumerate(
+ Iterator from, Iterator to,
+ function<void(TIteratorValue<Iterator>, size_t)> fn,
+ std::launch policy = std::launch::deferred | std::launch::async)
+{
+ using TN = size_t;
+ auto iN = to-from;
+ TN N = iN < 0? 0 : TN(iN);
+
+#ifdef USE_TBB
+ if((policy & std::launch::async) == std::launch::async) {
+ tbb::parallel_for<TN>(0, N, [from, fn] (TN n) { fn(*(from + n), n); } );
+ } else {
+ for(TN n = 0; n < N; n++) fn(*(from + n), n);
+ }
+#elif defined(_OPENMP)
+ if((policy & std::launch::async) == std::launch::async) {
+ #pragma omp parallel for
+ for(TN n = 0; n < N; n++) fn(*(from + n), n);
+ }
+ else {
+ for(TN n = 0; n < N; n++) fn(*(from + n), n);
+ }
+#else
+ std::vector<std::future<void>> rets(N);
+
+ auto it = from;
+ for(TN b = 0; b < N; b++) {
+ rets[b] = std::async(policy, fn, *it++, unsigned(b));
+ }
+
+ for(TN fi = 0; fi < N; ++fi) rets[fi].wait();
+#endif
+}
+
+class SpinLock {
+ static std::atomic_flag locked;
+public:
+ void lock() {
+ while (locked.test_and_set(std::memory_order_acquire)) { ; }
+ }
+ void unlock() {
+ locked.clear(std::memory_order_release);
+ }
+};
+
+std::atomic_flag SpinLock::locked = ATOMIC_FLAG_INIT ;
+
+}
+
+namespace __itemhash {
+
+using Key = size_t;
+
+template<class S>
+Key hash(const _Item<S>& item) {
+ using Point = TPoint<S>;
+ using Segment = _Segment<Point>;
+
+ static const int N = 26;
+ static const int M = N*N - 1;
+
+ std::string ret;
+ auto& rhs = item.rawShape();
+ auto& ctr = sl::getContour(rhs);
+ auto it = ctr.begin();
+ auto nx = std::next(it);
+
+ double circ = 0;
+ while(nx != ctr.end()) {
+ Segment seg(*it++, *nx++);
+ Radians a = seg.angleToXaxis();
+ double deg = Degrees(a);
+ int ms = 'A', ls = 'A';
+ while(deg > N) { ms++; deg -= N; }
+ ls += int(deg);
+ ret.push_back(char(ms)); ret.push_back(char(ls));
+ circ += seg.length();
+ }
+
+ it = ctr.begin(); nx = std::next(it);
+
+ while(nx != ctr.end()) {
+ Segment seg(*it++, *nx++);
+ auto l = int(M * seg.length() / circ);
+ int ms = 'A', ls = 'A';
+ while(l > N) { ms++; l -= N; }
+ ls += l;
+ ret.push_back(char(ms)); ret.push_back(char(ls));
+ }
+
+ return std::hash<std::string>()(ret);
+}
+
+template<class S>
+using Hash = std::unordered_map<Key, nfp::NfpResult<S>>;
+
+}
+
+namespace placers {
template<class RawShape>
struct NfpPConfig {
+ using ItemGroup = _ItemGroup<_Item<RawShape>>;
+
enum class Alignment {
CENTER,
BOTTOM_LEFT,
@@ -45,47 +172,19 @@ struct NfpPConfig {
* that will optimize for the best pack efficiency. With a custom fitting
* function you can e.g. influence the shape of the arranged pile.
*
- * \param shapes The first parameter is a container with all the placed
- * polygons excluding the current candidate. You can calculate a bounding
- * box or convex hull on this pile of polygons without the candidate item
- * or push back the candidate item into the container and then calculate
- * some features.
- *
- * \param item The second parameter is the candidate item.
- *
- * \param occupied_area The third parameter is the sum of areas of the
- * items in the first parameter so you don't have to iterate through them
- * if you only need their area.
- *
- * \param norm A norming factor for physical dimensions. E.g. if your score
- * is the distance between the item and the bin center, you should divide
- * that distance with the norming factor. If the score is an area than
- * divide it with the square of the norming factor. Imagine it as a unit of
- * distance.
- *
- * \param penality The fifth parameter is the amount of minimum penality if
- * the arranged pile would't fit into the bin. You can use the wouldFit()
- * function to check this. Note that the pile can be outside the bin's
- * boundaries while the placement algorithm is running. Your job is only to
- * check if the pile could be translated into a position in the bin where
- * all the items would be inside. For a box shaped bin you can use the
- * pile's bounding box to check whether it's width and height is small
- * enough. If the pile would not fit, you have to make sure that the
- * resulting score will be higher then the penality value. A good solution
- * would be to set score = 2*penality-score in case the pile wouldn't fit
- * into the bin.
+ * \param item The only parameter is the candidate item which has info
+ * about its current position. Your job is to rate this position compared to
+ * the already packed items.
*
*/
- std::function<double(Nfp::Shapes<RawShape>&, const _Item<RawShape>&,
- double, double, double)>
- object_function;
+ std::function<double(const _Item<RawShape>&)> object_function;
/**
* @brief The quality of search for an optimal placement.
* This is a compromise slider between quality and speed. Zero is the
* fast and poor solution while 1.0 is the slowest but most accurate.
*/
- float accuracy = 1.0;
+ float accuracy = 0.65f;
/**
* @brief If you want to see items inside other item's holes, you have to
@@ -96,6 +195,39 @@ struct NfpPConfig {
*/
bool explore_holes = false;
+ /**
+ * @brief If true, use all CPUs available. Run on a single core otherwise.
+ */
+ bool parallel = true;
+
+ /**
+ * @brief before_packing Callback that is called just before a search for
+ * a new item's position is started. You can use this to create various
+ * cache structures and update them between subsequent packings.
+ *
+ * \param merged pile A polygon that is the union of all items in the bin.
+ *
+ * \param pile The items parameter is a container with all the placed
+ * polygons excluding the current candidate. You can for instance check the
+ * alignment with the candidate item or do anything else.
+ *
+ * \param remaining A container with the remaining items waiting to be
+ * placed. You can use some features about the remaining items to alter to
+ * score of the current placement. If you know that you have to leave place
+ * for other items as well, that might influence your decision about where
+ * the current candidate should be placed. E.g. imagine three big circles
+ * which you want to place into a box: you might place them in a triangle
+ * shape which has the maximum pack density. But if there is a 4th big
+ * circle than you won't be able to pack it. If you knew apriori that
+ * there four circles are to be placed, you would have placed the first 3
+ * into an L shape. This parameter can be used to make these kind of
+ * decisions (for you or a more intelligent AI).
+ */
+ std::function<void(const nfp::Shapes<RawShape>&, // merged pile
+ const ItemGroup&, // packed items
+ const ItemGroup& // remaining items
+ )> before_packing;
+
NfpPConfig(): rotations({0.0, Pi/2.0, Pi, 3*Pi/2}),
alignment(Alignment::CENTER), starting_point(Alignment::CENTER) {}
};
@@ -129,11 +261,11 @@ template<class RawShape> class EdgeCache {
void createCache(const RawShape& sh) {
{ // For the contour
- auto first = ShapeLike::cbegin(sh);
+ auto first = shapelike::cbegin(sh);
auto next = std::next(first);
- auto endit = ShapeLike::cend(sh);
+ auto endit = shapelike::cend(sh);
- contour_.distances.reserve(ShapeLike::contourVertexCount(sh));
+ contour_.distances.reserve(shapelike::contourVertexCount(sh));
while(next != endit) {
contour_.emap.emplace_back(*(first++), *(next++));
@@ -142,7 +274,7 @@ template<class RawShape> class EdgeCache {
}
}
- for(auto& h : ShapeLike::holes(sh)) { // For the holes
+ for(auto& h : shapelike::holes(sh)) { // For the holes
auto first = h.begin();
auto next = std::next(first);
auto endit = h.end();
@@ -161,12 +293,11 @@ template<class RawShape> class EdgeCache {
}
size_t stride(const size_t N) const {
- using std::ceil;
using std::round;
using std::pow;
return static_cast<Coord>(
- std::round(N/std::pow(N, std::pow(accuracy_, 1.0/3.0)))
+ round(N/pow(N, pow(accuracy_, 1.0/3.0)))
);
}
@@ -177,6 +308,7 @@ template<class RawShape> class EdgeCache {
const auto S = stride(N);
contour_.corners.reserve(N / S + 1);
+ contour_.corners.emplace_back(0.0);
auto N_1 = N-1;
contour_.corners.emplace_back(0.0);
for(size_t i = 0; i < N_1; i += S) {
@@ -190,8 +322,8 @@ template<class RawShape> class EdgeCache {
if(!hc.corners.empty()) return;
const auto N = hc.distances.size();
- const auto S = stride(N);
auto N_1 = N-1;
+ const auto S = stride(N);
hc.corners.reserve(N / S + 1);
hc.corners.emplace_back(0.0);
for(size_t i = 0; i < N_1; i += S) {
@@ -290,11 +422,11 @@ public:
};
-template<NfpLevel lvl>
-struct Lvl { static const NfpLevel value = lvl; };
+template<nfp::NfpLevel lvl>
+struct Lvl { static const nfp::NfpLevel value = lvl; };
template<class RawShape>
-inline void correctNfpPosition(Nfp::NfpResult<RawShape>& nfp,
+inline void correctNfpPosition(nfp::NfpResult<RawShape>& nfp,
const _Item<RawShape>& stationary,
const _Item<RawShape>& orbiter)
{
@@ -314,131 +446,78 @@ inline void correctNfpPosition(Nfp::NfpResult<RawShape>& nfp,
auto dtouch = touch_sh - touch_other;
auto top_other = orbiter.rightmostTopVertex() + dtouch;
auto dnfp = top_other - nfp.second; // nfp.second is the nfp reference point
- ShapeLike::translate(nfp.first, dnfp);
+ shapelike::translate(nfp.first, dnfp);
}
template<class RawShape>
-inline void correctNfpPosition(Nfp::NfpResult<RawShape>& nfp,
+inline void correctNfpPosition(nfp::NfpResult<RawShape>& nfp,
const RawShape& stationary,
const _Item<RawShape>& orbiter)
{
- auto touch_sh = Nfp::rightmostUpVertex(stationary);
+ auto touch_sh = nfp::rightmostUpVertex(stationary);
auto touch_other = orbiter.leftmostBottomVertex();
auto dtouch = touch_sh - touch_other;
auto top_other = orbiter.rightmostTopVertex() + dtouch;
auto dnfp = top_other - nfp.second;
- ShapeLike::translate(nfp.first, dnfp);
-}
-
-template<class RawShape, class Container>
-Nfp::Shapes<RawShape> nfp( const Container& polygons,
- const _Item<RawShape>& trsh,
- Lvl<NfpLevel::CONVEX_ONLY>)
-{
- using Item = _Item<RawShape>;
-
- Nfp::Shapes<RawShape> nfps;
-
- //int pi = 0;
- for(Item& sh : polygons) {
- auto subnfp_r = Nfp::noFitPolygon<NfpLevel::CONVEX_ONLY>(
- sh.transformedShape(), trsh.transformedShape());
- #ifndef NDEBUG
- auto vv = ShapeLike::isValid(sh.transformedShape());
- assert(vv.first);
-
- auto vnfp = ShapeLike::isValid(subnfp_r.first);
- assert(vnfp.first);
- #endif
-
- correctNfpPosition(subnfp_r, sh, trsh);
-
- nfps = Nfp::merge(nfps, subnfp_r.first);
-
-// double SCALE = 1000000;
-// using SVGWriter = svg::SVGWriter<RawShape>;
-// SVGWriter::Config conf;
-// conf.mm_in_coord_units = SCALE;
-// SVGWriter svgw(conf);
-// Box bin(250*SCALE, 210*SCALE);
-// svgw.setSize(bin);
-// for(int i = 0; i <= pi; i++) svgw.writeItem(polygons[i]);
-// svgw.writeItem(trsh);
-//// svgw.writeItem(Item(subnfp_r.first));
-// for(auto& n : nfps) svgw.writeItem(Item(n));
-// svgw.save("nfpout");
-// pi++;
- }
-
- return nfps;
+ shapelike::translate(nfp.first, dnfp);
}
-template<class RawShape, class Container, class Level>
-Nfp::Shapes<RawShape> nfp( const Container& polygons,
- const _Item<RawShape>& trsh,
- Level)
-{
- using Item = _Item<RawShape>;
-
- Nfp::Shapes<RawShape> nfps;
-
- auto& orb = trsh.transformedShape();
- bool orbconvex = trsh.isContourConvex();
+template<class RawShape, class Circle = _Circle<TPoint<RawShape>> >
+Circle minimizeCircle(const RawShape& sh) {
+ using Point = TPoint<RawShape>;
+ using Coord = TCoord<Point>;
- for(Item& sh : polygons) {
- Nfp::NfpResult<RawShape> subnfp;
- auto& stat = sh.transformedShape();
+ auto& ctr = sl::getContour(sh);
+ if(ctr.empty()) return {{0, 0}, 0};
- if(sh.isContourConvex() && orbconvex)
- subnfp = Nfp::noFitPolygon<NfpLevel::CONVEX_ONLY>(stat, orb);
- else if(orbconvex)
- subnfp = Nfp::noFitPolygon<NfpLevel::ONE_CONVEX>(stat, orb);
- else
- subnfp = Nfp::noFitPolygon<Level::value>(stat, orb);
+ auto bb = sl::boundingBox(sh);
+ auto capprx = bb.center();
+ auto rapprx = pl::distance(bb.minCorner(), bb.maxCorner());
- correctNfpPosition(subnfp, sh, trsh);
- nfps = Nfp::merge(nfps, subnfp.first);
- }
+ opt::StopCriteria stopcr;
+ stopcr.max_iterations = 30;
+ stopcr.relative_score_difference = 1e-3;
+ opt::TOptimizer<opt::Method::L_SUBPLEX> solver(stopcr);
- return nfps;
+ std::vector<double> dists(ctr.size(), 0);
+ auto result = solver.optimize_min(
+ [capprx, rapprx, &ctr, &dists](double xf, double yf) {
+ auto xt = Coord( std::round(getX(capprx) + rapprx*xf) );
+ auto yt = Coord( std::round(getY(capprx) + rapprx*yf) );
-// using Item = _Item<RawShape>;
-// using sl = ShapeLike;
+ Point centr(xt, yt);
-// Nfp::Shapes<RawShape> nfps, stationary;
+ unsigned i = 0;
+ for(auto v : ctr) {
+ dists[i++] = pl::distance(v, centr);
+ }
-// for(Item& sh : polygons) {
-// stationary = Nfp::merge(stationary, sh.transformedShape());
-// }
+ auto mit = std::max_element(dists.begin(), dists.end());
-// for(RawShape& sh : stationary) {
+ assert(mit != dists.end());
-//// auto vv = sl::isValid(sh);
-//// std::cout << vv.second << std::endl;
+ return *mit;
+ },
+ opt::initvals(0.0, 0.0),
+ opt::bound(-1.0, 1.0), opt::bound(-1.0, 1.0)
+ );
+ double oxf = std::get<0>(result.optimum);
+ double oyf = std::get<1>(result.optimum);
+ auto xt = Coord( std::round(getX(capprx) + rapprx*oxf) );
+ auto yt = Coord( std::round(getY(capprx) + rapprx*oyf) );
-// Nfp::NfpResult<RawShape> subnfp;
-// bool shconvex = sl::isConvex<RawShape>(sl::getContour(sh));
-// if(shconvex && trsh.isContourConvex()) {
-// subnfp = Nfp::noFitPolygon<NfpLevel::CONVEX_ONLY>(
-// sh, trsh.transformedShape());
-// } else if(trsh.isContourConvex()) {
-// subnfp = Nfp::noFitPolygon<NfpLevel::ONE_CONVEX>(
-// sh, trsh.transformedShape());
-// }
-// else {
-// subnfp = Nfp::noFitPolygon<Level::value>( sh,
-// trsh.transformedShape());
-// }
+ Point cc(xt, yt);
+ auto r = result.score;
-// correctNfpPosition(subnfp, sh, trsh);
-
-// nfps = Nfp::merge(nfps, subnfp.first);
-// }
+ return {cc, r};
+}
-// return nfps;
+template<class RawShape>
+_Circle<TPoint<RawShape>> boundingCircle(const RawShape& sh) {
+ return minimizeCircle(sh);
}
template<class RawShape, class TBin = _Box<TPoint<RawShape>>>
@@ -452,20 +531,26 @@ class _NofitPolyPlacer: public PlacerBoilerplate<_NofitPolyPlacer<RawShape, TBin
using Box = _Box<TPoint<RawShape>>;
+ using MaxNfpLevel = nfp::MaxNfpLevel<RawShape>;
+
+ using ItemKeys = std::vector<__itemhash::Key>;
+
+ // Norming factor for the optimization function
const double norm_;
- const double penality_;
- using MaxNfpLevel = Nfp::MaxNfpLevel<RawShape>;
- using sl = ShapeLike;
+ // Caching calculated nfps
+ __itemhash::Hash<RawShape> nfpcache_;
+
+ // Storing item hash keys
+ ItemKeys item_keys_;
public:
- using Pile = Nfp::Shapes<RawShape>;
+ using Pile = nfp::Shapes<RawShape>;
inline explicit _NofitPolyPlacer(const BinType& bin):
Base(bin),
- norm_(std::sqrt(sl::area<RawShape>(bin))),
- penality_(1e6*norm_) {}
+ norm_(std::sqrt(sl::area(bin))) {}
_NofitPolyPlacer(const _NofitPolyPlacer&) = default;
_NofitPolyPlacer& operator=(const _NofitPolyPlacer&) = default;
@@ -475,81 +560,322 @@ public:
_NofitPolyPlacer& operator=(_NofitPolyPlacer&&) BP2D_NOEXCEPT = default;
#endif
- bool static inline wouldFit(const Box& bb, const RawShape& bin) {
- auto bbin = sl::boundingBox<RawShape>(bin);
+ static inline double overfit(const Box& bb, const RawShape& bin) {
+ auto bbin = sl::boundingBox(bin);
auto d = bbin.center() - bb.center();
_Rectangle<RawShape> rect(bb.width(), bb.height());
rect.translate(bb.minCorner() + d);
- return sl::isInside<RawShape>(rect.transformedShape(), bin);
+ return sl::isInside(rect.transformedShape(), bin) ? -1.0 : 1;
}
- bool static inline wouldFit(const RawShape& chull, const RawShape& bin) {
- auto bbch = sl::boundingBox<RawShape>(chull);
- auto bbin = sl::boundingBox<RawShape>(bin);
+ static inline double overfit(const RawShape& chull, const RawShape& bin) {
+ auto bbch = sl::boundingBox(chull);
+ auto bbin = sl::boundingBox(bin);
auto d = bbch.center() - bbin.center();
auto chullcpy = chull;
sl::translate(chullcpy, d);
- return sl::isInside<RawShape>(chullcpy, bin);
+ return sl::isInside(chullcpy, bin) ? -1.0 : 1.0;
}
- bool static inline wouldFit(const RawShape& chull, const Box& bin)
+ static inline double overfit(const RawShape& chull, const Box& bin)
{
- auto bbch = sl::boundingBox<RawShape>(chull);
- return wouldFit(bbch, bin);
+ auto bbch = sl::boundingBox(chull);
+ return overfit(bbch, bin);
}
- bool static inline wouldFit(const Box& bb, const Box& bin)
+ static inline double overfit(const Box& bb, const Box& bin)
{
- return bb.width() <= bin.width() && bb.height() <= bin.height();
+ auto wdiff = double(bb.width() - bin.width());
+ auto hdiff = double(bb.height() - bin.height());
+ double diff = 0;
+ if(wdiff > 0) diff += wdiff;
+ if(hdiff > 0) diff += hdiff;
+ return diff;
}
- bool static inline wouldFit(const Box& bb, const _Circle<Vertex>& bin)
+ static inline double overfit(const Box& bb, const _Circle<Vertex>& bin)
{
- return sl::isInside<RawShape>(bb, bin);
+ double boxr = 0.5*pl::distance(bb.minCorner(), bb.maxCorner());
+ double diff = boxr - bin.radius();
+ return diff;
}
- bool static inline wouldFit(const RawShape& chull,
+ static inline double overfit(const RawShape& chull,
const _Circle<Vertex>& bin)
{
- return sl::isInside<RawShape>(chull, bin);
+ double r = boundingCircle(chull).radius();
+ double diff = r - bin.radius();
+ return diff;
+ }
+
+ template<class Range = ConstItemRange<typename Base::DefaultIter>>
+ PackResult trypack(Item& item,
+ const Range& remaining = Range()) {
+ auto result = _trypack(item, remaining);
+
+ // Experimental
+ // if(!result) repack(item, result);
+
+ return result;
+ }
+
+ ~_NofitPolyPlacer() {
+ clearItems();
+ }
+
+ inline void clearItems() {
+ finalAlign(bin_);
+ Base::clearItems();
+ }
+
+private:
+
+ using Shapes = TMultiShape<RawShape>;
+ using ItemRef = std::reference_wrapper<Item>;
+ using ItemWithHash = const std::pair<ItemRef, __itemhash::Key>;
+
+ Shapes calcnfp(const ItemWithHash itsh, Lvl<nfp::NfpLevel::CONVEX_ONLY>)
+ {
+ using namespace nfp;
+
+ Shapes nfps(items_.size());
+ const Item& trsh = itsh.first;
+
+ __parallel::enumerate(items_.begin(), items_.end(),
+ [&nfps, &trsh](const Item& sh, size_t n)
+ {
+ auto& fixedp = sh.transformedShape();
+ auto& orbp = trsh.transformedShape();
+ auto subnfp_r = noFitPolygon<NfpLevel::CONVEX_ONLY>(fixedp, orbp);
+ correctNfpPosition(subnfp_r, sh, trsh);
+ nfps[n] = subnfp_r.first;
+ });
+
+// for(auto& n : nfps) {
+// auto valid = sl::isValid(n);
+// if(!valid.first) std::cout << "Warning: " << valid.second << std::endl;
+// }
+
+ return nfp::merge(nfps);
}
- PackResult trypack(Item& item) {
+ template<class Level>
+ Shapes calcnfp( const ItemWithHash itsh, Level)
+ { // Function for arbitrary level of nfp implementation
+ using namespace nfp;
+
+ Shapes nfps;
+ const Item& trsh = itsh.first;
+
+ auto& orb = trsh.transformedShape();
+ bool orbconvex = trsh.isContourConvex();
+
+ for(Item& sh : items_) {
+ nfp::NfpResult<RawShape> subnfp;
+ auto& stat = sh.transformedShape();
+
+ if(sh.isContourConvex() && orbconvex)
+ subnfp = nfp::noFitPolygon<NfpLevel::CONVEX_ONLY>(stat, orb);
+ else if(orbconvex)
+ subnfp = nfp::noFitPolygon<NfpLevel::ONE_CONVEX>(stat, orb);
+ else
+ subnfp = nfp::noFitPolygon<Level::value>(stat, orb);
+
+ correctNfpPosition(subnfp, sh, trsh);
+
+ nfps = nfp::merge(nfps, subnfp.first);
+ }
+
+ return nfps;
+ }
+
+ // Very much experimental
+ void repack(Item& item, PackResult& result) {
+
+ if((sl::area(bin_) - this->filledArea()) >= item.area()) {
+ auto prev_func = config_.object_function;
+
+ unsigned iter = 0;
+ ItemGroup backup_rf = items_;
+ std::vector<Item> backup_cpy;
+ for(Item& itm : items_) backup_cpy.emplace_back(itm);
+
+ auto ofn = [this, &item, &result, &iter, &backup_cpy, &backup_rf]
+ (double ratio)
+ {
+ auto& bin = bin_;
+ iter++;
+ config_.object_function = [bin, ratio](
+ nfp::Shapes<RawShape>& pile,
+ const Item& item,
+ const ItemGroup& /*remaining*/)
+ {
+ pile.emplace_back(item.transformedShape());
+ auto ch = sl::convexHull(pile);
+ auto pbb = sl::boundingBox(pile);
+ pile.pop_back();
+
+ double parea = 0.5*(sl::area(ch) + sl::area(pbb));
+
+ double pile_area = std::accumulate(
+ pile.begin(), pile.end(), item.area(),
+ [](double sum, const RawShape& sh){
+ return sum + sl::area(sh);
+ });
+
+ // The pack ratio -- how much is the convex hull occupied
+ double pack_rate = (pile_area)/parea;
+
+ // ratio of waste
+ double waste = 1.0 - pack_rate;
+
+ // Score is the square root of waste. This will extend the
+ // range of good (lower) values and shrink the range of bad
+ // (larger) values.
+ auto wscore = std::sqrt(waste);
+
+
+ auto ibb = item.boundingBox();
+ auto bbb = sl::boundingBox(bin);
+ auto c = ibb.center();
+ double norm = 0.5*pl::distance(bbb.minCorner(),
+ bbb.maxCorner());
+
+ double dscore = pl::distance(c, pbb.center()) / norm;
+
+ return ratio*wscore + (1.0 - ratio) * dscore;
+ };
+
+ auto bb = sl::boundingBox(bin);
+ double norm = bb.width() + bb.height();
+
+ auto items = items_;
+ clearItems();
+ auto it = items.begin();
+ while(auto pr = _trypack(*it++)) {
+ this->accept(pr); if(it == items.end()) break;
+ }
+
+ auto count_diff = items.size() - items_.size();
+ double score = count_diff;
+
+ if(count_diff == 0) {
+ result = _trypack(item);
+
+ if(result) {
+ std::cout << "Success" << std::endl;
+ score = 0.0;
+ } else {
+ score += result.overfit() / norm;
+ }
+ } else {
+ result = PackResult();
+ items_ = backup_rf;
+ for(unsigned i = 0; i < items_.size(); i++) {
+ items_[i].get() = backup_cpy[i];
+ }
+ }
+
+ std::cout << iter << " repack result: " << score << " "
+ << ratio << " " << count_diff << std::endl;
+
+ return score;
+ };
+
+ opt::StopCriteria stopcr;
+ stopcr.max_iterations = 30;
+ stopcr.stop_score = 1e-20;
+ opt::TOptimizer<opt::Method::L_SUBPLEX> solver(stopcr);
+ solver.optimize_min(ofn, opt::initvals(0.5),
+ opt::bound(0.0, 1.0));
+
+ // optimize
+ config_.object_function = prev_func;
+ }
+
+ }
+
+ struct Optimum {
+ double relpos;
+ unsigned nfpidx;
+ int hidx;
+ Optimum(double pos, unsigned nidx):
+ relpos(pos), nfpidx(nidx), hidx(-1) {}
+ Optimum(double pos, unsigned nidx, int holeidx):
+ relpos(pos), nfpidx(nidx), hidx(holeidx) {}
+ };
+
+ class Optimizer: public opt::TOptimizer<opt::Method::L_SUBPLEX> {
+ public:
+ Optimizer() {
+ opt::StopCriteria stopcr;
+ stopcr.max_iterations = 200;
+ stopcr.relative_score_difference = 1e-20;
+ this->stopcr_ = stopcr;
+ }
+ };
+
+ static Box boundingBox(const Box& pilebb, const Box& ibb ) {
+ auto& pminc = pilebb.minCorner();
+ auto& pmaxc = pilebb.maxCorner();
+ auto& iminc = ibb.minCorner();
+ auto& imaxc = ibb.maxCorner();
+ Vertex minc, maxc;
+
+ setX(minc, std::min(getX(pminc), getX(iminc)));
+ setY(minc, std::min(getY(pminc), getY(iminc)));
+
+ setX(maxc, std::max(getX(pmaxc), getX(imaxc)));
+ setY(maxc, std::max(getY(pmaxc), getY(imaxc)));
+ return Box(minc, maxc);
+ }
+
+ using Edges = EdgeCache<RawShape>;
+
+ template<class Range = ConstItemRange<typename Base::DefaultIter>>
+ PackResult _trypack(
+ Item& item,
+ const Range& remaining = Range()) {
PackResult ret;
bool can_pack = false;
+ double best_overfit = std::numeric_limits<double>::max();
+
+ auto remlist = ItemGroup(remaining.from, remaining.to);
+ size_t itemhash = __itemhash::hash(item);
if(items_.empty()) {
setInitialPosition(item);
- can_pack = item.isInside(bin_);
+ best_overfit = overfit(item.transformedShape(), bin_);
+ can_pack = best_overfit <= 0;
} else {
- double global_score = penality_;
+ double global_score = std::numeric_limits<double>::max();
auto initial_tr = item.translation();
auto initial_rot = item.rotation();
Vertex final_tr = {0, 0};
Radians final_rot = initial_rot;
- Nfp::Shapes<RawShape> nfps;
+ Shapes nfps;
for(auto rot : config_.rotations) {
item.translation(initial_tr);
item.rotation(initial_rot + rot);
+ item.boundingBox(); // fill the bb cache
// place the new item outside of the print bed to make sure
- // it is disjuct from the current merged pile
+ // it is disjunct from the current merged pile
placeOutsideOfBin(item);
- auto trsh = item.transformedShape();
+ nfps = calcnfp({item, itemhash}, Lvl<MaxNfpLevel::value>());
- nfps = nfp(items_, item, Lvl<MaxNfpLevel::value>());
- auto iv = Nfp::referenceVertex(trsh);
+ auto iv = item.referenceVertex();
auto startpos = item.translation();
- std::vector<EdgeCache<RawShape>> ecache;
+ std::vector<Edges> ecache;
ecache.reserve(nfps.size());
for(auto& nfp : nfps ) {
@@ -557,79 +883,59 @@ public:
ecache.back().accuracy(config_.accuracy);
}
- struct Optimum {
- double relpos;
- unsigned nfpidx;
- int hidx;
- Optimum(double pos, unsigned nidx):
- relpos(pos), nfpidx(nidx), hidx(-1) {}
- Optimum(double pos, unsigned nidx, int holeidx):
- relpos(pos), nfpidx(nidx), hidx(holeidx) {}
- };
-
- auto getNfpPoint = [&ecache](const Optimum& opt)
- {
- return opt.hidx < 0? ecache[opt.nfpidx].coords(opt.relpos) :
- ecache[opt.nfpidx].coords(opt.hidx, opt.relpos);
- };
-
- Nfp::Shapes<RawShape> pile;
+ Shapes pile;
pile.reserve(items_.size()+1);
- double pile_area = 0;
+ // double pile_area = 0;
for(Item& mitem : items_) {
pile.emplace_back(mitem.transformedShape());
- pile_area += mitem.area();
+ // pile_area += mitem.area();
}
- auto merged_pile = Nfp::merge(pile);
+ auto merged_pile = nfp::merge(pile);
+ auto& bin = bin_;
+ double norm = norm_;
+ auto pbb = sl::boundingBox(merged_pile);
+ auto binbb = sl::boundingBox(bin);
// This is the kernel part of the object function that is
// customizable by the library client
auto _objfunc = config_.object_function?
config_.object_function :
- [this, &merged_pile](
- Nfp::Shapes<RawShape>& /*pile*/,
- const Item& item,
- double occupied_area,
- double norm,
- double /*penality*/)
+ [norm, bin, binbb, pbb](const Item& item)
{
- merged_pile.emplace_back(item.transformedShape());
- auto ch = sl::convexHull(merged_pile);
- merged_pile.pop_back();
+ auto ibb = item.boundingBox();
+ auto fullbb = boundingBox(pbb, ibb);
- // The pack ratio -- how much is the convex hull occupied
- double pack_rate = occupied_area/sl::area(ch);
+ double score = pl::distance(ibb.center(), binbb.center());
+ score /= norm;
- // ratio of waste
- double waste = 1.0 - pack_rate;
-
- // Score is the square root of waste. This will extend the
- // range of good (lower) values and shring the range of bad
- // (larger) values.
- auto score = std::sqrt(waste);
-
- if(!wouldFit(ch, bin_)) score += norm;
+ double miss = overfit(fullbb, bin);
+ miss = miss > 0? miss : 0;
+ score += std::pow(miss, 2);
return score;
};
// Our object function for placement
- auto rawobjfunc = [&] (Vertex v)
+ auto rawobjfunc =
+ [_objfunc, iv, startpos] (Vertex v, Item& itm)
{
auto d = v - iv;
d += startpos;
- item.translation(d);
-
- double occupied_area = pile_area + item.area();
-
- double score = _objfunc(pile, item, occupied_area,
- norm_, penality_);
+ itm.translation(d);
+ return _objfunc(itm);
+ };
- return score;
+ auto getNfpPoint = [&ecache](const Optimum& opt)
+ {
+ return opt.hidx < 0? ecache[opt.nfpidx].coords(opt.relpos) :
+ ecache[opt.nfpidx].coords(opt.hidx, opt.relpos);
};
- auto boundaryCheck = [&](const Optimum& o) {
+ auto boundaryCheck =
+ [&merged_pile, &getNfpPoint, &item, &bin, &iv, &startpos]
+ (const Optimum& o)
+ {
auto v = getNfpPoint(o);
auto d = v - iv;
d += startpos;
@@ -639,84 +945,123 @@ public:
auto chull = sl::convexHull(merged_pile);
merged_pile.pop_back();
- return wouldFit(chull, bin_);
+ return overfit(chull, bin);
};
- opt::StopCriteria stopcr;
- stopcr.max_iterations = 100;
- stopcr.relative_score_difference = 1e-6;
- opt::TOptimizer<opt::Method::L_SUBPLEX> solver(stopcr);
-
Optimum optimum(0, 0);
- double best_score = penality_;
+ double best_score = std::numeric_limits<double>::max();
+ std::launch policy = std::launch::deferred;
+ if(config_.parallel) policy |= std::launch::async;
+
+ if(config_.before_packing)
+ config_.before_packing(merged_pile, items_, remlist);
+
+ using OptResult = opt::Result<double>;
+ using OptResults = std::vector<OptResult>;
// Local optimization with the four polygon corners as
// starting points
for(unsigned ch = 0; ch < ecache.size(); ch++) {
auto& cache = ecache[ch];
- auto contour_ofn = [&rawobjfunc, &getNfpPoint, ch]
- (double relpos)
- {
- return rawobjfunc(getNfpPoint(Optimum(relpos, ch)));
- };
+ OptResults results(cache.corners().size());
+
+ auto& rofn = rawobjfunc;
+ auto& nfpoint = getNfpPoint;
- std::for_each(cache.corners().begin(),
- cache.corners().end(),
- [ch, &contour_ofn, &solver, &best_score,
- &optimum, &boundaryCheck] (double pos)
+ __parallel::enumerate(
+ cache.corners().begin(),
+ cache.corners().end(),
+ [&results, &item, &rofn, &nfpoint, ch]
+ (double pos, size_t n)
{
+ Optimizer solver;
+
+ Item itemcpy = item;
+ auto contour_ofn = [&rofn, &nfpoint, ch, &itemcpy]
+ (double relpos)
+ {
+ Optimum op(relpos, ch);
+ return rofn(nfpoint(op), itemcpy);
+ };
+
try {
- auto result = solver.optimize_min(contour_ofn,
+ results[n] = solver.optimize_min(contour_ofn,
opt::initvals<double>(pos),
opt::bound<double>(0, 1.0)
);
-
- if(result.score < best_score) {
- Optimum o(std::get<0>(result.optimum), ch, -1);
- if(boundaryCheck(o)) {
- best_score = result.score;
- optimum = o;
- }
- }
} catch(std::exception& e) {
derr() << "ERROR: " << e.what() << "\n";
}
- });
+ }, policy);
+
+ auto resultcomp =
+ []( const OptResult& r1, const OptResult& r2 ) {
+ return r1.score < r2.score;
+ };
+
+ auto mr = *std::min_element(results.begin(), results.end(),
+ resultcomp);
+
+ if(mr.score < best_score) {
+ Optimum o(std::get<0>(mr.optimum), ch, -1);
+ double miss = boundaryCheck(o);
+ if(miss <= 0) {
+ best_score = mr.score;
+ optimum = o;
+ } else {
+ best_overfit = std::min(miss, best_overfit);
+ }
+ }
for(unsigned hidx = 0; hidx < cache.holeCount(); ++hidx) {
- auto hole_ofn =
- [&rawobjfunc, &getNfpPoint, ch, hidx]
- (double pos)
- {
- Optimum opt(pos, ch, hidx);
- return rawobjfunc(getNfpPoint(opt));
- };
+ results.clear();
+ results.resize(cache.corners(hidx).size());
- std::for_each(cache.corners(hidx).begin(),
+ // TODO : use parallel for
+ __parallel::enumerate(cache.corners(hidx).begin(),
cache.corners(hidx).end(),
- [&hole_ofn, &solver, &best_score,
- &optimum, ch, hidx, &boundaryCheck]
- (double pos)
+ [&results, &item, &nfpoint,
+ &rofn, ch, hidx]
+ (double pos, size_t n)
{
+ Optimizer solver;
+
+ Item itmcpy = item;
+ auto hole_ofn =
+ [&rofn, &nfpoint, ch, hidx, &itmcpy]
+ (double pos)
+ {
+ Optimum opt(pos, ch, hidx);
+ return rofn(nfpoint(opt), itmcpy);
+ };
+
try {
- auto result = solver.optimize_min(hole_ofn,
+ results[n] = solver.optimize_min(hole_ofn,
opt::initvals<double>(pos),
opt::bound<double>(0, 1.0)
);
- if(result.score < best_score) {
- Optimum o(std::get<0>(result.optimum),
- ch, hidx);
- if(boundaryCheck(o)) {
- best_score = result.score;
- optimum = o;
- }
- }
} catch(std::exception& e) {
derr() << "ERROR: " << e.what() << "\n";
}
- });
+ }, policy);
+
+ auto hmr = *std::min_element(results.begin(),
+ results.end(),
+ resultcomp);
+
+ if(hmr.score < best_score) {
+ Optimum o(std::get<0>(hmr.optimum),
+ ch, hidx);
+ double miss = boundaryCheck(o);
+ if(miss <= 0.0) {
+ best_score = hmr.score;
+ optimum = o;
+ } else {
+ best_overfit = std::min(miss, best_overfit);
+ }
+ }
}
}
@@ -736,24 +1081,39 @@ public:
if(can_pack) {
ret = PackResult(item);
+ item_keys_.emplace_back(itemhash);
+ } else {
+ ret = PackResult(best_overfit);
}
return ret;
}
- ~_NofitPolyPlacer() {
- clearItems();
+ inline void finalAlign(const RawShape& pbin) {
+ auto bbin = sl::boundingBox(pbin);
+ finalAlign(bbin);
}
- inline void clearItems() {
- Nfp::Shapes<RawShape> m;
+ inline void finalAlign(_Circle<TPoint<RawShape>> cbin) {
+ if(items_.empty()) return;
+ nfp::Shapes<RawShape> m;
m.reserve(items_.size());
+ for(Item& item : items_) m.emplace_back(item.transformedShape());
+ auto c = boundingCircle(sl::convexHull(m));
+
+ auto d = cbin.center() - c.center();
+ for(Item& item : items_) item.translate(d);
+ }
+
+ inline void finalAlign(Box bbin) {
+ if(items_.empty()) return;
+ nfp::Shapes<RawShape> m;
+ m.reserve(items_.size());
for(Item& item : items_) m.emplace_back(item.transformedShape());
- auto&& bb = sl::boundingBox<RawShape>(m);
+ auto&& bb = sl::boundingBox(m);
Vertex ci, cb;
- auto bbin = sl::boundingBox<RawShape>(bin_);
switch(config_.alignment) {
case Config::Alignment::CENTER: {
@@ -785,16 +1145,12 @@ public:
auto d = cb - ci;
for(Item& item : items_) item.translate(d);
-
- Base::clearItems();
}
-private:
-
void setInitialPosition(Item& item) {
Box&& bb = item.boundingBox();
Vertex ci, cb;
- auto bbin = sl::boundingBox<RawShape>(bin_);
+ auto bbin = sl::boundingBox(bin_);
switch(config_.starting_point) {
case Config::Alignment::CENTER: {
@@ -830,7 +1186,7 @@ private:
void placeOutsideOfBin(Item& item) {
auto&& bb = item.boundingBox();
- Box binbb = sl::boundingBox<RawShape>(bin_);
+ Box binbb = sl::boundingBox(bin_);
Vertex v = { getX(bb.maxCorner()), getY(bb.minCorner()) };
diff --git a/xs/src/libnest2d/libnest2d/placers/placer_boilerplate.hpp b/xs/src/libnest2d/libnest2d/placers/placer_boilerplate.hpp
index 9d2cb626b..0df1b8c91 100644
--- a/xs/src/libnest2d/libnest2d/placers/placer_boilerplate.hpp
+++ b/xs/src/libnest2d/libnest2d/placers/placer_boilerplate.hpp
@@ -3,14 +3,11 @@
#include "../libnest2d.hpp"
-namespace libnest2d { namespace strategies {
+namespace libnest2d { namespace placers {
struct EmptyConfig {};
-template<class Subclass, class RawShape, class TBin,
- class Cfg = EmptyConfig,
- class Store = std::vector<std::reference_wrapper<_Item<RawShape>>>
- >
+template<class Subclass, class RawShape, class TBin, class Cfg = EmptyConfig>
class PlacerBoilerplate {
mutable bool farea_valid_ = false;
mutable double farea_ = 0.0;
@@ -22,25 +19,30 @@ public:
using Coord = TCoord<Vertex>;
using Unit = Coord;
using Config = Cfg;
- using Container = Store;
+ using ItemGroup = _ItemGroup<Item>;
+ using DefaultIter = typename ItemGroup::const_iterator;
class PackResult {
Item *item_ptr_;
Vertex move_;
Radians rot_;
+ double overfit_;
friend class PlacerBoilerplate;
friend Subclass;
+
PackResult(Item& item):
item_ptr_(&item),
move_(item.translation()),
rot_(item.rotation()) {}
- PackResult(): item_ptr_(nullptr) {}
+
+ PackResult(double overfit = 1.0):
+ item_ptr_(nullptr), overfit_(overfit) {}
+
public:
operator bool() { return item_ptr_ != nullptr; }
+ double overfit() const { return overfit_; }
};
- using ItemGroup = const Container&;
-
inline PlacerBoilerplate(const BinType& bin, unsigned cap = 50): bin_(bin)
{
items_.reserve(cap);
@@ -56,8 +58,10 @@ public:
config_ = config;
}
- bool pack(Item& item) {
- auto&& r = static_cast<Subclass*>(this)->trypack(item);
+ template<class Range = ConstItemRange<DefaultIter>>
+ bool pack(Item& item,
+ const Range& rem = Range()) {
+ auto&& r = static_cast<Subclass*>(this)->trypack(item, rem);
if(r) {
items_.push_back(*(r.item_ptr_));
farea_valid_ = false;
@@ -79,14 +83,11 @@ public:
farea_valid_ = false;
}
- inline ItemGroup getItems() const { return items_; }
+ inline const ItemGroup& getItems() const { return items_; }
inline void clearItems() {
items_.clear();
farea_valid_ = false;
-#ifndef NDEBUG
- debug_items_.clear();
-#endif
}
inline double filledArea() const {
@@ -103,14 +104,10 @@ public:
return farea_;
}
-#ifndef NDEBUG
- std::vector<Item> debug_items_;
-#endif
-
protected:
BinType bin_;
- Container items_;
+ ItemGroup items_;
Cfg config_;
};
@@ -121,6 +118,7 @@ using Base::items_; \
using Base::config_; \
public: \
using typename Base::Item; \
+using typename Base::ItemGroup; \
using typename Base::BinType; \
using typename Base::Config; \
using typename Base::Vertex; \
@@ -128,7 +126,6 @@ using typename Base::Segment; \
using typename Base::PackResult; \
using typename Base::Coord; \
using typename Base::Unit; \
-using typename Base::Container; \
private:
}
diff --git a/xs/src/libnest2d/libnest2d/rotfinder.hpp b/xs/src/libnest2d/libnest2d/rotfinder.hpp
new file mode 100644
index 000000000..525fd8759
--- /dev/null
+++ b/xs/src/libnest2d/libnest2d/rotfinder.hpp
@@ -0,0 +1,41 @@
+#ifndef ROTFINDER_HPP
+#define ROTFINDER_HPP
+
+#include <libnest2d/libnest2d.hpp>
+#include <libnest2d/optimizer.hpp>
+#include <iterator>
+
+namespace libnest2d {
+
+template<class RawShape>
+Radians findBestRotation(_Item<RawShape>& item) {
+ opt::StopCriteria stopcr;
+ stopcr.absolute_score_difference = 0.01;
+ stopcr.max_iterations = 10000;
+ opt::TOptimizer<opt::Method::G_GENETIC> solver(stopcr);
+
+ auto orig_rot = item.rotation();
+
+ auto result = solver.optimize_min([&item, &orig_rot](Radians rot){
+ item.rotation(orig_rot + rot);
+ auto bb = item.boundingBox();
+ return std::sqrt(bb.height()*bb.width());
+ }, opt::initvals(Radians(0)), opt::bound<Radians>(-Pi/2, Pi/2));
+
+ item.rotation(orig_rot);
+
+ return std::get<0>(result.optimum);
+}
+
+template<class Iterator>
+void findMinimumBoundingBoxRotations(Iterator from, Iterator to) {
+ using V = typename std::iterator_traits<Iterator>::value_type;
+ std::for_each(from, to, [](V& item){
+ Radians rot = findBestRotation(item);
+ item.rotate(rot);
+ });
+}
+
+}
+
+#endif // ROTFINDER_HPP
diff --git a/xs/src/libnest2d/libnest2d/selections/djd_heuristic.hpp b/xs/src/libnest2d/libnest2d/selections/djd_heuristic.hpp
index e3ad97c10..ee93d0592 100644
--- a/xs/src/libnest2d/libnest2d/selections/djd_heuristic.hpp
+++ b/xs/src/libnest2d/libnest2d/selections/djd_heuristic.hpp
@@ -8,7 +8,7 @@
#include "selection_boilerplate.hpp"
-namespace libnest2d { namespace strategies {
+namespace libnest2d { namespace selections {
/**
* Selection heuristic based on [López-Camacho]\
@@ -118,7 +118,7 @@ public:
using Placer = PlacementStrategyLike<TPlacer>;
using ItemList = std::list<ItemRef>;
- const double bin_area = ShapeLike::area<RawShape>(bin);
+ const double bin_area = sl::area(bin);
const double w = bin_area * config_.waste_increment;
const double INITIAL_FILL_PROPORTION = config_.initial_fill_proportion;
@@ -227,10 +227,14 @@ public:
bool ret = false;
auto it = not_packed.begin();
+ auto pack = [&placer, &not_packed](ItemListIt it) {
+ return placer.pack(*it, rem(it, not_packed));
+ };
+
while(it != not_packed.end() && !ret &&
free_area - (item_area = it->get().area()) <= waste)
{
- if(item_area <= free_area && placer.pack(*it) ) {
+ if(item_area <= free_area && pack(it) ) {
free_area -= item_area;
filled_area = bin_area - free_area;
ret = true;
@@ -270,6 +274,11 @@ public:
auto it2 = it;
std::vector<TPair> wrong_pairs;
+ using std::placeholders::_1;
+
+ auto trypack = [&placer, &not_packed](ItemListIt it) {
+ return placer.trypack(*it, rem(it, not_packed));
+ };
while(it != endit && !ret &&
free_area - (item_area = it->get().area()) -
@@ -278,7 +287,7 @@ public:
if(item_area + smallestPiece(it, not_packed)->get().area() >
free_area ) { it++; continue; }
- auto pr = placer.trypack(*it);
+ auto pr = trypack(it);
// First would fit
it2 = not_packed.begin();
@@ -294,14 +303,14 @@ public:
}
placer.accept(pr);
- auto pr2 = placer.trypack(*it2);
+ auto pr2 = trypack(it2);
if(!pr2) {
placer.unpackLast(); // remove first
if(try_reverse) {
- pr2 = placer.trypack(*it2);
+ pr2 = trypack(it2);
if(pr2) {
placer.accept(pr2);
- auto pr12 = placer.trypack(*it);
+ auto pr12 = trypack(it);
if(pr12) {
placer.accept(pr12);
ret = true;
@@ -365,6 +374,14 @@ public:
return it->get().area();
};
+ auto trypack = [&placer, &not_packed](ItemListIt it) {
+ return placer.trypack(*it, rem(it, not_packed));
+ };
+
+ auto pack = [&placer, &not_packed](ItemListIt it) {
+ return placer.pack(*it, rem(it, not_packed));
+ };
+
while (it != endit && !ret) { // drill down 1st level
// We need to determine in each iteration the largest, second
@@ -394,7 +411,7 @@ public:
it++; continue;
}
- auto pr = placer.trypack(*it);
+ auto pr = trypack(it);
// Check for free area and try to pack the 1st item...
if(!pr) { it++; continue; }
@@ -420,15 +437,15 @@ public:
bool can_pack2 = false;
placer.accept(pr);
- auto pr2 = placer.trypack(*it2);
+ auto pr2 = trypack(it2);
auto pr12 = pr;
if(!pr2) {
placer.unpackLast(); // remove first
if(try_reverse) {
- pr2 = placer.trypack(*it2);
+ pr2 = trypack(it2);
if(pr2) {
placer.accept(pr2);
- pr12 = placer.trypack(*it);
+ pr12 = trypack(it);
if(pr12) can_pack2 = true;
placer.unpackLast();
}
@@ -463,7 +480,7 @@ public:
if(a3_sum > free_area) { it3++; continue; }
placer.accept(pr12); placer.accept(pr2);
- bool can_pack3 = placer.pack(*it3);
+ bool can_pack3 = pack(it3);
if(!can_pack3) {
placer.unpackLast();
@@ -473,16 +490,16 @@ public:
if(!can_pack3 && try_reverse) {
std::array<size_t, 3> indices = {0, 1, 2};
- std::array<ItemRef, 3>
- candidates = {*it, *it2, *it3};
+ std::array<typename ItemList::iterator, 3>
+ candidates = {it, it2, it3};
- auto tryPack = [&placer, &candidates](
+ auto tryPack = [&placer, &candidates, &pack](
const decltype(indices)& idx)
{
std::array<bool, 3> packed = {false};
for(auto id : idx) packed.at(id) =
- placer.pack(candidates[id]);
+ pack(candidates[id]);
bool check =
std::all_of(packed.begin(),
@@ -536,7 +553,7 @@ public:
{ auto it = store_.begin();
while (it != store_.end()) {
Placer p(bin); p.configure(pconfig);
- if(!p.pack(*it)) {
+ if(!p.pack(*it, rem(it, store_))) {
it = store_.erase(it);
} else it++;
}
@@ -551,11 +568,7 @@ public:
{
packed_bins_[idx] = placer.getItems();
-#ifndef NDEBUG
- packed_bins_[idx].insert(packed_bins_[idx].end(),
- placer.getDebugItems().begin(),
- placer.getDebugItems().end());
-#endif
+
// TODO here should be a spinlock
slock.lock();
acounter -= packednum;
@@ -601,7 +614,7 @@ public:
while(it != not_packed.end() &&
filled_area < INITIAL_FILL_AREA)
{
- if(placer.pack(*it)) {
+ if(placer.pack(*it, rem(it, not_packed))) {
filled_area += it->get().area();
free_area = bin_area - filled_area;
it = not_packed.erase(it);
diff --git a/xs/src/libnest2d/libnest2d/selections/filler.hpp b/xs/src/libnest2d/libnest2d/selections/filler.hpp
index d0018dc73..0da7220a1 100644
--- a/xs/src/libnest2d/libnest2d/selections/filler.hpp
+++ b/xs/src/libnest2d/libnest2d/selections/filler.hpp
@@ -3,7 +3,7 @@
#include "selection_boilerplate.hpp"
-namespace libnest2d { namespace strategies {
+namespace libnest2d { namespace selections {
template<class RawShape>
class _FillerSelection: public SelectionBoilerplate<RawShape> {
@@ -56,18 +56,13 @@ public:
std::sort(store_.begin(), store_.end(), sortfunc);
-// Container a = {store_[0], store_[1], store_[4], store_[5] };
-//// a.insert(a.end(), store_.end()-10, store_.end());
-// store_ = a;
-
PlacementStrategyLike<TPlacer> placer(bin);
placer.configure(pconfig);
auto it = store_.begin();
while(it != store_.end()) {
- if(!placer.pack(*it)) {
+ if(!placer.pack(*it, {std::next(it), store_.end()})) {
if(packed_bins_.back().empty()) ++it;
-// makeProgress(placer);
placer.clearItems();
packed_bins_.emplace_back();
} else {
@@ -76,9 +71,6 @@ public:
}
}
-// if(was_packed) {
-// packed_bins_.push_back(placer.getItems());
-// }
}
};
diff --git a/xs/src/libnest2d/libnest2d/selections/firstfit.hpp b/xs/src/libnest2d/libnest2d/selections/firstfit.hpp
index 665b9da9f..bca7497db 100644
--- a/xs/src/libnest2d/libnest2d/selections/firstfit.hpp
+++ b/xs/src/libnest2d/libnest2d/selections/firstfit.hpp
@@ -4,7 +4,7 @@
#include "../libnest2d.hpp"
#include "selection_boilerplate.hpp"
-namespace libnest2d { namespace strategies {
+namespace libnest2d { namespace selections {
template<class RawShape>
class _FirstFitSelection: public SelectionBoilerplate<RawShape> {
@@ -40,6 +40,7 @@ public:
packed_bins_.clear();
std::vector<Placer> placers;
+ placers.reserve(last-first);
std::copy(first, last, std::back_inserter(store_));
@@ -66,21 +67,25 @@ public:
}
}
- for(auto& item : store_ ) {
+ auto it = store_.begin();
+
+ while(it != store_.end()) {
bool was_packed = false;
+ size_t j = 0;
while(!was_packed) {
-
- for(size_t j = 0; j < placers.size() && !was_packed; j++) {
- if((was_packed = placers[j].pack(item)))
- makeProgress(placers[j], j);
+ for(; j < placers.size() && !was_packed; j++) {
+ if((was_packed = placers[j].pack(*it, rem(it, store_) )))
+ makeProgress(placers[j], j);
}
if(!was_packed) {
placers.emplace_back(bin);
placers.back().configure(pconfig);
packed_bins_.emplace_back();
+ j = placers.size() - 1;
}
}
+ ++it;
}
}
diff --git a/xs/src/libnest2d/libnest2d/selections/selection_boilerplate.hpp b/xs/src/libnest2d/libnest2d/selections/selection_boilerplate.hpp
index 59ef5cb23..05bbae658 100644
--- a/xs/src/libnest2d/libnest2d/selections/selection_boilerplate.hpp
+++ b/xs/src/libnest2d/libnest2d/selections/selection_boilerplate.hpp
@@ -3,8 +3,7 @@
#include "../libnest2d.hpp"
-namespace libnest2d {
-namespace strategies {
+namespace libnest2d { namespace selections {
template<class RawShape>
class SelectionBoilerplate {
diff --git a/xs/src/libnest2d/tests/test.cpp b/xs/src/libnest2d/tests/test.cpp
index 39315ff1a..323fb8d31 100644
--- a/xs/src/libnest2d/tests/test.cpp
+++ b/xs/src/libnest2d/tests/test.cpp
@@ -5,6 +5,7 @@
#include "printer_parts.h"
#include <libnest2d/geometry_traits_nfp.hpp>
//#include "../tools/libnfpglue.hpp"
+//#include "../tools/nfp_svgnest_glue.hpp"
std::vector<libnest2d::Item>& prusaParts() {
static std::vector<libnest2d::Item> ret;
@@ -99,6 +100,43 @@ TEST(BasicFunctionality, creationAndDestruction)
}
+TEST(GeometryAlgorithms, boundingCircle) {
+ using namespace libnest2d;
+ using placers::boundingCircle;
+
+ PolygonImpl p = {{{0, 10}, {10, 0}, {0, -10}, {0, 10}}, {}};
+ Circle c = boundingCircle(p);
+
+ ASSERT_EQ(c.center().X, 0);
+ ASSERT_EQ(c.center().Y, 0);
+ ASSERT_DOUBLE_EQ(c.radius(), 10);
+
+ shapelike::translate(p, PointImpl{10, 10});
+ c = boundingCircle(p);
+
+ ASSERT_EQ(c.center().X, 10);
+ ASSERT_EQ(c.center().Y, 10);
+ ASSERT_DOUBLE_EQ(c.radius(), 10);
+
+ auto parts = prusaParts();
+
+ int i = 0;
+ for(auto& part : parts) {
+ c = boundingCircle(part.transformedShape());
+ if(std::isnan(c.radius())) std::cout << "fail: radius is nan" << std::endl;
+
+ else for(auto v : shapelike::getContour(part.transformedShape()) ) {
+ auto d = pointlike::distance(v, c.center());
+ if(d > c.radius() ) {
+ auto e = std::abs( 1.0 - d/c.radius());
+ ASSERT_LE(e, 1e-3);
+ }
+ }
+ i++;
+ }
+
+}
+
TEST(GeometryAlgorithms, Distance) {
using namespace libnest2d;
@@ -107,14 +145,14 @@ TEST(GeometryAlgorithms, Distance) {
Point p2 = {10, 0};
Point p3 = {10, 10};
- ASSERT_DOUBLE_EQ(PointLike::distance(p1, p2), 10);
- ASSERT_DOUBLE_EQ(PointLike::distance(p1, p3), sqrt(200));
+ ASSERT_DOUBLE_EQ(pointlike::distance(p1, p2), 10);
+ ASSERT_DOUBLE_EQ(pointlike::distance(p1, p3), sqrt(200));
Segment seg(p1, p3);
- ASSERT_DOUBLE_EQ(PointLike::distance(p2, seg), 7.0710678118654755);
+ ASSERT_DOUBLE_EQ(pointlike::distance(p2, seg), 7.0710678118654755);
- auto result = PointLike::horizontalDistance(p2, seg);
+ auto result = pointlike::horizontalDistance(p2, seg);
auto check = [](Coord val, Coord expected) {
if(std::is_floating_point<Coord>::value)
@@ -127,11 +165,11 @@ TEST(GeometryAlgorithms, Distance) {
ASSERT_TRUE(result.second);
check(result.first, 10);
- result = PointLike::verticalDistance(p2, seg);
+ result = pointlike::verticalDistance(p2, seg);
ASSERT_TRUE(result.second);
check(result.first, -10);
- result = PointLike::verticalDistance(Point{10, 20}, seg);
+ result = pointlike::verticalDistance(Point{10, 20}, seg);
ASSERT_TRUE(result.second);
check(result.first, 10);
@@ -139,12 +177,12 @@ TEST(GeometryAlgorithms, Distance) {
Point p4 = {80, 0};
Segment seg2 = { {0, 0}, {0, 40} };
- result = PointLike::horizontalDistance(p4, seg2);
+ result = pointlike::horizontalDistance(p4, seg2);
ASSERT_TRUE(result.second);
check(result.first, 80);
- result = PointLike::verticalDistance(p4, seg2);
+ result = pointlike::verticalDistance(p4, seg2);
// Point should not be related to the segment
ASSERT_FALSE(result.second);
@@ -172,7 +210,7 @@ TEST(GeometryAlgorithms, Area) {
{61, 97}
};
- ASSERT_TRUE(ShapeLike::area(item.transformedShape()) > 0 );
+ ASSERT_TRUE(shapelike::area(item.transformedShape()) > 0 );
}
TEST(GeometryAlgorithms, IsPointInsidePolygon) {
@@ -182,21 +220,21 @@ TEST(GeometryAlgorithms, IsPointInsidePolygon) {
Point p = {1, 1};
- ASSERT_TRUE(rect.isPointInside(p));
+ ASSERT_TRUE(rect.isInside(p));
p = {11, 11};
- ASSERT_FALSE(rect.isPointInside(p));
+ ASSERT_FALSE(rect.isInside(p));
p = {11, 12};
- ASSERT_FALSE(rect.isPointInside(p));
+ ASSERT_FALSE(rect.isInside(p));
p = {3, 3};
- ASSERT_TRUE(rect.isPointInside(p));
+ ASSERT_TRUE(rect.isInside(p));
}
@@ -250,7 +288,7 @@ TEST(GeometryAlgorithms, LeftAndDownPolygon)
Item leftp(placer.leftPoly(item));
- ASSERT_TRUE(ShapeLike::isValid(leftp.rawShape()).first);
+ ASSERT_TRUE(shapelike::isValid(leftp.rawShape()).first);
ASSERT_EQ(leftp.vertexCount(), leftControl.vertexCount());
for(unsigned long i = 0; i < leftControl.vertexCount(); i++) {
@@ -260,7 +298,7 @@ TEST(GeometryAlgorithms, LeftAndDownPolygon)
Item downp(placer.downPoly(item));
- ASSERT_TRUE(ShapeLike::isValid(downp.rawShape()).first);
+ ASSERT_TRUE(shapelike::isValid(downp.rawShape()).first);
ASSERT_EQ(downp.vertexCount(), downControl.vertexCount());
for(unsigned long i = 0; i < downControl.vertexCount(); i++) {
@@ -297,7 +335,7 @@ TEST(GeometryAlgorithms, ArrangeRectanglesTight)
{20, 20} };
- Arranger<BottomLeftPlacer, DJDHeuristic> arrange(Box(210, 250));
+ Nester<BottomLeftPlacer, DJDHeuristic> arrange(Box(210, 250));
auto groups = arrange(rects.begin(), rects.end());
@@ -350,7 +388,7 @@ TEST(GeometryAlgorithms, ArrangeRectanglesLoose)
Coord min_obj_distance = 5;
- Arranger<BottomLeftPlacer, DJDHeuristic> arrange(Box(210, 250),
+ Nester<BottomLeftPlacer, DJDHeuristic> arrange(Box(210, 250),
min_obj_distance);
auto groups = arrange(rects.begin(), rects.end());
@@ -401,7 +439,7 @@ R"raw(<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
setX(v, getX(v)/SCALE);
rbin.setVertex(i, v);
}
- out << ShapeLike::serialize<Formats::SVG>(rbin.rawShape()) << std::endl;
+ out << shapelike::serialize<Formats::SVG>(rbin.rawShape()) << std::endl;
for(Item& sh : r) {
Item tsh(sh.transformedShape());
for(unsigned i = 0; i < tsh.vertexCount(); i++) {
@@ -410,7 +448,7 @@ R"raw(<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
setX(v, getX(v)/SCALE);
tsh.setVertex(i, v);
}
- out << ShapeLike::serialize<Formats::SVG>(tsh.rawShape()) << std::endl;
+ out << shapelike::serialize<Formats::SVG>(tsh.rawShape()) << std::endl;
}
out << "\n</svg>" << std::endl;
}
@@ -664,7 +702,7 @@ std::vector<ItemPair> nfp_concave_testdata = {
}
};
-template<NfpLevel lvl, Coord SCALE>
+template<nfp::NfpLevel lvl, Coord SCALE>
void testNfp(const std::vector<ItemPair>& testdata) {
using namespace libnest2d;
@@ -674,29 +712,33 @@ void testNfp(const std::vector<ItemPair>& testdata) {
auto& exportfun = exportSVG<SCALE, Box>;
- auto onetest = [&](Item& orbiter, Item& stationary){
+ auto onetest = [&](Item& orbiter, Item& stationary, unsigned testidx){
testcase++;
orbiter.translate({210*SCALE, 0});
- auto&& nfp = Nfp::noFitPolygon<lvl>(stationary.rawShape(),
+ auto&& nfp = nfp::noFitPolygon<lvl>(stationary.rawShape(),
orbiter.transformedShape());
- strategies::correctNfpPosition(nfp, stationary, orbiter);
+ placers::correctNfpPosition(nfp, stationary, orbiter);
- auto v = ShapeLike::isValid(nfp.first);
+ auto valid = shapelike::isValid(nfp.first);
- if(!v.first) {
- std::cout << v.second << std::endl;
- }
+ /*Item infp(nfp.first);
+ if(!valid.first) {
+ std::cout << "test instance: " << testidx << " "
+ << valid.second << std::endl;
+ std::vector<std::reference_wrapper<Item>> inp = {std::ref(infp)};
+ exportfun(inp, bin, testidx);
+ }*/
- ASSERT_TRUE(v.first);
+ ASSERT_TRUE(valid.first);
Item infp(nfp.first);
int i = 0;
auto rorbiter = orbiter.transformedShape();
- auto vo = Nfp::referenceVertex(rorbiter);
+ auto vo = nfp::referenceVertex(rorbiter);
ASSERT_TRUE(stationary.isInside(infp));
@@ -710,7 +752,7 @@ void testNfp(const std::vector<ItemPair>& testdata) {
bool touching = Item::touches(tmp, stationary);
- if(!touching) {
+ if(!touching || !valid.first) {
std::vector<std::reference_wrapper<Item>> inp = {
std::ref(stationary), std::ref(tmp), std::ref(infp)
};
@@ -722,22 +764,24 @@ void testNfp(const std::vector<ItemPair>& testdata) {
}
};
+ unsigned tidx = 0;
for(auto& td : testdata) {
auto orbiter = td.orbiter;
auto stationary = td.stationary;
- onetest(orbiter, stationary);
+ onetest(orbiter, stationary, tidx++);
}
+ tidx = 0;
for(auto& td : testdata) {
auto orbiter = td.stationary;
auto stationary = td.orbiter;
- onetest(orbiter, stationary);
+ onetest(orbiter, stationary, tidx++);
}
}
}
TEST(GeometryAlgorithms, nfpConvexConvex) {
- testNfp<NfpLevel::CONVEX_ONLY, 1>(nfp_testdata);
+ testNfp<nfp::NfpLevel::CONVEX_ONLY, 1>(nfp_testdata);
}
//TEST(GeometryAlgorithms, nfpConcaveConcave) {
@@ -758,7 +802,7 @@ TEST(GeometryAlgorithms, pointOnPolygonContour) {
Rectangle input(10, 10);
- strategies::EdgeCache<PolygonImpl> ecache(input);
+ placers::EdgeCache<PolygonImpl> ecache(input);
auto first = *input.begin();
ASSERT_TRUE(getX(first) == getX(ecache.coords(0)));
@@ -770,7 +814,7 @@ TEST(GeometryAlgorithms, pointOnPolygonContour) {
for(int i = 0; i <= 100; i++) {
auto v = ecache.coords(i*(0.01));
- ASSERT_TRUE(ShapeLike::touches(v, input.transformedShape()));
+ ASSERT_TRUE(shapelike::touches(v, input.transformedShape()));
}
}
@@ -784,17 +828,17 @@ TEST(GeometryAlgorithms, mergePileWithPolygon) {
rect2.translate({10, 0});
rect3.translate({25, 0});
- ShapeLike::Shapes<PolygonImpl> pile;
+ shapelike::Shapes<PolygonImpl> pile;
pile.push_back(rect1.transformedShape());
pile.push_back(rect2.transformedShape());
- auto result = Nfp::merge(pile, rect3.transformedShape());
+ auto result = nfp::merge(pile, rect3.transformedShape());
ASSERT_EQ(result.size(), 1);
Rectangle ref(45, 15);
- ASSERT_EQ(ShapeLike::area(result.front()), ref.area());
+ ASSERT_EQ(shapelike::area(result.front()), ref.area());
}
int main(int argc, char **argv) {
diff --git a/xs/src/libnest2d/tools/libnfpglue.cpp b/xs/src/libnest2d/tools/libnfpglue.cpp
index 18656fd40..31733acf9 100644
--- a/xs/src/libnest2d/tools/libnfpglue.cpp
+++ b/xs/src/libnest2d/tools/libnfpglue.cpp
@@ -56,7 +56,7 @@ libnfporb::point_t scale(const libnfporb::point_t& p, long double factor) {
NfpR _nfp(const PolygonImpl &sh, const PolygonImpl &cother)
{
- using Vertex = PointImpl;
+ namespace sl = shapelike;
NfpR ret;
@@ -85,7 +85,7 @@ NfpR _nfp(const PolygonImpl &sh, const PolygonImpl &cother)
// this can throw
auto nfp = libnfporb::generateNFP(pstat, porb, true);
- auto &ct = ShapeLike::getContour(ret.first);
+ auto &ct = sl::getContour(ret.first);
ct.reserve(nfp.front().size()+1);
for(auto v : nfp.front()) {
v = scale(v, refactor);
@@ -94,7 +94,7 @@ NfpR _nfp(const PolygonImpl &sh, const PolygonImpl &cother)
ct.push_back(ct.front());
std::reverse(ct.begin(), ct.end());
- auto &rholes = ShapeLike::holes(ret.first);
+ auto &rholes = sl::holes(ret.first);
for(size_t hidx = 1; hidx < nfp.size(); ++hidx) {
if(nfp[hidx].size() >= 3) {
rholes.emplace_back();
@@ -110,31 +110,31 @@ NfpR _nfp(const PolygonImpl &sh, const PolygonImpl &cother)
}
}
- ret.second = Nfp::referenceVertex(ret.first);
+ ret.second = nfp::referenceVertex(ret.first);
} catch(std::exception& e) {
std::cout << "Error: " << e.what() << "\nTrying with convex hull..." << std::endl;
// auto ch_stat = ShapeLike::convexHull(sh);
// auto ch_orb = ShapeLike::convexHull(cother);
- ret = Nfp::nfpConvexOnly(sh, cother);
+ ret = nfp::nfpConvexOnly(sh, cother);
}
return ret;
}
-NfpR Nfp::NfpImpl<PolygonImpl, NfpLevel::CONVEX_ONLY>::operator()(
+NfpR nfp::NfpImpl<PolygonImpl, nfp::NfpLevel::CONVEX_ONLY>::operator()(
const PolygonImpl &sh, const ClipperLib::PolygonImpl &cother)
{
return _nfp(sh, cother);//nfpConvexOnly(sh, cother);
}
-NfpR Nfp::NfpImpl<PolygonImpl, NfpLevel::ONE_CONVEX>::operator()(
+NfpR nfp::NfpImpl<PolygonImpl, nfp::NfpLevel::ONE_CONVEX>::operator()(
const PolygonImpl &sh, const ClipperLib::PolygonImpl &cother)
{
return _nfp(sh, cother);
}
-NfpR Nfp::NfpImpl<PolygonImpl, NfpLevel::BOTH_CONCAVE>::operator()(
+NfpR nfp::NfpImpl<PolygonImpl, nfp::NfpLevel::BOTH_CONCAVE>::operator()(
const PolygonImpl &sh, const ClipperLib::PolygonImpl &cother)
{
return _nfp(sh, cother);
diff --git a/xs/src/libnest2d/tools/libnfpglue.hpp b/xs/src/libnest2d/tools/libnfpglue.hpp
index 75f639445..1ff033cb9 100644
--- a/xs/src/libnest2d/tools/libnfpglue.hpp
+++ b/xs/src/libnest2d/tools/libnfpglue.hpp
@@ -5,22 +5,22 @@
namespace libnest2d {
-using NfpR = Nfp::NfpResult<PolygonImpl>;
+using NfpR = nfp::NfpResult<PolygonImpl>;
NfpR _nfp(const PolygonImpl& sh, const PolygonImpl& cother);
template<>
-struct Nfp::NfpImpl<PolygonImpl, NfpLevel::CONVEX_ONLY> {
+struct nfp::NfpImpl<PolygonImpl, nfp::NfpLevel::CONVEX_ONLY> {
NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother);
};
template<>
-struct Nfp::NfpImpl<PolygonImpl, NfpLevel::ONE_CONVEX> {
+struct nfp::NfpImpl<PolygonImpl, nfp::NfpLevel::ONE_CONVEX> {
NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother);
};
template<>
-struct Nfp::NfpImpl<PolygonImpl, NfpLevel::BOTH_CONCAVE> {
+struct nfp::NfpImpl<PolygonImpl, nfp::NfpLevel::BOTH_CONCAVE> {
NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother);
};
@@ -34,7 +34,7 @@ struct Nfp::NfpImpl<PolygonImpl, NfpLevel::BOTH_CONCAVE> {
// NfpResult operator()(const PolygonImpl& sh, const PolygonImpl& cother);
//};
-template<> struct Nfp::MaxNfpLevel<PolygonImpl> {
+template<> struct nfp::MaxNfpLevel<PolygonImpl> {
static const BP2D_CONSTEXPR NfpLevel value =
// NfpLevel::CONVEX_ONLY;
NfpLevel::BOTH_CONCAVE;
diff --git a/xs/src/libnest2d/tools/nfp_svgnest.hpp b/xs/src/libnest2d/tools/nfp_svgnest.hpp
new file mode 100644
index 000000000..ac5700c10
--- /dev/null
+++ b/xs/src/libnest2d/tools/nfp_svgnest.hpp
@@ -0,0 +1,1018 @@
+#ifndef NFP_SVGNEST_HPP
+#define NFP_SVGNEST_HPP
+
+#include <limits>
+#include <unordered_map>
+
+#include <libnest2d/geometry_traits_nfp.hpp>
+
+namespace libnest2d {
+
+namespace __svgnest {
+
+using std::sqrt;
+using std::min;
+using std::max;
+using std::abs;
+using std::isnan;
+
+//template<class Coord> struct _Scale {
+// static const BP2D_CONSTEXPR long long Value = 1000000;
+//};
+
+template<class S> struct _alg {
+ using Contour = TContour<S>;
+ using Point = TPoint<S>;
+ using iCoord = TCoord<Point>;
+ using Coord = double;
+ using Shapes = nfp::Shapes<S>;
+
+ static const Coord TOL;
+
+#define dNAN std::nan("")
+
+ struct Vector {
+ Coord x = 0.0, y = 0.0;
+ bool marked = false;
+ Vector() = default;
+ Vector(Coord X, Coord Y): x(X), y(Y) {}
+ Vector(const Point& p): x(Coord(getX(p))), y(Coord(getY(p))) {}
+ operator Point() const { return {iCoord(x), iCoord(y)}; }
+ Vector& operator=(const Point& p) {
+ x = getX(p), y = getY(p); return *this;
+ }
+ bool operator!=(const Vector& v) const {
+ return v.x != x || v.y != y;
+ }
+ Vector(std::initializer_list<Coord> il):
+ x(*il.begin()), y(*std::next(il.begin())) {}
+ };
+
+ static inline Coord x(const Point& p) { return Coord(getX(p)); }
+ static inline Coord y(const Point& p) { return Coord(getY(p)); }
+
+ static inline Coord x(const Vector& p) { return p.x; }
+ static inline Coord y(const Vector& p) { return p.y; }
+
+ class Cntr {
+ std::vector<Vector> v_;
+ public:
+ Cntr(const Contour& c) {
+ v_.reserve(c.size());
+ std::transform(c.begin(), c.end(), std::back_inserter(v_),
+ [](const Point& p) {
+ return Vector(double(x(p)) / 1e6, double(y(p)) / 1e6);
+ });
+ std::reverse(v_.begin(), v_.end());
+ v_.pop_back();
+ }
+ Cntr() = default;
+
+ Coord offsetx = 0;
+ Coord offsety = 0;
+ size_t size() const { return v_.size(); }
+ bool empty() const { return v_.empty(); }
+ typename std::vector<Vector>::const_iterator cbegin() const { return v_.cbegin(); }
+ typename std::vector<Vector>::const_iterator cend() const { return v_.cend(); }
+ typename std::vector<Vector>::iterator begin() { return v_.begin(); }
+ typename std::vector<Vector>::iterator end() { return v_.end(); }
+ Vector& operator[](size_t idx) { return v_[idx]; }
+ const Vector& operator[](size_t idx) const { return v_[idx]; }
+ template<class...Args>
+ void emplace_back(Args&&...args) {
+ v_.emplace_back(std::forward<Args>(args)...);
+ }
+ template<class...Args>
+ void push(Args&&...args) {
+ v_.emplace_back(std::forward<Args>(args)...);
+ }
+ void clear() { v_.clear(); }
+
+ operator Contour() const {
+ Contour cnt;
+ cnt.reserve(v_.size() + 1);
+ std::transform(v_.begin(), v_.end(), std::back_inserter(cnt),
+ [](const Vector& vertex) {
+ return Point(iCoord(vertex.x) * 1000000, iCoord(vertex.y) * 1000000);
+ });
+ if(!cnt.empty()) cnt.emplace_back(cnt.front());
+ S sh = shapelike::create<S>(cnt);
+
+// std::reverse(cnt.begin(), cnt.end());
+ return shapelike::getContour(sh);
+ }
+ };
+
+ inline static bool _almostEqual(Coord a, Coord b,
+ Coord tolerance = TOL)
+ {
+ return std::abs(a - b) < tolerance;
+ }
+
+ // returns true if p lies on the line segment defined by AB,
+ // but not at any endpoints may need work!
+ static bool _onSegment(const Vector& A, const Vector& B, const Vector& p) {
+
+ // vertical line
+ if(_almostEqual(A.x, B.x) && _almostEqual(p.x, A.x)) {
+ if(!_almostEqual(p.y, B.y) && !_almostEqual(p.y, A.y) &&
+ p.y < max(B.y, A.y) && p.y > min(B.y, A.y)){
+ return true;
+ }
+ else{
+ return false;
+ }
+ }
+
+ // horizontal line
+ if(_almostEqual(A.y, B.y) && _almostEqual(p.y, A.y)){
+ if(!_almostEqual(p.x, B.x) && !_almostEqual(p.x, A.x) &&
+ p.x < max(B.x, A.x) && p.x > min(B.x, A.x)){
+ return true;
+ }
+ else{
+ return false;
+ }
+ }
+
+ //range check
+ if((p.x < A.x && p.x < B.x) || (p.x > A.x && p.x > B.x) ||
+ (p.y < A.y && p.y < B.y) || (p.y > A.y && p.y > B.y))
+ return false;
+
+ // exclude end points
+ if((_almostEqual(p.x, A.x) && _almostEqual(p.y, A.y)) ||
+ (_almostEqual(p.x, B.x) && _almostEqual(p.y, B.y)))
+ return false;
+
+
+ double cross = (p.y - A.y) * (B.x - A.x) - (p.x - A.x) * (B.y - A.y);
+
+ if(abs(cross) > TOL) return false;
+
+ double dot = (p.x - A.x) * (B.x - A.x) + (p.y - A.y)*(B.y - A.y);
+
+ if(dot < 0 || _almostEqual(dot, 0)) return false;
+
+ double len2 = (B.x - A.x)*(B.x - A.x) + (B.y - A.y)*(B.y - A.y);
+
+ if(dot > len2 || _almostEqual(dot, len2)) return false;
+
+ return true;
+ }
+
+ // return true if point is in the polygon, false if outside, and null if exactly on a point or edge
+ static int pointInPolygon(const Vector& point, const Cntr& polygon) {
+ if(polygon.size() < 3){
+ return 0;
+ }
+
+ bool inside = false;
+ Coord offsetx = polygon.offsetx;
+ Coord offsety = polygon.offsety;
+
+ for (size_t i = 0, j = polygon.size() - 1; i < polygon.size(); j=i++) {
+ auto xi = polygon[i].x + offsetx;
+ auto yi = polygon[i].y + offsety;
+ auto xj = polygon[j].x + offsetx;
+ auto yj = polygon[j].y + offsety;
+
+ if(_almostEqual(xi, point.x) && _almostEqual(yi, point.y)){
+ return 0; // no result
+ }
+
+ if(_onSegment({xi, yi}, {xj, yj}, point)){
+ return 0; // exactly on the segment
+ }
+
+ if(_almostEqual(xi, xj) && _almostEqual(yi, yj)){ // ignore very small lines
+ continue;
+ }
+
+ bool intersect = ((yi > point.y) != (yj > point.y)) &&
+ (point.x < (xj - xi) * (point.y - yi) / (yj - yi) + xi);
+ if (intersect) inside = !inside;
+ }
+
+ return inside? 1 : -1;
+ }
+
+ static bool intersect(const Cntr& A, const Cntr& B){
+ Contour a = A, b = B;
+ return shapelike::intersects(shapelike::create<S>(a), shapelike::create<S>(b));
+ }
+
+ static Vector _normalizeVector(const Vector& v) {
+ if(_almostEqual(v.x*v.x + v.y*v.y, Coord(1))){
+ return Point(v); // given vector was already a unit vector
+ }
+ auto len = sqrt(v.x*v.x + v.y*v.y);
+ auto inverse = 1/len;
+
+ return { Coord(v.x*inverse), Coord(v.y*inverse) };
+ }
+
+ static double pointDistance( const Vector& p,
+ const Vector& s1,
+ const Vector& s2,
+ Vector normal,
+ bool infinite = false)
+ {
+ normal = _normalizeVector(normal);
+
+ Vector dir = {
+ normal.y,
+ -normal.x
+ };
+
+ auto pdot = p.x*dir.x + p.y*dir.y;
+ auto s1dot = s1.x*dir.x + s1.y*dir.y;
+ auto s2dot = s2.x*dir.x + s2.y*dir.y;
+
+ auto pdotnorm = p.x*normal.x + p.y*normal.y;
+ auto s1dotnorm = s1.x*normal.x + s1.y*normal.y;
+ auto s2dotnorm = s2.x*normal.x + s2.y*normal.y;
+
+ if(!infinite){
+ if (((pdot<s1dot || _almostEqual(pdot, s1dot)) &&
+ (pdot<s2dot || _almostEqual(pdot, s2dot))) ||
+ ((pdot>s1dot || _almostEqual(pdot, s1dot)) &&
+ (pdot>s2dot || _almostEqual(pdot, s2dot))))
+ {
+ // dot doesn't collide with segment,
+ // or lies directly on the vertex
+ return dNAN;
+ }
+ if ((_almostEqual(pdot, s1dot) && _almostEqual(pdot, s2dot)) &&
+ (pdotnorm>s1dotnorm && pdotnorm>s2dotnorm))
+ {
+ return min(pdotnorm - s1dotnorm, pdotnorm - s2dotnorm);
+ }
+ if ((_almostEqual(pdot, s1dot) && _almostEqual(pdot, s2dot)) &&
+ (pdotnorm<s1dotnorm && pdotnorm<s2dotnorm)){
+ return -min(s1dotnorm-pdotnorm, s2dotnorm-pdotnorm);
+ }
+ }
+
+ return -(pdotnorm - s1dotnorm + (s1dotnorm - s2dotnorm)*(s1dot - pdot)
+ / double(s1dot - s2dot));
+ }
+
+ static double segmentDistance( const Vector& A,
+ const Vector& B,
+ const Vector& E,
+ const Vector& F,
+ Vector direction)
+ {
+ Vector normal = {
+ direction.y,
+ -direction.x
+ };
+
+ Vector reverse = {
+ -direction.x,
+ -direction.y
+ };
+
+ auto dotA = A.x*normal.x + A.y*normal.y;
+ auto dotB = B.x*normal.x + B.y*normal.y;
+ auto dotE = E.x*normal.x + E.y*normal.y;
+ auto dotF = F.x*normal.x + F.y*normal.y;
+
+ auto crossA = A.x*direction.x + A.y*direction.y;
+ auto crossB = B.x*direction.x + B.y*direction.y;
+ auto crossE = E.x*direction.x + E.y*direction.y;
+ auto crossF = F.x*direction.x + F.y*direction.y;
+
+// auto crossABmin = min(crossA, crossB);
+// auto crossABmax = max(crossA, crossB);
+
+// auto crossEFmax = max(crossE, crossF);
+// auto crossEFmin = min(crossE, crossF);
+
+ auto ABmin = min(dotA, dotB);
+ auto ABmax = max(dotA, dotB);
+
+ auto EFmax = max(dotE, dotF);
+ auto EFmin = min(dotE, dotF);
+
+ // segments that will merely touch at one point
+ if(_almostEqual(ABmax, EFmin, TOL) || _almostEqual(ABmin, EFmax,TOL)) {
+ return dNAN;
+ }
+ // segments miss eachother completely
+ if(ABmax < EFmin || ABmin > EFmax){
+ return dNAN;
+ }
+
+ double overlap = 0;
+
+ if((ABmax > EFmax && ABmin < EFmin) || (EFmax > ABmax && EFmin < ABmin))
+ {
+ overlap = 1;
+ }
+ else{
+ auto minMax = min(ABmax, EFmax);
+ auto maxMin = max(ABmin, EFmin);
+
+ auto maxMax = max(ABmax, EFmax);
+ auto minMin = min(ABmin, EFmin);
+
+ overlap = (minMax-maxMin)/(maxMax-minMin);
+ }
+
+ auto crossABE = (E.y - A.y) * (B.x - A.x) - (E.x - A.x) * (B.y - A.y);
+ auto crossABF = (F.y - A.y) * (B.x - A.x) - (F.x - A.x) * (B.y - A.y);
+
+ // lines are colinear
+ if(_almostEqual(crossABE,0) && _almostEqual(crossABF,0)){
+
+ Vector ABnorm = {B.y-A.y, A.x-B.x};
+ Vector EFnorm = {F.y-E.y, E.x-F.x};
+
+ auto ABnormlength = sqrt(ABnorm.x*ABnorm.x + ABnorm.y*ABnorm.y);
+ ABnorm.x /= ABnormlength;
+ ABnorm.y /= ABnormlength;
+
+ auto EFnormlength = sqrt(EFnorm.x*EFnorm.x + EFnorm.y*EFnorm.y);
+ EFnorm.x /= EFnormlength;
+ EFnorm.y /= EFnormlength;
+
+ // segment normals must point in opposite directions
+ if(abs(ABnorm.y * EFnorm.x - ABnorm.x * EFnorm.y) < TOL &&
+ ABnorm.y * EFnorm.y + ABnorm.x * EFnorm.x < 0){
+ // normal of AB segment must point in same direction as
+ // given direction vector
+ auto normdot = ABnorm.y * direction.y + ABnorm.x * direction.x;
+ // the segments merely slide along eachother
+ if(_almostEqual(normdot,0, TOL)){
+ return dNAN;
+ }
+ if(normdot < 0){
+ return 0.0;
+ }
+ }
+ return dNAN;
+ }
+
+ std::vector<double> distances; distances.reserve(10);
+
+ // coincident points
+ if(_almostEqual(dotA, dotE)){
+ distances.emplace_back(crossA-crossE);
+ }
+ else if(_almostEqual(dotA, dotF)){
+ distances.emplace_back(crossA-crossF);
+ }
+ else if(dotA > EFmin && dotA < EFmax){
+ auto d = pointDistance(A,E,F,reverse);
+ if(!isnan(d) && _almostEqual(d, 0))
+ { // A currently touches EF, but AB is moving away from EF
+ auto dB = pointDistance(B,E,F,reverse,true);
+ if(dB < 0 || _almostEqual(dB*overlap,0)){
+ d = dNAN;
+ }
+ }
+ if(!isnan(d)){
+ distances.emplace_back(d);
+ }
+ }
+
+ if(_almostEqual(dotB, dotE)){
+ distances.emplace_back(crossB-crossE);
+ }
+ else if(_almostEqual(dotB, dotF)){
+ distances.emplace_back(crossB-crossF);
+ }
+ else if(dotB > EFmin && dotB < EFmax){
+ auto d = pointDistance(B,E,F,reverse);
+
+ if(!isnan(d) && _almostEqual(d, 0))
+ { // crossA>crossB A currently touches EF, but AB is moving away from EF
+ double dA = pointDistance(A,E,F,reverse,true);
+ if(dA < 0 || _almostEqual(dA*overlap,0)){
+ d = dNAN;
+ }
+ }
+ if(!isnan(d)){
+ distances.emplace_back(d);
+ }
+ }
+
+ if(dotE > ABmin && dotE < ABmax){
+ auto d = pointDistance(E,A,B,direction);
+ if(!isnan(d) && _almostEqual(d, 0))
+ { // crossF<crossE A currently touches EF, but AB is moving away from EF
+ double dF = pointDistance(F,A,B,direction, true);
+ if(dF < 0 || _almostEqual(dF*overlap,0)){
+ d = dNAN;
+ }
+ }
+ if(!isnan(d)){
+ distances.emplace_back(d);
+ }
+ }
+
+ if(dotF > ABmin && dotF < ABmax){
+ auto d = pointDistance(F,A,B,direction);
+ if(!isnan(d) && _almostEqual(d, 0))
+ { // && crossE<crossF A currently touches EF,
+ // but AB is moving away from EF
+ double dE = pointDistance(E,A,B,direction, true);
+ if(dE < 0 || _almostEqual(dE*overlap,0)){
+ d = dNAN;
+ }
+ }
+ if(!isnan(d)){
+ distances.emplace_back(d);
+ }
+ }
+
+ if(distances.empty()){
+ return dNAN;
+ }
+
+ return *std::min_element(distances.begin(), distances.end());
+ }
+
+ static double polygonSlideDistance( const Cntr& AA,
+ const Cntr& BB,
+ Vector direction,
+ bool ignoreNegative)
+ {
+// Vector A1, A2, B1, B2;
+ Cntr A = AA;
+ Cntr B = BB;
+
+ Coord Aoffsetx = A.offsetx;
+ Coord Boffsetx = B.offsetx;
+ Coord Aoffsety = A.offsety;
+ Coord Boffsety = B.offsety;
+
+ // close the loop for polygons
+ if(A[0] != A[A.size()-1]){
+ A.emplace_back(AA[0]);
+ }
+
+ if(B[0] != B[B.size()-1]){
+ B.emplace_back(BB[0]);
+ }
+
+ auto& edgeA = A;
+ auto& edgeB = B;
+
+ double distance = dNAN, d = dNAN;
+
+ Vector dir = _normalizeVector(direction);
+
+// Vector normal = {
+// dir.y,
+// -dir.x
+// };
+
+// Vector reverse = {
+// -dir.x,
+// -dir.y,
+// };
+
+ for(size_t i = 0; i < edgeB.size() - 1; i++){
+ for(size_t j = 0; j < edgeA.size() - 1; j++){
+ Vector A1 = {x(edgeA[j]) + Aoffsetx, y(edgeA[j]) + Aoffsety };
+ Vector A2 = {x(edgeA[j+1]) + Aoffsetx, y(edgeA[j+1]) + Aoffsety};
+ Vector B1 = {x(edgeB[i]) + Boffsetx, y(edgeB[i]) + Boffsety };
+ Vector B2 = {x(edgeB[i+1]) + Boffsetx, y(edgeB[i+1]) + Boffsety};
+
+ if((_almostEqual(A1.x, A2.x) && _almostEqual(A1.y, A2.y)) ||
+ (_almostEqual(B1.x, B2.x) && _almostEqual(B1.y, B2.y))){
+ continue; // ignore extremely small lines
+ }
+
+ d = segmentDistance(A1, A2, B1, B2, dir);
+
+ if(!isnan(d) && (isnan(distance) || d < distance)){
+ if(!ignoreNegative || d > 0 || _almostEqual(d, 0)){
+ distance = d;
+ }
+ }
+ }
+ }
+ return distance;
+ }
+
+ static double polygonProjectionDistance(const Cntr& AA,
+ const Cntr& BB,
+ Vector direction)
+ {
+ Cntr A = AA;
+ Cntr B = BB;
+
+ auto Boffsetx = B.offsetx;
+ auto Boffsety = B.offsety;
+ auto Aoffsetx = A.offsetx;
+ auto Aoffsety = A.offsety;
+
+ // close the loop for polygons
+ if(A[0] != A[A.size()-1]){
+ A.push(A[0]);
+ }
+
+ if(B[0] != B[B.size()-1]){
+ B.push(B[0]);
+ }
+
+ auto& edgeA = A;
+ auto& edgeB = B;
+
+ double distance = dNAN, d;
+// Vector p, s1, s2;
+
+ for(size_t i = 0; i < edgeB.size(); i++) {
+ // the shortest/most negative projection of B onto A
+ double minprojection = dNAN;
+ Vector minp;
+ for(size_t j = 0; j < edgeA.size() - 1; j++){
+ Vector p = {x(edgeB[i]) + Boffsetx, y(edgeB[i]) + Boffsety };
+ Vector s1 = {x(edgeA[j]) + Aoffsetx, y(edgeA[j]) + Aoffsety };
+ Vector s2 = {x(edgeA[j+1]) + Aoffsetx, y(edgeA[j+1]) + Aoffsety };
+
+ if(abs((s2.y-s1.y) * direction.x -
+ (s2.x-s1.x) * direction.y) < TOL) continue;
+
+ // project point, ignore edge boundaries
+ d = pointDistance(p, s1, s2, direction);
+
+ if(!isnan(d) && (isnan(minprojection) || d < minprojection)) {
+ minprojection = d;
+ minp = p;
+ }
+ }
+
+ if(!isnan(minprojection) && (isnan(distance) ||
+ minprojection > distance)){
+ distance = minprojection;
+ }
+ }
+
+ return distance;
+ }
+
+ static std::pair<bool, Vector> searchStartPoint(
+ const Cntr& AA, const Cntr& BB, bool inside, const std::vector<Cntr>& NFP = {})
+ {
+ // clone arrays
+ auto A = AA;
+ auto B = BB;
+
+// // close the loop for polygons
+// if(A[0] != A[A.size()-1]){
+// A.push(A[0]);
+// }
+
+// if(B[0] != B[B.size()-1]){
+// B.push(B[0]);
+// }
+
+ // returns true if point already exists in the given nfp
+ auto inNfp = [](const Vector& p, const std::vector<Cntr>& nfp){
+ if(nfp.empty()){
+ return false;
+ }
+
+ for(size_t i=0; i < nfp.size(); i++){
+ for(size_t j = 0; j< nfp[i].size(); j++){
+ if(_almostEqual(p.x, nfp[i][j].x) &&
+ _almostEqual(p.y, nfp[i][j].y)){
+ return true;
+ }
+ }
+ }
+
+ return false;
+ };
+
+ for(size_t i = 0; i < A.size() - 1; i++){
+ if(!A[i].marked) {
+ A[i].marked = true;
+ for(size_t j = 0; j < B.size(); j++){
+ B.offsetx = A[i].x - B[j].x;
+ B.offsety = A[i].y - B[j].y;
+
+ int Binside = 0;
+ for(size_t k = 0; k < B.size(); k++){
+ int inpoly = pointInPolygon({B[k].x + B.offsetx, B[k].y + B.offsety}, A);
+ if(inpoly != 0){
+ Binside = inpoly;
+ break;
+ }
+ }
+
+ if(Binside == 0){ // A and B are the same
+ return {false, {}};
+ }
+
+ auto startPoint = std::make_pair(true, Vector(B.offsetx, B.offsety));
+ if(((Binside && inside) || (!Binside && !inside)) &&
+ !intersect(A,B) && !inNfp(startPoint.second, NFP)){
+ return startPoint;
+ }
+
+ // slide B along vector
+ auto vx = A[i+1].x - A[i].x;
+ auto vy = A[i+1].y - A[i].y;
+
+ double d1 = polygonProjectionDistance(A,B,{vx, vy});
+ double d2 = polygonProjectionDistance(B,A,{-vx, -vy});
+
+ double d = dNAN;
+
+ // todo: clean this up
+ if(isnan(d1) && isnan(d2)){
+ // nothin
+ }
+ else if(isnan(d1)){
+ d = d2;
+ }
+ else if(isnan(d2)){
+ d = d1;
+ }
+ else{
+ d = min(d1,d2);
+ }
+
+ // only slide until no longer negative
+ // todo: clean this up
+ if(!isnan(d) && !_almostEqual(d,0) && d > 0){
+
+ }
+ else{
+ continue;
+ }
+
+ auto vd2 = vx*vx + vy*vy;
+
+ if(d*d < vd2 && !_almostEqual(d*d, vd2)){
+ auto vd = sqrt(vx*vx + vy*vy);
+ vx *= d/vd;
+ vy *= d/vd;
+ }
+
+ B.offsetx += vx;
+ B.offsety += vy;
+
+ for(size_t k = 0; k < B.size(); k++){
+ int inpoly = pointInPolygon({B[k].x + B.offsetx, B[k].y + B.offsety}, A);
+ if(inpoly != 0){
+ Binside = inpoly;
+ break;
+ }
+ }
+ startPoint = std::make_pair(true, Vector{B.offsetx, B.offsety});
+ if(((Binside && inside) || (!Binside && !inside)) &&
+ !intersect(A,B) && !inNfp(startPoint.second, NFP)){
+ return startPoint;
+ }
+ }
+ }
+ }
+
+ return {false, Vector(0, 0)};
+ }
+
+ static std::vector<Cntr> noFitPolygon(Cntr A,
+ Cntr B,
+ bool inside,
+ bool searchEdges)
+ {
+ if(A.size() < 3 || B.size() < 3) {
+ throw GeometryException(GeomErr::NFP);
+ return {};
+ }
+
+ A.offsetx = 0;
+ A.offsety = 0;
+
+ long i = 0, j = 0;
+
+ auto minA = y(A[0]);
+ long minAindex = 0;
+
+ auto maxB = y(B[0]);
+ long maxBindex = 0;
+
+ for(i = 1; i < A.size(); i++){
+ A[i].marked = false;
+ if(y(A[i]) < minA){
+ minA = y(A[i]);
+ minAindex = i;
+ }
+ }
+
+ for(i = 1; i < B.size(); i++){
+ B[i].marked = false;
+ if(y(B[i]) > maxB){
+ maxB = y(B[i]);
+ maxBindex = i;
+ }
+ }
+
+ std::pair<bool, Vector> startpoint;
+
+ if(!inside){
+ // shift B such that the bottom-most point of B is at the top-most
+ // point of A. This guarantees an initial placement with no
+ // intersections
+ startpoint = { true,
+ { x(A[minAindex]) - x(B[maxBindex]),
+ y(A[minAindex]) - y(B[maxBindex]) }
+ };
+ }
+ else {
+ // no reliable heuristic for inside
+ startpoint = searchStartPoint(A, B, true);
+ }
+
+ std::vector<Cntr> NFPlist;
+
+ struct Touch {
+ int type;
+ long A;
+ long B;
+ Touch(int t, long a, long b): type(t), A(a), B(b) {}
+ };
+
+ while(startpoint.first) {
+
+ B.offsetx = startpoint.second.x;
+ B.offsety = startpoint.second.y;
+
+ // maintain a list of touching points/edges
+ std::vector<Touch> touching;
+
+ struct V {
+ Coord x, y;
+ Vector *start, *end;
+ operator bool() {
+ return start != nullptr && end != nullptr;
+ }
+ operator Vector() const { return {x, y}; }
+ } prevvector = {0, 0, nullptr, nullptr};
+
+ Cntr NFP;
+ NFP.emplace_back(x(B[0]) + B.offsetx, y(B[0]) + B.offsety);
+
+ auto referencex = x(B[0]) + B.offsetx;
+ auto referencey = y(B[0]) + B.offsety;
+ auto startx = referencex;
+ auto starty = referencey;
+ unsigned counter = 0;
+
+ // sanity check, prevent infinite loop
+ while(counter < 10*(A.size() + B.size())){
+ touching.clear();
+
+ // find touching vertices/edges
+ for(i = 0; i < A.size(); i++){
+ long nexti = (i == A.size() - 1) ? 0 : i + 1;
+ for(j = 0; j < B.size(); j++){
+
+ long nextj = (j == B.size() - 1) ? 0 : j + 1;
+
+ if( _almostEqual(A[i].x, B[j].x+B.offsetx) &&
+ _almostEqual(A[i].y, B[j].y+B.offsety))
+ {
+ touching.emplace_back(0, i, j);
+ }
+ else if( _onSegment(
+ A[i], A[nexti],
+ { B[j].x+B.offsetx, B[j].y + B.offsety}) )
+ {
+ touching.emplace_back(1, nexti, j);
+ }
+ else if( _onSegment(
+ {B[j].x+B.offsetx, B[j].y + B.offsety},
+ {B[nextj].x+B.offsetx, B[nextj].y + B.offsety},
+ A[i]) )
+ {
+ touching.emplace_back(2, i, nextj);
+ }
+ }
+ }
+
+ // generate translation vectors from touching vertices/edges
+ std::vector<V> vectors;
+ for(i=0; i < touching.size(); i++){
+ auto& vertexA = A[touching[i].A];
+ vertexA.marked = true;
+
+ // adjacent A vertices
+ auto prevAindex = touching[i].A - 1;
+ auto nextAindex = touching[i].A + 1;
+
+ prevAindex = (prevAindex < 0) ? A.size() - 1 : prevAindex; // loop
+ nextAindex = (nextAindex >= A.size()) ? 0 : nextAindex; // loop
+
+ auto& prevA = A[prevAindex];
+ auto& nextA = A[nextAindex];
+
+ // adjacent B vertices
+ auto& vertexB = B[touching[i].B];
+
+ auto prevBindex = touching[i].B-1;
+ auto nextBindex = touching[i].B+1;
+
+ prevBindex = (prevBindex < 0) ? B.size() - 1 : prevBindex; // loop
+ nextBindex = (nextBindex >= B.size()) ? 0 : nextBindex; // loop
+
+ auto& prevB = B[prevBindex];
+ auto& nextB = B[nextBindex];
+
+ if(touching[i].type == 0){
+
+ V vA1 = {
+ prevA.x - vertexA.x,
+ prevA.y - vertexA.y,
+ &vertexA,
+ &prevA
+ };
+
+ V vA2 = {
+ nextA.x - vertexA.x,
+ nextA.y - vertexA.y,
+ &vertexA,
+ &nextA
+ };
+
+ // B vectors need to be inverted
+ V vB1 = {
+ vertexB.x - prevB.x,
+ vertexB.y - prevB.y,
+ &prevB,
+ &vertexB
+ };
+
+ V vB2 = {
+ vertexB.x - nextB.x,
+ vertexB.y - nextB.y,
+ &nextB,
+ &vertexB
+ };
+
+ vectors.emplace_back(vA1);
+ vectors.emplace_back(vA2);
+ vectors.emplace_back(vB1);
+ vectors.emplace_back(vB2);
+ }
+ else if(touching[i].type == 1){
+ vectors.emplace_back(V{
+ vertexA.x-(vertexB.x+B.offsetx),
+ vertexA.y-(vertexB.y+B.offsety),
+ &prevA,
+ &vertexA
+ });
+
+ vectors.emplace_back(V{
+ prevA.x-(vertexB.x+B.offsetx),
+ prevA.y-(vertexB.y+B.offsety),
+ &vertexA,
+ &prevA
+ });
+ }
+ else if(touching[i].type == 2){
+ vectors.emplace_back(V{
+ vertexA.x-(vertexB.x+B.offsetx),
+ vertexA.y-(vertexB.y+B.offsety),
+ &prevB,
+ &vertexB
+ });
+
+ vectors.emplace_back(V{
+ vertexA.x-(prevB.x+B.offsetx),
+ vertexA.y-(prevB.y+B.offsety),
+ &vertexB,
+ &prevB
+ });
+ }
+ }
+
+ // TODO: there should be a faster way to reject vectors that
+ // will cause immediate intersection. For now just check them all
+
+ V translate = {0, 0, nullptr, nullptr};
+ double maxd = 0;
+
+ for(i = 0; i < vectors.size(); i++) {
+ if(vectors[i].x == 0 && vectors[i].y == 0){
+ continue;
+ }
+
+ // if this vector points us back to where we came from, ignore it.
+ // ie cross product = 0, dot product < 0
+ if(prevvector && vectors[i].y * prevvector.y + vectors[i].x * prevvector.x < 0){
+
+ // compare magnitude with unit vectors
+ double vectorlength = sqrt(vectors[i].x*vectors[i].x+vectors[i].y*vectors[i].y);
+ Vector unitv = {Coord(vectors[i].x/vectorlength),
+ Coord(vectors[i].y/vectorlength)};
+
+ double prevlength = sqrt(prevvector.x*prevvector.x+prevvector.y*prevvector.y);
+ Vector prevunit = { prevvector.x/prevlength, prevvector.y/prevlength};
+
+ // we need to scale down to unit vectors to normalize vector length. Could also just do a tan here
+ if(abs(unitv.y * prevunit.x - unitv.x * prevunit.y) < 0.0001){
+ continue;
+ }
+ }
+
+ V vi = vectors[i];
+ double d = polygonSlideDistance(A, B, vi, true);
+ double vecd2 = vectors[i].x*vectors[i].x + vectors[i].y*vectors[i].y;
+
+ if(isnan(d) || d*d > vecd2){
+ double vecd = sqrt(vectors[i].x*vectors[i].x + vectors[i].y*vectors[i].y);
+ d = vecd;
+ }
+
+ if(!isnan(d) && d > maxd){
+ maxd = d;
+ translate = vectors[i];
+ }
+ }
+
+ if(!translate || _almostEqual(maxd, 0))
+ {
+ // didn't close the loop, something went wrong here
+ NFP.clear();
+ break;
+ }
+
+ translate.start->marked = true;
+ translate.end->marked = true;
+
+ prevvector = translate;
+
+ // trim
+ double vlength2 = translate.x*translate.x + translate.y*translate.y;
+ if(maxd*maxd < vlength2 && !_almostEqual(maxd*maxd, vlength2)){
+ double scale = sqrt((maxd*maxd)/vlength2);
+ translate.x *= scale;
+ translate.y *= scale;
+ }
+
+ referencex += translate.x;
+ referencey += translate.y;
+
+ if(_almostEqual(referencex, startx) &&
+ _almostEqual(referencey, starty)) {
+ // we've made a full loop
+ break;
+ }
+
+ // if A and B start on a touching horizontal line,
+ // the end point may not be the start point
+ bool looped = false;
+ if(NFP.size() > 0) {
+ for(i = 0; i < NFP.size() - 1; i++) {
+ if(_almostEqual(referencex, NFP[i].x) &&
+ _almostEqual(referencey, NFP[i].y)){
+ looped = true;
+ }
+ }
+ }
+
+ if(looped){
+ // we've made a full loop
+ break;
+ }
+
+ NFP.emplace_back(referencex, referencey);
+
+ B.offsetx += translate.x;
+ B.offsety += translate.y;
+
+ counter++;
+ }
+
+ if(NFP.size() > 0){
+ NFPlist.emplace_back(NFP);
+ }
+
+ if(!searchEdges){
+ // only get outer NFP or first inner NFP
+ break;
+ }
+
+ startpoint =
+ searchStartPoint(A, B, inside, NFPlist);
+
+ }
+
+ return NFPlist;
+ }
+};
+
+template<class S> const double _alg<S>::TOL = std::pow(10, -9);
+
+}
+}
+
+#endif // NFP_SVGNEST_HPP
diff --git a/xs/src/libnest2d/tools/nfp_svgnest_glue.hpp b/xs/src/libnest2d/tools/nfp_svgnest_glue.hpp
new file mode 100644
index 000000000..ea1fb4d07
--- /dev/null
+++ b/xs/src/libnest2d/tools/nfp_svgnest_glue.hpp
@@ -0,0 +1,75 @@
+#ifndef NFP_SVGNEST_GLUE_HPP
+#define NFP_SVGNEST_GLUE_HPP
+
+#include "nfp_svgnest.hpp"
+
+#include <libnest2d/clipper_backend/clipper_backend.hpp>
+
+namespace libnest2d {
+
+namespace __svgnest {
+
+//template<> struct _Tol<double> {
+// static const BP2D_CONSTEXPR TCoord<PointImpl> Value = 1000000;
+//};
+
+}
+
+namespace nfp {
+
+using NfpR = NfpResult<PolygonImpl>;
+
+template<> struct NfpImpl<PolygonImpl, NfpLevel::CONVEX_ONLY> {
+ NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother) {
+// return nfpConvexOnly(sh, cother);
+ namespace sl = shapelike;
+ using alg = __svgnest::_alg<PolygonImpl>;
+
+ auto nfp_p = alg::noFitPolygon(sl::getContour(sh),
+ sl::getContour(cother), false, false);
+
+ PolygonImpl nfp_cntr;
+ if(!nfp_p.empty()) nfp_cntr.Contour = nfp_p.front();
+ return {nfp_cntr, referenceVertex(nfp_cntr)};
+ }
+};
+
+template<> struct NfpImpl<PolygonImpl, NfpLevel::ONE_CONVEX> {
+ NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother) {
+// return nfpConvexOnly(sh, cother);
+ namespace sl = shapelike;
+ using alg = __svgnest::_alg<PolygonImpl>;
+
+ std::cout << "Itt vagyok" << std::endl;
+ auto nfp_p = alg::noFitPolygon(sl::getContour(sh),
+ sl::getContour(cother), false, false);
+
+ PolygonImpl nfp_cntr;
+ nfp_cntr.Contour = nfp_p.front();
+ return {nfp_cntr, referenceVertex(nfp_cntr)};
+ }
+};
+
+template<>
+struct NfpImpl<PolygonImpl, NfpLevel::BOTH_CONCAVE> {
+ NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother) {
+ namespace sl = shapelike;
+ using alg = __svgnest::_alg<PolygonImpl>;
+
+ auto nfp_p = alg::noFitPolygon(sl::getContour(sh),
+ sl::getContour(cother), true, false);
+
+ PolygonImpl nfp_cntr;
+ nfp_cntr.Contour = nfp_p.front();
+ return {nfp_cntr, referenceVertex(nfp_cntr)};
+ }
+};
+
+template<> struct MaxNfpLevel<PolygonImpl> {
+// static const BP2D_CONSTEXPR NfpLevel value = NfpLevel::BOTH_CONCAVE;
+ static const BP2D_CONSTEXPR NfpLevel value = NfpLevel::CONVEX_ONLY;
+};
+
+}}
+
+#endif // NFP_SVGNEST_GLUE_HPP
diff --git a/xs/src/libnest2d/tools/svgtools.hpp b/xs/src/libnest2d/tools/svgtools.hpp
index 3a83caa70..776dd5a1a 100644
--- a/xs/src/libnest2d/tools/svgtools.hpp
+++ b/xs/src/libnest2d/tools/svgtools.hpp
@@ -56,14 +56,14 @@ public:
auto d = static_cast<Coord>(
std::round(conf_.height*conf_.mm_in_coord_units) );
- auto& contour = ShapeLike::getContour(tsh);
+ auto& contour = shapelike::getContour(tsh);
for(auto& v : contour) setY(v, -getY(v) + d);
- auto& holes = ShapeLike::holes(tsh);
+ auto& holes = shapelike::holes(tsh);
for(auto& h : holes) for(auto& v : h) setY(v, -getY(v) + d);
}
- currentLayer() += ShapeLike::serialize<Formats::SVG>(tsh,
+ currentLayer() += shapelike::serialize<Formats::SVG>(tsh,
1.0/conf_.mm_in_coord_units) + "\n";
}
diff --git a/xs/src/libslic3r/Format/3mf.cpp b/xs/src/libslic3r/Format/3mf.cpp
index dd3500eba..5de1d26c5 100644
--- a/xs/src/libslic3r/Format/3mf.cpp
+++ b/xs/src/libslic3r/Format/3mf.cpp
@@ -1491,6 +1491,7 @@ namespace Slic3r {
stl_get_size(&stl);
volume->mesh.repair();
+ volume->calculate_convex_hull();
// apply volume's name and config data
for (const Metadata& metadata : volume_data.metadata)
diff --git a/xs/src/libslic3r/Format/AMF.cpp b/xs/src/libslic3r/Format/AMF.cpp
index 600aa6cd9..886bbae97 100644
--- a/xs/src/libslic3r/Format/AMF.cpp
+++ b/xs/src/libslic3r/Format/AMF.cpp
@@ -406,6 +406,7 @@ void AMFParserContext::endElement(const char * /* name */)
}
stl_get_size(&stl);
m_volume->mesh.repair();
+ m_volume->calculate_convex_hull();
m_volume_facets.clear();
m_volume = nullptr;
break;
diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp
index b34ba5441..2254c2154 100644
--- a/xs/src/libslic3r/GCode.cpp
+++ b/xs/src/libslic3r/GCode.cpp
@@ -276,7 +276,6 @@ std::string WipeTowerIntegration::rotate_wipe_tower_moves(const std::string& gco
}
-
std::string WipeTowerIntegration::prime(GCode &gcodegen)
{
assert(m_layer_idx == 0);
@@ -665,6 +664,14 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
_write_format(file, "\n");
}
+ // adds tags for time estimators
+ if (print.config.remaining_times.value)
+ {
+ _writeln(file, GCodeTimeEstimator::Normal_First_M73_Output_Placeholder_Tag);
+ if (m_silent_time_estimator_enabled)
+ _writeln(file, GCodeTimeEstimator::Silent_First_M73_Output_Placeholder_Tag);
+ }
+
// Prepare the helper object for replacing placeholders in custom G-code and output filename.
m_placeholder_parser = print.placeholder_parser;
m_placeholder_parser.update_timestamp();
@@ -724,7 +731,6 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
m_placeholder_parser.set("has_wipe_tower", has_wipe_tower);
m_placeholder_parser.set("has_single_extruder_multi_material_priming", has_wipe_tower && print.config.single_extruder_multi_material_priming);
std::string start_gcode = this->placeholder_parser_process("start_gcode", print.config.start_gcode.value, initial_extruder_id);
-
// Set bed temperature if the start G-code does not contain any bed temp control G-codes.
this->_print_first_layer_bed_temperature(file, print, start_gcode, initial_extruder_id, true);
// Set extruder(s) temperature before and after start G-code.
@@ -960,17 +966,20 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
// Get filament stats.
print.filament_stats.clear();
- print.total_used_filament = 0.;
- print.total_extruded_volume = 0.;
- print.total_weight = 0.;
- print.total_cost = 0.;
+ print.total_used_filament = 0.;
+ print.total_extruded_volume = 0.;
+ print.total_weight = 0.;
+ print.total_cost = 0.;
+ print.total_wipe_tower_cost = 0.;
+ print.total_wipe_tower_filament = 0.;
print.estimated_normal_print_time = m_normal_time_estimator.get_time_dhms();
print.estimated_silent_print_time = m_silent_time_estimator_enabled ? m_silent_time_estimator.get_time_dhms() : "N/A";
for (const Extruder &extruder : m_writer.extruders()) {
- double used_filament = extruder.used_filament();
- double extruded_volume = extruder.extruded_volume();
+ double used_filament = extruder.used_filament() + (has_wipe_tower ? print.m_wipe_tower_used_filament[extruder.id()] : 0.f);
+ double extruded_volume = extruder.extruded_volume() + (has_wipe_tower ? print.m_wipe_tower_used_filament[extruder.id()] * 2.4052f : 0.f); // assumes 1.75mm filament diameter
double filament_weight = extruded_volume * extruder.filament_density() * 0.001;
double filament_cost = filament_weight * extruder.filament_cost() * 0.001;
+
print.filament_stats.insert(std::pair<size_t, float>(extruder.id(), (float)used_filament));
_write_format(file, "; filament used = %.1lfmm (%.1lfcm3)\n", used_filament, extruded_volume * 0.001);
if (filament_weight > 0.) {
@@ -981,8 +990,10 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
_write_format(file, "; filament cost = %.1lf\n", filament_cost);
}
}
- print.total_used_filament = print.total_used_filament + used_filament;
- print.total_extruded_volume = print.total_extruded_volume + extruded_volume;
+ print.total_used_filament += used_filament;
+ print.total_extruded_volume += extruded_volume;
+ print.total_wipe_tower_filament += has_wipe_tower ? used_filament - extruder.used_filament() : 0.;
+ print.total_wipe_tower_cost += has_wipe_tower ? (extruded_volume - extruder.extruded_volume())* extruder.filament_density() * 0.001 * extruder.filament_cost() * 0.001 : 0.;
}
_write_format(file, "; total filament cost = %.1lf\n", print.total_cost);
_write_format(file, "; estimated printing time (normal mode) = %s\n", m_normal_time_estimator.get_time_dhms().c_str());
diff --git a/xs/src/libslic3r/GCode.hpp b/xs/src/libslic3r/GCode.hpp
index 4953c39fe..d319bee01 100644
--- a/xs/src/libslic3r/GCode.hpp
+++ b/xs/src/libslic3r/GCode.hpp
@@ -98,6 +98,7 @@ public:
void next_layer() { ++ m_layer_idx; m_tool_change_idx = 0; }
std::string tool_change(GCode &gcodegen, int extruder_id, bool finish_layer);
std::string finalize(GCode &gcodegen);
+ std::vector<float> used_filament_length() const;
private:
WipeTowerIntegration& operator=(const WipeTowerIntegration&);
diff --git a/xs/src/libslic3r/GCode/WipeTower.hpp b/xs/src/libslic3r/GCode/WipeTower.hpp
index 9bf350328..21c10969a 100644
--- a/xs/src/libslic3r/GCode/WipeTower.hpp
+++ b/xs/src/libslic3r/GCode/WipeTower.hpp
@@ -155,6 +155,12 @@ public:
// the wipe tower has been completely covered by the tool change extrusions,
// or the rest of the tower has been filled by a sparse infill with the finish_layer() method.
virtual bool layer_finished() const = 0;
+
+ // Returns used filament length per extruder:
+ virtual std::vector<float> get_used_filament() const = 0;
+
+ // Returns total number of toolchanges:
+ virtual int get_number_of_toolchanges() const = 0;
};
}; // namespace Slic3r
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index 3d0dba07a..61d8df035 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -111,9 +111,10 @@ public:
const WipeTower::xy start_pos_rotated() const { return m_start_pos; }
const WipeTower::xy pos_rotated() const { return WipeTower::xy(m_current_pos, 0.f, m_y_shift).rotate(m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle); }
float elapsed_time() const { return m_elapsed_time; }
+ float get_and_reset_used_filament_length() { float temp = m_used_filament_length; m_used_filament_length = 0.f; return temp; }
// Extrude with an explicitely provided amount of extrusion.
- Writer& extrude_explicit(float x, float y, float e, float f = 0.f)
+ Writer& extrude_explicit(float x, float y, float e, float f = 0.f, bool record_length = false)
{
if (x == m_current_pos.x && y == m_current_pos.y && e == 0.f && (f == 0.f || f == m_current_feedrate))
// Neither extrusion nor a travel move.
@@ -122,6 +123,8 @@ public:
float dx = x - m_current_pos.x;
float dy = y - m_current_pos.y;
double len = sqrt(dx*dx+dy*dy);
+ if (record_length)
+ m_used_filament_length += e;
// Now do the "internal rotation" with respect to the wipe tower center
@@ -162,8 +165,8 @@ public:
return *this;
}
- Writer& extrude_explicit(const WipeTower::xy &dest, float e, float f = 0.f)
- { return extrude_explicit(dest.x, dest.y, e, f); }
+ Writer& extrude_explicit(const WipeTower::xy &dest, float e, float f = 0.f, bool record_length = false)
+ { return extrude_explicit(dest.x, dest.y, e, f, record_length); }
// Travel to a new XY position. f=0 means use the current value.
Writer& travel(float x, float y, float f = 0.f)
@@ -177,7 +180,7 @@ public:
{
float dx = x - m_current_pos.x;
float dy = y - m_current_pos.y;
- return extrude_explicit(x, y, sqrt(dx*dx+dy*dy) * m_extrusion_flow, f);
+ return extrude_explicit(x, y, sqrt(dx*dx+dy*dy) * m_extrusion_flow, f, true);
}
Writer& extrude(const WipeTower::xy &dest, const float f = 0.f)
@@ -259,8 +262,8 @@ public:
// extrude quickly amount e to x2 with feed f.
Writer& ram(float x1, float x2, float dy, float e0, float e, float f)
{
- extrude_explicit(x1, m_current_pos.y + dy, e0, f);
- extrude_explicit(x2, m_current_pos.y, e);
+ extrude_explicit(x1, m_current_pos.y + dy, e0, f, true);
+ extrude_explicit(x2, m_current_pos.y, e, 0.f, true);
return *this;
}
@@ -404,6 +407,7 @@ private:
float m_last_fan_speed = 0.f;
int current_temp = -1;
const float m_default_analyzer_line_width;
+ float m_used_filament_length = 0.f;
std::string set_format_X(float x)
{
@@ -525,6 +529,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
++ m_num_tool_changes;
}
+ m_old_temperature = -1; // If the priming is turned off in config, the temperature changing commands will not actually appear
+ // in the output gcode - we should not remember emitting them (we will output them twice in the worst case)
+
// Reset the extruder current to a normal value.
writer.set_extruder_trimpot(550)
.feedrate(6000)
@@ -537,6 +544,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
// so that tool_change() will know to extrude the wipe tower brim:
m_print_brim = true;
+ // Ask our writer about how much material was consumed:
+ m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
+
ToolChangeResult result;
result.priming = true;
result.print_z = this->m_z_pos;
@@ -606,10 +616,10 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
toolchange_Load(writer, cleaning_box);
writer.travel(writer.x(),writer.y()-m_perimeter_width); // cooling and loading were done a bit down the road
toolchange_Wipe(writer, cleaning_box, wipe_volume); // Wipe the newly loaded filament until the end of the assigned wipe area.
+ ++ m_num_tool_changes;
} else
toolchange_Unload(writer, cleaning_box, m_filpar[m_current_tool].material, m_filpar[m_current_tool].temperature);
- ++ m_num_tool_changes;
m_depth_traversed += wipe_area;
if (last_change_in_layer) {// draw perimeter line
@@ -632,6 +642,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
";------------------\n"
"\n\n");
+ // Ask our writer about how much material was consumed:
+ m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
+
ToolChangeResult result;
result.priming = false;
result.print_z = this->m_z_pos;
@@ -683,6 +696,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(bool sideOnly, flo
m_print_brim = false; // Mark the brim as extruded
+ // Ask our writer about how much material was consumed:
+ m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
+
ToolChangeResult result;
result.priming = false;
result.print_z = this->m_z_pos;
@@ -793,14 +809,20 @@ void WipeTowerPrusaMM::toolchange_Unload(
float turning_point = (!m_left_to_right ? xl : xr );
float total_retraction_distance = m_cooling_tube_retraction + m_cooling_tube_length/2.f - 15.f; // the 15mm is reserved for the first part after ramming
writer.suppress_preview()
- .load_move_x_advanced(turning_point, -15.f, 83.f, 50.f) // this is done at fixed speed
+ .retract(15.f, m_filpar[m_current_tool].unloading_speed_start * 60.f) // feedrate 5000mm/min = 83mm/s
+ .retract(0.70f * total_retraction_distance, 1.0f * m_filpar[m_current_tool].unloading_speed * 60.f)
+ .retract(0.20f * total_retraction_distance, 0.5f * m_filpar[m_current_tool].unloading_speed * 60.f)
+ .retract(0.10f * total_retraction_distance, 0.3f * m_filpar[m_current_tool].unloading_speed * 60.f)
+
+ /*.load_move_x_advanced(turning_point, -15.f, 83.f, 50.f) // this is done at fixed speed
.load_move_x_advanced(old_x, -0.70f * total_retraction_distance, 1.0f * m_filpar[m_current_tool].unloading_speed)
.load_move_x_advanced(turning_point, -0.20f * total_retraction_distance, 0.5f * m_filpar[m_current_tool].unloading_speed)
.load_move_x_advanced(old_x, -0.10f * total_retraction_distance, 0.3f * m_filpar[m_current_tool].unloading_speed)
- .travel(old_x, writer.y()) // in case previous move was shortened to limit feedrate
+ .travel(old_x, writer.y()) // in case previous move was shortened to limit feedrate*/
.resume_preview();
-
- if (new_temperature != 0 && new_temperature != m_old_temperature ) { // Set the extruder temperature, but don't wait.
+ if (new_temperature != 0 && (new_temperature != m_old_temperature || m_is_first_layer) ) { // Set the extruder temperature, but don't wait.
+ // If the required temperature is the same as last time, don't emit the M104 again (if user adjusted the value, it would be reset)
+ // However, always change temperatures on the first layer (this is to avoid issues with priming lines turned off).
writer.set_extruder_temp(new_temperature, false);
m_old_temperature = new_temperature;
}
@@ -844,6 +866,9 @@ void WipeTowerPrusaMM::toolchange_Change(
const unsigned int new_tool,
material_type new_material)
{
+ // Ask the writer about how much of the old filament we consumed:
+ m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
+
// Speed override for the material. Go slow for flex and soluble materials.
int speed_override;
switch (new_material) {
@@ -874,10 +899,15 @@ void WipeTowerPrusaMM::toolchange_Load(
writer.append("; CP TOOLCHANGE LOAD\n")
.suppress_preview()
- .load_move_x_advanced(turning_point, 0.2f * edist, 0.3f * m_filpar[m_current_tool].loading_speed) // Acceleration
+ /*.load_move_x_advanced(turning_point, 0.2f * edist, 0.3f * m_filpar[m_current_tool].loading_speed) // Acceleration
.load_move_x_advanced(oldx, 0.5f * edist, m_filpar[m_current_tool].loading_speed) // Fast phase
.load_move_x_advanced(turning_point, 0.2f * edist, 0.3f * m_filpar[m_current_tool].loading_speed) // Slowing down
- .load_move_x_advanced(oldx, 0.1f * edist, 0.1f * m_filpar[m_current_tool].loading_speed) // Super slow
+ .load_move_x_advanced(oldx, 0.1f * edist, 0.1f * m_filpar[m_current_tool].loading_speed) // Super slow*/
+
+ .load(0.2f * edist, 60.f * m_filpar[m_current_tool].loading_speed_start)
+ .load_move_x_advanced(turning_point, 0.7f * edist, m_filpar[m_current_tool].loading_speed) // Fast phase
+ .load_move_x_advanced(oldx, 0.1f * edist, 0.1f * m_filpar[m_current_tool].loading_speed) // Super slow*/
+
.travel(oldx, writer.y()) // in case last move was shortened to limit x feedrate
.resume_preview();
@@ -901,7 +931,6 @@ void WipeTowerPrusaMM::toolchange_Wipe(
const float& xl = cleaning_box.ld.x;
const float& xr = cleaning_box.rd.x;
-
// Variables x_to_wipe and traversed_x are here to be able to make sure it always wipes at least
// the ordered volume, even if it means violating the box. This can later be removed and simply
// wipe until the end of the assigned area.
@@ -916,7 +945,6 @@ void WipeTowerPrusaMM::toolchange_Wipe(
m_left_to_right = !m_left_to_right;
}
-
// now the wiping itself:
for (int i = 0; true; ++i) {
if (i!=0) {
@@ -925,7 +953,7 @@ void WipeTowerPrusaMM::toolchange_Wipe(
else if (wipe_speed < 2210.f) wipe_speed = 4200.f;
else wipe_speed = std::min(4800.f, wipe_speed + 50.f);
}
-
+
float traversed_x = writer.x();
if (m_left_to_right)
writer.extrude(xr - (i % 4 == 0 ? 0 : 1.5*m_perimeter_width), writer.y(), wipe_speed * wipe_coeff);
@@ -1040,6 +1068,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer()
m_depth_traversed = m_wipe_tower_depth-m_perimeter_width;
+ // Ask our writer about how much material was consumed:
+ m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
+
ToolChangeResult result;
result.priming = false;
result.print_z = this->m_z_pos;
@@ -1157,6 +1188,8 @@ void WipeTowerPrusaMM::generate(std::vector<std::vector<WipeTower::ToolChangeRes
m_layer_info = m_plan.begin();
m_current_tool = (unsigned int)(-2); // we don't know which extruder to start with - we'll set it according to the first toolchange
+ for (auto& used : m_used_filament_length) // reset used filament stats
+ used = 0.f;
std::vector<WipeTower::ToolChangeResult> layer_result;
for (auto layer : m_plan)
@@ -1198,9 +1231,6 @@ void WipeTowerPrusaMM::generate(std::vector<std::vector<WipeTower::ToolChangeRes
}
}
-
-
-
void WipeTowerPrusaMM::make_wipe_tower_square()
{
const float width = m_wipe_tower_width - 3 * m_perimeter_width;
@@ -1224,7 +1254,6 @@ void WipeTowerPrusaMM::make_wipe_tower_square()
plan_tower(); // propagates depth downwards again (width has changed)
for (auto& lay : m_plan) // depths set, now the spacing
lay.extra_spacing = lay.depth / lay.toolchanges_depth();
-
}
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
index e1529bcf4..06625d189 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
@@ -46,7 +46,7 @@ public:
WipeTowerPrusaMM(float x, float y, float width, float rotation_angle, float cooling_tube_retraction,
float cooling_tube_length, float parking_pos_retraction, float extra_loading_move, float bridging,
const std::vector<std::vector<float>>& wiping_matrix, unsigned int initial_tool) :
- m_wipe_tower_pos(x, y),
+ m_wipe_tower_pos(x, y),
m_wipe_tower_width(width),
m_wipe_tower_rotation_angle(rotation_angle),
m_y_shift(0.f),
@@ -65,9 +65,9 @@ public:
// Set the extruder properties.
- void set_extruder(size_t idx, material_type material, int temp, int first_layer_temp, float loading_speed,
- float unloading_speed, float delay, int cooling_moves, float cooling_initial_speed,
- float cooling_final_speed, std::string ramming_parameters, float nozzle_diameter)
+ void set_extruder(size_t idx, material_type material, int temp, int first_layer_temp, float loading_speed, float loading_speed_start,
+ float unloading_speed, float unloading_speed_start, float delay, int cooling_moves,
+ float cooling_initial_speed, float cooling_final_speed, std::string ramming_parameters, float nozzle_diameter)
{
//while (m_filpar.size() < idx+1) // makes sure the required element is in the vector
m_filpar.push_back(FilamentParameters());
@@ -76,7 +76,9 @@ public:
m_filpar[idx].temperature = temp;
m_filpar[idx].first_layer_temperature = first_layer_temp;
m_filpar[idx].loading_speed = loading_speed;
+ m_filpar[idx].loading_speed_start = loading_speed_start;
m_filpar[idx].unloading_speed = unloading_speed;
+ m_filpar[idx].unloading_speed_start = unloading_speed_start;
m_filpar[idx].delay = delay;
m_filpar[idx].cooling_moves = cooling_moves;
m_filpar[idx].cooling_initial_speed = cooling_initial_speed;
@@ -92,6 +94,8 @@ public:
m_filpar[idx].ramming_step_multiplicator /= 100;
while (stream >> speed)
m_filpar[idx].ramming_speed.push_back(speed);
+
+ m_used_filament_length.resize(std::max(m_used_filament_length.size(), idx + 1)); // makes sure that the vector is big enough so we don't have to check later
}
@@ -170,6 +174,9 @@ public:
return ( (m_is_first_layer ? m_wipe_tower_depth - m_perimeter_width : m_layer_info->depth) - WT_EPSILON < m_depth_traversed);
}
+ virtual std::vector<float> get_used_filament() const override { return m_used_filament_length; }
+ virtual int get_number_of_toolchanges() const override { return m_num_tool_changes; }
+
private:
WipeTowerPrusaMM();
@@ -216,7 +223,9 @@ private:
int temperature = 0;
int first_layer_temperature = 0;
float loading_speed = 0.f;
+ float loading_speed_start = 0.f;
float unloading_speed = 0.f;
+ float unloading_speed_start = 0.f;
float delay = 0.f ;
int cooling_moves = 0;
float cooling_initial_speed = 0.f;
@@ -327,6 +336,9 @@ private:
std::vector<WipeTowerInfo> m_plan; // Stores information about all layers and toolchanges for the future wipe tower (filled by plan_toolchange(...))
std::vector<WipeTowerInfo>::iterator m_layer_info = m_plan.end();
+ // Stores information about used filament length per extruder:
+ std::vector<float> m_used_filament_length;
+
// Returns gcode for wipe tower brim
// sideOnly -- set to false -- experimental, draw brim on sides of wipe tower
diff --git a/xs/src/libslic3r/GCodeTimeEstimator.cpp b/xs/src/libslic3r/GCodeTimeEstimator.cpp
index c4ffb572a..7471367fe 100644
--- a/xs/src/libslic3r/GCodeTimeEstimator.cpp
+++ b/xs/src/libslic3r/GCodeTimeEstimator.cpp
@@ -168,6 +168,9 @@ namespace Slic3r {
}
#endif // ENABLE_MOVE_STATS
+ const std::string GCodeTimeEstimator::Normal_First_M73_Output_Placeholder_Tag = "; NORMAL_FIRST_M73_OUTPUT_PLACEHOLDER";
+ const std::string GCodeTimeEstimator::Silent_First_M73_Output_Placeholder_Tag = "; SILENT_FIRST_M73_OUTPUT_PLACEHOLDER";
+
GCodeTimeEstimator::GCodeTimeEstimator(EMode mode)
: _mode(mode)
{
@@ -294,7 +297,15 @@ namespace Slic3r {
throw std::runtime_error(std::string("Remaining times export failed.\nError while reading from file.\n"));
}
- gcode_line += "\n";
+ // replaces placeholders for initial line M73 with the real lines
+ if (((_mode == Normal) && (gcode_line == Normal_First_M73_Output_Placeholder_Tag)) ||
+ ((_mode == Silent) && (gcode_line == Silent_First_M73_Output_Placeholder_Tag)))
+ {
+ sprintf(time_line, time_mask.c_str(), "0", _get_time_minutes(_time).c_str());
+ gcode_line = time_line;
+ }
+ else
+ gcode_line += "\n";
// add remaining time lines where needed
_parser.parse_line(gcode_line,
diff --git a/xs/src/libslic3r/GCodeTimeEstimator.hpp b/xs/src/libslic3r/GCodeTimeEstimator.hpp
index 1fa74e304..e9da584c3 100644
--- a/xs/src/libslic3r/GCodeTimeEstimator.hpp
+++ b/xs/src/libslic3r/GCodeTimeEstimator.hpp
@@ -17,6 +17,9 @@ namespace Slic3r {
class GCodeTimeEstimator
{
public:
+ static const std::string Normal_First_M73_Output_Placeholder_Tag;
+ static const std::string Silent_First_M73_Output_Placeholder_Tag;
+
enum EMode : unsigned char
{
Normal,
diff --git a/xs/src/libslic3r/Geometry.cpp b/xs/src/libslic3r/Geometry.cpp
index 39b626ee3..aaf0352c9 100644
--- a/xs/src/libslic3r/Geometry.cpp
+++ b/xs/src/libslic3r/Geometry.cpp
@@ -195,47 +195,62 @@ using namespace boost::polygon; // provides also high() and low()
namespace Slic3r { namespace Geometry {
-static bool
-sort_points (Point a, Point b)
-{
- return (a.x < b.x) || (a.x == b.x && a.y < b.y);
-}
+struct SortPoints {
+ template <class T>
+ bool operator()(const T& a, const T& b) const {
+ return (b.x > a.x) || (a.x == b.x && b.y > a.y);
+ }
+};
-/* This implementation is based on Andrew's monotone chain 2D convex hull algorithm */
-Polygon
-convex_hull(Points points)
+// This implementation is based on Andrew's monotone chain 2D convex hull algorithm
+template<class T>
+static T raw_convex_hull(T& points)
{
assert(points.size() >= 3);
// sort input points
- std::sort(points.begin(), points.end(), sort_points);
+ std::sort(points.begin(), points.end(), SortPoints());
int n = points.size(), k = 0;
- Polygon hull;
+ T hull;
if (n >= 3) {
- hull.points.resize(2*n);
+ hull.resize(2*n);
// Build lower hull
for (int i = 0; i < n; i++) {
- while (k >= 2 && points[i].ccw(hull.points[k-2], hull.points[k-1]) <= 0) k--;
- hull.points[k++] = points[i];
+ while (k >= 2 && points[i].ccw(hull[k-2], hull[k-1]) <= 0) k--;
+ hull[k++] = points[i];
}
// Build upper hull
for (int i = n-2, t = k+1; i >= 0; i--) {
- while (k >= t && points[i].ccw(hull.points[k-2], hull.points[k-1]) <= 0) k--;
- hull.points[k++] = points[i];
+ while (k >= t && points[i].ccw(hull[k-2], hull[k-1]) <= 0) k--;
+ hull[k++] = points[i];
}
- hull.points.resize(k);
+ hull.resize(k);
- assert( hull.points.front().coincides_with(hull.points.back()) );
- hull.points.pop_back();
+ assert( hull.front().coincides_with(hull.back()) );
+ hull.pop_back();
}
return hull;
}
+Pointf3s
+convex_hull(Pointf3s points)
+{
+ return raw_convex_hull(points);
+}
+
+Polygon
+convex_hull(Points points)
+{
+ Polygon hull;
+ hull.points = raw_convex_hull(points);
+ return hull;
+}
+
Polygon
convex_hull(const Polygons &polygons)
{
@@ -243,7 +258,7 @@ convex_hull(const Polygons &polygons)
for (Polygons::const_iterator p = polygons.begin(); p != polygons.end(); ++p) {
pp.insert(pp.end(), p->points.begin(), p->points.end());
}
- return convex_hull(pp);
+ return convex_hull(std::move(pp));
}
/* accepts an arrayref of points and returns a list of indices
diff --git a/xs/src/libslic3r/Geometry.hpp b/xs/src/libslic3r/Geometry.hpp
index c2c3dc8b7..956ef82aa 100644
--- a/xs/src/libslic3r/Geometry.hpp
+++ b/xs/src/libslic3r/Geometry.hpp
@@ -108,8 +108,10 @@ inline bool segment_segment_intersection(const Pointf &p1, const Vectorf &v1, co
return true;
}
+Pointf3s convex_hull(Pointf3s points);
Polygon convex_hull(Points points);
Polygon convex_hull(const Polygons &polygons);
+
void chained_path(const Points &points, std::vector<Points::size_type> &retval, Point start_near);
void chained_path(const Points &points, std::vector<Points::size_type> &retval);
template<class T> void chained_path_items(Points &points, T &items, T &retval);
diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp
index f08736f4e..e5f4888a6 100644
--- a/xs/src/libslic3r/Model.cpp
+++ b/xs/src/libslic3r/Model.cpp
@@ -17,6 +17,11 @@
#include "SVG.hpp"
#include <Eigen/Dense>
+static const float UNIT_MATRIX[] = { 1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f,
+ 0.0f, 0.0f, 0.0f, 1.0f };
+
namespace Slic3r {
unsigned int Model::s_auto_extruder_id = 1;
@@ -235,14 +240,6 @@ BoundingBoxf3 Model::bounding_box() const
return bb;
}
-BoundingBoxf3 Model::transformed_bounding_box() const
-{
- BoundingBoxf3 bb;
- for (const ModelObject* obj : this->objects)
- bb.merge(obj->tight_bounding_box(false));
- return bb;
-}
-
void Model::center_instances_around_point(const Pointf &point)
{
// BoundingBoxf3 bb = this->bounding_box();
@@ -624,54 +621,6 @@ const BoundingBoxf3& ModelObject::bounding_box() const
return m_bounding_box;
}
-BoundingBoxf3 ModelObject::tight_bounding_box(bool include_modifiers) const
-{
- BoundingBoxf3 bb;
-
- for (const ModelVolume* vol : this->volumes)
- {
- if (include_modifiers || !vol->modifier)
- {
- for (const ModelInstance* inst : this->instances)
- {
- double c = cos(inst->rotation);
- double s = sin(inst->rotation);
-
- for (int f = 0; f < vol->mesh.stl.stats.number_of_facets; ++f)
- {
- const stl_facet& facet = vol->mesh.stl.facet_start[f];
-
- for (int i = 0; i < 3; ++i)
- {
- // original point
- const stl_vertex& v = facet.vertex[i];
- Pointf3 p((double)v.x, (double)v.y, (double)v.z);
-
- // scale
- p.x *= inst->scaling_factor;
- p.y *= inst->scaling_factor;
- p.z *= inst->scaling_factor;
-
- // rotate Z
- double x = p.x;
- double y = p.y;
- p.x = c * x - s * y;
- p.y = s * x + c * y;
-
- // translate
- p.x += inst->offset.x;
- p.y += inst->offset.y;
-
- bb.merge(p);
- }
- }
- }
- }
- }
-
- return bb;
-}
-
// A mesh containing all transformed instances of this object.
TriangleMesh ModelObject::mesh() const
{
@@ -756,37 +705,36 @@ void ModelObject::center_around_origin()
void ModelObject::translate(coordf_t x, coordf_t y, coordf_t z)
{
for (ModelVolume *v : this->volumes)
+ {
v->mesh.translate(float(x), float(y), float(z));
- if (m_bounding_box_valid)
+ v->m_convex_hull.translate(float(x), float(y), float(z));
+ }
+
+ if (m_bounding_box_valid)
m_bounding_box.translate(x, y, z);
}
void ModelObject::scale(const Pointf3 &versor)
{
for (ModelVolume *v : this->volumes)
+ {
v->mesh.scale(versor);
+ v->m_convex_hull.scale(versor);
+ }
// reset origin translation since it doesn't make sense anymore
this->origin_translation = Pointf3(0,0,0);
this->invalidate_bounding_box();
}
-void ModelObject::rotate(float angle, const Axis &axis)
+void ModelObject::rotate(float angle, const Pointf3& axis)
{
- float min_z = FLT_MAX;
for (ModelVolume *v : this->volumes)
{
v->mesh.rotate(angle, axis);
- min_z = std::min(min_z, v->mesh.stl.stats.min.z);
+ v->m_convex_hull.rotate(angle, axis);
}
- if (min_z != 0.0f)
- {
- // translate the object so that its minimum z lays on the bed
- for (ModelVolume *v : this->volumes)
- {
- v->mesh.translate(0.0f, 0.0f, -min_z);
- }
- }
+ center_around_origin();
this->origin_translation = Pointf3(0, 0, 0);
this->invalidate_bounding_box();
@@ -800,6 +748,7 @@ void ModelObject::transform(const float* matrix3x4)
for (ModelVolume* v : volumes)
{
v->mesh.transform(matrix3x4);
+ v->m_convex_hull.transform(matrix3x4);
}
origin_translation = Pointf3(0.0, 0.0, 0.0);
@@ -809,8 +758,12 @@ void ModelObject::transform(const float* matrix3x4)
void ModelObject::mirror(const Axis &axis)
{
for (ModelVolume *v : this->volumes)
+ {
v->mesh.mirror(axis);
- this->origin_translation = Pointf3(0,0,0);
+ v->m_convex_hull.mirror(axis);
+ }
+
+ this->origin_translation = Pointf3(0, 0, 0);
this->invalidate_bounding_box();
}
@@ -914,45 +867,20 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
void ModelObject::check_instances_print_volume_state(const BoundingBoxf3& print_volume)
{
- for (ModelVolume* vol : this->volumes)
+ for (const ModelVolume* vol : this->volumes)
{
if (!vol->modifier)
{
for (ModelInstance* inst : this->instances)
{
- BoundingBoxf3 bb;
-
- double c = cos(inst->rotation);
- double s = sin(inst->rotation);
+ std::vector<float> world_mat(UNIT_MATRIX, std::end(UNIT_MATRIX));
+ Eigen::Transform<float, 3, Eigen::Affine> m = Eigen::Transform<float, 3, Eigen::Affine>::Identity();
+ m.translate(Eigen::Vector3f((float)inst->offset.x, (float)inst->offset.y, 0.0f));
+ m.rotate(Eigen::AngleAxisf(inst->rotation, Eigen::Vector3f::UnitZ()));
+ m.scale(inst->scaling_factor);
+ ::memcpy((void*)world_mat.data(), (const void*)m.data(), 16 * sizeof(float));
- for (int f = 0; f < vol->mesh.stl.stats.number_of_facets; ++f)
- {
- const stl_facet& facet = vol->mesh.stl.facet_start[f];
-
- for (int i = 0; i < 3; ++i)
- {
- // original point
- const stl_vertex& v = facet.vertex[i];
- Pointf3 p((double)v.x, (double)v.y, (double)v.z);
-
- // scale
- p.x *= inst->scaling_factor;
- p.y *= inst->scaling_factor;
- p.z *= inst->scaling_factor;
-
- // rotate Z
- double x = p.x;
- double y = p.y;
- p.x = c * x - s * y;
- p.y = s * x + c * y;
-
- // translate
- p.x += inst->offset.x;
- p.y += inst->offset.y;
-
- bb.merge(p);
- }
- }
+ BoundingBoxf3 bb = vol->get_convex_hull().transformed_bounding_box(world_mat);
if (print_volume.contains(bb))
inst->print_volume_state = ModelInstance::PVS_Inside;
@@ -1035,6 +963,16 @@ ModelMaterial* ModelVolume::assign_unique_material()
return model->add_material(this->_material_id);
}
+void ModelVolume::calculate_convex_hull()
+{
+ m_convex_hull = mesh.convex_hull_3d();
+}
+
+const TriangleMesh& ModelVolume::get_convex_hull() const
+{
+ return m_convex_hull;
+}
+
// Split this volume, append the result to the object owning this volume.
// Return the number of volumes created from this one.
// This is useful to assign different materials to different volumes of an object.
diff --git a/xs/src/libslic3r/Model.hpp b/xs/src/libslic3r/Model.hpp
index 4c650f0de..dadd515de 100644
--- a/xs/src/libslic3r/Model.hpp
+++ b/xs/src/libslic3r/Model.hpp
@@ -105,9 +105,6 @@ public:
// This bounding box is being cached.
const BoundingBoxf3& bounding_box() const;
void invalidate_bounding_box() { m_bounding_box_valid = false; }
- // Returns a snug bounding box of the transformed instances.
- // This bounding box is not being cached.
- BoundingBoxf3 tight_bounding_box(bool include_modifiers) const;
// A mesh containing all transformed instances of this object.
TriangleMesh mesh() const;
@@ -123,7 +120,7 @@ public:
void translate(const Vectorf3 &vector) { this->translate(vector.x, vector.y, vector.z); }
void translate(coordf_t x, coordf_t y, coordf_t z);
void scale(const Pointf3 &versor);
- void rotate(float angle, const Axis &axis);
+ void rotate(float angle, const Pointf3& axis);
void transform(const float* matrix3x4);
void mirror(const Axis &axis);
size_t materials_count() const;
@@ -157,6 +154,10 @@ private:
class ModelVolume
{
friend class ModelObject;
+
+ // The convex hull of this model's mesh.
+ TriangleMesh m_convex_hull;
+
public:
std::string name;
// The triangular model.
@@ -180,19 +181,32 @@ public:
ModelMaterial* assign_unique_material();
+ void calculate_convex_hull();
+ const TriangleMesh& get_convex_hull() const;
+
private:
// Parent object owning this ModelVolume.
ModelObject* object;
t_model_material_id _material_id;
- ModelVolume(ModelObject *object, const TriangleMesh &mesh) : mesh(mesh), modifier(false), object(object) {}
- ModelVolume(ModelObject *object, TriangleMesh &&mesh) : mesh(std::move(mesh)), modifier(false), object(object) {}
- ModelVolume(ModelObject *object, const ModelVolume &other) :
- name(other.name), mesh(other.mesh), config(other.config), modifier(other.modifier), object(object)
- { this->material_id(other.material_id()); }
- ModelVolume(ModelObject *object, const ModelVolume &other, const TriangleMesh &&mesh) :
+ ModelVolume(ModelObject *object, const TriangleMesh &mesh) : mesh(mesh), modifier(false), object(object)
+ {
+ if (mesh.stl.stats.number_of_facets > 1)
+ calculate_convex_hull();
+ }
+ ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull) : mesh(std::move(mesh)), m_convex_hull(std::move(convex_hull)), modifier(false), object(object) {}
+ ModelVolume(ModelObject *object, const ModelVolume &other) :
+ name(other.name), mesh(other.mesh), m_convex_hull(other.m_convex_hull), config(other.config), modifier(other.modifier), object(object)
+ {
+ this->material_id(other.material_id());
+ }
+ ModelVolume(ModelObject *object, const ModelVolume &other, const TriangleMesh &&mesh) :
name(other.name), mesh(std::move(mesh)), config(other.config), modifier(other.modifier), object(object)
- { this->material_id(other.material_id()); }
+ {
+ this->material_id(other.material_id());
+ if (mesh.stl.stats.number_of_facets > 1)
+ calculate_convex_hull();
+ }
};
// A single instance of a ModelObject.
@@ -285,8 +299,6 @@ public:
bool add_default_instances();
// Returns approximate axis aligned bounding box of this model
BoundingBoxf3 bounding_box() const;
- // Returns tight axis aligned bounding box of this model
- BoundingBoxf3 transformed_bounding_box() const;
void center_instances_around_point(const Pointf &point);
void translate(coordf_t x, coordf_t y, coordf_t z) { for (ModelObject *o : this->objects) o->translate(x, y, z); }
TriangleMesh mesh() const;
diff --git a/xs/src/libslic3r/ModelArrange.hpp b/xs/src/libslic3r/ModelArrange.hpp
index f2d399ac6..b1ccf4d13 100644
--- a/xs/src/libslic3r/ModelArrange.hpp
+++ b/xs/src/libslic3r/ModelArrange.hpp
@@ -99,54 +99,56 @@ namespace bgi = boost::geometry::index;
using SpatElement = std::pair<Box, unsigned>;
using SpatIndex = bgi::rtree< SpatElement, bgi::rstar<16, 4> >;
+using ItemGroup = std::vector<std::reference_wrapper<Item>>;
+template<class TBin>
+using TPacker = typename placers::_NofitPolyPlacer<PolygonImpl, TBin>;
+
+const double BIG_ITEM_TRESHOLD = 0.02;
+
+Box boundingBox(const Box& pilebb, const Box& ibb ) {
+ auto& pminc = pilebb.minCorner();
+ auto& pmaxc = pilebb.maxCorner();
+ auto& iminc = ibb.minCorner();
+ auto& imaxc = ibb.maxCorner();
+ PointImpl minc, maxc;
+
+ setX(minc, std::min(getX(pminc), getX(iminc)));
+ setY(minc, std::min(getY(pminc), getY(iminc)));
+
+ setX(maxc, std::max(getX(pmaxc), getX(imaxc)));
+ setY(maxc, std::max(getY(pmaxc), getY(imaxc)));
+ return Box(minc, maxc);
+}
std::tuple<double /*score*/, Box /*farthest point from bin center*/>
objfunc(const PointImpl& bincenter,
- double /*bin_area*/,
- ShapeLike::Shapes<PolygonImpl>& pile, // The currently arranged pile
- double /*pile_area*/,
+ const shapelike::Shapes<PolygonImpl>& merged_pile,
+ const Box& pilebb,
+ const ItemGroup& items,
const Item &item,
+ double bin_area,
double norm, // A norming factor for physical dimensions
- std::vector<double>& areacache, // pile item areas will be cached
// a spatial index to quickly get neighbors of the candidate item
- SpatIndex& spatindex
+ const SpatIndex& spatindex,
+ const SpatIndex& smalls_spatindex,
+ const ItemGroup& remaining
)
{
- using pl = PointLike;
- using sl = ShapeLike;
+ using Coord = TCoord<PointImpl>;
- static const double BIG_ITEM_TRESHOLD = 0.2;
static const double ROUNDNESS_RATIO = 0.5;
static const double DENSITY_RATIO = 1.0 - ROUNDNESS_RATIO;
// We will treat big items (compared to the print bed) differently
- auto normarea = [norm](double area) { return std::sqrt(area)/norm; };
-
- // If a new bin has been created:
- if(pile.size() < areacache.size()) {
- areacache.clear();
- spatindex.clear();
- }
-
- // We must fill the caches:
- int idx = 0;
- for(auto& p : pile) {
- if(idx == areacache.size()) {
- areacache.emplace_back(sl::area(p));
- if(normarea(areacache[idx]) > BIG_ITEM_TRESHOLD)
- spatindex.insert({sl::boundingBox(p), idx});
- }
-
- idx++;
- }
+ auto isBig = [bin_area](double a) {
+ return a/bin_area > BIG_ITEM_TRESHOLD ;
+ };
// Candidate item bounding box
- auto ibb = item.boundingBox();
+ auto ibb = sl::boundingBox(item.transformedShape());
// Calculate the full bounding box of the pile with the candidate item
- pile.emplace_back(item.transformedShape());
- auto fullbb = ShapeLike::boundingBox(pile);
- pile.pop_back();
+ auto fullbb = boundingBox(pilebb, ibb);
// The bounding box of the big items (they will accumulate in the center
// of the pile
@@ -157,19 +159,11 @@ objfunc(const PointImpl& bincenter,
boost::geometry::convert(boostbb, bigbb);
}
- // The size indicator of the candidate item. This is not the area,
- // but almost...
- double item_normarea = normarea(item.area());
-
// Will hold the resulting score
double score = 0;
- if(item_normarea > BIG_ITEM_TRESHOLD) {
+ if(isBig(item.area()) || spatindex.empty()) {
// This branch is for the bigger items..
- // Here we will use the closest point of the item bounding box to
- // the already arranged pile. So not the bb center nor the a choosen
- // corner but whichever is the closest to the center. This will
- // prevent some unwanted strange arrangements.
auto minc = ibb.minCorner(); // bottom left corner
auto maxc = ibb.maxCorner(); // top right corner
@@ -190,48 +184,68 @@ objfunc(const PointImpl& bincenter,
// The smalles distance from the arranged pile center:
auto dist = *(std::min_element(dists.begin(), dists.end())) / norm;
+ auto bindist = pl::distance(ibb.center(), bincenter) / norm;
+ dist = 0.8*dist + 0.2*bindist;
// Density is the pack density: how big is the arranged pile
- auto density = std::sqrt(fullbb.width()*fullbb.height()) / norm;
-
- // Prepare a variable for the alignment score.
- // This will indicate: how well is the candidate item aligned with
- // its neighbors. We will check the aligment with all neighbors and
- // return the score for the best alignment. So it is enough for the
- // candidate to be aligned with only one item.
- auto alignment_score = std::numeric_limits<double>::max();
-
- auto& trsh = item.transformedShape();
-
- auto querybb = item.boundingBox();
+ double density = 0;
+
+ if(remaining.empty()) {
+
+ auto mp = merged_pile;
+ mp.emplace_back(item.transformedShape());
+ auto chull = sl::convexHull(mp);
+
+ placers::EdgeCache<PolygonImpl> ec(chull);
+
+ double circ = ec.circumference() / norm;
+ double bcirc = 2.0*(fullbb.width() + fullbb.height()) / norm;
+ score = 0.5*circ + 0.5*bcirc;
+
+ } else {
+ // Prepare a variable for the alignment score.
+ // This will indicate: how well is the candidate item aligned with
+ // its neighbors. We will check the alignment with all neighbors and
+ // return the score for the best alignment. So it is enough for the
+ // candidate to be aligned with only one item.
+ auto alignment_score = 1.0;
+
+ density = std::sqrt((fullbb.width() / norm )*
+ (fullbb.height() / norm));
+ auto querybb = item.boundingBox();
+
+ // Query the spatial index for the neighbors
+ std::vector<SpatElement> result;
+ result.reserve(spatindex.size());
+ if(isBig(item.area())) {
+ spatindex.query(bgi::intersects(querybb),
+ std::back_inserter(result));
+ } else {
+ smalls_spatindex.query(bgi::intersects(querybb),
+ std::back_inserter(result));
+ }
- // Query the spatial index for the neigbours
- std::vector<SpatElement> result;
- spatindex.query(bgi::intersects(querybb), std::back_inserter(result));
+ for(auto& e : result) { // now get the score for the best alignment
+ auto idx = e.second;
+ Item& p = items[idx];
+ auto parea = p.area();
+ if(std::abs(1.0 - parea/item.area()) < 1e-6) {
+ auto bb = boundingBox(p.boundingBox(), ibb);
+ auto bbarea = bb.area();
+ auto ascore = 1.0 - (item.area() + parea)/bbarea;
- for(auto& e : result) { // now get the score for the best alignment
- auto idx = e.second;
- auto& p = pile[idx];
- auto parea = areacache[idx];
- auto bb = sl::boundingBox(sl::Shapes<PolygonImpl>{p, trsh});
- auto bbarea = bb.area();
- auto ascore = 1.0 - (item.area() + parea)/bbarea;
+ if(ascore < alignment_score) alignment_score = ascore;
+ }
+ }
- if(ascore < alignment_score) alignment_score = ascore;
+ // The final mix of the score is the balance between the distance
+ // from the full pile center, the pack density and the
+ // alignment with the neighbors
+ if(result.empty())
+ score = 0.5 * dist + 0.5 * density;
+ else
+ score = 0.40 * dist + 0.40 * density + 0.2 * alignment_score;
}
-
- // The final mix of the score is the balance between the distance
- // from the full pile center, the pack density and the
- // alignment with the neigbours
- auto C = 0.33;
- score = C * dist + C * density + C * alignment_score;
-
- } else if( item_normarea < BIG_ITEM_TRESHOLD && spatindex.empty()) {
- // If there are no big items, only small, we should consider the
- // density here as well to not get silly results
- auto bindist = pl::distance(ibb.center(), bincenter) / norm;
- auto density = std::sqrt(fullbb.width()*fullbb.height()) / norm;
- score = ROUNDNESS_RATIO * bindist + DENSITY_RATIO * density;
} else {
// Here there are the small items that should be placed around the
// already processed bigger items.
@@ -259,7 +273,9 @@ void fillConfig(PConf& pcfg) {
// The accuracy of optimization.
// Goes from 0.0 to 1.0 and scales performance as well
- pcfg.accuracy = 0.6f;
+ pcfg.accuracy = 0.65f;
+
+ pcfg.parallel = true;
}
template<class TBin>
@@ -268,31 +284,65 @@ class AutoArranger {};
template<class TBin>
class _ArrBase {
protected:
- using Placer = strategies::_NofitPolyPlacer<PolygonImpl, TBin>;
+
+ using Placer = TPacker<TBin>;
using Selector = FirstFitSelection;
- using Packer = Arranger<Placer, Selector>;
+ using Packer = Nester<Placer, Selector>;
using PConfig = typename Packer::PlacementConfig;
using Distance = TCoord<PointImpl>;
- using Pile = ShapeLike::Shapes<PolygonImpl>;
+ using Pile = sl::Shapes<PolygonImpl>;
Packer pck_;
PConfig pconf_; // Placement configuration
double bin_area_;
- std::vector<double> areacache_;
SpatIndex rtree_;
+ SpatIndex smallsrtree_;
+ double norm_;
+ Pile merged_pile_;
+ Box pilebb_;
+ ItemGroup remaining_;
+ ItemGroup items_;
public:
_ArrBase(const TBin& bin, Distance dist,
std::function<void(unsigned)> progressind):
- pck_(bin, dist), bin_area_(ShapeLike::area<PolygonImpl>(bin))
+ pck_(bin, dist), bin_area_(sl::area(bin)),
+ norm_(std::sqrt(sl::area(bin)))
{
fillConfig(pconf_);
+
+ pconf_.before_packing =
+ [this](const Pile& merged_pile, // merged pile
+ const ItemGroup& items, // packed items
+ const ItemGroup& remaining) // future items to be packed
+ {
+ items_ = items;
+ merged_pile_ = merged_pile;
+ remaining_ = remaining;
+
+ pilebb_ = sl::boundingBox(merged_pile);
+
+ rtree_.clear();
+ smallsrtree_.clear();
+
+ // We will treat big items (compared to the print bed) differently
+ auto isBig = [this](double a) {
+ return a/bin_area_ > BIG_ITEM_TRESHOLD ;
+ };
+
+ for(unsigned idx = 0; idx < items.size(); ++idx) {
+ Item& itm = items[idx];
+ if(isBig(itm.area())) rtree_.insert({itm.boundingBox(), idx});
+ smallsrtree_.insert({itm.boundingBox(), idx});
+ }
+ };
+
pck_.progressIndicator(progressind);
}
template<class...Args> inline IndexedPackGroup operator()(Args&&...args) {
- areacache_.clear();
- return pck_.arrangeIndexed(std::forward<Args>(args)...);
+ rtree_.clear();
+ return pck_.executeIndexed(std::forward<Args>(args)...);
}
};
@@ -304,22 +354,71 @@ public:
std::function<void(unsigned)> progressind):
_ArrBase<Box>(bin, dist, progressind)
{
- pconf_.object_function = [this, bin] (
- Pile& pile,
- const Item &item,
- double pile_area,
- double norm,
- double /*penality*/) {
-
- auto result = objfunc(bin.center(), bin_area_, pile,
- pile_area, item, norm, areacache_, rtree_);
+
+ pconf_.object_function = [this, bin] (const Item &item) {
+
+ auto result = objfunc(bin.center(),
+ merged_pile_,
+ pilebb_,
+ items_,
+ item,
+ bin_area_,
+ norm_,
+ rtree_,
+ smallsrtree_,
+ remaining_);
+
double score = std::get<0>(result);
auto& fullbb = std::get<1>(result);
- auto wdiff = fullbb.width() - bin.width();
- auto hdiff = fullbb.height() - bin.height();
- if(wdiff > 0) score += std::pow(wdiff, 2) / norm;
- if(hdiff > 0) score += std::pow(hdiff, 2) / norm;
+ double miss = Placer::overfit(fullbb, bin);
+ miss = miss > 0? miss : 0;
+ score += miss*miss;
+
+ return score;
+ };
+
+ pck_.configure(pconf_);
+ }
+};
+
+using lnCircle = libnest2d::_Circle<libnest2d::PointImpl>;
+
+template<>
+class AutoArranger<lnCircle>: public _ArrBase<lnCircle> {
+public:
+
+ AutoArranger(const lnCircle& bin, Distance dist,
+ std::function<void(unsigned)> progressind):
+ _ArrBase<lnCircle>(bin, dist, progressind) {
+
+ pconf_.object_function = [this, &bin] (const Item &item) {
+
+ auto result = objfunc(bin.center(),
+ merged_pile_,
+ pilebb_,
+ items_,
+ item,
+ bin_area_,
+ norm_,
+ rtree_,
+ smallsrtree_,
+ remaining_);
+
+ double score = std::get<0>(result);
+
+ auto isBig = [this](const Item& itm) {
+ return itm.area()/bin_area_ > BIG_ITEM_TRESHOLD ;
+ };
+
+ if(isBig(item)) {
+ auto mp = merged_pile_;
+ mp.push_back(item.transformedShape());
+ auto chull = sl::convexHull(mp);
+ double miss = Placer::overfit(chull, bin);
+ if(miss < 0) miss = 0;
+ score += miss*miss;
+ }
return score;
};
@@ -335,27 +434,21 @@ public:
std::function<void(unsigned)> progressind):
_ArrBase<PolygonImpl>(bin, dist, progressind)
{
- pconf_.object_function = [this, &bin] (
- Pile& pile,
- const Item &item,
- double pile_area,
- double norm,
- double /*penality*/) {
-
- auto binbb = ShapeLike::boundingBox(bin);
- auto result = objfunc(binbb.center(), bin_area_, pile,
- pile_area, item, norm, areacache_, rtree_);
+ pconf_.object_function = [this, &bin] (const Item &item) {
+
+ auto binbb = sl::boundingBox(bin);
+ auto result = objfunc(binbb.center(),
+ merged_pile_,
+ pilebb_,
+ items_,
+ item,
+ bin_area_,
+ norm_,
+ rtree_,
+ smallsrtree_,
+ remaining_);
double score = std::get<0>(result);
- pile.emplace_back(item.transformedShape());
- auto chull = ShapeLike::convexHull(pile);
- pile.pop_back();
-
- // If it does not fit into the print bed we will beat it with a
- // large penality. If we would not do this, there would be only one
- // big pile that doesn't care whether it fits onto the print bed.
- if(!Placer::wouldFit(chull, bin)) score += norm;
-
return score;
};
@@ -370,15 +463,18 @@ public:
AutoArranger(Distance dist, std::function<void(unsigned)> progressind):
_ArrBase<Box>(Box(0, 0), dist, progressind)
{
- this->pconf_.object_function = [this] (
- Pile& pile,
- const Item &item,
- double pile_area,
- double norm,
- double /*penality*/) {
-
- auto result = objfunc({0, 0}, 0, pile, pile_area,
- item, norm, areacache_, rtree_);
+ this->pconf_.object_function = [this] (const Item &item) {
+
+ auto result = objfunc({0, 0},
+ merged_pile_,
+ pilebb_,
+ items_,
+ item,
+ 0,
+ norm_,
+ rtree_,
+ smallsrtree_,
+ remaining_);
return std::get<0>(result);
};
@@ -440,16 +536,104 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model) {
return ret;
}
-enum BedShapeHint {
+class Circle {
+ Point center_;
+ double radius_;
+public:
+
+ inline Circle(): center_(0, 0), radius_(std::nan("")) {}
+ inline Circle(const Point& c, double r): center_(c), radius_(r) {}
+
+ inline double radius() const { return radius_; }
+ inline const Point& center() const { return center_; }
+ inline operator bool() { return !std::isnan(radius_); }
+};
+
+enum class BedShapeType {
BOX,
CIRCLE,
IRREGULAR,
WHO_KNOWS
};
-BedShapeHint bedShape(const Slic3r::Polyline& /*bed*/) {
+struct BedShapeHint {
+ BedShapeType type;
+ /*union*/ struct { // I know but who cares...
+ Circle circ;
+ BoundingBox box;
+ Polyline polygon;
+ } shape;
+};
+
+BedShapeHint bedShape(const Polyline& bed) {
+ static const double E = 10/SCALING_FACTOR;
+
+ BedShapeHint ret;
+
+ auto width = [](const BoundingBox& box) {
+ return box.max.x - box.min.x;
+ };
+
+ auto height = [](const BoundingBox& box) {
+ return box.max.y - box.min.y;
+ };
+
+ auto area = [&width, &height](const BoundingBox& box) {
+ double w = width(box);
+ double h = height(box);
+ return w*h;
+ };
+
+ auto poly_area = [](Polyline p) {
+ Polygon pp; pp.points.reserve(p.points.size() + 1);
+ pp.points = std::move(p.points);
+ pp.points.emplace_back(pp.points.front());
+ return std::abs(pp.area());
+ };
+
+
+ auto bb = bed.bounding_box();
+
+ auto isCircle = [bb](const Polyline& polygon) {
+ auto center = bb.center();
+ std::vector<double> vertex_distances;
+ double avg_dist = 0;
+ for (auto pt: polygon.points)
+ {
+ double distance = center.distance_to(pt);
+ vertex_distances.push_back(distance);
+ avg_dist += distance;
+ }
+
+ avg_dist /= vertex_distances.size();
+
+ Circle ret(center, avg_dist);
+ for (auto el: vertex_distances)
+ {
+ if (abs(el - avg_dist) > 10 * SCALED_EPSILON)
+ ret = Circle();
+ break;
+ }
+
+ return ret;
+ };
+
+ auto parea = poly_area(bed);
+
+ if( (1.0 - parea/area(bb)) < 1e-3 ) {
+ ret.type = BedShapeType::BOX;
+ ret.shape.box = bb;
+ }
+ else if(auto c = isCircle(bed)) {
+ ret.type = BedShapeType::CIRCLE;
+ ret.shape.circ = c;
+ } else {
+ ret.type = BedShapeType::IRREGULAR;
+ ret.shape.polygon = bed;
+ }
+
// Determine the bed shape by hand
- return BOX;
+ return ret;
}
void applyResult(
@@ -525,7 +709,10 @@ bool arrange(Model &model, coordf_t min_obj_distance,
});
IndexedPackGroup result;
- BoundingBox bbb(bed.points);
+
+ if(bedhint.type == BedShapeType::WHO_KNOWS) bedhint = bedShape(bed);
+
+ BoundingBox bbb(bed);
auto binbb = Box({
static_cast<libnest2d::Coord>(bbb.min.x),
@@ -536,8 +723,8 @@ bool arrange(Model &model, coordf_t min_obj_distance,
static_cast<libnest2d::Coord>(bbb.max.y)
});
- switch(bedhint) {
- case BOX: {
+ switch(bedhint.type) {
+ case BedShapeType::BOX: {
// Create the arranger for the box shaped bed
AutoArranger<Box> arrange(binbb, min_obj_distance, progressind);
@@ -547,16 +734,22 @@ bool arrange(Model &model, coordf_t min_obj_distance,
result = arrange(shapes.begin(), shapes.end());
break;
}
- case CIRCLE:
+ case BedShapeType::CIRCLE: {
+
+ auto c = bedhint.shape.circ;
+ auto cc = lnCircle({c.center().x, c.center().y} , c.radius());
+
+ AutoArranger<lnCircle> arrange(cc, min_obj_distance, progressind);
+ result = arrange(shapes.begin(), shapes.end());
break;
- case IRREGULAR:
- case WHO_KNOWS: {
+ }
+ case BedShapeType::IRREGULAR:
+ case BedShapeType::WHO_KNOWS: {
+
using P = libnest2d::PolygonImpl;
auto ctour = Slic3rMultiPoint_to_ClipperPath(bed);
- P irrbed = ShapeLike::create<PolygonImpl>(std::move(ctour));
-
-// std::cout << ShapeLike::toString(irrbed) << std::endl;
+ P irrbed = sl::create<PolygonImpl>(std::move(ctour));
AutoArranger<P> arrange(irrbed, min_obj_distance, progressind);
@@ -567,6 +760,8 @@ bool arrange(Model &model, coordf_t min_obj_distance,
}
};
+ if(result.empty()) return false;
+
if(first_bin_only) {
applyResult(result.front(), 0, shapemap);
} else {
diff --git a/xs/src/libslic3r/Point.cpp b/xs/src/libslic3r/Point.cpp
index 2abcd26af..349b00bb6 100644
--- a/xs/src/libslic3r/Point.cpp
+++ b/xs/src/libslic3r/Point.cpp
@@ -263,6 +263,12 @@ operator<<(std::ostream &stm, const Pointf &pointf)
return stm << pointf.x << "," << pointf.y;
}
+double
+Pointf::ccw(const Pointf &p1, const Pointf &p2) const
+{
+ return (double)(p2.x - p1.x)*(double)(this->y - p1.y) - (double)(p2.y - p1.y)*(double)(this->x - p1.x);
+}
+
std::string
Pointf::wkt() const
{
diff --git a/xs/src/libslic3r/Point.hpp b/xs/src/libslic3r/Point.hpp
index 87104674f..347966c03 100644
--- a/xs/src/libslic3r/Point.hpp
+++ b/xs/src/libslic3r/Point.hpp
@@ -221,6 +221,7 @@ public:
static Pointf new_unscale(const Point &p) {
return Pointf(unscale(p.x), unscale(p.y));
};
+ double ccw(const Pointf &p1, const Pointf &p2) const;
std::string wkt() const;
std::string dump_perl() const;
void scale(double factor);
diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index 7d2906bcc..eb2112ef0 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -200,7 +200,9 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
|| opt_key == "filament_soluble"
|| opt_key == "first_layer_temperature"
|| opt_key == "filament_loading_speed"
+ || opt_key == "filament_loading_speed_start"
|| opt_key == "filament_unloading_speed"
+ || opt_key == "filament_unloading_speed_start"
|| opt_key == "filament_toolchange_delay"
|| opt_key == "filament_cooling_moves"
|| opt_key == "filament_minimal_purge_on_wipe_tower"
@@ -1123,7 +1125,9 @@ void Print::_make_wipe_tower()
this->config.temperature.get_at(i),
this->config.first_layer_temperature.get_at(i),
this->config.filament_loading_speed.get_at(i),
+ this->config.filament_loading_speed_start.get_at(i),
this->config.filament_unloading_speed.get_at(i),
+ this->config.filament_unloading_speed_start.get_at(i),
this->config.filament_toolchange_delay.get_at(i),
this->config.filament_cooling_moves.get_at(i),
this->config.filament_cooling_initial_speed.get_at(i),
@@ -1189,6 +1193,9 @@ void Print::_make_wipe_tower()
}
m_wipe_tower_final_purge = Slic3r::make_unique<WipeTower::ToolChangeResult>(
wipe_tower.tool_change((unsigned int)-1, false));
+
+ m_wipe_tower_used_filament = wipe_tower.get_used_filament();
+ m_wipe_tower_number_of_toolchanges = wipe_tower.get_number_of_toolchanges();
}
std::string Print::output_filename()
diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp
index e3430ad0e..95b8abc5b 100644
--- a/xs/src/libslic3r/Print.hpp
+++ b/xs/src/libslic3r/Print.hpp
@@ -240,7 +240,7 @@ public:
// TODO: status_cb
std::string estimated_normal_print_time;
std::string estimated_silent_print_time;
- double total_used_filament, total_extruded_volume, total_cost, total_weight;
+ double total_used_filament, total_extruded_volume, total_cost, total_weight, total_wipe_tower_cost, total_wipe_tower_filament;
std::map<size_t, float> filament_stats;
PrintState<PrintStep, psCount> state;
@@ -309,6 +309,8 @@ public:
std::unique_ptr<WipeTower::ToolChangeResult> m_wipe_tower_priming;
std::vector<std::vector<WipeTower::ToolChangeResult>> m_wipe_tower_tool_changes;
std::unique_ptr<WipeTower::ToolChangeResult> m_wipe_tower_final_purge;
+ std::vector<float> m_wipe_tower_used_filament;
+ int m_wipe_tower_number_of_toolchanges = -1;
std::string output_filename();
std::string output_filepath(const std::string &path);
diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp
index d8f2d85a6..f9f0b2056 100644
--- a/xs/src/libslic3r/PrintConfig.cpp
+++ b/xs/src/libslic3r/PrintConfig.cpp
@@ -473,6 +473,14 @@ PrintConfigDef::PrintConfigDef()
def->min = 0;
def->default_value = new ConfigOptionFloats { 28. };
+ def = this->add("filament_loading_speed_start", coFloats);
+ def->label = L("Loading speed at the start");
+ def->tooltip = L("Speed used at the very beginning of loading phase. ");
+ def->sidetext = L("mm/s");
+ def->cli = "filament-loading-speed-start=f@";
+ def->min = 0;
+ def->default_value = new ConfigOptionFloats { 3. };
+
def = this->add("filament_unloading_speed", coFloats);
def->label = L("Unloading speed");
def->tooltip = L("Speed used for unloading the filament on the wipe tower (does not affect "
@@ -482,6 +490,14 @@ PrintConfigDef::PrintConfigDef()
def->min = 0;
def->default_value = new ConfigOptionFloats { 90. };
+ def = this->add("filament_unloading_speed_start", coFloats);
+ def->label = L("Unloading speed at the start");
+ def->tooltip = L("Speed used for unloading the tip of the filament immediately after ramming. ");
+ def->sidetext = L("mm/s");
+ def->cli = "filament-unloading-speed-start=f@";
+ def->min = 0;
+ def->default_value = new ConfigOptionFloats { 100. };
+
def = this->add("filament_toolchange_delay", coFloats);
def->label = L("Delay after unloading");
def->tooltip = L("Time to wait after the filament is unloaded. "
@@ -518,7 +534,7 @@ PrintConfigDef::PrintConfigDef()
def->cli = "filament-minimal-purge-on-wipe-tower=f@";
def->sidetext = L("mm³");
def->min = 0;
- def->default_value = new ConfigOptionFloats { 5.f };
+ def->default_value = new ConfigOptionFloats { 15.f };
def = this->add("filament_cooling_final_speed", coFloats);
def->label = L("Speed of the last cooling move");
@@ -572,10 +588,7 @@ PrintConfigDef::PrintConfigDef()
def = this->add("filament_type", coStrings);
def->label = L("Filament type");
- def->tooltip = L("If you want to process the output G-code through custom scripts, just list their "
- "absolute paths here. Separate multiple scripts with a semicolon. Scripts will be passed "
- "the absolute path to the G-code file as the first argument, and they can access "
- "the Slic3r config settings by reading environment variables.");
+ def->tooltip = L("The filament material type for use in custom G-codes.");
def->cli = "filament_type=s@";
def->gui_type = "f_enum_open";
def->gui_flags = "show_value";
@@ -921,7 +934,7 @@ PrintConfigDef::PrintConfigDef()
def = this->add("remaining_times", coBool);
def->label = L("Supports remaining times");
- def->tooltip = L("Emit M73 P[percent printed] R[remaining time in seconds] at 1 minute"
+ def->tooltip = L("Emit M73 P[percent printed] R[remaining time in minutes] at 1 minute"
" intervals into the G-code to let the firmware show accurate remaining time."
" As of now only the Prusa i3 MK3 firmware recognizes M73."
" Also the i3 MK3 firmware supports M73 Qxx Sxx for the silent mode.");
@@ -1140,25 +1153,37 @@ PrintConfigDef::PrintConfigDef()
def->cli = "nozzle-diameter=f@";
def->default_value = new ConfigOptionFloats { 0.5 };
- def = this->add("octoprint_apikey", coString);
- def->label = L("API Key");
- def->tooltip = L("Slic3r can upload G-code files to OctoPrint. This field should contain "
- "the API Key required for authentication.");
- def->cli = "octoprint-apikey=s";
+ def = this->add("host_type", coEnum);
+ def->label = L("Host Type");
+ def->tooltip = L("Slic3r can upload G-code files to a printer host. This field must contain "
+ "the kind of the host.");
+ def->cli = "host-type=s";
+ def->enum_keys_map = &ConfigOptionEnum<PrintHostType>::get_enum_values();
+ def->enum_values.push_back("octoprint");
+ def->enum_values.push_back("duet");
+ def->enum_labels.push_back("OctoPrint");
+ def->enum_labels.push_back("Duet");
+ def->default_value = new ConfigOptionEnum<PrintHostType>(htOctoPrint);
+
+ def = this->add("printhost_apikey", coString);
+ def->label = L("API Key / Password");
+ def->tooltip = L("Slic3r can upload G-code files to a printer host. This field should contain "
+ "the API Key or the password required for authentication.");
+ def->cli = "printhost-apikey=s";
def->default_value = new ConfigOptionString("");
- def = this->add("octoprint_cafile", coString);
+ def = this->add("printhost_cafile", coString);
def->label = "HTTPS CA file";
def->tooltip = "Custom CA certificate file can be specified for HTTPS OctoPrint connections, in crt/pem format. "
"If left blank, the default OS CA certificate repository is used.";
- def->cli = "octoprint-cafile=s";
+ def->cli = "printhost-cafile=s";
def->default_value = new ConfigOptionString("");
- def = this->add("octoprint_host", coString);
+ def = this->add("print_host", coString);
def->label = L("Hostname, IP or URL");
- def->tooltip = L("Slic3r can upload G-code files to OctoPrint. This field should contain "
- "the hostname, IP address or URL of the OctoPrint instance.");
- def->cli = "octoprint-host=s";
+ def->tooltip = L("Slic3r can upload G-code files to a printer host. This field should contain "
+ "the hostname, IP address or URL of the printer host instance.");
+ def->cli = "print-host=s";
def->default_value = new ConfigOptionString("");
def = this->add("only_retract_when_crossing_perimeters", coBool);
@@ -2034,7 +2059,7 @@ PrintConfigDef::PrintConfigDef()
def = this->add("wipe_into_infill", coBool);
def->category = L("Extruders");
- def->label = L("Purge into this object's infill");
+ def->label = L("Wipe into this object's infill");
def->tooltip = L("Purging after toolchange will done inside this object's infills. "
"This lowers the amount of waste but may result in longer print time "
" due to additional travel moves.");
@@ -2043,7 +2068,7 @@ PrintConfigDef::PrintConfigDef()
def = this->add("wipe_into_objects", coBool);
def->category = L("Extruders");
- def->label = L("Purge into this object");
+ def->label = L("Wipe into this object");
def->tooltip = L("Object will be used to purge the nozzle after a toolchange to save material "
"that would otherwise end up in the wipe tower and decrease print time. "
"Colours of the objects will be mixed as a result.");
@@ -2110,10 +2135,6 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
std::ostringstream oss;
oss << "0x0," << p.value.x << "x0," << p.value.x << "x" << p.value.y << ",0x" << p.value.y;
value = oss.str();
-// Maybe one day we will rename octoprint_host to print_host as it has been done in the upstream Slic3r.
-// Commenting this out fixes github issue #869 for now.
-// } else if (opt_key == "octoprint_host" && !value.empty()) {
-// opt_key = "print_host";
} else if ((opt_key == "perimeter_acceleration" && value == "25")
|| (opt_key == "infill_acceleration" && value == "50")) {
/* For historical reasons, the world's full of configs having these very low values;
@@ -2124,6 +2145,12 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
} else if (opt_key == "support_material_pattern" && value == "pillars") {
// Slic3r PE does not support the pillars. They never worked well.
value = "rectilinear";
+ } else if (opt_key == "octoprint_host") {
+ opt_key = "print_host";
+ } else if (opt_key == "octoprint_cafile") {
+ opt_key = "printhost_cafile";
+ } else if (opt_key == "octoprint_apikey") {
+ opt_key = "printhost_apikey";
}
// Ignore the following obsolete configuration keys:
@@ -2133,9 +2160,6 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
"standby_temperature", "scale", "rotate", "duplicate", "duplicate_grid",
"start_perimeters_at_concave_points", "start_perimeters_at_non_overhang", "randomize_start",
"seal_position", "vibration_limit", "bed_size",
- // Maybe one day we will rename octoprint_host to print_host as it has been done in the upstream Slic3r.
- // Commenting this out fixes github issue #869 for now.
- // "octoprint_host",
"print_center", "g0", "threads", "pressure_advance", "wipe_tower_per_color_wipe"
};
@@ -2145,7 +2169,6 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
}
if (! print_config_def.has(opt_key)) {
- //printf("Unknown option %s\n", opt_key.c_str());
opt_key = "";
return;
}
diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp
index b18603d87..932ed6054 100644
--- a/xs/src/libslic3r/PrintConfig.hpp
+++ b/xs/src/libslic3r/PrintConfig.hpp
@@ -27,6 +27,10 @@ enum GCodeFlavor {
gcfSmoothie, gcfNoExtrusion,
};
+enum PrintHostType {
+ htOctoPrint, htDuet,
+};
+
enum InfillPattern {
ipRectilinear, ipGrid, ipTriangles, ipStars, ipCubic, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb,
ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral,
@@ -61,6 +65,15 @@ template<> inline t_config_enum_values& ConfigOptionEnum<GCodeFlavor>::get_enum_
return keys_map;
}
+template<> inline t_config_enum_values& ConfigOptionEnum<PrintHostType>::get_enum_values() {
+ static t_config_enum_values keys_map;
+ if (keys_map.empty()) {
+ keys_map["octoprint"] = htOctoPrint;
+ keys_map["duet"] = htDuet;
+ }
+ return keys_map;
+}
+
template<> inline t_config_enum_values& ConfigOptionEnum<InfillPattern>::get_enum_values() {
static t_config_enum_values keys_map;
if (keys_map.empty()) {
@@ -528,8 +541,10 @@ public:
ConfigOptionFloats filament_cost;
ConfigOptionFloats filament_max_volumetric_speed;
ConfigOptionFloats filament_loading_speed;
+ ConfigOptionFloats filament_loading_speed_start;
ConfigOptionFloats filament_load_time;
ConfigOptionFloats filament_unloading_speed;
+ ConfigOptionFloats filament_unloading_speed_start;
ConfigOptionFloats filament_toolchange_delay;
ConfigOptionFloats filament_unload_time;
ConfigOptionInts filament_cooling_moves;
@@ -594,8 +609,10 @@ protected:
OPT_PTR(filament_cost);
OPT_PTR(filament_max_volumetric_speed);
OPT_PTR(filament_loading_speed);
+ OPT_PTR(filament_loading_speed_start);
OPT_PTR(filament_load_time);
OPT_PTR(filament_unloading_speed);
+ OPT_PTR(filament_unloading_speed_start);
OPT_PTR(filament_unload_time);
OPT_PTR(filament_toolchange_delay);
OPT_PTR(filament_cooling_moves);
@@ -789,18 +806,20 @@ class HostConfig : public StaticPrintConfig
{
STATIC_PRINT_CONFIG_CACHE(HostConfig)
public:
- ConfigOptionString octoprint_host;
- ConfigOptionString octoprint_apikey;
- ConfigOptionString octoprint_cafile;
+ ConfigOptionEnum<PrintHostType> host_type;
+ ConfigOptionString print_host;
+ ConfigOptionString printhost_apikey;
+ ConfigOptionString printhost_cafile;
ConfigOptionString serial_port;
ConfigOptionInt serial_speed;
protected:
void initialize(StaticCacheBase &cache, const char *base_ptr)
{
- OPT_PTR(octoprint_host);
- OPT_PTR(octoprint_apikey);
- OPT_PTR(octoprint_cafile);
+ OPT_PTR(host_type);
+ OPT_PTR(print_host);
+ OPT_PTR(printhost_apikey);
+ OPT_PTR(printhost_cafile);
OPT_PTR(serial_port);
OPT_PTR(serial_speed);
}
diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp
index 65a31fada..97e60306f 100644
--- a/xs/src/libslic3r/TriangleMesh.cpp
+++ b/xs/src/libslic3r/TriangleMesh.cpp
@@ -1,6 +1,9 @@
#include "TriangleMesh.hpp"
#include "ClipperUtils.hpp"
#include "Geometry.hpp"
+#include "qhull/src/libqhullcpp/Qhull.h"
+#include "qhull/src/libqhullcpp/QhullFacetList.h"
+#include "qhull/src/libqhullcpp/QhullVertexSet.h"
#include <cmath>
#include <deque>
#include <queue>
@@ -10,11 +13,14 @@
#include <utility>
#include <algorithm>
#include <math.h>
+#include <type_traits>
#include <boost/log/trivial.hpp>
#include <tbb/parallel_for.h>
+#include <Eigen/Dense>
+
// for SLIC3R_DEBUG_SLICE_PROCESSING
#include "libslic3r.h"
@@ -321,6 +327,17 @@ void TriangleMesh::translate(float x, float y, float z)
stl_invalidate_shared_vertices(&this->stl);
}
+void TriangleMesh::rotate(float angle, Pointf3 axis)
+{
+ if (angle == 0.f)
+ return;
+
+ axis = normalize(axis);
+ Eigen::Transform<float, 3, Eigen::Affine> m = Eigen::Transform<float, 3, Eigen::Affine>::Identity();
+ m.rotate(Eigen::AngleAxisf(angle, Eigen::Vector3f(axis.x, axis.y, axis.z)));
+ stl_transform(&stl, (float*)m.data());
+}
+
void TriangleMesh::rotate(float angle, const Axis &axis)
{
if (angle == 0.f)
@@ -597,6 +614,140 @@ BoundingBoxf3 TriangleMesh::bounding_box() const
return bb;
}
+BoundingBoxf3 TriangleMesh::transformed_bounding_box(const std::vector<float>& matrix) const
+{
+ bool has_shared = (stl.v_shared != nullptr);
+ if (!has_shared)
+ stl_generate_shared_vertices(&stl);
+
+ unsigned int vertices_count = (stl.stats.shared_vertices > 0) ? (unsigned int)stl.stats.shared_vertices : 3 * (unsigned int)stl.stats.number_of_facets;
+
+ if (vertices_count == 0)
+ return BoundingBoxf3();
+
+ Eigen::MatrixXf src_vertices(3, vertices_count);
+
+ if (stl.stats.shared_vertices > 0)
+ {
+ stl_vertex* vertex_ptr = stl.v_shared;
+ for (int i = 0; i < stl.stats.shared_vertices; ++i)
+ {
+ src_vertices(0, i) = vertex_ptr->x;
+ src_vertices(1, i) = vertex_ptr->y;
+ src_vertices(2, i) = vertex_ptr->z;
+ vertex_ptr += 1;
+ }
+ }
+ else
+ {
+ stl_facet* facet_ptr = stl.facet_start;
+ unsigned int v_id = 0;
+ while (facet_ptr < stl.facet_start + stl.stats.number_of_facets)
+ {
+ for (int i = 0; i < 3; ++i)
+ {
+ src_vertices(0, v_id) = facet_ptr->vertex[i].x;
+ src_vertices(1, v_id) = facet_ptr->vertex[i].y;
+ src_vertices(2, v_id) = facet_ptr->vertex[i].z;
+ }
+ facet_ptr += 1;
+ ++v_id;
+ }
+ }
+
+ if (!has_shared && (stl.stats.shared_vertices > 0))
+ stl_invalidate_shared_vertices(&stl);
+
+ Eigen::Transform<float, 3, Eigen::Affine> m;
+ ::memcpy((void*)m.data(), (const void*)matrix.data(), 16 * sizeof(float));
+
+ Eigen::MatrixXf dst_vertices(3, vertices_count);
+ dst_vertices = m * src_vertices.colwise().homogeneous();
+
+ float min_x = dst_vertices(0, 0);
+ float max_x = dst_vertices(0, 0);
+ float min_y = dst_vertices(1, 0);
+ float max_y = dst_vertices(1, 0);
+ float min_z = dst_vertices(2, 0);
+ float max_z = dst_vertices(2, 0);
+
+ for (int i = 1; i < vertices_count; ++i)
+ {
+ min_x = std::min(min_x, dst_vertices(0, i));
+ max_x = std::max(max_x, dst_vertices(0, i));
+ min_y = std::min(min_y, dst_vertices(1, i));
+ max_y = std::max(max_y, dst_vertices(1, i));
+ min_z = std::min(min_z, dst_vertices(2, i));
+ max_z = std::max(max_z, dst_vertices(2, i));
+ }
+
+ return BoundingBoxf3(Pointf3((coordf_t)min_x, (coordf_t)min_y, (coordf_t)min_z), Pointf3((coordf_t)max_x, (coordf_t)max_y, (coordf_t)max_z));
+}
+
+TriangleMesh TriangleMesh::convex_hull_3d() const
+{
+ // Helper struct for qhull:
+ struct PointForQHull{
+ PointForQHull(float x_p, float y_p, float z_p) : x((realT)x_p), y((realT)y_p), z((realT)z_p) {}
+ realT x, y, z;
+ };
+ std::vector<PointForQHull> src_vertices;
+
+ // We will now fill the vector with input points for computation:
+ stl_facet* facet_ptr = stl.facet_start;
+ while (facet_ptr < stl.facet_start + stl.stats.number_of_facets)
+ {
+ for (int i = 0; i < 3; ++i)
+ {
+ const stl_vertex& v = facet_ptr->vertex[i];
+ src_vertices.emplace_back(v.x, v.y, v.z);
+ }
+
+ facet_ptr += 1;
+ }
+
+ // The qhull call:
+ orgQhull::Qhull qhull;
+ qhull.disableOutputStream(); // we want qhull to be quiet
+ try
+ {
+ qhull.runQhull("", 3, (int)src_vertices.size(), (const realT*)(src_vertices.data()), "Qt");
+ }
+ catch (...)
+ {
+ std::cout << "Unable to create convex hull" << std::endl;
+ return TriangleMesh();
+ }
+
+ // Let's collect results:
+ Pointf3s det_vertices;
+ std::vector<Point3> facets;
+ auto facet_list = qhull.facetList().toStdVector();
+ for (const orgQhull::QhullFacet& facet : facet_list)
+ { // iterate through facets
+ orgQhull::QhullVertexSet vertices = facet.vertices();
+ for (int i = 0; i < 3; ++i)
+ { // iterate through facet's vertices
+
+ orgQhull::QhullPoint p = vertices[i].point();
+ const float* coords = p.coordinates();
+ det_vertices.emplace_back(coords[0], coords[1], coords[2]);
+ }
+ unsigned int size = (unsigned int)det_vertices.size();
+ facets.emplace_back(size - 3, size - 2, size - 1);
+ }
+
+ TriangleMesh output_mesh(det_vertices, facets);
+ output_mesh.repair();
+ output_mesh.require_shared_vertices();
+ return output_mesh;
+}
+
+const float* TriangleMesh::first_vertex() const
+{
+ return stl.facet_start ? &stl.facet_start->vertex[0].x : nullptr;
+}
+
void TriangleMesh::require_shared_vertices()
{
BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - start";
diff --git a/xs/src/libslic3r/TriangleMesh.hpp b/xs/src/libslic3r/TriangleMesh.hpp
index 89742fd4c..24e903c0a 100644
--- a/xs/src/libslic3r/TriangleMesh.hpp
+++ b/xs/src/libslic3r/TriangleMesh.hpp
@@ -40,6 +40,7 @@ public:
void scale(const Pointf3 &versor);
void translate(float x, float y, float z);
void rotate(float angle, const Axis &axis);
+ void rotate(float angle, Pointf3 axis);
void rotate_x(float angle);
void rotate_y(float angle);
void rotate_z(float angle);
@@ -53,8 +54,13 @@ public:
TriangleMeshPtrs split() const;
void merge(const TriangleMesh &mesh);
ExPolygons horizontal_projection() const;
+ const float* first_vertex() const;
Polygon convex_hull();
BoundingBoxf3 bounding_box() const;
+ // Returns the bbox of this TriangleMesh transformed by the given matrix
+ BoundingBoxf3 transformed_bounding_box(const std::vector<float>& matrix) const;
+ // Returns the convex hull of this TriangleMesh
+ TriangleMesh convex_hull_3d() const;
void reset_repair_stats();
bool needed_repair() const;
size_t facets_count() const;
@@ -66,7 +72,7 @@ public:
// Count disconnected triangle patches.
size_t number_of_patches() const;
- stl_file stl;
+ mutable stl_file stl;
bool repaired;
private:
diff --git a/xs/src/libslic3r/libslic3r.h b/xs/src/libslic3r/libslic3r.h
index 34f61cb12..ddd056bc7 100644
--- a/xs/src/libslic3r/libslic3r.h
+++ b/xs/src/libslic3r/libslic3r.h
@@ -14,7 +14,7 @@
#include <boost/thread.hpp>
#define SLIC3R_FORK_NAME "Slic3r Prusa Edition"
-#define SLIC3R_VERSION "1.41.0-beta"
+#define SLIC3R_VERSION "1.41.0"
#define SLIC3R_BUILD "UNKNOWN"
typedef int32_t coord_t;
diff --git a/xs/src/perlglue.cpp b/xs/src/perlglue.cpp
index c8aadc8c3..d6bd0e94c 100644
--- a/xs/src/perlglue.cpp
+++ b/xs/src/perlglue.cpp
@@ -64,9 +64,9 @@ REGISTER_CLASS(PresetCollection, "GUI::PresetCollection");
REGISTER_CLASS(PresetBundle, "GUI::PresetBundle");
REGISTER_CLASS(TabIface, "GUI::Tab");
REGISTER_CLASS(PresetUpdater, "PresetUpdater");
-REGISTER_CLASS(OctoPrint, "OctoPrint");
REGISTER_CLASS(AppController, "AppController");
REGISTER_CLASS(PrintController, "PrintController");
+REGISTER_CLASS(PrintHost, "PrintHost");
SV* ConfigBase__as_hash(ConfigBase* THIS)
{
diff --git a/xs/src/qhull/Announce.txt b/xs/src/qhull/Announce.txt
new file mode 100644
index 000000000..635cff1af
--- /dev/null
+++ b/xs/src/qhull/Announce.txt
@@ -0,0 +1,47 @@
+
+ Qhull 2015.2 2016/01/18
+
+ http://www.qhull.org
+ git@github.com:qhull/qhull.git
+ http://www.geomview.org
+
+Qhull computes convex hulls, Delaunay triangulations, Voronoi diagrams,
+furthest-site Voronoi diagrams, and halfspace intersections about a point.
+It runs in 2-d, 3-d, 4-d, or higher. It implements the Quickhull algorithm
+for computing convex hulls. Qhull handles round-off errors from floating
+point arithmetic. It can approximate a convex hull.
+
+The program includes options for hull volume, facet area, partial hulls,
+input transformations, randomization, tracing, multiple output formats, and
+execution statistics. The program can be called from within your application.
+You can view the results in 2-d, 3-d and 4-d with Geomview.
+
+To download Qhull:
+ http://www.qhull.org/download
+ git@github.com:qhull/qhull.git
+
+Download qhull-96.ps for:
+
+ Barber, C. B., D.P. Dobkin, and H.T. Huhdanpaa, "The
+ Quickhull Algorithm for Convex Hulls," ACM Trans. on
+ Mathematical Software, 22(4):469-483, Dec. 1996.
+ http://www.acm.org/pubs/citations/journals/toms/1996-22-4/p469-barber/
+ http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.117.405
+
+Abstract:
+
+The convex hull of a set of points is the smallest convex set that contains
+the points. This article presents a practical convex hull algorithm that
+combines the two-dimensional Quickhull Algorithm with the general dimension
+Beneath-Beyond Algorithm. It is similar to the randomized, incremental
+algorithms for convex hull and Delaunay triangulation. We provide empirical
+evidence that the algorithm runs faster when the input contains non-extreme
+points, and that it uses less memory.
+
+Computational geometry algorithms have traditionally assumed that input sets
+are well behaved. When an algorithm is implemented with floating point
+arithmetic, this assumption can lead to serious errors. We briefly describe
+a solution to this problem when computing the convex hull in two, three, or
+four dimensions. The output is a set of "thick" facets that contain all
+possible exact convex hulls of the input. A variation is effective in five
+or more dimensions.
diff --git a/xs/src/qhull/CMakeLists.txt b/xs/src/qhull/CMakeLists.txt
new file mode 100644
index 000000000..d798b018f
--- /dev/null
+++ b/xs/src/qhull/CMakeLists.txt
@@ -0,0 +1,128 @@
+
+# This CMake file is written specifically to integrate qhull library with Slic3rPE
+# (see https://github.com/prusa3d/Slic3r for more information about the project)
+#
+# Only original libraries qhullstatic_r and qhullcpp are included.
+# They are built as a single statically linked library.
+#
+# Created by modification of the original qhull CMakeLists.
+# Lukas Matena (25.7.2018), lukasmatena@seznam.cz
+
+
+project(qhull)
+cmake_minimum_required(VERSION 2.6)
+
+# Define qhull_VERSION in CMakeLists.txt, Makefile, qhull-exports.def, qhull_p-exports.def, qhull_r-exports.def, qhull-warn.pri
+set(qhull_VERSION2 "2015.2 2016/01/18") # not used, See global.c, global_r.c, rbox.c, rbox_r.c
+set(qhull_VERSION "7.2.0") # Advance every release
+
+#include(CMakeModules/CheckLFS.cmake)
+#option(WITH_LFS "Enable Large File Support" ON)
+#check_lfs(WITH_LFS)
+
+
+message(STATUS "qhull Version: ${qhull_VERSION} (static linking)")
+
+
+set(libqhull_HEADERS
+ # reentrant qhull HEADERS:
+ src/libqhull_r/libqhull_r.h
+ src/libqhull_r/geom_r.h
+ src/libqhull_r/io_r.h
+ src/libqhull_r/mem_r.h
+ src/libqhull_r/merge_r.h
+ src/libqhull_r/poly_r.h
+ src/libqhull_r/qhull_ra.h
+ src/libqhull_r/qset_r.h
+ src/libqhull_r/random_r.h
+ src/libqhull_r/stat_r.h
+ src/libqhull_r/user_r.h
+
+ # C++ interface to reentrant Qhull HEADERS:
+ src/libqhullcpp/Coordinates.h
+ src/libqhullcpp/functionObjects.h
+ src/libqhullcpp/PointCoordinates.h
+ src/libqhullcpp/Qhull.h
+ src/libqhullcpp/QhullError.h
+ src/libqhullcpp/QhullFacet.h
+ src/libqhullcpp/QhullFacetList.h
+ src/libqhullcpp/QhullFacetSet.h
+ src/libqhullcpp/QhullHyperplane.h
+ src/libqhullcpp/QhullIterator.h
+ src/libqhullcpp/QhullLinkedList.h
+ src/libqhullcpp/QhullPoint.h
+ src/libqhullcpp/QhullPoints.h
+ src/libqhullcpp/QhullPointSet.h
+ src/libqhullcpp/QhullQh.h
+ src/libqhullcpp/QhullRidge.h
+ src/libqhullcpp/QhullSet.h
+ src/libqhullcpp/QhullSets.h
+ src/libqhullcpp/QhullStat.h
+ src/libqhullcpp/QhullVertex.h
+ src/libqhullcpp/QhullVertexSet.h
+ src/libqhullcpp/RboxPoints.h
+ src/libqhullcpp/RoadError.h
+ src/libqhullcpp/RoadLogEvent.h
+ src/qhulltest/RoadTest.h
+)
+
+set(libqhull_SOURCES
+ # reentrant qhull SOURCES:
+ src/libqhull_r/global_r.c
+ src/libqhull_r/stat_r.c
+ src/libqhull_r/geom2_r.c
+ src/libqhull_r/poly2_r.c
+ src/libqhull_r/merge_r.c
+ src/libqhull_r/libqhull_r.c
+ src/libqhull_r/geom_r.c
+ src/libqhull_r/poly_r.c
+ src/libqhull_r/qset_r.c
+ src/libqhull_r/mem_r.c
+ src/libqhull_r/random_r.c
+ src/libqhull_r/usermem_r.c
+ src/libqhull_r/userprintf_r.c
+ src/libqhull_r/io_r.c
+ src/libqhull_r/user_r.c
+ src/libqhull_r/rboxlib_r.c
+ src/libqhull_r/userprintf_rbox_r.c
+
+ # C++ interface to reentrant Qhull SOURCES:
+ src/libqhullcpp/Coordinates.cpp
+ src/libqhullcpp/PointCoordinates.cpp
+ src/libqhullcpp/Qhull.cpp
+ src/libqhullcpp/QhullFacet.cpp
+ src/libqhullcpp/QhullFacetList.cpp
+ src/libqhullcpp/QhullFacetSet.cpp
+ src/libqhullcpp/QhullHyperplane.cpp
+ src/libqhullcpp/QhullPoint.cpp
+ src/libqhullcpp/QhullPointSet.cpp
+ src/libqhullcpp/QhullPoints.cpp
+ src/libqhullcpp/QhullQh.cpp
+ src/libqhullcpp/QhullRidge.cpp
+ src/libqhullcpp/QhullSet.cpp
+ src/libqhullcpp/QhullStat.cpp
+ src/libqhullcpp/QhullVertex.cpp
+ src/libqhullcpp/QhullVertexSet.cpp
+ src/libqhullcpp/RboxPoints.cpp
+ src/libqhullcpp/RoadError.cpp
+ src/libqhullcpp/RoadLogEvent.cpp
+
+ # headers for both (libqhullr and libqhullcpp:
+ ${libqhull_HEADERS}
+)
+
+
+##################################################
+# combined library (reentrant qhull and qhullcpp) for Slic3r:
+set(qhull_STATIC qhull)
+add_library(${qhull_STATIC} STATIC ${libqhull_SOURCES})
+set_target_properties(${qhull_STATIC} PROPERTIES
+ VERSION ${qhull_VERSION})
+
+if(UNIX)
+ target_link_libraries(${qhull_STATIC} m)
+endif(UNIX)
+##################################################
+
+# LIBDIR is defined in the main xs CMake file:
+target_include_directories(${qhull_STATIC} PRIVATE ${LIBDIR}/qhull/src)
diff --git a/xs/src/qhull/COPYING.txt b/xs/src/qhull/COPYING.txt
new file mode 100644
index 000000000..2895ec6a3
--- /dev/null
+++ b/xs/src/qhull/COPYING.txt
@@ -0,0 +1,38 @@
+ Qhull, Copyright (c) 1993-2015
+
+ C.B. Barber
+ Arlington, MA
+
+ and
+
+ The National Science and Technology Research Center for
+ Computation and Visualization of Geometric Structures
+ (The Geometry Center)
+ University of Minnesota
+
+ email: qhull@qhull.org
+
+This software includes Qhull from C.B. Barber and The Geometry Center.
+Qhull is copyrighted as noted above. Qhull is free software and may
+be obtained via http from www.qhull.org. It may be freely copied, modified,
+and redistributed under the following conditions:
+
+1. All copyright notices must remain intact in all files.
+
+2. A copy of this text file must be distributed along with any copies
+ of Qhull that you redistribute; this includes copies that you have
+ modified, or copies of programs or other software products that
+ include Qhull.
+
+3. If you modify Qhull, you must include a notice giving the
+ name of the person performing the modification, the date of
+ modification, and the reason for such modification.
+
+4. When distributing modified versions of Qhull, or other software
+ products that include Qhull, you must provide notice that the original
+ source code may be obtained as noted above.
+
+5. There is no warranty or other guarantee of fitness for Qhull, it is
+ provided solely "as is". Bug reports or fixes may be sent to
+ qhull_bug@qhull.org; the authors may or may not act on them as
+ they desire.
diff --git a/xs/src/qhull/README.txt b/xs/src/qhull/README.txt
new file mode 100644
index 000000000..f4c7a3b22
--- /dev/null
+++ b/xs/src/qhull/README.txt
@@ -0,0 +1,623 @@
+This distribution of qhull library is only meant for interfacing qhull with Slic3rPE
+(https://github.com/prusa3d/Slic3r).
+
+The qhull source file was acquired from https://github.com/qhull/qhull at revision
+f0bd8ceeb84b554d7cdde9bbfae7d3351270478c.
+
+No changes to the qhull library were made, except for
+- setting REALfloat=1 in user_r.h to enforce calculations in floats
+- modifying CMakeLists.txt (the original was renamed to origCMakeLists.txt)
+
+Many thanks to C. Bradford Barber and all contributors.
+
+Lukas Matena (lukasmatena@seznam.cz)
+25.7.2018
+
+
+See original contents of the README file below.
+
+======================================================================================
+======================================================================================
+======================================================================================
+
+
+Name
+
+ qhull, rbox 2015.2 2016/01/18
+
+Convex hull, Delaunay triangulation, Voronoi diagrams, Halfspace intersection
+
+ Documentation:
+ html/index.htm
+ <http://www.qhull.org/html>
+
+ Available from:
+ <http://www.qhull.org>
+ <http://www.qhull.org/download>
+ <http://github.com/qhull/qhull> (git@github.com:qhull/qhull.git)
+
+ News and a paper:
+ <http://www.qhull.org/news>
+ <http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.117.405>
+
+ Version 1 (simplicial only):
+ <http://www.qhull.org/download/qhull-1.0.tar.gz>
+
+Purpose
+
+ Qhull is a general dimension convex hull program that reads a set
+ of points from stdin, and outputs the smallest convex set that contains
+ the points to stdout. It also generates Delaunay triangulations, Voronoi
+ diagrams, furthest-site Voronoi diagrams, and halfspace intersections
+ about a point.
+
+ Rbox is a useful tool in generating input for Qhull; it generates
+ hypercubes, diamonds, cones, circles, simplices, spirals,
+ lattices, and random points.
+
+ Qhull produces graphical output for Geomview. This helps with
+ understanding the output. <http://www.geomview.org>
+
+Environment requirements
+
+ Qhull and rbox should run on all 32-bit and 64-bit computers. Use
+ an ANSI C or C++ compiler to compile the program. The software is
+ self-contained. It comes with examples and test scripts.
+
+ Qhull's C++ interface uses the STL. The C++ test program uses QTestLib
+ from the Qt Framework. Qhull's C++ interface may change without
+ notice. Eventually, it will move into the qhull shared library.
+
+ Qhull is copyrighted software. Please read COPYING.txt and REGISTER.txt
+ before using or distributing Qhull.
+
+To cite Qhull, please use
+
+ Barber, C.B., Dobkin, D.P., and Huhdanpaa, H.T., "The Quickhull
+ algorithm for convex hulls," ACM Trans. on Mathematical Software,
+ 22(4):469-483, Dec 1996, http://www.qhull.org.
+
+To modify Qhull, particularly the C++ interface
+
+ Qhull is on GitHub
+ (http://github.com/qhull/qhull, git@github.com:qhull/qhull.git)
+
+ For internal documentation, see html/qh-code.htm
+
+To install Qhull
+
+ Qhull is precompiled for Windows 32-bit, otherwise it needs compilation.
+
+ Qhull includes Makefiles for gcc and other targets, CMakeLists.txt for CMake,
+ .sln/.vcproj/.vcxproj files for Microsoft Visual Studio, and .pro files
+ for Qt Creator. It compiles under Windows with mingw.
+
+ Install and build instructions follow.
+
+ See the end of this document for a list of distributed files.
+
+-----------------
+Installing Qhull on Windows 10, 8, 7 (32- or 64-bit), Windows XP, and Windows NT
+
+ The zip file contains rbox.exe, qhull.exe, qconvex.exe, qdelaunay.exe,
+ qhalf.exe, qvoronoi.exe, testqset.exe, user_eg*.exe, documentation files,
+ and source files. Qhull.exe and user-eg3.exe are compiled with the reentrant
+ library while the other executables use the non-reentrant library.
+
+ To install Qhull:
+ - Unzip the files into a directory (e.g., named 'qhull')
+ - Click on QHULL-GO or open a command window into Qhull's bin directory.
+ - Test with 'rbox D4 | qhull'
+
+ To uninstall Qhull
+ - Delete the qhull directory
+
+ To learn about Qhull:
+ - Execute 'qconvex' for a synopsis and examples.
+ - Execute 'rbox 10 | qconvex' to compute the convex hull of 10 random points.
+ - Execute 'rbox 10 | qconvex i TO file' to write results to 'file'.
+ - Browse the documentation: qhull\html\index.htm
+ - If an error occurs, Windows sends the error to stdout instead of stderr.
+ Use 'TO xxx' to send normal output to xxx
+
+ To improve the command window
+ - Double-click the window bar to increase the size of the window
+ - Right-click the window bar
+ - Select Properties
+ - Check QuickEdit Mode
+ Select text with right-click or Enter
+ Paste text with right-click
+ - Change Font to Lucinda Console
+ - Change Layout to Screen Buffer Height 999, Window Size Height 55
+ - Change Colors to Screen Background White, Screen Text Black
+ - Click OK
+ - Select 'Modify shortcut that started this window', then OK
+
+ If you use qhull a lot, install a bash shell such as
+ MSYS (www.mingw.org/wiki/msys), Road Bash (www.qhull.org/bash),
+ or Cygwin (www.cygwin.com).
+
+-----------------
+Installing Qhull on Unix with gcc
+
+ To build Qhull, static libraries, shared library, and C++ interface
+ - Download and extract Qhull (either GitHub, .tgz file, or .zip file)
+ - make
+ - export LD_LIBRARY_PATH=$PWD/lib:$LD_LIBRARY_PATH
+
+ The Makefiles may be edited for other compilers.
+ If 'testqset' exits with an error, qhull is broken
+
+ A simple Makefile for Qhull is in src/libqhull and src/libqhull_r.
+ To build the Qhull executables and libqhullstatic
+ - Extract Qhull from qhull...tgz or qhull...zip
+ - cd src/libqhull_r # cd src/libqhull
+ - make
+
+
+-----------------
+Installing Qhull with CMake 2.6 or later
+
+ See CMakeLists.txt for examples and further build instructions
+
+ To build Qhull, static libraries, shared library, and C++ interface
+ - Download and extract Qhull (either GitHub, .tgz file, or .zip file)
+ - cd build
+ - cmake --help # List build generators
+ - make -G "<generator>" .. && cmake ..
+ - cmake ..
+ - make
+ - make install
+
+ The ".." is important. It refers to the parent directory (i.e., qhull/)
+
+ On Windows, CMake installs to C:/Program Files/qhull. 64-bit generators
+ have a "Win64" tag.
+
+ If creating a qhull package, please include a pkg-config file based on build/qhull*.pc.in
+
+ If cmake fails with "No CMAKE_C_COMPILER could be found"
+ - cmake was not able to find the build environment specified by -G "..."
+
+-----------------
+Installing Qhull with Qt
+
+ To build Qhull, including its C++ test (qhulltest)
+ - Download and extract Qhull (either GitHub, .tgz file, or .zip file)
+ - Load src/qhull-all.pro into QtCreator
+ - Build
+
+-------------------
+Working with Qhull's C++ interface
+
+ See html/qh-code.htm#cpp for calling Qhull from C++ programs
+
+ See html/qh-code.htm#reentrant for converting from Qhull-2012
+
+ Examples of using the C++ interface
+ user_eg3_r.cpp
+ qhulltest/*_test.cpp
+
+ Qhull's C++ interface is likely to change. Stay current with GitHub.
+
+ To clone Qhull's next branch from http://github.com/qhull/qhull
+ git init
+ git clone git@github.com:qhull/qhull.git
+ cd qhull
+ git checkout next
+ ...
+ git pull origin next
+
+ Compile qhullcpp and libqhullstatic_r with the same compiler. Both libraries
+ use the C routines setjmp() and longjmp() for error handling. They must
+ be compiled with the same compiler.
+
+-------------------
+Calling Qhull from C programs
+
+ See html/qh-code.htm#library for calling Qhull from C programs
+
+ See html/qh-code.htm#reentrant for converting from Qhull-2012
+
+ Warning: You will need to understand Qhull's data structures and read the
+ code. Most users will find it easier to call Qhull as an external command.
+
+ The new, reentrant 'C' code (src/libqhull_r), passes a pointer to qhT
+ to most Qhull routines. This allows multiple instances of Qhull to run
+ at the same time. It simplifies the C++ interface.
+
+ The non-reentrant 'C' code (src/libqhull) looks unusual. It refers to
+ Qhull's global data structure, qhT, through a 'qh' macro (e.g., 'qh ferr').
+ This allows the same code to use static memory or heap memory.
+ If qh_QHpointer is defined, qh_qh is a pointer to an allocated qhT;
+ otherwise qh_qh is a global static data structure of type qhT.
+
+------------------
+Compiling Qhull with Microsoft Visual C++
+
+ To compile 32-bit Qhull with Microsoft Visual C++ 2010 and later
+ - Download and extract Qhull (either GitHub, .tgz file, or .zip file)
+ - Load solution build/qhull-32.sln
+ - Build target 'Win32'
+ - Project qhulltest requires Qt for DevStudio (http://www.qt.io)
+ Set the QTDIR environment variable to your Qt directory (e.g., c:/qt/5.2.0/5.2.0/msvc2012)
+ If QTDIR is incorrect, precompile will fail with 'Can not locate the file specified'
+
+ To compile 64-bit Qhull with Microsoft Visual C++ 2010 and later
+ - 64-bit Qhull has larger data structures due to 64-bit pointers
+ - Download and extract Qhull (either GitHub, .tgz file, or .zip file)
+ - Load solution build/qhull-64.sln
+ - Build target 'Win32'
+ - Project qhulltest requires Qt for DevStudio (http://www.qt.io)
+ Set the QTDIR environment variable to your Qt directory (e.g., c:/qt/5.2.0/5.2.0/msvc2012_64)
+ If QTDIR is incorrect, precompile will fail with 'Can not locate the file specified'
+
+ To compile Qhull with Microsoft Visual C++ 2005 (vcproj files)
+ - Download and extract Qhull (either GitHub, .tgz file, or .zip file)
+ - Load solution build/qhull.sln
+ - Build target 'win32' (not 'x64')
+ - Project qhulltest requires Qt for DevStudio (http://www.qt.io)
+ Set the QTDIR environment variable to your Qt directory (e.g., c:/qt/4.7.4)
+ If QTDIR is incorrect, precompile will fail with 'Can not locate the file specified'
+
+-----------------
+Compiling Qhull with Qt Creator
+
+ Qt (http://www.qt.io) is a C++ framework for Windows, Linux, and Macintosh
+
+ Qhull uses QTestLib to test qhull's C++ interface (see src/qhulltest/)
+
+ To compile Qhull with Qt Creator
+ - Download and extract Qhull (either GitHub, .tgz file, or .zip file)
+ - Download the Qt SDK
+ - Start Qt Creator
+ - Load src/qhull-all.pro
+ - Build
+
+-----------------
+Compiling Qhull with mingw on Windows
+
+ To compile Qhull with MINGW
+ - Download and extract Qhull (either GitHub, .tgz file, or .zip file)
+ - Install Road Bash (http://www.qhull.org/bash)
+ or install MSYS (http://www.mingw.org/wiki/msys)
+ - Install MINGW-w64 (http://sourceforge.net/projects/mingw-w64).
+ Mingw is included with Qt SDK.
+ - make
+
+-----------------
+Compiling Qhull with cygwin on Windows
+
+ To compile Qhull with cygwin
+ - Download and extract Qhull (either GitHub, .tgz file, or .zip file)
+ - Install cygwin (http://www.cygwin.com)
+ - Include packages for gcc, make, ar, and ln
+ - make
+
+-----------------
+Compiling from Makfile without gcc
+
+ The file, qhull-src.tgz, contains documentation and source files for
+ qhull and rbox.
+
+ To unpack the tgz file
+ - tar zxf qhull-src.tgz
+ - cd qhull
+ - Use qhull/Makefile
+ Simpler Makefiles are qhull/src/libqhull/Makefile and qhull/src/libqhull_r/Makefile
+
+ Compiling qhull and rbox with Makefile
+ - in Makefile, check the CC, CCOPTS1, PRINTMAN, and PRINTC defines
+ - the defaults are gcc and enscript
+ - CCOPTS1 should include the ANSI flag. It defines __STDC__
+ - in user.h, check the definitions of qh_SECticks and qh_CPUclock.
+ - use '#define qh_CLOCKtype 2' for timing runs longer than 1 hour
+ - type: make
+ - this builds: qhull qconvex qdelaunay qhalf qvoronoi rbox libqhull.a libqhull_r.a
+ - type: make doc
+ - this prints the man page
+ - See also qhull/html/index.htm
+ - if your compiler reports many errors, it is probably not a ANSI C compiler
+ - you will need to set the -ansi switch or find another compiler
+ - if your compiler warns about missing prototypes for fprintf() etc.
+ - this is ok, your compiler should have these in stdio.h
+ - if your compiler warns about missing prototypes for memset() etc.
+ - include memory.h in qhull_a.h
+ - if your compiler reports "global.c: storage size of 'qh_qh' isn't known"
+ - delete the initializer "={0}" in global.c, stat.c and mem.c
+ - if your compiler warns about "stat.c: improper initializer"
+ - this is ok, the initializer is not used
+ - if you have trouble building libqhull.a with 'ar'
+ - try 'make -f Makefile.txt qhullx'
+ - if the code compiles, the qhull test case will automatically execute
+ - if an error occurs, there's an incompatibility between machines
+ - If you can, try a different compiler
+ - You can turn off the Qhull memory manager with qh_NOmem in mem.h
+ - You can turn off compiler optimization (-O2 in Makefile)
+ - If you find the source of the problem, please let us know
+ - to install the programs and their man pages:
+ - define MANDIR and BINDIR
+ - type 'make install'
+
+ - if you have Geomview (www.geomview.org)
+ - try 'rbox 100 | qconvex G >a' and load 'a' into Geomview
+ - run 'q_eg' for Geomview examples of Qhull output (see qh-eg.htm)
+
+------------------
+Compiling on other machines and compilers
+
+ Qhull may compile with Borland C++ 5.0 bcc32. A Makefile is included.
+ Execute 'cd src/libqhull; make -f Mborland'. If you use the Borland IDE, set
+ the ANSI option in Options:Project:Compiler:Source:Language-compliance.
+
+ Qhull may compile with Borland C++ 4.02 for Win32 and DOS Power Pack.
+ Use 'cd src/libqhull; make -f Mborland -D_DPMI'. Qhull 1.0 compiles with
+ Borland C++ 4.02. For rbox 1.0, use "bcc32 -WX -w- -O2-e -erbox -lc rbox.c".
+ Use the same options for Qhull 1.0. [D. Zwick]
+
+ If you have troubles with the memory manager, you can turn it off by
+ defining qh_NOmem in mem.h.
+
+-----------------
+Distributed files
+
+ README.txt // Instructions for installing Qhull
+ REGISTER.txt // Qhull registration
+ COPYING.txt // Copyright notice
+ QHULL-GO.lnk // Windows icon for eg/qhull-go.bat
+ Announce.txt // Announcement
+ CMakeLists.txt // CMake build file (2.6 or later)
+ CMakeModules/CheckLFS.cmake // enables Large File Support in cmake
+ File_id.diz // Package descriptor
+ index.htm // Home page
+ Makefile // Makefile for gcc and other compilers
+ qhull*.md5sum // md5sum for all files
+
+ bin/* // Qhull executables and dll (.zip only)
+ build/qhull*.pc.in // pkg-config templates for qhull_r, qhull, and qhull_p
+ build/qhull-32.sln // 32-bit DevStudio solution and project files (2010 and later)
+ build/*-32.vcxproj
+ build/qhull-64.sln // 64-bit DevStudio solution and project files (2010 and later)
+ build/*-64.vcxproj
+ build/qhull.sln // DevStudio solution and project files (2005 and 2009)
+ build/*.vcproj
+ eg/* // Test scripts and geomview files from q_eg
+ html/index.htm // Manual
+ html/qh-faq.htm // Frequently asked questions
+ html/qh-get.htm // Download page
+ html/qhull-cpp.xml // C++ style notes as a Road FAQ (www.qhull.org/road)
+ src/Changes.txt // Change history for Qhull and rbox
+ src/qhull-all.pro // Qt project
+
+eg/
+ q_eg // shell script for Geomview examples (eg.01.cube)
+ q_egtest // shell script for Geomview test examples
+ q_test // shell script to test qhull
+ q_test-ok.txt // output from q_test
+ qhulltest-ok.txt // output from qhulltest (Qt only)
+ make-vcproj.sh // bash shell script to create vcproj and vcxprog files
+ qhull-zip.sh // bash shell script for distribution files
+
+rbox consists of (bin, html):
+ rbox.exe // Win32 executable (.zip only)
+ rbox.htm // html manual
+ rbox.man // Unix man page
+ rbox.txt
+
+qhull consists of (bin, html):
+ qconvex.exe // Win32 executables and dlls (.zip download only)
+ qhull.exe // Built with the reentrant library (about 2% slower)
+ qdelaunay.exe
+ qhalf.exe
+ qvoronoi.exe
+ qhull_r.dll
+ qhull-go.bat // command window
+ qconvex.htm // html manual
+ qdelaun.htm
+ qdelau_f.htm
+ qhalf.htm
+ qvoronoi.htm
+ qvoron_f.htm
+ qh-eg.htm
+ qh-code.htm
+ qh-impre.htm
+ index.htm
+ qh-opt*.htm
+ qh-quick.htm
+ qh--*.gif // images for manual
+ normal_voronoi_knauss_oesterle.jpg
+ qhull.man // Unix man page
+ qhull.txt
+
+bin/
+ msvcr80.dll // Visual C++ redistributable file (.zip download only)
+
+src/
+ qhull/unix.c // Qhull and rbox applications using non-reentrant libqhullstatic.a
+ rbox/rbox.c
+ qconvex/qconvex.c
+ qhalf/qhalf.c
+ qdelaunay/qdelaunay.c
+ qvoronoi/qvoronoi.c
+
+ qhull/unix_r.c // Qhull and rbox applications using reentrant libqhullstatic_r.a
+ rbox/rbox_r.c
+ qconvex/qconvex_r.c // Qhull applications built with reentrant libqhull_r/Makefile
+ qhalf/qhalf_r.c
+ qdelaunay/qdelaun_r.c
+ qvoronoi/qvoronoi_r.c
+
+ user_eg/user_eg_r.c // example of using qhull_r.dll from a user program
+ user_eg2/user_eg2_r.c // example of using libqhullstatic_r.a from a user program
+ user_eg3/user_eg3_r.cpp // example of Qhull's C++ interface libqhullcpp with libqhullstatic_r.a
+ qhulltest/qhulltest.cpp // Test of Qhull's C++ interface using Qt's QTestLib
+ qhull-*.pri // Include files for Qt projects
+ testqset_r/testqset_r.c // Test of reentrant qset_r.c and mem_r.c
+ testqset/testqset.c // Test of non-rentrant qset.c and mem.c
+
+
+src/libqhull
+ libqhull.pro // Qt project for non-rentrant, shared library (qhull.dll)
+ index.htm // design documentation for libqhull
+ qh-*.htm
+ qhull-exports.def // Export Definition file for Visual C++
+ Makefile // Simple gcc Makefile for qhull and libqhullstatic.a
+ Mborland // Makefile for Borland C++ 5.0
+
+ libqhull.h // header file for qhull
+ user.h // header file of user definable constants
+ libqhull.c // Quickhull algorithm with partitioning
+ user.c // user re-definable functions
+ usermem.c
+ userprintf.c
+ userprintf_rbox.c
+
+ qhull_a.h // include files for libqhull/*.c
+ geom.c // geometric routines
+ geom2.c
+ geom.h
+ global.c // global variables
+ io.c // input-output routines
+ io.h
+ mem.c // memory routines, this is stand-alone code
+ mem.h
+ merge.c // merging of non-convex facets
+ merge.h
+ poly.c // polyhedron routines
+ poly2.c
+ poly.h
+ qset.c // set routines, this only depends on mem.c
+ qset.h
+ random.c // utilities w/ Park & Miller's random number generator
+ random.h
+ rboxlib.c // point set generator for rbox
+ stat.c // statistics
+ stat.h
+
+src/libqhull_r
+ libqhull_r.pro // Qt project for rentrant, shared library (qhull_r.dll)
+ index.htm // design documentation for libqhull_r
+ qh-*_r.htm
+ qhull-exports_r.def // Export Definition file for Visual C++
+ Makefile // Simple gcc Makefile for qhull and libqhullstatic.a
+
+ libqhull_r.h // header file for qhull
+ user_r.h // header file of user definable constants
+ libqhull_r.c // Quickhull algorithm wi_r.hpartitioning
+ user_r.c // user re-definable functions
+ usermem.c
+ userprintf.c
+ userprintf_rbox.c
+ qhull_ra.h // include files for libqhull/*_r.c
+ geom_r.c // geometric routines
+ geom2.c
+ geom_r.h
+ global_r.c // global variables
+ io_r.c // input-output routines
+ io_r.h
+ mem_r.c // memory routines, this is stand-alone code
+ mem.h
+ merge_r.c // merging of non-convex facets
+ merge.h
+ poly_r.c // polyhedron routines
+ poly2.c
+ poly_r.h
+ qset_r.c // set routines, this only depends on mem_r.c
+ qset.h
+ random_r.c // utilities w/ Park & Miller's random number generator
+ random.h
+ rboxlib_r.c // point set generator for rbox
+ stat_r.c // statistics
+ stat.h
+
+src/libqhullcpp/
+ libqhullcpp.pro // Qt project for renentrant, static C++ library
+ Qhull.cpp // Calls libqhull_r.c from C++
+ Qhull.h
+ qt-qhull.cpp // Supporting methods for Qt
+
+ Coordinates.cpp // input classes
+ Coordinates.h
+
+ PointCoordinates.cpp
+ PointCoordinates.h
+ RboxPoints.cpp // call rboxlib.c from C++
+ RboxPoints.h
+
+ QhullFacet.cpp // data structure classes
+ QhullFacet.h
+ QhullHyperplane.cpp
+ QhullHyperplane.h
+ QhullPoint.cpp
+ QhullPoint.h
+ QhullQh.cpp
+ QhullRidge.cpp
+ QhullRidge.h
+ QhullVertex.cpp
+ QhullVertex.h
+
+ QhullFacetList.cpp // collection classes
+ QhullFacetList.h
+ QhullFacetSet.cpp
+ QhullFacetSet.h
+ QhullIterator.h
+ QhullLinkedList.h
+ QhullPoints.cpp
+ QhullPoints.h
+ QhullPointSet.cpp
+ QhullPointSet.h
+ QhullSet.cpp
+ QhullSet.h
+ QhullSets.h
+ QhullVertexSet.cpp
+ QhullVertexSet.h
+
+ functionObjects.h // supporting classes
+ QhullError.cpp
+ QhullError.h
+ QhullQh.cpp
+ QhullQh.h
+ QhullStat.cpp
+ QhullStat.h
+ RoadError.cpp // Supporting base classes
+ RoadError.h
+ RoadLogEvent.cpp
+ RoadLogEvent.h
+ usermem_r-cpp.cpp // Optional override for qh_exit() to throw an error
+
+src/libqhullstatic/
+ libqhullstatic.pro // Qt project for non-reentrant, static library
+
+src/libqhullstatic_r/
+ libqhullstatic_r.pro // Qt project for reentrant, static library
+
+src/qhulltest/
+ qhulltest.pro // Qt project for test of C++ interface
+ Coordinates_test.cpp // Test of each class
+ PointCoordinates_test.cpp
+ Qhull_test.cpp
+ QhullFacet_test.cpp
+ QhullFacetList_test.cpp
+ QhullFacetSet_test.cpp
+ QhullHyperplane_test.cpp
+ QhullLinkedList_test.cpp
+ QhullPoint_test.cpp
+ QhullPoints_test.cpp
+ QhullPointSet_test.cpp
+ QhullRidge_test.cpp
+ QhullSet_test.cpp
+ QhullVertex_test.cpp
+ QhullVertexSet_test.cpp
+ RboxPoints_test.cpp
+ RoadTest.cpp // Run multiple test files with QTestLib
+ RoadTest.h
+
+-----------------
+Authors:
+
+ C. Bradford Barber Hannu Huhdanpaa (Version 1.0)
+ bradb@shore.net hannu@qhull.org
+
+ Qhull 1.0 and 2.0 were developed under NSF grants NSF/DMS-8920161
+ and NSF-CCR-91-15793 750-7504 at the Geometry Center and Harvard
+ University. If you find Qhull useful, please let us know.
diff --git a/xs/src/qhull/REGISTER.txt b/xs/src/qhull/REGISTER.txt
new file mode 100644
index 000000000..16ccb1a58
--- /dev/null
+++ b/xs/src/qhull/REGISTER.txt
@@ -0,0 +1,32 @@
+Dear Qhull User
+
+We would like to find out how you are using our software. Think of
+Qhull as a new kind of shareware: you share your science and successes
+with us, and we share our software and support with you.
+
+If you use Qhull, please send us a note telling
+us what you are doing with it.
+
+We need to know:
+
+ (1) What you are working on - an abstract of your work would be
+ fine.
+
+ (2) How Qhull has helped you, for example, by increasing your
+ productivity or allowing you to do things you could not do
+ before. If Qhull had a direct bearing on your work, please
+ tell us about this.
+
+We encourage you to cite Qhull in your publications.
+
+To cite Qhull, please use
+
+ Barber, C.B., Dobkin, D.P., and Huhdanpaa, H.T., "The Quickhull
+ algorithm for convex hulls," ACM Trans. on Mathematical Software,
+ 22(4):469-483, Dec 1996, http://www.qhull.org.
+
+Please send e-mail to
+
+ bradb@shore.net
+
+Thank you!
diff --git a/xs/src/qhull/html/index.htm b/xs/src/qhull/html/index.htm
new file mode 100644
index 000000000..ca4789b47
--- /dev/null
+++ b/xs/src/qhull/html/index.htm
@@ -0,0 +1,935 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+
+<head>
+<meta http-equiv="Content-Type"
+content="text/html; charset=iso-8859-1">
+<meta name="GENERATOR" content="Microsoft FrontPage 2.0">
+<title>Qhull manual</title>
+<!-- Navigation links
+NOTE -- verify all links by 'grep href=' 'grep name=' add # 'sort /+7'
+ index.htm
+-->
+</head>
+
+<body>
+
+<p><a name="TOP"><b>Up:</b></a> <a
+href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b><a
+href="http://www.qhull.org/news">News</a> about Qhull<br>
+<b>Up:</b> <a href="http://www.qhull.org/html/qh-faq.htm">FAQ</a> about Qhull<br>
+<b>To:</b> <a href="#TOC">Qhull manual: Table of Contents</a>
+(please wait while loading) <br>
+<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
+&#149; <a href="qh-quick.htm#options">Options</a>
+&#149; <a href="qh-opto.htm#output">Output</a>
+&#149; <a href="qh-optf.htm#format">Formats</a>
+&#149; <a href="qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="qh-optp.htm#print">Print</a>
+&#149; <a href="qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="qh-optc.htm#prec">Precision</a>
+&#149; <a href="qh-optt.htm#trace">Trace</a>
+&#149; <a href="../src/libqhull_r/index.htm">Functions</a><br>
+
+<hr>
+<!-- Main text of document -->
+<h1><a
+href="http://www.geom.uiuc.edu/graphics/pix/Special_Topics/Computational_Geometry/fixed.html"><img
+src="qh--rand.gif" alt="[random-fixed]" align="middle"
+width="100" height="100"></a> Qhull manual </h1>
+
+<p>Qhull is a general dimension code for computing convex hulls,
+Delaunay triangulations, halfspace intersections about a point, Voronoi
+diagrams, furthest-site Delaunay triangulations, and
+furthest-site Voronoi diagrams. These structures have
+applications in science, engineering, statistics, and
+mathematics. See <a
+href="http://www.cs.mcgill.ca/~fukuda/soft/polyfaq/polyfaq.html">Fukuda's
+introduction</a> to convex hulls, Delaunay triangulations,
+Voronoi diagrams, and linear programming. For a detailed
+introduction, see O'Rourke [<a href="#orou94">'94</a>], <i>Computational
+Geometry in C</i>.
+</p>
+
+<p>There are six programs. Except for rbox, they use
+the same code. Each program includes instructions and examples.
+<blockquote>
+<ul>
+<li><a href="qconvex.htm">qconvex</a> -- convex hulls
+<li><a href="qdelaun.htm">qdelaunay</a> -- Delaunay triangulations and
+ furthest-site Delaunay triangulations
+<li><a href="qhalf.htm">qhalf</a> -- halfspace intersections about a point
+<li><a href="qhull.htm">qhull</a> -- all structures with additional options
+<li><a href="qvoronoi.htm">qvoronoi</a> -- Voronoi diagrams and
+ furthest-site Voronoi diagrams
+<li><a href="rbox.htm">rbox</a> -- generate point distributions for qhull
+</ul>
+</blockquote>
+
+<p>Qhull implements the Quickhull algorithm for computing the
+convex hull. Qhull includes options
+for hull volume, facet area, multiple output formats, and
+graphical output. It can approximate a convex hull. </p>
+
+<p>Qhull handles roundoff errors from floating point
+arithmetic. It generates a convex hull with "thick" facets.
+A facet's outer plane is clearly above all of the points;
+its inner plane is clearly below the facet's vertices. Any
+exact convex hull must lie between the inner and outer plane.
+
+<p>Qhull uses merged facets, triangulated output, or joggled
+input. Triangulated output triangulates non-simplicial, merged
+facets. Joggled input also
+guarantees simplicial output, but it
+is less accurate than merged facets. For merged facets, Qhull
+reports the maximum outer and inner plane.
+
+<p><i>Brad Barber, Arlington, MA</i></p>
+
+<p><b>Copyright &copy; 1995-2015 C.B. Barber</b></p>
+
+<hr>
+
+<h2><a href="#TOP">&#187;</a><a name="TOC">Qhull manual: Table of
+Contents </a></h2>
+
+<ul>
+ <li><a href="#when">When</a> to use Qhull
+ <ul>
+ <li><a href="http://www.qhull.org/news">News</a> for Qhull
+ with new features and reported bugs.
+ <li><a href="http://www.qhull.org">Home</a> for Qhull with additional URLs
+ (<a href=index.htm>local copy</a>)
+ <li><a href="http://www.qhull.org/html/qh-faq.htm">FAQ</a> for Qhull (<a href="qh-faq.htm">local copy</a>)
+ <li><a href="http://www.qhull.org/download">Download</a> Qhull (<a href=qh-get.htm>local copy</a>)
+ <li><a href="qh-quick.htm#programs">Quick</a> reference for Qhull and its <a href="qh-quick.htm#options">options</a>
+ <p>
+ <li><a href="../COPYING.txt">COPYING.txt</a> - copyright notice<br>
+ <li><a href="../REGISTER.txt">REGISTER.txt</a> - registration<br>
+ <li><a href="../README.txt">README.txt</a> - installation
+ instructions<br>
+ <li><a href="../src/Changes.txt">Changes.txt</a> - change history <br>
+ <li><a href="qhull.txt">qhull.txt</a> - Unix manual page
+ </ul>
+ <p>
+ <li><a href="#description">Description</a> of Qhull
+ <ul>
+ <li><a href="#definition">de</a>finition &#149; <a
+ href="#input">in</a>put &#149; <a href="#output">ou</a>tput
+ &#149; <a href="#algorithm">al</a>gorithm &#149; <a
+ href="#structure">da</a>ta structure </li>
+ <li><a href="qh-impre.htm">Imprecision</a> in Qhull</li>
+ <li><a href="qh-impre.htm#joggle">Merged facets</a> or joggled input
+ <li><a href="qh-eg.htm">Examples</a> of Qhull</li>
+ </ul>
+ <p>
+ <li><a href=qh-quick.htm#programs>Qhull programs</a>, with instructions and examples
+ <ul>
+ <li><a href="qconvex.htm">qconvex</a> -- convex hulls
+ <li><a href="qdelaun.htm">qdelaunay</a> -- Delaunay triangulations and
+ furthest-site Delaunay triangulations
+ <li><a href="qhalf.htm">qhalf</a> -- halfspace intersections about a point
+ <li><a href="qhull.htm">qhull</a> -- all structures with additional options
+ <li><a href="qvoronoi.htm">qvoronoi</a> -- Voronoi diagrams and
+ furthest-site Voronoi diagrams
+ <li><a href="rbox.htm">rbox</a> -- generate point distributions for qhull
+ </ul>
+ <p>
+ <li><a href="qh-quick.htm#options">Qhull options</a><ul>
+ <li><a href="qh-opto.htm#output">Output</a> formats</li>
+ <li><a href="qh-optf.htm#format">Additional</a> I/O
+ formats</li>
+ <li><a href="qh-optg.htm#geomview">Geomview</a>
+ output options</li>
+ <li><a href="qh-optp.htm#print">Print</a> options</li>
+ <li><a href="qh-optq.htm#qhull">Qhull</a> control
+ options</li>
+ <li><a href="qh-optc.htm#prec">Precision</a> options</li>
+ <li><a href="qh-optt.htm#trace">Trace</a> options</li>
+ </ul>
+ </li>
+ <p>
+ <li><a href="#geomview">Geomview</a>, Qhull's graphical viewer</li>
+ <ul>
+ <li><a href="#geomview-install">Installing Geomview</a></li>
+ <li><a href="#geomview-use">Using Geomview</a></li>
+ <li><a href="#geomview-win">Building Geomview for Windows</a></li>
+ </ul>
+ <p>
+ <li><a href="qh-code.htm">Qhull internals</a><ul>
+ <li><a href="qh-code.htm#reentrant">Reentrant</a> Qhull</li>
+ <li><a href="qh-code.htm#convert">How to convert</a> code to reentrant Qhull</li>
+ <li><a href="qh-code.htm#64bit">Qhull</a> on 64-bit computers</li>
+ <li><a href="qh-code.htm#cpp">Calling</a> Qhull
+ from C++ programs</li>
+ <li><a href="qh-code.htm#library">Calling</a> Qhull
+ from C programs</li>
+ <li><a href="qh-code.htm#performance">Performance</a>
+ of Qhull</li>
+ <li><a href="qh-code.htm#enhance">Enhancements</a> to
+ Qhull</li>
+ <li><a href="../src/libqhull_r/index.htm">Reentrant</a> Qhull functions, macros, and
+ data structures </li>
+ <li><a href="../src/libqhull/index.htm">Qhull</a> functions, macros, and
+ data structures </li>
+ </ul>
+ </li>
+ <p>
+ <li>Related URLs
+ <ul>
+
+ <li><a href="news:comp.graphics.algorithms">Newsgroup</a>:
+ comp.graphics.algorithms
+ <li><a
+ href="http://www.faqs.org/faqs/graphics/algorithms-faq/">FAQ</a> for computer graphics algorithms and
+ Exaflop's <a href="http://exaflop.org/docs/cgafaq/cga6.html">geometric</a> structures.
+ <li>Amenta's <a href="http://www.geom.uiuc.edu/software/cglist">Directory
+ of Computational Geometry Software </a></li>
+ <li>Erickson's <a
+ href="http://compgeom.cs.uiuc.edu/~jeffe/compgeom/code.html">Computational
+ Geometry Software</a> </li>
+ <li>Fukuda's <a
+ href="http://www.cs.mcgill.ca/~fukuda/soft/polyfaq/polyfaq.html">
+ introduction</a> to convex hulls, Delaunay triangulations,
+ Voronoi diagrams, and linear programming.
+ <li>Stony Brook's <a
+ href="http://www.cs.sunysb.edu/~algorith/major_section/1.6.shtml">Algorithm Repository</a> on computational geometry.
+ </li>
+ </ul>
+ <p>
+ <li><a href="#bugs">What to do</a> if something goes wrong</li>
+ <li><a href="#email">Email</a></li>
+ <li><a href="#authors">Authors</a></li>
+ <li><a href="#ref">References</a></li>
+ <li><a href="#acknowledge">Acknowledgments</a></li>
+</ul>
+<h2><a href="#TOC">&#187;</a><a name="when">When to use Qhull</a></h2>
+<blockquote>
+
+<p>Qhull constructs convex hulls, Delaunay triangulations,
+halfspace intersections about a point, Voronoi diagrams, furthest-site Delaunay
+triangulations, and furthest-site Voronoi diagrams.</p>
+
+<p>For convex hulls and halfspace intersections, Qhull may be used
+for 2-d upto 8-d. For Voronoi diagrams and Delaunay triangulations, Qhull may be
+used for 2-d upto 7-d. In higher dimensions, the size of the output
+grows rapidly and Qhull does not work well with virtual memory.
+If <i>n</i> is the size of
+the input and <i>d</i> is the dimension (d>=3), the size of the output
+and execution time
+grows by <i>n^(floor(d/2)</i>
+[see <a href=qh-code.htm#performance>Performance</a>]. For example, do
+not try to build a 16-d convex hull of 1000 points. It will
+have on the order of 1,000,000,000,000,000,000,000,000 facets.
+
+<p>On a 600 MHz Pentium 3, Qhull computes the 2-d convex hull of
+300,000 cocircular points in 11 seconds. It computes the
+2-d Delaunay triangulation and 3-d convex hull of 120,000 points
+in 12 seconds. It computes the
+3-d Delaunay triangulation and 4-d convex hull of 40,000 points
+in 18 seconds. It computes the
+4-d Delaunay triangulation and 5-d convex hull of 6,000 points
+in 12 seconds. It computes the
+5-d Delaunay triangulation and 6-d convex hull of 1,000 points
+in 12 seconds. It computes the
+6-d Delaunay triangulation and 7-d convex hull of 300 points
+in 15 seconds. It computes the
+7-d Delaunay triangulation and 8-d convex hull of 120 points
+in 15 seconds. It computes the
+8-d Delaunay triangulation and 9-d convex hull of 70 points
+in 15 seconds. It computes the
+9-d Delaunay triangulation and 10-d convex hull of 50 points
+in 17 seconds. The 10-d convex hull of 50 points has about 90,000 facets.
+
+<!-- duplicated in index.htm and html/index.htm -->
+<p>Qhull does <i>not</i> support constrained Delaunay
+triangulations, triangulation of non-convex surfaces, mesh
+generation of non-convex objects, or medium-sized inputs in 9-D
+and higher. </p>
+
+<p>This is a big package with many options. It is one of the
+fastest available. It is the only 3-d code that handles precision
+problems due to floating point arithmetic. For example, it
+implements the identity function for extreme points (see <a
+href="qh-impre.htm">Imprecision in Qhull</a>). </p>
+
+<p>[2016] A newly discovered, bad case for Qhull is multiple, nearly incident points within a 10^-13 ball of 3-d and higher
+Delaunay triangulations (input sites in the unit cube). Nearly incident points within substantially
+smaller or larger balls are OK. Error QH6271 is reported if a problem occurs. A future release of Qhull
+will handle this case. For more information, see "Nearly coincident points on an edge" in <a href="../html/qh-impre.htm#limit">Limitations of merged facets</a>
+
+<p>If you need a short code for convex hull, Delaunay
+triangulation, or Voronoi volumes consider Clarkson's <a
+href="http://www.netlib.org/voronoi/hull.html">hull
+program</a>. If you need 2-d Delaunay triangulations consider
+Shewchuk's <a href="http://www.cs.cmu.edu/~quake/triangle.html">triangle
+program</a>. It is much faster than Qhull and it allows
+constraints. Both programs use exact arithmetic. They are in <a
+href="http://www.netlib.org/voronoi/">http://www.netlib.org/voronoi/</a>.
+
+<p>If your input is in general position (i.e., no coplanar or colinear points),
+<li><a href="https://github.com/tomilov/quickhull/blob/master/include/quickhull.hpp">Tomilov's quickhull.hpp</a> (<a href"http://habrahabr.ru/post/245221/"documentation-ru</a/>)
+or Qhull <a
+href="http://www.qhull.org/download">version
+1.0</a> may meet your needs. Both programs detect precision problems,
+but do not handle them.</p>
+
+<p><a href=http://www.cgal.org>CGAL</a> is a library of efficient and reliable
+geometric algorithms. It uses C++ templates and the Boost library to produce dimension-specific
+code. This allows more efficient use of memory than Qhull's general-dimension
+code. CGAL simulates arbitrary precision while Qhull handles round-off error
+with thick facets. Compare the two approaches with <a href="http://doc.cgal.org/latest/Manual/devman_robustness.html">Robustness Issues in CGAL</a>,
+and <a href+"qh-impre.htm">Imprecision in Qhull</a>.
+
+
+<p><a href=http://www.algorithmic-solutions.com/enleda.htm>Leda</a> is a
+library for writing computational
+geometry programs and other combinatorial algorithms. It
+includes routines for computing 3-d convex
+hulls, 2-d Delaunay triangulations, and 3-d Delaunay triangulations.
+It provides rational arithmetic and graphical output. It runs on most
+platforms.
+
+<p>If your problem is in high dimensions with a few,
+non-simplicial facets, try Fukuda's <a
+href="http://www.cs.mcgill.ca/~fukuda/soft/cdd_home/cdd.html">cdd</a>.
+It is much faster than Qhull for these distributions. </p>
+
+<p>Custom software for 2-d and 3-d convex hulls may be faster
+than Qhull. Custom software should use less memory. Qhull uses
+general-dimension data structures and code. The data structures
+support non-simplicial facets.</p>
+
+<p>Qhull is not suitable for mesh generation or triangulation of
+arbitrary surfaces. You may use Qhull if the surface is convex or
+completely visible from an interior point (e.g., a star-shaped
+polyhedron). First, project each site to a sphere that is
+centered at the interior point. Then, compute the convex hull of
+the projected sites. The facets of the convex hull correspond to
+a triangulation of the surface. For mesh generation of arbitrary
+surfaces, see <a
+href="http://www.robertschneiders.de/meshgeneration/meshgeneration.html">Schneiders'
+Finite Element Mesh Generation</a>.</p>
+
+<p>Qhull is not suitable for constrained Delaunay triangulations.
+With a lot of work, you can write a program that uses Qhull to
+add constraints by adding additional points to the triangulation.</p>
+
+<p>Qhull is not suitable for the subdivision of arbitrary
+objects. Use <tt>qdelaunay</tt> to subdivide a convex object.</p>
+
+</blockquote>
+<h2><a href="#TOC">&#187;</a><a name="description">Description of
+Qhull </a></h2>
+<blockquote>
+
+<h3><a href="#TOC">&#187;</a><a name="definition">definition</a></h3>
+<blockquote>
+
+<p>The <i>convex hull</i> of a point set <i>P</i> is the smallest
+convex set that contains <i>P</i>. If <i>P</i> is finite, the
+convex hull defines a matrix <i>A</i> and a vector <i>b</i> such
+that for all <i>x</i> in <i>P</i>, <i>Ax+b &lt;= [0,...]</i>. </p>
+
+<p>Qhull computes the convex hull in 2-d, 3-d, 4-d, and higher
+dimensions. Qhull represents a convex hull as a list of facets.
+Each facet has a set of vertices, a set of neighboring facets,
+and a halfspace. A halfspace is defined by a unit normal and an
+offset (i.e., a row of <i>A</i> and an element of <i>b</i>). </p>
+
+<p>Qhull accounts for round-off error. It returns
+&quot;thick&quot; facets defined by two parallel hyperplanes. The
+outer planes contain all input points. The inner planes exclude
+all output vertices. See <a href="qh-impre.htm#imprecise">Imprecise
+convex hulls</a>.</p>
+
+<p>Qhull may be used for the Delaunay triangulation or the
+Voronoi diagram of a set of points. It may be used for the
+intersection of halfspaces. </p>
+
+</blockquote>
+<h3><a href="#TOC">&#187;</a><a name="input">input format</a></h3>
+<blockquote>
+
+<p>The input data on <tt>stdin</tt> consists of:</p>
+
+<ul>
+ <li>first line contains the dimension</li>
+ <li>second line contains the number of input points</li>
+ <li>remaining lines contain point coordinates</li>
+</ul>
+
+<p>For example: </p>
+
+<pre>
+ 3 #sample 3-d input
+ 5
+ 0.4 -0.5 1.0
+ 1000 -1e-5 -100
+ 0.3 0.2 0.1
+ 1.0 1.0 1.0
+ 0 0 0
+</pre>
+
+<p>Input may be entered by hand. End the input with a control-D
+(^D) character. </p>
+
+<p>To input data from a file, use I/O redirection or '<a
+href="qh-optt.htm#TI">TI file</a>'. The filename may not
+include spaces or quotes.</p>
+
+<p>A comment starts with a non-numeric character and continues to
+the end of line. The first comment is reported in summaries and
+statistics. With multiple <tt>qhull</tt> commands, use option '<a
+href="qh-optf.htm#FQ">FQ</a>' to place a comment in the output.</p>
+
+<p>The dimension and number of points can be reversed. Comments
+and line breaks are ignored. Error reporting is better if there
+is one point per line.</p>
+
+</blockquote>
+<h3><a href="#TOC">&#187;</a><a name="option">option format</a></h3>
+<blockquote>
+
+<p>Use options to specify the output formats and control
+Qhull. The <tt>qhull</tt> program takes all options. The
+other programs use a subset of the options. They disallow
+experimental and inappropriate options.
+
+<blockquote>
+<ul>
+<li>
+qconvex == qhull
+<li>
+qdelaunay == qhull d Qbb
+<li>
+qhalf == qhull H
+<li>
+qvoronoi == qhull v Qbb
+</ul>
+</blockquote>
+
+<p>Single letters are used for output formats and precision
+constants. The other options are grouped into menus for formats
+('<a href="qh-optf.htm#format">F</a>'), Geomview ('<a
+href="qh-optg.htm#geomview">G </a>'), printing ('<a
+href="qh-optp.htm#print">P</a>'), Qhull control ('<a
+href="qh-optq.htm#qhull">Q </a>'), and tracing ('<a
+href="qh-optt.htm#trace">T</a>'). The menu options may be listed
+together (e.g., 'GrD3' for 'Gr' and 'GD3'). Options may be in any
+order. Capitalized options take a numeric argument (except for '<a
+href="qh-optp.htm#PG">PG</a>' and '<a href="qh-optf.htm#format">F</a>'
+options). Use option '<a href="qh-optf.htm#FO">FO</a>' to print
+the selected options.</p>
+
+<p>Qhull uses zero-relative indexing. If there are <i>n</i>
+points, the index of the first point is <i>0</i> and the index of
+the last point is <i>n-1</i>.</p>
+
+<p>The default options are:</p>
+
+<ul>
+ <li>summary output ('<a href="qh-opto.htm#s">s</a>') </li>
+ <li>merged facets ('<a href="qh-optc.htm#C0">C-0</a>' in 2-d,
+ 3-d, 4-d; '<a href="qh-optq.htm#Qx">Qx</a>' in 5-d and
+ up)</li>
+</ul>
+
+<p>Except for bounding box
+('<a href="qh-optq.htm#Qbk">Qbk:n</a>', etc.), drop facets
+('<a href="qh-optp.htm#Pdk">Pdk:n</a>', etc.), and
+Qhull command ('<a href="qh-optf.htm#FQ">FQ</a>'), only the last
+occurence of an option counts.
+Bounding box and drop facets may be repeated for each dimension.
+Option 'FQ' may be repeated any number of times.
+
+<p>The Unix <tt>tcsh</tt> and <tt>ksh </tt>shells make it easy to
+try out different options. In Windows 95, use a command window with <tt>doskey</tt>
+and a window scroller (e.g., <tt>peruse</tt>). </p>
+
+</blockquote>
+<h3><a href="#TOC">&#187;</a><a name="output">output format</a></h3>
+<blockquote>
+
+<p>To write the results to a file, use I/O redirection or '<a
+href="qh-optt.htm#TO">TO file</a>'. Windows 95 users should use
+'TO file' or the console. If a filename is surrounded by single quotes,
+it may include spaces.
+</p>
+
+<p>The default output option is a short summary ('<a
+href="qh-opto.htm#s">s</a>') to <tt>stdout</tt>. There are many
+others (see <a href="qh-opto.htm">output</a> and <a
+href="qh-optf.htm">formats</a>). You can list vertex incidences,
+vertices and facets, vertex coordinates, or facet normals. You
+can view Qhull objects with Geomview, Mathematica, or Maple. You can
+print the internal data structures. You can call Qhull from your
+application (see <a href="qh-code.htm#library">Qhull library</a>).</p>
+
+<p>For example, 'qhull <a href="qh-opto.htm#o">o</a>' lists the
+vertices and facets of the convex hull. </p>
+
+<p>Error messages and additional summaries ('<a
+href="qh-opto.htm#s">s</a>') go to <tt>stderr</tt>. Unless
+redirected, <tt>stderr</tt> is the console.</p>
+
+</blockquote>
+<h3><a href="#TOC">&#187;</a><a name="algorithm">algorithm</a></h3>
+<blockquote>
+
+<p>Qhull implements the Quickhull algorithm for convex hull
+[Barber et al. <a href="#bar-dob96">'96</a>]. This algorithm
+combines the 2-d Quickhull algorithm with the <em>n</em>-d
+beneath-beyond algorithm [c.f., Preparata &amp; Shamos <a
+href="#pre-sha85">'85</a>]. It is similar to the randomized
+algorithms of Clarkson and others [Clarkson &amp; Shor <a
+href="#cla-sho89">'89</a>; Clarkson et al. <a href="#cla-meh93">'93</a>;
+Mulmuley <a href="#mulm94">'94</a>]. For a demonstration, see <a
+href="qh-eg.htm#how">How Qhull adds a point</a>. The main
+advantages of Quickhull are output sensitive performance (in
+terms of the number of extreme points), reduced space
+requirements, and floating-point error handling. </p>
+
+</blockquote>
+<h3><a href="#TOC">&#187;</a><a name="structure">data structures</a></h3>
+<blockquote>
+
+<p>Qhull produces the following data structures for dimension <i>d</i>:
+</p>
+
+<ul>
+ <li>A <em>coordinate</em> is a real number in floating point
+ format. </li>
+ <li>A <em>point</em> is an array of <i>d</i> coordinates.
+ With option '<a href="qh-optq.htm#QJn">QJ</a>', the
+ coordinates are joggled by a small amount. </li>
+ <li>A <em>vertex</em> is an input point. </li>
+ <li>A <em>hyperplane</em> is <i>d</i> normal coefficients and
+ an offset. The length of the normal is one. The
+ hyperplane defines a halfspace. If <i>V</i> is a normal, <i>b</i>
+ is an offset, and <i>x</i> is a point inside the convex
+ hull, then <i>Vx+b &lt;0</i>.</li>
+ <li>An <em>outer plane</em> is a positive
+ offset from a hyperplane. When Qhull is done, all points
+ will be below all outer planes.</li>
+ <li>An <em>inner plane</em> is a negative
+ offset from a hyperplane. When Qhull is done, all
+ vertices will be above the corresponding inner planes.</li>
+ <li>An <em>orientation</em> is either 'top' or 'bottom'. It is the
+ topological equivalent of a hyperplane's geometric
+ orientation. </li>
+ <li>A <em>simplicial facet</em> is a set of
+ <i>d</i> neighboring facets, a set of <i>d</i> vertices, a
+ hyperplane equation, an inner plane, an outer plane, and
+ an orientation. For example in 3-d, a simplicial facet is
+ a triangle. </li>
+ <li>A <em>centrum</em> is a point on a facet's hyperplane. A
+ centrum is the average of a facet's vertices. Neighboring
+ facets are <em>convex</em> if each centrum is below the
+ neighbor facet's hyperplane. </li>
+ <li>A <em>ridge</em> is a set of <i>d-1</i> vertices, two
+ neighboring facets, and an orientation. For example in
+ 3-d, a ridge is a line segment. </li>
+ <li>A <em>non-simplicial facet</em> is a set of ridges, a
+ hyperplane equation, a centrum, an outer plane, and an
+ inner plane. The ridges determine a set of neighboring
+ facets, a set of vertices, and an orientation. Qhull
+ produces a non-simplicial facet when it merges two facets
+ together. For example, a cube has six non-simplicial
+ facets. </li>
+</ul>
+
+<p>For examples, use option '<a href="qh-opto.htm#f">f</a>'. See <a
+href="../src/libqhull/qh-poly.htm">polyhedron operations</a> for further
+design documentation. </p>
+
+</blockquote>
+<h3><a href="#TOC">&#187;</a>Imprecision in Qhull</h3>
+<blockquote>
+
+<p>See <a href="qh-impre.htm">Imprecision in Qhull</a> and <a href="qh-impre.htm#joggle">Merged facets or joggled input</a></p>
+
+</blockquote>
+<h3><a href="#TOC">&#187;</a>Examples of Qhull</h3>
+<blockquote>
+
+<p>See <a href="qh-eg.htm">Examples of Qhull</a>. Most of these examples require <a href="#geomview">Geomview</a>.
+Some of the examples have <a
+href="http://www.geom.uiuc.edu/graphics/pix/Special_Topics/Computational_Geometry/welcome.html">pictures
+</a>.</p>
+
+</blockquote>
+</blockquote>
+<h2><a href="#TOC">&#187;</a>Options for using Qhull </h2>
+<blockquote>
+
+<p>See <a href="qh-quick.htm#options">Options</a>.</p>
+
+</blockquote>
+<h2><a href="#TOC">&#187;</a>Qhull internals </h2>
+<blockquote>
+
+<p>See <a href="qh-code.htm">Internals</a>.</p>
+
+</blockquote>
+<h2><a href="#TOC">&#187;</a><a name="geomview">Geomview, Qhull's
+graphical viewer</a></h2>
+<blockquote>
+
+<p><a href="http://www.geomview.org">Geomview</a>
+is an interactive geometry viewing program.
+Geomview provides a good visualization of Qhull's 2-d and 3-d results.
+
+<p>Qhull includes <a href="qh-eg.htm">Examples of Qhull</a> that may be viewed with Geomview.
+
+<p>Geomview can help visulalize a 3-d Delaunay triangulation or the surface of a 4-d convex hull,
+Use option '<a href="qh-optq.htm#QVn">QVn</a>' to select the 3-D facets adjacent to a vertex.
+
+<p>You may use Geomview to create movies that animate your objects (c.f., <a href="http://www.geomview.org/FAQ/answers.shtml#mpeg">How can I create a video animation?</a>).
+Geomview helped create the <a href="http://www.geom.uiuc.edu/video/">mathematical videos</a> "Not Knot", "Outside In", and "The Shape of Space" from the Geometry Center.
+
+
+<h3><a href="#TOC">&#187;</a><a name="geomview-install">Installing Geomview</a></h3>
+<blockquote>
+
+<p>Geomview is an <a href=http://sourceforge.net/projects/geomview>open source project</a>
+under SourceForge.
+
+<p>
+For build instructions see
+<a href="http://www.geomview.org/download/">Downloading Geomview</a>.
+Geomview builds under Linux, Unix, Macintosh OS X, and Windows.
+
+<p>Geomview has <a href="https://packages.debian.org/search?keywords=geomview">installable packages</a> for Debian and Ubuntu.
+The OS X build needs Xcode, an X11 SDK, and Lesstif or Motif.
+The Windows build uses Cygwin (see <a href="#geomview-win">Building Geomview</a> below for instructions).
+
+<p>If using Xforms (e.g., for Geomview's <a href="http://www.geomview.org/docs/html/Modules.html">External Modules</a>), install the 'libXpm-devel' package from cygwin and move the xforms directory into your geomview directory, e.g.,<br><tt>mv xforms-1.2.4 geomview-1.9.5/xforms</tt>
+
+<p>Geomview's <a href="http://www.geom.uiuc.edu/software/geomview/docs/NDview/manpagehelp.html">ndview<a/> provides multiple views into 4-d and higher objects.
+This module is out-of-date (<a href="http://sourceforge.net/p/geomview/mailman/message/2004152/">geomview-users: 4dview</a>).
+Download NDview-sgi.tar.Z at <a href="ftp://www.geom.uiuc.edu/pub/software/geomview/newpieces/sgi">newpieces</a> and 4dview at <a href="https://stuff.mit.edu/afs/sipb/project/3d/arch/sgi_62/lib/Geomview/modules/">Geomview/modules</a>.
+
+</blockquote>
+<h3><a href="#TOC">&#187;</a><a name="geomview-use">Using Geomview</a></h3>
+<blockquote>
+
+<p>Use Geomview to view <a href="qh-eg.htm">Examples of Qhull</a>. You can spin the convex hull, fly a camera through its facets,
+and see how Qhull produces thick facets in response to round-off error.
+
+<p>Follow these instructions to view 'eg,01.cube' from Examples of Qhull
+<ol>
+<li>Launch an XTerm command shell
+<ul>
+<li>If needed, start the X terminal server, Use 'xinit' or 'startx' in /usr/X11R6/bin<br><tt>xinit -- -multiwindow -clipboard</tt><br><tt>startx</tt>
+<li>Start an XTerm command shell. In Windows, click the Cygwin/bash icon on your desktop.
+<li>Set the DISPLAY variable, e.g.,<br><tt>export DISPLAY=:0</tt><br><tt>export DISPLAY=:0 >>~/.bashenv</tt>
+</ul>
+<li>Use Qhull's <a href="qh-optg.htm">Geomview options</a> to create a geomview object
+<ul>
+<li><tt>rbox c D3 | qconvex G >eg.01.cube</tt>
+<li>On windows, convert the output to Unix text format with 'd2u'<br><tt>rbox c D3 | qconvex G | d2u >eg.01.cube</tt><br><tt>d2u eg.*</tt>
+</ul>
+<li>Run Geomview
+<ul>
+<li>Start Geomview with your example<br><tt>./geomview eg.01.cube</tt>
+<li>Follow the instructions in <a href="http://www.geomview.org/docs/html/Tutorial.html">Gemoview Tutorial</a>
+<li>Geomview creates the <i>Geomview control panel</i> with Targets and External Module, the <i>Geomview toolbar</i> with buttons for controlling Geomview, and the <i>Geomview camera window</i> showing a cube.
+<li>Clear the camera window by selecting your object in the Targets list and 'Edit > Delete' or 'dd'
+<li>Load the <i>Geomview files panel</i>. Select 'Open' in the 'File' menu.
+<li>Set 'Filter' in the files panel to your example directory followed by '/*' (e.g., '/usr/local/qhull-2015.2/eg/*')
+<li>Click 'Filter' in the files panel to view your examples in the 'Files' list.
+<li>Load another example into the camera window by selecting it and clicking 'OK'.
+<li>Review the instructions for <a href="http://www.geomview.org/docs/html/Interaction.html">Interacting with Geomview</a>
+<li>When viewing multiple objects at once, you may want to turn off normalization. In the 'Inspect > Apperance' control panel, set 'Normalize' to 'None'.
+</ul>
+</ol>
+
+<p>Geomview defines GCL (a textual API for controlling Geomview) and OOGL (a textual file format for defining objects).
+<ul>
+<li>To control Geomview, you may use any program that reads and writes from stdin and stdout. For example, it could report Qhull's information about a vertex identified by a double-click 'pick' event.
+<li><a href="http://www.geomview.org/docs/html/GCL.html">GCL</a> command language for controlling Geomview
+<li><a href="http://www.geomview.org/docs/html/OOGL-File-Formats.html">OOGL</a> file format for defining objects (<a href="http://www.geomview.org/docs/oogltour.html">tutorial</a>).
+<li><a href="http://www.geomview.org/docs/html/Modules.html">External Modules</a> for interacting with Geomview via GCL
+<li>Interact with your objects via <a href="http://www.geomview.org/docs/html/pick.html">pick</a> commands in response to right-mouse double clicks. Enable pick events with the <a href="http://www.geomview.org/docs/html/interest.html">interest</a> command.
+</ul>
+
+</blockquote>
+<h3><a href="#TOC">&#187;</a><a name="geomview-win">Building Geomview for Windows</a></h3>
+<blockquote>
+
+<p>Compile Geomview under Cygwin. For detailed instructions, see
+<a href="http://www.ee.surrey.ac.uk/Personal/L.Wood/software/SaVi/building-under-Windows/"
+>Building Savi and Geomview under Windows</a>. These instructions are somewhat out-of-date. Updated
+instructions follow.
+
+<p>How to compile Geomview under 32-bit Cygwin (October 2015)</p>
+<ol>
+<li><b>Note:</b> L. Wood has run into multiple issues with Geomview on Cygwin. He recommends Virtualbox/Ubuntu
+and a one-click install of geomview via the Ubuntu package. See his Savi/Geomview link above.
+<li>Install 32-bit <a href="http://cygwin.com/">Cygwin</a> as follows.
+For additional guidance, see Cygwin's <a href="https://cygwin.com/install.html">Installing and Updating Cygwin Packages</a>
+and <a href="http://www.qhull.org/road/road-faq/xml/cmdline.xml#setup-cygwin">Setup cygwin</a>.
+<ul>
+<li>Launch the cygwin installer.
+<li>Select a mirror from <a href="http://cygwin.com/mirrors.html">Cygwin mirrors</a> (e.g., http://mirrors.kernel.org/sourceware/cygwin/ in California).
+<li>Select the packages to install. Besides the cygwin packages listed in the Savi/Windows instructions consider adding
+<ul>
+<li><b>Default</b> -- libXm-devel (required for /usr/include/Xm/Xm.h)
+<li><b>Devel</b> -- bashdb, gcc-core (in place of gcc), gdb
+<li><b>Lib</b> -- libGL-devel, libGLU1 (required, obsolete), libGLU-devel (required, obsolete), libjpeg-devel(XForms), libXext-devel (required), libXpm-devel (Xforms)
+libGL and lib
+<li><b>Math</b> -- bc
+<li><b>Net</b> -- autossh, inetutils, openssh
+<li><b>System</b> -- chere
+<li><b>Utils</b> -- dos2unix (required for qhull), keychain
+<li>If installing perl, ActiveState Perl may be a better choice than cygwin's perl. Perl is not used by Geomview or Qhull.
+<li><a href="https://cygwin.com/cgi-bin2/package-grep.cgi">Cygwin Package Search</a> -- Search for cygwin programs and packages
+</ul>
+<li>Click 'Next' to download and install the packages.
+<li>If the download is incomplete, try again.
+<li>If you try again after a successful install, cygwin will uninstall and reinstall all modules..
+<li>Click on the 'Cywin Terminal' icon on the Desktop. It sets up a user directory in /home from /etc/skel/...
+<li>Mount your disk drives<br>mount c: /c # Ignore the warning /c does not exist
+</ul>
+<li>Consider installing the <a href="http://www.qhull.org/bash/doc/road-bash.html">Road Bash</a> scripts (/etc/road-*) from <a href="http://www.qhull.org/road/">Road</a>.
+They define aliases and functions for Unix command shells (Unix, Linux, Mac OS X, Windows),
+<ul>
+<li>Download Road Bash and unzip the downloaded file
+<li>Copy .../bash/etc/road-* to the Cywin /etc directory (by default, C:\cygwin\etc).
+<li>Using the cygwin terminal, convert the road scripts to Unix format<br>d2u /etc/road-*
+<li>Try it<br>source /etc/road-home.bashrc
+<li>Install it<br>cp /etc/road-home.bashrc ~/.bashrc
+</ul>
+<li>Launch the X terminal server from '<tt>Start > All programs > Cygwin-X > Xwin Server</tt>'. Alternatively, run 'startx'
+<li>Launch an XTerm shell
+<ul>
+<li>Right click the Cywin icon on the system tray in the Windows taskbar.
+<li>Select '<tt>System Tools > XTerm</tt>'
+</ul>
+<li>Download and extract Geomview -- <a href="http://www.geomview.org/download/">Downloading Geomview</a>
+<li>Compile Geomview
+<ul>
+<li>./configure
+<li>make
+</ul>
+<li>If './configure' fails, check 'config.log' at the failing step. Look carefully for missing libraries, etc. The <a href="http://www.geomview.org/FAQ/answers.shtml">Geomview FAQ</a> contains suggestions (e.g., "configure claims it can't find OpenGl").
+<li>If 'make' fails, read the output carefully for error messages. Usually it is a missing include file or package. Locate and install the missing cygwin packages
+(<a href="https://cygwin.com/cgi-bin2/package-grep.cgi">Cygwin Package Search</a>).
+</ol>
+
+</blockquote>
+</blockquote>
+<h2><a href="#TOC">&#187;</a><a name="bugs">What to do if something
+goes wrong</a></h2>
+<blockquote>
+
+<p>Please report bugs to <a href=mailto:qhull_bug@qhull.org>qhull_bug@qhull.org</a>
+</a>. Please report if Qhull crashes. Please report if Qhull
+generates an &quot;internal error&quot;. Please report if Qhull
+produces a poor approximate hull in 2-d, 3-d or 4-d. Please
+report documentation errors. Please report missing or incorrect
+links.</p>
+
+<p>If you do not understand something, try a small example. The <a
+href="rbox.htm">rbox</a> program is an easy way to generate
+test cases. The <a href="#geomview">Geomview</a> program helps to
+visualize the output from Qhull.</p>
+
+<p>If Qhull does not compile, it is due to an incompatibility
+between your system and ours. The first thing to check is that
+your compiler is ANSI standard. Qhull produces a compiler error
+if __STDC__ is not defined. You may need to set a flag (e.g.,
+'-A' or '-ansi').</p>
+
+<p>If Qhull compiles but crashes on the test case (rbox D4),
+there's still incompatibility between your system and ours.
+Sometimes it is due to memory management. This can be turned off
+with qh_NOmem in mem.h. Please let us know if you figure out how
+to fix these problems. </p>
+
+<p>If you doubt the output from Qhull, add option '<a
+href="qh-optt.htm#Tv">Tv</a>'. It checks that every point is
+inside the outer planes of the convex hull. It checks that every
+facet is convex with its neighbors. It checks the topology of the
+convex hull.</p>
+
+<p>Qhull should work on all inputs. It may report precision
+errors if you turn off merged facets with option '<a
+href="qh-optq.htm#Q0">Q0</a>'. This can get as bad as facets with
+flipped orientation or two facets with the same vertices. You'll
+get a long help message if you run into such a case. They are
+easy to generate with <tt>rbox</tt>.</p>
+
+<p>If you do find a problem, try to simplify it before reporting
+the error. Try different size inputs to locate the smallest one
+that causes an error. You're welcome to hunt through the code
+using the execution trace ('<a href="qh-optt.htm#Tn">T4</a>') as
+a guide. This is especially true if you're incorporating Qhull
+into your own program. </p>
+
+<p>When you report an error, please attach a data set to the end
+of your message. Include the options that you used with Qhull,
+the results of option '<a href="qh-optf.htm#FO">FO</a>', and any
+messages generated by Qhull. This allows me to see the error for
+myself. Qhull is maintained part-time. </p>
+
+</blockquote>
+<h2><a href="#TOC">&#187;</a><a name="email">Email</a></h2>
+<blockquote>
+
+<p>Please send correspondence to Brad Barber at <a href=mailto:qhull@qhull.org>qhull@qhull.org</a>
+and report bugs to <a href=mailto:qhull_bug@qhull.org>qhull_bug@qhull.org</a>
+</a>. Let me know how you use Qhull. If you mention it in a
+paper, please send a reference and abstract.</p>
+
+<p>If you would like to get Qhull announcements (e.g., a new
+version) and news (any bugs that get fixed, etc.), let us know
+and we will add you to our mailing list. For Internet news about geometric algorithms
+and convex hulls, look at comp.graphics.algorithms and
+sci.math.num-analysis. For Qhull news look at <a
+href="http://www.qhull.org/news">qhull-news.html</a>.</p>
+
+</blockquote>
+<h2><a href="#TOC">&#187;</a><a name="authors">Authors</a></h2>
+<blockquote>
+
+<pre>
+ C. Bradford Barber Hannu Huhdanpaa
+ bradb@shore.net hannu@qhull.org
+</pre>
+
+</blockquote>
+<h2><a href="#TOC">&#187;</a><a name="acknowledge">Acknowledgments</a></h2>
+<blockquote>
+
+<p>A special thanks to David Dobkin for his guidance. A special
+thanks to Albert Marden, Victor Milenkovic, the Geometry Center,
+and Harvard University for supporting this work.</p>
+
+<p>A special thanks to Mark Phillips, Robert Miner, and Stuart Levy for running the Geometry
+ Center web site long after the Geometry Center closed.
+ Stuart moved the web site to the University of Illinois at Champaign-Urbana.
+Mark and Robert are founders of <a href=http://www.geomtech.com>Geometry Technologies</a>.
+Mark, Stuart, and Tamara Munzner are the original authors of <a href=http://www.geomview.org>Geomview</a>.
+
+<p>A special thanks to <a href="http://www.endocardial.com/">Endocardial
+Solutions, Inc.</a> of St. Paul, Minnesota for their support of the
+internal documentation (<a href=../src/libqhull/index.htm>src/libqhull/index.htm</a>). They use Qhull to build 3-d models of
+heart chambers.</p>
+
+<p>Qhull 1.0 and 2.0 were developed under National Science Foundation
+grants NSF/DMS-8920161 and NSF-CCR-91-15793 750-7504. If you find
+it useful, please let us know.</p>
+
+<p>The Geometry Center was supported by grant DMS-8920161 from the
+National Science Foundation, by grant DOE/DE-FG02-92ER25137 from
+the Department of Energy, by the University of Minnesota, and by
+Minnesota Technology, Inc.</p>
+
+</blockquote>
+<h2><a href="#TOC">&#187;</a><a name="ref">References</a></h2>
+<blockquote>
+
+<p><a name="aure91">Aurenhammer</a>, F., &quot;Voronoi diagrams
+-- A survey of a fundamental geometric data structure,&quot; <i>ACM
+Computing Surveys</i>, 1991, 23:345-405. </p>
+
+<p><a name="bar-dob96">Barber</a>, C. B., D.P. Dobkin, and H.T.
+Huhdanpaa, &quot;The Quickhull Algorithm for Convex Hulls,&quot; <i>ACM
+Transactions on Mathematical Software</i>, 22(4):469-483, Dec 1996, www.qhull.org
+[<a
+href="http://portal.acm.org/citation.cfm?doid=235815.235821">http://portal.acm.org</a>;
+<a href="http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.117.405">http://citeseerx.ist.psu.edu</a>].
+</p>
+
+<p><a name="cla-sho89">Clarkson</a>, K.L. and P.W. Shor,
+&quot;Applications of random sampling in computational geometry,
+II&quot;, <i>Discrete Computational Geometry</i>, 4:387-421, 1989</p>
+
+<p><a name="cla-meh93">Clarkson</a>, K.L., K. Mehlhorn, and R.
+Seidel, &quot;Four results on randomized incremental
+construction,&quot; <em>Computational Geometry: Theory and
+Applications</em>, vol. 3, p. 185-211, 1993.</p>
+
+<p><a name="devi01">Devillers</a>, et. al.,
+"Walking in a triangulation," <i>ACM Symposium on
+Computational Geometry</i>, June 3-5,2001, Medford MA.
+
+<p><a name="dob-kir90">Dobkin</a>, D.P. and D.G. Kirkpatrick,
+&quot;Determining the separation of preprocessed polyhedra--a
+unified approach,&quot; in <i>Proc. 17th Inter. Colloq. Automata
+Lang. Program.</i>, in <i>Lecture Notes in Computer Science</i>,
+Springer-Verlag, 443:400-413, 1990. </p>
+
+<p><a name="edel01">Edelsbrunner</a>, H, <i>Geometry and Topology for Mesh Generation</i>,
+Cambridge University Press, 2001.
+
+<p><a name=gart99>Gartner, B.</a>, "Fast and robust smallest enclosing balls", <i>Algorithms - ESA '99</i>, LNCS 1643.
+
+<p><a name=golub83>Golub, G.H. and van Loan, C.F.</a>, <i>Matric Computations</i>, Baltimore, Maryland, USA: John Hopkins Press, 1983
+
+<p><a name="fort93">Fortune, S.</a>, &quot;Computational
+geometry,&quot; in R. Martin, editor, <i>Directions in Geometric
+Computation</i>, Information Geometers, 47 Stockers Avenue,
+Winchester, SO22 5LB, UK, ISBN 1-874728-02-X, 1993.</p>
+
+<p><a name="mile93">Milenkovic, V.</a>, &quot;Robust polygon
+modeling,&quot; Computer-Aided Design, vol. 25, p. 546-566,
+September 1993. </p>
+
+<p><a name="muck96">Mucke</a>, E.P., I. Saias, B. Zhu, <i>Fast
+randomized point location without preprocessing in Two- and
+Three-dimensional Delaunay Triangulations</i>, ACM Symposium on
+Computational Geometry, p. 274-283, 1996 [<a
+href="http://www.geom.uiuc.edu/software/cglist/GeomDir/">GeomDir</a>].
+</p>
+
+<p><a name="mulm94">Mulmuley</a>, K., <i>Computational Geometry,
+An Introduction Through Randomized Algorithms</i>, Prentice-Hall,
+NJ, 1994.</p>
+
+<p><a name="orou94">O'Rourke</a>, J., <i>Computational Geometry
+in C</i>, Cambridge University Press, 1994.</p>
+
+<p><a name="pre-sha85">Preparata</a>, F. and M. Shamos, <i>Computational
+Geometry</i>, Springer-Verlag, New York, 1985.</p>
+
+</blockquote>
+<!-- Navigation links -->
+<hr>
+
+<p><b>Up:</b> <a
+href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b><a
+href="http://www.qhull.org/news">News</a> about Qhull<br>
+<b>Up:</b> <a href="http://www.qhull.org/html/qh-faq.htm">FAQ</a> about Qhull<br>
+<b>To:</b> <a href="#TOC">Qhull manual</a>: Table of Contents<br>
+<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
+&#149; <a href="qh-quick.htm#options">Options</a>
+&#149; <a href="qh-opto.htm#output">Output</a>
+&#149; <a href="qh-optf.htm#format">Formats</a>
+&#149; <a href="qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="qh-optp.htm#print">Print</a>
+&#149; <a href="qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="qh-optc.htm#prec">Precision</a>
+&#149; <a href="qh-optt.htm#trace">Trace</a>
+&#149; <a href="../src/libqhull_r/index.htm">Functions</a><br>
+<b>Dn:</b> <a href="qh-impre.htm">Imprecision in Qhull</a><br>
+<b>Dn:</b> <a href="qh-eg.htm">Description of Qhull examples</a><br>
+<b>Dn:</b> <a href="qh-code.htm">Qhull internals</a><br>
+<b>Dn:</b> <a href="../src/libqhull/index.htm">Qhull functions, macros, and data
+structures</a>
+<!-- GC common information -->
+<hr>
+
+<p><a href="http://www.geom.uiuc.edu/"><img src="qh--geom.gif"
+align="middle" width="40" height="40"></a><i>The Geometry Center
+Home Page </i></p>
+
+<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a>
+</a><br>
+Created: Sept. 25, 1995 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p>
+</body>
+</html>
diff --git a/xs/src/qhull/html/normal_voronoi_knauss_oesterle.jpg b/xs/src/qhull/html/normal_voronoi_knauss_oesterle.jpg
new file mode 100644
index 000000000..f46d42127
--- /dev/null
+++ b/xs/src/qhull/html/normal_voronoi_knauss_oesterle.jpg
Binary files differ
diff --git a/xs/src/qhull/html/qconvex.htm b/xs/src/qhull/html/qconvex.htm
new file mode 100644
index 000000000..38a363b08
--- /dev/null
+++ b/xs/src/qhull/html/qconvex.htm
@@ -0,0 +1,630 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+
+<head>
+<title>qconvex -- convex hull</title>
+</head>
+
+<body>
+<!-- Navigation links -->
+<a name="TOP"><b>Up</b></a><b>:</b>
+<a href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b> <a href="index.htm#TOC">Qhull manual</a> -- Table of Contents<br>
+<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
+&#149; <a href="qh-quick.htm#options">Options</a>
+&#149; <a href="qh-opto.htm#output">Output</a>
+&#149; <a href="qh-optf.htm#format">Formats</a>
+&#149; <a href="qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="qh-optp.htm#print">Print</a>
+&#149; <a href="qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="qh-optc.htm#prec">Precision</a>
+&#149; <a href="qh-optt.htm#trace">Trace</a>
+&#149; <a href="../src/libqhull_r/index.htm">Functions</a><br>
+<b>To:</b> <a href="#synopsis">sy</a>nopsis
+&#149; <a href="#input">in</a>put &#149; <a href="#outputs">ou</a>tputs
+&#149; <a href="#controls">co</a>ntrols &#149; <a href="#graphics">gr</a>aphics
+&#149; <a href="#notes">no</a>tes &#149; <a href="#conventions">co</a>nventions
+&#149; <a href="#options">op</a>tions
+<hr>
+<!-- Main text of document -->
+<h1><a
+href="http://www.geom.uiuc.edu/graphics/pix/Special_Topics/Computational_Geometry/cone.html"><img
+src="qh--cone.gif" alt="[cone]" align="middle" width="100"
+height="100"></a>qconvex -- convex hull</h1>
+
+<p>The convex hull of a set of points is the smallest convex set
+containing the points. See the detailed introduction by O'Rourke
+[<a href="index.htm#orou94">'94</a>]. See <a
+href="index.htm#description">Description of Qhull</a> and <a
+href="qh-eg.htm#how">How Qhull adds a point</a>.</p>
+
+<blockquote>
+<dl>
+ <dt><b>Example:</b> rbox 10 D3 | qconvex <a
+ href="qh-opto.htm#s">s</a> <a href="qh-opto.htm#o">o</a> <a
+ href="qh-optt.htm#TO">TO result</a></dt>
+ <dd>Compute the 3-d convex hull of 10 random points. Write a
+ summary to the console and the points and facets to
+ 'result'.</dd>
+
+ <dt>&nbsp;</dt>
+ <dt><b>Example:</b> rbox c | qconvex <a
+ href="qh-opto.htm#n">n</a></dt>
+ <dd>Print the normals for each facet of a cube.</dd>
+ <dt>&nbsp;</dt>
+ <dt><b>Example:</b> rbox c | qconvex <a
+ href="qh-opto.htm#i">i</a> <a href="qh-optq.htm#Qt">Qt</a></dt>
+ <dd>Print the triangulated facets of a cube.</dd>
+ <dt>&nbsp;</dt>
+ <dt><b>Example:</b> rbox y 500 W0 | qconvex</dt>
+ <dd>Compute the convex hull of a simplex with 500
+ points on its surface.</dd>
+ <dt>&nbsp;</dt>
+ <dt><b>Example:</b> rbox x W1e-12 1000 | qconvex
+ <a href="qh-optq.htm#QR">QR0</a></dt>
+ <dd>Compute the convex hull of 1000 points near the
+ surface of a randomly rotated simplex. Report
+ the maximum thickness of a facet.</dd>
+ <dt>&nbsp;</dt>
+ <dt><b>Example:</b> rbox 1000 s | qconvex <a
+ href="qh-opto.htm#s">s</a> <a
+ href="qh-optf.htm#FA">FA</a> </dt>
+ <dd>Compute the convex hull of 1000 cospherical
+ points. Verify the results and print a summary
+ with the total area and volume.</dd>
+ <dt>&nbsp;</dt>
+ <dt><b>Example:</b> rbox d D12 | qconvex <a
+ href="qh-optq.htm#QRn">QR0</a> <a
+ href="qh-optf.htm#FA">FA</a></dt>
+ <dd>Compute the convex hull of a 12-d diamond.
+ Randomly rotate the input. Note the large number
+ of facets and the small volume.</dd>
+ <dt>&nbsp;</dt>
+ <dt><b>Example:</b> rbox c D7 | qconvex <a
+ href="qh-optf.htm#FA">FA</a> <a
+ href="qh-optt.htm#TFn">TF1000</a></dt>
+ <dd>Compute the convex hull of the 7-d hypercube.
+ Report on progress every 1000 facets. Computing
+ the convex hull of the 9-d hypercube takes too
+ much time and space. </dd>
+ <dt>&nbsp;</dt>
+ <dt><b>Example:</b> rbox c d D2 | qconvex <a
+ href="qh-optq.htm#Qc">Qc</a> <a
+ href="qh-opto.htm#s">s</a> <a
+ href="qh-opto.htm#f">f</a> <a
+ href="qh-optf.htm#Fx">Fx</a> | more</dt>
+ <dd>Dump all fields of all facets for a square and a
+ diamond. Also print a summary and a list of
+ vertices. Note the coplanar points.</dd>
+ <dt>&nbsp;</dt>
+</dl>
+</blockquote>
+
+<p>Except for rbox, all of the qhull programs compute a convex hull.
+
+<p>By default, Qhull merges coplanar facets. For example, the convex
+hull of a cube's vertices has six facets.
+
+<p>If you use '<a href="qh-optq.htm#Qt">Qt</a>' (triangulated output),
+all facets will be simplicial (e.g., triangles in 2-d). For the cube
+example, it will have 12 facets. Some facets may be
+degenerate and have zero area.
+
+<p>If you use '<a href="qh-optq.htm#QJn">QJ</a>' (joggled input),
+all facets will be simplicial. The corresponding vertices will be
+slightly perturbed and identical points will be joggled apart.
+Joggled input is less accurate that triangulated
+output.See <a
+href="qh-impre.htm#joggle">Merged facets or joggled input</a>. </p>
+
+<p>The output for 4-d convex hulls may be confusing if the convex
+hull contains non-simplicial facets (e.g., a hypercube). See
+<a href=qh-faq.htm#extra>Why
+are there extra points in a 4-d or higher convex hull?</a><br>
+</p>
+</p>
+
+<p>The 'qconvex' program is equivalent to
+'<a href=qhull.htm#outputs>qhull</a>' in 2-d to 4-d, and
+'<a href=qhull.htm#outputs>qhull</a> <a href=qh-optq.htm#Qx>Qx</a>'
+in 5-d and higher. It disables the following Qhull
+<a href=qh-quick.htm#options>options</a>: <i>d v H Qbb Qf Qg Qm
+Qr Qu Qv Qx Qz TR E V Fp Gt Q0,etc</i>.
+
+<p><b>Copyright &copy; 1995-2015 C.B. Barber</b></p>
+
+<hr>
+
+<h3><a href="#TOP">&#187;</a><a name="synopsis">qconvex synopsis</a></h3>
+<pre>
+qconvex- compute the convex hull.
+ input (stdin): dimension, number of points, point coordinates
+ comments start with a non-numeric character
+
+options (qconvex.htm):
+ Qt - triangulated output
+ QJ - joggle input instead of merging facets
+ Tv - verify result: structure, convexity, and point inclusion
+ . - concise list of all options
+ - - one-line description of all options
+
+output options (subset):
+ s - summary of results (default)
+ i - vertices incident to each facet
+ n - normals with offsets
+ p - vertex coordinates (includes coplanar points if 'Qc')
+ Fx - extreme points (convex hull vertices)
+ FA - compute total area and volume
+ o - OFF format (dim, n, points, facets)
+ G - Geomview output (2-d, 3-d, and 4-d)
+ m - Mathematica output (2-d and 3-d)
+ QVn - print facets that include point n, -n if not
+ TO file- output results to file, may be enclosed in single quotes
+
+examples:
+ rbox c D2 | qconvex s n rbox c D2 | qconvex i
+ rbox c D2 | qconvex o rbox 1000 s | qconvex s Tv FA
+ rbox c d D2 | qconvex s Qc Fx rbox y 1000 W0 | qconvex s n
+ rbox y 1000 W0 | qconvex s QJ rbox d G1 D12 | qconvex QR0 FA Pp
+ rbox c D7 | qconvex FA TF1000
+</pre>
+
+<h3><a href="#TOP">&#187;</a><a name="input">qconvex
+input</a></h3>
+<blockquote>
+
+<p>The input data on <tt>stdin</tt> consists of:</p>
+<ul>
+ <li>dimension
+ <li>number of points</li>
+ <li>point coordinates</li>
+</ul>
+
+<p>Use I/O redirection (e.g., qconvex &lt; data.txt), a pipe (e.g., rbox 10 | qconvex),
+or the '<a href=qh-optt.htm#TI>TI</a>' option (e.g., qconvex TI data.txt).
+
+<p>Comments start with a non-numeric character. Error reporting is
+simpler if there is one point per line. Dimension
+and number of points may be reversed.
+
+<p>Here is the input for computing the convex
+hull of the unit cube. The output is the normals, one
+per facet.</p>
+
+<blockquote>
+ <p>rbox c &gt; data </p>
+ <pre>
+3 RBOX c
+8
+ -0.5 -0.5 -0.5
+ -0.5 -0.5 0.5
+ -0.5 0.5 -0.5
+ -0.5 0.5 0.5
+ 0.5 -0.5 -0.5
+ 0.5 -0.5 0.5
+ 0.5 0.5 -0.5
+ 0.5 0.5 0.5
+</pre>
+ <p>qconvex s n &lt; data</p>
+ <pre>
+
+Convex hull of 8 points in 3-d:
+
+ Number of vertices: 8
+ Number of facets: 6
+ Number of non-simplicial facets: 6
+
+Statistics for: RBOX c | QCONVEX s n
+
+ Number of points processed: 8
+ Number of hyperplanes created: 11
+ Number of distance tests for qhull: 35
+ Number of merged facets: 6
+ Number of distance tests for merging: 84
+ CPU seconds to compute hull (after input): 0.081
+
+4
+6
+ 0 0 -1 -0.5
+ 0 -1 0 -0.5
+ 1 0 0 -0.5
+ -1 0 0 -0.5
+ 0 1 0 -0.5
+ 0 0 1 -0.5
+</pre>
+</blockquote>
+
+</blockquote>
+<h3><a href="#TOP">&#187;</a><a name="outputs">qconvex outputs</a></h3>
+<blockquote>
+
+<p>These options control the output of qconvex. They may be used
+individually or together.</p>
+<blockquote>
+<dl compact>
+ <dt>&nbsp;</dt>
+ <dd><b>Vertices</b></dd>
+ <dt><a href="qh-optf.htm#Fx">Fx</a></dt>
+ <dd>list extreme points (i.e., vertices). The first line is the number of
+ extreme points. Each point is listed, one per line. The cube example
+ has eight vertices.</dd>
+ <dt><a href="qh-optf.htm#Fv">Fv</a></dt>
+ <dd>list vertices for each facet. The first line is the number of facets.
+ Each remaining line starts with the number of vertices. For the cube example,
+ each facet has four vertices.</dd>
+ <dt><a href="qh-opto.htm#i">i</a></dt>
+ <dd>list vertices for each facet. The first line is the number of facets. The
+ remaining lines list the vertices for each facet. In 4-d and
+ higher, triangulate non-simplicial facets by adding an extra point.</dd>
+ <dt>&nbsp;</dt>
+ <dt>&nbsp;</dt>
+ <dd><b>Coordinates</b></dd>
+ <dt><a href="qh-opto.htm#o">o</a></dt>
+ <dd>print vertices and facets of the convex hull in OFF format. The
+ first line is the dimension. The second line is the number of
+ vertices, facets, and ridges. The vertex
+ coordinates are next, followed by the facets. Each facet starts with
+ the number of vertices. The cube example has four vertices per facet.</dd>
+ <dt><a href="qh-optf.htm#Ft">Ft</a></dt>
+ <dd>print a triangulation of the convex hull in OFF format. The first line
+ is the dimension. The second line is the number of vertices and added points,
+ followed by the number of facets and the number of ridges.
+ The vertex coordinates are next, followed by the centrum coordinates. There is
+ one centrum for each non-simplicial facet.
+ The cube example has six centrums, one per square.
+ Each facet starts with the number of vertices or centrums.
+ In the cube example, each facet uses two vertices and one centrum.</dd>
+ <dt><a href="qh-opto.htm#p">p</a></dt>
+ <dd>print vertex coordinates. The first line is the dimension and the second
+ line is the number of vertices. The following lines are the coordinates of each
+ vertex. The cube example has eight vertices.</dd>
+ <dt><a href="qh-optq.htm#Qc">Qc</a> <a href="qh-opto.htm#p">p</a></dt>
+ <dd>print coordinates of vertices and coplanar points. The first line is the dimension.
+ The second line is the number of vertices and coplanar points. The coordinates
+ are next, one line per point. Use '<a href="qh-optq.htm#Qc">Qc</a> <a href="qh-optq.htm#Qi">Qi</a> p'
+ to print the coordinates of all points.</dd>
+ <dt>&nbsp;</dt>
+ <dt>&nbsp;</dt>
+ <dd><b>Facets</b></dd>
+ <dt><a href="qh-optf.htm#Fn">Fn</a></dt>
+ <dd>list neighboring facets for each facet. The first line is the
+ number of facets. Each remaining line starts with the number of
+ neighboring facets. The cube example has four neighbors per facet.</dd>
+ <dt><a href="qh-optf.htm#FN">FN</a></dt>
+ <dd>list neighboring facets for each point. The first line is the
+ total number of points. Each remaining line starts with the number of
+ neighboring facets. Each vertex of the cube example has three neighboring
+ facets. Use '<a href="qh-optq.htm#Qc">Qc</a> <a href="qh-optq.htm#Qi">Qi</a> FN'
+ to include coplanar and interior points. </dd>
+ <dt><a href="qh-optf.htm#Fa">Fa</a></dt>
+ <dd>print area for each facet. The first line is the number of facets.
+ Facet area follows, one line per facet. For the cube example, each facet has area one.</dd>
+ <dt><a href="qh-optf.htm#FI">FI</a></dt>
+ <dd>list facet IDs. The first line is the number of
+ facets. The IDs follow, one per line.</dd>
+
+ <dt>&nbsp;</dt>
+ <dt>&nbsp;</dt>
+ <dd><b>Coplanar and interior points</b></dd>
+ <dt><a href="qh-optf.htm#Fc">Fc</a></dt>
+ <dd>list coplanar points for each facet. The first line is the number
+ of facets. The remaining lines start with the number of coplanar points.
+ A coplanar point is assigned to one facet.</dd>
+ <dt><a href="qh-optq.htm#Qi">Qi</a> <a href="qh-optf.htm#Fc">Fc</a></dt>
+ <dd>list interior points for each facet. The first line is the number
+ of facets. The remaining lines start with the number of interior points.
+ A coplanar point is assigned to one facet.</dd>
+ <dt><a href="qh-optf.htm#FP">FP</a></dt>
+ <dd>print distance to nearest vertex for coplanar points. The first line is the
+ number of coplanar points. Each remaining line starts with the point ID of
+ a vertex, followed by the point ID of a coplanar point, its facet, and distance.
+ Use '<a href="qh-optq.htm#Qc">Qc</a> <a href="qh-optq.htm#Qi">Qi</a>
+ <a href="qh-optf.htm#FP">FP</a>' for coplanar and interior points.</dd>
+
+ <dt>&nbsp;</dt>
+ <dt>&nbsp;</dt>
+ <dd><b>Hyperplanes</b></dd>
+ <dt><a href="qh-opto.htm#n">n</a></dt>
+ <dd>print hyperplane for each facet. The first line is the dimension. The
+ second line is the number of facets. Each remaining line is the hyperplane's
+ coefficients followed by its offset.</dd>
+ <dt><a href="qh-optf.htm#Fo">Fo</a></dt>
+ <dd>print outer plane for each facet. The output plane is above all points.
+ The first line is the dimension. The
+ second line is the number of facets. Each remaining line is the outer plane's
+ coefficients followed by its offset.</dd>
+ <dt><a href="qh-optf.htm#Fi">Fi</a></dt>
+ <dd>print inner plane for each facet. The inner plane of a facet is
+ below its vertices.
+ The first line is the dimension. The
+ second line is the number of facets. Each remaining line is the inner plane's
+ coefficients followed by its offset.</dd>
+
+ <dt>&nbsp;</dt>
+ <dt>&nbsp;</dt>
+ <dd><b>General</b></dd>
+ <dt><a href="qh-opto.htm#s">s</a></dt>
+ <dd>print summary for the convex hull. Use '<a
+ href="qh-optf.htm#Fs">Fs</a>' and '<a
+ href="qh-optf.htm#FS">FS</a>' if you need numeric data.</dd>
+ <dt><a href="qh-optf.htm#FA">FA</a></dt>
+ <dd>compute total area and volume for '<a
+ href="qh-opto.htm#s">s</a>' and '<a href="qh-optf.htm#FS">FS</a>'</dd>
+ <dt><a href="qh-opto.htm#m">m</a></dt>
+ <dd>Mathematica output for the convex hull in 2-d or 3-d.</dd>
+ <dt><a href="qh-optf.htm#FM">FM</a></dt>
+ <dd>Maple output for the convex hull in 2-d or 3-d.</dd>
+ <dt><a href="qh-optg.htm#G">G</a></dt>
+ <dd>Geomview output for the convex hull in 2-d, 3-d, or 4-d.</dd>
+
+ <dt>&nbsp;</dt>
+ <dt>&nbsp;</dt>
+ <dd><b>Scaling and rotation</b></dd>
+ <dt><a href="qh-optq.htm#Qbk">Qbk:n</a></dt>
+ <dd>scale k'th coordinate to lower bound.</dd>
+ <dt><a href="qh-optq.htm#QBk">QBk:n</a></dt>
+ <dd>scale k'th coordinate to upper bound.</dd>
+ <dt><a href="qh-optq.htm#QbB">QbB</a></dt>
+ <dd>scale input to unit cube centered at the origin.</dd>
+ <dt><a href="qh-optq.htm#QRn">QRn</a></dt>
+ <dd>randomly rotate the input with a random seed of n. If n=0, the
+ seed is the time. If n=-1, use time for the random seed, but do
+ not rotate the input.</dd>
+ <dt><a href="qh-optq.htm#Qb0">Qbk:0Bk:0</a></dt>
+ <dd>remove k'th coordinate from input. This computes the
+ convex hull in one lower dimension.</dd>
+</dl>
+</blockquote>
+
+</blockquote>
+<h3><a href="#TOP">&#187;</a><a name="controls">qconvex controls</a></h3>
+<blockquote>
+
+<p>These options provide additional control:</p>
+
+<blockquote>
+<dl compact>
+ <dt><a href="qh-optq.htm#Qt">Qt</a></dt>
+ <dd>triangulated output. Qhull triangulates non-simplicial facets. It may produce
+ degenerate facets of zero area.</dd>
+ <dt><a href="qh-optq.htm#QJn">QJ</a></dt>
+ <dd>joggle the input instead of merging facets. This guarantees simplicial facets
+ (e.g., triangles in 3-d). It is less accurate than triangulated output ('Qt').</dd>
+ <dt><a href="qh-optq.htm#Qc">Qc</a></dt>
+ <dd>keep coplanar points</dd>
+ <dt><a href="qh-optq.htm#Qi">Qi</a></dt>
+ <dd>keep interior points</dd>
+ <dt><a href="qh-opto.htm#f">f </a></dt>
+ <dd>facet dump. Print the data structure for each facet.</dd>
+ <dt><a href="qh-optq.htm#QVn">QVn</a></dt>
+ <dd>select facets containing point <em>n</em> as a vertex,</dd>
+ <dt><a href="qh-optq.htm#QGn">QGn</a></dt>
+ <dd>select facets that are visible from point <em>n</em>
+ (marked 'good'). Use <em>-n</em> for the remainder.</dd>
+ <dt><a href="qh-optp.htm#PDk">PDk:0</a></dt>
+ <dd>select facets with a negative coordinate for dimension <i>k</i></dd>
+ <dt><a href="qh-optt.htm#TFn">TFn</a></dt>
+ <dd>report progress after constructing <em>n</em> facets</dd>
+ <dt><a href="qh-optt.htm#Tv">Tv</a></dt>
+ <dd>verify result</dd>
+ <dt><a href="qh-optt.htm#TO">TI file</a></dt>
+ <dd>input data from file. The filename may not use spaces or quotes.</dd>
+ <dt><a href="qh-optt.htm#TO">TO file</a></dt>
+ <dd>output results to file. Use single quotes if the filename
+ contains spaces (e.g., <tt>TO 'file with spaces.txt'</tt></dd>
+ <dt><a href="qh-optq.htm#Qs">Qs</a></dt>
+ <dd>search all points for the initial simplex. If Qhull can
+ not construct an initial simplex, it reports a
+descriptive message. Usually, the point set is degenerate and one
+or more dimensions should be removed ('<a href="qh-optq.htm#Qb0">Qbk:0Bk:0</a>').
+If not, use option 'Qs'. It performs an exhaustive search for the
+best initial simplex. This is expensive is high dimensions.</dd>
+</dl>
+</blockquote>
+
+</blockquote>
+<h3><a href="#TOP">&#187;</a><a name="graphics">qconvex graphics</a></h3>
+<blockquote>
+
+<p>Display 2-d, 3-d, and 4-d convex hulls with Geomview ('<a
+href="qh-optg.htm#G">G</a>').</p>
+
+<p>Display 2-d and 3-d convex hulls with Mathematica ('<a
+href="qh-opto.htm#m">m</a>').</p>
+
+<p>To view 4-d convex hulls in 3-d, use '<a
+href="qh-optp.htm#Pdk">Pd0d1d2d3</a>' to select the positive
+octant and '<a href="qh-optg.htm#GDn">GrD2</a>' to drop dimension
+2. </p>
+
+</blockquote>
+<h3><a href="#TOP">&#187;</a><a name="notes">qconvex notes</a></h3>
+<blockquote>
+
+<p>Qhull always computes a convex hull. The
+convex hull may be used for other geometric structures. The
+general technique is to transform the structure into an
+equivalent convex hull problem. For example, the Delaunay
+triangulation is equivalent to the convex hull of the input sites
+after lifting the points to a paraboloid.</p>
+
+</blockquote>
+<h3><a href="#TOP">&#187;</a><a name="conventions">qconvex
+conventions</a></h3>
+<blockquote>
+
+<p>The following terminology is used for convex hulls in Qhull.
+See <a href="index.htm#structure">Qhull's data structures</a>.</p>
+
+<ul>
+ <li><em>point</em> - <em>d</em> coordinates</li>
+ <li><em>vertex</em> - extreme point of the input set</li>
+ <li><em>ridge</em> - <i>d-1</i> vertices between two
+ neighboring facets</li>
+ <li><em>hyperplane</em> - halfspace defined by a unit normal
+ and offset</li>
+ <li><em>coplanar point</em> - a nearly incident point to a
+ hyperplane</li>
+ <li><em>centrum</em> - a point on the hyperplane for testing
+ convexity</li>
+ <li><em>facet</em> - a facet with vertices, ridges, coplanar
+ points, neighboring facets, and hyperplane</li>
+ <li><em>simplicial facet</em> - a facet with <em>d</em>
+ vertices, <em>d</em> ridges, and <em>d</em> neighbors</li>
+ <li><em>non-simplicial facet</em> - a facet with more than <em>d</em>
+ vertices</li>
+ <li><em>good facet</em> - a facet selected by '<a
+ href="qh-optq.htm#QVn">QVn</a>', etc.</li>
+</ul>
+</blockquote>
+<h3><a href="#TOP">&#187;</a><a name="options">qconvex options</a></h3>
+
+<pre>
+qconvex- compute the convex hull
+ http://www.qhull.org
+
+input (stdin):
+ first lines: dimension and number of points (or vice-versa).
+ other lines: point coordinates, best if one point per line
+ comments: start with a non-numeric character
+
+options:
+ Qt - triangulated output
+ QJ - joggle input instead of merging facets
+ Qc - keep coplanar points with nearest facet
+ Qi - keep interior points with nearest facet
+
+Qhull control options:
+ Qbk:n - scale coord k so that low bound is n
+ QBk:n - scale coord k so that upper bound is n (QBk is 0.5)
+ QbB - scale input to unit cube centered at the origin
+ Qbk:0Bk:0 - remove k-th coordinate from input
+ QJn - randomly joggle input in range [-n,n]
+ QRn - random rotation (n=seed, n=0 time, n=-1 time/no rotate)
+ Qs - search all points for the initial simplex
+ QGn - good facet if visible from point n, -n for not visible
+ QVn - good facet if it includes point n, -n if not
+
+Trace options:
+ T4 - trace at level n, 4=all, 5=mem/gauss, -1= events
+ Tc - check frequently during execution
+ Ts - print statistics
+ Tv - verify result: structure, convexity, and point inclusion
+ Tz - send all output to stdout
+ TFn - report summary when n or more facets created
+ TI file - input data from file, no spaces or single quotes
+ TO file - output results to file, may be enclosed in single quotes
+ TPn - turn on tracing when point n added to hull
+ TMn - turn on tracing at merge n
+ TWn - trace merge facets when width > n
+ TVn - stop qhull after adding point n, -n for before (see TCn)
+ TCn - stop qhull after building cone for point n (see TVn)
+
+Precision options:
+ Cn - radius of centrum (roundoff added). Merge facets if non-convex
+ An - cosine of maximum angle. Merge facets if cosine > n or non-convex
+ C-0 roundoff, A-0.99/C-0.01 pre-merge, A0.99/C0.01 post-merge
+ Rn - randomly perturb computations by a factor of [1-n,1+n]
+ Un - max distance below plane for a new, coplanar point
+ Wn - min facet width for outside point (before roundoff)
+
+Output formats (may be combined; if none, produces a summary to stdout):
+ f - facet dump
+ G - Geomview output (see below)
+ i - vertices incident to each facet
+ m - Mathematica output (2-d and 3-d)
+ n - normals with offsets
+ o - OFF file format (dim, points and facets; Voronoi regions)
+ p - point coordinates
+ s - summary (stderr)
+
+More formats:
+ Fa - area for each facet
+ FA - compute total area and volume for option 's'
+ Fc - count plus coplanar points for each facet
+ use 'Qc' (default) for coplanar and 'Qi' for interior
+ FC - centrum for each facet
+ Fd - use cdd format for input (homogeneous with offset first)
+ FD - use cdd format for numeric output (offset first)
+ FF - facet dump without ridges
+ Fi - inner plane for each facet
+ FI - ID for each facet
+ Fm - merge count for each facet (511 max)
+ FM - Maple output (2-d and 3-d)
+ Fn - count plus neighboring facets for each facet
+ FN - count plus neighboring facets for each point
+ Fo - outer plane (or max_outside) for each facet
+ FO - options and precision constants
+ FP - nearest vertex for each coplanar point
+ FQ - command used for qconvex
+ Fs - summary: #int (8), dimension, #points, tot vertices, tot facets,
+ for output: #vertices, #facets,
+ #coplanar points, #non-simplicial facets
+ #real (2), max outer plane, min vertex
+ FS - sizes: #int (0)
+ #real(2) tot area, tot volume
+ Ft - triangulation with centrums for non-simplicial facets (OFF format)
+ Fv - count plus vertices for each facet
+ FV - average of vertices (a feasible point for 'H')
+ Fx - extreme points (in order for 2-d)
+
+Geomview output (2-d, 3-d, and 4-d)
+ Ga - all points as dots
+ Gp - coplanar points and vertices as radii
+ Gv - vertices as spheres
+ Gi - inner planes only
+ Gn - no planes
+ Go - outer planes only
+ Gc - centrums
+ Gh - hyperplane intersections
+ Gr - ridges
+ GDn - drop dimension n in 3-d and 4-d output
+
+Print options:
+ PAn - keep n largest facets by area
+ Pdk:n - drop facet if normal[k] &lt;= n (default 0.0)
+ PDk:n - drop facet if normal[k] >= n
+ Pg - print good facets (needs 'QGn' or 'QVn')
+ PFn - keep facets whose area is at least n
+ PG - print neighbors of good facets
+ PMn - keep n facets with most merges
+ Po - force output. If error, output neighborhood of facet
+ Pp - do not report precision problems
+
+ . - list of all options
+ - - one line descriptions of all options
+
+</pre>
+
+<!-- Navigation links -->
+<hr>
+
+<p><b>Up:</b> <a href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b> <a href="index.htm#TOC">Qhull manual</a>: Table of Contents<br>
+<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
+&#149;<a href="qh-quick.htm#options">Options</a>
+&#149; <a href="qh-opto.htm#output">Output</a>
+&#149; <a href="qh-optf.htm#format">Formats</a>
+&#149; <a href="qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="qh-optp.htm#print">Print</a>
+&#149; <a href="qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="qh-optc.htm#prec">Precision</a>
+&#149; <a href="qh-optt.htm#trace">Trace</a>
+&#149; <a href="../src/libqhull_r/index.htm">Functions</a><br>
+<b>To:</b> <a href="#synopsis">sy</a>nopsis
+&#149; <a href="#input">in</a>put &#149; <a href="#outputs">ou</a>tputs
+&#149; <a href="#controls">co</a>ntrols &#149; <a href="#graphics">gr</a>aphics
+&#149; <a href="#notes">no</a>tes &#149; <a href="#conventions">co</a>nventions
+&#149; <a href="#options">op</a>tions
+<!-- GC common information -->
+<hr>
+
+<p><a href="http://www.geom.uiuc.edu/"><img src="qh--geom.gif"
+align="middle" width="40" height="40"></a><i>The Geometry Center
+Home Page </i></p>
+
+<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a>
+</a><br>
+Created: Sept. 25, 1995 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p>
+</body>
+</html>
diff --git a/xs/src/qhull/html/qdelau_f.htm b/xs/src/qhull/html/qdelau_f.htm
new file mode 100644
index 000000000..d8981e16b
--- /dev/null
+++ b/xs/src/qhull/html/qdelau_f.htm
@@ -0,0 +1,416 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+
+<head>
+<title>qdelaunay Qu -- furthest-site Delaunay triangulation</title>
+</head>
+
+<body>
+<!-- Navigation links -->
+<a name="TOP"><b>Up</b></a><b>:</b>
+<a href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b> <a href="index.htm#TOC">Qhull manual</a>: Table of Contents<br>
+<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
+&#149; <a href="qh-quick.htm#options">Options</a>
+&#149; <a href="qh-opto.htm#output">Output</a>
+&#149; <a href="qh-optf.htm#format">Formats</a>
+&#149; <a href="qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="qh-optp.htm#print">Print</a>
+&#149; <a href="qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="qh-optc.htm#prec">Precision</a>
+&#149; <a href="qh-optt.htm#trace">Trace</a>
+&#149; <a href="../src/libqhull_r/index.htm">Functions</a><br>
+<b>To:</b> <a href="#synopsis">sy</a>nopsis
+&#149; <a href="#input">in</a>put &#149; <a href="#outputs">ou</a>tputs
+&#149; <a href="#controls">co</a>ntrols &#149; <a href="#graphics">gr</a>aphics
+&#149; <a href="#notes">no</a>tes &#149; <a href="#conventions">co</a>nventions
+&#149; <a href="#options">op</a>tions
+
+<hr>
+<!-- Main text of document -->
+<h1><a
+href="http://www.geom.uiuc.edu/graphics/pix/Special_Topics/Computational_Geometry/delaunay.html"><img
+src="qh--dt.gif" alt="[delaunay]" align="middle" width="100"
+height="100"></a>qdelaunay Qu -- furthest-site Delaunay triangulation</h1>
+
+<p>The furthest-site Delaunay triangulation corresponds to the upper facets of the <a href="qdelaun.htm">Delaunay construction</a>.
+Its vertices are the
+extreme points of the input sites.
+It is the dual of the <a
+href="qvoron_f.htm">furthest-site Voronoi diagram</a>.
+
+<blockquote>
+<dl>
+ <dt><b>Example:</b> rbox 10 D2 | qdelaunay <a
+ href="qh-optq.htm#Qu">Qu</a> <a
+ href="qh-optq.htm#Qt">Qt</a> <a href="qh-opto.htm#s">s</a>
+ <a href="qh-opto.htm#i">i</a> <a href="qh-optt.htm#TO">TO
+ result</a></dt>
+ <dd>Compute the 2-d, furthest-site Delaunay triangulation of 10 random
+ points. Triangulate the output.
+ Write a summary to the console and the regions to
+ 'result'.</dd>
+ <dt>&nbsp;</dt>
+ <dt><b>Example:</b> rbox 10 D2 | qdelaunay <a
+ href="qh-optq.htm#Qu">Qu</a> <a
+ href="qh-optq.htm#QJn">QJ</a> <a href="qh-opto.htm#s">s</a>
+ <a href="qh-opto.htm#i">i</a> <a href="qh-optt.htm#TO">TO
+ result</a></dt>
+ <dd>Compute the 2-d, furthest-site Delaunay triangulation of 10 random
+ points. Joggle the input to guarantee triangular output.
+ Write a summary to the console and the regions to
+ 'result'.</dd>
+ <dt>&nbsp;</dt>
+ <dt><b>Example:</b> rbox r y c G1 D2 | qdelaunay <a
+ href="qh-optq.htm#Qu">Qu</a> <a href="qh-opto.htm#s">s</a>
+ <a href="qh-optf.htm#Fv">Fv</a> <a href="qh-optt.htm#TO">TO
+ result</a></dt>
+ <dd>Compute the 2-d, furthest-site Delaunay triangulation of a triangle inside
+ a square.
+ Write a summary to the console and unoriented regions to 'result'.
+ Merge regions for cocircular input sites (e.g., the square).
+ The square is the only furthest-site
+ Delaunay region.</dd>
+</dl>
+</blockquote>
+
+<p>As with the Delaunay triangulation, Qhull computes the
+furthest-site Delaunay triangulation by lifting the input sites to a
+paraboloid. The lower facets correspond to the Delaunay
+triangulation while the upper facets correspond to the
+furthest-site triangulation. Neither triangulation includes
+&quot;vertical&quot; facets (i.e., facets whose last hyperplane
+coefficient is nearly zero). Vertical facets correspond to input
+sites that are coplanar to the convex hull of the input. An
+example is points on the boundary of a lattice.</p>
+
+<p>By default, qdelaunay merges cocircular and cospherical regions.
+For example, the furthest-site Delaunay triangulation of a square inside a diamond
+('rbox D2 c d G4 | qdelaunay Qu') consists of one region (the diamond).
+
+<p>If you use '<a href="qh-optq.htm#Qt">Qt</a>' (triangulated output),
+all furthest-site Delaunay regions will be simplicial (e.g., triangles in 2-d).
+Some regions may be
+degenerate and have zero area.
+
+<p>If you use '<a href="qh-optq.htm#QJn">QJ</a>' (joggled input), all furthest-site
+Delaunay regions
+will be simplicial (e.g., triangles in 2-d). Joggled input
+is less accurate than triangulated output ('Qt'). See <a
+href="qh-impre.htm#joggle">Merged facets or joggled input</a>. </p>
+
+<p>The output for 3-d, furthest-site Delaunay triangulations may be confusing if the
+input contains cospherical data. See the FAQ item
+<a href=qh-faq.htm#extra>Why
+are there extra points in a 4-d or higher convex hull?</a>
+Avoid these problems with triangulated output ('<a href="qh-optq.htm#Qt">Qt</a>') or
+joggled input ('<a href="qh-optq.htm#QJn">QJ</a>').
+</p>
+
+<p>The 'qdelaunay' program is equivalent to
+'<a href=qhull.htm#outputs>qhull d</a> <a href=qh-optq.htm#Qbb>Qbb</a>' in 2-d to 3-d, and
+'<a href=qhull.htm#outputs>qhull d</a> <a href=qh-optq.htm#Qbb>Qbb</a> <a href=qh-optq.htm#Qx>Qx</a>'
+in 4-d and higher. It disables the following Qhull
+<a href=qh-quick.htm#options>options</a>: <i>d n v H U Qb QB Qc Qf Qg Qi
+Qm Qr QR Qv Qx TR E V FC Fi Fo Fp FV Q0,etc</i>.
+
+
+<p><b>Copyright &copy; 1995-2015 C.B. Barber</b></p>
+
+<hr>
+
+<h3><a href="#TOP">&#187;</a><a name="synopsis">furthest-site qdelaunay synopsis</a></h3>
+<blockquote>
+
+See <a href="qdelaun.htm#synopsis">qdelaunay synopsis</a>. The same
+program is used for both constructions. Use option '<a href="qh-optq.htm#Qu">Qu</a>'
+for furthest-site Delaunay triangulations.
+
+</blockquote>
+<h3><a href="#TOP">&#187;</a><a name="input">furthest-site qdelaunay
+input</a></h3>
+
+<blockquote>
+<p>The input data on <tt>stdin</tt> consists of:</p>
+<ul>
+ <li>dimension
+ <li>number of points</li>
+ <li>point coordinates</li>
+</ul>
+
+<p>Use I/O redirection (e.g., qdelaunay Qu &lt; data.txt), a pipe (e.g., rbox 10 | qdelaunay Qu),
+or the '<a href=qh-optt.htm#TI>TI</a>' option (e.g., qdelaunay Qu TI data.txt).
+
+<p>For example, this is a square containing four random points.
+Its furthest-site Delaunay
+triangulation contains one square.
+<p>
+<blockquote>
+<tt>rbox c 4 D2 &gt; data</tt>
+<blockquote><pre>
+2 RBOX c 4 D2
+8
+-0.4999921736307369 -0.3684622117955817
+0.2556053225468894 -0.0413498678629751
+0.0327672376602583 -0.2810408135699488
+-0.452955383763607 0.17886471718444
+ -0.5 -0.5
+ -0.5 0.5
+ 0.5 -0.5
+ 0.5 0.5
+</pre></blockquote>
+
+<p><tt>qdelaunay Qu i &lt; data</tt>
+<blockquote><pre>
+
+Furthest-site Delaunay triangulation by the convex hull of 8 points in 3-d:
+
+ Number of input sites: 8
+ Number of Delaunay regions: 1
+ Number of non-simplicial Delaunay regions: 1
+
+Statistics for: RBOX c 4 D2 | QDELAUNAY s Qu i
+
+ Number of points processed: 8
+ Number of hyperplanes created: 20
+ Number of facets in hull: 11
+ Number of distance tests for qhull: 34
+ Number of merged facets: 1
+ Number of distance tests for merging: 107
+ CPU seconds to compute hull (after input): 0.02
+
+1
+7 6 4 5
+</pre></blockquote>
+</blockquote>
+
+</blockquote>
+<h3><a href="#TOP">&#187;</a><a name="outputs">furthest-site qdelaunay
+outputs</a></h3>
+<blockquote>
+
+<p>These options control the output of furthest-site Delaunay triangulations:</p>
+<blockquote>
+
+<dl compact>
+ <dd><b>furthest-site Delaunay regions</b></dd>
+ <dt><a href="qh-opto.htm#i">i</a></dt>
+ <dd>list input sites for each furthest-site Delaunay region. The first line is the number of regions. The
+ remaining lines list the input sites for each region. The regions are
+ oriented. In 3-d and
+ higher, report cospherical sites by adding extra points. For the points-in-square example,
+ the square is the only furthest-site Delaunay region.</dd>
+ <dt><a href="qh-optf.htm#Fv">Fv</a></dt>
+ <dd>list input sites for each furthest-site Delaunay region. The first line is the number of regions.
+ Each remaining line starts with the number of input sites. The regions
+ are unoriented. For the points-in-square example,
+ the square is the only furthest-site Delaunay region.</dd>
+ <dt><a href="qh-optf.htm#Ft">Ft</a></dt>
+ <dd>print a triangulation of the furthest-site Delaunay regions in OFF format. The first line
+ is the dimension. The second line is the number of input sites and added points,
+ followed by the number of simplices and the number of ridges.
+ The input coordinates are next, followed by the centrum coordinates. There is
+ one centrum for each non-simplicial furthest-site Delaunay region. Each remaining line starts
+ with dimension+1. The
+ simplices are oriented.
+ For the points-in-square example, the square has a centrum at the
+ origin. It splits the square into four triangular regions.</dd>
+ <dt><a href="qh-optf.htm#Fn">Fn</a></dt>
+ <dd>list neighboring regions for each furthest-site Delaunay region. The first line is the
+ number of regions. Each remaining line starts with the number of
+ neighboring regions. Negative indices (e.g., <em>-1</em>) indicate regions
+ outside of the furthest-site Delaunay triangulation.
+ For the points-in-square example, the four neighboring regions
+ are outside of the triangulation. They belong to the regular
+ Delaunay triangulation.</dd>
+ <dt><a href="qh-optf.htm#FN">FN</a></dt>
+ <dd>list the furthest-site Delaunay regions for each input site. The first line is the
+ total number of input sites. Each remaining line starts with the number of
+ furthest-site Delaunay regions. Negative indices (e.g., <em>-1</em>) indicate regions
+ outside of the furthest-site Delaunay triangulation.
+ For the points-in-square example, the four random points belong to no region
+ while the square's vertices belong to region <em>0</em> and three
+ regions outside of the furthest-site Delaunay triangulation.</dd>
+ <dt><a href="qh-optf.htm#Fa">Fa</a></dt>
+ <dd>print area for each furthest-site Delaunay region. The first line is the number of regions.
+ The areas follow, one line per region. For the points-in-square example, the
+ square has unit area. </dd>
+
+ <dt>&nbsp;</dt>
+ <dt>&nbsp;</dt>
+ <dd><b>Input sites</b></dd>
+ <dt><a href="qh-optf.htm#Fx">Fx</a></dt>
+ <dd>list extreme points of the input sites. These points are vertices of the furthest-point
+ Delaunay triangulation. They are on the
+ boundary of the convex hull. The first line is the number of
+ extreme points. Each point is listed, one per line. The points-in-square example
+ has four extreme points.</dd>
+ <dt>&nbsp;</dt>
+ <dt>&nbsp;</dt>
+ <dd><b>General</b></dd>
+ <dt><a href="qh-optf.htm#FA">FA</a></dt>
+ <dd>compute total area for '<a href="qh-opto.htm#s">s</a>'
+ and '<a href="qh-optf.htm#FS">FS</a>'. This is the
+ same as the area of the convex hull.</dd>
+ <dt><a href="qh-opto.htm#o">o</a></dt>
+ <dd>print upper facets of the corresponding convex hull (a
+ paraboloid)</dd>
+ <dt><a href="qh-opto.htm#m">m</a></dt>
+ <dd>Mathematica output for the upper facets of the paraboloid (2-d triangulations).</dd>
+ <dt><a href="qh-optf.htm#FM">FM</a></dt>
+ <dd>Maple output for the upper facets of the paraboloid (2-d triangulations).</dd>
+ <dt><a href="qh-optg.htm#G">G</a></dt>
+ <dd>Geomview output for the paraboloid (2-d or 3-d triangulations).</dd>
+ <dt><a href="qh-opto.htm#s">s</a></dt>
+ <dd>print summary for the furthest-site Delaunay triangulation. Use '<a
+ href="qh-optf.htm#Fs">Fs</a>' and '<a
+ href="qh-optf.htm#FS">FS</a>' for numeric data.</dd>
+</dl>
+</blockquote>
+
+</blockquote>
+<h3><a href="#TOP">&#187;</a><a name="controls">furthest-site qdelaunay
+controls</a></h3>
+<blockquote>
+
+<p>These options provide additional control:</p>
+<blockquote>
+
+<dl compact>
+ <dt><a href="qh-optq.htm#Qu">Qu</a></dt>
+ <dd>must be used for furthest-site Delaunay triangulation.</dd>
+ <dt><a href="qh-optq.htm#Qt">Qt</a></dt>
+ <dd>triangulated output. Qhull triangulates non-simplicial facets. It may produce
+ degenerate facets of zero area.</dd>
+ <dt><a href="qh-optq.htm#QJn">QJ</a></dt>
+ <dd>joggle the input to avoid cospherical and coincident
+ sites. It is less accurate than triangulated output ('Qt').</dd>
+ <dt><a href="qh-optq.htm#QVn">QVn</a></dt>
+ <dd>select facets adjacent to input site <em>n</em> (marked
+ 'good').</dd>
+ <dt><a href="qh-optt.htm#Tv">Tv</a></dt>
+ <dd>verify result.</dd>
+ <dt><a href="qh-optt.htm#TO">TI file</a></dt>
+ <dd>input data from file. The filename may not use spaces or quotes.</dd>
+ <dt><a href="qh-optt.htm#TO">TO file</a></dt>
+ <dd>output results to file. Use single quotes if the filename
+ contains spaces (e.g., <tt>TO 'file with spaces.txt'</tt></dd>
+ <dt><a href="qh-optt.htm#TFn">TFn</a></dt>
+ <dd>report progress after constructing <em>n</em> facets</dd>
+ <dt><a href="qh-optp.htm#PDk">PDk:1</a></dt>
+ <dd>include upper and lower facets in the output. Set <em>k</em>
+ to the last dimension (e.g., 'PD2:1' for 2-d inputs). </dd>
+ <dt><a href="qh-opto.htm#f">f</a></dt>
+ <dd>facet dump. Print the data structure for each facet (i.e., furthest-site Delaunay region).</dd>
+</dl>
+</blockquote>
+
+</blockquote>
+<h3><a href="#TOP">&#187;</a><a name="graphics">furthest-site qdelaunay
+graphics</a></h3>
+<blockquote>
+
+See <a href="qdelaun.htm#graphics">Delaunay graphics</a>.
+They are the same except for Mathematica and Maple output.
+
+</blockquote>
+<h3><a href="#TOP">&#187;</a><a name="notes">furthest-site
+qdelaunay notes</a></h3>
+<blockquote>
+
+<p>The furthest-site Delaunay triangulation does not
+record coincident input sites. Use <tt>qdelaunay</tt> instead.
+
+<p><tt>qdelaunay Qu</tt> does not work for purely cocircular
+or cospherical points (e.g., rbox c | qdelaunay Qu). Instead,
+use <tt>qdelaunay Qz</tt> -- when all points are vertices of the convex
+hull of the input sites, the Delaunay triangulation is the same
+as the furthest-site Delaunay triangulation.
+
+<p>A non-simplicial, furthest-site Delaunay region indicates nearly cocircular or
+cospherical input sites. To avoid non-simplicial regions triangulate
+the output ('<a href="qh-optq.htm#Qt">Qt</a>') or joggle
+the input ('<a href="qh-optq.htm#QJn">QJ</a>'). Joggled input
+is less accurate than triangulated output.
+You may also triangulate
+non-simplicial regions with option '<a
+href="qh-optf.htm#Ft">Ft</a>'. It adds
+the centrum to non-simplicial regions. Alternatively, use an <a
+href="qh-impre.htm#exact">exact arithmetic code</a>.</p>
+
+<p>Furthest-site Delaunay triangulations do not include facets that are
+coplanar with the convex hull of the input sites. A facet is
+coplanar if the last coefficient of its normal is
+nearly zero (see <a href="../src/libqhull/user.h#ZEROdelaunay">qh_ZEROdelaunay</a>).
+
+</blockquote>
+<h3><a href="#TOP">&#187;</a><a name="conventions">furthest-site qdelaunay conventions</a></h3>
+<blockquote>
+
+<p>The following terminology is used for furthest-site Delaunay
+triangulations in Qhull. The underlying structure is the upper
+facets of a convex hull in one higher dimension. See <a
+href="qconvex.htm#conventions">convex hull conventions</a>, <a
+href="qdelaun.htm#conventions">Delaunay conventions</a>,
+and <a href="index.htm#structure">Qhull's data structures</a></p>
+<blockquote>
+<ul>
+ <li><em>input site</em> - a point in the input (one dimension
+ lower than a point on the convex hull)</li>
+ <li><em>point</em> - <i>d+1</i> coordinates. The last
+ coordinate is the sum of the squares of the input site's
+ coordinates</li>
+ <li><em>vertex</em> - a point on the paraboloid. It
+ corresponds to a unique input site. </li>
+ <li><em>furthest-site Delaunay facet</em> - an upper facet of the
+ paraboloid. The last coefficient of its normal is
+ clearly positive.</li>
+ <li><em>furthest-site Delaunay region</em> - a furthest-site Delaunay
+ facet projected to the input sites</li>
+ <li><em>non-simplicial facet</em> - more than <em>d</em>
+ points are cocircular or cospherical</li>
+ <li><em>good facet</em> - a furthest-site Delaunay facet with optional
+ restrictions by '<a href="qh-optq.htm#QVn">QVn</a>', etc.</li>
+</ul>
+</blockquote>
+</blockquote>
+<h3><a href="#TOP">&#187;</a><a name="options">furthest-site qdelaunay options</a></h3>
+<blockquote>
+
+See <a href="qdelaun.htm#options">qdelaunay options</a>. The same
+program is used for both constructions. Use option '<a href="qh-optq.htm#Qu">Qu</a>'
+for furthest-site Delaunay triangulations.
+
+</blockquote>
+<!-- Navigation links -->
+<hr>
+
+<p><b>Up:</b> <a href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b> <a href="index.htm#TOC">Qhull manual</a>: Table of Contents<br>
+<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
+&#149; <a href="qh-quick.htm#options">Options</a>
+&#149; <a href="qh-opto.htm#output">Output</a>
+&#149; <a href="qh-optf.htm#format">Formats</a>
+&#149; <a href="qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="qh-optp.htm#print">Print</a>
+&#149; <a href="qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="qh-optc.htm#prec">Precision</a>
+&#149; <a href="qh-optt.htm#trace">Trace</a>
+&#149; <a href="../src/libqhull_r/index.htm">Functions</a><br>
+<b>To:</b> <a href="#synopsis">sy</a>nopsis
+&#149; <a href="#input">in</a>put &#149; <a href="#outputs">ou</a>tputs
+&#149; <a href="#controls">co</a>ntrols &#149; <a href="#graphics">gr</a>aphics
+&#149; <a href="#notes">no</a>tes &#149; <a href="#conventions">co</a>nventions
+&#149; <a href="#options">op</a>tions
+<!-- GC common information -->
+<hr>
+
+<p><a href="http://www.geom.uiuc.edu/"><img src="qh--geom.gif"
+align="middle" width="40" height="40"></a><i>The Geometry Center
+Home Page </i></p>
+
+<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a>
+</a><br>
+Created: Sept. 25, 1995 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p>
+</body>
+</html>
diff --git a/xs/src/qhull/html/qdelaun.htm b/xs/src/qhull/html/qdelaun.htm
new file mode 100644
index 000000000..a42223c66
--- /dev/null
+++ b/xs/src/qhull/html/qdelaun.htm
@@ -0,0 +1,628 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+
+<head>
+<title>qdelaunay -- Delaunay triangulation</title>
+</head>
+
+<body>
+<!-- Navigation links -->
+<a name="TOP"><b>Up</b></a><b>:</b>
+<a href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b> <a href="index.htm#TOC">Qhull manual</a>: Table of Contents<br>
+<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
+&#149; <a href="qh-quick.htm#options">Options</a>
+&#149; <a href="qh-opto.htm#output">Output</a>
+&#149; <a href="qh-optf.htm#format">Formats</a>
+&#149; <a href="qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="qh-optp.htm#print">Print</a>
+&#149; <a href="qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="qh-optc.htm#prec">Precision</a>
+&#149; <a href="qh-optt.htm#trace">Trace</a>
+&#149; <a href="../src/libqhull_r/index.htm">Functions</a><br>
+<b>To:</b> <a href="#synopsis">sy</a>nopsis
+&#149; <a href="#input">in</a>put &#149; <a href="#outputs">ou</a>tputs
+&#149; <a href="#controls">co</a>ntrols &#149; <a href="#graphics">gr</a>aphics
+&#149; <a href="#notes">no</a>tes &#149; <a href="#conventions">co</a>nventions
+&#149; <a href="#options">op</a>tions
+
+<hr>
+<!-- Main text of document -->
+<h1><a
+href="http://www.geom.uiuc.edu/graphics/pix/Special_Topics/Computational_Geometry/delaunay.html"><img
+src="qh--dt.gif" alt="[delaunay]" align="middle" width="100"
+height="100"></a>qdelaunay -- Delaunay triangulation</h1>
+
+<p>The Delaunay triangulation is the triangulation with empty
+circumspheres. It has many useful properties and applications.
+See the survey article by Aurenhammer [<a
+href="index.htm#aure91">'91</a>] and the detailed introduction
+by O'Rourke [<a href="index.htm#orou94">'94</a>]. </p>
+
+<blockquote>
+<dl>
+ <dt><b>Example:</b> rbox r y c G0.1 D2 | qdelaunay <a href="qh-opto.htm#s">s</a>
+ <a href="qh-optf.htm#Fv">Fv</a> <a href="qh-optt.htm#TO">TO
+ result</a></dt>
+ <dd>Compute the 2-d Delaunay triangulation of a triangle and
+ a small square.
+ Write a summary to the console and unoriented regions to 'result'.
+ Merge regions for cocircular input sites (i.e., the
+ square).</dd>
+ <dt>&nbsp;</dt>
+ <dt><b>Example:</b> rbox r y c G0.1 D2 | qdelaunay <a href="qh-opto.htm#s">s</a>
+ <a href="qh-optf.htm#Fv">Fv</a> <a href="qh-optq.htm#Qt">Qt</a></dt>
+ <dd>Compute the 2-d Delaunay triangulation of a triangle and
+ a small square. Write a summary and unoriented
+ regions to the console. Produce triangulated output.</dd>
+ <dt>&nbsp;</dt>
+ <dt><b>Example:</b> rbox 10 D2 | qdelaunay <a
+ href="qh-optq.htm#QJn">QJ</a> <a href="qh-opto.htm#s">s</a>
+ <a href="qh-opto.htm#i">i</a> <a href="qh-optt.htm#TO">TO
+ result</a></dt>
+ <dd>Compute the 2-d Delaunay triangulation of 10 random
+ points. Joggle the input to guarantee triangular output.
+ Write a summary to the console and the regions to
+ 'result'.</dd>
+</dl>
+</blockquote>
+
+<p>Qhull computes the Delaunay triangulation by computing a
+convex hull. It lifts the input sites to a paraboloid by adding
+the sum of the squares of the coordinates. It scales the height
+of the paraboloid to improve numeric precision ('<a href=qh-optq.htm#Qbb>Qbb</a>').
+It computes the convex
+hull of the lifted sites, and projects the lower convex hull to
+the input.
+
+<p>Each region of the Delaunay triangulation
+corresponds to a facet of the lower half of the convex hull.
+Facets of the upper half of the convex hull correspond to the <a
+href="qdelau_f.htm">furthest-site Delaunay triangulation</a>.
+See the examples, <a href="qh-eg.htm#delaunay">Delaunay and
+Voronoi diagrams</a>.</p>
+
+<p>See <a href="http://www.qhull.org/html/qh-faq.htm#TOC">Qhull FAQ</a> - Delaunay and
+Voronoi diagram questions.</p>
+
+<p>By default, qdelaunay merges cocircular and cospherical regions.
+For example, the Delaunay triangulation of a square inside a diamond
+('rbox D2 c d G4 | qdelaunay') contains one region for the square.
+
+<p>Use option '<a href="qh-optq.htm#Qz">Qz</a>' if the input is circular, cospherical, or
+nearly so. It improves precision by adding a point "at infinity," above the corresponding paraboloid.
+
+<p>If you use '<a href="qh-optq.htm#Qt">Qt</a>' (triangulated output),
+all Delaunay regions will be simplicial (e.g., triangles in 2-d).
+Some regions may be
+degenerate and have zero area. Triangulated output identifies coincident
+points.
+
+<p>If you use '<a href="qh-optq.htm#QJn">QJ</a>' (joggled input), all Delaunay regions
+will be simplicial (e.g., triangles in 2-d). Coincident points will
+create small regions since the points are joggled apart. Joggled input
+is less accurate than triangulated output ('Qt'). See <a
+href="qh-impre.htm#joggle">Merged facets or joggled input</a>. </p>
+
+<p>The output for 3-d Delaunay triangulations may be confusing if the
+input contains cospherical data. See the FAQ item
+<a href=qh-faq.htm#extra>Why
+are there extra points in a 4-d or higher convex hull?</a>
+Avoid these problems with triangulated output ('<a href="qh-optq.htm#Qt">Qt</a>') or
+joggled input ('<a href="qh-optq.htm#QJn">QJ</a>').
+</p>
+
+<p>The 'qdelaunay' program is equivalent to
+'<a href=qhull.htm#outputs>qhull d</a> <a href=qh-optq.htm#Qbb>Qbb</a>' in 2-d to 3-d, and
+'<a href=qhull.htm#outputs>qhull d</a> <a href=qh-optq.htm#Qbb>Qbb</a> <a href=qh-optq.htm#Qx>Qx</a>'
+in 4-d and higher. It disables the following Qhull
+<a href=qh-quick.htm#options>options</a>: <i>d n v H U Qb QB Qc Qf Qg Qi
+Qm Qr QR Qv Qx TR E V FC Fi Fo Fp Ft FV Q0,etc</i>.
+
+
+<p><b>Copyright &copy; 1995-2015 C.B. Barber</b></p>
+
+<hr>
+
+<h3><a href="#TOP">&#187;</a><a name="synopsis">qdelaunay synopsis</a></h3>
+
+<pre>
+qdelaunay- compute the Delaunay triangulation.
+ input (stdin): dimension, number of points, point coordinates
+ comments start with a non-numeric character
+
+options (qdelaun.htm):
+ Qt - triangulated output
+ QJ - joggle input instead of merging facets
+ Qu - furthest-site Delaunay triangulation
+ Tv - verify result: structure, convexity, and in-circle test
+ . - concise list of all options
+ - - one-line description of all options
+
+output options (subset):
+ s - summary of results (default)
+ i - vertices incident to each Delaunay region
+ Fx - extreme points (vertices of the convex hull)
+ o - OFF format (shows the points lifted to a paraboloid)
+ G - Geomview output (2-d and 3-d points lifted to a paraboloid)
+ m - Mathematica output (2-d inputs lifted to a paraboloid)
+ QVn - print Delaunay regions that include point n, -n if not
+ TO file- output results to file, may be enclosed in single quotes
+
+examples:
+ rbox c P0 D2 | qdelaunay s o rbox c P0 D2 | qdelaunay i
+ rbox c P0 D3 | qdelaunay Fv Qt rbox c P0 D2 | qdelaunay s Qu Fv
+ rbox c G1 d D2 | qdelaunay s i rbox c G1 d D2 | qdelaunay s i Qt
+ rbox M3,4 z 100 D2 | qdelaunay s rbox M3,4 z 100 D2 | qdelaunay s Qt
+</pre>
+
+
+<h3><a href="#TOP">&#187;</a><a name="input">qdelaunay
+input</a></h3>
+
+<blockquote>
+<p>The input data on <tt>stdin</tt> consists of:</p>
+<ul>
+ <li>dimension
+ <li>number of points</li>
+ <li>point coordinates</li>
+</ul>
+
+<p>Use I/O redirection (e.g., qdelaunay &lt; data.txt), a pipe (e.g., rbox 10 | qdelaunay),
+or the '<a href=qh-optt.htm#TI>TI</a>' option (e.g., qdelaunay TI data.txt).
+
+<p>For example, this is four cocircular points inside a square. Its Delaunay
+triangulation contains 8 triangles and one four-sided
+figure.
+<p>
+<blockquote>
+<tt>rbox s 4 W0 c G1 D2 &gt; data</tt>
+<blockquote><pre>
+2 RBOX s 4 W0 c D2
+8
+-0.4941988586954018 -0.07594397977563715
+-0.06448037284989526 0.4958248496365813
+0.4911154367094632 0.09383830681375946
+-0.348353580869097 -0.3586778257652367
+ -1 -1
+ -1 1
+ 1 -1
+ 1 1
+</pre></blockquote>
+
+<p><tt>qdelaunay s i &lt; data</tt>
+<blockquote><pre>
+
+Delaunay triangulation by the convex hull of 8 points in 3-d
+
+ Number of input sites: 8
+ Number of Delaunay regions: 9
+ Number of non-simplicial Delaunay regions: 1
+
+Statistics for: RBOX s 4 W0 c D2 | QDELAUNAY s i
+
+ Number of points processed: 8
+ Number of hyperplanes created: 18
+ Number of facets in hull: 10
+ Number of distance tests for qhull: 33
+ Number of merged facets: 2
+ Number of distance tests for merging: 102
+ CPU seconds to compute hull (after input): 0.028
+
+9
+1 7 5
+6 3 4
+2 3 6
+7 2 6
+2 7 1
+0 5 4
+3 0 4
+0 1 5
+1 0 3 2
+</pre></blockquote>
+</blockquote>
+
+</blockquote>
+<h3><a href="#TOP">&#187;</a><a name="outputs">qdelaunay
+outputs</a></h3>
+<blockquote>
+
+<p>These options control the output of Delaunay triangulations:</p>
+<blockquote>
+
+<dl compact>
+ <dd><b>Delaunay regions</b></dd>
+ <dt><a href="qh-opto.htm#i">i</a></dt>
+ <dd>list input sites for each Delaunay region. The first line is the number of regions. The
+ remaining lines list the input sites for each region. The regions are
+ oriented. In 3-d and
+ higher, report cospherical sites by adding extra points. Use triangulated
+ output ('<a href="qh-optq.htm#Qt">Qt</a>') to avoid non-simpicial regions. For the circle-in-square example,
+ eight Delaunay regions are triangular and the ninth has four input sites.</dd>
+ <dt><a href="qh-optf.htm#Fv">Fv</a></dt>
+ <dd>list input sites for each Delaunay region. The first line is the number of regions.
+ Each remaining line starts with the number of input sites. The regions
+ are unoriented. For the circle-in-square example,
+ eight Delaunay regions are triangular and the ninth has four input sites.</dd>
+ <dt><a href="qh-optf.htm#Fn">Fn</a></dt>
+ <dd>list neighboring regions for each Delaunay region. The first line is the
+ number of regions. Each remaining line starts with the number of
+ neighboring regions. Negative indices (e.g., <em>-1</em>) indicate regions
+ outside of the Delaunay triangulation.
+ For the circle-in-square example, the four regions on the square are neighbors to
+ the region-at-infinity.</dd>
+ <dt><a href="qh-optf.htm#FN">FN</a></dt>
+ <dd>list the Delaunay regions for each input site. The first line is the
+ total number of input sites. Each remaining line starts with the number of
+ Delaunay regions. Negative indices (e.g., <em>-1</em>) indicate regions
+ outside of the Delaunay triangulation.
+ For the circle-in-square example, each point on the circle belongs to four
+ Delaunay regions. Use '<a href="qh-optq.htm#Qc">Qc</a> FN'
+ to include coincident input sites and deleted vertices. </dd>
+ <dt><a href="qh-optf.htm#Fa">Fa</a></dt>
+ <dd>print area for each Delaunay region. The first line is the number of regions.
+ The areas follow, one line per region. For the circle-in-square example, the
+ cocircular region has area 0.4. </dd>
+ <dt>&nbsp;</dt>
+ <dt>&nbsp;</dt>
+ <dd><b>Input sites</b></dd>
+ <dt><a href="qh-optf.htm#Fc">Fc</a></dt>
+ <dd>list coincident input sites for each Delaunay region.
+ The first line is the number of regions. The remaining lines start with
+ the number of coincident sites and deleted vertices. Deleted vertices
+ indicate highly degenerate input (see'<a href="qh-optf.htm#Fs">Fs</a>').
+ A coincident site is assigned to one Delaunay
+ region. Do not use '<a href="qh-optq.htm#QJn">QJ</a>' with 'Fc'; the joggle will separate
+ coincident sites.</dd>
+ <dt><a href="qh-optf.htm#FP">FP</a></dt>
+ <dd>print coincident input sites with distance to
+ nearest site (i.e., vertex). The first line is the
+ number of coincident sites. Each remaining line starts with the point ID of
+ an input site, followed by the point ID of a coincident point, its region, and distance.
+ Includes deleted vertices which
+ indicate highly degenerate input (see'<a href="qh-optf.htm#Fs">Fs</a>').
+ Do not use '<a href="qh-optq.htm#QJn">QJ</a>' with 'FP'; the joggle will separate
+ coincident sites.</dd>
+ <dt><a href="qh-optf.htm#Fx">Fx</a></dt>
+ <dd>list extreme points of the input sites. These points are on the
+ boundary of the convex hull. The first line is the number of
+ extreme points. Each point is listed, one per line. The circle-in-square example
+ has four extreme points.</dd>
+ <dt>&nbsp;</dt>
+ <dt>&nbsp;</dt>
+ <dd><b>General</b></dd>
+ <dt><a href="qh-optf.htm#FA">FA</a></dt>
+ <dd>compute total area for '<a href="qh-opto.htm#s">s</a>'
+ and '<a href="qh-optf.htm#FS">FS</a>'</dd>
+ <dt><a href="qh-opto.htm#o">o</a></dt>
+ <dd>print lower facets of the corresponding convex hull (a
+ paraboloid)</dd>
+ <dt><a href="qh-opto.htm#m">m</a></dt>
+ <dd>Mathematica output for the lower facets of the paraboloid (2-d triangulations).</dd>
+ <dt><a href="qh-optf.htm#FM">FM</a></dt>
+ <dd>Maple output for the lower facets of the paraboloid (2-d triangulations).</dd>
+ <dt><a href="qh-optg.htm#G">G</a></dt>
+ <dd>Geomview output for the paraboloid (2-d or 3-d triangulations).</dd>
+ <dt><a href="qh-opto.htm#s">s</a></dt>
+ <dd>print summary for the Delaunay triangulation. Use '<a
+ href="qh-optf.htm#Fs">Fs</a>' and '<a
+ href="qh-optf.htm#FS">FS</a>' for numeric data.</dd>
+</dl>
+</blockquote>
+
+</blockquote>
+<h3><a href="#TOP">&#187;</a><a name="controls">qdelaunay
+controls</a></h3>
+<blockquote>
+
+<p>These options provide additional control:</p>
+<blockquote>
+
+<dl compact>
+ <dt><a href="qh-optq.htm#Qt">Qt</a></dt>
+ <dd>triangulated output. Qhull triangulates non-simplicial facets. It may produce
+degenerate facets of zero area.</dd>
+ <dt><a href="qh-optq.htm#QJn">QJ</a></dt>
+ <dd>joggle the input to avoid cospherical and coincident
+ sites. It is less accurate than triangulated output ('Qt').</dd>
+ <dt><a href="qh-optq.htm#Qu">Qu</a></dt>
+ <dd>compute the <a href="qdelau_f.htm">furthest-site Delaunay triangulation</a>.</dd>
+ <dt><a href="qh-optq.htm#Qz">Qz</a></dt>
+ <dd>add a point above the paraboloid to reduce precision
+ errors. Use it for nearly cocircular/cospherical input
+ (e.g., 'rbox c | qdelaunay Qz'). The point is printed for
+ options '<a href="qh-optf.htm#Ft">Ft</a>' and '<a
+ href="qh-opto.htm#o">o</a>'.</dd>
+ <dt><a href="qh-optq.htm#QVn">QVn</a></dt>
+ <dd>select facets adjacent to input site <em>n</em> (marked
+ 'good').</dd>
+ <dt><a href="qh-optt.htm#Tv">Tv</a></dt>
+ <dd>verify result.</dd>
+ <dt><a href="qh-optt.htm#TO">TI file</a></dt>
+ <dd>input data from file. The filename may not use spaces or quotes.</dd>
+ <dt><a href="qh-optt.htm#TO">TO file</a></dt>
+ <dd>output results to file. Use single quotes if the filename
+ contains spaces (e.g., <tt>TO 'file with spaces.txt'</tt></dd>
+ <dt><a href="qh-optt.htm#TFn">TFn</a></dt>
+ <dd>report progress after constructing <em>n</em> facets</dd>
+ <dt><a href="qh-optp.htm#PDk">PDk:1</a></dt>
+ <dd>include upper and lower facets in the output. Set <em>k</em>
+ to the last dimension (e.g., 'PD2:1' for 2-d inputs). </dd>
+ <dt><a href="qh-opto.htm#f">f</a></dt>
+ <dd>facet dump. Print the data structure for each facet (i.e., Delaunay region).</dd>
+</dl>
+</blockquote>
+
+</blockquote>
+<h3><a href="#TOP">&#187;</a><a name="graphics">qdelaunay
+graphics</a></h3>
+<blockquote>
+
+<p>For 2-d and 3-d Delaunay triangulations, Geomview ('qdelaunay <a
+href="qh-optg.htm#G">G</a>') displays the corresponding convex
+hull (a paraboloid). </p>
+
+<p>To view a 2-d Delaunay triangulation, use 'qdelaunay <a
+href="qh-optg.htm#GDn">GrD2</a>' to drop the last dimension. This
+is the same as viewing the hull without perspective (see
+Geomview's 'cameras' menu). </p>
+
+<p>To view a 3-d Delaunay triangulation, use 'qdelaunay <a
+href="qh-optg.htm#GDn">GrD3</a>' to drop the last dimension. You
+may see extra edges. These are interior edges that Geomview moves
+towards the viewer (see 'lines closer' in Geomview's camera
+options). Use option '<a href="qh-optg.htm#Gt">Gt</a>' to make
+the outer ridges transparent in 3-d. See <a
+href="qh-eg.htm#delaunay">Delaunay and Voronoi examples</a>.</p>
+
+<p>For 2-d Delaunay triangulations, Mathematica ('<a
+href="qh-opto.htm#m">m</a>') and Maple ('<a
+href="qh-optf.htm#FM">FM</a>') output displays the lower facets of the corresponding convex
+hull (a paraboloid). </p>
+
+<p>For 2-d, furthest-site Delaunay triangulations, Maple and Mathematica output ('<a
+href="qh-optq.htm#Qu">Qu</a> <a
+href="qh-opto.htm#m">m</a>') displays the upper facets of the corresponding convex
+hull (a paraboloid). </p>
+
+</blockquote>
+<h3><a href="#TOP">&#187;</a><a name="notes">qdelaunay
+notes</a></h3>
+<blockquote>
+
+<p>You can simplify the Delaunay triangulation by enclosing the input
+sites in a large square or cube. This is particularly recommended
+for cocircular or cospherical input data.
+
+<p>A non-simplicial Delaunay region indicates nearly cocircular or
+cospherical input sites. To avoid non-simplicial regions either triangulate
+the output ('<a href="qh-optq.htm#Qt">Qt</a>') or joggle
+the input ('<a href="qh-optq.htm#QJn">QJ</a>'). Triangulated output
+is more accurate than joggled input. Alternatively, use an <a
+href="qh-impre.htm#exact">exact arithmetic code</a>.</p>
+
+<p>Delaunay triangulations do not include facets that are
+coplanar with the convex hull of the input sites. A facet is
+coplanar if the last coefficient of its normal is
+nearly zero (see <a href="../src/libqhull/user.h#ZEROdelaunay">qh_ZEROdelaunay</a>).
+
+<p>See <a href=qh-impre.htm#delaunay>Imprecision issues :: Delaunay triangulations</a>
+for a discussion of precision issues. Deleted vertices indicate
+highly degenerate input. They are listed in the summary output and
+option '<a href="qh-optf.htm#Fs">Fs</a>'.</p>
+
+<p>To compute the Delaunay triangulation of points on a sphere,
+compute their convex hull. If the sphere is the unit sphere at
+the origin, the facet normals are the Voronoi vertices of the
+input. The points may be restricted to a hemisphere. [S. Fortune]
+</p>
+
+<p>The 3-d Delaunay triangulation of regular points on a half
+spiral (e.g., 'rbox 100 l | qdelaunay') has quadratic size, while the Delaunay triangulation
+of random 3-d points is
+approximately linear for reasonably sized point sets.
+
+<p>With the <a href="qh-code.htm#library">Qhull library</a>, you
+can use <tt>qh_findbestfacet</tt> in <tt>poly2.c</tt> to locate the facet
+that contains a point. You should first lift the point to the
+paraboloid (i.e., the last coordinate is the sum of the squares
+of the point's coordinates -- <tt>qh_setdelaunay</tt>). Do not use options
+'<a href="qh-optq.htm#Qbb">Qbb</a>', '<a href="qh-optq.htm#QbB">QbB</a>',
+'<a href="qh-optq.htm#Qbk">Qbk:n</a>', or '<a
+href="qh-optq.htm#QBk">QBk:n</a>' since these scale the last
+coordinate. </p>
+
+<p>If a point is interior to the convex hull of the input set, it
+is interior to the adjacent vertices of the Delaunay
+triangulation. This is demonstrated by the following pipe for
+point 0:
+
+<pre>
+ qdelaunay &lt;data s FQ QV0 p | qconvex s Qb3:0B3:0 p
+</pre>
+
+<p>The first call to qdelaunay returns the neighboring points of
+point 0 in the Delaunay triangulation. The second call to qconvex
+returns the vertices of the convex hull of these points (after
+dropping the lifted coordinate). If point 0 is interior to the
+original point set, it is interior to the reduced point set. </p>
+
+</blockquote>
+<h3><a href="#TOP">&#187;</a><a name="conventions">qdelaunay conventions</a></h3>
+<blockquote>
+
+<p>The following terminology is used for Delaunay triangulations
+in Qhull for dimension <i>d</i>. The underlying structure is the
+lower facets of a convex hull in dimension <i>d+1</i>. For
+further information, see <a href="index.htm#structure">data
+structures</a> and <a href="qconvex.htm#conventions">convex hull
+conventions</a>.</p>
+<blockquote>
+<ul>
+ <li><em>input site</em> - a point in the input (one dimension
+ lower than a point on the convex hull)</li>
+ <li><em>point</em> - a point has <i>d+1</i> coordinates. The
+ last coordinate is the sum of the squares of the input
+ site's coordinates</li>
+ <li><em>coplanar point</em> - a <em>coincident</em>
+ input site or a deleted vertex. Deleted vertices
+ indicate highly degenerate input.</li>
+ <li><em>vertex</em> - a point on the paraboloid. It
+ corresponds to a unique input site. </li>
+ <li><em>point-at-infinity</em> - a point added above the
+ paraboloid by option '<a href="qh-optq.htm#Qz">Qz</a>'</li>
+ <li><em>lower facet</em> - a facet corresponding to a
+ Delaunay region. The last coefficient of its normal is
+ clearly negative.</li>
+ <li><em>upper facet</em> - a facet corresponding to a
+ furthest-site Delaunay region. The last coefficient of
+ its normal is clearly positive. </li>
+ <li><em>Delaunay region</em> - a
+ lower facet projected to the input sites</li>
+ <li><em>upper Delaunay region</em> - an upper facet projected
+ to the input sites</li>
+ <li><em>non-simplicial facet</em> - more than <em>d</em>
+ input sites are cocircular or cospherical</li>
+ <li><em>good facet</em> - a Delaunay region with optional
+ restrictions by '<a href="qh-optq.htm#QVn">QVn</a>', etc.</li>
+</ul>
+</blockquote>
+</blockquote>
+<h3><a href="#TOP">&#187;</a><a name="options">qdelaunay options</a></h3>
+
+<pre>
+qdelaunay- compute the Delaunay triangulation
+ http://www.qhull.org
+
+input (stdin):
+ first lines: dimension and number of points (or vice-versa).
+ other lines: point coordinates, best if one point per line
+ comments: start with a non-numeric character
+
+options:
+ Qt - triangulated output
+ QJ - joggle input instead of merging facets
+ Qu - compute furthest-site Delaunay triangulation
+
+Qhull control options:
+ QJn - randomly joggle input in range [-n,n]
+ Qs - search all points for the initial simplex
+ Qz - add point-at-infinity to Delaunay triangulation
+ QGn - print Delaunay region if visible from point n, -n if not
+ QVn - print Delaunay regions that include point n, -n if not
+
+Trace options:
+ T4 - trace at level n, 4=all, 5=mem/gauss, -1= events
+ Tc - check frequently during execution
+ Ts - print statistics
+ Tv - verify result: structure, convexity, and in-circle test
+ Tz - send all output to stdout
+ TFn - report summary when n or more facets created
+ TI file - input data from file, no spaces or single quotes
+ TO file - output results to file, may be enclosed in single quotes
+ TPn - turn on tracing when point n added to hull
+ TMn - turn on tracing at merge n
+ TWn - trace merge facets when width > n
+ TVn - stop qhull after adding point n, -n for before (see TCn)
+ TCn - stop qhull after building cone for point n (see TVn)
+
+Precision options:
+ Cn - radius of centrum (roundoff added). Merge facets if non-convex
+ An - cosine of maximum angle. Merge facets if cosine > n or non-convex
+ C-0 roundoff, A-0.99/C-0.01 pre-merge, A0.99/C0.01 post-merge
+ Rn - randomly perturb computations by a factor of [1-n,1+n]
+ Wn - min facet width for outside point (before roundoff)
+
+Output formats (may be combined; if none, produces a summary to stdout):
+ f - facet dump
+ G - Geomview output (see below)
+ i - vertices incident to each Delaunay region
+ m - Mathematica output (2-d only, lifted to a paraboloid)
+ o - OFF format (dim, points, and facets as a paraboloid)
+ p - point coordinates (lifted to a paraboloid)
+ s - summary (stderr)
+
+More formats:
+ Fa - area for each Delaunay region
+ FA - compute total area for option 's'
+ Fc - count plus coincident points for each Delaunay region
+ Fd - use cdd format for input (homogeneous with offset first)
+ FD - use cdd format for numeric output (offset first)
+ FF - facet dump without ridges
+ FI - ID of each Delaunay region
+ Fm - merge count for each Delaunay region (511 max)
+ FM - Maple output (2-d only, lifted to a paraboloid)
+ Fn - count plus neighboring region for each Delaunay region
+ FN - count plus neighboring region for each point
+ FO - options and precision constants
+ FP - nearest point and distance for each coincident point
+ FQ - command used for qdelaunay
+ Fs - summary: #int (8), dimension, #points, tot vertices, tot facets,
+ for output: #vertices, #Delaunay regions,
+ #coincident points, #non-simplicial regions
+ #real (2), max outer plane, min vertex
+ FS - sizes: #int (0)
+ #real (2), tot area, 0
+ Fv - count plus vertices for each Delaunay region
+ Fx - extreme points of Delaunay triangulation (on convex hull)
+
+Geomview options (2-d and 3-d)
+ Ga - all points as dots
+ Gp - coplanar points and vertices as radii
+ Gv - vertices as spheres
+ Gi - inner planes only
+ Gn - no planes
+ Go - outer planes only
+ Gc - centrums
+ Gh - hyperplane intersections
+ Gr - ridges
+ GDn - drop dimension n in 3-d and 4-d output
+ Gt - transparent outer ridges to view 3-d Delaunay
+
+Print options:
+ PAn - keep n largest Delaunay regions by area
+ Pdk:n - drop facet if normal[k] &lt;= n (default 0.0)
+ PDk:n - drop facet if normal[k] >= n
+ Pg - print good Delaunay regions (needs 'QGn' or 'QVn')
+ PFn - keep Delaunay regions whose area is at least n
+ PG - print neighbors of good regions (needs 'QGn' or 'QVn')
+ PMn - keep n Delaunay regions with most merges
+ Po - force output. If error, output neighborhood of facet
+ Pp - do not report precision problems
+
+ . - list of all options
+ - - one line descriptions of all options
+</pre>
+
+<!-- Navigation links -->
+<hr>
+
+<p><b>Up:</b> <a href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b> <a href="index.htm#TOC">Qhull manual</a>: Table of Contents<br>
+<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
+&#149; <a href="qh-quick.htm#options">Options</a>
+&#149; <a href="qh-opto.htm#output">Output</a>
+&#149; <a href="qh-optf.htm#format">Formats</a>
+&#149; <a href="qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="qh-optp.htm#print">Print</a>
+&#149; <a href="qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="qh-optc.htm#prec">Precision</a>
+&#149; <a href="qh-optt.htm#trace">Trace</a>
+&#149; <a href="../src/libqhull_r/index.htm">Functions</a><br>
+<b>To:</b> <a href="#synopsis">sy</a>nopsis
+&#149; <a href="#input">in</a>put &#149; <a href="#outputs">ou</a>tputs
+&#149; <a href="#controls">co</a>ntrols &#149; <a href="#graphics">gr</a>aphics
+&#149; <a href="#notes">no</a>tes &#149; <a href="#conventions">co</a>nventions
+&#149; <a href="#options">op</a>tions
+<!-- GC common information -->
+<hr>
+
+<p><a href="http://www.geom.uiuc.edu/"><img src="qh--geom.gif"
+align="middle" width="40" height="40"></a><i>The Geometry Center
+Home Page </i></p>
+
+<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a>
+</a><br>
+Created: Sept. 25, 1995 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p>
+</body>
+</html>
diff --git a/xs/src/qhull/html/qh--4d.gif b/xs/src/qhull/html/qh--4d.gif
new file mode 100644
index 000000000..08be18c8a
--- /dev/null
+++ b/xs/src/qhull/html/qh--4d.gif
Binary files differ
diff --git a/xs/src/qhull/html/qh--cone.gif b/xs/src/qhull/html/qh--cone.gif
new file mode 100644
index 000000000..78470ee8f
--- /dev/null
+++ b/xs/src/qhull/html/qh--cone.gif
Binary files differ
diff --git a/xs/src/qhull/html/qh--dt.gif b/xs/src/qhull/html/qh--dt.gif
new file mode 100644
index 000000000..b6d4e2672
--- /dev/null
+++ b/xs/src/qhull/html/qh--dt.gif
Binary files differ
diff --git a/xs/src/qhull/html/qh--geom.gif b/xs/src/qhull/html/qh--geom.gif
new file mode 100644
index 000000000..9c70b5499
--- /dev/null
+++ b/xs/src/qhull/html/qh--geom.gif
Binary files differ
diff --git a/xs/src/qhull/html/qh--half.gif b/xs/src/qhull/html/qh--half.gif
new file mode 100644
index 000000000..80a4d0883
--- /dev/null
+++ b/xs/src/qhull/html/qh--half.gif
Binary files differ
diff --git a/xs/src/qhull/html/qh--rand.gif b/xs/src/qhull/html/qh--rand.gif
new file mode 100644
index 000000000..4d4e4daee
--- /dev/null
+++ b/xs/src/qhull/html/qh--rand.gif
Binary files differ
diff --git a/xs/src/qhull/html/qh-code.htm b/xs/src/qhull/html/qh-code.htm
new file mode 100644
index 000000000..fff7faddb
--- /dev/null
+++ b/xs/src/qhull/html/qh-code.htm
@@ -0,0 +1,1062 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+
+<head>
+<title>Qhull code</title>
+<!-- Navigation links -->
+</head>
+
+<body>
+
+<p><a name="TOP"><b>Up:</b></a> <a
+href="http://www.qhull.org">Home page for Qhull</a>
+<br>
+<b>Up:</b> <a href="index.htm#TOC">Qhull manual: Table of
+Contents</a><br>
+<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
+&#149; <a href="qh-quick.htm#options">Options</a>
+&#149; <a href="qh-opto.htm#output">Output</a>
+&#149; <a href="qh-optf.htm#format">Formats</a>
+&#149; <a href="qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="qh-optp.htm#print">Print</a>
+&#149; <a href="qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="qh-optc.htm#prec">Precision</a>
+&#149; <a href="qh-optt.htm#trace">Trace</a>
+&#149; <a href="../src/libqhull_r/index.htm">Functions</a><br>
+<b>To:</b> <a href="#TOC">Qhull code</a>: Table of Contents
+(please wait while loading) <br>
+<b>Dn:</b> <a href="../src/libqhull_r/index.htm">Qhull functions</a>, macros, and data
+structures
+</p>
+
+<hr>
+<!-- Main text of document -->
+<h1><a
+href="http://www.geom.uiuc.edu/graphics/pix/Special_Topics/Computational_Geometry/4dcube.html"><img
+src="qh--4d.gif" alt="[4-d cube]" align="middle" width="100"
+height="100"></a> Qhull code</h1>
+
+<p>This section discusses the code for Qhull. </p>
+
+<p><b>Copyright &copy; 1995-2015 C.B. Barber</b></p>
+
+<hr>
+
+<h2><a href="#TOP">&#187;</a><a name="TOC">Qhull code: Table of
+Contents </a></h2>
+
+<ul>
+ <li><a href="#reentrant">Reentrant</a> Qhull
+ <li><a href="#convert">How to convert</a> code to reentrant Qhull
+ <li><a href="#64bit">Qhull</a> on 64-bit computers
+ <li><a href="#cpp">Calling</a> Qhull from C++ programs
+ <ul>
+ <li><a href="#questions-cpp">Cpp questions for Qhull</a></li>
+ <li><a href="#coordinate-cpp">CoordinateIterator</a></li>
+ <li><a href="#qhull-cpp">Qhull</a></li>
+ <li><a href="#error-cpp">QhullError</a></li>
+ <li><a href="#facet-cpp">QhullFacet</a></li>
+ <li><a href="#facetlist-cpp">QhullFacetList</a></li>
+ <li><a href="#facetset-cpp">QhullFacetSet</a></li>
+ <li><a href="#iterator-cpp">QhullIterator</a></li>
+ <li><a href="#linkedlist-cpp">QhullLinkedList</a></li>
+ <li><a href="#point-cpp">QhullPoint</a></li>
+ <li><a href="#qh-cpp">QhullQh</a></li>
+ <li><a href="#pointset-cpp">QhullPointSet</a></li>
+ <li><a href="#ridge-cpp">QhullRidge</a></li>
+ <li><a href="#ridgeset-cpp">QhullRidgeSet</a></li>
+ <li><a href="#set-cpp">QhullSet</a></li>
+ <li><a href="#vertex-cpp">QhullVertex</a></li>
+ <li><a href="#vertexlist-cpp">QhullVertexList</a></li>
+ <li><a href="#vertexset-cpp">QhullVertexSet</a></li>
+ <li><a href="#rbox-cpp">RboxPoints</a></li>
+ </ul>
+ <li><a href="#library">Calling</a> Qhull from C programs
+ <ul>
+ <li><a href="#exit">How to avoid</a> exit(), fprintf(), stderr, and stdout</li>
+ <li><a href="#constrained">Constrained Delaunay</a>
+ triangulation</li>
+ <li><a href="#dids">Delaunay triangulations</a> and point indices</li>
+ <li><a href="#findfacet">Locate facet</a> with
+ qh_findbestfacet()</li>
+ <li><a href="#inc">On-line construction</a> with
+ qh_addpoint()</li>
+ <li><a href="#mem">Sets and quick memory</a> allocation</li>
+ <li><a href="#tricoplanar">Tricoplanar facets</a> and option 'Qt'</li>
+ <li><a href="#vneighbor">Vertex neighbors</a> of a vertex</li>
+ <li><a href="#vertices">Voronoi vertices</a> of a region</li>
+ <li><a href="#ridge">Voronoi vertices</a> of a ridge</li>
+ </ul>
+ </li>
+ <li><a href="#performance">Performance</a> of Qhull</li>
+ <li><a href="#enhance">Enhancements</a> to Qhull</li>
+ <li><a href="../src/libqhull_r/index.htm">Qhull</a> functions, macros, and data
+ structures </li>
+</ul>
+
+<hr>
+
+<h2><a href="#TOC">&#187;</a><a name="reentrant">Reentrant Qhull</a></h2>
+
+<p>Qhull-2015 introduces reentrant Qhull (libqhull_r). Reentrant Qhull uses a qhT* argument instead of global data structures.
+The qhT* pointer is the first argument to most Qhull routines. It allows multiple instances of Qhull to run at the same time.
+It simplifies the C++ interface to Qhull.
+
+<p>New code should be written with libqhull_r. Existing users of libqhull should consider converting to libqhull_r.
+Although libqhull will be supported indefinitely, improvements may not be implemented.
+Reentrant qhull is 1-2% slower than non-reentrant qhull.
+
+<p><b>Note:</b> Reentrant Qhull is <i>not</i> thread safe. Do not invoke Qhull routines with the same qhT* pointer from multiple threads.
+
+<h2><a href="#TOC">&#187;</a><a name="convert">How to convert</a> code to reentrant Qhull</h2>
+
+<p>C++ users need to convert to libqhull_r.
+The new C++ interface does a better, but not perfect, job of hiding Qhull's C data structures.
+The previous C++ interface was unusual due to Qhull's global data structures.
+
+<p>All other users should consider converting to libqhull_r. The conversion is straight forward.
+The original conversion of libqhull to libqhull_r required thousands of changes, mostly global
+search and replace. The first run of Qhull (unix_r.c) produced the same
+output, and nearly the same log files, as the original (unix.c).
+
+<p>Suggestions to help with conversion.
+<ul>
+<li>Compare qconvex_r.c with qconvex.c. Define a qhT object and a pointer it. The qhT* pointer is the first argument to most Qhull functions.
+Clear <tt>qh_qh-&lt;NOerrext</tt> before calling qh_initflags(). Invoke QHULL_LIB_CHECK to check for a compatible Qhull library.
+<li>Compare user_eg2_r.c with user_eg2.c
+<li>Compare user_eg_r.c with user_eg.c. If you use qhT before invoking qh_init_A, call qh_zero() to clear the qhT object.
+user_eg_r.c includes multiple Qhull runs.
+<li>Review user_eg3_r.cpp. As with the other programs, invoke QHULL_LIB_CHECK.
+Simple C++ programs should compile as is.
+<li>Compare QhullFacet.cpp with the same file in Qhull-2012.1. UsingLibQhull was replaced with the macro QH_TRY_() and '<tt>qh_qh-&lt;NOerrext= true</tt>'.
+<li>For detailed notes on libqhull_r, see "libqhull_r (reentrant Qhull)" and "Source code changes for libqhull_r" in <a href="../src/Changes.txt">Changes.txt</a>.
+<li>For detailed notes on libqhullcpp, see "C++ interface" and following sections in <a href="../src/Changes.txt">Changes.txt</a>.
+<li>For regexps and conversion notes, see <a href="http://www.qhull.org/html/README_r.txt">README_r.txt</a> (unedited).
+</ul>
+
+<h2><a href="#TOC">&#187;</a><a name="64bit">Qhull on 64-bit computers</a></h2>
+
+<p>Qhull compiles for 64-bit hosts. Since the size of a pointer on a 64-bit host is double the size on a 32-bit host,
+memory consumption increases about 50% for simplicial facets and up-to 100% for non-simplicial facets.
+
+<p>You can check memory consumption with option <a href="qh-optt.htm#Ts">Ts</a>. It includes the size of
+each data structure:
+<ul>
+<li>32-bit -- merge 24 ridge 20 vertex 28 facet 88 normal 24 ridge vertices 16 facet vertices or neighbors 20
+<li>64-bit -- merge 32 ridge 32 vertex 48 facet 120 normal 32 ridge vertices 40 facet vertices or neighbors 48
+</ul>
+
+<p>For Qhull 2015, the maximum identifier for ridges, vertices, and facets was increased
+from 24-bits to 32-bits. This allows for larger convex hulls, but may increase the size of
+the corresponding data structures. The sizes for Qhull 2012.1 were
+<ul>
+<li>32-bit -- merge 24 ridge 16 vertex 24 facet 88
+<li>64-bit -- merge 32 ridge 32 vertex 40 facet 120
+</ul>
+
+<h2><a href="#TOC">&#187;</a><a name="cpp">Calling Qhull from
+C++ programs</a></h2>
+
+<p>Qhull 2015 uses reentrant Qhull for its C++ interface. If you used
+the C++ interface from qhull 2012.1, you may need to adjust how you initialize and use
+the Qhull classes. See <a href="#convert">How to convert code to reentrant Qhull</a>.
+
+<p>
+Qhull's C++ interface allows you to explore the results of running Qhull.
+It provides access to Qhull's data structures.
+Most of the classes derive from the corresponding qhull data structure.
+For example, <a href="#facet-cpp">QhullFacet</a> is an instance of Qhull's <a href="../src/libqhull_r/libqhull_r.h#facetT">facetT</a>.
+</p>
+
+<p>You can retain most of the data in Qhull and use the C++ interface to explore its results.
+Each object contains a reference the Qhull's data structure (via QhullQh), making the C++ representation less memory efficient.
+</p>
+
+<p>Besides using the C++ interface, you can also use libqhull_r directly. For example,
+the FOREACHfacet_(...) macro will visit each facet in turn.
+</p>
+
+<p>The C++ interface to Qhull is incomplete. You may need to extend the interface.
+If so, you will need to understand Qhull's data structures and read the code.
+
+Example (c.f., <code>user_eg3 eg-100</code>). It prints the facets generated by Qhull.
+
+<pre>
+ RboxPoints rbox;
+ rbox.appendRandomPoints("100");
+ Qhull qhull;
+ qhull.runQhull("", rbox);
+ QhullFacetList facets(qhull);
+ cout<< facets;
+</pre>
+
+<p>
+The C++ iterface for RboxPoints redefines the fprintf() calls
+in rboxlib.c. Instead of writing its output to stdout, RboxPoints appends
+the output to a std::vector.
+The same technique may be used for calling Qhull from C++.
+</p>
+<ul><li>
+Run Qhull with option '<a href="qh-optt.htm#Ta">Ta</a>' to annotate the
+output with qh_fprintf() identifiers.
+</li><li>
+Redefine qh_fprintf() for these identifiers.
+</li><li>
+See RboxPoints.cpp for an example.
+</li></ul>
+<p>
+Since the C++ interface uses reentrant Qhull, multiple threads may run Qhull at the same time. Each thread is
+one run of Qhull.
+</p>
+
+<p>
+Do <i>not</i> have two threads accessing the same Qhull instance. Qhull is not thread-safe.
+</p>
+
+<h3><a href="#TOC">&#187;</a><a name="coordinate-cpp">CoordinateIterator</a></h3>
+<p>
+A CoordinateIterator or ConstCoordinateIterator [RboxPoints.cpp] is a <code>std::vector&lt;realT>::iterator</code> for Rbox and Qhull coordinates.
+It is the result type of <a href="#rbox-cpp">RboxPoints</a>.coordinates().
+</p>
+
+<p>Qhull does not use CoordinateIterator for its data structures. A point in Qhull is an array of reals instead of a std::vector.
+See <a href="#point-cpp">QhullPoint</a>.
+</p>
+
+<h3><a href="#TOC">&#187;</a><a name="qhull-cpp">Qhull</a></h3>
+<p>
+Qhull is the top-level class for running Qhull.
+It initializes Qhull, runs the computation, and records errors.
+It provides access to the global data structure <a href="#qh-cpp">QhullQh</a>,
+Qhull's <a href="#facet-cpp">facets</a>, and <a href="#vertex-cpp">vertices</a>.
+</p>
+
+<h3><a href="#TOC">&#187;</a><a name="error-cpp">QhullError</a></h3>
+<p>
+QhullError is derived from <code>std::exception</code>. It reports errors from Qhull and captures the output to stderr.
+</p>
+
+<p>
+If error handling is not set up, Qhull exits with a code from 1 to 5. The codes are defined by
+qh_ERR* in libqhull_r.h. The exit is via qh_exit() in usermem_r.c.
+The C++ interface does not report the
+captured output in QhullError. Call Qhull::setErrorStream to send output to cerr instead.
+</p>
+
+<h3><a href="#TOC">&#187;</a><a name="facet-cpp">QhullFacet</a></h3>
+<p>
+A QhullFacet is a facet of the convex hull, a region of the Delaunay triangulation, a vertex of a Voronoi diagram,
+or an intersection of the halfspace intersection about a point.
+A QhullFacet has a set of <a href="#vertex-cpp">QhullVertex</a>, a set of <a href="#ridge-cpp">QhullRidge</a>, and
+a set of neighboring QhullFacets.
+</p>
+
+<h3><a href="#TOC">&#187;</a><a name="facetlist-cpp">QhullFacetList</a></h3>
+<p>
+A QhullFacetList is a linked list of <a href="#facet-cpp">QhullFacet</a>. The result of <code>Qhull.runQhull</code> is a QhullFacetList stored
+in <a href="#qh-cpp">QhullQh</a>.
+</p>
+
+<h3><a href="#TOC">&#187;</a><a name="facetset-cpp">QhullFacetSet</a></h3>
+<p>
+A QhullFacetSet is a <a href="#set-cpp">QhullSet</a> of <a href="#facet-cpp">QhullFacet</a>. QhullFacetSet may be ordered or unordered. The neighboring facets of a QhullFacet is a QhullFacetSet.
+The neighbors of a <a href="#facet-cpp">QhullFacet</a> is a QhullFacetSet.
+The neighbors are ordered for simplicial facets, matching the opposite vertex of the facet.
+</p>
+
+<h3><a href="#TOC">&#187;</a><a name="iterator-cpp">QhullIterator</a></h3>
+<p>
+QhullIterator contains macros for defining Java-style iterator templates from a STL-style iterator template.
+</p>
+
+<h3><a href="#TOC">&#187;</a><a name="linkedlist-cpp">QhullLinkedList</a></h3>
+<p>
+A QhullLinkedLIst is a template for linked lists with next and previous pointers.
+<a href="#facetlist-cpp">QhullFacetList</a> and <a href="#facetlist-cpp">QhullVertexList</a> are QhullLinkedLists.
+</p>
+
+<h3><a href="#TOC">&#187;</a><a name="point-cpp">QhullPoint</a></h3>
+<p>
+A QhullPoint is an array of point coordinates, typically doubles. The length of the array is <a href="#qh-cpp">QhullQh</a>.hull_dim.
+The identifier of a QhullPoint is its 0-based index from QhullQh.first_point followed by QhullQh.other_points.
+</p>
+
+<h3><a href="#TOC">&#187;</a><a name="pointset-cpp">QhullPointSet</a></h3>
+<p>
+A QhullPointSet is a <a href="#set-cpp">QhullSet</a> of <a href="#point-cpp">QhullPoint</a>. The QhullPointSet of a <a href="#facet-cpp">QhullFacet</a> is its coplanar points.
+</p>
+
+<h3><a href="#TOC">&#187;</a><a name="qh-cpp">QhullQh</a></h3>
+<p>
+QhullQh is the root of Qhull's data structure.
+It contains initialized constants, sets, buffers, and variables.
+It contains an array and a set of <a href="#point-cpp">QhullPoint</a>,
+a list of <a href="#facet-cpp">QhullFacet</a>, and a list of <a href="#vertex-cpp">QhullVertex</a>.
+The points are the input to Qhull. The facets and vertices are the result of running Qhull.
+</p>
+
+<p>
+Qhull's functions access QhullQh through the global variable, <code>qh_qh</code>.
+The global data structures, qh_stat and qh_mem, record statistics and manage memory respectively.
+</p>
+
+<h3><a href="#TOC">&#187;</a><a name="ridge-cpp">QhullRidge</a></h3>
+
+<p>
+A QhullRidge represents the edge between two <a href="#facet-cpp">QhullFacet</a>'s.
+It is always simplicial with qh.hull_dim-1 <a href="#vertex-cpp">QhullVertex</a>)'s.
+</p>
+
+<h3><a href="#TOC">&#187;</a><a name="ridgeset-cpp">QhullRidgeSet</a></h3>
+
+<p>
+A QhullRidgeSet is a <a href="#set-cpp">QhullSet</a> of <a href="#ridge-cpp">QhullRidge</a>. Each <a href="#facet-cpp">QhullFacet</a> contains a QhullRidgeSet.
+</p>
+
+<h3><a href="#TOC">&#187;</a><a name="set-cpp">QhullSet</a></h3>
+
+<p>
+A QhullSet is a set of pointers to objects. QhullSets may be ordered or unordered. They are the core data structure for Qhull.
+</p>
+
+<h3><a href="#TOC">&#187;</a><a name="vertex-cpp">QhullVertex</a></h3>
+
+<p>
+A QhullVertex is a vertex of the convex hull. A simplicial <a href="#facet-cpp">QhullFacet</a> has qh.hull_dim-1 vertices. A QhullVertex contains a <a href="#point-cpp">QhullPoint</a>.
+It may list its neighboring <a href="#facet-cpp">QhullFacet</a>'s.
+</p>
+
+<h3><a href="#TOC">&#187;</a><a name="vertexlist-cpp">QhullVertexList</a></h3>
+
+<p>
+A QhullVertexList is a <a href="#linkedlist-cpp">QhullLinkedList</a> of <a href="#vertex-cpp">QhullVertex</a>.
+The global data structure, <a href="#qh-cpp">QhullQh</a> contains a QhullVertexList of all
+the vertices.
+</p>
+
+<h3><a href="#TOC">&#187;</a><a name="vertexset-cpp">QhullVertexSet</a></h3>
+
+<p>
+A QhullVertexSet is a <a href="#set-cpp">QhullSet</a> of <a href="#vertex-cpp">QhullVertex</a>.
+The QhullVertexSet of a <a href="#facet-cpp">QhullFacet</a> is the vertices of the facet. It is
+ordered for simplicial facets and unordered for non-simplicial facets.
+</p>
+
+<h3><a href="#TOC">&#187;</a><a name="rbox-cpp">RboxPoints</a></h3>
+
+<p>
+RboxPoints is a std::vector of point coordinates (<a href="#point-cpp">QhullPoint</a>).
+It's iterator is <a href="#coordinate-cpp">CoordinateIterator</a>.
+</p>
+<p>
+<code>RboxPoints.appendRandomPoints()</code> appends points from a variety of distributions such as uniformly distributed within a cube and random points on a sphere.
+It can also append a cube's vertices or specific points.
+</p>
+
+<h3><a href="#TOC">&#187;</a><a name="questions-cpp">Cpp questions for Qhull</a></h3>
+
+Developing C++ code requires many conventions, idioms, and technical details.
+The following questions have either
+mystified the author or do not have a clear answer. See also
+<a href="http://www.qhull.org/road/road-faq/xml/cpp-guideline.xml">C++ and Perl Guidelines</a>.
+and FIXUP notes in the code.
+Please add notes to <a href="http://github.com/qhull/qhull/wiki">Qhull Wiki</a>.
+
+<ul>
+<li>FIXUP QH11028 Should return reference, but get reference to temporary
+<pre>iterator Coordinates::operator++() { return iterator(++i); }</pre>
+<li>size() as size_t, size_type, or int
+<li>Should all containers have a reserve()?
+<li>Qhull.feasiblePoint interface
+<li>How to avoid copy constructor while logging, maybeThrowQhullMessage()
+<li>How to configure Qhull output. Trace and results should go to stdout/stderr
+<li>Qhull and RboxPoints messaging. e.g., ~Qhull, hasQhullMessage(). Rename them as QhullErrorMessage?
+<li>How to add additional output to an error message, e.g., qh_setprint
+<li>Is idx the best name for an index? It's rather cryptic, but BSD strings.h defines index().
+<li>Qhull::feasiblePoint Qhull::useOutputStream as field or getter?
+<li>Define virtual functions for user customization of Qhull (e.g., qh_fprintf, qh_memfree,etc.)
+<li>Figure out RoadError::global_log. clearQhullMessage currently clearGlobalLog
+<li>Should the false QhullFacet be NULL or empty? e.g., QhullFacet::tricoplanarOwner() and QhullFacetSet::end()
+<li>Should output format for floats be predefined (qh_REAL_1, 2.2g, 10.7g) or as currently set for stream
+<li>Should cout << !point.defined() be blank or 'undefined'
+<li>Infinite point as !defined()
+<li>qlist and qlinkedlist define pointer, reference, size_type, difference_type, const_pointer, const_reference for the class but not for iterator and const_iterator
+ vector.h -- <pre>reference operator[](difference_type _Off) const</pre>
+<li>When forwarding an implementation is base() an approriate name (e.g., Coordinates::iterator::base() as std::vector<coordT>::iterator).
+<li>When forwarding an implementation, does not work "returning address of temporary"
+<li>Also --, +=, and -=
+ <pre>iterator &operator++() { return iterator(i++); }</pre>
+<li>if vector<coordT> inheritance is bad, is QhullVertexSet OK?
+<li>Should QhullPointSet define pointer and reference data types?
+</ul>
+
+<h2><a href="#TOC">&#187;</a><a name="library">Calling Qhull from
+C programs</a></h2>
+
+<p><b>Warning:</b> Qhull was not designed for calling from C
+programs. You may find the <a href="#cpp">C++ interface</a> easier to use.
+You will need to understand the data structures and read the code.
+Most users will find it easier to call Qhull as an external
+command.
+
+<p>For examples of calling Qhull, see GNU Octave's
+<a href=http://www.gnu.org/software/octave/doc/interpreter/Geometry.html>computational geometry code</a>,
+and Qhull's
+<a href=../src/user_eg/user_eg_r.c>user_eg_r.c</a>,
+<a href=../src/user_eg2/user_eg2_r.c>user_eg2_r.c</a>, and
+<a href=../src/libqhull_r/user_r.c>user_r.c</a>. To see how Qhull calls its library, read
+<a href=../src/qhull/unix_r.c>unix_r.c</a>,
+<a href=../src/qconvex/qconvex.c>qconvex.c</a>,
+<a href=../src/qdelaunay/qdelaun.c>qdelaun.c</a>,
+<a href=../src/qhalf/qhalf.c>qhalf.c</a>, and
+<a href=../src/qvoronoi/qvoronoi.c>qvoronoi.c</a>. The '*_r.c' files are reentrant, otherwise they are non-reentrant.
+Either version may be used. New code should use reentrant Qhull.
+
+<p>See <a href="../src/libqhull_r/index.htm">Reentrant Qhull functions, macros, and data
+structures</a> for internal documentation of Qhull. The
+documentation provides an overview and index. To use the library
+you will need to read and understand the code. For most users, it
+is better to write data to a file, call the qhull program, and
+read the results from the output file.</p>
+
+<p>If you use non-reentrant Qhull, be aware of the macros &quot;qh&quot;
+and &quot;qhstat&quot;, e.g., &quot;qh hull_dim&quot;. They are
+defined in <tt>libqhull.h</tt>. They allow the global data
+structures to be pre-allocated (faster access) or dynamically
+allocated (allows multiple copies). </p>
+
+<p>Qhull's <tt>Makefile</tt> produces a library, <tt>libqhull_r.a</tt>,
+for inclusion in your programs. First review <tt>libqhull_r.h</tt>.
+This defines the data structures used by Qhull and provides
+prototypes for the top-level functions.
+Most users will only need libqhull_r.h in their programs. For
+example, the Qhull program is defined with <tt>libqhull_r.h</tt> and <tt>unix_r.c</tt>.
+To access all functions, use <tt>qhull_ra.h</tt>. Include the file
+with &quot;<tt>#include &lt;libqhull_r/qhull_ra.h&gt;</tt>&quot;. This
+avoids potential name conflicts.</p>
+
+<p>If you use the Qhull library, you are on your own as far as
+bugs go. Start with small examples for which you know the output.
+If you get a bug, try to duplicate it with the Qhull program. The
+'<a href="qh-optt.htm#Tc">Tc</a>' option will catch many problems
+as they occur. When an error occurs, use '<a
+href="qh-optt.htm#Tn">T4</a> <a href="qh-optt.htm#TPn">TPn</a>'
+to trace from the last point added to the hull. Compare your
+trace with the trace output from the Qhull program.</p>
+
+<p>Errors in the Qhull library are more likely than errors in the
+Qhull program. These are usually due to feature interactions that
+do not occur in the Qhull program. Please report all errors that
+you find in the Qhull library. Please include suggestions for
+improvement. </p>
+
+<h3><a href="#TOC">&#187;</a><a name="exit">How to avoid exit(), fprintf(), stderr, and stdout</a></h3>
+
+<p>Qhull sends output to qh.fout and errors, log messages, and summaries to qh.ferr. qh.fout is normally
+stdout and qh.ferr is stderr. qh.fout may be redefined by option '<a
+href="qh-optt.htm#TO">TO</a>' or the caller. qh.ferr may be redirected to qh.fout by option '<a
+href="qh-optt.htm#Tz">Tz</a>'.</p>
+
+<p>Qhull does not use stderr, stdout, fprintf(), or exit() directly.</p>
+
+<p>Qhull reports errors via qh_errexit() by writting a message to qh.ferr and invoking longjmp().
+This returns the caller to the corresponding setjmp() (c.f., QH_TRY_ in QhullQh.h). If
+qh_errexit() is not available, Qhull functions call qh_exit(). qh_exit() normally calls exit(),
+but may be redefined by the user. An example is
+libqhullcpp/usermem_r-cpp.cpp. It redefines qh_exit() as a 'throw'.</p>
+
+<p>If qh_meminit() or qh_new_qhull() is called with ferr==NULL, then they set ferr to stderr.
+Otherwise the Qhull libraries use qh->ferr and qh->qhmem.ferr for error output.</p>
+
+<p>If an error occurs before qh->ferr is initialized, Qhull invokes qh_fprintf_stderr(). The user
+may redefine this function along with qh_exit(), qh_malloc(), and qh_free().
+
+<p>The Qhull libraries write output via qh_fprintf() [userprintf_r.c]. Otherwise, the Qhull
+libraries do not use stdout, fprintf(), or printf(). Like qh_exit(), the user may redefine
+qh_fprintf().</p>
+
+<h3><a href="#TOC">&#187;</a><a name="mem">sets and quick memory
+allocation</a></h3>
+
+<p>You can use <tt>mem_r.c</tt> and <tt>qset_r.c</tt> individually. <tt>Mem_r.c
+</tt>implements quick-fit memory allocation. It is faster than
+malloc/free in applications that allocate and deallocate lots of
+memory. </p>
+
+<p><tt>Qset_r.c</tt> implements sets and related collections. It's
+the inner loop of Qhull, so speed is more important than
+abstraction. Set iteration is particularly fast. <tt>qset_r.c</tt>
+just includes the functions needed for Qhull. </p>
+
+<h3><a href="#TOC">&#187;</a><a name="dids">Delaunay triangulations
+and point indices</a></h3>
+
+<p>Here some unchecked code to print the point indices of each
+Delaunay triangle. Use option 'QJ' if you want to avoid
+non-simplicial facets. Note that upper Delaunay regions are
+skipped. These facets correspond to the furthest-site Delaunay
+triangulation. </p>
+
+<blockquote>
+ <pre>
+ facetT *facet;
+ vertexT *vertex, **vertexp;
+
+ FORALLfacets {
+ if (!facet-&gt;upperdelaunay) {
+ printf (&quot;%d&quot;, qh_setsize (facet-&gt;vertices);
+ FOREACHvertex_(facet-&gt;vertices)
+ printf (&quot; %d&quot;, qh_pointid (vertex-&gt;point));
+ printf (&quot;\n&quot;);
+ }
+ }
+
+</pre>
+</blockquote>
+
+<h3><a href="#TOC">&#187;</a><a name="findfacet">locate a facet with
+qh_findbestfacet()</a></h3>
+
+<p>The routine qh_findbestfacet in <tt>poly2_r.c</tt> is
+particularly useful. It uses a directed search to locate the
+facet that is furthest below a point. For Delaunay
+triangulations, this facet is the Delaunay triangle that contains
+the lifted point. For convex hulls, the distance of a point to
+the convex hull is either the distance to this facet or the
+distance to a subface of the facet.</p>
+
+<blockquote>
+<p><b>Warning:</b> If triangulated output ('<a href=qh-optq.htm#Qt>Qt</a>') and
+the best facet is triangulated, qh_findbestfacet() returns one of
+the corresponding 'tricoplanar' facets. The actual best facet may be a different
+tricoplanar facet.
+<p>
+See qh_nearvertex() in poly2.c for sample code to visit each
+tricoplanar facet. To identify the correct tricoplanar facet,
+see Devillers, et. al., [<a href="index.htm#devi01">'01</a>]
+and Mucke, et al [<a href="index.htm#muck96">'96</a>]. If you
+implement this test in general dimension, please notify
+<a href="mailto:qhull@qhull.org">qhull@qhull.org</a>.
+</blockquote>
+
+<p>qh_findbestfacet performs an exhaustive search if its directed
+search returns a facet that is above the point. This occurs when
+the point is inside the hull or if the curvature of the convex
+hull is less than the curvature of a sphere centered at the point
+(e.g., a point near a lens-shaped convex hull). When the later
+occurs, the distance function is bimodal and a directed search
+may return a facet on the far side of the convex hull. </p>
+
+<p>Algorithms that retain the previously constructed hulls
+usually avoid an exhaustive search for the best facet. You may
+use a hierarchical decomposition of the convex hull [Dobkin and
+Kirkpatrick <a href="index.htm#dob-kir90">'90</a>]. </p>
+
+<p>To use qh_findbestfacet with Delaunay triangulations, lift the
+point to a paraboloid by summing the squares of its coordinates
+(see qh_setdelaunay in geom2_r.c). Do not scale the input with
+options 'Qbk', 'QBk', 'QbB' or 'Qbb'. See Mucke, et al [<a
+href="index.htm#muck96">'96</a>] for a good point location
+algorithm.</p>
+
+<p>The intersection of a ray with the convex hull may be found by
+locating the facet closest to a distant point on the ray.
+Intersecting the ray with the facet's hyperplane gives a new
+point to test. </p>
+
+<h3><a href="#TOC">&#187;</a><a name="inc">on-line construction with
+qh_addpoint()</a></h3>
+
+<p>The Qhull library may be used for the on-line construction of
+convex hulls, Delaunay triangulations, and halfspace
+intersections about a point. It may be slower than implementations that retain
+intermediate convex hulls (e.g., Clarkson's <a
+href="http://www.netlib.org/voronoi/hull.html">hull
+program</a>). These implementations always use a directed search.
+For the on-line construction of convex hulls and halfspace
+intersections, Qhull may use an exhaustive search
+(qh_findbestfacet). </p>
+
+<p>You may use qh_findbestfacet and qh_addpoint (<tt>libqhull.c</tt>) to add a point to
+a convex hull. Do not modify the point's coordinates since
+qh_addpoint does not make a copy of the coordinates. For Delaunay
+triangulations, you need to lift the point to a paraboloid by
+summing the squares of the coordinates (see qh_setdelaunay in
+geom2.c). Do not scale the input with options 'Qbk', 'QBk', 'QbB'
+or 'Qbb'. Do not deallocate the point's coordinates. You need to
+provide a facet that is below the point (<a href="#findfacet">qh_findbestfacet</a>).
+</p>
+
+<p>You can not delete points. Another limitation is that Qhull
+uses the initial set of points to determine the maximum roundoff
+error (via the upper and lower bounds for each coordinate). </p>
+
+<p>For many applications, it is better to rebuild the hull from
+scratch for each new point. This is especially true if the point
+set is small or if many points are added at a time.</p>
+
+<p>Calling qh_addpoint from your program may be slower than
+recomputing the convex hull with qh_qhull. This is especially
+true if the added points are not appended to the qh first_point
+array. In this case, Qhull must search a set to determine a
+point's ID. [R. Weber] </p>
+
+<p>See user_eg.c for examples of the on-line construction of
+convex hulls, Delaunay triangulations, and halfspace
+intersections. The outline is: </p>
+
+<blockquote>
+ <pre>
+initialize qhull with an initial set of points
+qh_qhull();
+
+for each additional point p
+ append p to the end of the point array or allocate p separately
+ lift p to the paraboloid by calling qh_setdelaunay
+ facet= qh_findbestfacet (p, !qh_ALL, &amp;bestdist, &amp;isoutside);
+ if (isoutside)
+ if (!qh_addpoint (point, facet, False))
+ break; /* user requested an early exit with 'TVn' or 'TCn' */
+
+call qh_check_maxout() to compute outer planes
+terminate qhull</pre>
+</blockquote>
+
+<h3><a href="#TOC">&#187;</a><a name="constrained">Constrained
+Delaunay triangulation </a></h3>
+
+<p>With a fair amount of work, Qhull is suitable for constrained
+Delaunay triangulation. See Shewchuk, ACM Symposium on
+Computational Geometry, Minneapolis 1998.</p>
+
+<p>Here's a quick way to add a constraint to a Delaunay
+triangulation: subdivide the constraint into pieces shorter than
+the minimum feature separation. You will need an independent
+check of the constraint in the output since the minimum feature
+separation may be incorrect. [H. Geron] </p>
+
+<h3><a href="#TOC">&#187;</a><a name="tricoplanar">Tricoplanar facets and option 'Qt'</h3>
+
+<p>Option '<a href=qh-optq.htm#Qt>Qt</a>' triangulates non-simplicial
+facets (e.g., a square facet in 3-d or a cubical facet in 4-d).
+All facets share the same apex (i.e., the first vertex in facet->vertices).
+For each triangulated facet, Qhull
+sets facet->tricoplanar true and copies facet->center, facet->normal, facet->offset, and facet->maxoutside. One of
+the facets owns facet->normal; its facet->keepcentrum is true.
+If facet->isarea is false, facet->triowner points to the owning
+facet.
+
+<p>Qhull sets facet->degenerate if the facet's vertices belong
+to the same ridge of the non-simplicial facet.
+
+<p>To visit each tricoplanar facet of a non-simplicial facet,
+either visit all neighbors of the apex or recursively visit
+all neighbors of a tricoplanar facet. The tricoplanar facets
+will have the same facet->center.</p>
+
+<p>See <a href=../src/libqhull_r/io_r.c#detvridge>qh_detvridge</a> for an example of ignoring tricoplanar facets.</p>
+
+<h3><a href="#TOC">&#187;</a><a name="vertices">Voronoi vertices of a
+region</a></h3>
+
+<p>The following code iterates over all Voronoi vertices for each
+Voronoi region. Qhull computes Voronoi vertices from the convex
+hull that corresponds to a Delaunay triangulation. An input site
+corresponds to a vertex of the convex hull and a Voronoi vertex
+corresponds to an adjacent facet. A facet is
+&quot;upperdelaunay&quot; if it corresponds to a Voronoi vertex
+&quot;at-infinity&quot;. Qhull uses qh_printvoronoi in <tt>io.c</tt>
+for '<a href=qvoronoi.htm>qvoronoi</a> <a href="qh-opto.htm#o">o'</a> </p>
+
+<blockquote>
+ <pre>
+/* please review this code for correctness */
+qh_setvoronoi_all();
+FORALLvertices {
+ site_id = qh_pointid (vertex-&gt;point);
+ if (qh hull_dim == 3)
+ qh_order_vertexneighbors(vertex);
+ infinity_seen = 0;
+ FOREACHneighbor_(vertex) {
+ if (neighbor-&gt;upperdelaunay) {
+ if (!infinity_seen) {
+ infinity_seen = 1;
+ ... process a Voronoi vertex &quot;at infinity&quot; ...
+ }
+ }else {
+ voronoi_vertex = neighbor-&gt;center;
+ ... your code goes here ...
+ }
+ }
+}
+</pre>
+</blockquote>
+
+<h3><a href="#TOC">&#187;</a><a name="ridge">Voronoi vertices of a
+ridge</a></h3>
+
+<p>Qhull uses qh_printvdiagram() in io.c to print the ridges of a
+Voronoi diagram for option '<a href="qh-optf.htm#Fv2">Fv</a>'.
+The helper function qh_eachvoronoi() does the real work. It calls
+the callback 'printvridge' for each ridge of the Voronoi diagram.
+</p>
+
+<p>You may call qh_printvdiagram2(), qh_eachvoronoi(), or
+qh_eachvoronoi_all() with your own function. If you do not need
+the total number of ridges, you can skip the first call to
+qh_printvdiagram2(). See qh_printvridge() and qh_printvnorm() in
+io.c for examples. </p>
+
+<h3><a href="#TOC">&#187;</a><a name="vneighbor">vertex neighbors of
+a vertex</a></h3>
+
+<p>To visit all of the vertices that share an edge with a vertex:
+</p>
+
+<ul>
+ <li>Generate neighbors for each vertex with
+ qh_vertexneighbors in <tt>poly2.c</tt>. </li>
+ <li>For simplicial facets, visit the vertices of each
+ neighbor </li>
+ <li>For non-simplicial facets, <ul>
+ <li>Generate ridges for neighbors with qh_makeridges
+ in <tt>merge.c</tt>. </li>
+ <li>Generate ridges for a vertex with qh_vertexridges
+ in <tt>merge.c</tt>. </li>
+ <li>Visit the vertices of these ridges. </li>
+ </ul>
+ </li>
+</ul>
+
+<p>For non-simplicial facets, the ridges form a simplicial
+decomposition of the (d-2)-faces between each pair of facets --
+if you need 1-faces, you probably need to generate the full face
+graph of the convex hull. </p>
+
+<h2><a href="#TOC">&#187;</a><a name="performance">Performance of
+Qhull </a></h2>
+
+<p>Empirically, Qhull's performance is balanced in the sense that
+the average case happens on average. This may always be true if
+the precision of the input is limited to at most <i>O(log n)</i>
+bits. Empirically, the maximum number of vertices occurs at the
+end of constructing the hull. </p>
+
+<p>Let <i>n</i> be the number of input points, <i>v</i> be the
+number of output vertices, and <i>f_v </i>be the maximum number
+of facets for a convex hull of <i>v</i> vertices. If both
+conditions hold, Qhull runs in <i>O(n log v)</i> in 2-d and 3-d
+and <i>O(n f_v/v)</i> otherwise. The function <i>f_v</i>
+increases rapidly with dimension. It is <em>O(v^floor(d/2) /
+floor(d/2)!)</em>.</p>
+
+<p>The time complexity for merging is unknown. Options '<a
+href="qh-optc.htm#C0">C-0</a>' and '<a href="qh-optq.htm#Qx">Qx</a>'
+(defaults) handle precision problems due to floating-point
+arithmetic. They are optimized for simplicial outputs. </p>
+
+<p>When running large data sets, you should monitor Qhull's
+performance with the '<a href="qh-optt.htm#TFn">TFn</a>' option.
+The time per facet is approximately constant. In high-d with many
+merged facets, the size of the ridge sets grows rapidly. For
+example the product of 8-d simplices contains 18 facets and
+500,000 ridges. This will increase the time needed per facet. </p>
+
+<p>As dimension increases, the number of facets and ridges in a
+convex hull grows rapidly for the same number of vertices. For
+example, the convex hull of 300 cospherical points in 6-d has
+30,000 facets. </p>
+
+<p>If Qhull appears to stop processing facets, check the memory
+usage of Qhull. If more than 5-10% of Qhull is in virtual memory,
+its performance will degrade rapidly. </p>
+
+<p>When building hulls in 20-d and higher, you can follow the
+progress of Qhull with option '<a href="qh-optt.htm#Tn">T1</a>'.
+It reports each major event in processing a point. </p>
+
+<p>To reduce memory requirements, recompile Qhull for
+single-precision reals (REALfloat in <tt>user.h</tt>).
+Single-precision does not work with joggle ('<a
+href="qh-optq.htm#QJn">QJ</a>'). Check qh_MEMalign in <tt>user.h</tt>
+and the match between free list sizes and data structure sizes
+(see the end of the statistics report from '<a
+href="qh-optt.htm#Ts">Ts</a>'). If free list sizes do not match,
+you may be able to use a smaller qh_MEMalign. Setting
+qh_COMPUTEfurthest saves a small amount of memory, as does
+clearing qh_MAXoutside (both in <tt>user.h</tt>).</p>
+
+<p>Shewchuk is working on a 3-d version of his triangle
+program. It is optimized for 3-d simplicial Delaunay triangulation
+and uses less memory than Qhull.</p>
+
+<p>To reduce the size of the Qhull executable, consider
+qh_NOtrace and qh_KEEPstatistics 0 in <tt>user.h</tt>. By
+changing <tt>user.c </tt>you can also remove the input/output
+code in <tt>io.c</tt>. If you don't need facet merging, then
+version 1.01 of Qhull is much smaller. It contains some bugs that
+prevent Qhull from initializing in simple test cases. It is
+slower in high dimensions.</p>
+
+<p>The precision options, '<a href="qh-optc.htm#Vn">Vn</a>', '<a
+href="qh-optc.htm#Wn">Wn</a>', '<a href="qh-optc.htm#Un">Un</a>'.
+'<a href="qh-optc.htm#An">A-n</a>', '<a href="qh-optc.htm#Cn">C-n</a>',
+'<a href="qh-optc.htm#An2">An</a>', '<a href="qh-optc.htm#Cn2">Cn</a>',
+and '<a href="qh-optq.htm#Qx">Qx</a>', may have large effects on
+Qhull performance. You will need to experiment to find the best
+combination for your application. </p>
+
+<p>The verify option ('<a href="qh-optt.htm#Tv">Tv</a>') checks
+every point after the hull is complete. If facet merging is used,
+it checks that every point is inside every facet. This can take a
+very long time if there are many points and many facets. You can
+interrupt the verify without losing your output. If facet merging
+is not used and there are many points and facets, Qhull uses a
+directed search instead of an exhaustive search. This should be
+fast enough for most point sets. Directed search is not used for
+facet merging because directed search was already used for
+updating the facets' outer planes.</p>
+
+<p>The check-frequently option ('<a href="qh-optt.htm#Tc">Tc</a>')
+becomes expensive as the dimension increases. The verify option
+('<a href="qh-optt.htm#Tv">Tv</a>') performs many of the same
+checks before outputting the results.</p>
+
+<p>Options '<a href="qh-optq.htm#Q0">Q0</a>' (no pre-merging), '<a
+href="qh-optq.htm#Q3">Q3</a>' (no checks for redundant vertices),
+'<a href="qh-optq.htm#Q5">Q5</a>' (no updates for outer planes),
+and '<a href="qh-optq.htm#Q8">Q8</a>' (no near-interior points)
+increase Qhull's speed. The corresponding operations may not be
+needed in your application.</p>
+
+<p>In 2-d and 3-d, a partial hull may be faster to produce.
+Option '<a href="qh-optq.htm#QGn">QgGn</a>' only builds facets
+visible to point n. Option '<a href="qh-optq.htm#QVn">QgVn</a>'
+only builds facets that contain point n. In higher-dimensions,
+this does not reduce the number of facets.</p>
+
+<p><tt>User.h</tt> includes a number of performance-related
+constants. Changes may improve Qhull performance on your data
+sets. To understand their effect on performance, you will need to
+read the corresponding code. </p>
+
+<p>GNU <tt>gprof</tt> reports that the dominate cost for 3-d
+convex hull of cosperical points is qh_distplane(), mainly called
+from qh_findbestnew(). The dominate cost for 3-d Delaunay triangulation
+is creating new facets in qh_addpoint(), while qh_distplane() remains
+the most expensive function.
+
+</p>
+<h2><a href="#TOC">&#187;</a><a name="enhance">Enhancements to Qhull </a></h2>
+
+<p>There are many ways in which Qhull can be improved. </p>
+
+<pre>
+[Jan 2016] Suggestions
+------------
+To do for a future verson of Qhull
+ - Add a post-merge pass for Delaunay slivers. Merge into a neighbor with a circumsphere that includes the opposite point. [M. Treacy]
+ - Add a merge pass before cone creation to remove duplicate subridges between horizon facets
+ - Option to add a bounding box for Delaunay triangulations, e,g., nearly coincident points
+ - Report error when rbox Cn,r,m does not produce points (e.g., 'r' distributions)
+ - Rescale output to match 'QbB' on input [J. Metz, 1/30/2014 12:21p]
+ - Run through valgrind
+ - Notes to compgeom on conformant triangulation and Voronoi volume
+ - Git: Create signed tags for Qhull versions
+ - Implement weighted Delaunay triangulation and weighted Voronoi diagram [A. Liebscher]
+ e.g., Sugihara, "Three-dimensional convex hull as a fruitful source of diagrams," Theoretical Computer Science, 2000, 235:325-337
+ - testqset: test qh_setdelnth and move-to-front
+ - Makefile: Re-review gcc/g++ warnings. OK in 2011.
+ - Break up -Wextra into its components or figure out how to override -Wunused-but-set-variable
+ unused-but-set-variable is reporting incorrectly. All instances are annotated.
+ - CMakelists.txt: Why are files duplicated for cmake build
+ - CMakeLists.txt: configure the 'ctest' target
+ - The size of maxpoints in qh_maxsimplex should be d+3 unique points to help avoid QH6154
+
+ - Can countT be defined as 'int', 'unsigned int', or 64-bit int?
+ countT is currently defined as 'int' in qset_r.h
+ Vertex ID and ridge ID perhaps should be countT, They are currently 'unsigned'
+ Check use of 'int' vs. countT in all cpp code
+ Check use of 'int' vs. countT in all c code
+ qset_r.h defines countT -- duplicates code in user_r.h -- need to add to qset.h/user.h
+ countT -1 used as a flag in Coordinates.mid(), QhullFacet->id()
+ Also QhullPoints indexOf and lastIndexOf
+ Also QhullPointSet indexOf and lastIndexOf
+ Coordinates.indexOf assumes countT is signed (from end)
+ Coordinates.lastIndexOf assumes countT is signed (from end)
+ All error messages with countT are wrong, convert to int?
+ RboxPoints.qh_fprintf_rbox, etc. message 9393 assumes countT but may be int, va_arg(args, countT); Need to split
+
+------------
+To do for a furture version of the C++ interface
+ - Fix C++ memory leak in user_eg3 [M. Sandim]
+ - Document C++ using Doxygen conventions (//! and //!<)
+ - Should Qhull manage the output formats for doubles? QH11010 user_r.h defines qh_REAL_1 as %6.8g
+ - Allocate memory for QhullSet using Qhull.qhmem. Create default constructors for QhullVertexSet etc. Also mid() etc.
+ - Add interior point for automatic translation?
+ - Add hasNext() to all next() iterators (e.g., QhullVertex)
+ - Add defineAs() to each object
+ - Write a program with concurrent Qhull
+ - Write QhullStat and QhullStat_test
+ - Add QList and vector instance of facetT*, etc.
+ - Generalize QhullPointSetIterator
+ - qh-code.htm: Document changes to C++ interface.
+ Organize C++ documentation into collection classes, etc.
+ - Review all C++ classes and C++ tests
+ - QhullVertexSet uses QhullSetBase::referenceSetT() to free it's memory. Probably needed elsewhere
+ - The Boost Graph Library provides C++ classes for graph data structures. It may help
+ enhance Qhull's C++ interface [Dr. Dobb's 9/00 p. 29-38; OOPSLA '99 p. 399-414].
+
+[Jan 2010] Suggestions
+ - Generate vcproj from qtpro files
+ cd qtpro && qmake -spec win32-msvc2005 -tp vc -recursive
+ sed -i 's/C\:\/bash\/local\/qhull\/qtpro\///' qhull-all.sln
+ Change qhullcpp to libqhull.dll
+ Allow both builds on same host (keep /tmp separate)
+ - Make distribution -- remove tmp, news, .git, leftovers from project, change CRLF
+ search for 2010.1, Dates
+ qhulltest --all added to output
+ Add md5sum
+ Add test of user_eg3, etc.
+ - C++ class for access to statistics, accumulate vs. add
+ - Add dialog box to RoadError-- a virtual function?
+ - Option 'Gt' does not make visible all facets of the mesh example, rbox 32 M1,0,1 | qhull d Gt
+ - Option to select bounded Voronoi regions [A. Uzunovic]
+ - Merge small volume boundary cells into unbounded regions [Dominik Szczerba]
+ - Postmerge with merge options
+ - Add const to C code
+ - Add modify operators and MutablePointCoordinateIterator to PointCoordinates
+ - Add Qtest::toString() functions for QhullPoint and others. QByteArray and qstrdup()
+ - Fix option Qt for conformant triangulations of merged facets
+ - Investigate flipped facet -- rbox 100 s D3 t1263080158 | qhull R1e-3 Tcv Qc
+ - Add doc comments to c++ code
+ - Measure performance of Qhull, seconds per point by dimension
+ - Report potential wraparound of 64-bit ints -- e.g., a large set or points
+
+Documentation
+- Qhull::addPoint(). Problems with qh_findbestfacet and otherpoints see
+ qh-code.htm#inc on-line construction with qh_addpoint()
+- How to handle 64-bit possible loss of data. WARN64, ptr_intT, size_t/int
+- Show custom of qh_fprintf
+- grep 'qh_mem ' x | sort | awk '{ print $2; }' | uniq -c | grep -vE ' (2|4|6|8|10|12|14|16|20|64|162)[^0-9]'
+- qtpro/qhulltest contains .pro and Makefile. Remove Makefiles by setting shadow directory to ../../tmp/projectname
+- Rules for use of qh_qh and multi processes
+ UsingQhull
+ errorIfAnotherUser
+ ~QhullPoints() needs ownership of qh_qh
+ Does !qh_pointer work?
+ When is qh_qh required? Minimize the time.
+ qhmem, qhstat.ferr
+ qhull_inuse==1 when qhull globals active [not useful?]
+ rbox_inuse==1 when rbox globals active
+ - Multithreaded -- call largest dimension for infinityPoint() and origin()
+ - Better documentation for qhmem totshort, freesize, etc.
+ - how to change .h, .c, and .cpp to text/html. OK in Opera
+ - QhullVertex.dimension() is not quite correct, epensive
+ - Check globalAngleEpsilon
+ - Deprecate save_qhull()
+
+[Dec 2003] Here is a partial list:
+ - fix finddelaunay() in user_eg.c for tricoplanar facets
+ - write a BGL, C++ interface to Qhull
+ http://www.boost.org/libs/graph/doc/table_of_contents.html
+ - change qh_save_qhull to swap the qhT structure instead of using pointers
+ - change error handling and tracing to be independent of 'qh ferr'
+ - determine the maximum width for a given set of parameters
+ - prove that directed search locates all coplanar facets
+ - in high-d merging, can a loop of facets become disconnected?
+ - find a way to improve inner hulls in 5-d and higher
+ - determine the best policy for facet visibility ('<a href="qh-optc.htm#Vn">Vn</a>')
+ - determine the limitations of '<a href="qh-optq.htm#Qg">Qg</a>'
+
+Precision improvements:
+ - For 'Qt', resolve cross-linked, butterfly ridges.
+ May allow retriangulation in qh_addpoint().
+ - for Delaunay triangulations ('d' or 'v') under joggled input ('QJ'),
+ remove vertical facets whose lowest vertex may be coplanar with convex hull
+ - review use of 'Qbb' with 'd QJ'. Is MAXabs_coord better than MAXwidth?
+ - check Sugihara and Iri's better in-sphere test [Canadian
+ Conf. on Comp. Geo., 1989; Univ. of Tokyo RMI 89-05]
+ - replace centrum with center of mass and facet area
+ - handle numeric overflow in qh_normalize and elsewhere
+ - merge flipped facets into non-flipped neighbors.
+ currently they merge into best neighbor (appears ok)
+ - determine min norm for Cramer's rule (qh_sethyperplane_det). It looks high.
+ - improve facet width for very narrow distributions
+
+New features:
+ - implement Matlab's tsearch() using Qhull
+ - compute volume of Voronoi regions. You need to determine the dual face
+ graph in all dimensions [see Clarkson's hull program]
+ - compute alpha shapes [see Clarkson's hull program]
+ - implement deletion of Delaunay vertices
+ see Devillers, ACM Symposium on Computational Geometry, Minneapolis 1999.
+ - compute largest empty circle [see O'Rourke, chapter 5.5.3] [Hase]
+ - list redundant (i.e., coincident) vertices [Spitz]
+ - implement Mucke, et al, ['96] for point location in Delaunay triangulations
+ - implement convex hull of moving points
+ - implement constrained Delaunay diagrams
+ see Shewchuk, ACM Symposium on Computational Geometry, Minneapolis 1998.
+ - estimate outer volume of hull
+ - automatically determine lower dimensional hulls
+ - allow &quot;color&quot; data for input points
+ need to insert a coordinate for Delaunay triangulations
+
+Input/output improvements:
+ - Support the VTK Visualization Toolkit, http://www.kitware.com/vtk.html
+ - generate output data array for Qhull library [Gautier]
+ - need improved DOS window with screen fonts, scrollbar, cut/paste
+ - generate Geomview output for Voronoi ridges and unbounded rays
+ - generate Geomview output for halfspace intersection
+ - generate Geomview display of furthest-site Voronoi diagram
+ - use '<a href="qh-optg.htm#GDn">GDn</a>' to view 5-d facets in 4-d
+ - convert Geomview output for other 3-d viewers
+ - add interactive output option to avoid recomputing a hull
+ - orient vertex neighbors for '<a href="qh-optf.htm#Fv">Fv</a>' in 3-d and 2-d
+ - track total number of ridges for summary and logging
+
+Performance improvements:
+ - optimize Qhull for 2-d Delaunay triangulations
+ - use O'Rourke's <a href="index.htm#orou94">'94</a> vertex-&gt;duplicate_edge
+ - add bucketing
+ - better to specialize all of the code (ca. 2-3x faster w/o merging)
+ - use updated LU decomposition to speed up hyperplane construction
+ - [Gill et al. 1974, Math. Comp. 28:505-35]
+ - construct hyperplanes from the corresponding horizon/visible facets
+ - for merging in high d, do not use vertex-&gt;neighbors
+
+</pre>
+
+<p>Please let us know about your applications and improvements. </p>
+<!-- Navigation links -->
+<hr>
+
+<p><b>Up:</b> <a href="http://www.qhull.org">Home
+page for Qhull</a> <br>
+<b>Up:</b> <a href="index.htm#TOC">Qhull manual: Table of
+Contents</a><br>
+<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
+&#149; <a href="qh-quick.htm#options">Options</a>
+&#149; <a href="qh-opto.htm#output">Output</a>
+&#149; <a href="qh-optf.htm#format">Formats</a>
+&#149; <a href="qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="qh-optp.htm#print">Print</a>
+&#149; <a href="qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="qh-optc.htm#prec">Precision</a>
+&#149; <a href="qh-optt.htm#trace">Trace</a>
+&#149; <a href="../src/libqhull_r/index.htm">Functions</a><br>
+<b>To:</b> <a href="#TOC">Qhull code</a>: Table of Contents <br>
+<b>Dn:</b> <a href="../src/libqhull_r/index.htm">Qhull functions</a>, macros, and data
+structures <!-- GC common information -->
+
+<hr>
+
+<p><a href="http://www.geom.uiuc.edu/"><img src="qh--geom.gif"
+align="middle" width="40" height="40"></a><i>The Geometry Center
+Home Page </i></p>
+
+<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a>
+</a><br>
+Created: Sept. 25, 1995 --- <!-- hhmts start --> Last modified: see changes.txt <!-- hhmts end --> </p>
+</body>
+</html>
diff --git a/xs/src/qhull/html/qh-eg.htm b/xs/src/qhull/html/qh-eg.htm
new file mode 100644
index 000000000..a08f0d13f
--- /dev/null
+++ b/xs/src/qhull/html/qh-eg.htm
@@ -0,0 +1,693 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+
+<head>
+<title>Examples of Qhull</title>
+</head>
+
+<body>
+<!-- Navigation links -->
+<p><a name="TOP"><b>Up:</b></a> <a href="http://www.qhull.org">Home
+page</a> for Qhull <br>
+<b>Up:</b> <a href="index.htm#TOC">Qhull manual</a>: Table of Contents<br>
+<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
+&#149; <a href="qh-quick.htm#options">Options</a>
+&#149; <a href="qh-opto.htm#output">Output</a>
+&#149; <a href="qh-optf.htm#format">Formats</a>
+&#149; <a href="qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="qh-optp.htm#print">Print</a>
+&#149; <a href="qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="qh-optc.htm#prec">Precision</a>
+&#149; <a href="qh-optt.htm#trace">Trace</a>
+&#149; <a href="../src/libqhull_r/index.htm">Functions</a><br>
+<b>To: </b><a href="#TOC">Qhull examples: Table of Contents</a> (please wait
+while loading)<br>
+
+<hr>
+<!-- Main text of document -->
+<h1><a
+href="http://www.geom.uiuc.edu/graphics/pix/Special_Topics/Computational_Geometry/half.html"><img
+src="qh--half.gif" alt="[halfspace]" align="middle" width="100"
+height="100"></a> Examples of Qhull</h1>
+
+<p>This section of the Qhull manual will introduce you to Qhull
+and its options. Each example is a file for viewing with <a
+href="index.htm#geomview">Geomview</a>. You will need to
+use a Unix computer with a copy of Geomview.
+<p>
+If you are not running Unix, you can view <a
+href="http://www.geom.uiuc.edu/graphics/pix/Special_Topics/Computational_Geometry/welcome.html">pictures</a>
+for some of the examples. To understand Qhull without Geomview, try the
+examples in <a href="qh-quick.htm#programs">Programs</a> and
+<a href="qh-quick.htm#programs">Programs/input</a>. You can also try small
+examples that you compute by hand. Use <a href="rbox.htm">rbox</a>
+to generate examples.
+<p>
+To generate the Geomview examples, execute the shell script <tt>eg/q_eg</tt>.
+It uses <tt>rbox</tt>. The shell script <tt>eg/q_egtest</tt> generates
+test examples, and <tt>eg/q_test</tt> exercises the code. If you
+find yourself viewing the inside of a 3-d example, use Geomview's
+normalization option on the 'obscure' menu.</p>
+
+<p><b>Copyright &copy; 1995-2015 C.B. Barber</b></p>
+
+<hr>
+
+<h2><a href="#TOP">&#187;</a><a name="TOC">Qhull examples: Table of
+Contents </a></h2>
+
+<ul>
+ <li><a href="#2d">2-d and 3-d examples</a></li>
+ <li><a href="#how">How Qhull adds a point</a></li>
+ <li><a href="#joggle">Triangulated output or joggled input</a></li>
+ <li><a href="#delaunay">Delaunay and Voronoi diagrams</a></li>
+ <li><a href="#merge">Facet merging for imprecision</a></li>
+ <li><a href="#4d">4-d objects</a></li>
+ <li><a href="#half">Halfspace intersections</a></li>
+</ul>
+
+<hr>
+<ul>
+ <li><a href="#TOC">&#187;</a><a name="2d">2-d and 3-d examples</a><ul>
+ <li><a href="#01">eg.01.cube</a></li>
+ <li><a href="#02">eg.02.diamond.cube</a></li>
+ <li><a href="#03">eg.03.sphere</a></li>
+ <li><a href="#04">eg.04.circle</a></li>
+ <li><a href="#05">eg.05.spiral</a></li>
+ <li><a href="#06">eg.06.merge.square</a></li>
+ <li><a href="#07">eg.07.box</a></li>
+ <li><a href="#08a">eg.08a.cube.sphere</a></li>
+ <li><a href="#08b">eg.08b.diamond.sphere</a></li>
+ <li><a href="#09">eg.09.lens</a></li>
+ </ul>
+ </li>
+ <li><a href="#TOC">&#187;</a><a name="how">How Qhull adds a point</a><ul>
+ <li><a href="#10a">eg.10a.sphere.visible</a></li>
+ <li><a href="#10b">eg.10b.sphere.beyond</a></li>
+ <li><a href="#10c">eg.10c.sphere.horizon</a></li>
+ <li><a href="#10d">eg.10d.sphere.cone</a></li>
+ <li><a href="#10e">eg.10e.sphere.new</a></li>
+ <li><a href="#14">eg.14.sphere.corner</a></li>
+ </ul>
+ </li>
+ <li><a href="#TOC">&#187;</a> <a name="joggle">Triangulated output or joggled input</a>
+ <ul>
+ <li><a href="#15a">eg.15a.surface</a></li>
+ <li><a href="#15b">eg.15b.triangle</a></li>
+ <li><a href="#15c">eg.15c.joggle</a></li>
+ </ul>
+ <li><a href="#TOC">&#187;</a><a name="delaunay"> Delaunay and
+ Voronoi diagrams</a><ul>
+ <li><a href="#17a">eg.17a.delaunay.2</a></li>
+ <li><a href="#17b">eg.17b.delaunay.2i</a></li>
+ <li><a href="#17c">eg.17c.delaunay.2-3</a></li>
+ <li><a href="#17d">eg.17d.voronoi.2</a></li>
+ <li><a href="#17e">eg.17e.voronoi.2i</a></li>
+ <li><a href="#17f">eg.17f.delaunay.3</a></li>
+ <li><a href="#18a">eg.18a.furthest.2-3</a></li>
+ <li><a href="#18b">eg.18b.furthest-up.2-3</a></li>
+ <li><a href="#18c">eg.18c.furthest.2</a></li>
+ <li><a href="#19">eg.19.voronoi.region.3</a></li>
+ </ul>
+ </li>
+ <li><a href="#TOC">&#187;</a><a name="merge">Facet merging for
+ imprecision </a><ul>
+ <li><a href="#20">eg.20.cone</a></li>
+ <li><a href="#21a">eg.21a.roundoff.errors</a></li>
+ <li><a href="#21b">eg.21b.roundoff.fixed</a></li>
+ <li><a href="#22a">eg.22a.merge.sphere.01</a></li>
+ <li><a href="#22b">eg.22b.merge.sphere.-01</a></li>
+ <li><a href="#22c">eg.22c.merge.sphere.05</a></li>
+ <li><a href="#22d">eg.22d.merge.sphere.-05</a></li>
+ <li><a href="#23">eg.23.merge.cube</a></li>
+ </ul>
+ </li>
+ <li><a href="#TOC">&#187;</a><a name="4d">4-d objects</a><ul>
+ <li><a href="#24">eg.24.merge.cube.4d-in-3d</a></li>
+ <li><a href="#30">eg.30.4d.merge.cube</a></li>
+ <li><a href="#31">eg.31.4d.delaunay</a></li>
+ <li><a href="#32">eg.32.4d.octant</a></li>
+ </ul>
+ </li>
+ <li><a href="#TOC">&#187;</a><a name="half">Halfspace
+ intersections</a><ul>
+ <li><a href="#33a">eg.33a.cone</a></li>
+ <li><a href="#33b">eg.33b.cone.dual</a></li>
+ <li><a href="#33c">eg.33c.cone.halfspace</a></li>
+ </ul>
+ </li>
+</ul>
+
+<hr>
+
+<h2><a href="#TOC">&#187;</a>2-d and 3-d examples</h2>
+
+<h3><a href="#2d">&#187;</a><a name="01">rbox c D3 | qconvex G
+&gt;eg.01.cube </a></h3>
+
+<p>The first example is a cube in 3-d. The color of each facet
+indicates its normal. For example, normal [0,0,1] along the Z
+axis is (r=0.5, g=0.5, b=1.0). With the 'Dn' option in <tt>rbox</tt>,
+you can generate hypercubes in any dimension. Above 7-d the
+number of intermediate facets grows rapidly. Use '<a
+href="qh-optt.htm#TFn">TFn</a>' to track qconvex's progress. Note
+that each facet is a square that qconvex merged from coplanar
+triangles.</p>
+
+<h3><a href="#2d">&#187;</a><a name="02">rbox c d G3.0 | qconvex G
+&gt;eg.02.diamond.cube </a></h3>
+
+<p>The second example is a cube plus a diamond ('d') scaled by <tt>rbox</tt>'s
+'G' option. In higher dimensions, diamonds are much simpler than
+hypercubes. </p>
+
+<h3><a href="#2d">&#187;</a><a name="03">rbox s 100 D3 | qconvex G
+&gt;eg.03.sphere </a></h3>
+
+<p>The <tt>rbox s</tt> option generates random points and
+projects them to the d-sphere. All points should be on the convex
+hull. Notice that random points look more clustered than you
+might expect. You can get a smoother distribution by merging
+facets and printing the vertices, e.g.,<i> rbox 1000 s | qconvex
+A-0.95 p | qconvex G &gt;eg.99</i>.</p>
+
+<h3><a href="#2d">&#187;</a><a name="04">rbox s 100 D2 | qconvex G
+&gt;eg.04.circle </a></h3>
+
+<p>In 2-d, there are many ways to generate a convex hull. One of
+the earliest algorithms, and one of the fastest, is the 2-d
+Quickhull algorithm [c.f., Preparata &amp; Shamos <a
+href="index.htm#pre-sha85">'85</a>]. It was the model for
+Qhull.</p>
+
+<h3><a href="#2d">&#187;</a><a name="05">rbox 10 l | qconvex G
+&gt;eg.05.spiral </a></h3>
+
+<p>One rotation of a spiral.</p>
+
+<h3><a href="#2d">&#187;</a><a name="06">rbox 1000 D2 | qconvex C-0.03
+Qc Gapcv &gt;eg.06.merge.square</a></h3>
+
+<p>This demonstrates how Qhull handles precision errors. Option '<a
+href="qh-optc.htm#Cn">C-0.03</a>' requires a clearly convex angle
+between adjacent facets. Otherwise, Qhull merges the facets. </p>
+
+<p>This is the convex hull of random points in a square. The
+facets have thickness because they must be outside all points and
+must include their vertices. The colored lines represent the
+original points and the spheres represent the vertices. Floating
+in the middle of each facet is the centrum. Each centrum is at
+least 0.03 below the planes of its neighbors. This guarantees
+that the facets are convex.</p>
+
+<h3><a href="#2d">&#187;</a><a name="07">rbox 1000 D3 | qconvex G
+&gt;eg.07.box </a></h3>
+
+<p>Here's the same distribution but in 3-d with Qhull handling
+machine roundoff errors. Note the large number of facets. </p>
+
+<h3><a href="#2d">&#187;</a><a name="08a">rbox c G0.4 s 500 | qconvex G
+&gt;eg.08a.cube.sphere </a></h3>
+
+<p>The sphere is just barely poking out of the cube. Try the same
+distribution with randomization turned on ('<a
+href="qh-optq.htm#Qr">Qr</a>'). This turns Qhull into a
+randomized incremental algorithm. To compare Qhull and
+randomization, look at the number of hyperplanes created and the
+number of points partitioned. Don't compare CPU times since Qhull's
+implementation of randomization is inefficient. The number of
+hyperplanes and partitionings indicate the dominant costs for
+Qhull. With randomization, you'll notice that the number of
+facets created is larger than before. This is especially true as
+you increase the number of points. It is because the randomized
+algorithm builds most of the sphere before it adds the cube's
+vertices.</p>
+
+<h3><a href="#2d">&#187;</a><a name="08b">rbox d G0.6 s 500 | qconvex G
+&gt;eg.08b.diamond.sphere </a></h3>
+
+<p>This is a combination of the diamond distribution and the
+sphere.</p>
+
+<h3><a href="#2d">&#187;</a><a name="09">rbox 100 L3 G0.5 s | qconvex
+G &gt;eg.09.lens </a></h3>
+
+<p>Each half of the lens distribution lies on a sphere of radius
+three. A directed search for the furthest facet below a point
+(e.g., qh_findbest in <tt>geom.c</tt>) may fail if started from
+an arbitrary facet. For example, if the first facet is on the
+opposite side of the lens, a directed search will report that the
+point is inside the convex hull even though it is outside. This
+problem occurs whenever the curvature of the convex hull is less
+than a sphere centered at the test point. </p>
+
+<p>To prevent this problem, Qhull does not use directed search
+all the time. When Qhull processes a point on the edge of the
+lens, it partitions the remaining points with an exhaustive
+search instead of a directed search (see qh_findbestnew in <tt>geom2.c</tt>).
+</p>
+
+<h2><a href="#TOC">&#187;</a>How Qhull adds a point</h2>
+
+<h3><a href="#how">&#187;</a><a name="10a">rbox 100 s P0.5,0.5,0.5 |
+qconvex Ga QG0 &gt;eg.10a.sphere.visible</a></h3>
+
+<p>The next 4 examples show how Qhull adds a point. The point
+[0.5,0.5,0.5] is at one corner of the bounding box. Qhull adds a
+point using the beneath-beyond algorithm. First Qhull finds all
+of the facets that are visible from the point. Qhull will replace
+these facets with new facets.</p>
+
+<h3><a href="#how">&#187;</a><a name="10b">rbox 100 s
+P0.5,0.5,0.5|qconvex Ga QG-0 &gt;eg.10b.sphere.beyond </a></h3>
+
+<p>These are the facets that are not visible from the point.
+Qhull will keep these facets.</p>
+
+<h3><a href="#how">&#187;</a><a name="10c">rbox 100 s P0.5,0.5,0.5 |
+qconvex PG Ga QG0 &gt;eg.10c.sphere.horizon </a></h3>
+
+<p>These facets are the horizon facets; they border the visible
+facets. The inside edges are the horizon ridges. Each horizon
+ridge will form the base for a new facet.</p>
+
+<h3><a href="#how">&#187;</a><a name="10d">rbox 100 s P0.5,0.5,0.5 |
+qconvex Ga QV0 PgG &gt;eg.10d.sphere.cone </a></h3>
+
+<p>This is the cone of points from the new point to the horizon
+facets. Try combining this image with <tt>eg.10c.sphere.horizon</tt>
+and <tt>eg.10a.sphere.visible</tt>.
+</p>
+
+<h3><a href="#how">&#187;</a><a name="10e">rbox 100 s P0.5,0.5,0.5 |
+qconvex Ga &gt;eg.10e.sphere.new</a></h3>
+
+<p>This is the convex hull after [0.5,0.5,0.5] has been added.
+Note that in actual practice, the above sequence would never
+happen. Unlike the randomized algorithms, Qhull always processes
+a point that is furthest in an outside set. A point like
+[0.5,0.5,0.5] would be one of the first points processed.</p>
+
+<h3><a href="#how">&#187;</a><a name="14">rbox 100 s P0.5,0.5,0.5 |
+qhull Ga QV0g Q0 &gt;eg.14.sphere.corner</a></h3>
+
+<p>The '<a href="qh-optq.htm#QVn">QVn</a>', '<a
+href="qh-optq.htm#QGn">QGn </a>' and '<a href="qh-optp.htm#Pdk">Pdk</a>'
+options define good facets for Qhull. In this case '<a
+href="qh-optq.htm#QVn">QV0</a>' defines the 0'th point
+[0.5,0.5,0.5] as the good vertex, and '<a href="qh-optq.htm#Qg">Qg</a>'
+tells Qhull to only build facets that might be part of a good
+facet. This technique reduces output size in low dimensions. It
+does not work with facet merging.</p>
+
+<h2><a href="#TOC">&#187;</a>Triangulated output or joggled input</h2>
+
+<h3><a href="#joggle">&#187;</a><a name="15a">rbox 500 W0 | qconvex QR0 Qc Gvp &gt;eg.15a.surface</a></h3>
+
+<p>This is the convex hull of 500 points on the surface of
+a cube. Note the large, non-simplicial facet for each face.
+Qhull merges non-convex facets.
+
+<p>If the facets were not merged, Qhull
+would report precision problems. For example, turn off facet merging
+with option '<a href="qh-optq.htm#Q0">Q0</a>'. Qhull may report concave
+facets, flipped facets, or other precision errors:
+<blockquote>
+rbox 500 W0 | qhull QR0 Q0
+</blockquote>
+
+<p>
+<h3><a href="#joggle">&#187;</a><a name="15b">rbox 500 W0 | qconvex QR0 Qt Qc Gvp &gt;eg.15b.triangle</a></h3>
+
+<p>Like the previous examples, this is the convex hull of 500 points on the
+surface of a cube. Option '<a href="qh-optq.htm#Qt">Qt</a>' triangulates the
+non-simplicial facets. Triangulated output is
+particularly helpful for Delaunay triangulations.
+
+<p>
+<h3><a href="#joggle">&#187;</a><a name="15c">rbox 500 W0 | qconvex QR0 QJ5e-2 Qc Gvp &gt;eg.15c.joggle</a></h3>
+
+<p>This is the convex hull of 500 joggled points on the surface of
+a cube. The option '<a href="qh-optq.htm#QJn">QJ5e-2</a>'
+sets a very large joggle to make the effect visible. Notice
+that all of the facets are triangles. If you rotate the cube,
+you'll see red-yellow lines for coplanar points.
+<p>
+With option '<a href="qh-optq.htm#QJn">QJ</a>', Qhull joggles the
+input to avoid precision problems. It adds a small random number
+to each input coordinate. If a precision
+error occurs, it increases the joggle and tries again. It repeats
+this process until no precision problems occur.
+<p>
+Joggled input is a simple solution to precision problems in
+computational geometry. Qhull can also merge facets to handle
+precision problems. See <a href="qh-impre.htm#joggle">Merged facets or joggled input</a>.
+
+<h2><a href="#TOC">&#187;</a>Delaunay and Voronoi diagrams</h2>
+
+<h3><a href="#delaunay">&#187;</a><a name="17a">qdelaunay Qt
+&lt;eg.data.17 GnraD2 &gt;eg.17a.delaunay.2</a></h3>
+
+<p>
+The input file, <tt>eg.data.17</tt>, consists of a square, 15 random
+points within the outside half of the square, and 6 co-circular
+points centered on the square.
+
+<p>The Delaunay triangulation is the triangulation with empty
+circumcircles. The input for this example is unusual because it
+includes six co-circular points. Every triangular subset of these
+points has the same circumcircle. Option '<a href="qh-optq.htm#Qt">Qt</a>'
+triangulates the co-circular facet.</p>
+
+<h3><a href="#delaunay">&#187;</a><a name="17b">qdelaunay &lt;eg.data.17
+GnraD2 &gt;eg.17b.delaunay.2i</a></h3>
+
+<p>This is the same example without triangulated output ('<a href="qh-optq.htm#Qt">Qt</a>'). qdelaunay
+merges the non-unique Delaunay triangles into a hexagon.</p>
+
+<h3><a href="#delaunay">&#187;</a><a name="17c">qdelaunay &lt;eg.data.17
+Ga &gt;eg.17c.delaunay.2-3 </a></h3>
+
+<p>This is how Qhull generated both diagrams. Use Geomview's
+'obscure' menu to turn off normalization, and Geomview's
+'cameras' menu to turn off perspective. Then load this <a
+href="http://www.geom.uiuc.edu/graphics/pix/Special_Topics/Computational_Geometry/delaunay.html">object</a>
+with one of the previous diagrams.</p>
+
+<p>The points are lifted to a paraboloid by summing the squares
+of each coordinate. These are the light blue points. Then the
+convex hull is taken. That's what you see here. If you look up
+the Z-axis, you'll see that points and edges coincide.</p>
+
+<h3><a href="#delaunay">&#187;</a><a name="17d">qvoronoi QJ
+&lt;eg.data.17 Gna &gt;eg.17d.voronoi.2</a></h3>
+
+<p>The Voronoi diagram is the dual of the Delaunay triangulation.
+Here you see the original sites and the Voronoi vertices.
+Notice the each
+vertex is equidistant from three sites. The edges indicate the
+Voronoi region for a site. Qhull does not draw the unbounded
+edges. Instead, it draws extra edges to close the unbounded
+Voronoi regions. You may find it helpful to enclose the input
+points in a square. You can compute the unbounded
+rays from option '<a href="qh-optf.htm#Fo2">Fo</a>'.
+</p>
+
+<p>Instead
+of triangulated output ('<a href="qh-optq.htm#Qt">Qt</a>'), this
+example uses joggled input ('<a href="qh-optq.htm#QJn">QJ</a>').
+Normally, you should use neither 'QJ' nor 'Qt' for Voronoi diagrams.
+
+<h3><a href="#delaunay">&#187;</a><a name="17e">qvoronoi &lt;eg.data.17
+Gna &gt;eg.17e.voronoi.2i </a></h3>
+
+<p>This looks the same as the previous diagrams, but take a look
+at the data. Run 'qvoronoi p &lt;eg/eg.data.17'. This prints
+the Voronoi vertices.
+
+<p>With 'QJ', there are four nearly identical Voronoi vertices
+within 10^-11 of the origin. Option 'QJ' joggled the input. After the joggle,
+the cocircular
+input sites are no longer cocircular. The corresponding Voronoi vertices are
+similar but not identical.
+
+<p>This example does not use options 'Qt' or 'QJ'. The cocircular
+input sites define one Voronoi vertex near the origin. </p>
+
+<p>Option 'Qt' would triangulate the corresponding Delaunay region into
+four triangles. Each triangle is assigned the same Voronoi vertex.</p>
+
+<h3><a href="#delaunay">&#187;</a><a name="17f"> rbox c G0.1 d |
+qdelaunay Gt Qz &lt;eg.17f.delaunay.3 </a></h3>
+
+<p>This is the 3-d Delaunay triangulation of a small cube inside
+a prism. Since the outside ridges are transparent, it shows the
+interior of the outermost facets. If you slice open the
+triangulation with Geomview's ginsu, you will see that the innermost
+facet is a cube. Note the use of '<a href="qh-optq.htm#Qz">Qz</a>'
+to add a point &quot;at infinity&quot;. This avoids a degenerate
+input due to cospherical points.</p>
+
+<h3><a href="#delaunay">&#187;</a><a name="18a">rbox 10 D2 d | qdelaunay
+Qu G &gt;eg.18a.furthest.2-3 </a></h3>
+
+<p>The furthest-site Voronoi diagram contains Voronoi regions for
+points that are <i>furthest </i>from an input site. It is the
+dual of the furthest-site Delaunay triangulation. You can
+determine the furthest-site Delaunay triangulation from the
+convex hull of the lifted points (<a href="#17c">eg.17c.delaunay.2-3</a>).
+The upper convex hull (blue) generates the furthest-site Delaunay
+triangulation. </p>
+
+<h3><a href="#delaunay">&#187;</a><a name="18b">rbox 10 D2 d | qdelaunay
+Qu Pd2 G &gt;eg.18b.furthest-up.2-3</a></h3>
+
+<p>This is the upper convex hull of the preceding example. The
+furthest-site Delaunay triangulation is the projection of the
+upper convex hull back to the input points. The furthest-site
+Voronoi vertices are the circumcenters of the furthest-site
+Delaunay triangles. </p>
+
+<h3><a href="#delaunay">&#187;</a><a name="18c">rbox 10 D2 d | qvoronoi
+Qu Gv &gt;eg.18c.furthest.2</a></h3>
+
+<p>This shows an incomplete furthest-site Voronoi diagram. It
+only shows regions with more than two vertices. The regions are
+artificially truncated. The actual regions are unbounded. You can
+print the regions' vertices with 'qvoronoi Qu <a
+href="qh-opto.htm#o">o</a>'. </p>
+
+<p>Use Geomview's 'obscure' menu to turn off normalization, and
+Geomview's 'cameras' menu to turn off perspective. Then load this
+with the upper convex hull.</p>
+
+<h3><a href="#delaunay">&#187;</a><a name="19">rbox 10 D3 | qvoronoi QV5
+p | qconvex G &gt;eg.19.voronoi.region.3 </a></h3>
+
+<p>This shows the Voronoi region for input site 5 of a 3-d
+Voronoi diagram.</p>
+
+<h2><a href="#TOC">&#187;</a>Facet merging for imprecision</h2>
+
+<h3><a href="#merge">&#187;</a><a name="20">rbox r s 20 Z1 G0.2 |
+qconvex G &gt;eg.20.cone </a></h3>
+
+<p>There are two things unusual about this <a
+href="http://www.geom.uiuc.edu/graphics/pix/Special_Topics/Computational_Geometry/cone.html">cone</a>.
+One is the large flat disk at one end and the other is the
+rectangles about the middle. That's how the points were
+generated, and if those points were exact, this is the correct
+hull. But <tt>rbox</tt> used floating point arithmetic to
+generate the data. So the precise convex hull should have been
+triangles instead of rectangles. By requiring convexity, Qhull
+has recovered the original design.</p>
+
+<h3><a href="#merge">&#187;</a><a name="21a">rbox 200 s | qhull Q0
+R0.01 Gav Po &gt;eg.21a.roundoff.errors </a></h3>
+
+<p>This is the convex hull of 200 cospherical points with
+precision errors ignored ('<a href="qh-optq.htm#Q0">Q0</a>'). To
+demonstrate the effect of roundoff error, we've added a random
+perturbation ('<a href="qh-optc.htm#Rn">R0.01</a>') to every
+distance and hyperplane calculation. Qhull, like all other convex
+hull algorithms with floating point arithmetic, makes
+inconsistent decisions and generates wildly wrong results. In
+this case, one or more facets are flipped over. These facets have
+the wrong color. You can also turn on 'normals' in Geomview's
+appearances menu and turn off 'facing normals'. There should be
+some white lines pointing in the wrong direction. These
+correspond to flipped facets. </p>
+
+<p>Different machines may not produce this picture. If your
+machine generated a long error message, decrease the number of
+points or the random perturbation ('<a href="qh-optc.htm#Rn">R0.01</a>').
+If it did not report flipped facets, increase the number of
+points or perturbation.</p>
+
+<h3><a href="#merge">&#187;</a><a name="21b">rbox 200 s | qconvex Qc
+R0.01 Gpav &gt;eg.21b.roundoff.fixed </a></h3>
+
+<p>Qhull handles the random perturbations and returns an
+imprecise <a
+href="http://www.geom.uiuc.edu/graphics/pix/Special_Topics/Computational_Geometry/fixed.html">sphere</a>.
+In this case, the output is a weak approximation to the points.
+This is because a random perturbation of '<a
+href="qh-optc.htm#Rn">R0.01 </a>' is equivalent to losing all but
+1.8 digits of precision. The outer planes float above the points
+because Qhull needs to allow for the maximum roundoff error. </p>
+<p>
+If you start with a smaller random perturbation, you
+can use joggle ('<a href="qh-optq.htm#QJn">QJn</a>') to avoid
+precision problems. You need to set <i>n</i> significantly
+larger than the random perturbation. For example, try
+'rbox 200 s | qconvex Qc R1e-4 QJ1e-1'.
+
+<h3><a href="#merge">&#187;</a><a name="22a">rbox 1000 s| qconvex C0.01
+Qc Gcrp &gt;eg.22a.merge.sphere.01</a></h3>
+
+<h3><a href="#merge">&#187;</a><a name="22b">rbox 1000 s| qconvex
+C-0.01 Qc Gcrp &gt;eg.22b.merge.sphere.-01</a></h3>
+
+<h3><a href="#merge">&#187;</a><a name="22c">rbox 1000 s| qconvex C0.05
+Qc Gcrpv &gt;eg.22c.merge.sphere.05</a></h3>
+
+<h3><a href="#merge">&#187;</a><a name="22d">rbox 1000 s| qconvex
+C-0.05 Qc Gcrpv &gt;eg.22d.merge.sphere.-05</a></h3>
+
+<p>The next four examples compare post-merging and pre-merging ('<a
+href="qh-optc.htm#Cn2">Cn</a>' vs. '<a href="qh-optc.htm#Cn">C-n</a>').
+Qhull uses '-' as a flag to indicate pre-merging.</p>
+
+<p>Post-merging happens after the convex hull is built. During
+post-merging, Qhull repeatedly merges an independent set of
+non-convex facets. For a given set of parameters, the result is
+about as good as one can hope for.</p>
+
+<p>Pre-merging does the same thing as post-merging, except that
+it happens after adding each point to the convex hull. With
+pre-merging, Qhull guarantees a convex hull, but the facets are
+wider than those from post-merging. If a pre-merge option is not
+specified, Qhull handles machine round-off errors.</p>
+
+<p>You may see coplanar points appearing slightly outside
+the facets of the last example. This is becomes Geomview moves
+line segments forward toward the viewer. You can avoid the
+effect by setting 'lines closer' to '0' in Geomview's camera menu.
+
+<h3><a href="#merge">&#187;</a><a name="23">rbox 1000 | qconvex s
+Gcprvah C0.1 Qc &gt;eg.23.merge.cube</a></h3>
+
+<p>Here's the 3-d imprecise cube with all of the Geomview
+options. There's spheres for the vertices, radii for the coplanar
+points, dots for the interior points, hyperplane intersections,
+centrums, and inner and outer planes. The radii are shorter than
+the spheres because this uses post-merging ('<a href="qh-optc.htm#Cn2">C0.1</a>')
+instead of pre-merging.
+
+<h2><a href="#TOC">&#187;</a>4-d objects</h2>
+
+<p>With Qhull and Geomview you can develop an intuitive sense of
+4-d surfaces. When you get into trouble, think of viewing the
+surface of a 3-d sphere in a 2-d plane.</p>
+
+<h3><a href="#4d">&#187;</a><a name="24">rbox 5000 D4 | qconvex s GD0v
+Pd0:0.5 C-0.02 C0.1 &gt;eg.24.merge.cube.4d-in-3d</a></h3>
+
+<p>Here's one facet of the imprecise cube in 4-d. It's projected
+into 3-d (the '<a href="qh-optg.htm#GDn">GDn</a>' option drops
+dimension n). Each ridge consists of two triangles between this
+facet and the neighboring facet. In this case, Geomview displays
+the topological ridges, i.e., as triangles between three
+vertices. That is why the cube looks lopsided.</p>
+
+<h3><a href="#4d">&#187;</a><a name="30">rbox 5000 D4 | qconvex s
+C-0.02 C0.1 Gh &gt;eg.30.4d.merge.cube </a></h3>
+
+<p><a
+href="http://www.geom.uiuc.edu/graphics/pix/Special_Topics/Computational_Geometry/4dcube.html">Here</a>
+is the equivalent in 4-d of the imprecise <a href="#06">square</a>
+and imprecise <a href="#23">cube</a>. It's the imprecise convex
+hull of 5000 random points in a hypercube. It's a full 4-d object
+so Geomview's <tt>ginsu </tt>does not work. If you view it in
+Geomview, you'll be inside the hypercube. To view 4-d objects
+directly, either load the <tt>4dview</tt> module or the <tt>ndview
+</tt>module. For <tt>4dview</tt>, you must have started Geomview
+in the same directory as the object. For <tt>ndview</tt>,
+initialize a set of windows with the prefab menu, and load the
+object through Geomview. The <tt>4dview</tt> module includes an
+option for slicing along any hyperplane. If you do this in the x,
+y, or z plane, you'll see the inside of a hypercube.</p>
+
+<p>The '<a href="qh-optg.htm#Gh">Gh</a>' option prints the
+geometric intersections between adjacent facets. Note the strong
+convexity constraint for post-merging ('<a href="qh-optc.htm#Cn2">C0.1</a>').
+It deletes the small facets.</p>
+
+<h3><a href="#4d">&#187;</a><a name="31">rbox 20 D3 | qdelaunay G
+&gt;eg.31.4d.delaunay </a></h3>
+
+<p>The Delaunay triangulation of 3-d sites corresponds to a 4-d
+convex hull. You can't see 4-d directly but each facet is a 3-d
+object that you can project to 3-d. This is exactly the same as
+projecting a 2-d facet of a soccer ball onto a plane.</p>
+
+<p>Here we see all of the facets together. You can use Geomview's
+<tt>ndview</tt> to look at the object from several directions.
+Try turning on edges in the appearance menu. You'll notice that
+some edges seem to disappear. That's because the object is
+actually two sets of overlapping facets.</p>
+
+<p>You can slice the object apart using Geomview's <tt>4dview</tt>.
+With <tt>4dview</tt>, try slicing along the w axis to get a
+single set of facets and then slice along the x axis to look
+inside. Another interesting picture is to slice away the equator
+in the w dimension.</p>
+
+<h3><a href="#4d">&#187;</a><a name="32">rbox 30 s D4 | qconvex s G
+Pd0d1d2D3</a></h3>
+
+<p>This is the positive octant of the convex hull of 30 4-d
+points. When looking at 4-d, it's easier to look at just a few
+facets at once. If you picked a facet that was directly above
+you, then that facet looks exactly the same in 3-d as it looks in
+4-d. If you pick several facets, then you need to imagine that
+the space of a facet is rotated relative to its neighbors. Try
+Geomview's <tt>ndview</tt> on this example.</p>
+
+<h2><a href="#TOC">&#187;</a>Halfspace intersections</h2>
+
+<h3><a href="#half">&#187;</a><a name="33a">rbox 10 r s Z1 G0.3 |
+qconvex G &gt;eg.33a.cone </a></h3>
+
+<h3><a href="#half">&#187;</a><a name="33b">rbox 10 r s Z1 G0.3 |
+qconvex FV n | qhalf G &gt;eg.33b.cone.dual</a></h3>
+
+<h3><a href="#half">&#187;</a><a name="33c">rbox 10 r s Z1 G0.3 |
+qconvex FV n | qhalf Fp | qconvex G &gt;eg.33c.cone.halfspace</a></h3>
+
+<p>These examples illustrate halfspace intersection. The first
+picture is the convex hull of two 20-gons plus an apex. The
+second picture is the dual of the first. Try loading <a
+href="http://www.geom.uiuc.edu/graphics/pix/Special_Topics/Computational_Geometry/half.html">both</a>
+at once. Each vertex of the second picture corresponds to a facet
+or halfspace of the first. The vertices with four edges
+correspond to a facet with four neighbors. Similarly the facets
+correspond to vertices. A facet's normal coefficients divided by
+its negative offset is the vertice's coordinates. The coordinates
+are the intersection of the original halfspaces. </p>
+
+<p>The third picture shows how Qhull can go back and forth
+between equivalent representations. It starts with a cone,
+generates the halfspaces that define each facet of the cone, and
+then intersects these halfspaces. Except for roundoff error, the
+third picture is a duplicate of the first. </p>
+<!-- Navigation links -->
+<hr>
+
+<p><b>Up:</b> <a href="http://www.qhull.org">Home
+page for Qhull</a> <br>
+<b>Up:</b> <a href="index.htm#TOC">Qhull manual: Table of Contents</a><br>
+<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
+&#149; <a href="qh-quick.htm#options">Options</a>
+&#149; <a href="qh-opto.htm#output">Output</a>
+&#149; <a href="qh-optf.htm#format">Formats</a>
+&#149; <a href="qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="qh-optp.htm#print">Print</a>
+&#149; <a href="qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="qh-optc.htm#prec">Precision</a>
+&#149; <a href="qh-optt.htm#trace">Trace</a>
+&#149; <a href="../src/libqhull_r/index.htm">Functions</a><br>
+<b>To: </b><a href="#TOC">Qhull examples: Table of Contents</a> (please wait
+while loading)<br>
+<!-- GC common information -->
+<hr>
+
+<p><a href="http://www.geom.uiuc.edu/"><img src="qh--geom.gif"
+align="middle" width="40" height="40"></a><i>The Geometry Center
+Home Page </i></p>
+
+<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a>
+</a><br>
+Created: Sept. 25, 1995 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p>
+</body>
+</html>
diff --git a/xs/src/qhull/html/qh-faq.htm b/xs/src/qhull/html/qh-faq.htm
new file mode 100644
index 000000000..feda544a7
--- /dev/null
+++ b/xs/src/qhull/html/qh-faq.htm
@@ -0,0 +1,1547 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+
+<head>
+<meta http-equiv="Content-Type"
+content="text/html; charset=iso-8859-1">
+<meta name="GENERATOR" content="Microsoft FrontPage 2.0">
+<title>Qhull FAQ</title>
+<!-- Navigation links
+NOTE -- verify all links by 'grep href=' 'grep name=' add # 'sort /+7'
+<base href> does not work since #TOC is relative to base instead of doc
+-->
+</head>
+
+<body>
+
+<p><a name="TOP"><b>Up:</b></a> <a
+ href="http://www.qhull.org">Home page</a> for Qhull
+(http://www.qhull.org)<br>
+<b>Up:</b> <a href="index.htm#TOC">Qhull manual</a>: Table of Contents<br>
+<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
+&#149; <a href="qh-quick.htm#options">Options</a>
+&#149; <a href="qh-opto.htm#output">Output</a>
+&#149; <a href="qh-optf.htm#format">Formats</a>
+&#149; <a href="qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="qh-optp.htm#print">Print</a>
+&#149; <a href="qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="qh-optc.htm#prec">Precision</a>
+&#149; <a href="qh-optt.htm#trace">Trace</a>
+&#149; <a href="../src/libqhull_r/index.htm">Functions</a><br>
+<b>To:</b> <a href="#TOC">FAQ: Table of Contents</a> (please
+wait while loading) <br>
+
+<hr>
+<!-- Main text of document -->
+<h1><a
+ href="http://www.geom.uiuc.edu/graphics/pix/Special_Topics/Computational_Geometry/4dcube.html"><IMG
+ align=middle alt ="[4-d cube]"
+ height=100 src="qh--4d.gif" width=100 ></a> Frequently Asked Questions about Qhull</h1><!--
+<p><i>This is the most recent FAQ. It was updated Sept. 13, 1999.</i>
+-->
+<p>If your question does not appear here, see: </p>
+
+<ul>
+ <li><a href="http://www.qhull.org/news">News</a> about Qhull
+ <li><a href="index.htm#TOC">Qhull manual:</a> table of contents
+ <li><a href="../README.txt">Installation</a> instructions for Qhull and rbox
+
+ <li><a href="mailto:qhull@qhull.org">Send e-mail</a> to
+ qhull@qhull.org
+ <li><a href="mailto:qhull_bug@qhull.org">Report bugs</a>
+ to qhull_bug@qhull.org </li>
+</ul>
+
+<p>Qhull is a general dimension code for computing convex hulls,
+Delaunay triangulations, halfspace intersections about a point,
+Voronoi diagrams, furthest-site Delaunay triangulations, and
+furthest-site Voronoi diagrams. These structures have
+applications in science, engineering, statistics, and
+mathematics. For a detailed introduction, see O'Rourke [<a
+ href="index.htm#orou94" >'94</a>], <i>Computational Geometry in C</i>.
+</p>
+
+<p>There are separate programs for each application of
+Qhull. These programs disable experimental and inappropriate
+options. If you prefer, you may use Qhull directly. All programs
+run the same code.
+
+<p>Version 3.1 added triangulated output ('<a href="qh-optq.htm#Qt">Qt</a>').
+It should be used for Delaunay triangulations instead of
+using joggled input ('<a href="qh-optq.htm#QJn">QJ</a>').
+
+<p><i>Brad Barber, Arlington MA,
+2010/01/09 <!--
+--> </i></p>
+
+<p><b>Copyright &copy; 1998-2015 C.B. Barber</b></p>
+
+<hr>
+
+<h2><a href="#TOP">&#187;</a><a name="TOC">FAQ: Table of Contents </a></h2>
+
+<p>Within each category, the most recently asked questions are
+first.
+<ul>
+ <li>Startup questions <ul>
+ <li><a href="#console">How</a> do I run Qhull from Windows?
+ <li><a href="#input">How</a> do I enter points for Qhull?
+ <li><a href="#learn">How</a> do I learn to use Qhull?</li>
+ </ul>
+ <li>Convex hull questions<ul>
+ <li><a href="#area">How</a> do I report just the area and volume of a
+ convex hull?
+ <li><a href="#extra">Why</a> are there extra points in a 4-d or higher
+ convex hull?
+ <li><a href="#dup">How</a> do I report duplicate
+ vertices? </li>
+ </ul>
+ <li>Delaunay triangulation questions<ul>
+ <li><a href="#flat">How</a> do I get rid of nearly flat Delaunay
+ triangles?
+ <li><a href="#vclosest">How</a> do I find the Delaunay triangle or Voronoi
+ region that is closest to a point?
+
+ <li><a href="#mesh">How</a> do I compute the Delaunay triangulation of a
+ non-convex object?
+
+ <li><a href="#mesh">How</a> do I mesh a volume from a set of triangulated
+ surface points?
+
+ <li><a href="#constrained">Can</a> Qhull produce a triangular mesh for an
+ object?
+
+ <li><a href="#dridges">For</a> 3-d Delaunay triangulations, how do I
+ report the triangles of each tetrahedron?
+
+ <li><a href="#3dd">How</a> do I construct a 3-d Delaunay triangulation?
+ <li><a href="#2d">How</a> do I get the triangles for a 2-d Delaunay
+ triangulation and the vertices of its Voronoi diagram?
+ <li><a href="#big">Can </a>Qhull triangulate a
+ hundred 16-d points?</li>
+ </ul>
+
+ <li>Voronoi diagram questions<ul>
+ <li>See also "Delaunay diagram questions". Qhull computes the Voronoi diagram from the Delaunay triagulation.
+ <li><a href="#volume">How</a> do I compute the volume of a Voronoi region?
+ <li><a href="#maxsphere">How</a> do I get the radii of the empty
+ spheres for each Voronoi vertex?
+
+ <li><a href="#square">What</a> is the Voronoi diagram of a square?
+
+ <li><a href="#vsphere">How</a> do I construct the Voronoi diagram of
+ cospherical points?
+ <li><a href="#rays">Can</a> Qhull compute the unbounded rays of the
+ Voronoi diagram?
+ </ul>
+ <li>Approximation questions<ul>
+ <li><a href="#simplex">How</a> do I approximate data
+ with a simplex?</li>
+ </ul>
+ <li>Halfspace questions<ul>
+ <li><a href="#halfspace">How</a> do I compute the
+ intersection of halfspaces with Qhull?</li>
+ </ul>
+ <li><a name="library">Qhull library</a> questions<ul>
+ <li><a href="#math">Is</a> Qhull available for Mathematica, Matlab, or
+ Maple?
+
+ <li><a href="#ridges">Why</a> are there too few ridges?
+ <li><a href="#call">Can</a> Qhull use coordinates without placing them in
+ a data file?
+ <li><a href="#size">How</a> large are Qhull's data structures?
+ <li><a href="#inc">Can</a> Qhull construct convex hulls and Delaunay
+ triangulations one point at a time?
+ <li><a href="#ridges2">How</a> do I visit the ridges of a Delaunay
+ triangulation?
+ <li><a href="#listd">How</a> do I visit the Delaunay facets?
+ <LI><a
+ href="#outside">When</a> is a point outside or inside a facet?
+ <li><a href="#closest">How</a> do I find the facet that is closest to a
+ point?
+ <li><a href="#vclosest">How</a> do I find the Delaunay triangle or Voronoi
+ region that is closest to a point?
+ <li><a href="#vertices">How</a> do I list the vertices?
+ <li><a href="#test">How</a> do I test code that uses the Qhull library?
+ <li><a href="#orient">When</a> I compute a plane
+ equation from a facet, I sometimes get an
+ outward-pointing normal and sometimes an
+ inward-pointing normal</li>
+ </ul>
+ </li>
+</ul>
+
+<hr>
+
+<h2><a href="#TOC">&#187;</a><a name="startup">Startup</a> questions</h2>
+
+<h4><a href="#TOC">&#187;</a><a name="console">How</a> do I run Qhull
+from Windows?</h4><blockquote>
+
+<p>Qhull is a console program. You will first need a command window
+(i.e., a "command prompt"). You can double click on
+'eg\Qhull-go.bat'. </p>
+
+<blockquote><ul>
+ <li>Type 'qconvex', 'qdelaunay', 'qhalf', 'qvoronoi,
+ 'qhull', and 'rbox' for a synopsis of each program.
+
+ <li>Type 'rbox c D2 | qconvex s i' to compute the
+ convex hull of a square.
+
+ <li>Type 'rbox c D2 | qconvex s i TO results.txt' to
+ write the results to the file 'results.txt'. A summary is still printed on
+ the the console.
+
+ <li>Type 'rbox c D2' to see the input format for
+ qconvex.
+
+ <li>Type 'qconvex &lt; data.txt s i TO results.txt' to
+ read input data from 'data.txt'.
+
+ <li>If you want to enter data by hand, type 'qconvex s i TO
+ results.txt' to read input data from the console. Type in
+ the numbers and end with a ctrl-D. </li>
+</ul></blockquote>
+
+</blockquote><h4><a href="#TOC">&#187;</a><a name="input">How</a> do I enter
+points for Qhull?</h4><blockquote>
+
+<p>Qhull takes its data from standard input. For example, create
+a file named 'data.txt' with the following contents: </p>
+
+<blockquote>
+ <pre>
+2 #sample 2-d input
+5 #number of points
+1 2 #coordinates of points
+-1.1 3
+3 2.2
+4 5
+-10 -10
+</pre>
+</blockquote>
+
+<p>Then call qconvex with 'qconvex &lt; data.txt'. It will print a
+summary of the convex hull. Use 'qconvex &lt; data.txt o' to print
+the vertices and edges. See also <a href="index.htm#input">input
+format</a>. </p>
+
+<p>You can generate sample data with rbox, e.g., 'rbox 10'
+generates 10 random points in 3-d. Use a pipe ('|') to run rbox
+and qhull together, e.g., </p>
+
+<blockquote>
+ <p>rbox c | qconvex o </p>
+</blockquote>
+
+<p>computes the convex hull of a cube. </p>
+
+</blockquote><h4><a href="#TOC">&#187;</a><a name="learn">How</a> do I learn to
+use Qhull?</h4><blockquote>
+
+<p>First read: </p>
+
+<ul>
+ <li><a href="index.htm">Introduction</a> to Qhull
+ <li><a href="index.htm#when">When</a> to use Qhull
+ <li><a href="qconvex.htm">qconvex</a> -- convex hull
+ <li><a href="qdelaun.htm">qdelaunay</a> -- Delaunay triangulation
+ <li><a href="qhalf.htm">qhalf</a> -- half-space intersection about a point
+
+ <li><a href="qvoronoi.htm">qvoronoi</a> -- Voronoi diagram
+ <li><a href="rbox.htm">Rbox</a>, for sample inputs
+ <li><a href="qh-eg.htm">Examples</a> of Qhull</li>
+</ul>
+
+<p>Look at Qhull's on-line documentation: </p>
+
+<ul>
+ <li>'qconvex' gives a synopsis of qconvex and its options
+
+ <li>'rbox' lists all of the options for generating point
+ sets
+ <li>'qconvex - | more' lists the options for qconvex
+ <li>'qconvex .' gives a concise list of options
+ <li>'qdelaunay', 'qhalf', 'qvoronoi', and 'qhull' also have a synopsis and option list</li>
+</ul>
+
+<p>Then try out the Qhull programs on small examples. </p>
+
+<ul>
+ <li>'rbox c' lists the vertices of a cube
+ <li>'rbox c | qconvex' is the convex hull of a cube
+ <li>'rbox c | qconvex o' lists the vertices and facets of
+ a cube
+ <li>'rbox c | qconvex Qt o' triangulates the cube
+ <li>'rbox c | qconvex QJ o' joggles the input and
+ triangulates the cube
+ <li>'rbox c D2 | qconvex' generates the convex hull of a
+ square
+ <li>'rbox c D4 | qconvex' generates the convex hull of a
+ hypercube
+ <li>'rbox 6 s D2 | qconvex p Fx' lists 6 random points in
+ a circle and lists the vertices of their convex hull in order
+ <li>'rbox c D2 c G2 | qdelaunay' computes the Delaunay
+ triangulation of two embedded squares. It merges the cospherical facets.
+ <li>'rbox c D2 c G2 | qdelaunay Qt' computes the Delaunay
+ triangulation of two embedded squares. It triangulates the cospherical facets.
+ <li>'rbox c D2 c G2 | qvoronoi o' computes the
+ corresponding Voronoi vertices and regions.
+ <li>'rbox c D2 c G2 | qvoronio Fv' shows the Voronoi diagram
+ for the previous example. Each line is one edge of the
+ diagram. The first number is 4, the next two numbers list
+ a pair of input sites, and the last two numbers list the
+ corresponding pair of Voronoi vertices. </li>
+</ul>
+
+<p>Install <a href="http://www.geomview.org">Geomview</a>
+if you are running SGI Irix, Solaris, SunOS, Linux, HP, IBM
+RS/6000, DEC Alpha, or Next. You can then visualize the output of
+Qhull. Qhull comes with Geomview <a href="qh-eg.htm">examples</a>.
+</p>
+
+<p>Then try Qhull with a small example of your application. Work
+out the results by hand. Then experiment with Qhull's options to
+find the ones that you need. </p>
+
+<p>You will need to decide how Qhull should handle precision
+problems. It can triangulate the output ('<a
+ href="qh-optq.htm#Qt" >Qt</a>'), joggle the input ('<a
+ href="qh-optq.htm#QJn" >QJ</a>'), or merge facets (the default). </p>
+
+<ul>
+ <li>With joggle, Qhull produces simplicial (i.e.,
+ triangular) output by joggling the input. After joggle,
+ no points are cocircular or cospherical.
+ <li>With facet merging, Qhull produces a better
+ approximation and does not modify the input.
+ <li>With triangulated output, Qhull merges facets and triangulates
+ the result.</li>
+ <li>See <a href="qh-impre.htm#joggle">Merged facets or joggled input</a>. </li>
+</ul>
+
+</blockquote>
+<h2><a href="#TOC">&#187;</a><a name="convex">Convex hull questions</a></h2>
+
+<h4><a href="#TOC">&#187;</a><a name="area">How</a> do I report just the area
+ and volume of a convex hull?</h4><blockquote>
+
+Use option 'FS'. For example,
+
+<blockquote><pre>
+C:\qhull>rbox 10 | qconvex FS
+0
+2 2.192915621644613 0.2027867899638665
+
+C:\qhull>rbox 10 | qconvex FA
+
+Convex hull of 10 points in 3-d:
+
+ Number of vertices: 10
+ Number of facets: 16
+
+Statistics for: RBOX 10 | QCONVEX FA
+
+ Number of points processed: 10
+ Number of hyperplanes created: 28
+ Number of distance tests for qhull: 44
+ CPU seconds to compute hull (after input): 0
+ Total facet area: 2.1929156
+ Total volume: 0.20278679
+</pre></blockquote>
+
+</blockquote><h4><a href="#TOC">&#187;</a><a name="extra">Why</a> are there extra
+points in a 4-d or higher convex hull?</h4><blockquote>
+
+<p>You may see extra points if you use options '<a
+ href="qh-opto.htm#i" >i</a>' or '<a href="qh-optf.htm#Ft">Ft</a>'
+ without using triangulated output ('<a href="qh-optq.htm#Qt">Qt</a>').
+The extra points occur when a facet is non-simplicial (i.e., a
+facet with more than <i>d</i> vertices). For example, Qhull
+reports the following for one facet of the convex hull of a hypercube.
+Option 'Pd0:0.5' returns the facet along the positive-x axis: </p>
+
+<blockquote>
+ <pre>
+rbox c D4 | qconvex i Pd0:0.5
+12
+17 13 14 15
+17 13 12 14
+17 11 13 15
+17 14 11 15
+17 10 11 14
+17 14 12 8
+17 12 13 8
+17 10 14 8
+17 11 10 8
+17 13 9 8
+17 9 11 8
+17 11 9 13
+</pre>
+</blockquote>
+
+<p>The 4-d hypercube has 16 vertices; so point "17" was
+added by qconvex. Qhull adds the point in order to report a
+simplicial decomposition of the facet. The point corresponds to
+the "centrum" which Qhull computes to test for
+convexity. </p>
+
+<p>Triangulate the output ('<a href="qh-optq.htm#Qt">Qt</a>') to avoid the extra points.
+Since the hypercube is 4-d, each simplicial facet is a tetrahedron.
+<blockquote>
+<pre>
+C:\qhull3.1>rbox c D4 | qconvex i Pd0:0.5 Qt
+9
+9 13 14 15
+12 9 13 14
+9 11 13 15
+11 9 14 15
+9 10 11 14
+12 9 14 8
+9 12 13 8
+9 10 14 8
+10 9 11 8
+</pre>
+</blockquote>
+
+<p>Use the '<a href="qh-optf.htm#Fv">Fv</a>' option to print the
+vertices of simplicial and non-simplicial facets. For example,
+here is the same hypercube facet with option 'Fv' instead of 'i':
+</p>
+
+<blockquote>
+ <pre>
+C:\qhull&gt;rbox c D4 | qconvex Pd0:0.5 Fv
+1
+8 9 10 12 11 13 14 15 8
+</pre>
+</blockquote>
+
+<p>The coordinates of the extra point are printed with the '<A
+ href="qh-optf.htm#Ft" >Ft</a>' option. </p>
+
+<blockquote>
+ <pre>
+rbox c D4 | qconvex Pd0:0.5 Ft
+4
+17 12 3
+ -0.5 -0.5 -0.5 -0.5
+ -0.5 -0.5 -0.5 0.5
+ -0.5 -0.5 0.5 -0.5
+ -0.5 -0.5 0.5 0.5
+ -0.5 0.5 -0.5 -0.5
+ -0.5 0.5 -0.5 0.5
+ -0.5 0.5 0.5 -0.5
+ -0.5 0.5 0.5 0.5
+ 0.5 -0.5 -0.5 -0.5
+ 0.5 -0.5 -0.5 0.5
+ 0.5 -0.5 0.5 -0.5
+ 0.5 -0.5 0.5 0.5
+ 0.5 0.5 -0.5 -0.5
+ 0.5 0.5 -0.5 0.5
+ 0.5 0.5 0.5 -0.5
+ 0.5 0.5 0.5 0.5
+ 0.5 0 0 0
+4 16 13 14 15
+4 16 13 12 14
+4 16 11 13 15
+4 16 14 11 15
+4 16 10 11 14
+4 16 14 12 8
+4 16 12 13 8
+4 16 10 14 8
+4 16 11 10 8
+4 16 13 9 8
+4 16 9 11 8
+4 16 11 9 13
+</pre>
+</blockquote>
+
+</blockquote><h4><a href="#TOC">&#187;</a><a name="dup">How</a> do I report
+duplicate vertices?</h4><blockquote>
+
+<p>There's no direct way. You can use option
+'<a href="qh-optf.htm#FP">FP</a>' to
+report the distance to the nearest vertex for coplanar input
+points. Select the minimum distance for a duplicated vertex, and
+locate all input sites less than this distance. </p>
+
+<p>For Delaunay triangulations, all coplanar points are nearly
+incident to a vertex. If you want a report of coincident input
+sites, do not use option '<a href="qh-optq.htm#QJn">QJ</a>'. By
+adding a small random quantity to each input coordinate, it
+prevents coincident input sites. </p>
+
+</blockquote>
+<h2><a href="#TOC">&#187;</a><a name="delaunay">Delaunay triangulation questions</a></h2>
+
+<h4><a href="#TOC">&#187;</a><a name="flat">How</a> do I get rid of
+nearly flat Delaunay triangles?</h4><blockquote>
+
+<p>Nearly flat triangles occur when boundary points are nearly
+collinear or coplanar. They also occur for nearly coincident
+points. Both events can easily occur when using joggle. For example
+(rbox 10 W0 D2 | qdelaunay QJ Fa) lists the areas of the Delaunay
+triangles of 10 points on the boundary of a square. Some of
+these triangles are nearly flat. This occurs when one point
+is joggled inside of two other points. In this case, nearly flat
+triangles do not occur with triangulated output (rbox 10 W0 D2 | qdelaunay Qt Fa).
+
+
+<p>Another example, (rbox c P0 P0 D2 | qdelaunay QJ Fa), computes the
+areas of the Delaunay triangles for the unit square and two
+instances of the origin. Four of the triangles have an area
+of 0.25 while two have an area of 2.0e-11. The later are due to
+the duplicated origin. With triangulated output (rbox c P0 P0 D2 | qdelaunay Qt Fa)
+there are four triangles of equal area.
+
+<p>Nearly flat triangles also occur without using joggle. For
+example, (rbox c P0 P0,0.4999999999 | qdelaunay Fa), computes
+the areas of the Delaunay triangles for the unit square,
+a nearly collinear point, and the origin. One triangle has an
+area of 3.3e-11.
+
+<p>Unfortunately, none of Qhull's merging options remove nearly
+flat Delaunay triangles due to nearly collinear or coplanar boundary
+points.
+The merging options concern the empty circumsphere
+property of Delaunay triangles. This is independent of the area of
+the Delaunay triangles. Qhull does handle nearly coincident points.
+
+<p>If you are calling Qhull from a program, you can merge slivers into an adjacent facet.
+In d dimensions with simplicial facets (e.g., from 'Qt'), each facet has
+d+1 neighbors. Each neighbor shares d vertices of the facet's d+1 vertices. Let the
+other vertex be the <i>opposite</i> vertex. For each neighboring facet, if its circumsphere
+includes the opposite.vertex, the two facets can be merged. [M. Treacy]
+
+<p>You can handle collinear or coplanar boundary points by
+enclosing the points in a box. For example,
+(rbox c P0 P0,0.4999999999 c G1 | qdelaunay Fa), surrounds the
+previous points with [(1,1), (1,-1), (-1,-1), (-1, 1)].
+Its Delaunay triangulation does not include a
+nearly flat triangle. The box also simplifies the graphical
+output from Qhull.
+
+<p>Without joggle, Qhull lists coincident points as "coplanar"
+points. For example, (rbox c P0 P0 D2 | qdelaunay Fa), ignores
+the duplicated origin and lists four triangles of size 0.25.
+Use 'Fc' to list the coincident points (e.g.,
+rbox c P0 P0 D2 | qdelaunay Fc).
+
+<p>There is no easy way to determine coincident points with joggle.
+Joggle removes all coincident, cocircular, and cospherical points
+before running Qhull. Instead use facet merging (the default)
+or triangulated output ('<a href="qh-optq.htm#Qt">Qt</a>').
+
+</blockquote><h4><a href="#TOC">&#187;</a><a name="mesh">How</a> do I compute
+the Delaunay triangulation of a non-convex object?</h4><blockquote>
+
+<p>A similar question is
+"How do I mesh a volume from a set of triangulated surface points?"
+
+<p>This is an instance of the constrained Delaunay Triangulation
+problem. Qhull does not handle constraints. The boundary of the
+Delaunay triangulation is always convex. But if the input set
+contains enough points, the triangulation will include the
+boundary. The number of points needed depends on the input.
+
+<p>Shewchuk has developed a theory of constrained Delaunay triangulations.
+See his
+<a href="http://www.cs.cmu.edu/~jrs/jrspapers.html#cdt">paper</a> at the
+1998 Computational Geometry Conference. Using these ideas, constraints
+could be added to Qhull. They would have many applications.
+
+<p>There is a large literature on mesh generation and many commercial
+offerings. For pointers see
+<a href="http://www.imr.sandia.gov/papers/topics.html">Owen's International Meshing Roundtable</a>
+and <a href="http://www.robertschneiders.de/meshgeneration/meshgeneration.html">Schneiders'
+Finite Element Mesh Generation page</a>.</p>
+
+</blockquote><h4><a href="#TOC">&#187;</a><a name="constrained">Can</a> Qhull
+produce a triangular mesh for an object?</h4><blockquote>
+
+<p>Yes for convex objects, no for non-convex objects. For
+non-convex objects, it triangulates the concavities. Unless the
+object has many points on its surface, triangles may cross the
+surface. </p>
+
+</blockquote><h4><a href="#TOC">&#187;</a><a name="dridges">For</a> 3-d Delaunay
+triangulations, how do I report the triangles of each
+tetrahedron?</h4><blockquote>
+
+<p>For points in general position, a 3-d Delaunay triangulation
+generates tetrahedron. Each face of a tetrahedron is a triangle.
+For example, the 3-d Delaunay triangulation of random points on
+the surface of a cube, is a cellular structure of tetrahedron. </p>
+
+<p>Use triangulated output ('qdelaunay Qt i') or joggled input ('qdelaunay QJ i')
+to generate the Delaunay triangulation.
+Option 'i' reports each tetrahedron. The triangles are
+every combination of 3 vertices. Each triangle is a
+"ridge" of the Delaunay triangulation. </p>
+
+<p>For example, </p>
+
+<pre>
+ rbox 10 | qdelaunay Qt i
+ 14
+ 9 5 8 7
+ 0 9 8 7
+ 5 3 8 7
+ 3 0 8 7
+ 5 4 8 1
+ 4 6 8 1
+ 2 9 5 8
+ 4 2 5 8
+ 4 2 9 5
+ 6 2 4 8
+ 9 2 0 8
+ 2 6 0 8
+ 2 4 9 1
+ 2 6 4 1
+</pre>
+
+<p>is the Delaunay triangulation of 10 random points. Ridge 9-5-8
+occurs twice. Once for tetrahedron 9 5 8 7 and the other for
+tetrahedron 2 9 5 8. </p>
+
+<p>You can also use the Qhull library to generate the triangles.
+See "<a href="#ridges2">How</a> do I visit the ridges of a
+Delaunay triangulation?"</p>
+
+</blockquote><h4><a href="#TOC">&#187;</a><a name="3dd">How</a> do I construct a
+3-d Delaunay triangulation?</h4><blockquote>
+
+<p>For 3-d Delaunay triangulations with cospherical input sites,
+use triangulated output ('<a href="qh-optq.htm#Qt">Qt</a>') or
+joggled input ('<a href="qh-optq.htm#QJn">QJ</a>'). Otherwise
+option 'i' will
+triangulate non-simplicial facets by adding a point to the facet.
+
+<p>If you want non-simplicial output for cospherical sites, use
+option
+'<a href="qh-optf.htm#Fv">Fv</a>' or '<a href="qh-opto.htm#o">o</a>'.
+For option 'o', ignore the last coordinate. It is the lifted
+coordinate for the corresponding convex hull in 4-d.
+
+<p>The following example is a cube
+inside a tetrahedron. The 8-vertex facet is the cube. Ignore the
+last coordinates. </p>
+
+<blockquote>
+ <pre>
+C:\qhull&gt;rbox r y c G0.1 | qdelaunay Fv
+4
+12 20 44
+ 0.5 0 0 0.3055555555555555
+ 0 0.5 0 0.3055555555555555
+ 0 0 0.5 0.3055555555555555
+ -0.5 -0.5 -0.5 0.9999999999999999
+ -0.1 -0.1 -0.1 -6.938893903907228e-018
+ -0.1 -0.1 0.1 -6.938893903907228e-018
+ -0.1 0.1 -0.1 -6.938893903907228e-018
+ -0.1 0.1 0.1 -6.938893903907228e-018
+ 0.1 -0.1 -0.1 -6.938893903907228e-018
+ 0.1 -0.1 0.1 -6.938893903907228e-018
+ 0.1 0.1 -0.1 -6.938893903907228e-018
+ 0.1 0.1 0.1 -6.938893903907228e-018
+4 2 11 1 0
+4 10 1 0 3
+4 11 10 1 0
+4 2 9 0 3
+4 9 11 2 0
+4 7 2 1 3
+4 11 7 2 1
+4 8 10 0 3
+4 9 8 0 3
+5 8 9 10 11 0
+4 10 6 1 3
+4 6 7 1 3
+5 6 8 10 4 3
+5 6 7 10 11 1
+4 5 9 2 3
+4 7 5 2 3
+5 5 8 9 4 3
+5 5 6 7 4 3
+8 5 6 8 7 9 10 11 4
+5 5 7 9 11 2
+</pre>
+</blockquote>
+
+<p>If you want simplicial output use options
+'<a href="qh-optq.htm#Qt">Qt</a> <A
+ href="qh-optf.htm#Ft" >i</a>' or
+'<a href="qh-optq.htm#QJn">QJ</a> <A
+ href="qh-optf.htm#Ft" >i</a>', e.g.,
+</p>
+
+<blockquote>
+ <pre>
+rbox r y c G0.1 | qdelaunay Qt i
+31
+2 11 1 0
+11 10 1 0
+9 11 2 0
+11 7 2 1
+8 10 0 3
+9 8 0 3
+10 6 1 3
+6 7 1 3
+5 9 2 3
+7 5 2 3
+9 8 10 11
+8 10 11 0
+9 8 11 0
+6 8 10 4
+8 6 10 3
+6 8 4 3
+6 7 10 11
+10 6 11 1
+6 7 11 1
+8 5 4 3
+5 8 9 3
+5 6 4 3
+6 5 7 3
+5 9 10 11
+8 5 9 10
+7 5 10 11
+5 6 7 10
+8 5 10 4
+5 6 10 4
+5 9 11 2
+7 5 11 2
+</pre>
+</blockquote>
+
+</blockquote><h4><a href="#TOC">&#187;</a><a name="2d">How</a> do I get the
+triangles for a 2-d Delaunay triangulation and the vertices of
+its Voronoi diagram?</h4><blockquote>
+
+<p>To compute the Delaunay triangles indexed by the indices of
+the input sites, use </p>
+
+<blockquote>
+ <p>rbox 10 D2 | qdelaunay Qt i </p>
+</blockquote>
+
+<p>To compute the Voronoi vertices and the Voronoi region for
+each input site, use </p>
+
+<blockquote>
+ <p>rbox 10 D2 | qvoronoi o </p>
+</blockquote>
+
+<p>To compute each edge ("ridge") of the Voronoi
+diagram for each pair of adjacent input sites, use</p>
+
+<blockquote>
+ <p>rbox 10 D2 | qvoronoi Fv </p>
+</blockquote>
+
+<p>To compute the area and volume of the Voronoi region for input site 5 (site 0 is the first one),
+use </p>
+
+<blockquote>
+ <p>rbox 10 D2 | qvoronoi QV5 p | qconvex s FS </p>
+</blockquote>
+
+<p>To compute the lines ("hyperplanes") that define the
+Voronoi region for input site 5, use </p>
+
+<blockquote>
+ <p>rbox 10 D2 | qvoronoi QV5 p | qconvex n </p>
+</blockquote>
+or
+<blockquote>
+ <p>rbox 10 D2 | qvoronoi QV5 Fi Fo</p>
+</blockquote>
+
+<p>To list the extreme points of the input sites use </p>
+
+<blockquote>
+ <p>rbox 10 D2 | qdelaunay Fx </p>
+</blockquote>
+
+<p>You will get the same point ids with </p>
+
+<blockquote>
+ <p>rbox 10 D2 | qconvex Fx </p>
+</blockquote>
+
+</blockquote><h4><a href="#TOC">&#187;</a><a name="big">Can </a>Qhull triangulate
+a hundred 16-d points?</h4><blockquote>
+
+<p>No. This is an immense structure. A triangulation of 19, 16-d
+points has 43 simplices. If you add one point at a time, the
+triangulation increased as follows: 43, 189, 523, 1289, 2830,
+6071, 11410, 20487. The last triangulation for 26 points used 13
+megabytes of memory. When Qhull uses virtual memory, it becomes
+too slow to use. </p>
+
+</blockquote>
+<h2><a href="#TOC">&#187;</a><a name="voronoi">Voronoi
+diagram questions</a></h2>
+
+<h4><a href="#TOC">&#187;</a><a name="volume">How</a> do I compute the volume of a Voronoi region?</h4><blockquote>
+
+<p>For each Voronoi region, compute the convex hull of the region's Voronoi vertices. The volume of each convex hull is the volume
+of the corresponding Vornoi region.</p>
+
+<p>For example, to compute the volume of the bounded Voronoi region about [0,0,0]: output the origin's Voronoi vertices and
+compute the volume of their convex hull. The last number from option '<a href="qh-optf.htm#FS">FS</a>' is the volume.</p>
+<blockquote><pre>
+rbox P0 10 | qvoronoi QV0 p | qhull FS
+0
+2 1.448134756744281 0.1067973560800857
+</pre></blockquote>
+
+<p>For another example, see <a href="#2d">How</a> do I get the triangles for a 2-d Delaunay
+ triangulation and the vertices of its Voronoi diagram?</p>
+
+<p>This approach is slow if you are using the command line. A faster approcach is to call Qhull from
+a program. The fastest method is Clarkson's <a href="http://www.netlib.org/voronoi/hull.html">hull</a> program.
+It computes the volume for all Voronoi regions.</p>
+
+<p>An unbounded Voronoi region does not have a volume.</p>
+
+</blockquote><h4><a href="#TOC">&#187;</a><a name="maxsphere">How</a> do I get the radii of the empty
+ spheres for each Voronoi vertex?</h4><blockquote>
+
+Use option '<a href="qh-optf.htm#Fi">Fi</a>' to list each bisector (i.e. Delaunay ridge). Then compute the
+minimum distance for each Voronoi vertex.
+
+<p>There's other ways to get the same information. Let me know if you
+find a better method.
+
+</blockquote><h4><a href="#TOC">&#187;</a><a name="square">What</a> is the Voronoi diagram
+ of a square?</h4><blockquote>
+
+<p>
+Consider a square,
+<blockquote><pre>
+C:\qhull&gt;rbox c D2
+2 RBOX c D2
+4
+ -0.5 -0.5
+ -0.5 0.5
+ 0.5 -0.5
+ 0.5 0.5
+</pre></blockquote>
+
+<p>There's two ways to compute the Voronoi diagram: with facet merging
+or with joggle. With facet merging, the
+result is:
+
+<blockquote><pre>
+C:\qhull&gt;rbox c D2 | qvoronoi Qz
+
+Voronoi diagram by the convex hull of 5 points in 3-d:
+
+ Number of Voronoi regions and at-infinity: 5
+ Number of Voronoi vertices: 1
+ Number of facets in hull: 5
+
+Statistics for: RBOX c D2 | QVORONOI Qz
+
+ Number of points processed: 5
+ Number of hyperplanes created: 7
+ Number of distance tests for qhull: 8
+ Number of merged facets: 1
+ Number of distance tests for merging: 29
+ CPU seconds to compute hull (after input): 0
+
+C:\qhull&gt;rbox c D2 | qvoronoi Qz o
+2
+2 5 1
+-10.101 -10.101
+ 0 0
+2 0 1
+2 0 1
+2 0 1
+2 0 1
+0
+
+C:\qhull&gt;rbox c D2 | qvoronoi Qz Fv
+4
+4 0 1 0 1
+4 0 2 0 1
+4 1 3 0 1
+4 2 3 0 1
+</pre></blockquote>
+
+<p>There is one Voronoi vertex at the origin and rays from the origin
+along each of the coordinate axes.
+The last line '4 2 3 0 1' means that there is
+a ray that bisects input points #2 and #3 from infinity (vertex 0) to
+the origin (vertex 1).
+Option 'Qz' adds an artificial point since the input is cocircular.
+Coordinates -10.101 indicate the
+vertex at infinity.
+
+<p>With triangulated output, the Voronoi vertex is
+duplicated:
+
+<blockquote><pre>
+C:\qhull3.1>rbox c D2 | qvoronoi Qt Qz
+
+Voronoi diagram by the convex hull of 5 points in 3-d:
+
+ Number of Voronoi regions and at-infinity: 5
+ Number of Voronoi vertices: 2
+ Number of triangulated facets: 1
+
+Statistics for: RBOX c D2 | QVORONOI Qt Qz
+
+ Number of points processed: 5
+ Number of hyperplanes created: 7
+ Number of facets in hull: 6
+ Number of distance tests for qhull: 8
+ Number of distance tests for merging: 33
+ Number of distance tests for checking: 30
+ Number of merged facets: 1
+ CPU seconds to compute hull (after input): 0.05
+
+C:\qhull3.1>rbox c D2 | qvoronoi Qt Qz o
+2
+3 5 1
+-10.101 -10.101
+ 0 0
+ 0 0
+3 2 0 1
+2 1 0
+2 2 0
+3 2 0 1
+0
+
+C:\qhull3.1>rbox c D2 | qvoronoi Qt Qz Fv
+4
+4 0 2 0 2
+4 0 1 0 1
+4 1 3 0 1
+4 2 3 0 2
+</pre></blockquote>
+
+
+<p>With joggle, the input is no longer cocircular and the Voronoi vertex is
+split into two:
+
+<blockquote><pre>
+C:\qhull&gt;rbox c D2 | qvoronoi Qt Qz
+
+C:\qhull&gt;rbox c D2 | qvoronoi QJ o
+2
+3 4 1
+-10.101 -10.101
+-4.71511718558304e-012 -1.775812830118184e-011
+9.020340030474472e-012 -4.02267108512433e-012
+2 0 1
+3 2 1 0
+3 2 0 1
+2 2 0
+
+C:\qhull&gt;rbox c D2 | qvoronoi QJ Fv
+5
+4 0 2 0 1
+4 0 1 0 1
+4 1 2 1 2
+4 1 3 0 2
+4 2 3 0 2
+</pre></blockquote>
+
+<p>Note that the Voronoi diagram includes the same rays as
+ before plus a short edge between the two vertices.</p>
+
+
+</blockquote><h4><a href="#TOC">&#187;</a><a name="vsphere">How</a> do I construct
+the Voronoi diagram of cospherical points?</h4><blockquote>
+
+<p>Three-d terrain data can be approximated with cospherical
+points. The Delaunay triangulation of cospherical points is the
+same as their convex hull. If the points lie on the unit sphere,
+the facet normals are the Voronoi vertices [via S. Fortune]. </p>
+
+<p>For example, consider the points {[1,0,0], [-1,0,0], [0,1,0],
+...}. Their convex hull is: </p>
+
+<pre>
+rbox d G1 | qconvex o
+3
+6 8 12
+ 0 0 -1
+ 0 0 1
+ 0 -1 0
+ 0 1 0
+ -1 0 0
+ 1 0 0
+3 3 1 4
+3 1 3 5
+3 0 3 4
+3 3 0 5
+3 2 1 5
+3 1 2 4
+3 2 0 4
+3 0 2 5
+</pre>
+
+<p>The facet normals are: </p>
+
+<pre>
+rbox d G1 | qconvex n
+4
+8
+-0.5773502691896258 0.5773502691896258 0.5773502691896258 -0.5773502691896258
+ 0.5773502691896258 0.5773502691896258 0.5773502691896258 -0.5773502691896258
+-0.5773502691896258 0.5773502691896258 -0.5773502691896258 -0.5773502691896258
+ 0.5773502691896258 0.5773502691896258 -0.5773502691896258 -0.5773502691896258
+ 0.5773502691896258 -0.5773502691896258 0.5773502691896258 -0.5773502691896258
+-0.5773502691896258 -0.5773502691896258 0.5773502691896258 -0.5773502691896258
+-0.5773502691896258 -0.5773502691896258 -0.5773502691896258 -0.5773502691896258
+ 0.5773502691896258 -0.5773502691896258 -0.5773502691896258 -0.5773502691896258
+</pre>
+
+<p>If you drop the offset from each line (the last number), each
+line is the Voronoi vertex for the corresponding facet. The
+neighboring facets for each point define the Voronoi region for
+each point. For example: </p>
+
+<pre>
+rbox d G1 | qconvex FN
+6
+4 7 3 2 6
+4 5 0 1 4
+4 7 4 5 6
+4 3 1 0 2
+4 6 2 0 5
+4 7 3 1 4
+</pre>
+
+<p>The Voronoi vertices {7, 3, 2, 6} define the Voronoi region
+for point 0. Point 0 is [0,0,-1]. Its Voronoi vertices are </p>
+
+<pre>
+-0.5773502691896258 0.5773502691896258 -0.5773502691896258
+ 0.5773502691896258 0.5773502691896258 -0.5773502691896258
+-0.5773502691896258 -0.5773502691896258 -0.5773502691896258
+ 0.5773502691896258 -0.5773502691896258 -0.5773502691896258
+</pre>
+
+<p>In this case, the Voronoi vertices are oriented, but in
+general they are unordered. </p>
+
+<p>By taking the dual of the Delaunay triangulation, you can
+construct the Voronoi diagram. For cospherical points, the convex
+hull vertices for each facet, define the input sites for each
+Voronoi vertex. In 3-d, the input sites are oriented. For
+example: </p>
+
+<pre>
+rbox d G1 | qconvex i
+8
+3 1 4
+1 3 5
+0 3 4
+3 0 5
+2 1 5
+1 2 4
+2 0 4
+0 2 5
+</pre>
+
+<p>The convex hull vertices for facet 0 are {3, 1, 4}. So Voronoi
+vertex 0 (i.e., [-0.577, 0.577, 0.577]) is the Voronoi vertex for
+input sites {3, 1, 4} (i.e., {[0,1,0], [0,0,1], [-1,0,0]}). </p>
+
+</blockquote><h4><a href="#TOC">&#187;</a><a name="rays">Can</a> Qhull compute the
+unbounded rays of the Voronoi diagram?</h4><blockquote>
+
+<p>Use '<a href="qh-optf.htm#Fo2">Fo</a>' to compute the separating
+hyperplanes for unbounded Voronoi regions. The corresponding ray
+goes to infinity from the Voronoi vertices. If you enclose the
+input sites in a large enough box, the outermost bounded regions
+will represent the unbounded regions of the original points. </p>
+
+<p>If you do not box the input sites, you can identify the
+unbounded regions. They list '0' as a vertex. Vertex 0 represents
+"infinity". Each unbounded ray includes vertex 0 in
+option '<a href="qh-optf.htm#Fv2">Fv</a>. See <A
+ href="qvoronoi.htm#graphics" >Voronoi graphics</a> and <A
+ href="qvoronoi.htm#notes" >Voronoi notes</a>. </p>
+
+</blockquote>
+<h2><a href="#TOC">&#187;</a>Approximation questions</h2>
+
+<h4><a href="#TOC">&#187;</a><a name="simplex">How</a> do I
+approximate data with a simplex</h4><blockquote>
+
+<p>Qhull may be used to help select a simplex that approximates a
+data set. It will take experimentation. Geomview will help to
+visualize the results. This task may be difficult to do in 5-d
+and higher. Use rbox options 'x' and 'y' to produce random
+distributions within a simplex. Your methods work if you can
+recover the simplex. </p>
+
+<p>Use Qhull's precision options to get a first approximation to
+the hull, say with 10 to 50 facets. For example, try 'C0.05' to
+remove small facets after constructing the hull. Use 'W0.05' to
+ignore points within 0.05 of a facet. Use 'PA5' to print the five
+largest facets by area. </p>
+
+<p>Then use other methods to fit a simplex to this data. Remove
+outlying vertices with few nearby points. Look for large facets
+in different quadrants. You can use option 'Pd0d1d2' to print all
+the facets in a quadrant. </p>
+
+<p>In 4-d and higher, use the outer planes (option 'Fo' or
+'facet-&gt;maxoutside') since the hyperplane of an approximate
+facet may be below many of the input points. </p>
+
+<p>For example, consider fitting a cube to 1000 uniformly random
+points in the unit cube. In this case, the first try was good: </p>
+
+<blockquote>
+ <pre>
+rbox 1000 | qconvex W0.05 C0.05 PA6 Fo
+4
+6
+0.35715408374381 0.08706467018177928 -0.9299788727015564 -0.5985514741284483
+0.995841591359023 -0.02512604712761577 0.08756829720435189 -0.5258834069202866
+0.02448099521570909 -0.02685210459017302 0.9993396046151313 -0.5158104982631999
+-0.9990223929415094 -0.01261133513150079 0.04236994958247349 -0.509218270408407
+-0.0128069014364698 -0.9998380680115362 0.01264203427283151 -0.5002512653670584
+0.01120895057872914 0.01803671994177704 -0.9997744926535512 -0.5056824072956361
+</pre>
+</blockquote>
+
+</blockquote>
+<h2><a href="#TOC">&#187;</a>Halfspace questions</h2>
+
+<h4><a href="#TOC">&#187;</a><a name="halfspace">How</a> do I compute the
+ intersection of halfspaces with Qhull?</h4><blockquote>
+
+<p>Qhull computes the halfspace intersection about a point. The
+point must be inside all of the halfspaces. Given a point, a
+duality turns a halfspace intersection problem into a convex
+hull problem.
+
+<p>Use linear programming if you
+do not know a point in the interior of the halfspaces.
+See the <a href="qhalf.htm#notes">notes</a> for qhalf. You will need
+ a linear programming code. This may require a fair amount of work to
+ implement.</p>
+
+
+
+</blockquote>
+<h2><a href="#TOC">&#187;</a><a name="library">Qhull library
+questions</a></h2>
+
+<h4><a href="#TOC">&#187;</a><a name="math">Is</a> Qhull available for Mathematica, Matlab, or Maple?</h4><blockquote>
+
+<p><b>MATLAB</b>
+
+<p>Z. You of <a href="http://www.mathworks.com">MathWorks</a> added qhull to MATLAB 6.
+See functions <a href="http://www.mathworks.com/help/techdoc/ref/convhulln.html"
+ >convhulln</a>,
+ <a href="http://www.mathworks.com/help/techdoc/ref/delaunayn.html"
+ >delaunayn</a>,
+ <a href="http://www.mathworks.com/help/techdoc/ref/griddata3.html"
+ >griddata3</a>,
+ <a href="http://www.mathworks.com/help/techdoc/ref/griddatan.html"
+ >griddatan</a>,
+ <a href="http://www.mathworks.com/help/techdoc/ref/tsearch.html"
+ >tsearch</a>,
+ <a href="http://www.mathworks.com/help/techdoc/ref/tsearchn.html"
+ >tsearchn</a>, and
+ <a href="http://www.mathworks.com/help/techdoc/ref/voronoin.html"
+ >voronoin</a>. V. Brumberg update MATLAB R14 for Qhull 2003.1 and triangulated output.
+
+<p>Engwirda wrote <a href="http://www.mathworks.com/matlabcentral/fileexchange/loadFile.do?objectId=10307&objectType=file">mesh2d</a> for unstructured mesh generation in MATLAB.
+It is based on the iterative method of Persson and generally results in better quality meshes than delaunay refinement.
+
+
+<p><b>Mathematica and Maple</b>
+
+<p>See <a href="http://library.wolfram.com/infocenter/MathSource/1160/"
+ >qh-math</a>
+for a Delaunay interface to Mathematica. It includes projects for CodeWarrior
+on the Macintosh and Visual C++ on Win32 PCs.
+
+<p>See Mathematica ('<a
+href="qh-opto.htm#m">m</a>') and Maple ('<a
+href="qh-optf.htm#FM">FM</a>') output options.
+
+<p></p>
+</blockquote><h4><a href="#TOC">&#187;</a><a name="ridges">Why</a> are there too few ridges?</h4><blockquote>
+
+The following sample code may produce fewer ridges than expected:
+
+<blockquote><pre>
+ facetT *facetp;
+ ridgeT *ridge, **ridgep;
+
+ FORALLfacets {
+ printf("facet f%d\n", facet->id);
+ FOREACHridge_(facet->ridges) {
+ printf(" ridge r%d between f%d and f%d\n", ridge->id, ridge->top->id, ridge->bottom->id);
+ }
+ }
+</pre></blockquote>
+
+<p> Qhull does not create ridges for simplicial facets.
+Instead it computes ridges from facet-&gt;neighbors. To make ridges for a
+simplicial facet, use qh_makeridges() in merge.c. Usefacet-&gt;visit_id to visit
+each ridge once (instead of twice). For example,
+
+<blockquote><pre>
+ facetT *facet, *neighbor;
+ ridgeT *ridge, **ridgep;
+
+ qh visit_id++;
+ FORALLfacets {
+ printf("facet f%d\n", facet->id);
+ qh_makeridges(facet);
+ facet->visitId= qh visit_id;
+ FOREACHridge_(facet->ridges) {
+ neighbor= otherfacet_(ridge, visible);
+ if (neighbor->visitid != qh visit_id)
+ printf(" ridge r%d between f%d and f%d\n", ridge->id, ridge->top->id, ridge->bottom->id);
+ }
+ }
+</pre></blockquote>
+
+</blockquote><h4><a href="#TOC">&#187;</a><a name="call">Can</a> Qhull use coordinates without placing
+ them in a data file?</h4><blockquote>
+
+<p>You may call Qhull from a program. Please use the reentrant Qhull library (libqhullstatic_r.a, libqhull_r.so, or qhull_r.dll).
+
+See user_eg.c and "Qhull-template" in user_r.c for examples..
+
+See <a href="qh-code.htm">Qhull internals</a> for an introduction to Qhull's reentrant library and its C++ interface.
+
+<p>Hint: Start with a small example for which you know the
+ answer.</p>
+
+</blockquote><h4><a href="#TOC">&#187;</a><a name="size">How</a> large are Qhull's data structures?</h4><blockquote>
+
+<p>Qhull uses a general-dimension data structure.
+The size depends on the dimension. Use option 'Ts' to print
+out the memory statistics [e.g., 'rbox D2 10 | qconvex Ts'].
+
+
+</blockquote><h4><a href="#TOC">&#187;</a><a name="inc">Can</a> Qhull construct
+convex hulls and Delaunay triangulations one point at a time?</h4><blockquote>
+
+<p>The Qhull library may be used to construct convex hulls and
+Delaunay triangulations one point at a time. It may not be used
+for deleting points or moving points. </p>
+
+<p>Qhull is designed for batch processing. Neither Clarkson's
+randomized incremental algorithm nor Qhull are designed for
+on-line operation. For many applications, it is better to
+reconstruct the convex hull or Delaunay triangulation from
+scratch for each new point. </p>
+
+<p>With random point sets and on-line processing, Clarkson's
+algorithm should run faster than Qhull. Clarkson uses the
+intermediate facets to reject new, interior points, while Qhull,
+when used on-line, visits every facet to reject such points. If
+used on-line for n points, Clarkson may take O(n) times as much
+memory as the average off-line case, while Qhull's space
+requirement does not change. </p>
+
+<p>If you triangulate the output before adding all the points
+(option 'Qt' and procedure qh_triangulate), you must set
+option '<a href="qh-optq.htm#Q11">Q11</a>'. It duplicates the
+normals of triangulated facets and recomputes the centrums.
+This should be avoided for regular use since triangulated facets
+are not clearly convex with their neighbors. It appears to
+work most of the time, but fails for cases that Qhull normally
+handles well [see the test call to qh_triangulate in qh_addpoint].
+
+</blockquote><h4><a href="#TOC">&#187;</a><a name="ridges2">How</a> do I visit the
+ridges of a Delaunay triangulation?</h4><blockquote>
+
+<p>To visit the ridges of a Delaunay triangulation, visit each
+facet. Each ridge will appear twice since it belongs to two
+facets. In pseudo-code: </p>
+
+<pre>
+ for each facet of the triangulation
+ if the facet is Delaunay (i.e., part of the lower convex hull)
+ for each ridge of the facet
+ if the ridge's neighboring facet has not been visited
+ ... process a ridge of the Delaunay triangulation ...
+</pre>
+
+<p>In undebugged, C code: </p>
+
+<pre>
+ qh visit_id++;
+ FORALLfacets_(facetlist)
+ if (!facet-&gt;upperdelaunay) {
+ facet-&gt;visitid= qh visit_id;
+ qh_makeridges(facet);
+ FOREACHridge_(facet-&gt;ridges) {
+ neighbor= otherfacet_(ridge, facet);
+ if (neighbor-&gt;visitid != qh visit_id) {
+ /* Print ridge here with facet-id and neighbor-id */
+ /*fprintf(fp, "f%d\tf%d\t",facet-&gt;id,neighbor-&gt;ID);*/
+ FOREACHvertex_(ridge-&gt;vertices)
+ fprintf(fp,"%d ",qh_pointid (vertex-&gt;point) );
+ qh_printfacetNvertex_simplicial (fp, facet, format);
+ fprintf(fp," ");
+ if(neighbor-&gt;upperdelaunay)
+ fprintf(fp," -1 -1 -1 -1 ");
+ else
+ qh_printfacetNvertex_simplicial (fp, neighbor, format);
+ fprintf(fp,"\n");
+ }
+ }
+ }
+ }
+</pre>
+
+<p>Qhull should be redesigned as a class library, or at least as
+an API. It currently provides everything needed, but the
+programmer has to do a lot of work. Hopefully someone will write
+C++ wrapper classes or a Python module for Qhull. </p>
+
+</blockquote><h4><a href="#TOC">&#187;</a><a name="listd">How</a> do I visit the
+Delaunay regions?</h4><blockquote>
+
+<p>Qhull constructs a Delaunay triangulation by lifting the
+input sites to a paraboloid. The Delaunay triangulation
+corresponds to the lower convex hull of the lifted points. To
+visit each facet of the lower convex hull, use: </p>
+
+<pre>
+ facetT *facet;
+
+ ...
+ FORALLfacets {
+ if (!facet-&gt;upperdelaunay) {
+ ... only facets for Delaunay regions ...
+ }
+ }
+</pre>
+
+</blockquote><h4><a href="#TOC">&#187;</a><a name="outside">When</a> is a point
+outside or inside a facet?</h4><blockquote>
+
+<p>A point is outside of a facet if it is clearly outside the
+facet's outer plane. The outer plane is defined by an offset
+(facet-&gt;maxoutside) from the facet's hyperplane. </p>
+
+<pre>
+ facetT *facet;
+ pointT *point;
+ realT dist;
+
+ ...
+ qh_distplane(point, facet, &amp;dist);
+ if (dist &gt; facet-&gt;maxoutside + 2 * qh DISTround) {
+ /* point is clearly outside of facet */
+ }
+</pre>
+
+<p>A point is inside of a facet if it is clearly inside the
+facet's inner plane. The inner plane is computed as the maximum
+distance of a vertex to the facet. It may be computed for an
+individual facet, or you may use the maximum over all facets. For
+example: </p>
+
+<pre>
+ facetT *facet;
+ pointT *point;
+ realT dist;
+
+ ...
+ qh_distplane(point, facet, &amp;dist);
+ if (dist &lt; qh min_vertex - 2 * qh DISTround) {
+ /* point is clearly inside of facet */
+ }
+</pre>
+
+<p>Both tests include two qh.DISTrounds because the computation
+of the furthest point from a facet may be off by qh.DISTround and
+the computation of the current distance to the facet may be off
+by qh.DISTround. </p>
+
+</blockquote><h4><a href="#TOC">&#187;</a><a name="closest">How</a> do I find the
+facet that is closest to a point?</h4><blockquote>
+
+<p>Use qh_findbestfacet(). For example, </p>
+
+<pre>
+ coordT point[ DIM ];
+ boolT isoutside;
+ realT bestdist;
+ facetT *facet;
+
+ ... set coordinates for point ...
+
+ facet= qh_findbestfacet (point, qh_ALL, &amp;bestdist, &amp;isoutside);
+
+ /* 'facet' is the closest facet to 'point' */
+</pre>
+
+<p>qh_findbestfacet() performs a directed search for the facet
+furthest below the point. If the point lies inside this facet,
+qh_findbestfacet() performs an exhaustive search of all facets.
+An exhaustive search may be needed because a facet on the far
+side of a lens-shaped distribution may be closer to a point than
+all of the facet's neighbors. The exhaustive search may be
+skipped for spherical distributions. </p>
+
+<p>Also see, "<a href="#vclosest">How</a> do I find the
+Delaunay triangle that is closest to a
+point?" </p>
+
+</blockquote><h4><a href="#TOC">&#187;</a><a name="vclosest">How</a> do I find the
+Delaunay triangle or Voronoi region that is closest to a point?</h4><blockquote>
+
+<p>A Delaunay triangulation subdivides the plane, or in general
+dimension, subdivides space. Given a point, how do you determine
+the subdivision containing the point? Or, given a set of points,
+how do you determine the subdivision containing each point of the set?
+Efficiency is important -- an exhaustive search of the subdivision
+is too slow.
+
+<p>First compute the Delaunay triangle with qh_new_qhull() in user_r.c or Qhull::runQhull().
+Lift the point to the paraboloid by summing the squares of the
+coordinates. Use qh_findbestfacet() [poly2.c] to find the closest Delaunay
+triangle. Determine the closest vertex to find the corresponding
+Voronoi region. Do not use options
+'<a href="qh-optq.htm#Qbb">Qbb</a>', '<a href="qh-optq.htm#QbB">QbB</a>',
+'<a href="qh-optq.htm#Qbk">Qbk:n</a>', or '<A
+ href="qh-optq.htm#QBk" >QBk:n</a>' since these scale the last
+coordinate. Optimizations of qh_findbestfacet() should
+be possible for Delaunay triangulations.</p>
+
+<p>You first need to lift the point to the paraboloid (i.e., the
+last coordinate is the sum of the squares of the point's coordinates).
+The
+routine, qh_setdelaunay() [geom2.c], lifts an array of points to the
+paraboloid. The following excerpt is from findclosest() in
+user_eg.c. </p>
+
+<pre>
+ coordT point[ DIM + 1]; /* one extra coordinate for lifting the point */
+ boolT isoutside;
+ realT bestdist;
+ facetT *facet;
+
+ ... set coordinates for point[] ...
+
+ qh_setdelaunay (DIM+1, 1, point);
+ facet= qh_findbestfacet (point, qh_ALL, &amp;bestdist, &amp;isoutside);
+ /* 'facet' is the closest Delaunay triangle to 'point' */
+</pre>
+
+<p>The returned facet either contains the point or it is the
+closest Delaunay triangle along the convex hull of the input set.
+
+<p>Point location is an active research area in Computational
+Geometry. For a practical approach, see Mucke, et al, "Fast randomized
+point location without preprocessing in two- and
+three-dimensional Delaunay triangulations," <i>Computational
+Geometry '96</i>, p. 274-283, May 1996.
+For an introduction to planar point location see [O'Rourke '93].
+Also see, "<A
+ href="#closest" >How</a> do I find the facet that is closest to a
+point?" </p>
+
+<p>To locate the closest Voronoi region, determine the closest
+vertex of the closest Delaunay triangle. </p>
+
+<pre>
+ realT dist, bestdist= REALmax;
+ vertexT *bestvertex= NULL, *vertex, **vertexp;
+
+ /* 'facet' is the closest Delaunay triangle to 'point' */
+
+ FOREACHvertex_( facet-&gt;vertices ) {
+ dist= qh_pointdist( point, vertex-&gt;point, DIM );
+ if (dist &lt; bestdist) {
+ bestdist= dist;
+ bestvertex= vertex;
+ }
+ }
+ /* 'bestvertex' represents the Voronoi region closest to 'point'. The corresponding
+ input site is 'bestvertex-&gt;point' */
+</pre>
+
+</blockquote><h4><a href="#TOC">&#187;</a><a name="vertices">How</a> do I list the
+vertices?</h4><blockquote>
+
+<p>To list the vertices (i.e., extreme points) of the convex hull
+use </p>
+
+<blockquote>
+ <pre>
+ vertexT *vertex;
+
+ FORALLvertices {
+ ...
+ // vertex-&gt;point is the coordinates of the vertex
+ // qh_pointid(vertex-&gt;point) is the point ID of the vertex
+ ...
+ }
+ </pre>
+</blockquote>
+
+</blockquote><h4><a href="#TOC">&#187;</a><a name="test">How</a> do I test code
+that uses the Qhull library?</h4><blockquote>
+
+<p>Compare the output from your program with the output from the
+Qhull program. Use option 'T1' or 'T4' to trace what Qhull is
+doing. Prepare a <i>small</i> example for which you know the
+output. Run the example through the Qhull program and your code.
+Compare the trace outputs. If you do everything right, the two
+trace outputs should be almost the same. The trace output will
+also guide you to the functions that you need to review. </p>
+
+</blockquote><h4><a href="#TOC">&#187;</a><a name="orient">When</a> I compute a
+plane equation from a facet, I sometimes get an outward-pointing
+normal and sometimes an inward-pointing normal</h4><blockquote>
+
+<p>Qhull orients simplicial facets, and prints oriented output
+for 'i', 'Ft', and other options. The orientation depends on <i>both</i>
+the vertex order and the flag facet-&gt;toporient.</p>
+
+<p>Qhull does not orient
+ non-simplicial facets. Instead it orients the facet's ridges. These are
+ printed with the 'Qt' and 'Ft' option. The facet's hyperplane is oriented. </p>
+
+</blockquote>
+<hr><!-- Navigation links -->
+
+<p><a><b>Up:</b> </a><a
+ href="http://www.qhull.org">Home page for Qhull</a><br>
+<b>Up:</b> <a href="index.htm#TOC">Qhull manual</a>: Table of Contents <br>
+<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
+&#149; <a href="qh-quick.htm#options">Options</a>
+&#149; <a href="qh-opto.htm#output">Output</a>
+&#149; <a href="qh-optf.htm#format">Formats</a>
+&#149; <a href="qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="qh-optp.htm#print">Print</a>
+&#149; <a href="qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="qh-optc.htm#prec">Precision</a>
+&#149; <a href="qh-optt.htm#trace">Trace</a>
+&#149; <a href="../src/libqhull_r/index.htm">Functions</a><br>
+<b>To:</b> <a href="#TOC">FAQ: Table of Contents</a><br><!-- GC common information -->
+
+<hr>
+
+<p><a href="http://www.geom.uiuc.edu/"><IMG align=middle
+ height=40 src="qh--geom.gif" width=40 ></a><i>The Geometry Center
+Home Page </i></p>
+
+<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a>
+</a><br>
+Created:
+Sept. 25, 1995 --- <!-- hhmts start -->Last modified: see top
+<!-- hhmts end --> </p>
+</body>
+</html>
diff --git a/xs/src/qhull/html/qh-get.htm b/xs/src/qhull/html/qh-get.htm
new file mode 100644
index 000000000..c39ed2256
--- /dev/null
+++ b/xs/src/qhull/html/qh-get.htm
@@ -0,0 +1,106 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+
+<head>
+<title>Qhull Downloads</title>
+</head>
+
+<body>
+<!-- Navigation links -->
+<p><b>Up:</b> <a href="http://www.qhull.org"><i>Qhull Home Page</i></a><br>
+</p>
+
+<hr>
+
+<h1><a
+href="http://www.geom.uiuc.edu/graphics/pix/Special_Topics/Computational_Geometry/cone.html"><img
+src="../html/qh--cone.gif" alt="[CONE]" align="middle"
+width="100" height="100"></a> Qhull Downloads</h1>
+
+<ul>
+ <li><a href="http://www.qhull.org">Qhull Home Page</a> <p>Qhull
+ computes the convex hull, Delaunay triangulation, Voronoi diagram, halfspace
+ intersection about a point, furthest-site Delaunay
+ triangulation, and furthest-site Voronoi diagram. It
+ runs in 2-d, 3-d, 4-d, and higher dimensions. It
+ implements the Quickhull algorithm for computing the
+ convex hull. Qhull handles roundoff errors from floating
+ point arithmetic. It can approximate a convex hull. </p>
+
+ <p>Visit <a href="http://www.qhull.org/news">Qhull News</a>
+ for news, bug reports, change history, and users.
+ If you use Qhull 2003.1 or 2009.1, please upgrade to 2015.2 or apply
+ <a href="http://www.qhull.org/download/poly.c-qh_gethash.patch">poly.c-qh_gethash.patch</a>.</p>
+ </li>
+ <li><a
+ href="http://www.qhull.org/download/qhull-2015.2.zip">Download:
+ Qhull 2015.2 for Windows 10, 8, 7, XP, and NT</a> (2.6 MB,
+ <a href="http://www.qhull.org/README.txt">readme</a>,
+ <a href="http://www.qhull.org/download/qhull-2015.2.md5sum">md5sum</a>,
+ <a href="http://www.qhull.org/download/qhull-2015.2.zip.md5sum">contents</a>)
+ <p>Type: console programs for Windows (32- or 64-bit)</p>
+ <p>Includes 32-bit executables, documentation, and sources files. It runs in a
+ command window. Qhull may be compiled for 64-bits.</p>
+ </li>
+ <li><a href="http://github.com/qhull/qhull/wiki">GitHub Qhull</a> (git@github.com:qhull/qhull.git)
+ <p>Type: git repository for Qhull. See recent <a href="https://github.com/qhull/qhull/blob/master/src/Changes.txt">Changes.txt</a></p>
+ <p>Includes documentation, source files, C++ interface, and test programs. It builds with gcc, mingw,
+ CMake, DevStudio, and Qt Creator.
+ </p>
+ </li>
+ <li><a href="http://www.qhull.org/download/qhull-2015-src-7.2.0.tgz">Download: Qhull 2015.2 for Unix</a> (1.0 MB,
+ <a href="http://www.qhull.org/README.txt">readme</a>,
+ <a href="http://www.qhull.org/download/qhull-2015.2.md5sum">md5sum</a>,
+ <a href="http://www.qhull.org/download/qhull-2015-src-7.2.0.tgz.md5sum">contents</a>)
+ <p>Type: C/C++ source code for 32-bit and 64-bit architectures. See <a href="http://www.qhull.org/src/Changes.txt">Changes.txt</a></p>
+ <p>Includes documentation, source files, Makefiles, CMakeLists.txt, DevStudio projects, and Qt projects.
+ Includes preliminary C++ support.</p>
+ <p>Download and search sites for pre-built packages include
+ <ul>
+ <li><a href="https://launchpad.net/ubuntu/+source/qhull">https://launchpad.net/ubuntu/+source/qhull</a>
+ <li><a href="http://software.opensuse.org/download.html?project=openSUSE%3AFactory&package=qhull">http://software.opensuse.org/download.html?project=openSUSE%3AFactory&package=qhull</a>
+ <li><a href="https://www.archlinux.org/packages/extra/i686/qhull/">https://www.archlinux.org/packages/extra/i686/qhull/</a>
+ <li><a href="http://rpmfind.net/linux/rpm2html/search.php?query=qhull">http://rpmfind.net/linux/rpm2html/search.php?query=qhull</a>
+ <li><a href="http://rpm.pbone.net/index.php3/stat/3/srodzaj/1/search/qhull">http://rpm.pbone.net/index.php3/stat/3/srodzaj/1/search/qhull</a>
+ </ul></p>
+ </li>
+
+ <li><a
+ href="http://dl.acm.org/author_page.cfm?id=81100129162">Download:
+ Article about Qhull</a> (307K)
+ <p>Type: PDF on ACM Digital Library (from this page only)</p>
+ <p>Barber, C.B., Dobkin, D.P., and Huhdanpaa, H.T.,
+ &quot;The Quickhull algorithm for convex hulls,&quot; <i>ACM
+ Transactions on Mathematical Software</i>, 22(4):469-483, Dec 1996 [<a
+ href="http://portal.acm.org/citation.cfm?doid=235815.235821">abstract</a>].</p>
+ </li>
+
+ <li><a
+ href="http://www.qhull.org/download/qhull-1.0.tar.gz">Download:
+ Qhull version 1.0</a> (92K) <p>Type: C source code for
+ 32-bit architectures </p>
+ <p>Version 1.0 is a fifth the size of version 2.4. It
+ computes convex hulls and Delaunay triangulations. If a
+ precision error occurs, it stops with an error message.
+ It reports an initialization error for inputs made with
+ 0/1 coordinates. </p>
+ <p>Version 1.0 compiles on a PC with Borland C++ 4.02 for
+ Win32 and DOS Power Pack. The options for rbox are
+ &quot;bcc32 -WX -w- -O2-e -erbox -lc rbox.c&quot;. The
+ options for qhull are the same. [D. Zwick] </p>
+ </li>
+</ul>
+<!-- Navigation links -->
+<hr>
+
+<p><b>Up:</b> <a href="http://www.qhull.org"><i>Qhull Home Page</i></a><br>
+<!-- GC common information --></p>
+
+<hr>
+
+<p><a href="http://www.geom.uiuc.edu/"><img src="../html/qh--geom.gif" alt="[HOME]"
+align="middle"></a> <i>The Geometry Center Home Page</i> </p>
+
+<p>Comments to: <a href="mailto:qhull@qhull.org">qhull@qhull.org</a><br>
+</body>
+</html>
diff --git a/xs/src/qhull/html/qh-impre.htm b/xs/src/qhull/html/qh-impre.htm
new file mode 100644
index 000000000..cfbe0acb8
--- /dev/null
+++ b/xs/src/qhull/html/qh-impre.htm
@@ -0,0 +1,826 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+
+<head>
+<title>Imprecision in Qhull</title>
+</head>
+
+<body>
+<!-- Navigation links -->
+<p><a name="TOP"><b>Up:</b></a> <a href="http://www.qhull.org">Home
+page</a> for Qhull <br>
+<b>Up:</b> <a href="index.htm#TOC">Qhull manual</a>: Table of
+Contents<br>
+<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
+&#149; <a href="qh-quick.htm#options">Options</a>
+&#149; <a href="qh-opto.htm#output">Output</a>
+&#149; <a href="qh-optf.htm#format">Formats</a>
+&#149; <a href="qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="qh-optp.htm#print">Print</a>
+&#149; <a href="qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="qh-optc.htm#prec">Precision</a>
+&#149; <a href="qh-optt.htm#trace">Trace</a>
+&#149; <a href="../src/libqhull_r/index.htm">Functions</a><br>
+<b>To: </b><a href="#TOC">Qhull imprecision</a>: Table of Contents
+(please wait while loading)
+
+<hr>
+<!-- Main text of document -->
+<h1><a
+href="http://www.geom.uiuc.edu/graphics/pix/Special_Topics/Computational_Geometry/4dcube.html"><img
+src="qh--4d.gif" alt="[4-d cube]" align="middle" width="100"
+height="100"></a> Imprecision in Qhull</h1>
+
+<p>This section of the Qhull manual discusses the problems caused
+by coplanar points and why Qhull uses options '<a
+href="qh-optc.htm#C0">C-0</a>' or '<a href="qh-optq.htm#Qx">Qx</a>'
+by default. If you ignore precision issues with option '<a
+href="qh-optq.htm#Q0">Q0</a>', the output from Qhull can be
+arbitrarily bad. Qhull avoids precision problems if you merge facets (the default) or joggle
+the input ('<a
+href="qh-optq.htm#QJn">QJ</a>'). </p>
+
+<p>Use option '<a href="qh-optt.htm#Tv">Tv</a>' to verify the
+output from Qhull. It verifies that adjacent facets are clearly
+convex. It verifies that all points are on or below all facets. </p>
+
+<p>Qhull automatically tests for convexity if it detects
+precision errors while constructing the hull. </p>
+
+<p><b>Copyright &copy; 1995-2015 C.B. Barber</b></p>
+
+<hr>
+
+<h2><a href="#TOP">&#187;</a><a name="TOC">Qhull
+imprecision: Table of Contents </a></h2>
+
+<ul>
+ <li><a href="#prec">Precision problems</a></li>
+ <li><a href="#joggle">Merged facets or joggled input</a></li>
+ <li><a href="#delaunay">Delaunay triangulations</a></li>
+ <li><a href="#halfspace">Halfspace intersection/a></li>
+ <li><a href="#imprecise">Merged facets</a></li>
+ <li><a href="#how">How Qhull merges facets</a></li>
+ <li><a href="#limit">Limitations of merged facets</a></li>
+ <li><a href="#injoggle">Joggled input</a></li>
+ <li><a href="#exact">Exact arithmetic</a></li>
+ <li><a href="#approximate">Approximating a convex hull</a></li>
+</ul>
+
+<hr>
+
+<h2><a href="#TOC">&#187;</a><a name="prec">Precision problems</a></h2>
+
+<p>Since Qhull uses floating point arithmetic, roundoff error
+occurs with each calculation. This causes problems for
+geometric algorithms. Other floating point codes for convex
+hulls, Delaunay triangulations, and Voronoi diagrams also suffer
+from these problems. Qhull handles most of them.</p>
+
+<p>There are several kinds of precision errors:</p>
+
+<ul>
+ <li>Representation error occurs when there are not enough
+ digits to represent a number, e.g., 1/3.</li>
+ <li>Measurement error occurs when the input coordinates are
+ from measurements. </li>
+ <li>Roundoff error occurs when a calculation is rounded to a
+ fixed number of digits, e.g., a floating point
+ calculation.</li>
+ <li>Approximation error occurs when the user wants an
+ approximate result because the exact result contains too
+ much detail.</li>
+</ul>
+
+<p>Under imprecision, calculations may return erroneous results.
+For example, roundoff error can turn a small, positive number
+into a small, negative number. See Milenkovic [<a
+href="index.htm#mile93">'93</a>] for a discussion of <em>strict
+robust geometry</em>. Qhull does not meet Milenkovic's criterion
+for accuracy. Qhull's error bound is empirical instead of
+theoretical.</p>
+
+<p>Qhull 1.0 checked for precision errors but did not handle
+them. The output could contain concave facets, facets with
+inverted orientation (&quot;flipped&quot; facets), more than two
+facets adjacent to a ridge, and two facets with exactly the same
+set of vertices.</p>
+
+<p>Qhull 2.4 and later automatically handles errors due to
+machine round-off. Option '<a href="qh-optc.htm#C0">C-0</a>' or '<a
+href="qh-optq.htm#Qx">Qx</a>' is set by default. In 5-d and
+higher, the output is clearly convex but an input point could be
+outside of the hull. This may be corrected by using option '<a
+href="qh-optc.htm#C0">C-0</a>', but then the output may contain
+wide facets.</p>
+
+<p>Qhull 2.5 and later provides option '<a href="qh-optq.htm#QJn">QJ</a>'
+to joggled input. Each input coordinate is modified by a
+small, random quantity. If a precision error occurs, a larger
+modification is tried. When no precision errors occur, Qhull is
+done. </p>
+
+<p>Qhull 3.1 and later provides option '<a href="qh-optq.htm#Qt">Qt</a>'
+for triangulated output. This removes the need for
+joggled input ('<a href="qh-optq.htm#QJn">QJ</a>').
+Non-simplicial facets are triangulated.
+The facets may have zero area.
+Triangulated output is particularly useful for Delaunay triangulations.</p>
+
+<p>By handling round-off errors, Qhull can provide a variety of
+output formats. For example, it can return the halfspace that
+defines each facet ('<a href="qh-opto.htm#n">n</a>'). The
+halfspaces include roundoff error. If the halfspaces were exact,
+their intersection would return the original extreme points. With
+imprecise halfspaces and exact arithmetic, nearly incident points
+may be returned for an original extreme point. By handling
+roundoff error, Qhull returns one intersection point for each of
+the original extreme points. Qhull may split or merge an extreme
+point, but this appears to be unlikely.</p>
+
+<p>The following pipe implements the identity function for
+extreme points (with roundoff):
+<blockquote>
+ qconvex <a href="qh-optf.htm#FV">FV</a> <a
+ href="qh-opto.htm#n">n</a> | qhalf <a href="qh-optf.htm#Fp">Fp</a>
+</blockquote>
+
+<p>Bernd Gartner published his
+<a href=http://www.inf.ethz.ch/personal/gaertner/miniball.html>Miniball</a>
+algorithm ["Fast and robust smallest enclosing balls", <i>Algorithms - ESA '99</i>, LNCS 1643].
+It uses floating point arithmetic and a carefully designed primitive operation.
+It is practical to 20-D or higher, and identifies at least two points on the
+convex hull of the input set. Like Qhull, it is an incremental algorithm that
+processes points furthest from the intermediate result and ignores
+points that are close to the intermediate result.
+
+<h2><a href="#TOC">&#187;</a><a name="joggle">Merged facets or joggled input</a></h2>
+
+<p>This section discusses the choice between merged facets and joggled input.
+By default, Qhull uses merged facets to handle
+precision problems. With option '<a href="qh-optq.htm#QJn">QJ</a>',
+the input is joggled. See <a href="qh-eg.htm#joggle">examples</a>
+of joggled input and triangulated output.
+<ul>
+<li>Use merged facets (the default)
+when you want non-simplicial output (e.g., the faces of a cube).
+<li>Use merged facets and triangulated output ('<a href="qh-optq.htm#Qt">Qt</a>') when
+you want simplicial output and coplanar facets (e.g., triangles for a Delaunay triangulation).
+<li>Use joggled input ('<a href="qh-optq.htm#QJn">QJ</a>') when you need clearly-convex,
+simplicial output.
+</ul>
+
+<p>The choice between merged facets and joggled input depends on
+the application. Both run about the same speed. Joggled input may
+be faster if the initial joggle is sufficiently large to avoid
+precision errors.
+
+<p>Most applications should used merged facets
+with triangulated output. </p>
+
+<p>Use merged facets (the
+default, '<a href="qh-optc.htm#C0">C-0</a>')
+or triangulated output ('<a href="qh-optq.htm#Qt">Qt</a>') if </p>
+
+<ul>
+ <li>Your application supports non-simplicial facets, or
+ it allows degenerate, simplicial facets (option '<a href="qh-optq.htm#Qt">Qt</a>'). </li>
+ <li>You do not want the input modified. </li>
+ <li>You want to set additional options for approximating the
+ hull. </li>
+ <li>You use single precision arithmetic (<a href="../src/libqhull/user.h#realT">realT</a>).
+ </li>
+</ul>
+
+<p>Use joggled input ('<a href="qh-optq.htm#QJn">QJ</a>') if </p>
+
+<ul>
+ <li>Your application needs clearly convex, simplicial output</li>
+ <li>Your application supports perturbed input points and narrow triangles.</li>
+ <li>Seven significant digits is sufficient accuracy.</li>
+</ul>
+
+<p>You may use both techniques or combine joggle with
+post-merging ('<a href="qh-optc.htm#Cn2">Cn</a>'). </p>
+
+<p>Other researchers have used techniques similar to joggled
+input. Sullivan and Beichel [ref?] randomly perturb the input
+before computing the Delaunay triangulation. Corkum and Wyllie
+[news://comp.graphics, 1990] randomly rotate a polytope before
+testing point inclusion. Edelsbrunner and Mucke [Symp. Comp.
+Geo., 1988] and Yap [J. Comp. Sys. Sci., 1990] symbolically
+perturb the input to remove singularities. </p>
+
+<p>Merged facets ('<a href="qh-optc.htm#C0">C-0</a>') handles
+precision problems directly. If a precision problem occurs, Qhull
+merges one of the offending facets into one of its neighbors.
+Since all precision problems in Qhull are associated with one or
+more facets, Qhull will either fix the problem or attempt to merge the
+last remaining facets. </p>
+
+<h2><a href="#TOC">&#187;</a><a name="delaunay">Delaunay
+triangulations </a></h2>
+
+<p>Programs that use Delaunay triangulations traditionally assume
+a triangulated input. By default, <a href=qdelaun.htm>qdelaunay</a>
+merges regions with cocircular or cospherical input sites.
+If you want a simplicial triangulation
+use triangulated output ('<a href="qh-optq.htm#Qt">Qt</a>') or joggled
+input ('<a href="qh-optq.htm#QJn">QJ</a>').
+
+<p>For Delaunay triangulations, triangulated
+output should produce good results. All points are within roundoff error of
+a paraboloid. If two points are nearly incident, one will be a
+coplanar point. So all points are clearly separated and convex.
+If qhull reports deleted vertices, the triangulation
+may contain serious precision faults. Deleted vertices are reported
+in the summary ('<a href="qh-opto.htm#s">s</a>', '<a href="qh-optf.htm#Fs">Fs</a>'</p>
+
+<p>You should use option '<a href="qh-optq.htm#Qbb">Qbb</a>' with Delaunay
+triangulations. It scales the last coordinate and may reduce
+roundoff error. It is automatically set for <a href=qdelaun.htm>qdelaunay</a>,
+<a href=qvoronoi.htm>qvoronoi</a>, and option '<a
+href="qh-optq.htm#QJn">QJ</a>'.</p>
+
+<p>Edelsbrunner, H, <i>Geometry and Topology for Mesh Generation</i>, Cambridge University Press, 2001.
+Good mathematical treatise on Delaunay triangulation and mesh generation for 2-d
+and 3-d surfaces. The chapter on surface simplification is
+particularly interesting. It is similar to facet merging in Qhull.
+
+<p>Veron and Leon published an algorithm for shape preserving polyhedral
+simplification with bounded error [<i>Computers and Graphics</i>, 22.5:565-585, 1998].
+It remove nodes using front propagation and multiple remeshing.
+
+<h2><a href="#TOC">&#187;</a><a name="halfspace">Halfspace intersection</a></h2>
+
+<p>
+The identity pipe for Qhull reveals some precision questions for
+halfspace intersections. The identity pipe creates the convex hull of
+a set of points and intersects the facets' hyperplanes. It should return the input
+points, but narrow distributions may drop points while offset distributions may add
+points. It may be better to normalize the input set about the origin.
+For example, compare the first results with the later two results: [T. Abraham]
+<blockquote>
+ rbox 100 s t | tee r | qconvex FV n | qhalf Fp | cat - r | /bin/sort -n | tail
+<br>
+ rbox 100 L1e5 t | tee r | qconvex FV n | qhalf Fp | cat - r | /bin/sort -n | tail
+<br>
+ rbox 100 s O10 t | tee r | qconvex FV n | qhalf Fp | cat - r | /bin/sort -n | tail
+</blockquote>
+
+
+<h2><a href="#TOC">&#187;</a><a name="imprecise">Merged facets </a></h2>
+
+<p>Qhull detects precision
+problems when computing distances. A precision problem occurs if
+the distance computation is less than the maximum roundoff error.
+Qhull treats the result of a hyperplane computation as if it
+were exact.</p>
+
+<p>Qhull handles precision problems by merging non-convex facets.
+The result of merging two facets is a thick facet defined by an <i>inner
+plane</i> and an <i>outer plane</i>. The inner and outer planes
+are offsets from the facet's hyperplane. The inner plane is
+clearly below the facet's vertices. At the end of Qhull, the
+outer planes are clearly above all input points. Any exact convex
+hull must lie between the inner and outer planes.</p>
+
+<p>Qhull tests for convexity by computing a point for each facet.
+This point is called the facet's <i>centrum</i>. It is the
+arithmetic center of the facet's vertices projected to the
+facet's hyperplane. For simplicial facets with <em>d</em>
+vertices, the centrum is equivalent to the centroid or center of
+gravity. </p>
+
+<p>Two neighboring facets are convex if each centrum is clearly
+below the other hyperplane. The '<a href="qh-optc.htm#Cn2">Cn</a>'
+or '<a href="qh-optc.htm#Cn">C-n</a>' options sets the centrum's
+radius to <i>n </i>. A centrum is clearly below a hyperplane if
+the computed distance from the centrum to the hyperplane is
+greater than the centrum's radius plus two maximum roundoff
+errors. Two are required because the centrum can be the maximum
+roundoff error above its hyperplane and the distance computation
+can be high by the maximum roundoff error.</p>
+
+<p>With the '<a href="qh-optc.htm#Cn">C-n</a>' or '<a
+href="qh-optc.htm#An">A-n </a>' options, Qhull merges non-convex
+facets while constructing the hull. The remaining facets are
+clearly convex. With the '<a href="qh-optq.htm#Qx">Qx </a>'
+option, Qhull merges coplanar facets after constructing the hull.
+While constructing the hull, it merges coplanar horizon facets,
+flipped facets, concave facets and duplicated ridges. With '<a
+href="qh-optq.htm#Qx">Qx</a>', coplanar points may be missed, but
+it appears to be unlikely.</p>
+
+<p>If the user sets the '<a href="qh-optc.htm#An2">An</a>' or '<a
+href="qh-optc.htm#An">A-n</a>' option, then all neighboring
+facets are clearly convex and the maximum (exact) cosine of an
+angle is <i>n</i>.</p>
+
+<p>If '<a href="qh-optc.htm#C0">C-0</a>' or '<a
+href="qh-optq.htm#Qx">Qx</a>' is used without other precision
+options (default), Qhull tests vertices instead of centrums for
+adjacent simplices. In 3-d, if simplex <i>abc</i> is adjacent to
+simplex <i>bcd</i>, Qhull tests that vertex <i>a</i> is clearly
+below simplex <i>bcd </i>, and vertex <i>d</i> is clearly below
+simplex <i>abc</i>. When building the hull, Qhull tests vertices
+if the horizon is simplicial and no merges occur. </p>
+
+<h2><a href="#TOC">&#187;</a><a name="how">How Qhull merges facets</a></h2>
+
+<p>If two facets are not clearly convex, then Qhull removes one
+or the other facet by merging the facet into a neighbor. It
+selects the merge which minimizes the distance from the
+neighboring hyperplane to the facet's vertices. Qhull also
+performs merges when a facet has fewer than <i>d</i> neighbors (called a
+degenerate facet), when a facet's vertices are included in a
+neighboring facet's vertices (called a redundant facet), when a
+facet's orientation is flipped, or when a ridge occurs between
+more than two facets.</p>
+
+<p>Qhull performs merges in a series of passes sorted by merge
+angle. Each pass merges those facets which haven't already been
+merged in that pass. After a pass, Qhull checks for redundant
+vertices. For example, if a vertex has only two neighbors in 3-d,
+the vertex is redundant and Qhull merges it into an adjacent
+vertex.</p>
+
+<p>Merging two simplicial facets creates a non-simplicial facet
+of <em>d+1</em> vertices. Additional merges create larger facets.
+When merging facet A into facet B, Qhull retains facet B's
+hyperplane. It merges the vertices, neighbors, and ridges of both
+facets. It recomputes the centrum if a wide merge has not
+occurred (qh_WIDEcoplanar) and the number of extra vertices is
+smaller than a constant (qh_MAXnewcentrum).</p>
+
+
+<h2><a href="#TOC">&#187;</a><a name="limit">Limitations of merged
+facets</a></h2>
+
+<ul>
+<li><b>Uneven dimensions</b> --
+If one coordinate has a larger absolute value than other
+coordinates, it may dominate the effect of roundoff errors on
+distance computations. You may use option '<a
+href="qh-optq.htm#QbB">QbB</a>' to scale points to the unit cube.
+For Delaunay triangulations and Voronoi diagrams, <a href=qdelaun.htm>qdelaunay</a>
+and <a href=qvoronoi.htm>qvoronoi</a> always set
+option '<a href="qh-optq.htm#Qbb">Qbb</a>'. It scales the last
+coordinate to [0,m] where <i>m</i> is the maximum width of the
+other coordinates. Option '<a href="qh-optq.htm#Qbb">Qbb</a>' is
+needed for Delaunay triangulations of integer coordinates
+and nearly cocircular points.
+
+<p>For example, compare
+<pre>
+ rbox 1000 W0 t | qconvex Qb2:-1e-14B2:1e-14
+</pre>
+with
+<pre>
+ rbox 1000 W0 t | qconvex
+</pre>
+The distributions are the same but the first is compressed to a 2e-14 slab.
+<p>
+<li><b>Post-merging of coplanar facets</b> -- In 5-d and higher, option '<a href="qh-optq.htm#Qx">Qx</a>'
+(default) delays merging of coplanar facets until post-merging.
+This may allow &quot;dents&quot; to occur in the intermediate
+convex hulls. A point may be poorly partitioned and force a poor
+approximation. See option '<a href="qh-optq.htm#Qx">Qx</a>' for
+further discussion.</p>
+
+<p>This is difficult to produce in 5-d and higher. Option '<a href="qh-optq.htm#Q6">Q6</a>' turns off merging of concave
+facets. This is similar to 'Qx'. It may lead to serious precision errors,
+for example,
+<pre>
+ rbox 10000 W1e-13 | qhull Q6 Tv
+</pre>
+
+<p>
+<li><b>Maximum facet width</b> --
+Qhull reports the maximum outer plane and inner planes (if
+more than roundoff error apart). There is no upper bound
+for either figure. This is an area for further research. Qhull
+does a good job of post-merging in all dimensions. Qhull does a
+good job of pre-merging in 2-d, 3-d, and 4-d. With the '<a
+href="qh-optq.htm#Qx">Qx</a>' option, it does a good job in
+higher dimensions. In 5-d and higher, Qhull does poorly at
+detecting redundant vertices. </p>
+
+<p>In the summary ('<a href="qh-opto.htm#s">s</a>'), look at the
+ratio between the maximum facet width and the maximum width of a
+single merge, e.g., &quot;(3.4x)&quot;. Qhull usually reports a
+ratio of four or lower in 3-d and six or lower in 4-d. If it
+reports a ratio greater than 10, this may indicate an
+implementation error. Narrow distributions (see following) may
+produce wide facets.
+
+<p>For example, if special processing for narrow distributions is
+turned off ('<a href="qh-optq.htm#Q10">Q10</a>'), qhull may produce
+a wide facet:</p>
+<pre>
+ rbox 1000 L100000 s G1e-16 t1002074964 | qhull Tv Q10
+</pre>
+
+<p>
+<li><b>Narrow distribution</b> -- In 3-d, a narrow distribution may result in a poor
+approximation. For example, if you do not use qdelaunay nor option
+'<a href="qh-optq.htm#Qbb">Qbb</a>', the furthest-site
+Delaunay triangulation of nearly cocircular points may produce a poor
+approximation:
+<pre>
+ rbox s 5000 W1e-13 D2 t1002151341 | qhull d Qt
+ rbox 1000 s W1e-13 t1002231672 | qhull d Tv
+</pre>
+
+<p>During
+construction of the hull, a point may be above two
+facets with opposite orientations that span the input
+set. Even though the point may be nearly coplanar with both
+facets, and can be distant from the precise convex
+hull of the input sites. Additional facets leave the point distant from
+a facet. To fix this problem, add option '<a href="qh-optq.htm#Qbb">Qbb</a>'
+(it scales the last coordinate). Option '<a href="qh-optq.htm#Qbb">Qbb</a>'
+is automatically set for <a href=qdelaun.htm>qdelaunay</a> and <a href=qvoronoi.htm>qvoronoi</a>.
+
+<p>Qhull generates a warning if the initial simplex is narrow.
+For narrow distributions, Qhull changes how it processes coplanar
+points -- it does not make a point coplanar until the hull is
+finished.
+Use option '<a href="qh-optq.htm#Q10">Q10</a>' to try Qhull without
+special processing for narrow distributions.
+For example, special processing is needed for:
+<pre>
+ rbox 1000 L100000 s G1e-16 t1002074964 | qhull Tv Q10
+</pre>
+
+<p>You may turn off the warning message by reducing
+qh_WARNnarrow in <tt>user.h</tt> or by setting option
+'<a href="qh-optp.htm#Pp">Pp</a>'. </p>
+
+<p>Similar problems occur for distributions with a large flat facet surrounded
+with many small facet at a sharp angle to the large facet.
+Qhull 3.1 fixes most of these problems, but a poor approximation can occur.
+A point may be left outside of the convex hull ('<a href="qh-optt.htm#Tv">Tv</a>').
+Examples include
+the furthest-site Delaunay triangulation of nearly cocircular points plus the origin, and the convex hull of a cone of nearly cocircular points. The width of the band is 10^-13.
+<pre>
+ rbox s 1000 W1e-13 P0 D2 t996799242 | qhull d Tv
+ rbox 1000 s Z1 G1e-13 t1002152123 | qhull Tv
+ rbox 1000 s Z1 G1e-13 t1002231668 | qhull Tv
+</pre>
+
+<p>
+<li><b>Quadratic running time</b> -- If the output contains large, non-simplicial
+facets, the running time for Qhull may be quadratic in the size of the triangulated
+output. For example, <tt>rbox 1000 s W1e-13 c G2 | qhull d</tt> is 4 times
+faster for 500 points. The convex hull contains two large nearly spherical facets and
+many nearly coplanar facets. Each new point retriangulates the spherical facet and repartitions the remaining points into all of the nearly coplanar facets.
+In this case, quadratic running time is avoided if you use qdelaunay,
+add option '<a href="qh-optq.htm#Qbb">Qbb</a>',
+or add the origin ('P0') to the input.
+<p>
+<li><b>Nearly coincident points within 1e-13</b> --
+Multiple, nearly coincident points within a 1e-13 ball of points in the unit cube
+may lead to wide facets or quadratic running time.
+For example, the convex hull a 1000 coincident, cospherical points in 4-D,
+or the 3-D Delaunay triangulation of nearly coincident points, may lead to very
+wide facets (e.g., 2267021951.3x).
+
+<p>For Delaunay triangulations, the problem typically occurs for extreme points of the input
+set (i.e., on the edge between the upper and lower convex hull). After multiple facet merges, four
+facets may share the same, duplicate ridge and must be merged.
+Some of these facets may be long and narrow, leading to a very wide merged facet.
+If so, error QH6271 is reported. It may be overriden with option '<a href="qh-optq.htm#Q12">Q12</a>'.
+
+<p>Duplicate ridges occur when the horizon facets for a new point is "pinched".
+In a duplicate ridge, a subridge (e.g., a line segment in 3-d) is shared by two horizon facets.
+At least two of its vertices are nearly coincident. It is easy to generate coincident points with
+option 'Cn,r,m' of rbox. It generates n points within an r ball for each of m input sites. For example,
+every point of the following distributions has a nearly coincident point within a 1e-13 ball.
+Substantially smaller or larger balls do not lead to pinched horizons.
+<pre>
+ rbox 1000 C1,1e-13 D4 s t | qhull
+ rbox 75 C1,1e-13 t | qhull d
+</pre>
+For Delaunay triangulations, a bounding box may alleviate this error (e.g., <tt>rbox 500 C1,1E-13 t c G1 | qhull d</tt>).
+A later release of qhull will avoid pinched horizons by merging duplicate subridges. A subridge is
+merged by merging adjacent vertices.
+<p>
+<li><b>Facet with zero-area</b> --
+It is possible for a zero-area facet to be convex with its
+neighbors. This can occur if the hyperplanes of neighboring
+facets are above the facet's centrum, and the facet's hyperplane
+is above the neighboring centrums. Qhull computes the facet's
+hyperplane so that it passes through the facet's vertices. The
+vertices can be collinear. </p>
+
+<p>
+<li><b>No more facets</b> -- Qhull reports an error if there are <em>d+1</em> facets left
+and two of the facets are not clearly convex. This typically
+occurs when the convexity constraints are too strong or the input
+points are degenerate. The former is more likely in 5-d and
+higher -- especially with option '<a href="qh-optc.htm#Cn">C-n</a>'.</p>
+
+<p>
+<li><b>Deleted cone</b> -- Lots of merging can end up deleting all
+of the new facets for a point. This is a rare event that has
+only been seen while debugging the code.
+
+<p>
+<li><b>Triangulated output leads to precision problems</b> -- With sufficient
+merging, the ridges of a non-simplicial facet may have serious topological
+and geometric problems. A ridge may be between more than two
+neighboring facets. If so, their triangulation ('<a href="qh-optq.htm#Qt">Qt</a>')
+will fail since two facets have the same vertex set. Furthermore,
+a triangulated facet may have flipped orientation compared to its
+neighbors.</li>
+
+<p>The triangulation process detects degenerate facets with
+only two neighbors. These are marked degenerate. They have
+zero area.
+
+<p>
+<li><b>Coplanar points</b> --
+Option '<a href="qh-optq.htm#Qc">Qc</a>' is determined by
+qh_check_maxout() after constructing the hull. Qhull needs to
+retain all possible coplanar points in the facets' coplanar sets.
+This depends on qh_RATIOnearInside in <tt>user.h.</tt>
+Furthermore, the cutoff for a coplanar point is arbitrarily set
+at the minimum vertex. If coplanar points are important to your
+application, remove the interior points by hand (set '<a
+href="qh-optq.htm#Qc">Qc</a> <a href="qh-optq.htm#Qi">Qi</a>') or
+make qh_RATIOnearInside sufficiently large.</p>
+
+<p>
+<li><b>Maximum roundoff error</b> -- Qhull computes the maximum roundoff error from the maximum
+coordinates of the point set. Usually the maximum roundoff error
+is a reasonable choice for all distance computations. The maximum
+roundoff error could be computed separately for each point or for
+each distance computation. This is expensive and it conflicts
+with option '<a href="qh-optc.htm#Cn">C-n</a>'.
+
+<p>
+<li><b>All flipped or upper Delaunay</b> -- When a lot of merging occurs for
+Delaunay triangulations, a new point may lead to no good facets. For example,
+try a strong convexity constraint:
+<pre>
+ rbox 1000 s t993602376 | qdelaunay C-1e-3
+</pre>
+
+</ul>
+
+<h2><a href="#TOC">&#187;</a><a name="injoggle">Joggled input</a></h2>
+
+<p>Joggled input is a simple work-around for precision problems
+in computational geometry [&quot;joggle: to shake or jar
+slightly,&quot; Amer. Heritage Dictionary]. Other names are
+<i>jostled input</i> or <i>random perturbation</i>.
+Qhull joggles the
+input by modifying each coordinate by a small random quantity. If
+a precision problem occurs, Qhull joggles the input with a larger
+quantity and the algorithm is restarted. This process continues
+until no precision problems occur. Unless all inputs incur
+precision problems, Qhull will terminate. Qhull adjusts the inner
+and outer planes to account for the joggled input. </p>
+
+<p>Neither joggle nor merged facets has an upper bound for the width of the output
+facets, but both methods work well in practice. Joggled input is
+easier to justify. Precision errors occur when the points are
+nearly singular. For example, four points may be coplanar or
+three points may be collinear. Consider a line and an incident
+point. A precision error occurs if the point is within some
+epsilon of the line. Now joggle the point away from the line by a
+small, uniformly distributed, random quantity. If the point is
+changed by more than epsilon, the precision error is avoided. The
+probability of this event depends on the maximum joggle. Once the
+maximum joggle is larger than epsilon, doubling the maximum
+joggle will halve the probability of a precision error. </p>
+
+<p>With actual data, an analysis would need to account for each
+point changing independently and other computations. It is easier
+to determine the probabilities empirically ('<a href="qh-optt.htm#TRn">TRn</a>') . For example, consider
+computing the convex hull of the unit cube centered on the
+origin. The arithmetic has 16 significant decimal digits. </p>
+
+<blockquote>
+ <p><b>Convex hull of unit cube</b> </p>
+ <table border="1">
+ <tr>
+ <th align="left">joggle</th>
+ <th align="right">error prob. </th>
+ </tr>
+ <tr>
+ <td align="right">1.0e-15</td>
+ <td align="center">0.983 </td>
+ </tr>
+ <tr>
+ <td align="right">2.0e-15</td>
+ <td align="center">0.830 </td>
+ </tr>
+ <tr>
+ <td align="right">4.0e-15</td>
+ <td align="center">0.561 </td>
+ </tr>
+ <tr>
+ <td align="right">8.0e-15</td>
+ <td align="center">0.325 </td>
+ </tr>
+ <tr>
+ <td align="right">1.6e-14</td>
+ <td align="center">0.185 </td>
+ </tr>
+ <tr>
+ <td align="right">3.2e-14</td>
+ <td align="center">0.099 </td>
+ </tr>
+ <tr>
+ <td align="right">6.4e-14</td>
+ <td align="center">0.051 </td>
+ </tr>
+ <tr>
+ <td align="right">1.3e-13</td>
+ <td align="center">0.025 </td>
+ </tr>
+ <tr>
+ <td align="right">2.6e-13</td>
+ <td align="center">0.010 </td>
+ </tr>
+ <tr>
+ <td align="right">5.1e-13</td>
+ <td align="center">0.004 </td>
+ </tr>
+ <tr>
+ <td align="right">1.0e-12</td>
+ <td align="center">0.002 </td>
+ </tr>
+ <tr>
+ <td align="right">2.0e-12</td>
+ <td align="center">0.001 </td>
+ </tr>
+ </table>
+</blockquote>
+
+<p>A larger joggle is needed for multiple points. Since the
+number of potential singularities increases, the probability of
+one or more precision errors increases. Here is an example. </p>
+
+<blockquote>
+ <p><b>Convex hull of 1000 points on unit cube</b> </p>
+ <table border="1">
+ <tr>
+ <th align="left">joggle</th>
+ <th align="right">error prob. </th>
+ </tr>
+ <tr>
+ <td align="right">1.0e-12</td>
+ <td align="center">0.870 </td>
+ </tr>
+ <tr>
+ <td align="right">2.0e-12</td>
+ <td align="center">0.700 </td>
+ </tr>
+ <tr>
+ <td align="right">4.0e-12</td>
+ <td align="center">0.450 </td>
+ </tr>
+ <tr>
+ <td align="right">8.0e-12</td>
+ <td align="center">0.250 </td>
+ </tr>
+ <tr>
+ <td align="right">1.6e-11</td>
+ <td align="center">0.110 </td>
+ </tr>
+ <tr>
+ <td align="right">3.2e-11</td>
+ <td align="center">0.065 </td>
+ </tr>
+ <tr>
+ <td align="right">6.4e-11</td>
+ <td align="center">0.030 </td>
+ </tr>
+ <tr>
+ <td align="right">1.3e-10</td>
+ <td align="center">0.010 </td>
+ </tr>
+ <tr>
+ <td align="right">2.6e-10</td>
+ <td align="center">0.008 </td>
+ </tr>
+ <tr>
+ <td align="right">5.1e-09</td>
+ <td align="center">0.003 </td>
+ </tr>
+ </table>
+</blockquote>
+
+<p>Other distributions behave similarly. No distribution should
+behave significantly worse. In Euclidean space, the probability
+measure of all singularities is zero. With floating point
+numbers, the probability of a singularity is non-zero. With
+sufficient digits, the probability of a singularity is extremely
+small for random data. For a sufficiently large joggle, all data
+is nearly random data. </p>
+
+<p>Qhull uses an initial joggle of 30,000 times the maximum
+roundoff error for a distance computation. This avoids most
+potential singularities. If a failure occurs, Qhull retries at
+the initial joggle (in case bad luck occurred). If it occurs
+again, Qhull increases the joggle by ten-fold and tries again.
+This process repeats until the joggle is a hundredth of the width
+of the input points. Qhull reports an error after 100 attempts.
+This should never happen with double-precision arithmetic. Once
+the probability of success is non-zero, the probability of
+success increases about ten-fold at each iteration. The
+probability of repeated failures becomes extremely small. </p>
+
+<p>Merged facets produces a significantly better approximation.
+Empirically, the maximum separation between inner and outer
+facets is about 30 times the maximum roundoff error for a
+distance computation. This is about 2,000 times better than
+joggled input. Most applications though will not notice the
+difference. </p>
+
+<h2><a href="#TOC">&#187;</a><a name="exact">Exact arithmetic</a></h2>
+
+<p>Exact arithmetic may be used instead of floating point.
+Singularities such as coplanar points can either be handled
+directly or the input can be symbolically perturbed. Using exact
+arithmetic is slower than using floating point arithmetic and the
+output may take more space. Chaining a sequence of operations
+increases the time and space required. Some operations are
+difficult to do.</p>
+
+<p>Clarkson's <a
+href="http://www.netlib.org/voronoi/hull.html">hull
+program</a> and Shewchuk's <a
+href="http://www.cs.cmu.edu/~quake/triangle.html">triangle
+program</a> are practical implementations of exact arithmetic.</p>
+
+<p>Clarkson limits the input precision to about fifteen digits.
+This reduces the number of nearly singular computations. When a
+determinant is nearly singular, he uses exact arithmetic to
+compute a precise result.</p>
+
+<h2><a href="#TOC">&#187;</a><a name="approximate">Approximating a
+convex hull</a></h2>
+
+<p>Qhull may be used for approximating a convex hull. This is
+particularly valuable in 5-d and higher where hulls can be
+immense. You can use '<a href="qh-optq.htm#Qx">Qx</a> <a
+href="qh-optc.htm#Cn">C-n</a>' to merge facets as the hull is
+being constructed. Then use '<a href="qh-optc.htm#Cn2">Cn</a>'
+and/or '<a href="qh-optc.htm#An2">An</a>' to merge small facets
+during post-processing. You can print the <i>n</i> largest facets
+with option '<a href="qh-optp.htm#PAn">PAn</a>'. You can print
+facets whose area is at least <i>n</i> with option '<a
+href="qh-optp.htm#PFn">PFn</a>'. You can output the outer planes
+and an interior point with '<a href="qh-optf.htm#FV">FV</a> <a
+href="qh-optf.htm#Fo">Fo</a>' and then compute their intersection
+with 'qhalf'. </p>
+
+<p>To approximate a convex hull in 6-d and higher, use
+post-merging with '<a href="qh-optc.htm#Wn">Wn</a>' (e.g., qhull
+W1e-1 C1e-2 TF2000). Pre-merging with a convexity constraint
+(e.g., qhull Qx C-1e-2) often produces a poor approximation or
+terminates with a simplex. Option '<a href="qh-optq.htm#QbB">QbB</a>'
+may help to spread out the data.</p>
+
+<p>You will need to experiment to determine a satisfactory set of
+options. Use <a href="rbox.htm">rbox</a> to generate test sets
+quickly and <a href="index.htm#geomview">Geomview</a> to view
+the results. You will probably want to write your own driver for
+Qhull using the Qhull library. For example, you could select the
+largest facet in each quadrant.</p>
+
+<!-- Navigation links -->
+<hr>
+
+<p><b>Up:</b> <a href="http://www.qhull.org">Home
+page</a> for Qhull <br>
+<b>Up:</b> <a href="index.htm#TOC">Qhull manual</a>: Table of
+Contents<br>
+<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
+&#149; <a href="qh-quick.htm#options">Options</a>
+&#149; <a href="qh-opto.htm#output">Output</a>
+&#149; <a href="qh-optf.htm#format">Formats</a>
+&#149; <a href="qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="qh-optp.htm#print">Print</a>
+&#149; <a href="qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="qh-optc.htm#prec">Precision</a>
+&#149; <a href="qh-optt.htm#trace">Trace</a>
+&#149; <a href="../src/libqhull_r/index.htm">Functions</a><br>
+<b>To:</b> <a href="#TOC">Qhull imprecision: Table of Contents</a>
+
+<!-- GC common information -->
+<hr>
+
+<p><a href="http://www.geom.uiuc.edu/"><img src="qh--geom.gif"
+align="middle" width="40" height="40"></a><i>The Geometry Center
+Home Page </i></p>
+
+<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a>
+</a><br>
+Created: Sept. 25, 1995 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p>
+</body>
+</html>
diff --git a/xs/src/qhull/html/qh-optc.htm b/xs/src/qhull/html/qh-optc.htm
new file mode 100644
index 000000000..87308180d
--- /dev/null
+++ b/xs/src/qhull/html/qh-optc.htm
@@ -0,0 +1,292 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+
+<head>
+<title>Qhull precision options</title>
+</head>
+
+<body>
+<!-- Navigation links -->
+<p><b>Up:</b> <a href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b> <a href="index.htm#TOC">Qhull manual</a>: Table of Contents<br>
+<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
+&#149; <a href="qh-quick.htm#options">Options</a>
+&#149; <a href="qh-opto.htm#output">Output</a>
+&#149; <a href="qh-optf.htm#format">Formats</a>
+&#149; <a href="qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="qh-optp.htm#print">Print</a>
+&#149; <a href="qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="qh-optc.htm#prec">Precision</a>
+&#149; <a href="qh-optt.htm#trace">Trace</a>
+&#149; <a href="../src/libqhull_r/index.htm">Functions</a></p>
+
+<hr>
+<!-- Main text of document -->
+<h1><a
+href="http://www.geom.uiuc.edu/graphics/pix/Special_Topics/Computational_Geometry/delaunay.html"><img
+src="qh--dt.gif" alt="[delaunay]" align="middle" width="100"
+height="100"></a> Qhull precision options</h1>
+
+This section lists the precision options for Qhull. These options are
+indicated by an upper-case letter followed by a number.
+
+<p><b>Copyright &copy; 1995-2015 C.B. Barber</b></p>
+
+<hr>
+
+<p><a href="index.htm#TOC">&#187;</a> <a href="qh-quick.htm#programs">Programs</a>
+<a name="prec">&#149;</a> <a href="qh-quick.htm#options">Options</a>
+&#149; <a href="qh-opto.htm#output">Output</a>
+&#149; <a href="qh-optf.htm#format">Formats</a>
+&#149; <a href="qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="qh-optp.htm#print">Print</a>
+&#149; <a href="qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="qh-optc.htm#prec">Precision</a>
+&#149; <a href="qh-optt.htm#trace">Trace</a>
+&#149; <a href="../src/libqhull_r/index.htm">Functions</a></p>
+
+<h2>Precision options</h2>
+
+<p>Most users will not need to set these options. They are best
+used for <a href="qh-impre.htm#approximate">approximating</a> a
+convex hull. They may also be used for testing Qhull's handling
+of precision errors.</p>
+
+<p>By default, Qhull uses options '<a href="#C0">C-0</a>' for
+2-d, 3-d and 4-d, and '<a href="qh-optq.htm#Qx">Qx</a>' for 5-d
+and higher. These options use facet merging to handle precision
+errors. You may also use joggled input '<a href="qh-optq.htm#QJn">QJ</a>'
+to avoid precision problems.
+For more information see <a
+href="qh-impre.htm">Imprecision in Qhull</a>.</p>
+
+<dl compact>
+ <dt>&nbsp;</dt>
+ <dd><b>General</b></dd>
+ <dt><a href="#Cn2">Cn</a></dt>
+ <dd>centrum radius for post-merging</dd>
+ <dt><a href="#Cn">C-n</a></dt>
+ <dd>centrum radius for pre-merging</dd>
+ <dt><a href="#An2">An</a></dt>
+ <dd>cosine of maximum angle for post-merging</dd>
+ <dt><a href="#An">A-n</a></dt>
+ <dd>cosine of maximum angle for pre-merging</dd>
+ <dt><a href="qh-optq.htm#Qx">Qx</a></dt>
+ <dd>exact pre-merges (allows coplanar facets)</dd>
+ <dt><a href="#C0">C-0</a></dt>
+ <dd>handle all precision errors</dd>
+ <dt><a href="#Wn">Wn</a></dt>
+ <dd>min distance above plane for outside points</dd>
+</dl>
+
+<dl compact>
+ <dt>&nbsp;</dt>
+ <dd><b>Experimental</b></dd>
+ <dt><a href="#Un">Un</a></dt>
+ <dd>max distance below plane for a new, coplanar point</dd>
+ <dt><a href="#En">En</a></dt>
+ <dd>max roundoff error for distance computation</dd>
+ <dt><a href="#Vn">Vn</a></dt>
+ <dd>min distance above plane for a visible facet</dd>
+ <dt><a href="#Rn">Rn</a></dt>
+ <dd>randomly perturb computations by a factor of [1-n,1+n]</dd>
+</dl>
+
+<dl compact>
+</dl>
+
+<hr>
+
+<h3><a href="#prec">&#187;</a><a name="An">A-n - cosine of maximum
+angle for pre-merging.</a></h3>
+
+<p>Pre-merging occurs while Qhull constructs the hull. It is
+indicated by '<a href="#Cn">C-n</a>', 'A-n', or '<a
+href="qh-optq.htm#Qx">Qx</a>'.</p>
+
+<p>If the angle between a pair of facet normals is greater than <i>n</i>,
+Qhull merges one of the facets into a neighbor. It selects the
+facet that is closest to a neighboring facet.</p>
+
+<p>For example, option 'A-0.99' merges facets during the
+construction of the hull. If the cosine of the angle between
+facets is greater than 0.99, one or the other facet is merged.
+Qhull accounts for the maximum roundoff error.</p>
+
+<p>If 'A-n' is set without '<a href="#Cn">C-n</a>', then '<a
+href="#C0">C-0</a>' is automatically set. </p>
+
+<p>In 5-d and higher, you should set '<a href="qh-optq.htm#Qx">Qx</a>'
+along with 'A-n'. It skips merges of coplanar facets until after
+the hull is constructed and before '<a href="#An2">An</a>' and '<a
+href="#Cn2">Cn</a>' are checked. </p>
+
+<h3><a href="#prec">&#187;</a><a name="An2">An - cosine of maximum angle for
+post-merging.</a></h3>
+
+<p>Post merging occurs after the hull is constructed. For
+example, option 'A0.99' merges a facet if the cosine of the angle
+between facets is greater than 0.99. Qhull accounts for the
+maximum roundoff error.</p>
+
+<p>If 'An' is set without '<a href="#Cn2">Cn</a>', then '<a
+href="#Cn2">C0</a>' is automatically set. </p>
+
+<h3><a href="#prec">&#187;</a><a name="C0">C-0 - handle all precision
+errors </a></h3>
+
+<p>Qhull handles precision errors by merging facets. The 'C-0'
+option handles all precision errors in 2-d, 3-d, and 4-d. It is
+set by default. It may be used in higher dimensions, but
+sometimes the facet width grows rapidly. It is usually better to
+use '<a href="qh-optq.htm#Qx">Qx</a>' in 5-d and higher.
+Use '<a href="qh-optq.htm#QJn">QJ</a>' to joggle the input
+instead of merging facets.
+Use '<a
+href="qh-optq.htm#Q0">Q0</a>' to turn both options off.</p>
+
+<p>Qhull optimizes 'C-0' (&quot;_zero-centrum&quot;) by testing
+vertices instead of centrums for adjacent simplices. This may be
+slower in higher dimensions if merges decrease the number of
+processed points. The optimization may be turned off by setting a
+small value such as 'C-1e-30'. See <a href="qh-impre.htm">How
+Qhull handles imprecision</a>.</p>
+
+<h3><a href="#prec">&#187;</a><a name="Cn">C-n - centrum radius for
+pre-merging</a></h3>
+
+<p>Pre-merging occurs while Qhull constructs the hull. It is
+indicated by 'C-n', '<a href="#An">A-n</a>', or '<a
+href="qh-optq.htm#Qx">Qx</a>'.</p>
+
+<p>The <i>centrum</i> of a facet is a point on the facet for
+testing facet convexity. It is the average of the vertices
+projected to the facet's hyperplane. Two adjacent facets are
+convex if each centrum is clearly below the other facet. </p>
+
+<p>If adjacent facets are non-convex, one of the facets is merged
+into a neighboring facet. Qhull merges the facet that is closest
+to a neighboring facet. </p>
+
+<p>For option 'C-n', <i>n</i> is the centrum radius. For example,
+'C-0.001' merges facets whenever the centrum is less than 0.001
+from a neighboring hyperplane. Qhull accounts for roundoff error
+when testing the centrum.</p>
+
+<p>In 5-d and higher, you should set '<a href="qh-optq.htm#Qx">Qx</a>'
+along with 'C-n'. It skips merges of coplanar facets until after
+the hull is constructed and before '<a href="#An2">An</a>' and '<a
+href="#Cn2">Cn</a>' are checked. </p>
+
+<h3><a href="#prec">&#187;</a><a name="Cn2">Cn - centrum radius for
+post-merging</a></h3>
+
+<p>Post-merging occurs after Qhull constructs the hull. It is
+indicated by '<a href="#Cn2">Cn</a>' or '<a href="#An2">An</a>'. </p>
+
+<p>For option '<a href="#Cn2">Cn</a>', <i>n</i> is the centrum
+radius. For example, 'C0.001' merges facets when the centrum is
+less than 0.001 from a neighboring hyperplane. Qhull accounts for
+roundoff error when testing the centrum.</p>
+
+<p>Both pre-merging and post-merging may be defined. If only
+post-merging is used ('<a href="qh-optq.htm#Q0">Q0</a>' with
+'Cn'), Qhull may fail to produce a hull due to precision errors
+during the hull's construction.</p>
+
+<h3><a href="#prec">&#187;</a><a name="En">En - max roundoff error
+for distance computations</a></h3>
+
+<p>This allows the user to change the maximum roundoff error
+computed by Qhull. The value computed by Qhull may be overly
+pessimistic. If 'En' is set too small, then the output may not be
+convex. The statistic &quot;max. distance of a new vertex to a
+facet&quot; (from option '<a href="qh-optt.htm#Ts">Ts</a>') is a
+reasonable upper bound for the actual roundoff error. </p>
+
+<h3><a href="#prec">&#187;</a><a name="Rn">Rn - randomly perturb
+computations </a></h3>
+
+<p>This option perturbs every distance, hyperplane, and angle
+computation by up to <i>(+/- n * max_coord)</i>. It simulates the
+effect of roundoff errors. Unless '<a href="#En">En</a>' is
+explicitly set, it is adjusted for 'Rn'. The command 'qhull Rn'
+will generate a convex hull despite the perturbations. See the <a
+href="qh-eg.htm#merge">Examples </a>section for an example.</p>
+
+<p>Options 'Rn C-n' have the effect of '<a href="#Wn">W2n</a>'
+and '<a href="#Cn">C-2n</a>'. To use time as the random number
+seed, use option '<a href="qh-optq.htm#QRn">QR-1</a>'.</p>
+
+<h3><a href="#prec">&#187;</a><a name="Un">Un - max distance for a
+new, coplanar point </a></h3>
+
+<p>This allows the user to set coplanarity. When pre-merging ('<a
+href="#Cn">C-n </a>', '<a href="#An">A-n</a>' or '<a
+href="qh-optq.htm#Qx">Qx</a>'), Qhull merges a new point into any
+coplanar facets. The default value for 'Un' is '<a href="#Vn">Vn</a>'.</p>
+
+<h3><a href="#prec">&#187;</a><a name="Vn">Vn - min distance for a
+visible facet </a></h3>
+
+<p>This allows the user to set facet visibility. When adding a
+point to the convex hull, Qhull determines all facets that are
+visible from the point. A facet is visible if the distance from
+the point to the facet is greater than 'Vn'.</p>
+
+<p>Without merging, the default value for 'Vn' is the roundoff
+error ('<a href="#En">En</a>'). With merging, the default value
+is the pre-merge centrum ('<a href="#Cn">C-n</a>') in 2-d or 3-d,
+or three times that in other dimensions. If the outside width is
+specified with option '<a href="#Wn">Wn </a>', the maximum,
+default value for 'Vn' is '<a href="#Wn">Wn</a>'.</p>
+
+<p>Qhull warns if 'Vn' is greater than '<a href="#Wn">Wn</a>' and
+furthest outside ('<a href="qh-optq.htm#Qf">Qf</a>') is not
+selected; this combination usually results in flipped facets
+(i.e., reversed normals).</p>
+
+<h3><a href="#prec">&#187;</a><a name="Wn">Wn - min distance above
+plane for outside points</a></h3>
+
+<p>Points are added to the convex hull only if they are clearly
+outside of a facet. A point is outside of a facet if its distance
+to the facet is greater than 'Wn'. Without pre-merging, the
+default value for 'Wn' is '<a href="#En">En </a>'. If the user
+specifies pre-merging and does not set 'Wn', than 'Wn' is set to
+the maximum of '<a href="#Cn">C-n</a>' and <i>maxcoord*(1 - </i><a
+href="#An"><i>A-n</i></a><i>)</i>.</p>
+
+<p>This option is good for <a href="qh-impre.htm#approximate">approximating</a>
+a convex hull.</p>
+
+<p>Options '<a href="qh-optq.htm#Qc">Qc</a>' and '<a
+href="qh-optq.htm#Qi">Qi</a>' use the minimum vertex to
+distinguish coplanar points from interior points.</p>
+<!-- Navigation links -->
+<hr>
+
+<p><b>Up:</b> <a href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b> <a href="index.htm#TOC">Qhull manual</a>: Table of Contents<br>
+<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
+&#149; <a href="qh-quick.htm#options">Options</a>
+&#149; <a href="qh-opto.htm#output">Output</a>
+&#149; <a href="qh-optf.htm#format">Formats</a>
+&#149; <a href="qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="qh-optp.htm#print">Print</a>
+&#149; <a href="qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="qh-optc.htm#prec">Precision</a>
+&#149; <a href="qh-optt.htm#trace">Trace</a>
+&#149; <a href="../src/libqhull_r/index.htm">Functions</a></p>
+<!-- GC common information -->
+<hr>
+
+<p><a href="http://www.geom.uiuc.edu/"><img src="qh--geom.gif"
+align="middle" width="40" height="40"></a><i>The Geometry Center
+Home Page </i></p>
+
+<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a>
+</a><br>
+Created: Sept. 25, 1995 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p>
+</body>
+</html>
diff --git a/xs/src/qhull/html/qh-optf.htm b/xs/src/qhull/html/qh-optf.htm
new file mode 100644
index 000000000..3c7a2b1db
--- /dev/null
+++ b/xs/src/qhull/html/qh-optf.htm
@@ -0,0 +1,736 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+
+<head>
+<title>Qhull format options (F)</title>
+</head>
+
+<body><!-- Navigation links -->
+<p><b>Up:</b> <a href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b> <a href="index.htm#TOC">Qhull manual</a>: Table of Contents<br>
+<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
+&#149; <a href="qh-quick.htm#options">Options</a>
+&#149; <a href="qh-opto.htm#output">Output</a>
+&#149; <a href="qh-optf.htm#format">Formats</a>
+&#149; <a href="qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="qh-optp.htm#print">Print</a>
+&#149; <a href="qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="qh-optc.htm#prec">Precision</a>
+&#149; <a href="qh-optt.htm#trace">Trace</a>
+&#149; <a href="../src/libqhull_r/index.htm">Functions</a></p>
+<hr>
+<!-- Main text of document -->
+<h1><a
+ href="http://www.geom.uiuc.edu/graphics/pix/Special_Topics/Computational_Geometry/delaunay.html"><IMG
+ align=middle alt=[delaunay] height=100
+ src="qh--dt.gif" width=100 ></a> Qhull format options (F)</h1>
+
+<p>This section lists the format options for Qhull. These options
+are indicated by 'F' followed by a letter. See <A
+ href="qh-opto.htm#output" >Output</a>, <a href="qh-optp.htm#print">Print</a>,
+and <a href="qh-optg.htm#geomview">Geomview</a> for other output
+options. </p>
+
+<p><b>Copyright &copy; 1995-2015 C.B. Barber</b></p>
+
+<hr>
+
+<p><a href="index.htm#TOC">&#187;</a> <a href="qh-quick.htm#programs">Programs</a>
+<a name="format">&#149;</a> <a href="qh-quick.htm#options">Options</a>
+&#149; <a href="qh-opto.htm#output">Output</a>
+&#149; <a href="qh-optf.htm#format">Formats</a>
+&#149; <a href="qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="qh-optp.htm#print">Print</a>
+&#149; <a href="qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="qh-optc.htm#prec">Precision</a>
+&#149; <a href="qh-optt.htm#trace">Trace</a>
+&#149; <a href="../src/libqhull_r/index.htm">Functions</a></p>
+
+<h2>Additional input &amp; output formats</h2>
+
+<p>These options allow for automatic processing of Qhull output.
+Options '<a href="qh-opto.htm#i">i</a>', '<a href="qh-opto.htm#o">o</a>',
+'<a href="qh-opto.htm#n">n</a>', and '<a href="qh-opto.htm#p">p</a>'
+may also be used.</p>
+
+<dl compact>
+ <dt>
+ <dd><b>Summary and control</b>
+ <dt><a href="#FA">FA</a>
+ <dd>compute total area and volume for option '<A
+ href="qh-opto.htm#s">s</a>'
+
+ <dt><a href="#FV">FV</a>
+ <dd>print average vertex (interior point for '<A
+ href="qhalf.htm">qhalf</a>')
+ <dt><a href="#FQ">FQ</a>
+ <dd>print command for qhull and input
+ <dt><a href="#FO">FO</a>
+ <dd>print options to stderr or stdout
+ <dt><a href="#FS">FS</a>
+ <dd>print sizes: total area and volume
+ <dt><a href="#Fs">Fs</a>
+ <dd>print summary: dim, #points, total vertices and
+ facets, #vertices, #facets, max outer and inner plane
+ <dt><a href="#Fd">Fd</a>
+ <dd>use format for input (offset first)
+ <dt><a href="#FD">FD</a>
+ <dd>use cdd format for normals (offset first)
+ <dt><a href="#FM">FM</a>
+ <dd>print Maple output (2-d and 3-d)
+ <dt>
+ <dt>
+ <dd><b>Facets, points, and vertices</b>
+ <dt><a href="#Fa">Fa</a>
+ <dd>print area for each facet
+ <dt><a href="#FC">FC</a>
+ <dd>print centrum for each facet
+ <dt><a href="#Fc">Fc</a>
+ <dd>print coplanar points for each facet
+ <dt><a href="#Fx">Fx</a>
+ <dd>print extreme points (i.e., vertices) of convex hull.
+
+ <dt><a href="#FF">FF</a>
+ <dd>print facets w/o ridges
+ <dt><a href="#FI">FI</a>
+ <dd>print ID for each facet
+ <dt><a href="#Fi">Fi</a>
+ <dd>print inner planes for each facet
+ <dt><a href="#Fm">Fm</a>
+ <dd>print merge count for each facet (511 max)
+ <dt><a href="#FP">FP</a>
+ <dd>print nearest vertex for coplanar points
+ <dt><a href="#Fn">Fn</a>
+ <dd>print neighboring facets for each facet
+ <dt><a href="#FN">FN</a>
+ <dd>print neighboring facets for each point
+ <dt><a href="#Fo">Fo</a>
+ <dd>print outer planes for each facet
+ <dt><a href="#Ft">Ft</a>
+ <dd>print triangulation with added points
+ <dt><a href="#Fv">Fv</a>
+ <dd>print vertices for each facet
+ <dt>
+ <dt>
+ <dd><b>Delaunay, Voronoi, and halfspace</b>
+ <dt><a href="#Fx">Fx</a>
+ <dd>print extreme input sites of Delaunay triangulation
+ or Voronoi diagram.
+ <dt><a href="#Fp">Fp</a>
+ <dd>print points at halfspace intersections
+ <dt><a href="#Fi2">Fi</a>
+ <dd>print separating hyperplanes for inner, bounded
+ Voronoi regions
+ <dt><a href="#Fo2">Fo</a>
+ <dd>print separating hyperplanes for outer, unbounded
+ Voronoi regions
+ <dt><a href="#Fv2">Fv</a>
+ <dd>print Voronoi diagram as ridges for each input pair
+ <dt><a href="#FC">FC</a>
+ <dd>print Voronoi vertex ("center") for each facet</dd>
+</dl>
+
+<hr>
+
+<h3><a href="#format">&#187;</a><a name="Fa">Fa - print area for each
+facet </a></h3>
+
+<p>The first line is the number of facets. The remaining lines
+are the area for each facet, one facet per line. See '<A
+ href="#FA" >FA</a>' and '<a href="#FS">FS</a>' for computing the total area and volume.</p>
+
+<p>Use '<a href="qh-optp.htm#PAn">PAn</a>' for printing the n
+largest facets. Use option '<a href="qh-optp.htm#PFn">PFn</a>'
+for printing facets larger than <i>n</i>.</p>
+
+<p>For Delaunay triangulations, the area is the area of each
+Delaunay triangle. For Voronoi vertices, the area is the area of
+the dual facet to each vertex. </p>
+
+<p>Qhull uses the centrum and ridges to triangulate
+non-simplicial facets. The area for non-simplicial facets is the
+sum of the areas for each triangle. It is an approximation of the
+actual area. The ridge's vertices are projected to the facet's
+hyperplane. If a vertex is far below a facet (qh_WIDEcoplanar in <tt>user.h</tt>),
+the corresponding triangles are ignored.</p>
+
+<p>For non-simplicial facets, vertices are often below the
+facet's hyperplane. If so, the approximation is less than the
+actual value and it may be significantly less. </p>
+
+<h3><a href="#format">&#187;</a><a name="FA">FA - compute total area
+and volume for option 's' </a></h3>
+
+<p>With option 'FA', Qhull includes the total area and volume in
+the summary ('<a href="qh-opto.htm#s">s</a>'). Option '<a href="#FS">FS</a>' also includes the total area and volume.
+If facets are
+merged, the area and volume are approximations. Option 'FA' is
+automatically set for options '<a href="#Fa">Fa</a>', '<A
+ href="qh-optp.htm#PAn" >PAn</a>', and '<a href="qh-optp.htm#PFn">PFn</a>'.
+</p>
+
+<p>With '<a href="qdelaun.htm">qdelaunay</a> <A
+ href="qh-opto.htm#s" >s</a> FA', Qhull computes the total area of
+the Delaunay triangulation. This equals the volume of the convex
+hull of the data points. With options '<a href="qdelau_f.htm">qdelaunay Qu</a>
+<a href="qh-opto.htm#s">s</a> FA', Qhull computes the
+total area of the furthest-site Delaunay triangulation. This
+equals of the total area of the Delaunay triangulation. </p>
+
+<p>See '<a href="#Fa">Fa</a>' for further details. Option '<a href="#FS">FS</a>' also computes the total area and volume.</p>
+
+<h3><a href="#format">&#187;</a><a name="Fc">Fc - print coplanar
+points for each facet </a></h3>
+
+<p>The output starts with the number of facets. Then each facet
+is printed one per line. Each line is the number of coplanar
+points followed by the point ids. </p>
+
+<p>By default, option 'Fc' reports coplanar points
+('<a href="qh-optq.htm#Qc">Qc</a>'). You may also use
+option '<a href="qh-optq.htm#Qi">Qi</a>'. Options 'Qi Fc' prints
+interior points while 'Qci Fc' prints both coplanar and interior
+points.
+
+<p>Each coplanar point or interior point is assigned to the
+facet it is furthest above (resp., least below). </p>
+
+<p>Use 'Qc <a href="qh-opto.htm#p">p</a>' to print vertex and
+coplanar point coordinates. Use '<a href="qh-optf.htm#Fv">Fv</a>'
+to print vertices. </p>
+
+<h3><a href="#format">&#187;</a><a name="FC">FC - print centrum or
+Voronoi vertex for each facet </a></h3>
+
+<p>The output starts with the dimension followed by the number of
+facets. Then each facet centrum is printed, one per line. For
+<a href="qvoronoi.htm">qvoronoi</a>, Voronoi vertices are
+printed instead. </p>
+
+<h3><a href="#format">&#187;</a><a name="Fd">Fd - use cdd format for
+input </a></h3>
+
+<p>The input starts with comments. The first comment is reported
+in the summary. Data starts after a "begin" line. The
+next line is the number of points followed by the dimension plus
+one and "real" or "integer". Then the points
+are listed with a leading "1" or "1.0". The
+data ends with an "end" line.</p>
+
+<p>For halfspaces ('<a href="qhalf.htm">qhalf</a> Fd'),
+the input format is the same. Each halfspace starts with its
+offset. The signs of the offset and coefficients are the
+opposite of Qhull's
+convention. The first two lines of the input may be an interior
+point in '<a href="#FV">FV</a>' format.</p>
+
+<h3><a href="#format">&#187;</a><a name="FD">FD - use cdd format for
+normals </a></h3>
+
+<p>Option 'FD' prints normals ('<a href="qh-opto.htm#n">n</a>', '<A
+ href="#Fo" >Fo</a>', '<a href="#Fi">Fi</a>') or points ('<A
+ href="qh-opto.htm#p" >p</a>') in cdd format. The first line is the
+command line that invoked Qhull. Data starts with a
+"begin" line. The next line is the number of normals or
+points followed by the dimension plus one and "real".
+Then the normals or points are listed with the offset before the
+coefficients. The offset for points is 1.0. For normals,
+the offset and coefficients use the opposite sign from Qhull.
+The data ends with an "end" line.</p>
+
+<h3><a href="#format">&#187;</a><a name="FF">FF - print facets w/o
+ridges </a></h3>
+
+<p>Option 'FF' prints all fields of all facets (as in '<A
+ href="qh-opto.htm#f" >f</a>') without printing the ridges. This is
+useful in higher dimensions where a facet may have many ridges.
+For simplicial facets, options 'FF' and '<a href="qh-opto.htm#f">f
+</a>' are equivalent.</p>
+
+<h3><a href="#format">&#187;</a><a name="Fi">Fi - print inner planes
+for each facet </a></h3>
+
+<p>The first line is the dimension plus one. The second line is
+the number of facets. The remainder is one inner plane per line.
+The format is the same as option '<a href="qh-opto.htm#n">n</a>'.</p>
+
+<p>The inner plane is a plane that is below the facet's vertices.
+It is an offset from the facet's hyperplane. It includes a
+roundoff error for computing the vertex distance.</p>
+
+<p>Note that the inner planes for Geomview output ('<A
+ href="qh-optg.htm#Gi" >Gi</a>') include an additional offset for
+vertex visualization and roundoff error. </p>
+
+<h3><a href="#format">&#187;</a><a name="Fi2">Fi - print separating
+hyperplanes for inner, bounded Voronoi regions</a></h3>
+
+<p>With <a href="qvoronoi.htm" >qvoronoi</a>, 'Fi' prints the
+separating hyperplanes for inner, bounded regions of the Voronoi
+diagram. The first line is the number of ridges. Then each
+hyperplane is printed, one per line. A line starts with the
+number of indices and floats. The first pair of indices indicates
+an adjacent pair of input sites. The next <i>d</i> floats are the
+normalized coefficients for the hyperplane, and the last float is
+the offset. The hyperplane is oriented toward '<A
+ href="qh-optq.htm#QVn" >QVn</a>' (if defined), or the first input
+site of the pair. </p>
+
+<p>Use '<a href="qh-optf.htm#Fo2">Fo</a>' for unbounded regions,
+and '<a href="qh-optf.htm#Fv2">Fv</a>' for the corresponding
+Voronoi vertices. </p>
+
+<p>Use '<a href="qh-optt.htm#Tv">Tv</a>' to verify that the
+hyperplanes are perpendicular bisectors. It will list relevant
+statistics to stderr. The hyperplane is a perpendicular bisector
+if the midpoint of the input sites lies on the plane, all Voronoi
+vertices in the ridge lie on the plane, and the angle between the
+input sites and the plane is ninety degrees. This is true if all
+statistics are zero. Roundoff and computation errors make these
+non-zero. The deviations appear to be largest when the
+corresponding Delaunay triangles are large and thin; for example,
+the Voronoi diagram of nearly cospherical points. </p>
+
+<h3><a href="#format">&#187;</a><a name="FI">FI - print ID for each
+facet </a></h3>
+
+<p>Print facet identifiers. These are used internally and listed
+with options '<a href="qh-opto.htm#f">f</a>' and '<a href="#FF">FF</a>'.
+Options '<a href="#Fn">Fn </a>' and '<a href="#FN">FN</a>' use
+facet identifiers for negative indices. </p>
+
+<h3><a href="#format">&#187;</a><a name="Fm">Fm - print merge count
+for each facet </a></h3>
+
+<p>The first line is the number of facets. The remainder is the
+number of merges for each facet, one per line. At most 511 merges
+are reported for a facet. See '<a href="qh-optp.htm#PMn">PMn</a>'
+for printing the facets with the most merges. </p>
+
+<h3><a href="#format">&#187;</a><a name="FM">FM - print Maple
+output </a></h3>
+
+<p>Qhull writes a Maple file for 2-d and 3-d convex hulls,
+2-d and 3-d halfspace intersections,
+and 2-d Delaunay triangulations. Qhull produces a 2-d
+or 3-d plot.
+
+<p><i>Warning</i>: This option has not been tested in Maple.
+
+<p>[From T. K. Abraham with help from M. R. Feinberg and N. Platinova.]
+The following steps apply while working within the
+Maple worksheet environment :
+<ol>
+<li>Generate the data and store it as an array . For example, in 3-d, data generated
+in Maple is of the form : x[i],y[i],z[i]
+<p>
+<li>Create a single variable and assign the entire array of data points to this variable.
+Use the "seq" command within square brackets as shown in the following example.
+(The square brackets are essential for the rest of the steps to work.)
+<p>
+>data:=[seq([x[i],y[i],z[i]],i=1..n)]:# here n is the number of data points
+
+<li>Next we need to write the data to a file to be read by qhull. Before
+writing the data to a file, make sure that the qhull executable files and
+the data file lie in the same subdirectory. If the executable files are
+stored in the "C:\qhull3.1\" subdirectory, then save the file in the same
+subdirectory, say "C:\qhull3.1\datafile.txt". For the sake of integrity of
+the data file , it is best to first ensure that the data file does not
+exist before writing into the data file. This can be done by running a
+delete command first . To write the data to the file, use the "writedata"
+and the "writedata[APPEND]" commands as illustrated in the following example :
+<p>
+>system("del c:\\qhull3.1\\datafile.txt");#To erase any previous versions of the file
+<br>>writedata("c:\\qhull3.1\\datafile.txt ",[3, nops(data)]);#writing in qhull format
+<br>>writedata[APPEND]("c:\\ qhull3.1\\datafile.txt ", data);#writing the data points
+<li>
+Use the 'FM' option to produce Maple output. Store the output as a ".mpl" file.
+For example, using the file we created above, we type the following (in DOS environment)
+<p>
+qconvex s FM &lt;datafile.txt >dataplot.mpl
+
+<li>
+To read 3-d output in Maple, we use the 'read' command followed by
+a 'display3d' command. For example (in Maple environment):
+<p>
+>with (plots):
+<br>>read `c:\\qhull3.1\\dataplot.mpl`:#IMPORTANT - Note that the punctuation mark used is ' and NOT '. The correct punctuation mark is the one next to the key for "1" (not the punctuation mark near the enter key)
+<br>> qhullplot:=%:
+<br>> display3d(qhullplot);
+</ol>
+
+<p>For Delaunay triangulation orthogonal projection is better.
+
+<p>For halfspace intersections, Qhull produces the dual
+convex hull.
+
+<p>See <a href="qh-faq.htm#math">Is Qhull available for Maple?</a>
+for other URLs.
+
+<h3><a href="#format">&#187;</a><a name="Fn">Fn - print neighboring
+facets for each facet </a></h3>
+
+<p>The output starts with the number of facets. Then each facet
+is printed one per line. Each line is the number of neighbors
+followed by an index for each neighbor. The indices match the
+other facet output formats.</p>
+
+<p>For simplicial facets, each neighbor is opposite
+the corresponding vertex (option '<a href="#Fv">Fv</a>').
+Do not compare to option '<a href="qh-opto.htm#i">i</a>'. Option 'i'
+orients facets by reversing the order of two vertices. For non-simplicial facets,
+the neighbors are unordered.
+
+<p>A negative index indicates an unprinted facet due to printing
+only good facets ('<a href="qh-optp.htm#Pg">Pg</a>', <a href="qdelaun.htm" >qdelaunay</a>,
+<a href="qvoronoi.htm" >qvoronoi</a>). It
+is the negation of the facet's ID (option '<a href="#FI">FI</a>').
+For example, negative indices are used for facets "at
+infinity" in the Delaunay triangulation.</p>
+
+<h3><a href="#format">&#187;</a><a name="FN">FN - print neighboring
+facets for each point </a></h3>
+
+<p>The first line is the number of points. Then each point is
+printed, one per line. For unassigned points (either interior or
+coplanar), the line is "0". For assigned coplanar
+points ('<a href="qh-optq.htm#Qc">Qc</a>'), the line is
+"1" followed by the index of the facet that is furthest
+below the point. For assigned interior points ('<A
+ href="qh-optq.htm#Qi" >Qi</a>'), the line is "1"
+followed by the index of the facet that is least above the point.
+For vertices that do not belong to good facet, the line is
+"0"</p>
+
+<p>For vertices of good facets, the line is the number of
+neighboring facets followed by the facet indices. The indices
+correspond to the other '<a href="#format">F</a>' formats. In 4-d
+and higher, the facets are sorted by index. In 3-d, the facets
+are in adjacency order (not oriented).</p>
+
+<p>A negative index indicates an unprinted facet due to printing
+only good facets (<a href="qdelaun.htm" >qdelaunay</a>,
+<a href="qvoronoi.htm" >qvoronoi</a>, '<a href="qh-optp.htm#Pdk">Pdk</a>',
+'<a href="qh-optp.htm#Pg">Pg</a>'). It is the negation of the
+facet's ID ('<a href="#FI"> FI</a>'). For example, negative
+indices are used for facets "at infinity" in the
+Delaunay triangulation.</p>
+
+<p>For Voronoi vertices, option 'FN' lists the vertices of the
+Voronoi region for each input site. Option 'FN' lists the regions
+in site ID order. Option 'FN' corresponds to the second half of
+option '<a href="qh-opto.htm#o">o</a>'. To convert from 'FN' to '<A
+ href="qh-opto.htm#o" >o</a>', replace negative indices with zero
+and increment non-negative indices by one. </p>
+
+<p>If you are using the <a href="qh-code.htm#library">Qhull
+library</a> or <a href="qh-code.htm#cpp">C++ interface</a>, option 'FN' has the side effect of reordering the
+neighbors for a vertex </p>
+
+<h3><a href="#format">&#187;</a><a name="Fo">Fo - print outer planes
+for each facet </a></h3>
+
+<p>The first line is the dimension plus one. The second line is
+the number of facets. The remainder is one outer plane per line.
+The format is the same as option '<a href="qh-opto.htm#n">n</a>'.</p>
+
+<p>The outer plane is a plane that is above all points. It is an
+offset from the facet's hyperplane. It includes a roundoff error
+for computing the point distance. When testing the outer plane
+(e.g., '<a href="qh-optt.htm#Tv">Tv</a>'), another roundoff error
+should be added for the tested point.</p>
+
+<p>If outer planes are not checked ('<a href="qh-optq.htm#Q5">Q5</a>')
+or not computed (!qh_MAXoutside), the maximum, computed outside
+distance is used instead. This can be much larger than the actual
+outer planes.</p>
+
+<p>Note that the outer planes for Geomview output ('<A
+ href="qh-optg.htm#G" >G</a>') include an additional offset for
+vertex/point visualization, 'lines closer,' and roundoff error.</p>
+
+<h3><a href="#format">&#187;</a><a name="Fo2">Fo - print separating
+hyperplanes for outer, unbounded Voronoi regions</a></h3>
+
+<p>With <a href="qvoronoi.htm" >qvoronoi</a>, 'Fo' prints the
+separating hyperplanes for outer, unbounded regions of the
+Voronoi diagram. The first line is the number of ridges. Then
+each hyperplane is printed, one per line. A line starts with the
+number of indices and floats. The first pair of indices indicates
+an adjacent pair of input sites. The next <i>d</i> floats are the
+normalized coefficients for the hyperplane, and the last float is
+the offset. The hyperplane is oriented toward '<A
+ href="qh-optq.htm#QVn" >QVn</a>' (if defined), or the first input
+site of the pair. </p>
+
+<p>Option 'Fo' gives the hyperplanes for the unbounded rays of
+the unbounded regions of the Voronoi diagram. Each hyperplane
+goes through the midpoint of the corresponding input sites. The
+rays are directed away from the input sites. </p>
+
+<p>Use '<a href="qh-optf.htm#Fi2">Fi</a>' for bounded regions,
+and '<a href="qh-optf.htm#Fv2">Fv</a>' for the corresponding
+Voronoi vertices. Use '<a href="qh-optt.htm#Tv">Tv</a>' to verify
+that the corresponding Voronoi vertices lie on the hyperplane. </p>
+
+<h3><a href="#format">&#187;</a><a name="FO">FO - print list of
+selected options </a></h3>
+
+<p>Lists selected options and default values to stderr.
+Additional 'FO's are printed to stdout. </p>
+
+<h3><a href="#format">&#187;</a><a name="Fp">Fp - print points at
+halfspace intersections</a></h3>
+
+<p>The first line is the number of intersection points. The
+remainder is one intersection point per line. A intersection
+point is the intersection of <i>d</i> or more halfspaces from
+'<a href="qhalf.htm">qhalf</a>'. It corresponds to a
+facet of the dual polytope. The "infinity" point
+[-10.101,-10.101,...] indicates an unbounded intersection.</p>
+
+<p>If [x,y,z] are the dual facet's normal coefficients and <i>b&lt;0</i>
+is its offset, the halfspace intersection occurs at
+[x/-b,y/-b,z/-b] plus the interior point. If <i>b&gt;=0</i>, the
+halfspace intersection is unbounded. </p>
+
+<h3><a href="#format">&#187;</a><a name="FP">FP - print nearest
+vertex for coplanar points </a></h3>
+
+<p>The output starts with the number of coplanar points. Then
+each coplanar point is printed one per line. Each line is the
+point ID of the closest vertex, the point ID of the coplanar
+point, the corresponding facet ID, and the distance. Sort the
+lines to list the coplanar points nearest to each vertex. </p>
+
+<p>Use options '<a href="qh-optq.htm#Qc">Qc</a>' and/or '<A
+ href="qh-optq.htm#Qi" >Qi</a>' with 'FP'. Options 'Qc FP' prints
+coplanar points while 'Qci FP' prints coplanar and interior
+points. Option 'Qc' is automatically selected if 'Qi' is not
+selected.
+
+<p>For Delaunay triangulations (<a href="qdelaun.htm" >qdelaunay</a>
+or <a href="qvoronoi.htm" >qvoronoi</a>), a coplanar point is nearly
+incident to a vertex. The distance is the distance in the
+original point set.</p>
+
+<p>If imprecision problems are severe, Qhull will delete input
+sites when constructing the Delaunay triangulation. Option 'FP' will
+list these points along with coincident points.</p>
+
+<p>If there are many coplanar or coincident points and non-simplicial
+facets are triangulated ('<a href="qh-optq.htm#Qt">Qt</a>'), option
+'FP' may be inefficient. It redetermines the original vertex set
+for each coplanar point.</p>
+
+<h3><a href="#format">&#187;</a><a name="FQ">FQ - print command for
+qhull and input </a></h3>
+
+<p>Prints qhull and input command, e.g., "rbox 10 s | qhull
+FQ". Option 'FQ' may be repeated multiple times.</p>
+
+<h3><a href="#format">&#187;</a><a name="Fs">Fs - print summary</a></h3>
+
+<p>The first line consists of number of integers ("10")
+followed by the:
+<ul>
+<li>dimension
+<li>number of points
+<li>number of vertices
+<li>number of facets
+<li>number of vertices selected for output
+<li>number of facets selected for output
+<li>number of coplanar points for selected facets
+<li>number of nonsimplicial or merged facets selected for
+ output
+<LI>number of deleted vertices</LI>
+<LI>number of triangulated facets ('<a href="qh-optq.htm#Qt">Qt</a>')</LI>
+</ul>
+
+<p>The second line consists of the number of reals
+("2") followed by the:
+<ul>
+<li>maximum offset to an outer plane
+<li>minimum offset to an inner plane.</li>
+</ul>
+Roundoff and joggle are included.
+<P></P>
+
+<p>For Delaunay triangulations and Voronoi diagrams, the
+number of deleted vertices should be zero. If greater than zero, then the
+input is highly degenerate and coplanar points are not necessarily coincident
+points. For example, <tt>'RBOX 1000 s W1e-13 t995138628 | QHULL d Qbb'</tt> reports
+deleted vertices; the input is nearly cospherical.</p>
+
+<P>Later versions of Qhull may produce additional integers or reals.</P>
+
+<h3><a href="#format">&#187;</a><a name="FS">FS - print sizes</a></h3>
+
+<p>The first line consists of the number of integers
+("0"). The second line consists of the number of reals
+("2"), followed by the total facet area, and the total
+volume. Later versions of Qhull may produce additional integers
+or reals.</p>
+
+<p>The total volume measures the volume of the intersection of
+the halfspaces defined by each facet. It is computed from the
+facet area. Both area and volume are approximations for
+non-simplicial facets. See option '<a href="#Fa">Fa </a>' for
+further notes. Option '<a href="#FA">FA </a>' also computes the total area and volume. </p>
+
+<h3><a href="#format">&#187;</a><a name="Ft">Ft - print triangulation</a></h3>
+
+<p>Prints a triangulation with added points for non-simplicial
+facets. The output is </p>
+
+<ul>
+ <li>The first line is the dimension
+ <li>The second line is the number of points, the number
+ of facets, and the number of ridges.
+ <li>All of the input points follow, one per line.
+ <li>The centrums follow, one per non-simplicial facet
+ <li>Then the facets follow as a list of point indices
+ preceded by the number of points. The simplices are
+ oriented. </li>
+</ul>
+
+<p>For convex hulls with simplicial facets, the output is the
+same as option '<a href="qh-opto.htm#o">o</a>'.</p>
+
+<p>The added points are the centrums of the non-simplicial
+facets. Except for large facets, the centrum is the average
+vertex coordinate projected to the facet's hyperplane. Large
+facets may use an old centrum to avoid recomputing the centrum
+after each merge. In either case, the centrum is clearly below
+neighboring facets. See <a href="qh-impre.htm">Precision issues</a>.
+</p>
+
+<p>The new simplices will not be clearly convex with their
+neighbors and they will not satisfy the Delaunay property. They
+may even have a flipped orientation. Use triangulated input ('<A
+ href="qh-optq.htm#Qt">Qt</a>') for Delaunay triangulations.
+
+<p>For Delaunay triangulations with simplicial facets, the output is the
+same as option '<a href="qh-opto.htm#o">o</a>' without the lifted
+coordinate. Since 'Ft' is invalid for merged Delaunay facets, option
+'Ft' is not available for qdelaunay or qvoronoi. It may be used with
+joggled input ('<a href="qh-optq.htm#QJn" >QJ</a>') or triangulated output ('<A
+ href="qh-optq.htm#Qt" >Qt</a>'), for example, rbox 10 c G 0.01 | qhull d QJ Ft</p>
+
+<p>If you add a point-at-infinity with '<a href="qh-optq.htm#Qz">Qz</a>',
+it is printed after the input sites and before any centrums. It
+will not be used in a Delaunay facet.</p>
+
+<h3><a href="#format">&#187;</a><a name="Fv">Fv - print vertices for
+each facet</a></h3>
+
+<p>The first line is the number of facets. Then each facet is
+printed, one per line. Each line is the number of vertices
+followed by the corresponding point ids. Vertices are listed in
+the order they were added to the hull (the last one added is the
+first listed).
+</p>
+<p>Option '<a href="qh-opto.htm#i">i</a>' also lists the vertices,
+but it orients facets by reversing the order of two
+vertices. Option 'i' triangulates non-simplicial, 4-d and higher facets by
+adding vertices for the centrums.
+</p>
+
+<h3><a href="#format">&#187;</a><a name="Fv2">Fv - print Voronoi
+diagram</a></h3>
+
+<p>With <a href="qvoronoi.htm" >qvoronoi</a>, 'Fv' prints the
+Voronoi diagram or furthest-site Voronoi diagram. The first line
+is the number of ridges. Then each ridge is printed, one per
+line. The first number is the count of indices. The second pair
+of indices indicates a pair of input sites. The remaining indices
+list the corresponding ridge of Voronoi vertices. Vertex 0 is the
+vertex-at-infinity. It indicates an unbounded ray. </p>
+
+<p>All vertices of a ridge are coplanar. If the ridge is
+unbounded, add the midpoint of the pair of input sites. The
+unbounded ray is directed from the Voronoi vertices to infinity. </p>
+
+<p>Use '<a href="qh-optf.htm#Fo2">Fo</a>' for separating
+hyperplanes of outer, unbounded regions. Use '<A
+ href="qh-optf.htm#Fi2" >Fi</a>' for separating hyperplanes of
+inner, bounded regions. </p>
+
+<p>Option 'Fv' does not list ridges that require more than one
+midpoint. For example, the Voronoi diagram of cospherical points
+lists zero ridges (e.g., 'rbox 10 s | qvoronoi Fv Qz').
+Other examples are the Voronoi diagrams of a rectangular mesh
+(e.g., 'rbox 27 M1,0 | qvoronoi Fv') or a point set with
+a rectangular corner (e.g.,
+'rbox P4,4,4 P4,2,4 P2,4,4 P4,4,2 10 | qvoronoi Fv').
+Both cases miss unbounded rays at the corners.
+To determine these ridges, surround the points with a
+large cube (e.g., 'rbox 10 s c G2.0 | qvoronoi Fv Qz').
+The cube needs to be large enough to bound all Voronoi regions of the original point set.
+Please report any other cases that are missed. If you
+can formally describe these cases or
+write code to handle them, please send email to <A
+ href="mailto:qhull@qhull.org" >qhull@qhull.org</a>. </p>
+
+<h3><a href="#format">&#187;</a><a name="FV">FV - print average
+vertex </a></h3>
+
+<p>The average vertex is the average of all vertex coordinates.
+It is an interior point for halfspace intersection. The first
+line is the dimension and "1"; the second line is the
+coordinates. For example,</p>
+
+<blockquote>
+ <p>qconvex FV <A
+ href="qh-opto.htm#n">n</a> | qhalf <a href="#Fp">Fp</a></p>
+</blockquote>
+
+<p>prints the extreme points of the original point set (roundoff
+included). </p>
+
+<h3><a href="#format">&#187;</a><a name="Fx">Fx - print extreme
+points (vertices) of convex hulls and Delaunay triangulations</a></h3>
+
+<p>The first line is the number of points. The following lines
+give the index of the corresponding points. The first point is
+'0'. </p>
+
+<p>In 2-d, the extreme points (vertices) are listed in
+counterclockwise order (by qh_ORIENTclock in user.h). </p>
+
+<p>In 3-d and higher convex hulls, the extreme points (vertices)
+are sorted by index. This is the same order as option '<A
+ href="qh-opto.htm#p" >p</a>' when it doesn't include coplanar or
+interior points. </p>
+
+<p>For Delaunay triangulations, 'Fx' lists the extreme
+points of the input sites (i.e., the vertices of their convex hull). The points
+are unordered. <!-- Navigation links --> </p>
+
+<hr>
+
+<p><b>Up:</b> <a href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b> <a href="index.htm#TOC">Qhull manual</a>: Table of Contents<br>
+<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
+&#149; <a href="qh-quick.htm#options">Options</a>
+&#149; <a href="qh-opto.htm#output">Output</a>
+&#149; <a href="qh-optf.htm#format">Formats</a>
+&#149; <a href="qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="qh-optp.htm#print">Print</a>
+&#149; <a href="qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="qh-optc.htm#prec">Precision</a>
+&#149; <a href="qh-optt.htm#trace">Trace</a>
+&#149; <a href="../src/libqhull_r/index.htm">Functions</a></p><!-- GC common information -->
+<hr>
+
+<p><a href="http://www.geom.uiuc.edu/"><IMG align=middle
+ height=40 src="qh--geom.gif" width=40 ></a><i>The Geometry Center
+Home Page </i></p>
+
+<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a>
+</a><br>
+Created:
+Sept. 25, 1995 --- <!-- hhmts start -->Last modified: see top
+<!-- hhmts end --> </p>
+</body>
+</html>
diff --git a/xs/src/qhull/html/qh-optg.htm b/xs/src/qhull/html/qh-optg.htm
new file mode 100644
index 000000000..a56e29df1
--- /dev/null
+++ b/xs/src/qhull/html/qh-optg.htm
@@ -0,0 +1,274 @@
+<html>
+
+<head>
+<title>Qhull Geomview options (G)</title>
+</head>
+
+<body>
+<!-- Navigation links -->
+
+<p><b>Up:</b> <a href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b> <a href="index.htm#TOC">Qhull manual</a>: Table of Contents<br>
+<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
+&#149; <a href="qh-quick.htm#options">Options</a>
+&#149; <a href="qh-opto.htm#output">Output</a>
+&#149; <a href="qh-optf.htm#format">Formats</a>
+&#149; <a href="qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="qh-optp.htm#print">Print</a>
+&#149; <a href="qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="qh-optc.htm#prec">Precision</a>
+&#149; <a href="qh-optt.htm#trace">Trace</a>
+&#149; <a href="../src/libqhull_r/index.htm">Functions</a></p>
+
+<hr>
+<!-- Main text of document -->
+
+<h1><a
+href="http://www.geom.uiuc.edu/graphics/pix/Special_Topics/Computational_Geometry/delaunay.html"><img
+src="qh--dt.gif" alt="[delaunay]" align="middle" width="100"
+height="100"></a> Qhull Geomview options (G)</h1>
+
+This section lists the Geomview options for Qhull. These options are
+indicated by 'G' followed by a letter. See
+<a href="qh-opto.htm#output">Output</a>, <a href="qh-optp.htm#print">Print</a>,
+and <a href="qh-optf.htm#format">Format</a> for other output options.
+
+
+<p><b>Copyright &copy; 1995-2015 C.B. Barber</b></p>
+
+<hr>
+
+<p><a href="index.htm#TOC">&#187;</a> <a href="qh-quick.htm#programs">Programs</a>
+<a name="geomview">&#149;</a> <a href="qh-quick.htm#options">Options</a>
+&#149; <a href="qh-opto.htm#output">Output</a>
+&#149; <a href="qh-optf.htm#format">Formats</a>
+&#149; <a href="qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="qh-optp.htm#print">Print</a>
+&#149; <a href="qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="qh-optc.htm#prec">Precision</a>
+&#149; <a href="qh-optt.htm#trace">Trace</a>
+&#149; <a href="../src/libqhull_r/index.htm">Functions</a></p>
+
+<h2>Geomview output options</h2>
+
+<p><a href="http://www.geomview.org">Geomview</a> is the graphical
+viewer for visualizing Qhull output in 2-d, 3-d and 4-d.</p>
+
+<p>Geomview displays each facet of the convex hull. The color of
+a facet is determined by the coefficients of the facet's normal
+equation. For imprecise hulls, Geomview displays the inner and
+outer hull. Geomview can also display points, ridges, vertices,
+coplanar points, and facet intersections. </p>
+
+<p>For 2-d Delaunay triangulations, Geomview displays the
+corresponding paraboloid. Geomview displays the 2-d Voronoi
+diagram. For halfspace intersections, it displays the
+dual convex hull. </p>
+
+<dl compact>
+ <dt>&nbsp;</dt>
+ <dd><b>General</b></dd>
+ <dt><a href="#G">G</a></dt>
+ <dd>display Geomview output</dd>
+ <dt><a href="#Gt">Gt</a></dt>
+ <dd>display transparent 3-d Delaunay triangulation</dd>
+ <dt><a href="#GDn">GDn</a></dt>
+ <dd>drop dimension n in 3-d and 4-d output </dd>
+
+ <dt>&nbsp;</dt>
+ <dt>&nbsp;</dt>
+ <dd><b>Specific</b></dd>
+ <dt><a href="#Ga">Ga</a></dt>
+ <dd>display all points as dots</dd>
+ <dt><a href="#Gc">Gc</a></dt>
+ <dd>display centrums (2-d, 3-d)</dd>
+ <dt><a href="#Gp">Gp</a></dt>
+ <dd>display coplanar points and vertices as radii</dd>
+ <dt><a href="#Gh">Gh</a></dt>
+ <dd>display hyperplane intersections</dd>
+ <dt><a href="#Gi">Gi</a></dt>
+ <dd>display inner planes only (2-d, 3-d)</dd>
+ <dt><a href="#Go">Go</a></dt>
+ <dd>display outer planes only (2-d, 3-d)</dd>
+ <dt><a href="#Gr">Gr</a></dt>
+ <dd>display ridges (3-d)</dd>
+ <dt><a href="#Gv">Gv</a></dt>
+ <dd>display vertices as spheres</dd>
+ <dt><a href="#Gn">Gn</a></dt>
+ <dd>do not display planes</dd>
+
+</dl>
+
+<hr>
+
+<h3><a href="#geomview">&#187;</a><a name="G">G - produce output for
+viewing with Geomview</a></h3>
+
+<p>By default, option 'G' displays edges in 2-d, outer planes in
+3-d, and ridges in 4-d.</p>
+
+<p>A ridge can be explicit or implicit. An explicit ridge is a <i>(d-1)</i>-dimensional
+simplex between two facets. In 4-d, the explicit ridges are
+triangles. An implicit ridge is the topological intersection of
+two neighboring facets. It is the union of explicit ridges.</p>
+
+<p>For non-simplicial 4-d facets, the explicit ridges can be
+quite complex. When displaying a ridge in 4-d, Qhull projects the
+ridge's vertices to one of its facets' hyperplanes. Use '<a
+href="#Gh">Gh</a>' to project ridges to the intersection of both
+hyperplanes. This usually results in a cleaner display. </p>
+
+<p>For 2-d Delaunay triangulations, Geomview displays the
+corresponding paraboloid. Geomview displays the 2-d Voronoi
+diagram. For halfspace intersections, it displays the
+dual convex hull.
+
+<h3><a href="#geomview">&#187;</a><a name="Ga">Ga - display all
+points as dots </a></h3>
+
+<p>Each input point is displayed as a green dot.</p>
+
+<h3><a href="#geomview">&#187;</a><a name="Gc">Gc - display centrums
+(3-d) </a></h3>
+
+<p>The centrum is defined by a green radius sitting on a blue
+plane. The plane corresponds to the facet's hyperplane. If you
+sight along a facet's hyperplane, you will see that all
+neighboring centrums are below the facet. The radius is defined
+by '<a href="qh-optc.htm#Cn">C-n</a>' or '<a
+href="qh-optc.htm#Cn2">Cn</a>'.</p>
+
+<h3><a href="#geomview">&#187;</a><a name="GDn">GDn - drop dimension
+n in 3-d and 4-d output </a></h3>
+
+<p>The result is a 2-d or 3-d object. In 4-d, this corresponds to
+viewing the 4-d object from the nth axis without perspective.
+It's best to view 4-d objects in pieces. Use the '<a
+href="qh-optp.htm#Pdk">Pdk</a>' '<a href="qh-optp.htm#Pg">Pg</a>'
+'<a href="qh-optp.htm#PG">PG</a>' '<a href="qh-optq.htm#QGn">QGn</a>'
+and '<a href="qh-optq.htm#QVn">QVn</a>' options to select a few
+facets. If one of the facets is perpendicular to an axis, then
+projecting along that axis will show the facet exactly as it is
+in 4-d. If you generate many facets, use Geomview's <tt>ginsu</tt>
+module to view the interior</p>
+
+<p>To view multiple 4-d dimensions at once, output the object
+without 'GDn' and read it with Geomview's <tt>ndview</tt>. As you
+rotate the object in one set of dimensions, you can see how it
+changes in other sets of dimensions.</p>
+
+<p>For additional control over 4-d objects, output the object
+without 'GDn' and read it with Geomview's <tt>4dview</tt>. You
+can slice the object along any 4-d plane. You can also flip the
+halfspace that's deleted when slicing. By combining these
+features, you can get some interesting cross sections.</p>
+
+<h3><a href="#geomview">&#187;</a><a name="Gh">Gh - display
+hyperplane intersections (3-d, 4-d)</a></h3>
+
+<p>In 3-d, the intersection is a black line. It lies on two
+neighboring hyperplanes, c.f., the blue squares associated with
+centrums ('<a href="#Gc">Gc </a>'). In 4-d, the ridges are
+projected to the intersection of both hyperplanes. If you turn on
+edges (Geomview's 'appearances' menu), each triangle corresponds
+to one ridge. The ridges may overlap each other.</p>
+
+<h3><a href="#geomview">&#187;</a><a name="Gi">Gi - display inner
+planes only (2-d, 3-d)</a></h3>
+
+<p>The inner plane of a facet is below all of its vertices. It is
+parallel to the facet's hyperplane. The inner plane's color is
+the opposite of the outer plane's color, i.e., <i>[1-r,1-g,1-b] </i>.
+Its edges are determined by the vertices.</p>
+
+<h3><a href="#geomview">&#187;</a><a name="Gn">Gn - do not display
+planes </a></h3>
+
+<p>By default, Geomview displays the precise plane (no merging)
+or both inner and output planes (if merging). If merging,
+Geomview does not display the inner plane if the the difference
+between inner and outer is too small.</p>
+
+<h3><a href="#geomview">&#187;</a><a name="Go">Go - display outer
+planes only (2-d, 3-d)</a></h3>
+
+<p>The outer plane of a facet is above all input points. It is
+parallel to the facet's hyperplane. Its color is determined by
+the facet's normal, and its edges are determined by the vertices.</p>
+
+<h3><a href="#geomview">&#187;</a><a name="Gp">Gp - display coplanar
+points and vertices as radii </a></h3>
+
+<p>Coplanar points ('<a href="qh-optq.htm#Qc">Qc</a>'), interior
+points ('<a href="qh-optq.htm#Qi">Qi</a>'), outside points ('<a
+href="qh-optt.htm#TCn">TCn</a>' or '<a href="qh-optt.htm#TVn">TVn</a>'),
+and vertices are displayed as red and yellow radii. The radii are
+perpendicular to the corresponding facet. Vertices are aligned
+with an interior point. The radii define a ball which corresponds
+to the imprecision of the point. The imprecision is the maximum
+of the roundoff error, the centrum radius, and <i>maxcoord * (1 -
+</i><a href="qh-optc.htm#An"><i>A-n</i></a><i>)</i>. It is at
+least 1/20'th of the maximum coordinate, and ignores post merging
+if pre-merging is done.</p>
+
+<p>If '<a href="qh-optg.htm#Gv">Gv</a>' (print vertices as
+spheres) is also selected, option 'Gp' displays coplanar
+points as radii. Select options <a href="qh-optq.htm#Qc">Qc</a>'
+and/or '<a href="qh-optq.htm#Qi">Qi</a>'. Options 'Qc Gpv' displays
+coplanar points while 'Qci Gpv' displays coplanar and interior
+points. Option 'Qc' is automatically selected if 'Qi' is not
+selected with options 'Gpv'.
+
+<h3><a href="#geomview">&#187;</a><a name="Gr">Gr - display ridges
+(3-d) </a></h3>
+
+<p>A ridge connects the two vertices that are shared by
+neighboring facets. It is displayed in green. A ridge is the
+topological edge between two facets while the hyperplane
+intersection is the geometric edge between two facets. Ridges are
+always displayed in 4-d.</p>
+
+<h3><a href="#geomview">&#187;</a><a name="Gt">Gt - transparent 3-d
+Delaunay </a></h3>
+
+<p>A 3-d Delaunay triangulation looks like a convex hull with
+interior facets. Option 'Gt' removes the outside ridges to reveal
+the outermost facets. It automatically sets options '<a
+href="#Gr">Gr</a>' and '<a href="#GDn">GDn</a>'. See example <a
+href="qh-eg.htm#17f">eg.17f.delaunay.3</a>.</p>
+
+<h3><a href="#geomview">&#187;</a><a name="Gv">Gv - display vertices
+as spheres (2-d, 3-d)</a></h3>
+
+<p>The radius of the sphere corresponds to the imprecision of the
+data. See '<a href="#Gp">Gp</a>' for determining the radius.</p>
+
+<!-- Navigation links -->
+
+<hr>
+
+<p><b>Up:</b> <a href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b> <a href="index.htm#TOC">Qhull manual</a>: Table of Contents<br>
+<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
+&#149; <a href="qh-quick.htm#options">Options</a>
+&#149; <a href="qh-opto.htm#output">Output</a>
+&#149; <a href="qh-optf.htm#format">Formats</a>
+&#149; <a href="qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="qh-optp.htm#print">Print</a>
+&#149; <a href="qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="qh-optc.htm#prec">Precision</a>
+&#149; <a href="qh-optt.htm#trace">Trace</a>
+&#149; <a href="../src/libqhull_r/index.htm">Functions</a></p>
+<!-- GC common information -->
+
+<hr>
+
+<p><a href="http://www.geom.uiuc.edu/"><img src="qh--geom.gif"
+align="middle" width="40" height="40"></a><i>The Geometry Center
+Home Page </i></p>
+
+<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a>
+</a><br>
+Created: Sept. 25, 1995 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p>
+</body>
+</html>
diff --git a/xs/src/qhull/html/qh-opto.htm b/xs/src/qhull/html/qh-opto.htm
new file mode 100644
index 000000000..e7b21745c
--- /dev/null
+++ b/xs/src/qhull/html/qh-opto.htm
@@ -0,0 +1,353 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+
+<head>
+<title>Qhull output options</title>
+</head>
+
+<body>
+<!-- Navigation links -->
+<p><b>Up:</b> <a href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b> <a href="index.htm#TOC">Qhull manual</a>: Table of Contents<br>
+<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
+&#149; <a href="qh-quick.htm#options">Options</a>
+&#149; <a href="qh-opto.htm#output">Output</a>
+&#149; <a href="qh-optf.htm#format">Formats</a>
+&#149; <a href="qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="qh-optp.htm#print">Print</a>
+&#149; <a href="qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="qh-optc.htm#prec">Precision</a>
+&#149; <a href="qh-optt.htm#trace">Trace</a>
+&#149; <a href="../src/libqhull_r/index.htm">Functions</a></p>
+
+<hr>
+<!-- Main text of document -->
+<h1><a
+href="http://www.geom.uiuc.edu/graphics/pix/Special_Topics/Computational_Geometry/delaunay.html"><img
+src="qh--dt.gif" alt="[delaunay]" align="middle" width="100"
+height="100"></a> Qhull output options</h1>
+
+<p>This section lists the output options for Qhull. These options
+are indicated by lower case characters. See <a
+href="qh-optf.htm#format">Formats</a>, <a
+href="qh-optp.htm#print">Print</a>, and <a
+href="qh-optg.htm#geomview">Geomview</a> for other output
+options. </p>
+
+<p><b>Copyright &copy; 1995-2015 C.B. Barber</b></p>
+
+<hr>
+
+<p><a href="index.htm#TOC">&#187;</a> <a href="qh-quick.htm#programs">Programs</a>
+<a name="output">&#149;</a> <a href="qh-quick.htm#options">Options</a>
+&#149; <a href="qh-opto.htm#output">Output</a>
+&#149; <a href="qh-optf.htm#format">Formats</a>
+&#149; <a href="qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="qh-optp.htm#print">Print</a>
+&#149; <a href="qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="qh-optc.htm#prec">Precision</a>
+&#149; <a href="qh-optt.htm#trace">Trace</a>
+&#149; <a href="../src/libqhull_r/index.htm">Functions</a></p>
+
+<h2>Output options</h2>
+
+<p>Qhull prints its output to standard out. All output is printed
+text. The default output is a summary (option '<a href="#s">s</a>').
+Other outputs may be specified as follows. </p>
+
+<dl compact>
+ <dt><a href="#f">f</a></dt>
+ <dd>print all fields of all facets</dd>
+ <dt><a href="#n">n</a></dt>
+ <dd>print hyperplane normals with offsets</dd>
+ <dt><a href="#m">m</a></dt>
+ <dd>print Mathematica output (2-d and 3-d)</dd>
+ <dt><a href="#o">o</a></dt>
+ <dd>print OFF file format (dim, points and facets)</dd>
+ <dt><a href="#s">s</a></dt>
+ <dd>print summary to stderr</dd>
+ <dt><a href="#p">p</a></dt>
+ <dd>print vertex and point coordinates</dd>
+ <dt><a href="#i">i</a></dt>
+ <dd>print vertices incident to each facet </dd>
+ <dt>&nbsp;</dt>
+ <dt>&nbsp;</dt>
+ <dd><b>Related options</b></dd>
+ <dt><a href="qh-optf.htm#format">F</a></dt>
+ <dd>additional input/output formats</dd>
+ <dt><a href="qh-optg.htm#geomview">G</a></dt>
+ <dd>Geomview output</dd>
+ <dt><a href="qh-optp.htm#print">P</a></dt>
+ <dd>Print options</dd>
+ <dt><a href="qh-optf.htm#Ft">Ft</a></dt>
+ <dd>print triangulation with added points</dd>
+ <dt>&nbsp;</dt>
+</dl>
+
+<hr>
+
+<h3><a href="#output">&#187;</a><a name="f">f - print all fields of
+all facets </a></h3>
+
+<p>Print <a href=../src/libqhull.h#facetT>all fields</a> of all facets.
+The facet is the primary <a href=index.htm#structure>data structure</a> for
+Qhull.
+
+<p>Option 'f' is for
+debugging. Most of the fields are available via the '<a
+href="qh-optf.htm#format">F</a>' options. If you need specialized
+information from Qhull, you can use the <a
+href="qh-code.htm#library">Qhull library</a> or <a
+href="qh-code.htm#cpp">C++ interface</a>.</p>
+
+<p>Use the '<a href="qh-optf.htm#FF">FF</a>' option to print the
+facets but not the ridges. </p>
+
+<h3><a href="#output">&#187;</a><a name="i">i - print vertices
+incident to each facet </a></h3>
+
+<p>The first line is the number of facets. The remaining lines
+list the vertices for each facet, one facet per line. The indices
+are 0-relative indices of the corresponding input points. The
+facets are oriented. Option '<a href="qh-optf.htm#Fv">Fv</a>'
+displays an unoriented list of vertices with a vertex count per
+line. Options '<a href="qh-opto.htm#o">o</a>' and '<a
+href="qh-optf.htm#Ft">Ft</a>' displays coordinates for each
+vertex prior to the vertices for each facet. </p>
+
+<p>Simplicial facets (e.g., triangles in 3-d) consist of <i>d</i>
+vertices. Non-simplicial facets in 3-d consist of 4 or more
+vertices. For example, a facet of a cube consists of 4 vertices.
+Use option '<a href="qh-optq.htm#Qt">Qt</a>' to triangulate non-simplicial facets.</p>
+
+<p>For 4-d and higher convex hulls and 3-d and higher Delaunay
+triangulations, <i>d</i> vertices are listed for all facets. A
+non-simplicial facet is triangulated with its centrum and each
+ridge. The index of the centrum is higher than any input point.
+Use option '<a href="qh-optf.htm#Fv">Fv</a>' to list the vertices
+of non-simplicial facets as is. Use option '<a
+href="qh-optf.htm#Ft">Ft</a>' to print the coordinates of the
+centrums as well as those of the input points. </p>
+
+<h3><a href="#output">&#187;</a><a name="m">m - print Mathematica
+output </a></h3>
+
+<p>Qhull writes a Mathematica file for 2-d and 3-d convex hulls,
+2-d and 3-d halfspace intersections,
+and 2-d Delaunay triangulations. Qhull produces a list of
+objects that you can assign to a variable in Mathematica, for
+example: &quot;<tt>list= &lt;&lt; &lt;outputfilename&gt; </tt>&quot;.
+If the object is 2-d, it can be visualized by &quot;<tt>Show[Graphics[list]]
+</tt>&quot;. For 3-d objects the command is &quot;<tt>Show[Graphics3D[list]]
+</tt>&quot;. Now the object can be manipulated by commands of the
+form <tt>&quot;Show[%, &lt;parametername&gt; -&gt;
+&lt;newvalue&gt;]</tt>&quot;. </p>
+
+<p>For Delaunay triangulation orthogonal projection is better.
+This can be specified, for example, by &quot;<tt>BoxRatios:
+Show[%, BoxRatios -&gt; {1, 1, 1e-8}]</tt>&quot;. To see the
+meaningful side of the 3-d object used to visualize 2-d Delaunay,
+you need to change the viewpoint: &quot;<tt>Show[%, ViewPoint
+-&gt; {0, 0, -1}]</tt>&quot;. By specifying different viewpoints
+you can slowly rotate objects. </p>
+
+<p>For halfspace intersections, Qhull produces the dual
+convex hull.
+
+<p>See <a href="qh-faq.htm#math">Is Qhull available for Mathematica?</a>
+for URLs.
+
+<h3><a href="#output">&#187;</a><a name="n">n - print hyperplane
+normals with offsets </a></h3>
+
+<p>The first line is the dimension plus one. The second line is
+the number of facets. The remaining lines are the normals for
+each facet, one normal per line. The facet's offset follows its
+normal coefficients.</p>
+
+<p>The normals point outward, i.e., the convex hull satisfies <i>Ax
+&lt;= -b </i>where <i>A</i> is the matrix of coefficients and <i>b</i>
+is the vector of offsets.</p>
+
+<p>A point is <i>inside</i> or <i>below</i> a hyperplane if its distance
+to the hyperplane is negative. A point is <i>outside</i> or <i>above</i> a hyperplane
+if its distance to the hyperplane is positive. Otherwise a point is <i>on</i> or
+<i>coplanar to</i> the hyperplane.
+
+<p>If cdd output is specified ('<a href="qh-optf.htm#FD">FD</a>'),
+Qhull prints the command line, the keyword &quot;begin&quot;, the
+number of facets, the dimension (plus one), the keyword
+&quot;real&quot;, and the normals for each facet. The facet's
+negative offset precedes its normal coefficients (i.e., if the
+origin is an interior point, the offset is positive). Qhull ends
+the output with the keyword &quot;end&quot;. </p>
+
+<h3><a href="#output">&#187;</a><a name="o">o - print OFF file format
+</a></h3>
+
+<p>The output is: </p>
+
+<ul>
+ <li>The first line is the dimension </li>
+ <li>The second line is the number of points, the number of
+ facets, and the number of ridges. </li>
+ <li>All of the input points follow, one per line. </li>
+ <li>Then Qhull prints the vertices for each facet. Each facet
+ is on a separate line. The first number is the number of
+ vertices. The remainder is the indices of the
+ corresponding points. The vertices are oriented in 2-d,
+ 3-d, and in simplicial facets. </li>
+</ul>
+
+<p>Option '<a href="qh-optf.htm#Ft">Ft</a>' prints the same
+information with added points for non-simplicial facets.</p>
+
+<p>Option '<a href="qh-opto.htm#i">i</a>' displays vertices
+without the point coordinates. Option '<a href="qh-opto.htm#p">p</a>'
+displays the point coordinates without vertex and facet information.</p>
+
+<p>In 3-d, Geomview can load the file directly if you delete the
+first line (e.g., by piping through '<tt>tail +2</tt>').</p>
+
+<p>For Voronoi diagrams (<a href=qvoronoi.htm>qvoronoi</a>), option
+'o' prints Voronoi vertices and Voronoi regions instead of input
+points and facets. The first vertex is the infinity vertex
+[-10.101, -10.101, ...]. Then, option 'o' lists the vertices in
+the Voronoi region for each input site. The regions appear in
+site ID order. In 2-d, the vertices of a Voronoi region are
+sorted by adjacency (non-oriented). In 3-d and higher, the
+Voronoi vertices are sorted by index. See the '<a
+href="qh-optf.htm#FN">FN</a>' option for listing Voronoi regions
+without listing Voronoi vertices.</p>
+
+<p>If you are using the Qhull library, options 'v o' have the
+side effect of reordering the neighbors for a vertex.</p>
+
+<h3><a href="#output">&#187;</a><a name="p">p - print vertex and
+point coordinates </a></h3>
+
+<p>The first line is the dimension. The second line is the number
+of vertices. The remaining lines are the vertices, one vertex per
+line. A vertex consists of its point coordinates</p>
+
+<p>With the '<a href="qh-optg.htm#Gc">Gc</a>' and '<a
+href="qh-optg.htm#Gi">Gi</a>' options, option 'p' also prints
+coplanar and interior points respectively.</p>
+
+<p>For <a href=qvoronoi.htm>qvoronoi</a>, it prints the
+coordinates of each Voronoi vertex.</p>
+
+<p>For <a href=qdelaun.htm>qdelaunay</a>, it prints the
+input sites as lifted to a paraboloid. For <a href=qhalf.htm>qhalf</a>
+it prints the dual points. For both, option 'p' is the same as the first
+section of option '<a href="qh-opto.htm#o">o</a>'.</p>
+
+<p>Use '<a href="qh-optf.htm#Fx">Fx</a>' to list the point ids of
+the extreme points (i.e., vertices). </p>
+
+<p>If a subset of the facets is selected ('<a
+href="qh-optp.htm#Pdk">Pdk</a>', '<a href="qh-optp.htm#PDk">PDk</a>',
+'<a href="qh-optp.htm#Pg">Pg</a>' options), option 'p' only
+prints vertices and points associated with those facets.</p>
+
+<p>If cdd-output format is selected ('<a href="qh-optf.htm#FD">FD</a>'),
+the first line is &quot;begin&quot;. The second line is the
+number of vertices, the dimension plus one, and &quot;real&quot;.
+The vertices follow with a leading &quot;1&quot;. Output ends
+with &quot;end&quot;. </p>
+
+<h3><a href="#output">&#187;</a><a name="s">s - print summary to
+stderr </a></h3>
+
+<p>The default output of Qhull is a summary to stderr. Options '<a
+href="qh-optf.htm#FS">FS</a>' and '<a href="qh-optf.htm#Fs">Fs</a>'
+produce the same information for programs. <b>Note</b>: Windows 95 and 98
+treats stderr the same as stdout. Use option '<a href="qh-optt.htm#TO">TO file</a>' to separate
+stderr and stdout.</p>
+
+<p>The summary lists the number of input points, the dimension,
+the number of vertices in the convex hull, and the number of
+facets in the convex hull. It lists the number of selected
+(&quot;good&quot;) facets for options '<a href="qh-optp.htm#Pg">Pg</a>',
+'<a href="qh-optp.htm#Pdk">Pdk</a>', <a href=qdelaun.htm>qdelaunay</a>,
+or <a href=qvoronoi.htm>qvoronoi</a> (Delaunay triangulations only
+use the lower half of a convex hull). It lists the number of
+coplanar points. For Delaunay triangulations without '<a
+href="qh-optq.htm#Qc">Qc</a>', it lists the total number of
+coplanar points. It lists the number of simplicial facets in
+the output.</p>
+
+<p>The terminology depends on the output structure. </p>
+
+<p>The summary lists these statistics:</p>
+
+<ul>
+ <li>number of points processed by Qhull </li>
+ <li>number of hyperplanes created</li>
+ <li>number of distance tests (not counting statistics,
+ summary, and checking) </li>
+ <li>number of merged facets (if any)</li>
+ <li>number of distance tests for merging (if any)</li>
+ <li>CPU seconds to compute the hull</li>
+ <li>the maximum joggle for '<a href="qh-optq.htm#QJn">QJ</a>'<br>
+ or, the probability of precision errors for '<a
+ href="qh-optq.htm#QJn">QJ</a> <a href="qh-optt.htm#TRn">TRn</a>'
+ </li>
+ <li>total area and volume (if computed, see '<a
+ href="qh-optf.htm#FS">FS</a>' '<a href="qh-optf.htm#FA">FA</a>'
+ '<a href="qh-optf.htm#Fa">Fa</a>' '<a
+ href="qh-optp.htm#PAn">PAn</a>')</li>
+ <li>max. distance of a point above a facet (if non-zero)</li>
+ <li>max. distance of a vertex below a facet (if non-zero)</li>
+</ul>
+
+<p>The statistics include intermediate hulls. For example 'rbox d
+D4 | qhull' reports merged facets even though the final hull is
+simplicial. </p>
+
+<p>Qhull starts counting CPU seconds after it has read and
+projected the input points. It stops counting before producing
+output. In the code, CPU seconds measures the execution time of
+function qhull() in <tt>libqhull.c</tt>. If the number of CPU
+seconds is clearly wrong, check qh_SECticks in <tt>user.h</tt>. </p>
+
+<p>The last two figures measure the maximum distance from a point
+or vertex to a facet. They are not printed if less than roundoff
+or if not merging. They account for roundoff error in computing
+the distance (c.f., option '<a href="qh-optc.htm#Rn">Rn</a>').
+Use '<a href="qh-optf.htm#Fs">Fs</a>' to report the maximum outer
+and inner plane. </p>
+
+<p>A number may appear in parentheses after the maximum distance
+(e.g., 2.1x). It is the ratio between the maximum distance and
+the worst-case distance due to merging two simplicial facets. It
+should be small for 2-d, 3-d, and 4-d, and for higher dimensions
+with '<a href="qh-optq.htm#Qx">Qx</a>'. It is not printed if less
+than 0.05. </p>
+<!-- Navigation links -->
+<hr>
+
+<p><b>Up:</b> <a href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b> <a href="index.htm#TOC">Qhull manual</a>: Table of Contents<br>
+<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
+&#149; <a href="qh-quick.htm#options">Options</a>
+&#149; <a href="qh-opto.htm#output">Output</a>
+&#149; <a href="qh-optf.htm#format">Formats</a>
+&#149; <a href="qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="qh-optp.htm#print">Print</a>
+&#149; <a href="qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="qh-optc.htm#prec">Precision</a>
+&#149; <a href="qh-optt.htm#trace">Trace</a>
+&#149; <a href="../src/libqhull_r/index.htm">Functions</a></p>
+<!-- GC common information -->
+<hr>
+
+<p><a href="http://www.geom.uiuc.edu/"><img src="qh--geom.gif"
+align="middle" width="40" height="40"></a><i>The Geometry Center
+Home Page </i></p>
+
+<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a>
+</a><br>
+Created: Sept. 25, 1995 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p>
+</body>
+</html>
diff --git a/xs/src/qhull/html/qh-optp.htm b/xs/src/qhull/html/qh-optp.htm
new file mode 100644
index 000000000..9c6df90f5
--- /dev/null
+++ b/xs/src/qhull/html/qh-optp.htm
@@ -0,0 +1,253 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+
+<head>
+<title>Qhull print options (P)</title>
+</head>
+
+<body>
+<!-- Navigation links -->
+<p><b>Up:</b> <a href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b> <a href="index.htm#TOC">Qhull manual</a>: Table of Contents<br>
+<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
+&#149; <a href="qh-quick.htm#options">Options</a>
+&#149; <a href="qh-opto.htm#output">Output</a>
+&#149; <a href="qh-optf.htm#format">Formats</a>
+&#149; <a href="qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="qh-optp.htm#print">Print</a>
+&#149; <a href="qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="qh-optc.htm#prec">Precision</a>
+&#149; <a href="qh-optt.htm#trace">Trace</a>
+&#149; <a href="../src/libqhull_r/index.htm">Functions</a></p>
+
+<hr>
+<!-- Main text of document -->
+<h1><a
+href="http://www.geom.uiuc.edu/graphics/pix/Special_Topics/Computational_Geometry/delaunay.html"><img
+src="qh--dt.gif" alt="[delaunay]" align="middle" width="100"
+height="100"></a> Qhull print options (P)</h1>
+
+This section lists the print options for Qhull. These options are
+indicated by 'P' followed by a letter. See
+<a href="qh-opto.htm#output">Output</a>, <a href="qh-optg.htm#geomview">Geomview</a>,
+and <a href="qh-optf.htm#format">Format</a> for other output options.
+
+
+<p><b>Copyright &copy; 1995-2015 C.B. Barber</b></p>
+
+<hr>
+
+<p><a href="index.htm#TOC">&#187;</a> <a href="qh-quick.htm#programs">Programs</a>
+<a name="format">&#149;</a> <a href="qh-quick.htm#options">Options</a>
+&#149; <a href="qh-opto.htm#output">Output</a>
+&#149; <a href="qh-optf.htm#format">Formats</a>
+&#149; <a href="qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="qh-optp.htm#print">Print</a>
+&#149; <a href="qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="qh-optc.htm#prec">Precision</a>
+&#149; <a href="qh-optt.htm#trace">Trace</a>
+&#149; <a href="../src/libqhull_r/index.htm">Functions</a></p>
+
+<h2>Print options</h2>
+<blockquote>
+<dl compact>
+ <dt>&nbsp;</dt>
+ <dd><b>General</b></dd>
+ <dt><a href="#Pp">Pp</a></dt>
+ <dd>do not report precision problems </dd>
+ <dt><a href="#Po">Po</a></dt>
+ <dd>force output despite precision problems</dd>
+ <dt><a href="#Po2">Po</a></dt>
+ <dd>if error, output neighborhood of facet</dd>
+ <dt>&nbsp;</dt>
+ <dt>&nbsp;</dt>
+ <dd><b>Select</b></dd>
+ <dt><a href="#Pdk">Pdk:n</a></dt>
+ <dd>print facets with normal[k] &gt;= n (default 0.0)</dd>
+ <dt><a href="#PDk">PDk:n</a></dt>
+ <dd>print facets with normal[k] &lt;= n </dd>
+ <dt><a href="#PFn">PFn</a></dt>
+ <dd>print facets whose area is at least n</dd>
+ <dt><a href="#Pg">Pg</a></dt>
+ <dd>print good facets only (needs '<a href="qh-optq.htm#QGn">QGn</a>'
+ or '<a href="qh-optq.htm#QVn">QVn </a>')</dd>
+ <dt><a href="#PMn">PMn</a></dt>
+ <dd>print n facets with most merges</dd>
+ <dt><a href="#PAn">PAn</a></dt>
+ <dd>print n largest facets by area</dd>
+ <dt><a href="#PG">PG</a></dt>
+ <dd>print neighbors of good facets</dd>
+</dl>
+</blockquote>
+<hr>
+
+<h3><a href="#print">&#187;</a><a name="PAn">PAn - keep n largest
+facets by area</a></h3>
+
+<p>The <i>n</i> largest facets are marked good for printing. This
+may be useful for <a href="qh-impre.htm#approximate">approximating
+a hull</a>. Unless '<a href="#PG">PG</a>' is set, '<a href="#Pg">Pg</a>'
+is automatically set. </p>
+
+<h3><a href="#print">&#187;</a><a name="Pdk">Pdk:n - print facet if
+normal[k] &gt;= n </a></h3>
+
+<p>For a given output, print only those facets with <i>normal[k] &gt;= n</i>
+and <i>drop</i> the others. For example, 'Pd0:0.5' prints facets with <i>normal[0]
+&gt;= 0.5 </i>. The default value of <i>n</i> is zero. For
+example in 3-d, 'Pd0d1d2' prints facets in the positive octant.
+<p>
+If no facets match, the closest facet is returned.</p>
+<p>
+On Windows 95, do not combine multiple options. A 'd' is considered
+part of a number. For example, use 'Pd0:0.5 Pd1:0.5' instead of
+'Pd0:0.5d1:0.5'.
+
+<h3><a href="#print">&#187;</a><a name="PDk">PDk:n - print facet if
+normal[k] &lt;= n</a></h3>
+
+<p>For a given output, print only those facets with <i>normal[k] &lt;= n</i>
+and <i>drop</i> the others.
+For example, 'PD0:0.5' prints facets with <i>normal[0]
+&lt;= 0.5 </i>. The default value of <i>n</i> is zero. For
+example in 3-d, 'PD0D1D2' displays facets in the negative octant.
+<p>
+If no facets match, the closest facet is returned.</p>
+
+<p>In 2-d, 'd G PD2' displays the Delaunay triangulation instead
+of the corresponding paraboloid. </p>
+
+<p>Be careful of placing 'Dk' or 'dk' immediately after a real
+number. Some compilers treat the 'D' as a double precision
+exponent. </p>
+
+<h3><a href="#print">&#187;</a><a name="PFn">PFn - keep facets whose
+area is at least n</a></h3>
+
+<p>The facets with area at least <i>n</i> are marked good for
+printing. This may be useful for <a
+href="qh-impre.htm#approximate">approximating a hull</a>. Unless
+'<a href="#PG">PG</a>' is set, '<a href="#Pg">Pg</a>' is
+automatically set. </p>
+
+<h3><a href="#print">&#187;</a><a name="Pg">Pg - print good facets </a></h3>
+
+<p>Qhull can mark facets as &quot;good&quot;. This is used to</p>
+
+<ul>
+ <li>mark the lower convex hull for Delaunay triangulations
+ and Voronoi diagrams</li>
+ <li>mark the facets that are visible from a point (the '<a
+ href="qh-optq.htm#QGn">QGn </a>' option)</li>
+ <li>mark the facets that contain a point (the '<a
+ href="qh-optq.htm#QVn">QVn</a>' option).</li>
+ <li>indicate facets with a large enough area (options '<a
+ href="#PAn">PAn</a>' and '<a href="#PFn">PFn</a>')</li>
+</ul>
+
+<p>Option '<a href="#Pg">Pg</a>' only prints good facets that
+also meet '<a href="#Pdk">Pdk</a>' and '<a href="#PDk">PDk</a>'
+options. It is automatically set for options '<a href="#PAn">PAn</a>',
+'<a href="#PFn">PFn </a>', '<a href="qh-optq.htm#QGn">QGn</a>',
+and '<a href="qh-optq.htm#QVn">QVn</a>'.</p>
+
+<h3><a href="#print">&#187;</a><a name="PG">PG - print neighbors of
+good facets</a></h3>
+
+<p>Option 'PG' can be used with or without option '<a href="#Pg">Pg</a>'
+to print the neighbors of good facets. For example, options '<a
+href="qh-optq.htm#QGn">QGn</a>' and '<a href="qh-optq.htm#QVn">QVn</a>'
+print the horizon facets for point <i>n. </i></p>
+
+<h3><a href="#print">&#187;</a><a name="PMn">PMn - keep n facets with
+most merges</a></h3>
+
+<p>The n facets with the most merges are marked good for
+printing. This may be useful for <a
+href="qh-impre.htm#approximate">approximating a hull</a>. Unless
+'<a href="#PG">PG</a>' is set, '<a href="#Pg">Pg</a>' is
+automatically set. </p>
+
+<p>Use option '<a href="qh-optf.htm#Fm">Fm</a>' to print merges
+per facet.
+
+<h3><a href="#print">&#187;</a><a name="Po">Po - force output despite
+precision problems</a></h3>
+
+<p>Use options 'Po' and '<a href="qh-optq.htm#Q0">Q0</a>' if you
+can not merge facets, triangulate the output ('<a href="qh-optq.htm#Qt">Qt</a>'),
+or joggle the input (<a href="qh-optq.htm#QJn">QJ</a>).
+
+<p>Option 'Po' can not force output when
+duplicate ridges or duplicate facets occur. It may produce
+erroneous results. For these reasons, merged facets, joggled input, or <a
+href="qh-impre.htm#exact">exact arithmetic</a> are better.</p>
+
+<p>If you need a simplicial Delaunay triangulation, use
+joggled input '<a href="qh-optq.htm#QJn">QJ</a>' or triangulated
+output '<a
+href="qh-optf.htm#Ft">Ft</a>'.
+
+<p>Option 'Po' may be used without '<a href="qh-optq.htm#Q0">Q0</a>'
+to remove some steps from Qhull or to output the neighborhood of
+an error.</p>
+
+<p>Option 'Po' may be used with option '<a href="qh-optq.htm#Q5">Q5</a>')
+to skip qh_check_maxout (i.e., do not determine the maximum outside distance).
+This can save a significant amount of time.
+
+<p>If option 'Po' is used,</p>
+
+<ul>
+ <li>most precision errors allow Qhull to continue. </li>
+ <li>verify ('<a href="qh-optt.htm#Tv">Tv</a>') does not check
+ coplanar points.</li>
+ <li>points are not partitioned into flipped facets and a
+ flipped facet is always visible to a point. This may
+ delete flipped facets from the output. </li>
+</ul>
+
+<h3><a href="#print">&#187;</a><a name="Po2">Po - if error, output
+neighborhood of facet</a></h3>
+
+<p>If an error occurs before the completion of Qhull and tracing
+is not active, 'Po' outputs a neighborhood of the erroneous
+facets (if any). It uses the current output options.</p>
+
+<p>See '<a href="qh-optp.htm#Po">Po</a>' - force output despite
+precision problems.
+
+<h3><a href="#print">&#187;</a><a name="Pp">Pp - do not report
+precision problems </a></h3>
+
+<p>With option 'Pp', Qhull does not print statistics about
+precision problems, and it removes some of the warnings. It
+removes the narrow hull warning.</p>
+
+<!-- Navigation links -->
+<hr>
+
+<p><b>Up:</b> <a href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b> <a href="index.htm#TOC">Qhull manual</a>: Table of Contents<br>
+<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
+&#149; <a href="qh-quick.htm#options">Options</a>
+&#149; <a href="qh-opto.htm#output">Output</a>
+&#149; <a href="qh-optf.htm#format">Formats</a>
+&#149; <a href="qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="qh-optp.htm#print">Print</a>
+&#149; <a href="qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="qh-optc.htm#prec">Precision</a>
+&#149; <a href="qh-optt.htm#trace">Trace</a>
+&#149; <a href="../src/libqhull_r/index.htm">Functions</a></p>
+<!-- GC common information -->
+<hr>
+
+<p><a href="http://www.geom.uiuc.edu/"><img src="qh--geom.gif"
+align="middle" width="40" height="40"></a><i>The Geometry Center
+Home Page </i></p>
+
+<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a>
+</a><br>
+Created: Sept. 25, 1995 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p>
+</body>
+</html>
diff --git a/xs/src/qhull/html/qh-optq.htm b/xs/src/qhull/html/qh-optq.htm
new file mode 100644
index 000000000..2edbb1fd4
--- /dev/null
+++ b/xs/src/qhull/html/qh-optq.htm
@@ -0,0 +1,731 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+
+<head>
+<title>Qhull control options (Q)</title>
+</head>
+
+<body>
+<!-- Navigation links -->
+<p><b>Up:</b> <a href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b> <a href="index.htm#TOC">Qhull manual</a>: Table of Contents<br>
+<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
+&#149; <a href="qh-quick.htm#options">Options</a>
+&#149; <a href="qh-opto.htm#output">Output</a>
+&#149; <a href="qh-optf.htm#format">Formats</a>
+&#149; <a href="qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="qh-optp.htm#print">Print</a>
+&#149; <a href="qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="qh-optc.htm#prec">Precision</a>
+&#149; <a href="qh-optt.htm#trace">Trace</a>
+&#149; <a href="../src/libqhull_r/index.htm">Functions</a></p>
+
+<hr>
+<!-- Main text of document -->
+<h1><a
+href="http://www.geom.uiuc.edu/graphics/pix/Special_Topics/Computational_Geometry/delaunay.html"><img
+src="qh--dt.gif" alt="[delaunay]" align="middle" width="100"
+height="100"></a> Qhull control options (Q)</h1>
+
+<p>This section lists the control options for Qhull. These
+options are indicated by 'Q' followed by a letter. </p>
+
+<p><b>Copyright &copy; 1995-2015 C.B. Barber</b></p>
+
+<hr>
+
+<p><a href="index.htm#TOC">&#187;</a> <a href="qh-quick.htm#programs">Programs</a>
+<a name="qhull">&#149;</a> <a href="qh-quick.htm#options">Options</a>
+&#149; <a href="qh-opto.htm#output">Output</a>
+&#149; <a href="qh-optf.htm#format">Formats</a>
+&#149; <a href="qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="qh-optp.htm#print">Print</a>
+&#149; <a href="qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="qh-optc.htm#prec">Precision</a>
+&#149; <a href="qh-optt.htm#trace">Trace</a>
+&#149; <a href="../src/libqhull_r/index.htm">Functions</a></p>
+
+<h2>Qhull control options</h2>
+
+<dl compact>
+ <dt>&nbsp;</dt>
+ <dd><b>General</b></dd>
+ <dt><a href="#Qu">Qu</a></dt>
+ <dd>compute upper hull for furthest-site Delaunay
+ triangulation </dd>
+ <dt><a href="#Qc">Qc</a></dt>
+ <dd>keep coplanar points with nearest facet</dd>
+ <dt><a href="#Qi">Qi</a></dt>
+ <dd>keep interior points with nearest facet</dd>
+ <dt><a href="#QJn">QJ</a></dt>
+ <dd>joggled input to avoid precision problems</dd>
+ <dt><a href="#Qt">Qt</a></dt>
+ <dd>triangulated output</dd>
+ <dt>&nbsp;</dt>
+ <dt>&nbsp;</dt>
+ <dd><b>Precision handling</b></dd>
+ <dt><a href="#Qz">Qz</a></dt>
+ <dd>add a point-at-infinity for Delaunay triangulations</dd>
+ <dt><a href="#Qx">Qx</a></dt>
+ <dd>exact pre-merges (allows coplanar facets)</dd>
+ <dt><a href="#Qs">Qs</a></dt>
+ <dd>search all points for the initial simplex</dd>
+ <dt><a href="#Qbb">Qbb</a></dt>
+ <dd>scale last coordinate to [0,m] for Delaunay</dd>
+ <dt><a href="#Qv">Qv</a></dt>
+ <dd>test vertex neighbors for convexity</dd>
+ <dt>&nbsp;</dt>
+ <dt>&nbsp;</dt>
+ <dd><b>Transform input</b></dd>
+ <dt><a href="#Qb0">Qbk:0Bk:0</a></dt>
+ <dd>drop dimension k from input</dd>
+ <dt><a href="#QRn">QRn</a></dt>
+ <dd>random rotation (n=seed, n=0 time, n=-1 time/no rotate)</dd>
+ <dt><a href="#Qbk">Qbk:n</a></dt>
+ <dd>scale coord[k] to low bound of n (default -0.5)</dd>
+ <dt><a href="#QBk">QBk:n</a></dt>
+ <dd>scale coord[k] to upper bound of n (default 0.5)</dd>
+ <dt><a href="#QbB">QbB</a></dt>
+ <dd>scale input to fit the unit cube</dd>
+ <dt>&nbsp;</dt>
+ <dt>&nbsp;</dt>
+ <dd><b>Select facets</b></dd>
+ <dt><a href="#QVn">QVn</a></dt>
+ <dd>good facet if it includes point n, -n if not</dd>
+ <dt><a href="#QGn">QGn</a></dt>
+ <dd>good facet if visible from point n, -n for not visible</dd>
+ <dt><a href="#Qg">Qg</a></dt>
+ <dd>only build good facets (needs '<a href="#QGn">QGn</a>', '<a
+ href="#QVn">QVn </a>', or '<a href="qh-optp.htm#Pdk">Pdk</a>')</dd>
+ <dt>&nbsp;</dt>
+ <dt>&nbsp;</dt>
+ <dd><b>Experimental</b></dd>
+ <dt><a href="#Q4">Q4</a></dt>
+ <dd>avoid merging old facets into new facets</dd>
+ <dt><a href="#Q5">Q5</a></dt>
+ <dd>do not correct outer planes at end of qhull</dd>
+ <dt><a href="#Q3">Q3</a></dt>
+ <dd>do not merge redundant vertices</dd>
+ <dt><a href="#Q6">Q6</a></dt>
+ <dd>do not pre-merge concave or coplanar facets</dd>
+ <dt><a href="#Q0">Q0</a></dt>
+ <dd>do not pre-merge facets with 'C-0' or 'Qx'</dd>
+ <dt><a href="#Q8">Q8</a></dt>
+ <dd>ignore near-interior points</dd>
+ <dt><a href="#Q2">Q2</a></dt>
+ <dd>merge all non-convex at once instead of independent sets</dd>
+ <dt><a href="#Qf">Qf</a></dt>
+ <dd>partition point to furthest outside facet</dd>
+ <dt><a href="#Q7">Q7</a></dt>
+ <dd>process facets depth-first instead of breadth-first</dd>
+ <dt><a href="#Q9">Q9</a></dt>
+ <dd>process furthest of furthest points</dd>
+ <dt><a href="#Q10">Q10</a></dt>
+ <dd>no special processing for narrow distributions</dd>
+ <dt><a href="#Q11">Q11</a></dt>
+ <dd>copy normals and recompute centrums for tricoplanar facets</dd>
+ <dt><a href="#Q12">Q12</a></dt>
+ <dd>do not error on wide merge due to duplicate ridge and nearly coincident points</dd>
+ <dt><a href="#Qm">Qm</a></dt>
+ <dd>process points only if they would increase the max. outer
+ plane </dd>
+ <dt><a href="#Qr">Qr</a></dt>
+ <dd>process random outside points instead of furthest one</dd>
+ <dt><a href="#Q1">Q1</a></dt>
+ <dd>sort merges by type instead of angle</dd>
+</dl>
+
+<hr>
+
+<h3><a href="#qhull">&#187;</a><a name="Qbb">Qbb - scale the last
+coordinate to [0,m] for Delaunay</a></h3>
+
+<p>After scaling with option 'Qbb', the lower bound of the last
+coordinate will be 0 and the upper bound will be the maximum
+width of the other coordinates. Scaling happens after projecting
+the points to a paraboloid and scaling other coordinates. </p>
+
+<p>Option 'Qbb' is automatically set for <a href=qdelaun.htm>qdelaunay</a>
+and <a href=qvoronoi.htm>qvoronoi</a>. Option 'Qbb' is automatically set for joggled input '<a
+href="qh-optq.htm#QJn">QJ</a>'. </p>
+
+<p>Option 'Qbb' should be used for Delaunay triangulations with
+integer coordinates. Since the last coordinate is the sum of
+squares, it may be much larger than the other coordinates. For
+example, <tt>rbox 10000 D2 B1e8 | qhull d</tt> has precision
+problems while <tt>rbox 10000 D2 B1e8 | qhull d Qbb</tt> is OK. </p>
+
+<h3><a href="#qhull">&#187;</a><a name="QbB">QbB - scale the input to
+fit the unit cube</a></h3>
+
+<p>After scaling with option 'QbB', the lower bound will be -0.5
+and the upper bound +0.5 in all dimensions. For different bounds
+change qh_DEFAULTbox in <tt>user.h</tt> (0.5 is best for <a
+href="index.htm#geomview">Geomview</a>).</p>
+
+<p>For Delaunay and Voronoi diagrams, scaling happens after
+projection to the paraboloid. Under precise arithmetic, scaling
+does not change the topology of the convex hull. Scaling may
+reduce precision errors if coordinate values vary widely.</p>
+
+<h3><a href="#qhull">&#187;</a><a name="Qbk">Qbk:n - scale coord[k]
+to low bound</a></h3>
+
+<p>After scaling, the lower bound for dimension k of the input
+points will be n. 'Qbk' scales coord[k] to -0.5. </p>
+
+<h3><a href="#qhull">&#187;</a><a name="QBk">QBk:n - scale coord[k]
+to upper bound </a></h3>
+
+<p>After scaling, the upper bound for dimension k of the input
+points will be n. 'QBk' scales coord[k] to 0.5. </p>
+
+<h3><a href="#qhull">&#187;</a><a name="Qb0">Qbk:0Bk:0 - drop
+dimension k from the input points</a></h3>
+
+<p>Drop dimension<em> k </em>from the input points. For example,
+'Qb1:0B1:0' deletes the y-coordinate from all input points. This
+allows the user to take convex hulls of sub-dimensional objects.
+It happens before the Delaunay and Voronoi transformation.
+It happens after the halfspace transformation for both the data
+and the feasible point.</p>
+
+<h3><a href="#qhull">&#187;</a><a name="Qc">Qc - keep coplanar points
+with nearest facet </a></h3>
+
+<p>During construction of the hull, a point is coplanar if it is
+between '<a href="qh-optc.htm#Wn">Wn</a>' above and '<a
+href="qh-optc.htm#Un">Un</a>' below a facet's hyperplane. A
+different definition is used for output from Qhull. </p>
+
+<p>For output, a coplanar point is above the minimum vertex
+(i.e., above the inner plane). With joggle ('<a
+href="qh-optq.htm#QJn">QJ</a>'), a coplanar point includes points
+within one joggle of the inner plane. </p>
+
+<p>With option 'Qc', output formats '<a href="qh-opto.htm#p">p </a>',
+'<a href="qh-opto.htm#f">f</a>', '<a href="qh-optg.htm#Gp">Gp</a>',
+'<a href="qh-optf.htm#Fc">Fc</a>', '<a href="qh-optf.htm#FN">FN</a>',
+and '<a href="qh-optf.htm#FP">FP</a>' will print the coplanar
+points. With options 'Qc <a href="#Qi">Qi</a>' these outputs
+include the interior points.</p>
+
+<p>For Delaunay triangulations (<a href=qdelaun.htm>qdelaunay</a>
+or <a href=qvoronoi.htm>qvoronoi</a>), a coplanar point is a point
+that is nearly incident to a vertex. All input points are either
+vertices of the triangulation or coplanar.</p>
+
+<p>Qhull stores coplanar points with a facet. While constructing
+the hull, it retains all points within qh_RATIOnearInside
+(user.h) of a facet. In qh_check_maxout(), it uses these points
+to determine the outer plane for each facet. With option 'Qc',
+qh_check_maxout() retains points above the minimum vertex for the
+hull. Other points are removed. If qh_RATIOnearInside is wrong or
+if options '<a href="#Q5">Q5</a> <a href="#Q8">Q8</a>' are set, a
+coplanar point may be missed in the output (see <a
+href="qh-impre.htm#limit">Qhull limitations</a>).</p>
+
+<h3><a href="#qhull">&#187;</a><a name="Qf">Qf - partition point to
+furthest outside facet </a></h3>
+
+<p>After adding a new point to the convex hull, Qhull partitions
+the outside points and coplanar points of the old, visible
+facets. Without the '<a href="qh-opto.htm#f">f </a>' option and
+merging, it assigns a point to the first facet that it is outside
+('<a href="qh-optc.htm#Wn">Wn</a>'). When merging, it assigns a
+point to the first facet that is more than several times outside
+(see qh_DISToutside in user.h).</p>
+
+<p>If option 'Qf' is selected, Qhull performs a directed search
+(no merging) or an exhaustive search (merging) of new facets.
+Option 'Qf' may reduce precision errors if pre-merging does not
+occur.</p>
+
+<p>Option '<a href="#Q9">Q9</a>' processes the furthest of all
+furthest points.</p>
+
+<h3><a href="#qhull">&#187;</a><a name="Qg">Qg - only build good
+facets (needs 'QGn' 'QVn' or 'Pdk') </a></h3>
+
+<p>Qhull has several options for defining and printing good
+facets. With the '<a href="#Qg">Qg</a>' option, Qhull will only
+build those facets that it needs to determine the good facets in
+the output. This may speed up Qhull in 2-d and 3-d. It is
+useful for furthest-site Delaunay
+triangulations (<a href=qdelau_f.htm>qdelaunay Qu</a>,
+invoke with 'qhull d Qbb <a href="#Qu">Qu</a> Qg').
+It is not effective in higher
+dimensions because many facets see a given point and contain a
+given vertex. It is not guaranteed to work for all combinations.</p>
+
+<p>See '<a href="#QGn">QGn</a>', '<a href="#QVn">QVn</a>', and '<a
+href="qh-optp.htm#Pdk">Pdk</a>' for defining good facets, and '<a
+href="qh-optp.htm#Pg">Pg</a>' and '<a href="qh-optp.htm#PG">PG</a>'
+for printing good facets and their neighbors. If pre-merging ('<a
+href="qh-optc.htm#Cn">C-n</a>') is not used and there are
+coplanar facets, then 'Qg Pg' may produce a different result than
+'<a href="qh-optp.htm#Pg">Pg</a>'. </p>
+
+<h3><a href="#qhull">&#187;</a><a name="QGn">QGn - good facet if
+visible from point n, -n for not visible </a></h3>
+
+<p>With option 'QGn', a facet is good (see '<a href="#Qg">Qg</a>'
+and '<a href="qh-optp.htm#Pg">Pg</a>') if it is visible from
+point n. If <i>n &lt; 0</i>, a facet is good if it is not visible
+from point n. Point n is not added to the hull (unless '<a
+href="qh-optt.htm#TCn">TCn</a>' or '<a href="qh-optt.htm#TPn">TPn</a>').</p>
+
+<p>With <a href="rbox.htm">rbox</a>, use the 'Pn,m,r' option
+to define your point; it will be point 0 ('QG0'). </p>
+
+<h3><a href="#qhull">&#187;</a><a name="Qi">Qi - keep interior points
+with nearest facet </a></h3>
+
+<p>Normally Qhull ignores points that are clearly interior to the
+convex hull. With option 'Qi', Qhull treats interior points the
+same as coplanar points. Option 'Qi' does not retain coplanar
+points. You will probably want '<a href="#Qc">Qc </a>' as well. </p>
+
+<p>Option 'Qi' is automatically set for '<a href=qdelaun.htm>qdelaunay</a>
+<a href="#Qc">Qc</a>' and '<a href=qvoronoi.htm>qvoronoi</a>
+<a href="#Qc">Qc</a>'. If you use
+'<a href=qdelaun.htm>qdelaunay</a> Qi' or '<a href=qvoronoi.htm>qvoronoi</a>
+Qi', option '<a href="qh-opto.htm#s">s</a>' reports all nearly
+incident points while option '<a href="qh-optf.htm#Fs">Fs</a>'
+reports the number of interior points (should always be zero).</p>
+
+<p>With option 'Qi', output formats '<a href="qh-opto.htm#p">p</a>',
+'<a href="qh-opto.htm#f">f</a>','<a href="qh-optg.htm#Gp">Gp</a>',
+'<a href="qh-optf.htm#Fc">Fc</a>', '<a href="qh-optf.htm#FN">FN</a>',
+and '<a href="qh-optf.htm#FP">FP</a>' include interior points. </p>
+
+<h3><a href="#qhull">&#187;</a><a name="QJ">QJ</a> or <a name="QJn">QJn</a> - joggled
+input to avoid precision errors</a></h3>
+
+<p>Option 'QJ' or 'QJn' joggles each input coordinate by adding a
+random number in the range [-n,n]. If a precision error occurs,
+It tries again. If precision errors still occur, Qhull increases <i>n</i>
+ten-fold and tries again. The maximum value for increasing <i>n</i>
+is 0.01 times the maximum width of the input. Option 'QJ' selects
+a default value for <i>n</i>. <a href="../src/user.h#JOGGLEdefault">User.h</a>
+defines these parameters and a maximum number of retries. See <a
+href="qh-impre.htm#joggle">Merged facets or joggled input</a>. </p>
+
+<p>Users of joggled input should consider converting to
+triangulated output ('<a href="../html/qh-optq.htm#Qt">Qt</a>'). Triangulated output is
+approximately 1000 times more accurate than joggled input.
+
+<p>Option 'QJ' also sets '<a href="qh-optq.htm#Qbb">Qbb</a>' for
+Delaunay triangulations and Voronoi diagrams. It does not set
+'Qbb' if '<a href="qh-optq.htm#Qbk">Qbk:n</a>' or '<a
+href="qh-optq.htm#QBk">QBk:n</a>' are set. </p>
+
+<p>If 'QJn' is set, Qhull does not merge facets unless requested
+to. All facets are simplicial (triangular in 2-d). This may be
+important for your application. You may also use triangulated output
+('<a href="qh-optq.htm#Qt">Qt</a>') or Option '<a href="qh-optf.htm#Ft">Ft</a>'.
+
+<p>Qhull adjusts the outer and inner planes for 'QJn' ('<a
+href="qh-optf.htm#Fs">Fs</a>'). They are increased by <i>sqrt(d)*n</i>
+to account for the maximum distance between a joggled point and
+the corresponding input point. Coplanar points ('<a
+href="qh-optq.htm#Qc">Qc</a>') require an additional <i>sqrt(d)*n</i>
+since vertices and coplanar points may be joggled in opposite
+directions. </p>
+
+<p>For Delaunay triangulations (<a href=qdelaun.htm>qdelaunay</a>), joggle
+happens before lifting the input sites to a paraboloid. Instead of
+'QJ', you may use triangulated output ('<a
+href="qh-optq.htm#Qt">Qt</a>')</p>
+
+<p>This option is deprecated for Voronoi diagrams (<a href=qvoronoi.htm>qvoronoi</a>).
+It triangulates cospherical points, leading to duplicated Voronoi vertices.</p>
+
+<p>By default, 'QJn' uses a fixed random number seed. To use time
+as the random number seed, select '<a href="qh-optq.htm#QRn">QR-1</a>'.
+The summary ('<a href="qh-opto.htm#s">s</a>') will show the
+selected seed as 'QR-n'.
+
+<p>With 'QJn', Qhull does not error on degenerate hyperplane
+computations. Except for Delaunay and Voronoi computations, Qhull
+does not error on coplanar points. </p>
+
+<p>Use option '<a href="qh-optf.htm#FO">FO</a>' to display the
+selected options. Option 'FO' displays the joggle and the joggle
+seed. If Qhull restarts, it will redisplay the options. </p>
+
+<p>Use option '<a href="qh-optt.htm#TRn">TRn</a>' to estimate the
+probability that Qhull will fail for a given 'QJn'.
+
+<h3><a href="#qhull">&#187;</a><a name="Qm">Qm - only process points
+that increase the maximum outer plane </a></h3>
+
+<p>Qhull reports the maximum outer plane in its summary ('<a
+href="qh-opto.htm#s">s</a>'). With option 'Qm', Qhull does not
+process points that are below the current, maximum outer plane.
+This is equivalent to always adjusting '<a href="qh-optc.htm#Wn">Wn
+</a>' to the maximum distance of a coplanar point to a facet. It
+is ignored for points above the upper convex hull of a Delaunay
+triangulation. Option 'Qm' is no longer important for merging.</p>
+
+<h3><a href="#qhull">&#187;</a><a name="Qr">Qr - process random
+outside points instead of furthest ones </a></h3>
+
+<p>Normally, Qhull processes the furthest point of a facet's
+outside points. Option 'Qr' instead selects a random outside
+point for processing. This makes Qhull equivalent to the
+randomized incremental algorithms.</p>
+
+<p>The original randomization algorithm by Clarkson and Shor [<a
+href="index.htm#cla-sho89">'89</a>] used a conflict list which
+is equivalent to Qhull's outside set. Later randomized algorithms
+retained the previously constructed facets. </p>
+
+<p>To compare Qhull to the randomized algorithms with option
+'Qr', compare &quot;hyperplanes constructed&quot; and
+&quot;distance tests&quot;. Qhull does not report CPU time
+because the randomization is inefficient. </p>
+
+<h3><a href="#qhull">&#187;</a><a name="QRn">QRn - random rotation </a></h3>
+
+<p>Option 'QRn' randomly rotates the input. For Delaunay
+triangulations (<a href=qdelaun.htm>qdelaunay</a> or <a href=qvoronoi.htm>qvoronoi</a>),
+it rotates the lifted points about
+the last axis. </p>
+
+<p>If <em>n=0</em>, use time as the random number seed. If <em>n&gt;0</em>,
+use n as the random number seed. If <em>n=-1</em>, don't rotate
+but use time as the random number seed. If <em>n&lt;-1</em>,
+don't rotate but use <em>n</em> as the random number seed. </p>
+
+<h3><a href="#qhull">&#187;</a><a name="Qs">Qs - search all points
+for the initial simplex </a></h3>
+
+<p>Qhull constructs an initial simplex from <i>d+1</i> points. It
+selects points with the maximum and minimum coordinates and
+non-zero determinants. If this fails, it searches all other
+points. In 8-d and higher, Qhull selects points with the minimum
+x or maximum coordinate (see qh_initialvertices in <tt>poly2.c </tt>).
+It rejects points with nearly zero determinants. This should work
+for almost all input sets.</p>
+
+<p>If Qhull can not construct an initial simplex, it reports a
+descriptive message. Usually, the point set is degenerate and one
+or more dimensions should be removed ('<a href="#Qb0">Qbk:0Bk:0</a>').
+If not, use option 'Qs'. It performs an exhaustive search for the
+best initial simplex. This is expensive is high dimensions. </p>
+
+<h3><a href="#qhull">&#187;</a><a name="Qt">Qt - triangulated output</a></h3>
+
+<p>By default, qhull merges facets to handle precision errors. This
+produces non-simplicial facets (e.g., the convex hull of a cube has 6 square
+facets). Each facet is non-simplicial because it has four vertices.
+
+<p>Use option 'Qt' to triangulate all non-simplicial facets before generating
+results. Alternatively, use joggled input ('<a href="#QJn">QJ</a>') to
+prevent non-simplical facets. Unless '<a href="qh-optp.htm#Pp">Pp</a>' is set,
+qhull produces a warning if 'QJ' and 'Qt' are used together.
+
+<p>For Delaunay triangulations (<a href=qdelaun.htm>qdelaunay</a>), triangulation
+occurs after lifting the input sites to a paraboloid and computing the convex hull.
+</p>
+
+<p>Option 'Qt' is deprecated for Voronoi diagrams (<a href=qvoronoi.htm>qvoronoi</a>).
+It triangulates cospherical points, leading to duplicated Voronoi vertices.</p>
+
+<p>Option 'Qt' may produce degenerate facets with zero area.</p>
+
+<p>Facet area and hull volumes may differ with and without
+'Qt'. The triangulations are different and different triangles
+may be ignored due to precision errors.
+
+<p>With sufficient merging, the ridges of a non-simplicial facet may share more than two neighboring facets. If so, their triangulation ('<a href="#Qt">Qt</a>') will fail since two facets have the same vertex set. </p>
+
+<h3><a href="#qhull">&#187;</a><a name="Qu">Qu - compute upper hull
+for furthest-site Delaunay triangulation </a></h3>
+
+<p>When computing a Delaunay triangulation (<a href=qdelaun.htm>qdelaunay</a>
+or <a href=qvoronoi.htm>qvoronoi</a>),
+Qhull computes both the the convex hull of points on a
+paraboloid. It normally prints facets of the lower hull. These
+correspond to the Delaunay triangulation. With option 'Qu', Qhull
+prints facets of the upper hull. These correspond to the <a
+href="qdelau_f.htm">furthest-site Delaunay triangulation</a>
+and the <a href="qvoron_f.htm">furthest-site Voronoi diagram</a>.</p>
+
+<p>Option 'qhull d Qbb Qu <a href="#Qg">Qg</a>' may improve the speed of option
+'Qu'. If you use the Qhull library, a faster method is 1) use
+Qhull to compute the convex hull of the input sites; 2) take the
+extreme points (vertices) of the convex hull; 3) add one interior
+point (e.g.,
+'<a href="qh-optf.htm#FV">FV</a>', the average of <em>d</em> extreme points); 4) run
+'qhull d Qbb Qu' or 'qhull v Qbb Qu' on these points.</p>
+
+<h3><a href="#qhull">&#187;</a><a name="Qv">Qv - test vertex
+neighbors for convexity </a></h3>
+
+<p>Normally, Qhull tests all facet neighbors for convexity.
+Non-neighboring facets which share a vertex may not satisfy the
+convexity constraint. This occurs when a facet undercuts the
+centrum of another facet. They should still be convex. Option
+'Qv' extends Qhull's convexity testing to all neighboring facets
+of each vertex. The extra testing occurs after the hull is
+constructed.. </p>
+
+<h3><a href="#qhull">&#187;</a><a name="QVn">QVn - good facet if it
+includes point n, -n if not </a></h3>
+
+<p>With option 'QVn', a facet is good ('<a href="#Qg">Qg</a>',
+'<a href="qh-optp.htm#Pg">Pg</a>') if one of its vertices is
+point n. If <i>n&lt;0</i>, a good facet does not include point n.
+
+<p>If options '<a href="qh-optp.htm#PG">PG</a>'
+and '<a href="#Qg">Qg</a>' are not set, option '<a href="qh-optp.htm#Pg">Pg</a>'
+(print only good)
+is automatically set.
+</p>
+
+<p>Option 'QVn' behaves oddly with options '<a href="qh-optf.htm#Fx">Fx</a>'
+and '<a href=qvoronoi.htm>qvoronoi</a> <a href="qh-optf.htm#Fv2">Fv</a>'.
+
+<p>If used with option '<a href="#Qg">Qg</a>' (only process good facets), point n is
+either in the initial simplex or it is the first
+point added to the hull. Options 'QVn Qg' require either '<a href="#QJn">QJ</a>' or
+'<a href="#Q0">Q0</a>' (no merging).</p>
+
+<h3><a href="#qhull">&#187;</a><a name="Qx">Qx - exact pre-merges
+(allows coplanar facets) </a></h3>
+
+<p>Option 'Qx' performs exact merges while building the hull.
+Option 'Qx' is set by default in 5-d and higher. Use option '<a
+href="#Q0">Q0</a>' to not use 'Qx' by default. Unless otherwise
+specified, option 'Qx' sets option '<a href="qh-optc.htm#C0">C-0</a>'.
+</p>
+
+<p>The &quot;exact&quot; merges are merging a point into a
+coplanar facet (defined by '<a href="qh-optc.htm#Vn">Vn </a>', '<a
+href="qh-optc.htm#Un">Un</a>', and '<a href="qh-optc.htm#Cn">C-n</a>'),
+merging concave facets, merging duplicate ridges, and merging
+flipped facets. Coplanar merges and angle coplanar merges ('<a
+href="qh-optc.htm#An">A-n</a>') are not performed. Concavity
+testing is delayed until a merge occurs.</p>
+
+<p>After the hull is built, all coplanar merges are performed
+(defined by '<a href="qh-optc.htm#Cn">C-n</a>' and '<a
+href="qh-optc.htm#An">A-n</a>'), then post-merges are performed
+(defined by '<a href="qh-optc.htm#Cn2">Cn</a>' and '<a
+href="qh-optc.htm#An2">An</a>'). If facet progress is logged ('<a
+href="qh-optt.htm#TFn">TFn</a>'), Qhull reports each phase and
+prints intermediate summaries and statistics ('<a
+href="qh-optt.htm#Ts">Ts</a>'). </p>
+
+<p>Without 'Qx' in 5-d and higher, options '<a
+href="qh-optc.htm#Cn">C-n</a>' and '<a href="qh-optc.htm#An">A-n</a>'
+may merge too many facets. Since redundant vertices are not
+removed effectively, facets become increasingly wide. </p>
+
+<p>Option 'Qx' may report a wide facet. With 'Qx', coplanar
+facets are not merged. This can produce a &quot;dent&quot; in an
+intermediate hull. If a point is partitioned into a dent and it
+is below the surrounding facets but above other facets, one or
+more wide facets will occur. In practice, this is unlikely. To
+observe this effect, run Qhull with option '<a href="#Q6">Q6</a>'
+which doesn't pre-merge concave facets. A concave facet makes a
+large dent in the intermediate hull.</p>
+
+<p>Option 'Qx' may set an outer plane below one of the input
+points. A coplanar point may be assigned to the wrong facet
+because of a &quot;dent&quot; in an intermediate hull. After
+constructing the hull, Qhull double checks all outer planes with
+qh_check_maxout in <tt>poly2.c </tt>. If a coplanar point is
+assigned to the wrong facet, qh_check_maxout may reach a local
+maximum instead of locating all coplanar facets. This appears to
+be unlikely.</p>
+
+<h3><a href="#qhull">&#187;</a><a name="Qz">Qz - add a
+point-at-infinity for Delaunay triangulations</a></h3>
+
+<p>Option 'Qz' adds a point above the paraboloid of lifted sites
+for a Delaunay triangulation. It allows the Delaunay
+triangulation of cospherical sites. It reduces precision errors
+for nearly cospherical sites.</p>
+
+<h3><a href="#qhull">&#187;</a><a name="Q0">Q0 - no merging with C-0
+and Qx</a></h3>
+
+<p>Turn off default merge options '<a href="qh-optc.htm#C0">C-0</a>'
+and '<a href="#Qx">Qx</a>'. </p>
+
+<p>With 'Q0' and without other pre-merge options, Qhull ignores
+precision issues while constructing the convex hull. This may
+lead to precision errors. If so, a descriptive warning is
+generated. See <a href="qh-impre.htm">Precision issues</a>.</p>
+
+<h3><a href="#qhull">&#187;</a><a name="Q1">Q1 - sort merges by type
+instead of angle </a></h3>
+
+<p>Qhull sorts the coplanar facets before picking a subset of the
+facets to merge. It merges concave and flipped facets first. Then
+it merges facets that meet at a steep angle. With 'Q1', Qhull
+sorts merges by type (coplanar, angle coplanar, concave) instead
+of by angle. This may make the facets wider. </p>
+
+<h3><a href="#qhull">&#187;</a><a name="Q2">Q2 - merge all non-convex
+at once instead of independent sets </a></h3>
+
+<p>With 'Q2', Qhull merges all facets at once instead of
+performing merges in independent sets. This may make the facets
+wider. </p>
+
+<h3><a href="#qhull">&#187;</a><a name="Q3">Q3 - do not merge
+redundant vertices </a></h3>
+
+<p>With 'Q3', Qhull does not remove redundant vertices. In 6-d
+and higher, Qhull never removes redundant vertices (since
+vertices are highly interconnected). Option 'Q3' may be faster,
+but it may result in wider facets. Its effect is easiest to see
+in 3-d and 4-d.</p>
+
+<h3><a href="#qhull">&#187;</a><a name="Q4">Q4 - avoid merging </a>old
+facets into new facets</h3>
+
+<p>With 'Q4', Qhull avoids merges of an old facet into a new
+facet. This sometimes improves facet width and sometimes makes it
+worse. </p>
+
+<h3><a href="#qhull">&#187;</a><a name="Q5">Q5 - do not correct outer
+planes at end of qhull </a></h3>
+
+<p>When merging facets or approximating a hull, Qhull tests
+coplanar points and outer planes after constructing the hull. It
+does this by performing a directed search (qh_findbest in <tt>geom.c</tt>).
+It includes points that are just inside the hull. </p>
+
+<p>With options 'Q5' or '<a href="qh-optp.htm#Po">Po</a>', Qhull
+does not test outer planes. The maximum outer plane is used
+instead. Coplanar points ('<a href="#Qc">Qc</a>') are defined by
+'<a href="qh-optc.htm#Un">Un</a>'. An input point may be outside
+of the maximum outer plane (this appears to be unlikely). An
+interior point may be above '<a href="qh-optc.htm#Un">Un</a>'
+from a hyperplane.</p>
+
+<p>Option 'Q5' may be used if outer planes are not needed. Outer
+planes are needed for options '<a href="qh-opto.htm#s">s</a>', '<a
+href="qh-optg.htm#G">G</a>', '<a href="qh-optg.htm#Go">Go </a>',
+'<a href="qh-optf.htm#Fs">Fs</a>', '<a href="qh-optf.htm#Fo">Fo</a>',
+'<a href="qh-optf.htm#FF">FF</a>', and '<a href="qh-opto.htm#f">f</a>'.</p>
+
+<h3><a href="#qhull">&#187;</a><a name="Q6">Q6 - do not pre-merge
+concave or coplanar facets </a></h3>
+
+<p>With 'Q6', Qhull does not pre-merge concave or coplanar
+facets. This demonstrates the effect of &quot;dents&quot; when
+using '<a href="#Qx">Qx</a>'. </p>
+
+<h3><a href="#qhull">&#187;</a><a name="Q7">Q7 - depth-first
+processing instead of breadth-first </a></h3>
+
+<p>With 'Q7', Qhull processes facets in depth-first order instead
+of breadth-first order. This may increase the locality of
+reference in low dimensions. If so, Qhull may be able to use
+virtual memory effectively. </p>
+
+<p>In 5-d and higher, many facets are visible from each
+unprocessed point. So each iteration may access a large
+proportion of allocated memory. This makes virtual memory
+ineffectual. Once real memory is used up, Qhull will spend most
+of its time waiting for I/O.</p>
+
+<p>Under 'Q7', Qhull runs slower and the facets may be wider. </p>
+
+<h3><a href="#qhull">&#187;</a><a name="Q8">Q8 - ignore near-interior
+points </a></h3>
+
+<p>With 'Q8' and merging, Qhull does not process interior points
+that are near to a facet (as defined by qh_RATIOnearInside in
+user.h). This avoids partitioning steps. It may miss a coplanar
+point when adjusting outer hulls in qh_check_maxout(). The best
+value for qh_RATIOnearInside is not known. Options 'Q8 <a
+href="#Qc">Qc</a>' may be sufficient. </p>
+
+<h3><a href="#qhull">&#187;</a><a name="Q9">Q9 - process furthest of
+furthest points </a></h3>
+
+<p>With 'Q9', Qhull processes the furthest point of all outside
+sets. This may reduce precision problems. The furthest point of
+all outside sets is not necessarily the furthest point from the
+convex hull.</p>
+
+<h3><a href="#qhull">&#187;</a><a name="Q10">Q10 - no special processing
+for narrow distributions</a></h3>
+
+<p>With 'Q10', Qhull does not special-case narrow distributions.
+See <a href=qh-impre.htm#limit>Limitations of merged facets</a> for
+more information.
+
+<h3><a href="#qhull">&#187;</a><a name="Q11">Q11 - copy normals and recompute
+centrums for
+tricoplanar facets</a></h3>
+
+Option '<a href="#Qt">Qt</a>' triangulates non-simplicial facets
+into "tricoplanar" facets.
+Normally tricoplanar facets share the same normal, centrum, and
+Voronoi vertex. They can not be merged or replaced. With
+option 'Q11', Qhull duplicates the normal and Voronoi vertex.
+It recomputes the centrum.
+
+<p>Use 'Q11' if you use the Qhull library to add points
+incrementally and call qh_triangulate() after each point.
+Otherwise, Qhull will report an error when it tries to
+merge and replace a tricoplanar facet.
+
+<p>With sufficient merging and new points, option 'Q11' may
+lead to precision problems such
+as duplicate ridges and concave facets. For example, if qh_triangulate()
+is added to qh_addpoint(), RBOX 1000 s W1e-12 t1001813667 P0 | QHULL d Q11 Tv,
+reports an error due to a duplicate ridge.
+
+<h3><a href="#qhull">&#187;</a><a name="Q12">Q12 - do not error
+on wide merge due to duplicate ridge and nearly coincident points</a></h3>
+
+<p>In 3-d and higher Delaunay Triangulations or 4-d and higher convex hulls, multiple,
+nearly coincident points may lead to very wide facets. An error is reported if a
+merge across a duplicate ridge would increase the facet width by 100x or more.
+
+<p>Use option 'Q12' to log a warning instead of throwing an error.
+
+<p>For Delaunay triangulations, a bounding box may alleviate this error (e.g., <tt>rbox 500 C1,1E-13 t c G1 | qhull d</tt>).
+This avoids the ill-defined edge between upper and lower convex hulls.
+The problem will be fixed in a future release of Qhull.
+
+<p>To demonstrate the problem, use rbox option 'Cn,r,m' to generate nearly coincident points.
+For more information, see "Nearly coincident points on an edge"
+in <a href="qh-impre.htm#limit"</a>Nearly coincident points on an edge</a>.
+
+<!-- Navigation links -->
+<hr>
+
+<p><b>Up:</b> <a href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b> <a href="index.htm#TOC">Qhull manual</a>: Table of Contents<br>
+<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
+&#149; <a href="qh-quick.htm#options">Options</a>
+&#149; <a href="qh-opto.htm#output">Output</a>
+&#149; <a href="qh-optf.htm#format">Formats</a>
+&#149; <a href="qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="qh-optp.htm#print">Print</a>
+&#149; <a href="qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="qh-optc.htm#prec">Precision</a>
+&#149; <a href="qh-optt.htm#trace">Trace</a>
+&#149; <a href="../src/libqhull_r/index.htm">Functions</a></p>
+<!-- GC common information -->
+<hr>
+
+<p><a href="http://www.geom.uiuc.edu/"><img src="qh--geom.gif"
+align="middle" width="40" height="40"></a><i>The Geometry Center
+Home Page </i></p>
+
+<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a>
+</a><br>
+Created: Sept. 25, 1995 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p>
+</body>
+</html>
diff --git a/xs/src/qhull/html/qh-optt.htm b/xs/src/qhull/html/qh-optt.htm
new file mode 100644
index 000000000..0709f58c6
--- /dev/null
+++ b/xs/src/qhull/html/qh-optt.htm
@@ -0,0 +1,278 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+
+<head>
+<title>Qhull trace options (T)</title>
+</head>
+
+<body>
+<!-- Navigation links -->
+<p><b>Up:</b> <a href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b> <a href="index.htm#TOC">Qhull manual</a>: Table of Contents<br>
+<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
+&#149; <a href="qh-quick.htm#options">Options</a>
+&#149; <a href="qh-opto.htm#output">Output</a>
+&#149; <a href="qh-optf.htm#format">Formats</a>
+&#149; <a href="qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="qh-optp.htm#print">Print</a>
+&#149; <a href="qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="qh-optc.htm#prec">Precision</a>
+&#149; <a href="qh-optt.htm#trace">Trace</a>
+&#149; <a href="../src/libqhull_r/index.htm">Functions</a></p>
+
+<hr>
+<!-- Main text of document -->
+<h1><a
+href="http://www.geom.uiuc.edu/graphics/pix/Special_Topics/Computational_Geometry/delaunay.html"><img
+src="qh--dt.gif" alt="[delaunay]" align="middle" width="100"
+height="100"></a> Qhull trace options (T)</h1>
+
+This section lists the trace options for Qhull. These options are
+indicated by 'T' followed by a letter.
+
+<p><b>Copyright &copy; 1995-2015 C.B. Barber</b></p>
+
+<hr>
+
+<p><a href="index.htm#TOC">&#187;</a> <a href="qh-quick.htm#programs">Programs</a>
+<a name="trace">&#149;</a> <a href="qh-quick.htm#options">Options</a>
+&#149; <a href="qh-opto.htm#output">Output</a>
+&#149; <a href="qh-optf.htm#format">Formats</a>
+&#149; <a href="qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="qh-optp.htm#print">Print</a>
+&#149; <a href="qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="qh-optc.htm#prec">Precision</a>
+&#149; <a href="qh-optt.htm#trace">Trace</a>
+&#149; <a href="../src/libqhull_r/index.htm">Functions</a></p>
+
+<h2>Trace options</h2>
+
+<dl compact>
+ <dt>&nbsp;</dt>
+ <dd><b>General</b></dd>
+ <dt><a href="#Tz">Tz</a></dt>
+ <dd>output error information to stdout instead of stderr</dd>
+ <dt><a href="#TI">TI file</a></dt>
+ <dd>input data from a file</dd>
+ <dt><a href="#TO">TO file</a></dt>
+ <dd>output results to a file</dd>
+ <dt><a href="#Ts">Ts</a></dt>
+ <dd>print statistics</dd>
+ <dt><a href="#TFn">TFn</a></dt>
+ <dd>report progress whenever n or more facets created</dd>
+ <dt><a href="#TRn">TRn</a></dt>
+ <dd>rerun qhull n times</dd>
+ <dt><a href="#Tv">Tv</a></dt>
+ <dd>verify result: structure, convexity, and point inclusion</dd>
+
+ <dt>&nbsp;</dt>
+ <dt>&nbsp;</dt>
+ <dd><b>Debugging</b></dd>
+ <dt><a href="#Tc">Tc</a></dt>
+ <dd>check frequently during execution</dd>
+ <dt><a href="#TVn2">TVn</a></dt>
+ <dd>stop qhull after adding point n </dd>
+ <dt><a href="#TCn">TCn</a></dt>
+ <dd>stop qhull after building cone for point n</dd>
+ <dt><a href="#TVn">TV-n</a></dt>
+ <dd>stop qhull before adding point n</dd>
+ <dt><a href="#Tn">T4</a></dt>
+ <dd>trace at level n, 4=all, 5=mem/gauss, -1= events</dd>
+ <dt><a href="#TWn">TWn</a></dt>
+ <dd>trace merge facets when width &gt; n</dd>
+ <dt><a href="#TMn">TMn</a></dt>
+ <dd>turn on tracing at merge n</dd>
+ <dt><a href="#TPn">TPn</a></dt>
+ <dd>turn on tracing when point n added to hull</dd>
+</dl>
+
+<hr>
+
+<h3><a href="#trace">&#187;</a><a name="Tc">Tc - check frequently
+during execution </a></h3>
+
+<p>Qhull includes frequent checks of its data structures. Option
+'Tc' will catch most inconsistency errors. It is slow and should
+not be used for production runs. Option '<a href="#Tv">Tv</a>'
+performs the same checks after the hull is constructed.</p>
+
+<h3><a href="#trace">&#187;</a><a name="TCn">TCn - stop qhull after
+building cone for point n</a></h3>
+
+<p>Qhull builds a cone from the point to its horizon facets.
+Option 'TCn' stops Qhull just after building the cone. The output
+for '<a href="qh-opto.htm#f">f</a>' includes the cone and the old
+hull.'. </p>
+
+<h3><a href="#trace">&#187;</a><a name="TFn">TFn - report summary
+whenever n or more facets created </a></h3>
+
+<p>Option 'TFn' reports progress whenever more than n facets are
+created. The test occurs just before adding a new point to the
+hull. During post-merging, 'TFn' reports progress after more than
+<i>n/2</i> merges. </p>
+
+<h3><a href="#trace">&#187;</a><a name="TI">TI file - input data from file</a></h3>
+
+<p>Input data from 'file' instead of stdin. The filename may not
+contain spaces or use single quotes.
+You may use I/O redirection
+instead (e.g., 'rbox 10 | qdelaunay >results').</P>
+
+<h3><a href="#trace">&#187;</a><a name="TMn">TMn - turn on tracing at
+merge n </a></h3>
+
+<p>Turn on tracing at n'th merge. </p>
+
+<h3><a href="#trace">&#187;</a><a name="Tn">Tn - trace at level n</a></h3>
+
+<p>Qhull includes full execution tracing. 'T-1' traces events.
+'T1' traces the overall execution of the program. 'T2' and 'T3'
+trace overall execution and geometric and topological events.
+'T4' traces the algorithm. 'T5' includes information about memory
+allocation and Gaussian elimination. 'T1' is useful for logging
+progress of Qhull in high dimensions.</p>
+
+<p>Option 'Tn' can produce large amounts of output. Use options '<a
+href="#TPn">TPn</a>', '<a href="#TWn">TWn</a>', and '<a href="#TMn">TMn</a>' to selectively
+turn on tracing. Since all errors report the last processed
+point, option '<a href="#TPn">TPn</a>' is particularly useful.</p>
+
+<p>Different executions of the same program may produce different
+traces and different results. The reason is that Qhull uses hashing
+to match ridges of non-simplicial facets. For performance reasons,
+the hash computation uses
+memory addresses which may change across executions.
+
+<h3><a href="#trace">&#187;</a><a name="TO">TO file - output results to file</a></h3>
+
+<p>Redirect stdout to 'file'. The filename may be enclosed in
+single quotes. Unix and Windows NT users may use I/O redirection
+instead (e.g., 'rbox 10 | qdelaunay >results').</P>
+<p>
+Windows95 users should always use 'TO file'. If they use I/O redirection,
+error output is not sent to the console. Qhull uses single quotes instead
+of double quotes because a missing double quote can
+freeze Windows95 (e.g., do not run, rbox 10 | qhull TO &quot;x)</p>
+<p>
+
+<h3><a href="#trace">&#187;</a><a name="TPn">TPn - turn on tracing
+when point n added to hull </a></h3>
+
+<p>Option 'TPn' turns on tracing when point n is added to
+the hull. It also traces partitions of point n. This option
+reduces the output size when tracing. It is the normal
+method to determine the cause of a Qhull error. All Qhull errors
+report the last point added.
+
+<p>Use options 'TPn <a href="qh-optt.htm#TVn">TVn</a>' to
+trace the addition of point n to the convex hull and stop when done.</p>
+
+<p>If used with option '<a href="qh-optt.htm#TWn">TWn</a>',
+'TPn' turns off tracing after adding point n to the hull.
+Use options 'TPn TWn' to
+trace the addition of point n to the convex hull, partitions
+of point n, and wide merges.</p>
+
+<h3><a href="#trace">&#187;</a><a name="TRn">TRn - rerun qhull n times</a></h3>
+
+<p>Option 'TRn' reruns Qhull n times. It is usually used
+with '<a href="qh-optq.htm#QJn">QJn</a>' to determine the probability
+that a given joggle will fail. The summary
+('<a href="qh-opto.htm#s">s</a>') lists the failure
+rate and the precision errors that occurred.
+Option '<a href="#Ts">Ts</a>' will report statistics for
+all of the runs. Trace and output options only apply to the last
+run. An event trace, '<a href="#Tn">T-1</a>' reports events for all runs.
+
+<p>Tracing applies to the last run of Qhull. If an error
+is reported, the options list the run number as "_run".
+To trace this run, set 'TRn' to the same value.</p>
+
+<h3><a href="#trace">&#187;</a><a name="Ts">Ts - print statistics </a></h3>
+
+<p>Option 'Ts' collects statistics and prints them to stderr. For
+Delaunay triangulations, the angle statistics are restricted to
+the lower or upper envelope.</p>
+
+<h3><a href="#trace">&#187;</a><a name="Tv">Tv - verify result:
+structure, convexity, and point inclusion </a></h3>
+
+<p>Option 'Tv' checks the topological structure, convexity, and
+point inclusion. If precision problems occurred, facet convexity
+is tested whether or not 'Tv' is selected. Option 'Tv' does not
+check point inclusion if forcing output with '<a
+href="qh-optp.htm#Po">Po</a>', or if '<a href="qh-optq.htm#Q5">Q5</a>'
+is set. </p>
+
+<p>The convex hull of a set of points is the smallest polytope
+that includes the points. Option 'Tv' tests point inclusion.
+Qhull verifies that all points are below all outer planes
+(facet-&gt;maxoutside). Point inclusion is exhaustive if merging
+or if the facet-point product is small enough; otherwise Qhull
+verifies each point with a directed search (qh_findbest). To
+force an exhaustive test when using option '<a
+href="qh-optc.htm#C0">C-0</a>' (default), use 'C-1e-30' instead. </p>
+
+<p>Point inclusion testing occurs after producing output. It
+prints a message to stderr unless option '<a
+href="qh-optp.htm#Pp">Pp</a>' is used. This allows the user to
+interrupt Qhull without changing the output. </p>
+
+<p>With '<a href=qvoronoi.htm>qvoronoi</a> <a href="qh-optf.htm#Fi2">Fi</a>'
+and '<a href=qvoronoi.htm>qvoronoi</a> <a href="qh-optf.htm#Fo2">Fo</a>',
+option 'Tv' collects statistics that verify all Voronoi vertices lie
+on the separating hyperplane, and for bounded regions, all
+separating hyperplanes are perpendicular bisectors.
+
+<h3><a href="#trace">&#187;</a><a name="TVn">TV-n - stop qhull before
+adding point n</a></h3>
+
+<p>Qhull adds one point at a time to the convex hull. See <a
+href="qh-eg.htm#how">how Qhull adds a point</a>. Option 'TV-n'
+stops Qhull just before adding a new point. Output shows the hull
+at this time.</p>
+
+<h3><a href="#trace">&#187;</a><a name="TVn2">TVn - stop qhull after
+adding point n</a></h3>
+
+<p>Option 'TVn' stops Qhull after it has added point n. Output
+shows the hull at this time.</p>
+
+<h3><a href="#trace">&#187;</a><a name="TWn">TWn - trace merge facets
+when width &gt; n </a></h3>
+
+<p>Along with TMn, this option allows the user to determine the
+cause of a wide merge.</p>
+<h3><a href="#trace">&#187;</a><a name="Tz">Tz - send all output to
+stdout </a></h3>
+
+<p>Redirect stderr to stdout. </p>
+
+<!-- Navigation links -->
+<hr>
+
+<p><b>Up:</b> <a href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b> <a href="index.htm#TOC">Qhull manual</a>: Table of Contents<br>
+<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
+&#149; <a href="qh-quick.htm#options">Options</a>
+&#149; <a href="qh-opto.htm#output">Output</a>
+&#149; <a href="qh-optf.htm#format">Formats</a>
+&#149; <a href="qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="qh-optp.htm#print">Print</a>
+&#149; <a href="qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="qh-optc.htm#prec">Precision</a>
+&#149; <a href="qh-optt.htm#trace">Trace</a>
+&#149; <a href="../src/libqhull_r/index.htm">Functions</a></p>
+<!-- GC common information -->
+<hr>
+
+<p><a href="http://www.geom.uiuc.edu/"><img src="qh--geom.gif"
+align="middle" width="40" height="40"></a><i>The Geometry Center
+Home Page </i></p>
+
+<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a>
+</a><br>
+Created: Sept. 25, 1995 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p>
+</body>
+</html>
diff --git a/xs/src/qhull/html/qh-quick.htm b/xs/src/qhull/html/qh-quick.htm
new file mode 100644
index 000000000..9d52e7d75
--- /dev/null
+++ b/xs/src/qhull/html/qh-quick.htm
@@ -0,0 +1,495 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+
+<head>
+<title>Qhull quick reference</title>
+</head>
+
+<body>
+<!-- Navigation links -->
+<p><a name="TOC"><b>Up:</b></a> <a href="http://www.qhull.org">Home
+page for Qhull</a> <br>
+<b>Up:</b> <a href="index.htm#TOC">Qhull manual</a> <br>
+<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
+&#149; <a href="qh-quick.htm#options">Options</a>
+&#149; <a href="qh-opto.htm#output">Output</a>
+&#149; <a href="qh-optf.htm#format">Formats</a>
+&#149; <a href="qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="qh-optp.htm#print">Print</a>
+&#149; <a href="qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="qh-optc.htm#prec">Precision</a>
+&#149; <a href="qh-optt.htm#trace">Trace</a>
+&#149; <a href="../src/libqhull_r/index.htm">Functions</a><br>
+<b>To:</b> <a href="qh-code.htm#TOC">Qhull internals</a><br>
+<b>To:</b> <a href="../src/libqhull/index.htm">Qhull functions</a>, macros, and data structures<br>
+<b>To:</b> <a href="../src/libqhull/index.htm#TOC">Qhull files</a><br>
+<b>To:</b> <a href="../src/libqhull/qh-geom.htm">Geom</a> &#149; <a href="../src/libqhull/qh-globa.htm">Global</a>
+&#149; <a href="../src/libqhull/qh-io.htm">Io</a> &#149; <a href="../src/libqhull/qh-mem.htm">Mem</a>
+&#149; <a href="../src/libqhull/qh-merge.htm">Merge</a> &#149; <a href="../src/libqhull/qh-poly.htm">Poly</a>
+&#149; <a href="../src/libqhull/qh-qhull.htm">Qhull</a> &#149; <a href="../src/libqhull/qh-set.htm">Set</a>
+&#149; <a href="../src/libqhull/qh-stat.htm">Stat</a> &#149; <a href="../src/libqhull/qh-user.htm">User</a>
+</p>
+
+<hr>
+<!-- Main text of document -->
+<h1><a
+href="http://www.geom.uiuc.edu/graphics/pix/Special_Topics/Computational_Geometry/cone.html"><img
+src="qh--cone.gif" alt="[cone]" align="middle" width="100"
+height="100"></a> Qhull quick reference</h1>
+
+This section lists all programs and options in Qhull.
+
+<p><b>Copyright &copy; 1995-2015 C.B. Barber</b></p>
+
+<p>
+<a name="programs">&nbsp;</a>
+<hr>
+<b>Qhull programs</b>
+<p><a href="#TOC">&#187;</a> <a href="qh-quick.htm#programs">Programs</a>
+&#149; <a href="qh-quick.htm#options">Options</a>
+&#149; <a href="qh-opto.htm#output">Output</a>
+&#149; <a href="qh-optf.htm#format">Formats</a>
+&#149; <a href="qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="qh-optp.htm#print">Print</a>
+&#149; <a href="qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="qh-optc.htm#prec">Precision</a>
+&#149; <a href="qh-optt.htm#trace">Trace</a>
+&#149; <a href="../src/libqhull_r/index.htm">Functions</a></p>
+
+<dl>
+ <dt><a href="qconvex.htm">qconvex</a> -- convex hull</dt>
+ <dd><a href="qconvex.htm#synopsis">sy</a>nopsis &#149; <a
+ href="qconvex.htm#input">in</a>put &#149; <a
+ href="qconvex.htm#outputs">ou</a>tputs &#149; <a
+ href="qconvex.htm#controls">co</a>ntrols &#149; <a
+ href="qconvex.htm#graphics">gr</a>aphics &#149; <a
+ href="qconvex.htm#notes">no</a>tes &#149; <a
+ href="qconvex.htm#conventions">co</a>nventions &#149; <a
+ href="qconvex.htm#options">op</a>tions</dd>
+ <dt>&nbsp;</dt>
+ <dt><a href="qdelaun.htm">qdelaunay</a> -- Delaunay triangulation</dt>
+ <dd><a href="qdelaun.htm#synopsis">sy</a>nopsis &#149; <a
+ href="qdelaun.htm#input">in</a>put &#149; <a
+ href="qdelaun.htm#outputs">ou</a>tputs &#149; <a
+ href="qdelaun.htm#controls">co</a>ntrols &#149; <a
+ href="qdelaun.htm#graphics">gr</a>aphics &#149; <a
+ href="qdelaun.htm#notes">no</a>tes &#149; <a
+ href="qdelaun.htm#conventions">co</a>nventions &#149; <a
+ href="qdelaun.htm#options">op</a>tions</dd>
+ <dt>&nbsp;</dt>
+ <dt><a href="qdelau_f.htm">qdelaunay Qu</a> -- furthest-site Delaunay triangulation</dt>
+ <dd><a href="qdelau_f.htm#synopsis">sy</a>nopsis &#149; <a
+ href="qdelau_f.htm#input">in</a>put &#149; <a
+ href="qdelau_f.htm#outputs">ou</a>tputs &#149; <a
+ href="qdelau_f.htm#controls">co</a>ntrols &#149; <a
+ href="qdelau_f.htm#graphics">gr</a>aphics &#149; <a
+ href="qdelau_f.htm#notes">no</a>tes &#149; <a
+ href="qdelau_f.htm#conventions">co</a>nventions &#149; <a
+ href="qdelau_f.htm#options">op</a>tions</dd>
+ <dt>&nbsp;</dt>
+ <dt><a href="qhalf.htm">qhalf</a> -- halfspace intersection about a point</dt>
+ <dd><a href="qhalf.htm#synopsis">sy</a>nopsis &#149; <a
+ href="qhalf.htm#input">in</a>put &#149; <a
+ href="qhalf.htm#outputs">ou</a>tputs &#149; <a
+ href="qhalf.htm#controls">co</a>ntrols &#149; <a
+ href="qhalf.htm#graphics">gr</a>aphics &#149; <a
+ href="qhalf.htm#notes">no</a>tes &#149; <a
+ href="qhalf.htm#conventions">co</a>nventions &#149; <a
+ href="qhalf.htm#options">op</a>tions</dd>
+ <dt>&nbsp;</dt>
+ <dt><a href="qvoronoi.htm">qvoronoi</a> -- Voronoi diagram</dt>
+ <dd><a href="qvoronoi.htm#synopsis">sy</a>nopsis &#149; <a
+ href="qvoronoi.htm#input">in</a>put &#149; <a
+ href="qvoronoi.htm#outputs">ou</a>tputs &#149;
+ <a href="qvoronoi.htm#controls">co</a>ntrols &#149; <a
+ href="qvoronoi.htm#graphics">gr</a>aphics &#149; <a
+ href="qvoronoi.htm#notes">no</a>tes &#149; <a
+ href="qvoronoi.htm#conventions">co</a>nventions &#149; <a
+ href="qvoronoi.htm#options">op</a>tions</dd>
+ <dt>&nbsp;</dt>
+ <dt><a href="qvoron_f.htm">qvoronoi Qu</a> -- furthest-site Voronoi diagram</dt>
+ <dd><a href="qvoron_f.htm#synopsis">sy</a>nopsis &#149; <a
+ href="qvoron_f.htm#input">in</a>put &#149; <a
+ href="qvoron_f.htm#outputs">ou</a>tputs &#149; <a
+ href="qvoron_f.htm#controls">co</a>ntrols &#149; <a
+ href="qvoron_f.htm#graphics">gr</a>aphics &#149; <a
+ href="qvoron_f.htm#notes">no</a>tes &#149; <a
+ href="qvoron_f.htm#conventions">co</a>nventions &#149; <a
+ href="qvoron_f.htm#options">op</a>tions</dd>
+ <dt>&nbsp;</dt>
+ <dt><a href="rbox.htm">rbox</a> -- generate point distributions for qhull</dt>
+ <dd><a href="rbox.htm#synopsis">sy</a>nopsis &#149; <a
+ href="rbox.htm#outputs">ou</a>tputs &#149; <a
+ href="rbox.htm#examples">ex</a>amples &#149; <a
+ href="rbox.htm#notes">no</a>tes &#149; <a
+ href="rbox.htm#options">op</a>tions</dd>
+ <dt>&nbsp;</dt>
+ <dt><a href="qhull.htm">qhull</a> -- convex hull and related structures</dt>
+ <dd><a href="qhull.htm#synopsis">sy</a>nopsis &#149; <a
+ href="qhull.htm#input">in</a>put &#149; <a
+ href="qhull.htm#outputs">ou</a>tputs &#149; <a
+ href="qhull.htm#controls">co</a>ntrols &#149; <a
+ href="qhull.htm#options">op</a>tions</dd>
+ </dl>
+<a name="options">&nbsp;</a>
+<hr>
+<b>Qhull options</b>
+
+<p><a href="#TOC">&#187;</a> <a href="qh-quick.htm#programs">Programs</a>
+&#149; <a href="qh-quick.htm#options">Options</a>
+&#149; <a href="qh-opto.htm#output">Output</a>
+&#149; <a href="qh-optf.htm#format">Formats</a>
+&#149; <a href="qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="qh-optp.htm#print">Print</a>
+&#149; <a href="qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="qh-optc.htm#prec">Precision</a>
+&#149; <a href="qh-optt.htm#trace">Trace</a>
+&#149; <a href="../src/libqhull_r/index.htm">Functions</a></p>
+
+<p><table>
+<!-- Note: keep all lines the same for easy reorganization -->
+<tr>
+<td><nobr>'<a href=qh-optf.htm#Fa>Fa</a>'
+Farea
+</nobr></td><td><nobr>'<a href=qh-optf.htm#Fa>FA</a>'
+FArea-total
+</nobr></td><td><nobr>'<a href=qh-optf.htm#Fc>Fc</a>'
+Fcoplanars
+</nobr></td><td><nobr>'<a href=qh-optf.htm#FC>FC</a>'
+FCentrums
+
+</nobr></td></tr><tr>
+<td><nobr>'<a href=qh-optf.htm#Fd>Fd</a>'
+Fd-cdd-in
+</nobr></td><td><nobr>'<a href=qh-optf.htm#FD>FD</a>'
+FD-cdd-out
+</nobr></td><td><nobr>'<a href=qh-optf.htm#FF>FF</a>'
+FF-dump-xridge
+</nobr></td><td><nobr>'<a href=qh-optf.htm#Fi>Fi</a>'
+Finner
+
+</nobr></td></tr><tr>
+<td><nobr>'<a href=qh-optf.htm#Fi2>Fi</a>'
+Finner_bounded
+</nobr></td><td><nobr>'<a href=qh-optf.htm#FI>FI</a>'
+FIDs
+</nobr></td><td><nobr>'<a href=qh-optf.htm#Fm>Fm</a>'
+Fmerges
+</nobr></td><td><nobr>'<a href=qh-optf.htm#FM>FM</a>'
+FMaple
+
+</nobr></td></tr><tr>
+<td><nobr>'<a href=qh-optf.htm#Fn>Fn</a>'
+Fneighbors
+</nobr></td><td><nobr>'<a href=qh-optf.htm#FN>FN</a>'
+FNeigh-vertex
+</nobr></td><td><nobr>'<a href=qh-optf.htm#Fo>Fo</a>'
+Fouter
+</nobr></td><td><nobr>'<a href=qh-optf.htm#Fo2>Fo</a>'
+Fouter_unbounded
+
+</nobr></td></tr><tr>
+<td><nobr>'<a href=qh-optf.htm#FO>FO</a>'
+FOptions
+</nobr></td><td><nobr>'<a href=qh-optf.htm#Fp>Fp</a>'
+Fpoint-intersect
+</nobr></td><td><nobr>'<a href=qh-optf.htm#FP>FP</a>'
+FPoint_near
+</nobr></td><td><nobr>'<a href=qh-optf.htm#FQ>FQ</a>'
+FQhull
+
+</nobr></td></tr><tr>
+<td><nobr>'<a href=qh-optf.htm#Fs>Fs</a>'
+Fsummary
+</nobr></td><td><nobr>'<a href=qh-optf.htm#FS>FS</a>'
+FSize
+</nobr></td><td><nobr>'<a href=qh-optf.htm#Ft>Ft</a>'
+Ftriangles
+</nobr></td><td><nobr>'<a href=qh-optf.htm#Fv>Fv</a>'
+Fvertices
+
+</nobr></td></tr><tr>
+<td><nobr>'<a href=qh-optf.htm#Fv2>Fv</a>'
+Fvoronoi
+</nobr></td><td><nobr>'<a href=qh-optf.htm#FV>FV</a>'
+FVertex-ave
+</nobr></td><td><nobr>'<a href=qh-optf.htm#Fx>Fx</a>'
+Fxtremes
+</nobr></td><td colspan=2><nobr>
+<a href=qh-impre.htm#joggle>Merged facets or joggled input</a>
+
+</nobr></td></tr>
+<tr><td>&nbsp;</td></tr><tr>
+<td><nobr>'<a href=qh-optp.htm#PAn>PAn</a>'
+PArea-keep
+</nobr></td><td><nobr>'<a href=qh-optp.htm#Pdk>Pdk:n</a>'
+Pdrop_low
+</nobr></td><td><nobr>'<a href=qh-optp.htm#PDk>PDk:n</a>'
+Pdrop_high
+</nobr></td><td><nobr>'<a href=qh-optp.htm#Pg>Pg</a>'
+Pgood
+
+</nobr></td></tr><tr>
+<td><nobr>'<a href=qh-optp.htm#PFn>PFn</a>'
+PFacet_area_keep
+</nobr></td><td><nobr>'<a href=qh-optp.htm#PG>PG</a>'
+PGood_neighbors
+</nobr></td><td><nobr>'<a href=qh-optp.htm#PMn>PMn</a>'
+PMerge-keep
+</nobr></td><td><nobr>'<a href=qh-optp.htm#Po>Po</a>'
+Poutput_forced
+
+</nobr></td></tr><tr>
+<td><nobr>'<a href=qh-optp.htm#Po2>Po</a>'
+Poutput_error
+</nobr></td><td><nobr>'<a href=qh-optp.htm#Pp>Pp</a>'
+Pprecision_not
+
+</nobr></td></tr>
+<tr><td>&nbsp;</td></tr><tr>
+<td><nobr>'<a href=qhull.htm#outputs>d</a>'
+delaunay
+</nobr></td><td><nobr>'<a href=qhull.htm#outputs>v</a>'
+voronoi
+</nobr></td><td><nobr>'<a href=qh-optg.htm>G</a>'
+Geomview
+</nobr></td><td><nobr>'<a href=qhull.htm#outputs>H</a>'
+Halfspace
+
+</nobr></td></tr><tr>
+<td><nobr>'<a href=qh-opto.htm#f>f</a>'
+facet_dump
+</nobr></td><td><nobr>'<a href=qh-opto.htm#i>i</a>'
+incidences
+</nobr></td><td><nobr>'<a href=qh-opto.htm#m>m</a>'
+mathematica
+</nobr></td><td><nobr>'<a href=qh-opto.htm#n>n</a>'
+normals
+
+</nobr></td></tr><tr>
+<td><nobr>'<a href=qh-opto.htm#o>o</a>'
+OFF_format
+</nobr></td><td><nobr>'<a href=qh-opto.htm#p>p</a>'
+points
+</nobr></td><td><nobr>'<a href=qh-opto.htm#s>s</a>'
+summary
+
+</nobr></td></tr>
+<tr><td>&nbsp;</td></tr><tr>
+<td><nobr>'<a href=qh-optg.htm#Gv>Gv</a>'
+Gvertices
+</nobr></td><td><nobr>'<a href=qh-optg.htm#Gp>Gp</a>'
+Gpoints
+</nobr></td><td><nobr>'<a href=qh-optg.htm#Ga>Ga</a>'
+Gall_points
+</nobr></td><td><nobr>'<a href=qh-optg.htm#Gn>Gn</a>'
+Gno_planes
+
+</nobr></td></tr><tr>
+<td><nobr>'<a href=qh-optg.htm#Gi>Gi</a>'
+Ginner
+</nobr></td><td><nobr>'<a href=qh-optg.htm#Gc>Gc</a>'
+Gcentrums
+</nobr></td><td><nobr>'<a href=qh-optg.htm#Gh>Gh</a>'
+Ghyperplanes
+</nobr></td><td><nobr>'<a href=qh-optg.htm#Gr>Gr</a>'
+Gridges
+
+</nobr></td></tr><tr>
+<td><nobr>'<a href=qh-optg.htm#Go>Go</a>'
+Gouter
+</nobr></td><td><nobr>'<a href=qh-optg.htm#GDn>GDn</a>'
+GDrop_dim
+</nobr></td><td><nobr>'<a href=qh-optg.htm#Gt>Gt</a>'
+Gtransparent
+
+</nobr></td></tr>
+<tr><td>&nbsp;</td></tr><tr>
+<td><nobr>'<a href=qh-optt.htm#Tn>T4</a>'
+T4_trace
+</nobr></td><td><nobr>'<a href=qh-optt.htm#Tc>Tc</a>'
+Tcheck_often
+</nobr></td><td><nobr>'<a href=qh-optt.htm#Ts>Ts</a>'
+Tstatistics
+</nobr></td><td><nobr>'<a href=qh-optt.htm#Tv>Tv</a>'
+Tverify
+
+</nobr></td></tr><tr>
+<td><nobr>'<a href=qh-optt.htm#Tz>Tz</a>'
+Tz_stdout
+</nobr></td><td><nobr>'<a href=qh-optt.htm#TFn>TFn</a>'
+TFacet_log
+<td><nobr>'<a href=qh-optt.htm#TI>TI file</a>'
+TInput_file
+</nobr></td><td><nobr>'<a href=qh-optt.htm#TPn>TPn</a>'
+TPoint_trace
+
+</nobr></td></tr><tr>
+<td><nobr>'<a href=qh-optt.htm#TMn>TMn</a>'
+TMerge_trace
+</nobr></td><td><nobr>'<a href=qh-optt.htm#TO>TO file</a>'
+TOutput_file
+</nobr></td><td><nobr>'<a href=qh-optt.htm#TRn>TRn</a>'
+TRerun
+</nobr></td><td><nobr>'<a href=qh-optt.htm#TWn>TWn</a>'
+TWide_trace
+
+</nobr></td></tr><tr>
+<td><nobr>'<a href=qh-optt.htm#TVn>TV-n</a>'
+TVertex_stop_before
+</nobr></td></tr><tr>
+<td><nobr>'<a href=qh-optt.htm#TVn2>TVn</a>'
+TVertex_stop_after
+</nobr></td><td><nobr>'<a href=qh-optt.htm#TCn>TCn</a>'
+TCone_stop_after
+
+</nobr></td></tr>
+<tr><td>&nbsp;</td></tr><tr>
+<td><nobr>'<a href=qh-optc.htm#An>A-n</a>'
+Angle_max_pre
+</nobr></td><td><nobr>'<a href=qh-optc.htm#An2>An</a>'
+Angle_max_post
+</nobr></td><td><nobr>'<a href=qh-optc.htm#C0>C-0</a>'
+Centrum_roundoff
+</nobr></td><td><nobr>'<a href=qh-optc.htm#Cn>C-n</a>'
+Centrum_size_pre
+
+</nobr></td></tr><tr>
+<td><nobr>'<a href=qh-optc.htm#Cn2>Cn</a>'
+Centrum_size_post
+</nobr></td><td><nobr>'<a href=qh-optc.htm#En>En</a>'
+Error_round
+</nobr></td><td><nobr>'<a href=qh-optc.htm#Rn>Rn</a>'
+Random_dist
+</nobr></td><td><nobr>'<a href=qh-optc.htm#Vn>Vn</a>'
+Visible_min
+
+</nobr></td></tr><tr>
+<td><nobr>'<a href=qh-optc.htm#Un>Un</a>'
+Ucoplanar_max
+</nobr></td><td><nobr>'<a href=qh-optc.htm#Wn>Wn</a>'
+Wide_outside
+
+</nobr></td></tr>
+<tr><td>&nbsp;</td></tr><tr>
+<td><nobr>'<a href=qh-optq.htm#Qbk>Qbk:n</a>'
+Qbound_low
+</nobr></td><td><nobr>'<a href=qh-optq.htm#QBk>QBk:n</a>'
+QBound_high
+</nobr></td><td><nobr>'<a href=qh-optq.htm#Qb0>Qbk:0Bk:0</a>'
+Qbound_drop
+</nobr></td><td><nobr>'<a href=qh-optq.htm#QbB>QbB</a>'
+QbB-scale-box
+
+</nobr></td></tr><tr>
+<td><nobr>'<a href=qh-optq.htm#Qbb>Qbb</a>'
+Qbb-scale-last
+</nobr></td><td><nobr>'<a href=qh-optq.htm#Qc>Qc</a>'
+Qcoplanar
+</nobr></td><td><nobr>'<a href=qh-optq.htm#Qf>Qf</a>'
+Qfurthest
+</nobr></td><td><nobr>'<a href=qh-optq.htm#Qg>Qg</a>'
+Qgood_only
+
+</nobr></td></tr><tr>
+<td><nobr>'<a href=qh-optq.htm#QGn>QGn</a>'
+QGood_point
+</nobr></td><td><nobr>'<a href=qh-optq.htm#Qi>Qi</a>'
+Qinterior
+</nobr></td><td><nobr>'<a href=qh-optq.htm#Qm>Qm</a>'
+Qmax_out
+</nobr></td><td><nobr>'<a href=qh-optq.htm#QJn>QJn</a>'
+QJoggle
+
+</nobr></td></tr><tr>
+<td><nobr>'<a href=qh-optq.htm#Qr>Qr</a>'
+Qrandom
+</nobr></td><td><nobr>'<a href=qh-optq.htm#QRn>QRn</a>'
+QRotate
+</nobr></td><td><nobr>'<a href=qh-optq.htm#Qs>Qs</a>'
+Qsearch_1st
+</nobr></td><td><nobr>'<a href=qh-optq.htm#Qt>Qt</a>'
+Qtriangulate
+
+</nobr></td></tr><tr>
+<td><nobr>'<a href=qh-optq.htm#Qu>Qu</a>'
+QupperDelaunay
+</nobr><td><nobr>'<a href=qh-optq.htm#QVn>QVn</a>'
+QVertex_good
+</nobr></td><td><nobr>'<a href=qh-optq.htm#Qv>Qv</a>'
+Qvneighbors
+</nobr></td><td><nobr>'<a href=qh-optq.htm#Qx>Qx</a>'
+Qxact_merge
+
+</nobr></td></tr><tr>
+<td><nobr>'<a href=qh-optq.htm#Qz>Qz</a>'
+Qzinfinite
+
+</nobr></td></tr>
+<tr><td>&nbsp;</td></tr><tr>
+<td><nobr>'<a href=qh-optq.htm#Q0>Q0</a>'
+Q0_no_premerge
+</nobr></td><td><nobr>'<a href=qh-optq.htm#Q1>Q1</a>'
+Q1_no_angle
+</nobr></td><td><nobr>'<a href=qh-optq.htm#Q2>Q2</a>'
+Q2_no_independ
+</nobr></td><td><nobr>'<a href=qh-optq.htm#Q3>Q3</a>'
+Q3_no_redundant
+
+</nobr></td></tr><tr>
+<td><nobr>'<a href=qh-optq.htm#Q4>Q4</a>'
+Q4_no_old
+</nobr></td><td><nobr>'<a href=qh-optq.htm#Q5>Q5</a>'
+Q5_no_check_out
+</nobr></td><td><nobr>'<a href=qh-optq.htm#Q6>Q6</a>'
+Q6_no_concave
+</nobr></td><td><nobr>'<a href=qh-optq.htm#Q7>Q7</a>'
+Q7_depth_first
+
+</nobr></td></tr><tr>
+<td><nobr>'<a href=qh-optq.htm#Q8>Q8</a>'
+Q8_no_near_in
+</nobr></td><td><nobr>'<a href=qh-optq.htm#Q9>Q9</a>'
+Q9_pick_furthest
+</nobr></td><td><nobr>'<a href=qh-optq.htm#Q10>Q10</a>'
+Q10_no_narrow
+</nobr></td><td><nobr>'<a href=qh-optq.htm#Q11>Q11</a>'
+Q11_trinormals
+</td></tr></table>
+
+<!-- Navigation links -->
+<hr>
+
+<p><b>Up:</b> <a href="http://www.qhull.org">Home
+page for Qhull</a> <br>
+<b>Up:</b> <a href="index.htm#TOC">Qhull manual</a> <br>
+<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
+&#149; <a href="qh-quick.htm#options">Options</a>
+&#149; <a href="qh-opto.htm#output">Output</a>
+&#149; <a href="qh-optf.htm#format">Formats</a>
+&#149; <a href="qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="qh-optp.htm#print">Print</a>
+&#149; <a href="qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="qh-optc.htm#prec">Precision</a>
+&#149; <a href="qh-optt.htm#trace">Trace</a>
+&#149; <a href="../src/libqhull_r/index.htm">Functions</a><br>
+<b>To:</b> <a href="qh-code.htm#TOC">Qhull internals</a><br>
+<b>To:</b> <a href="../src/libqhull/index.htm">Qhull functions</a>, macros, and data structures<br>
+<b>To:</b> <a href="../src/libqhull/index.htm#TOC">Qhull files</a><br>
+<b>To:</b> <a href="../src/libqhull/qh-geom.htm">Geom</a> &#149; <a href="../src/libqhull/qh-globa.htm">Global</a>
+&#149; <a href="../src/libqhull/qh-io.htm">Io</a> &#149; <a href="../src/libqhull/qh-mem.htm">Mem</a>
+&#149; <a href="../src/libqhull/qh-merge.htm">Merge</a> &#149; <a href="../src/libqhull/qh-poly.htm">Poly</a>
+&#149; <a href="../src/libqhull/qh-qhull.htm">Qhull</a> &#149; <a href="../src/libqhull/qh-set.htm">Set</a>
+&#149; <a href="../src/libqhull/qh-stat.htm">Stat</a> &#149; <a href="../src/libqhull/qh-user.htm">User</a><br>
+<!-- GC common information -->
+<hr>
+
+<p><a href="http://www.geom.uiuc.edu/"><img src="qh--geom.gif"
+align="middle" width="40" height="40"></a><i>The Geometry Center
+Home Page </i></p>
+
+<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a>
+</a><br>
+Created: Sept. 25, 1995 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p>
+</body>
+</html>
diff --git a/xs/src/qhull/html/qhalf.htm b/xs/src/qhull/html/qhalf.htm
new file mode 100644
index 000000000..c87fe719e
--- /dev/null
+++ b/xs/src/qhull/html/qhalf.htm
@@ -0,0 +1,626 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+
+<head>
+<title>qhalf -- halfspace intersection about a point</title>
+</head>
+
+<body>
+<!-- Navigation links -->
+<p><a name="TOP"><b>Up</b></a><b>:</b> <a href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b> <a href="index.htm#TOC">Qhull manual</a>: Table of Contents<br>
+<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
+&#149; <a href="qh-quick.htm#options">Options</a>
+&#149; <a href="qh-opto.htm#output">Output</a>
+&#149; <a href="qh-optf.htm#format">Formats</a>
+&#149; <a href="qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="qh-optp.htm#print">Print</a>
+&#149; <a href="qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="qh-optc.htm#prec">Precision</a>
+&#149; <a href="qh-optt.htm#trace">Trace</a>
+&#149; <a href="../src/libqhull_r/index.htm">Functions</a><br>
+<b>To:</b> <a href="#synopsis">sy</a>nopsis
+&#149; <a href="#input">in</a>put &#149; <a href="#outputs">ou</a>tputs
+&#149; <a href="#controls">co</a>ntrols &#149; <a href="#graphics">gr</a>aphics
+&#149; <a href="#notes">no</a>tes &#149; <a href="#conventions">co</a>nventions
+&#149; <a href="#options">op</a>tions
+
+<hr>
+<!-- Main text of document -->
+<h1><a
+href="http://www.geom.uiuc.edu/graphics/pix/Special_Topics/Computational_Geometry/half.html"><img
+src="qh--half.gif" alt="[halfspace]" align="middle" width="100"
+height="100"></a>qhalf -- halfspace intersection about a point</h1>
+
+<p>The intersection of a set of halfspaces is a polytope. The
+polytope may be unbounded. See Preparata &amp; Shamos [<a
+href="index.htm#pre-sha85">'85</a>] for a discussion. In low
+dimensions, halfspace intersection may be used for linear
+programming.
+
+<blockquote>
+<dl compact>
+ <dt><p><b>Example:</b> rbox c | qconvex <a href="qh-optf.htm#FQ">FQ</a> <a href="qh-optf.htm#FV">FV</a>
+ <a href="qh-opto.htm#n">n</a> | qhalf <a
+ href="qh-optf.htm#Fp">Fp</a></dt>
+ <dd>Print the intersection of the facets of a cube. <tt>rbox c</tt>
+ generates the vertices of a cube. <tt>qconvex FV n</tt> returns of average
+ of the cube's vertices (in this case, the origin) and the halfspaces
+ that define the cube. <tt>qhalf Fp</tt> computes the intersection of
+ the halfspaces about the origin. The intersection is the vertices
+ of the original cube.</dd>
+
+ <dt><p><b>Example:</b> rbox c d G0.55 | qconvex <a href="qh-optf.htm#FQ">FQ</a> <a href="qh-optf.htm#FV">FV</a>
+ <a href="qh-opto.htm#n">n</a> | qhalf <a
+ href="qh-optf.htm#Fp">Fp</a></dt>
+ <dd>Print the intersection of the facets of a cube and a diamond. There
+ are 24 facets and 14 intersection points. Four facets define each diamond
+ vertex. Six facets define each cube vertex.
+ </dd>
+
+ <dt><p><b>Example:</b> rbox c d G0.55 | qconvex <a href="qh-optf.htm#FQ">FQ</a> <a href="qh-optf.htm#FV">FV</a>
+ <a href="qh-opto.htm#n">n</a> | qhalf <a
+ href="qh-optf.htm#Fp">Fp</a>
+ <a href="qh-optq.htm#Qt">Qt</a></dt>
+ <dd>Same as above except triangulate before computing
+ the intersection points. Three facets define each intersection
+ point. There are two duplicates of the diamond and four duplicates of the cube.
+ </dd>
+
+ <dt><p><b>Example:</b> rbox 10 s t10 | qconvex <a href="qh-optf.htm#FQ">FQ</a> <a href="qh-optf.htm#FV">FV</a>
+ <a href="qh-opto.htm#n">n</a> | qhalf <a
+ href="qh-optf.htm#Fp">Fp</a> <a
+ href="qh-optf.htm#Fn">Fn</a></dt>
+ <dd>Print the intersection of the facets of the convex hull of 10 cospherical points.
+ Include the intersection points and the neighboring intersections.
+ As in the previous examples, the intersection points are nearly the same as the
+ original input points.
+ </dd>
+</dl>
+</blockquote>
+
+<p>In Qhull, a <i>halfspace</i> is defined by the points on or below a hyperplane.
+The distance of each point to the hyperplane is less than or equal to zero.
+
+<p>Qhull computes a halfspace intersection by the geometric
+duality between points and halfspaces.
+See <a href="qh-eg.htm#half">halfspace examples</a>,
+<a href="#notes">qhalf notes</a>, and
+option 'p' of <a href="#outputs">qhalf outputs</a>. </p>
+
+<p>Qhalf's <a href="#outputs">outputs</a> are the intersection
+points (<a href="qh-optf.htm#Fp">Fp</a>) and
+the neighboring intersection points (<a href="qh-optf.htm#Fn">Fn</a>).
+For random inputs, halfspace
+intersections are usually defined by more than <i>d</i> halfspaces. See the sphere example.
+
+<p>You can try triangulated output ('<a href="qh-optq.htm#Qt">Qt</a>') and joggled input ('<a href="qh-optq.htm#QJn">QJ</a>').
+It demonstrates that triangulated output is more accurate than joggled input.
+
+<p>If you use '<a href="qh-optq.htm#Qt">Qt</a>' (triangulated output), all
+halfspace intersections are simplicial (e.g., three halfspaces per
+intersection in 3-d). In 3-d, if more than three halfspaces intersect
+at the same point, triangulated output will produce
+duplicate intersections, one for each additional halfspace. See the third example, or
+add 'Qt' to the sphere example.</p>
+
+<p>If you use '<a href="qh-optq.htm#QJn">QJ</a>' (joggled input), all halfspace
+intersections are simplicial. This may lead to nearly identical
+intersections. For example, either replace 'Qt' with 'QJ' above, or add
+'QJ' to the sphere example.
+See <a
+href="qh-impre.htm#joggle">Merged facets or joggled input</a>. </p>
+
+<p>The 'qhalf' program is equivalent to
+'<a href=qhull.htm#outputs>qhull H</a>' in 2-d to 4-d, and
+'<a href=qhull.htm#outputs>qhull H</a> <a href=qh-optq.htm#Qx>Qx</a>'
+in 5-d and higher. It disables the following Qhull
+<a href=qh-quick.htm#options>options</a>: <i>d n v Qbb QbB Qf Qg Qm
+Qr QR Qv Qx Qz TR E V Fa FA FC FD FS Ft FV Gt Q0,etc</i>.
+
+
+<p><b>Copyright &copy; 1995-2015 C.B. Barber</b></p>
+<hr>
+
+<h3><a href="#TOP">&#187;</a><a name="synopsis">qhalf synopsis</a></h3>
+<pre>
+qhalf- halfspace intersection about a point.
+ input (stdin): [dim, 1, interior point]
+ dim+1, n
+ halfspace coefficients + offset
+ comments start with a non-numeric character
+
+options (qhalf.htm):
+ Hn,n - specify coordinates of interior point
+ Qt - triangulated output
+ QJ - joggle input instead of merging facets
+ Tv - verify result: structure, convexity, and redundancy
+ . - concise list of all options
+ - - one-line description of all options
+
+output options (subset):
+ s - summary of results (default)
+ Fp - intersection coordinates
+ Fv - non-redundant halfspaces incident to each intersection
+ Fx - non-redundant halfspaces
+ o - OFF file format (dual convex hull)
+ G - Geomview output (dual convex hull)
+ m - Mathematica output (dual convex hull)
+ QVn - print intersections for halfspace n, -n if not
+ TO file - output results to file, may be enclosed in single quotes
+
+examples:
+ rbox d | qconvex n | qhalf s H0,0,0 Fp
+ rbox c | qconvex FV n | qhalf s i
+ rbox c | qconvex FV n | qhalf s o
+</pre>
+
+<h3><a href="#TOP">&#187;</a><a name="input">qhalf input</a></h3>
+
+<blockquote>
+<p>The input data on <tt>stdin</tt> consists of:</p>
+<ul>
+ <li>[optional] interior point
+ <ul>
+ <li>dimension
+ <li>1
+ <li>coordinates of interior point
+ </ul>
+ <li>dimension + 1
+ <li>number of halfspaces</li>
+ <li>halfspace coefficients followed by offset</li>
+</ul>
+
+<p>Use I/O redirection (e.g., qhalf &lt; data.txt), a pipe (e.g., rbox c | qconvex FV n | qhalf),
+or the '<a href=qh-optt.htm#TI>TI</a>' option (e.g., qhalf TI data.txt).
+
+<p>Qhull needs an interior point to compute the halfspace
+intersection. An interior point is clearly inside all of the halfspaces.
+A point is <i>inside</i> a halfspace if its distance to the corresponding hyperplane is negative.
+
+<p>The interior point may be listed at the beginning of the input (as shown above).
+If not, option
+'Hn,n' defines the interior point as
+[n,n,0,...] where <em>0</em> is the default coordinate (e.g.,
+'H0' is the origin). Use linear programming if you do not know
+the interior point (see <a href="#notes">halfspace notes</a>),</p>
+
+<p>The input to qhalf is a set of halfspaces that are defined by their hyperplanes.
+Each halfspace is defined by
+<em>d</em> coefficients followed by a signed offset. This defines
+a linear inequality. The coefficients define a vector that is
+normal to the halfspace.
+The vector may have any length. If it
+has length one, the offset is the distance from the origin to the
+halfspace's boundary. Points in the halfspace have a negative distance to the hyperplane.
+The distance from the interior point to each
+halfspace is likewise negative.</p>
+
+<p>The halfspace format is the same as Qhull's output options '<a
+href="qh-opto.htm#n">n</a>', '<a href="qh-optf.htm#Fo">Fo</a>',
+and '<a href="qh-optf.htm#Fi">Fi</a>'. Use option '<a
+href="qh-optf.htm#Fd">Fd</a>' to use cdd format for the
+halfspaces.</p>
+
+<p>For example, here is the input for computing the intersection
+of halfplanes that form a cube.</p>
+
+<blockquote>
+ <p>rbox c | qconvex FQ FV n TO data </p>
+ <pre>
+RBOX c | QCONVEX FQ FV n
+3 1
+ 0 0 0
+4
+6
+ 0 0 -1 -0.5
+ 0 -1 0 -0.5
+ 1 0 0 -0.5
+ -1 0 0 -0.5
+ 0 1 0 -0.5
+ 0 0 1 -0.5
+</pre>
+ <p>qhalf s Fp &lt; data </p>
+ <pre>
+
+Halfspace intersection by the convex hull of 6 points in 3-d:
+
+ Number of halfspaces: 6
+ Number of non-redundant halfspaces: 6
+ Number of intersection points: 8
+
+Statistics for: RBOX c | QCONVEX FQ FV n | QHALF s Fp
+
+ Number of points processed: 6
+ Number of hyperplanes created: 11
+ Number of distance tests for qhull: 11
+ Number of merged facets: 1
+ Number of distance tests for merging: 45
+ CPU seconds to compute hull (after input): 0
+
+3
+3
+8
+ -0.5 0.5 0.5
+ 0.5 0.5 0.5
+ -0.5 0.5 -0.5
+ 0.5 0.5 -0.5
+ 0.5 -0.5 0.5
+ -0.5 -0.5 0.5
+ -0.5 -0.5 -0.5
+ 0.5 -0.5 -0.5
+</pre>
+</blockquote>
+
+</blockquote>
+<h3><a href="#TOP">&#187;</a><a name="outputs">qhalf outputs</a></h3>
+<blockquote>
+
+<p>The following options control the output for halfspace
+intersection.</p>
+<blockquote>
+<dl compact>
+ <dt>&nbsp;</dt>
+ <dd><b>Intersections</b></dd>
+ <dt><a href="qh-optf.htm#FN">FN</a></dt>
+ <dd>list intersection points for each non-redundant
+ halfspace. The first line
+ is the number of non-redundant halfspaces. Each remaining
+ lines starts with the number of intersection points. For the cube
+ example, each halfspace has four intersection points.</dd>
+ <dt><a href="qh-optf.htm#Fn">Fn</a></dt>
+ <dd>list neighboring intersections for each intersection point. The first line
+ is the number of intersection points. Each remaining line
+ starts with the number of neighboring intersections. For the cube
+ example, each intersection point has three neighboring intersections.
+ <p>
+ In 3-d, a non-simplicial intersection has more than three neighboring
+ intersections. For random data (e.g., the sphere example), non-simplicial intersections are the norm.
+ Option '<a href="qh-optq.htm#Qt">Qt</a>' produces three
+ neighboring intersections per intersection by duplicating the intersection
+ points. Option <a href="qh-optq.htm#QJn">QJ</a>' produces three
+ neighboring intersections per intersection by joggling the hyperplanes and
+ hence their intersections.
+ </dd>
+ <dt><a href="qh-optf.htm#Fp">Fp</a></dt>
+ <dd>print intersection coordinates. The first line is the dimension and the
+ second line is the number of intersection points. The following lines are the
+ coordinates of each intersection.</dd>
+ <dt><a href="qh-optf.htm#FI">FI</a></dt>
+ <dd>list intersection IDs. The first line is the number of
+ intersections. The IDs follow, one per line.</dd>
+ <dt>&nbsp;</dt>
+ <dt>&nbsp;</dt>
+ <dd><b>Halfspaces</b></dd>
+ <dt><a href="qh-optf.htm#Fx">Fx</a></dt>
+ <dd>list non-redundant halfspaces. The first line is the number of
+ non-redundant halfspaces. The other lines list one halfspace per line.
+ A halfspace is <i>non-redundant</i> if it
+ defines a facet of the intersection. Redundant halfspaces are ignored. For
+ the cube example, all of the halfspaces are non-redundant.
+ </dd>
+ <dt><a href="qh-optf.htm#Fv">Fv</a></dt>
+ <dd>list non-redundant halfspaces incident to each intersection point.
+ The first line is the number of
+ non-redundant halfspaces. Each remaining line starts with the number
+ of non-redundant halfspaces. For the
+ cube example, each intersection is incident to three halfspaces.</dd>
+ <dt><a href="qh-opto.htm#i">i</a></dt>
+ <dd>list non-redundant halfspaces incident to each intersection point. The first
+ line is the number of intersection points. Each remaining line
+ lists the incident, non-redundant halfspaces. For the
+ cube example, each intersection is incident to three halfspaces.
+ </dd>
+ <dt><a href="qh-optf.htm#Fc">Fc</a></dt>
+ <dd>list coplanar halfspaces for each intersection point. The first line is
+ the number of intersection points. Each remaining line starts with
+ the number of coplanar halfspaces. A coplanar halfspace is listed for
+ one intersection point even though it is coplanar to multiple intersection
+ points.</dd>
+ <dt><a href="qh-optq.htm#Qc">Qi</a> <a href="qh-optf.htm#Fc">Fc</a></dt>
+ <dd>list redundant halfspaces for each intersection point. The first line is
+ the number of intersection points. Each remaining line starts with
+ the number of redundant halfspaces. Use options '<a href="qh-optq.htm#Qc">Qc</a> Qi Fc' to list
+ coplanar and redundant halfspaces.</dd>
+
+ <dt>&nbsp;</dt>
+ <dt>&nbsp;</dt>
+ <dd><b>General</b></dd>
+ <dt><a href="qh-opto.htm#s">s</a></dt>
+ <dd>print summary for the halfspace intersection. Use '<a
+ href="qh-optf.htm#Fs">Fs</a>' if you need numeric data.</dd>
+ <dt><a href="qh-opto.htm#o">o</a></dt>
+ <dd>print vertices and facets of the dual convex hull. The
+ first line is the dimension. The second line is the number of
+ vertices, facets, and ridges. The vertex
+ coordinates are next, followed by the facets, one per line.</dd>
+ <dt><a href="qh-opto.htm#p">p</a></dt>
+ <dd>print vertex coordinates of the dual convex hull. Each vertex corresponds
+ to a non-redundant halfspace. Its coordinates are the negative of the hyperplane's coefficients
+ divided by the offset plus the inner product of the coefficients and
+ the interior point (-c/(b+a.p).
+ Options 'p <a href="qh-optq.htm#Qc">Qc</a>' includes coplanar halfspaces.
+ Options 'p <a href="qh-optq.htm#Qi">Qi</a>' includes redundant halfspaces.</dd>
+ <dt><a href="qh-opto.htm#m">m</a></dt>
+ <dd>Mathematica output for the dual convex hull in 2-d or 3-d.</dd>
+ <dt><a href="qh-optf.htm#FM">FM</a></dt>
+ <dd>Maple output for the dual convex hull in 2-d or 3-d.</dd>
+ <dt><a href="qh-optg.htm#G">G</a></dt>
+ <dd>Geomview output for the dual convex hull in 2-d, 3-d, or 4-d.</dd>
+ </dl>
+</blockquote>
+
+</blockquote>
+<h3><a href="#TOP">&#187;</a><a name="controls">qhalf controls</a></h3>
+<blockquote>
+
+<p>These options provide additional control:</p>
+
+<blockquote>
+<dl compact>
+ <dt><a href="qh-optq.htm#Qt">Qt</a></dt>
+ <dd>triangulated output. If a 3-d intersection is defined by more than
+ three hyperplanes, Qhull produces duplicate intersections -- one for
+ each extra hyperplane.</dd>
+ <dt><a href="qh-optq.htm#QJn">QJ</a></dt>
+ <dd>joggle the input instead of merging facets. In 3-d, this guarantees that
+ each intersection is defined by three hyperplanes.</dd>
+ <dt><a href="qh-opto.htm#f">f </a></dt>
+ <dd>facet dump. Print the data structure for each intersection (i.e.,
+ facet)</dd>
+ <dt><a href="qh-optt.htm#TFn">TFn</a></dt>
+ <dd>report summary after constructing <em>n</em>
+ intersections</dd>
+ <dt><a href="qh-optq.htm#QVn">QVn</a></dt>
+ <dd>select intersection points for halfspace <em>n</em>
+ (marked 'good')</dd>
+ <dt><a href="qh-optq.htm#QGn">QGn</a></dt>
+ <dd>select intersection points that are visible to halfspace <em>n</em>
+ (marked 'good'). Use <em>-n</em> for the remainder.</dd>
+ <dt><a href="qh-optq.htm#Qb0">Qbk:0Bk:0</a></dt>
+ <dd>remove the k-th coordinate from the input. This computes the
+ halfspace intersection in one lower dimension.</dd>
+ <dt><a href="qh-optt.htm#Tv">Tv</a></dt>
+ <dd>verify result</dd>
+ <dt><a href="qh-optt.htm#TO">TI file</a></dt>
+ <dd>input data from file. The filename may not use spaces or quotes.</dd>
+ <dt><a href="qh-optt.htm#TO">TO file</a></dt>
+ <dd>output results to file. Use single quotes if the filename
+ contains spaces (e.g., <tt>TO 'file with spaces.txt'</tt></dd>
+ <dt><a href="qh-optq.htm#Qs">Qs</a></dt>
+ <dd>search all points for the initial simplex. If Qhull can
+ not construct an initial simplex, it reports a
+descriptive message. Usually, the point set is degenerate and one
+or more dimensions should be removed ('<a href="qh-optq.htm#Qb0">Qbk:0Bk:0</a>').
+If not, use option 'Qs'. It performs an exhaustive search for the
+best initial simplex. This is expensive is high dimensions.</dd>
+</dl>
+</blockquote>
+
+
+</blockquote>
+<h3><a href="#TOP">&#187;</a><a name="graphics">qhalf graphics</a></h3>
+<blockquote>
+
+<p>To view the results with Geomview, compute the convex hull of
+the intersection points ('qhull FQ H0 Fp | qhull G'). See <a
+href="qh-eg.htm#half">Halfspace examples</a>.</p>
+
+</blockquote>
+<h3><a href="#TOP">&#187;</a><a name="notes">qhalf notes</a></h3>
+<blockquote>
+
+<p>See <a href="qh-impre.htm#halfspace">halfspace intersection</a> for precision issues related to qhalf.</p>
+
+<p>If you do not know an interior point for the halfspaces, use
+linear programming to find one. Assume, <em>n</em> halfspaces
+defined by: <em>aj*x1+bj*x2+cj*x3+dj&lt;=0, j=1..n</em>. Perform
+the following linear program: </p>
+
+<blockquote>
+ <p>max(x5) aj*x1+bj*x2+cj*x3+dj*x4+x5&lt;=0, j=1..n</p>
+</blockquote>
+
+<p>Then, if <em>[x1,x2,x3,x4,x5]</em> is an optimal solution with
+<em>x4&gt;0</em> and <em>x5&gt;0</em> we get: </p>
+
+<blockquote>
+ <p>aj*(x1/x4)+bj*(x2/x4)+cj*(x3/x4)+dj&lt;=(-x5/x4) j=1..n and (-x5/x4)&lt;0,
+ </p>
+</blockquote>
+
+<p>and conclude that the point <em>[x1/x4,x2/x4,x3/x4]</em> is in
+the interior of all the halfspaces. Since <em>x5</em> is
+optimal, this point is &quot;way in&quot; the interior (good
+for precision errors).</p>
+
+<p>After finding an interior point, the rest of the intersection
+algorithm is from Preparata &amp; Shamos [<a
+href="index.htm#pre-sha85">'85</a>, p. 316, &quot;A simple case
+...&quot;]. Translate the halfspaces so that the interior point
+is the origin. Calculate the dual polytope. The dual polytope is
+the convex hull of the vertices dual to the original faces in
+regard to the unit sphere (i.e., halfspaces at distance <em>d</em>
+from the origin are dual to vertices at distance <em>1/d</em>).
+Then calculate the resulting polytope, which is the dual of the
+dual polytope, and translate the origin back to the interior
+point [S. Spitz, S. Teller, D. Strawn].</p>
+
+
+</blockquote>
+<h3><a href="#TOP">&#187;</a><a name="conventions">qhalf
+conventions</a></h3>
+<blockquote>
+
+<p>The following terminology is used for halfspace intersection
+in Qhull. This is the hardest structure to understand. The
+underlying structure is a convex hull with one vertex per
+non-redundant halfspace. See <a href="#conventions">convex hull
+conventions</a> and <a href="index.htm#structure">Qhull's data structures</a>.</p>
+
+<ul>
+ <li><em>interior point</em> - a point in the intersection of
+ the halfspaces. Qhull needs an interior point to compute
+ the intersection. See <a href="#input">halfspace input</a>.</li>
+ <li><em>halfspace</em> - <em>d</em> coordinates for the
+ normal and a signed offset. The distance to an interior
+ point is negative.</li>
+ <li><em>non-redundant halfspace</em> - a halfspace that
+ defines an output facet</li>
+ <li><em>vertex</em> - a dual vertex in the convex hull
+ corresponding to a non-redundant halfspace</li>
+ <li><em>coplanar point</em> - the dual point corresponding to
+ a similar halfspace</li>
+ <li><em>interior point</em> - the dual point corresponding to
+ a redundant halfspace</li>
+ <li><em>intersection point</em>- the intersection of <em>d</em>
+ or more non-redundant halfspaces</li>
+ <li><em>facet</em> - a dual facet in the convex hull
+ corresponding to an intersection point</li>
+ <li><em>non-simplicial facet</em> - more than <em>d</em>
+ halfspaces intersect at a point</li>
+ <li><em>good facet</em> - an intersection point that
+ satisfies restriction '<a href="qh-optq.htm#QVn">QVn</a>',
+ etc.</li>
+</ul>
+
+</blockquote>
+<h3><a href="#TOP">&#187;</a><a name="options">qhalf options</a></h3>
+
+<pre>
+qhalf- compute the intersection of halfspaces about a point
+ http://www.qhull.org
+
+input (stdin):
+ optional interior point: dimension, 1, coordinates
+ first lines: dimension+1 and number of halfspaces
+ other lines: halfspace coefficients followed by offset
+ comments: start with a non-numeric character
+
+options:
+ Hn,n - specify coordinates of interior point
+ Qt - triangulated ouput
+ QJ - joggle input instead of merging facets
+ Qc - keep coplanar halfspaces
+ Qi - keep other redundant halfspaces
+
+Qhull control options:
+ QJn - randomly joggle input in range [-n,n]
+ Qbk:0Bk:0 - remove k-th coordinate from input
+ Qs - search all halfspaces for the initial simplex
+ QGn - print intersection if redundant to halfspace n, -n for not
+ QVn - print intersections for halfspace n, -n if not
+
+Trace options:
+ T4 - trace at level n, 4=all, 5=mem/gauss, -1= events
+ Tc - check frequently during execution
+ Ts - print statistics
+ Tv - verify result: structure, convexity, and redundancy
+ Tz - send all output to stdout
+ TFn - report summary when n or more facets created
+ TI file - input data from file, no spaces or single quotes
+ TO file - output results to file, may be enclosed in single quotes
+ TPn - turn on tracing when halfspace n added to intersection
+ TMn - turn on tracing at merge n
+ TWn - trace merge facets when width > n
+ TVn - stop qhull after adding halfspace n, -n for before (see TCn)
+ TCn - stop qhull after building cone for halfspace n (see TVn)
+
+Precision options:
+ Cn - radius of centrum (roundoff added). Merge facets if non-convex
+ An - cosine of maximum angle. Merge facets if cosine > n or non-convex
+ C-0 roundoff, A-0.99/C-0.01 pre-merge, A0.99/C0.01 post-merge
+ Rn - randomly perturb computations by a factor of [1-n,1+n]
+ Un - max distance below plane for a new, coplanar halfspace
+ Wn - min facet width for outside halfspace (before roundoff)
+
+Output formats (may be combined; if none, produces a summary to stdout):
+ f - facet dump
+ G - Geomview output (dual convex hull)
+ i - non-redundant halfspaces incident to each intersection
+ m - Mathematica output (dual convex hull)
+ o - OFF format (dual convex hull: dimension, points, and facets)
+ p - vertex coordinates of dual convex hull (coplanars if 'Qc' or 'Qi')
+ s - summary (stderr)
+
+More formats:
+ Fc - count plus redundant halfspaces for each intersection
+ - Qc (default) for coplanar and Qi for other redundant
+ Fd - use cdd format for input (homogeneous with offset first)
+ FF - facet dump without ridges
+ FI - ID of each intersection
+ Fm - merge count for each intersection (511 max)
+ FM - Maple output (dual convex hull)
+ Fn - count plus neighboring intersections for each intersection
+ FN - count plus intersections for each non-redundant halfspace
+ FO - options and precision constants
+ Fp - dim, count, and intersection coordinates
+ FP - nearest halfspace and distance for each redundant halfspace
+ FQ - command used for qhalf
+ Fs - summary: #int (8), dim, #halfspaces, #non-redundant, #intersections
+ for output: #non-redundant, #intersections, #coplanar
+ halfspaces, #non-simplicial intersections
+ #real (2), max outer plane, min vertex
+ Fv - count plus non-redundant halfspaces for each intersection
+ Fx - non-redundant halfspaces
+
+Geomview output (2-d, 3-d and 4-d; dual convex hull)
+ Ga - all points (i.e., transformed halfspaces) as dots
+ Gp - coplanar points and vertices as radii
+ Gv - vertices (i.e., non-redundant halfspaces) as spheres
+ Gi - inner planes (i.e., halfspace intersections) only
+ Gn - no planes
+ Go - outer planes only
+ Gc - centrums
+ Gh - hyperplane intersections
+ Gr - ridges
+ GDn - drop dimension n in 3-d and 4-d output
+
+Print options:
+ PAn - keep n largest facets (i.e., intersections) by area
+ Pdk:n- drop facet if normal[k] &lt;= n (default 0.0)
+ PDk:n- drop facet if normal[k] >= n
+ Pg - print good facets (needs 'QGn' or 'QVn')
+ PFn - keep facets whose area is at least n
+ PG - print neighbors of good facets
+ PMn - keep n facets with most merges
+ Po - force output. If error, output neighborhood of facet
+ Pp - do not report precision problems
+
+ . - list of all options
+ - - one line descriptions of all options
+</pre>
+
+<!-- Navigation links -->
+<hr>
+
+<p><b>Up:</b> <a href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b> <a href="index.htm#TOC">Qhull manual</a>: Table of Contents<br>
+<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
+&#149; <a href="qh-quick.htm#options">Options</a>
+&#149; <a href="qh-opto.htm#output">Output</a>
+&#149; <a href="qh-optf.htm#format">Formats</a>
+&#149; <a href="qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="qh-optp.htm#print">Print</a>
+&#149; <a href="qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="qh-optc.htm#prec">Precision</a>
+&#149; <a href="qh-optt.htm#trace">Trace</a>
+&#149; <a href="../src/libqhull_r/index.htm">Functions</a><br>
+<b>To:</b> <a href="#synopsis">sy</a>nopsis
+&#149; <a href="#input">in</a>put &#149; <a href="#outputs">ou</a>tputs
+&#149; <a href="#controls">co</a>ntrols &#149; <a href="#graphics">gr</a>aphics
+&#149; <a href="#notes">no</a>tes &#149; <a href="#conventions">co</a>nventions
+&#149; <a href="#options">op</a>tions
+<!-- GC common information -->
+<hr>
+
+<p><a href="http://www.geom.uiuc.edu/"><img src="qh--geom.gif"
+align="middle" width="40" height="40"></a><i>The Geometry Center
+Home Page </i></p>
+
+<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a>
+</a><br>
+Created: Sept. 25, 1995 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p>
+</body>
+</html>
+
diff --git a/xs/src/qhull/html/qhull-cpp.xml b/xs/src/qhull/html/qhull-cpp.xml
new file mode 100644
index 000000000..ae755e826
--- /dev/null
+++ b/xs/src/qhull/html/qhull-cpp.xml
@@ -0,0 +1,214 @@
+<?xml version="1.0" encoding="utf-8"?>
+<?xml-stylesheet type="text/xsl" href="../../road-faq/xsl/road-faq.xsl"?>
+
+<rf:topic xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://schemas.roadintranet.org/road-faq-1 /road/road-faq/xsl/road-faq.xsd"
+ xmlns:rf="http://schemas.roadintranet.org/road-faq-1"
+ title=" C++ interface to Qhull"
+ file="qhull-cpp.xml"
+ fileid="$Id: //main/2015/qhull/html/qhull-cpp.xml#2 $$Change: 2027 $"
+ fileChange="$DateTime: 2015/11/09 23:18:11 $$Author: bbarber $">
+ <div><h4>Qhull C++ -- C++ interface to Qhull</h4></div>
+ <rf:copyright>
+ <a href="../cpp/COPYING.txt">Copyright</a> (c) 2009-2015, C.B. Barber
+ </rf:copyright>
+ <rf:section id="cpp-cpp-links" title="Useful Links for Qhull C++">
+ <div>
+ <p> This draft
+ document records some of the design decisions for Qhull C++. Convert it to HTML by road-faq.xsl from <a href="http://www.qhull.org/road/road-faq/road-faq.html">road-faq</a>.
+
+ Please send comments and suggestions to <a
+ href="mailto:bradb@shore.net">bradb@shore.net</a>
+ </p>
+ </div>
+ <div class="twocol">
+ <div class="col leftcol">
+ Help
+ <ul><li>
+ </li><li>
+ </li><li>
+ </li></ul>
+ </div>
+ <div class="col rightcol">
+ <ul><li>
+ </li><li>
+ </li></ul>
+ </div>
+ </div>
+ <div>
+ . <!-- clear the two column display -->
+ </div>
+
+ </rf:section>
+ <rf:section id="qhull-api" title="Qhull's collection classes">
+
+ <rf:item id="collection-api" title="API for Qhull collections" date="Feb 2009" author="bbarber">
+ Qhull's collection APIs are modeled on Qt's collection API (QList, QVector, QHash) w/o QT_STRICT_ITERATORS. They support STL and Qt programming.
+
+ <p>Some of Qhull's collection classes derive from STL classes. If so,
+ please avoid additional STL functions and operators added by inheritance.
+ These collection classes may be rewritten to derive from Qt classes instead.
+ See Road's <rf:iref item="cpp-collection-api"/>.
+ </p>
+
+
+ Qhull's collection API (where applicable). For documentation, see Qt's QList, QMap, QListIterator, QMapIterator, QMutableListIterator, and QMutableMapIterator
+ <ul><li>
+ STL types [list, qlinkedlist, qlist, qvector, vector] -- const_iterator, iterator
+ </li><li>
+ STL types describing iterators [list, qlinkedlist, qlist, qvector, vector] -- const_pointer, const_reference, difference_type,
+ pointer, reference, size_type, value_type.
+ Pointer and reference types not defined if unavailable (not needed for &lt;algorithm&gt;)
+ </li><li>
+ const_iterator, iterator types -- difference_type, iterator_category, pointer, reference, value_type
+ </li><li>
+ Qt types [qlinkedlist, qlist, qvector] -- ConstIterator, Iterator, QhullclassIterator, MutableQhullclassIterator.
+ Qt's foreach requires const_iterator.
+ </li><li>
+ Types for sets/maps [hash_map, QHash] -- key_compare, key_type, mapped_type
+ </li><li>
+ Constructor -- default constructor, copy constructor, assignment operator, destructor
+ </li><li>
+ Conversion -- to/from/as corresponding C, STL, and Qt constructs. Include toQList and toStdVector (may be filtered, e.g., QhullFacetSet).
+ Do not define fromStdList and fromQList if container is not reference counted (i.e., acts like a value)
+ </li><li>
+ Get/set -- configuration options for class
+ </li><li>
+ STL-style iterator - begin, constBegin, constEnd, end, key, value, =, *, [], ->, ++, --, +, -, ==, !=, &lt;,
+ &lt;=, &gt;, &gt;=, const_iterator(iterator), iterator COMPARE const_iterator.
+ An iterator is an abstraction of a pointer. It is not aware of its container.
+ </li><li>
+ Java-style iterator [qiterator.h] - countRemaining, findNext, findPrevious, hasNext, hasPrevious, next, peekNext, peekPrevious, previous, toBack, toFront, = Coordinates
+ </li><li>
+ Mutable Java-style iterator adds - insert, remove, setValue, value
+ </li><li>
+ Element access -- back, first, front, last
+ </li><li>
+ Element access w/ index -- [], at (const&amp; only), constData, data, mid, value
+ </li><li>
+ Read-only - (int)count, empty, isEmpty, (size_t)size. Count() and size() may be filtered. If so, they may be zero when !empty().
+ </li><li>
+ Read-only for sets/maps - capacity, key, keys, reserve, resize, values
+ </li><li>
+ Operator - ==, !=, +, +=, &lt;&lt;
+ </li><li>
+ Read-write -- append, clear, erase, insert, move, prepend, pop_back, pop_front, push_back, push_front, removeAll, removeAt, removeFirst, removeLast, replace,
+ swap, takeAt, takeFirst, takeLast
+ </li><li>
+ Read-write for sets/maps -- insertMulti, squeeze, take, unite
+ </li><li>
+ Search -- contains(const T &amp;), count(const T &amp;), indexOf, lastIndexOf
+ </li><li>
+ Search for sets/maps -- constFind, lowerBound, upperBound
+ </li><li>
+ Stream I/O -- stream &lt;&lt;
+ </li></ul>
+
+ STL list and vector -- For unfiltered access to each element.
+ <ul><li>
+ <a href="http://stdcxx.apache.org/doc/stdlibug/16-3.html">Apache: Creating your own containers</a> -- requirements for STL containers. Iterators should define the types from 'iterator_traits'.
+ </li><li>
+ STL types -- allocator_type, const_iterator, const_pointer, const_reference, const_reverse_iterator, difference_type, iterator, iterator_category, pointer, reference, reverse_iterator, size_type, value_type
+ </li><li>
+ STL constructors -- MyType(), MyType(count), MyType(count, value), MyType(first, last),
+ MyType(MyType&amp;),
+ </li><li>
+ STL getter/setters -- at (random_access only), back, begin, capacity, end, front, rbegin, rend, size, max_size
+ </li><li>
+ STL predicates -- empty
+ </li><li>
+ STL iterator types -- const_pointer, const_reference, difference_type, iterator_category, pointer, reference, value_type
+ </li><li>
+ STL iterator operators -- *, -&lt;, ++, --, +=, -=, +, -, [], ==, !=, &lt;, &gt;, &gt;=, &lt;=
+ </li><li>
+ STL operators -- =, [] (random_access only), ==, !=, &lt;, &gt;, &lt;=, &gt;=
+ </li><li>
+ STL modifiers -- assign, clear, erase, insert, pop_back, push_back, reserve, resize, swap
+ </li><li>
+ </li></ul>
+
+ Qt Qlist -- For unfiltered access to each element
+ <ul><li>
+ </li><li>
+ Additional Qt types -- ConstIterator, Iterator, QListIterator, QMutableListIterator
+ </li><li>
+ Additional Qt get/set -- constBegin, constEnd, count, first, last, value (random_access only)
+ </li><li>
+ Additional Qt predicates -- isEmpty
+ </li><li>
+ Additional Qt -- mid (random_access only)
+ </li><li>
+ Additional Qt search -- contains, count(T&amp;), indexOf (random_access only), lastIndeOf (random_access only)
+ </li><li>
+ Additional Qt modifiers -- append, insert(index,value) (random_access only), move (random_access only), pop_front, prepend, push_front, removeAll, removeAt (random_access only), removeFirst, removeLast, replace, swap by index, takeAt, takeFirst, takeLast
+ </li><li>
+ Additional Qt operators -- +, &lt;&lt;, +=,
+ stream &lt;&lt; and &gt;&gt;
+ </li><li>
+ Unsupported types by Qt -- allocator_type, const_reverse_iterator, reverse_iterator
+ </li><li>
+ Unsupported accessors by Qt -- max_size, rbegin, rend
+ </li><li>
+ Unsupported constructors by Qt -- multi-value constructors
+ </li><li>
+ unsupported modifiers by Qt -- assign, muli-value inserts, STL's swaps
+ </li><li>
+ </li></ul>
+
+ STL map and Qt QMap. These use nearly the same API as list and vector classes. They add the following.
+ <ul><li>
+ STL types -- key_compare, key_type, mapped_type
+ </li><li>
+ STL search -- equal_range, find, lower_bound, upper_bound
+ </li><li>
+ Qt removes -- equal_range, key_compare
+ </li><li>
+ Qt renames -- lowerBound, upperBound
+ </li><li>
+ Qt adds -- constFind, insertMulti, key, keys, take, uniqueKeys, unite, values
+ </li><li>
+ Not applicable to map and QMap -- at, back, pop_back, pop_front, push_back, push_front, swap
+ </li><li>
+ Not applicable to QMap -- append, first, last, lastIndexOf, mid, move, prepend, removeAll, removeAt, removeFirst, removeLast, replace, squeeze, takeAt, takeFirst, takeLast
+ </li><li>
+ Not applicable to map -- assign
+ </li></ul>
+
+ Qt QHash. STL extensions provide similar classes, e.g., Microsoft's stdext::hash_set. THey are nearly the same as QMap
+ <ul><li>
+ </li><li>
+ </li><li>
+ Not applicable to Qhash -- lowerBound, unite, upperBound,
+ </li><li>
+ Qt adds -- squeeze
+ </li></ul>
+ </rf:item>
+ <rf:item id="class-api" title="API for Qhull collections" date="Feb 2009" author="bbarber">
+ <ul><li>
+ check... -- Throw error on failure
+ </li><li>
+ try... -- Return false on failure. Do not throw errors.
+ </li><li>
+ ...Temporarily -- lifetime depends on source. e.g., toByteArrayTemporarily
+ </li><li>
+ ...p -- indicates pointer-to.
+ </li><li>
+ end... -- points to one beyond the last available
+ </li><li>
+ private functions -- No syntactic indication. They may become public later on.
+ </li><li>
+ Error messages -- Preceed error messages with the name of the class throwing the error (e.g. "ClassName: ..."). If this is an internal error, use "ClassName inconsistent: ..."
+ </li><li>
+ parameter order -- qhRunId, dimension, coordinates, count.
+ </li><li>
+ toClass -- Convert into a Class object (makes a deep copy)
+ </li><li>
+ qRunId -- Requires Qh installed. Some routines allow 0 for limited info (e.g., operator&lt;&lt;)
+ </li><li>
+ Disable methods in derived classes -- If the default constructor, copy constructor, or copy assignment is disabled, it should be also disabled in derived classes (better error messages).
+ </li><li>
+ Constructor order -- default constructor, other constructors, copy constructor, copy assignment, destructor
+ </li></ul>
+ </rf:item>
+ </rf:section>
+</rf:topic>
diff --git a/xs/src/qhull/html/qhull.htm b/xs/src/qhull/html/qhull.htm
new file mode 100644
index 000000000..0a2aa75e0
--- /dev/null
+++ b/xs/src/qhull/html/qhull.htm
@@ -0,0 +1,473 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+
+<head>
+<title>qhull -- convex hull and related structures</title>
+</head>
+
+<body>
+<!-- Navigation links -->
+<p><b><a name="TOP">Up</a></b><b>:</b> <a href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b> <a href="index.htm#TOC">Qhull manual</a>: Table of Contents<br>
+<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
+&#149; <a href="qh-quick.htm#options">Options</a>
+&#149; <a href="qh-opto.htm#output">Output</a>
+&#149; <a href="qh-optf.htm#format">Formats</a>
+&#149; <a href="qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="qh-optp.htm#print">Print</a>
+&#149; <a href="qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="qh-optc.htm#prec">Precision</a>
+&#149; <a href="qh-optt.htm#trace">Trace</a>
+&#149; <a href="../src/libqhull_r/index.htm">Functions</a><br>
+<b>To:</b> <a href="#synopsis">sy</a>nopsis &#149; <a href="#input">in</a>put
+&#149; <a href="#outputs">ou</a>tputs &#149; <a href="#controls">co</a>ntrols
+&#149; <a href="#options">op</a>tions
+<hr>
+<!-- Main text of document -->
+<h1><a
+href="http://www.geom.uiuc.edu/graphics/pix/Special_Topics/Computational_Geometry/cone.html"><img
+src="qh--cone.gif" alt="[cone]" align="middle" width="100"
+height="100"></a>qhull -- convex hull and related structures</h1>
+
+<p>The convex hull of a set of points is the smallest convex set
+containing the points. The Delaunay triangulation and furthest-site
+Delaunay triangulation are equivalent to a convex hull in one
+higher dimension. Halfspace intersection about a point is
+equivalent to a convex hull by polar duality.
+
+<p>The <tt>qhull</tt> program provides options to build these
+structures and to experiment with the process. Use the
+<a href=qconvex.htm>qconvex</a>,
+<a href=qdelaun.htm>qdelaunay</a>, <a href=qhalf.htm>qhalf</a>,
+and <a href=qvoronoi.htm>qvoronoi</a> programs
+to build specific structures. You may use <tt>qhull</tt> instead.
+It takes the same options and uses the same code.
+<blockquote>
+<dl>
+ <dt><b>Example:</b> rbox 1000 D3 | qhull
+ <a href="qh-optc.htm#Cn">C-1e-4</a>
+ <a href="qh-optf.htm#FO">FO</a>
+ <a href="qh-optt.htm#Ts">Ts</a>
+ </dt>
+ <dd>Compute the 3-d convex hull of 1000 random
+ points.
+ Centrums must be 10^-4 below neighboring
+ hyperplanes. Print the options and precision constants.
+ When done, print statistics. These options may be
+ used with any of the Qhull programs.</dd>
+ <dt>&nbsp;</dt>
+ <dt><b>Example:</b> rbox 1000 D3 | qhull <a href=qhull.htm#outputs>d</a>
+ <a href="qh-optq.htm#Qbb">Qbb</a>
+ <a href="qh-optc.htm#Rn">R1e-4</a>
+ <a href="qh-optq.htm#Q0">Q0</a></dt>
+ <dd>Compute the 3-d Delaunay triangulation of 1000 random
+ points. Randomly perturb all calculations by
+ [0.9999,1.0001]. Do not correct precision problems.
+ This leads to serious precision errors.</dd>
+</dl>
+</blockquote>
+<p>Use the following equivalences when calling <tt>qhull</tt> in 2-d to 4-d (a 3-d
+Delaunay triangulation is a 4-d convex hull):
+<blockquote>
+<ul>
+<li>
+<a href="qconvex.htm">qconvex</a> == qhull
+<li>
+<a href=qdelaun.htm>qdelaunay</a> == qhull d <a href="qh-optq.htm#Qbb">Qbb</a>
+<li>
+<a href=qhalf.htm>qhalf</a> == qhull H
+<li>
+<a href=qvoronoi.htm>qvoronoi</a> == qhull v <a href="qh-optq.htm#Qbb">Qbb</a>
+</ul>
+</blockquote>
+
+<p>Use the following equivalences when calling <tt>qhull</tt> in 5-d and higher (a 4-d
+Delaunay triangulation is a 5-d convex hull):
+<blockquote>
+<ul>
+<li>
+<a href="qconvex.htm">qconvex</a> == qhull <a href="qh-optq.htm#Qx">Qx</a>
+<li>
+<a href=qdelaun.htm>qdelaunay</a> == qhull d <a href="qh-optq.htm#Qbb">Qbb</a> <a href="qh-optq.htm#Qx">Qx</a>
+<li>
+<a href=qhalf.htm>qhalf</a> == qhull H <a href="qh-optq.htm#Qx">Qx</a>
+<li>
+<a href=qvoronoi.htm>qvoronoi</a> == qhull v <a href="qh-optq.htm#Qbb">Qbb</a> <a href="qh-optq.htm#Qx">Qx</a>
+</ul>
+</blockquote>
+
+
+<p>By default, Qhull merges coplanar facets. For example, the convex
+hull of a cube's vertices has six facets.
+
+<p>If you use '<a href="qh-optq.htm#Qt">Qt</a>' (triangulated output),
+all facets will be simplicial (e.g., triangles in 2-d). For the cube
+example, it will have 12 facets. Some facets may be
+degenerate and have zero area.
+
+<p>If you use '<a href="qh-optq.htm#QJn">QJ</a>' (joggled input),
+all facets will be simplicial. The corresponding vertices will be
+slightly perturbed. Joggled input is less accurate that triangulated
+output.See <a
+href="qh-impre.htm#joggle">Merged facets or joggled input</a>. </p>
+
+<p>The output for 4-d convex hulls may be confusing if the convex
+hull contains non-simplicial facets (e.g., a hypercube). See
+<a href=qh-faq.htm#extra>Why
+are there extra points in a 4-d or higher convex hull?</a><br>
+</p>
+
+<p><b>Copyright &copy; 1995-2015 C.B. Barber</b></p>
+
+<hr>
+
+<h3><a href="#TOP">&#187;</a><a name="synopsis">qhull synopsis</a></h3>
+<pre>
+qhull- compute convex hulls and related structures.
+ input (stdin): dimension, n, point coordinates
+ comments start with a non-numeric character
+ halfspace: use dim+1 and put offsets after coefficients
+
+options (qh-quick.htm):
+ d - Delaunay triangulation by lifting points to a paraboloid
+ d Qu - furthest-site Delaunay triangulation (upper convex hull)
+ v - Voronoi diagram as the dual of the Delaunay triangulation
+ v Qu - furthest-site Voronoi diagram
+ H1,1 - Halfspace intersection about [1,1,0,...] via polar duality
+ Qt - triangulated output
+ QJ - joggle input instead of merging facets
+ Tv - verify result: structure, convexity, and point inclusion
+ . - concise list of all options
+ - - one-line description of all options
+
+Output options (subset):
+ s - summary of results (default)
+ i - vertices incident to each facet
+ n - normals with offsets
+ p - vertex coordinates (if 'Qc', includes coplanar points)
+ if 'v', Voronoi vertices
+ Fp - halfspace intersections
+ Fx - extreme points (convex hull vertices)
+ FA - compute total area and volume
+ o - OFF format (if 'v', outputs Voronoi regions)
+ G - Geomview output (2-d, 3-d and 4-d)
+ m - Mathematica output (2-d and 3-d)
+ QVn - print facets that include point n, -n if not
+ TO file- output results to file, may be enclosed in single quotes
+
+examples:
+ rbox c d D2 | qhull Qc s f Fx | more rbox 1000 s | qhull Tv s FA
+ rbox 10 D2 | qhull d QJ s i TO result rbox 10 D2 | qhull v Qbb Qt p
+ rbox 10 D2 | qhull d Qu QJ m rbox 10 D2 | qhull v Qu QJ o
+ rbox c | qhull n rbox c | qhull FV n | qhull H Fp
+ rbox d D12 | qhull QR0 FA rbox c D7 | qhull FA TF1000
+ rbox y 1000 W0 | qhull rbox 10 | qhull v QJ o Fv
+</pre>
+
+<h3><a href="#TOP">&#187;</a><a name="input">qhull input</a></h3>
+<blockquote>
+
+<p>The input data on <tt>stdin</tt> consists of:</p>
+<ul>
+ <li>dimension
+ <li>number of points</li>
+ <li>point coordinates</li>
+</ul>
+
+<p>Use I/O redirection (e.g., qhull &lt; data.txt), a pipe (e.g., rbox 10 | qhull),
+or the '<a href=qh-optt.htm#TI>TI</a>' option (e.g., qhull TI data.txt).
+
+<p>Comments start with a non-numeric character. Error reporting is
+simpler if there is one point per line. Dimension
+and number of points may be reversed. For halfspace intersection,
+an interior point may be prepended (see <a href=qhalf.htm#input>qhalf input</a>).
+
+<p>Here is the input for computing the convex
+hull of the unit cube. The output is the normals, one
+per facet.</p>
+
+<blockquote>
+ <p>rbox c &gt; data </p>
+ <pre>
+3 RBOX c
+8
+ -0.5 -0.5 -0.5
+ -0.5 -0.5 0.5
+ -0.5 0.5 -0.5
+ -0.5 0.5 0.5
+ 0.5 -0.5 -0.5
+ 0.5 -0.5 0.5
+ 0.5 0.5 -0.5
+ 0.5 0.5 0.5
+</pre>
+ <p>qhull s n &lt; data</p>
+ <pre>
+
+Convex hull of 8 points in 3-d:
+
+ Number of vertices: 8
+ Number of facets: 6
+ Number of non-simplicial facets: 6
+
+Statistics for: RBOX c | QHULL s n
+
+ Number of points processed: 8
+ Number of hyperplanes created: 11
+ Number of distance tests for qhull: 35
+ Number of merged facets: 6
+ Number of distance tests for merging: 84
+ CPU seconds to compute hull (after input): 0.081
+
+4
+6
+ 0 0 -1 -0.5
+ 0 -1 0 -0.5
+ 1 0 0 -0.5
+ -1 0 0 -0.5
+ 0 1 0 -0.5
+ 0 0 1 -0.5
+</pre>
+</blockquote>
+
+</blockquote>
+<h3><a href="#TOP">&#187;</a><a name="outputs">qhull outputs</a></h3>
+<blockquote>
+
+<p>These options control the output of qhull. They may be used
+individually or together.</p>
+<blockquote>
+<dl compact>
+ <dt>&nbsp;</dt>
+ <dd><b>General</b></dd>
+ <dt><a>qhull</a></dt>
+ <dd>compute the convex hull of the input points.
+ See <a href=qconvex.htm>qconvex</a>.</dd>
+ <dt><a name="d">qhull d Qbb</a></dt>
+ <dd>compute the Delaunay triangulation by lifting the points
+ to a paraboloid. Use option '<a href="qh-optq.htm#Qbb">Qbb</a>'
+ to scale the paraboloid and improve numeric precision.
+ See <a href=qdelaun.htm>qdelaunay</a>.</dd>
+ <dt><a name="v">qhull v Qbb</a></dt>
+ <dd>compute the Voronoi diagram by computing the Delaunay
+ triangulation. Use option '<a href="qh-optq.htm#Qbb">Qbb</a>'
+ to scale the paraboloid and improve numeric precision.
+ See <a href=qvoronoi.htm>qvoronoi</a>.</dd>
+ <dt><a name="H">qhull H</a></dt>
+ <dd>compute the halfspace intersection about a point via polar
+ duality. The point is below the hyperplane that defines the halfspace.
+ See <a href=qhalf.htm>qhalf</a>.</dd>
+</dl>
+</blockquote>
+
+<p>For a full list of output options see
+<blockquote>
+<ul>
+ <li><a href="qh-opto.htm#output">Output</a> formats</li>
+ <li><a href="qh-optf.htm#format">Additional</a> I/O
+ formats</li>
+ <li><a href="qh-optg.htm#geomview">Geomview</a>
+ output options</li>
+ <li><a href="qh-optp.htm#print">Print</a> options</li>
+</ul>
+</blockquote>
+
+</blockquote>
+<h3><a href="#TOP">&#187;</a><a name="controls">qhull controls</a></h3>
+<blockquote>
+
+<p>For a full list of control options see
+<blockquote>
+<ul>
+ <li><a href="qh-optq.htm#qhull">Qhull</a> control
+ options</li>
+ <li><a href="qh-optc.htm#prec">Precision</a> options</li>
+ <li><a href="qh-optt.htm#trace">Trace</a> options</li>
+</ul>
+</blockquote>
+
+</blockquote>
+<h3><a href="#TOP">&#187;</a><a name="options">qhull options</a></h3>
+
+<pre>
+qhull- compute convex hulls and related structures.
+ http://www.qhull.org
+
+input (stdin):
+ first lines: dimension and number of points (or vice-versa).
+ other lines: point coordinates, best if one point per line
+ comments: start with a non-numeric character
+ halfspaces: use dim plus one and put offset after coefficients.
+ May be preceded by a single interior point ('H').
+
+options:
+ d - Delaunay triangulation by lifting points to a paraboloid
+ d Qu - furthest-site Delaunay triangulation (upper convex hull)
+ v - Voronoi diagram (dual of the Delaunay triangulation)
+ v Qu - furthest-site Voronoi diagram
+ Hn,n,... - halfspace intersection about point [n,n,0,...]
+ Qt - triangulated output
+ QJ - joggle input instead of merging facets
+ Qc - keep coplanar points with nearest facet
+ Qi - keep interior points with nearest facet
+
+Qhull control options:
+ Qbk:n - scale coord k so that low bound is n
+ QBk:n - scale coord k so that upper bound is n (QBk is 0.5)
+ QbB - scale input to unit cube centered at the origin
+ Qbb - scale last coordinate to [0,m] for Delaunay triangulations
+ Qbk:0Bk:0 - remove k-th coordinate from input
+ QJn - randomly joggle input in range [-n,n]
+ QRn - random rotation (n=seed, n=0 time, n=-1 time/no rotate)
+ Qf - partition point to furthest outside facet
+ Qg - only build good facets (needs 'QGn', 'QVn', or 'PdD')
+ Qm - only process points that would increase max_outside
+ Qr - process random outside points instead of furthest ones
+ Qs - search all points for the initial simplex
+ Qu - for 'd' or 'v', compute upper hull without point at-infinity
+ returns furthest-site Delaunay triangulation
+ Qv - test vertex neighbors for convexity
+ Qx - exact pre-merges (skips coplanar and anglomaniacs facets)
+ Qz - add point-at-infinity to Delaunay triangulation
+ QGn - good facet if visible from point n, -n for not visible
+ QVn - good facet if it includes point n, -n if not
+ Q0 - turn off default p remerge with 'C-0'/'Qx'
+ Q1 - sort merges by type instead of angle
+ Q2 - merge all non-convex at once instead of independent sets
+ Q3 - do not merge redundant vertices
+ Q4 - avoid old>new merges
+ Q5 - do not correct outer planes at end of qhull
+ Q6 - do not pre-merge concave or coplanar facets
+ Q7 - depth-first processing instead of breadth-first
+ Q8 - do not process near-inside points
+ Q9 - process furthest of furthest points
+ Q10 - no special processing for narrow distributions
+ Q11 - copy normals and recompute centrums for tricoplanar facets
+ Q12 - do not error on wide merge due to duplicate ridge and nearly coincident points
+
+Towpaths Trace options:
+ T4 - trace at level n, 4=all, 5=mem/gauss, -1= events
+ Tc - check frequently during execution
+ Ts - print statistics
+ Tv - verify result: structure, convexity, and point inclusion
+ Tz - send all output to stdout
+ TFn - report summary when n or more facets created
+ TI file - input data from file, no spaces or single quotes
+ TO file - output results to file, may be enclosed in single quotes
+ TPn - turn on tracing when point n added to hull
+ TMn - turn on tracing at merge n
+ TWn - trace merge facets when width > n
+ TRn - rerun qhull n times. Use with 'QJn'
+ TVn - stop qhull after adding point n, -n for before (see TCn)
+ TCn - stop qhull after building cone for point n (see TVn)
+
+Precision options:
+ Cn - radius of centrum (roundoff added). Merge facets if non-convex
+ An - cosine of maximum angle. Merge facets if cosine > n or non-convex
+ C-0 roundoff, A-0.99/C-0.01 pre-merge, A0.99/C0.01 post-merge
+ En - max roundoff error for distance computation
+ Rn - randomly perturb computations by a factor of [1-n,1+n]
+ Vn - min distance above plane for a visible facet (default 3C-n or En)
+ Un - max distance below plane for a new, coplanar point (default Vn)
+ Wn - min facet width for outside point (before roundoff, default 2Vn)
+
+Output formats (may be combined; if none, produces a summary to stdout):
+ f - facet dump
+ G - Geomview output (see below)
+ i - vertices incident to each facet
+ m - Mathematica output (2-d and 3-d)
+ o - OFF format (dim, points and facets; Voronoi regions)
+ n - normals with offsets
+ p - vertex coordinates or Voronoi vertices (coplanar points if 'Qc')
+ s - summary (stderr)
+
+More formats:
+ Fa - area for each facet
+ FA - compute total area and volume for option 's'
+ Fc - count plus coplanar points for each facet
+ use 'Qc' (default) for coplanar and 'Qi' for interior
+ FC - centrum or Voronoi center for each facet
+ Fd - use cdd format for input (homogeneous with offset first)
+ FD - use cdd format for numeric output (offset first)
+ FF - facet dump without ridges
+ Fi - inner plane for each facet
+ for 'v', separating hyperplanes for bounded Voronoi regions
+ FI - ID of each facet
+ Fm - merge count for each facet (511 max)
+ FM - Maple output (2-d and 3-d)
+ Fn - count plus neighboring facets for each facet
+ FN - count plus neighboring facets for each point
+ Fo - outer plane (or max_outside) for each facet
+ for 'v', separating hyperplanes for unbounded Voronoi regions
+ FO - options and precision constants
+ Fp - dim, count, and intersection coordinates (halfspace only)
+ FP - nearest vertex and distance for each coplanar point
+ FQ - command used for qhull
+ Fs - summary: #int (8), dimension, #points, tot vertices, tot facets,
+ output: #vertices, #facets, #coplanars, #nonsimplicial
+ #real (2), max outer plane, min vertex
+ FS - sizes: #int (0)
+ #real(2) tot area, tot volume
+ Ft - triangulation with centrums for non-simplicial facets (OFF format)
+ Fv - count plus vertices for each facet
+ for 'v', Voronoi diagram as Voronoi vertices for pairs of sites
+ FV - average of vertices (a feasible point for 'H')
+ Fx - extreme points (in order for 2-d)
+
+Geomview options (2-d, 3-d, and 4-d; 2-d Voronoi)
+ Ga - all points as dots
+ Gp - coplanar points and vertices as radii
+ Gv - vertices as spheres
+ Gi - inner planes only
+ Gn - no planes
+ Go - outer planes only
+ Gc - centrums
+ Gh - hyperplane intersections
+ Gr - ridges
+ GDn - drop dimension n in 3-d and 4-d output
+ Gt - for 3-d 'd', transparent outer ridges
+
+Print options:
+ PAn - keep n largest facets by area
+ Pdk:n - drop facet if normal[k] &lt;= n (default 0.0)
+ PDk:n - drop facet if normal[k] >= n
+ Pg - print good facets (needs 'QGn' or 'QVn')
+ PFn - keep facets whose area is at least n
+ PG - print neighbors of good facets
+ PMn - keep n facets with most merges
+ Po - force output. If error, output neighborhood of facet
+ Pp - do not report precision problems
+
+ . - list of all options
+ - - one line descriptions of all options
+</pre>
+
+<!-- Navigation links -->
+<hr>
+
+<p><b>Up:</b> <a href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b> <a href="index.htm#TOC">Qhull manual</a>: Table of Contents<br>
+<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
+&#149; <a href="qh-quick.htm#options">Options</a>
+&#149; <a href="qh-opto.htm#output">Output</a>
+&#149; <a href="qh-optf.htm#format">Formats</a>
+&#149; <a href="qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="qh-optp.htm#print">Print</a>
+&#149; <a href="qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="qh-optc.htm#prec">Precision</a>
+&#149; <a href="qh-optt.htm#trace">Trace</a>
+&#149; <a href="../src/libqhull_r/index.htm">Functions</a><br>
+<b>To:</b> <a href="#synopsis">sy</a>nopsis &#149; <a href="#input">in</a>put
+&#149; <a href="#outputs">ou</a>tputs &#149; <a href="#controls">co</a>ntrols
+&#149; <a href="#options">op</a>tions
+<!-- GC common information -->
+<hr>
+
+<p><a href="http://www.geom.uiuc.edu/"><img src="qh--geom.gif"
+align="middle" width="40" height="40"></a><i>The Geometry Center
+Home Page </i></p>
+
+<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a>
+</a><br>
+Created: Sept. 25, 1995 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p>
+</body>
+</html>
diff --git a/xs/src/qhull/html/qhull.man b/xs/src/qhull/html/qhull.man
new file mode 100644
index 000000000..8d1dc08ac
--- /dev/null
+++ b/xs/src/qhull/html/qhull.man
@@ -0,0 +1,1008 @@
+.\" This is the Unix manual page for qhull, written in nroff, the standard
+.\" manual formatter for Unix systems. To format it, type
+.\"
+.\" nroff -man qhull.man
+.\"
+.\" This will print a formatted copy to standard output. If you want
+.\" to ensure that the output is plain ASCII, free of any control
+.\" characters that nroff uses for underlining etc, pipe the output
+.\" through "col -b":
+.\"
+.\" nroff -man qhull.man | col -b
+.\"
+.\" Warning: a leading quote "'" or dot "." will not format correctly
+.\"
+.TH qhull 1 "2003/12/30" "Geometry Center"
+.SH NAME
+qhull \- convex hull, Delaunay triangulation, Voronoi diagram,
+halfspace intersection about a point, hull volume, facet area
+.SH SYNOPSIS
+.nf
+qhull- compute convex hulls and related structures
+ input (stdin): dimension, #points, point coordinates
+ first comment (non-numeric) is listed in the summary
+ halfspace: use dim plus one with offsets after coefficients
+
+options (qh-quick.htm):
+ d - Delaunay triangulation by lifting points to a paraboloid
+ v - Voronoi diagram via the Delaunay triangulation
+ H1,1 - Halfspace intersection about [1,1,0,...]
+ d Qu - Furthest-site Delaunay triangulation (upper convex hull)
+ v Qu - Furthest-site Voronoi diagram
+ Qt - triangulated output
+ QJ - Joggle the input to avoid precision problems
+ . - concise list of all options
+ - - one-line description of all options
+
+Output options (subset):
+ FA - compute total area and volume
+ Fx - extreme points (convex hull vertices)
+ G - Geomview output (2-d, 3-d and 4-d)
+ Fp - halfspace intersection coordinates
+ m - Mathematica output (2-d and 3-d)
+ n - normals with offsets
+ o - OFF file format (if Voronoi, outputs regions)
+ TO file- output results to file, may be enclosed in single quotes
+ f - print all fields of all facets
+ s - summary of results (default)
+ Tv - verify result: structure, convexity, and point inclusion
+ p - vertex coordinates (centers for Voronoi)
+ i - vertices incident to each facet
+
+example:
+ rbox 1000 s | qhull Tv s FA
+.fi
+
+ - html manual: index.htm
+ - installation: README.txt
+ - see also: COPYING.txt, REGISTER.txt, Changes.txt
+ - WWW: <http://www.qhull.org>
+ - GIT: <git@github.com:qhull/qhull.git>
+ - mirror: <http://www6.uniovi.es/ftp/pub/mirrors/geom.umn.edu/software/ghindex.html>
+ - news: <http://www.qhull.org/news>
+ - Geomview: <http://www.geomview.org>
+ - news group: <news:comp.graphics.algorithms>
+ - FAQ: <http://www.faqs.org/faqs/graphics/algorithms-faq/>
+ - email: qhull@qhull.org
+ - bug reports: qhull_bug@qhull.org
+
+The sections are:
+ - INTRODUCTION
+ - DESCRIPTION, a description of Qhull
+ - IMPRECISION, how Qhull handles imprecision
+ - OPTIONS
+ - Input and output options
+ - Additional input/output formats
+ - Precision options
+ - Geomview options
+ - Print options
+ - Qhull options
+ - Trace options
+ - BUGS
+ - E-MAIL
+ - SEE ALSO
+ - AUTHORS
+ - ACKNOWLEGEMENTS
+
+This man page briefly describes all Qhull options. Please report
+any mismatches with Qhull's html manual (index.htm).
+
+.PP
+.SH INTRODUCTION
+Qhull is a general dimension code for computing convex hulls, Delaunay
+triangulations, Voronoi diagram, furthest\[hy]site Voronoi diagram,
+furthest\[hy]site Delaunay triangulations, and
+halfspace intersections about a point. It implements the Quickhull algorithm for
+computing the convex hull. Qhull handles round\[hy]off errors from floating
+point arithmetic. It can approximate a convex hull.
+
+The program includes options for hull volume, facet area, partial hulls,
+input transformations, randomization, tracing, multiple output formats, and
+execution statistics. The program can be called from within your application.
+You can view the results in 2\[hy]d, 3\[hy]d and 4\[hy]d with Geomview.
+.PP
+.SH DESCRIPTION
+.PP
+The format of input is the following: first line contains the dimension,
+second line contains the number of input points, and point coordinates follow.
+The dimension and number of points can be reversed.
+Comments and line breaks are ignored. A comment starts with a
+non\[hy]numeric character and continues to the end of line. The first comment
+is reported in summaries and statistics.
+Error reporting is
+better if there is one point per line.
+.PP
+The default printout option is a short summary. There are many
+other output formats.
+.PP
+Qhull implements the Quickhull algorithm for convex hull. This algorithm combines
+the 2\[hy]d Quickhull algorithm with the n\[hy]d beneath\[hy]beyond algorithm
+[c.f., Preparata & Shamos '85].
+It is similar to the randomized algorithms of Clarkson and
+others [Clarkson et al. '93]. The main
+advantages of Quickhull are output sensitive performance, reduced
+space requirements, and automatic handling of precision problems.
+.PP
+The data structure produced by Qhull consists of vertices, ridges, and facets.
+A vertex is a point of the input set. A ridge is a set of d vertices
+and two neighboring facets. For example in 3\[hy]d, a ridge is an edge of the
+polyhedron. A facet is a set of ridges, a set of neighboring facets, a set
+of incident vertices, and a hyperplane equation. For simplicial facets, the
+ridges are defined by the vertices and neighboring facets. When Qhull
+merges two facets, it produces a non\[hy]simplicial
+facet. A non\[hy]simplicial facet has more than d neighbors and may share more than
+one ridge with a neighbor.
+.PP
+.SH IMPRECISION
+.PP
+Since Qhull uses floating point arithmetic, roundoff error may occur for each
+calculation. This causes problems
+for most geometric algorithms.
+.PP
+Qhull automatically sets option 'C\-0' in 2\[hy]d, 3\[hy]d, and 4\[hy]d, or
+option 'Qx' in 5\[hy]d and higher. These options handle precision problems
+by merging facets. Alternatively, use option 'QJ' to joggle the
+input.
+.PP
+With 'C\-0', Qhull merges non\[hy]convex
+facets while constructing the hull. The remaining facets are
+clearly convex. With 'Qx', Qhull merges
+coplanar horizon facets, flipped facets, concave facets and
+duplicated ridges. It merges coplanar facets after constructing
+the hull.
+With 'Qx', coplanar points may be missed, but it
+appears to be unlikely.
+.PP
+To guarantee triangular output, joggle the input with option 'QJ'. Facet
+merging will not occur.
+.SH OPTIONS
+.PP
+To get a list of the most important options, execute 'qhull' by itself.
+To get a complete list of options,
+execute 'qhull \-'.
+To get a complete, concise list of options, execute 'qhull .'.
+
+Options can be in any order.
+Capitalized options take an argument (except 'PG' and 'F' options).
+Single letters are used for output formats and precision constants. The
+other options are grouped into menus for other output formats ('F'),
+Geomview output ('G'),
+printing ('P'), Qhull control ('Q'), and tracing ('T').
+.TP
+Main options:
+.TP
+default
+Compute the convex hull of the input points. Report a summary of
+the result.
+.TP
+d
+Compute the Delaunay triangulation by lifting the input points to a
+paraboloid. The 'o' option prints the input points and facets.
+The 'QJ' option guarantees triangular output. The 'Ft'
+option prints a triangulation. It adds points (the centrums) to non\[hy]simplicial
+facets.
+.TP
+v
+Compute the Voronoi diagram from the Delaunay triangulation.
+The 'p' option prints the Voronoi vertices.
+The 'o' option prints the Voronoi vertices and the
+vertices in each Voronoi region. It lists regions in
+site ID order.
+The 'Fv' option prints each ridge of the Voronoi diagram.
+The first or zero'th vertex
+indicates the infinity vertex. Its coordinates are
+qh_INFINITE (\-10.101). It indicates unbounded Voronoi
+regions or degenerate Delaunay triangles.
+.TP
+Hn,n,...
+Compute halfspace intersection about [n,n,0,...].
+The input is a set of halfspaces
+defined in the same format as 'n', 'Fo', and 'Fi'.
+Use 'Fp' to print the intersection points. Use 'Fv'
+to list the intersection points for each halfspace. The
+other output formats display the dual convex hull.
+
+The point [n,n,n,...] is a feasible point for the halfspaces, i.e.,
+a point that is inside all
+of the halfspaces (Hx+b <= 0). The default coordinate value is 0.
+
+The input may start with a feasible point. If so, use 'H' by itself.
+The input starts with a feasible point when the first number is the dimension,
+the second number is "1", and the coordinates complete a line. The 'FV'
+option produces a feasible point for a convex hull.
+.TP
+d Qu
+Compute the furthest\[hy]site Delaunay triangulation from the upper
+convex hull. The 'o' option prints the input points and facets.
+The 'QJ' option guarantees triangular otuput. You can also use 'Ft'
+to triangulate via the centrums of non\[hy]simplicial
+facets.
+.TP
+v Qu
+Compute the furthest\[hy]site Voronoi diagram.
+The 'p' option prints the Voronoi vertices.
+The 'o' option prints the Voronoi vertices and the
+vertices in each Voronoi region.
+The 'Fv' option prints each ridge of the Voronoi diagram.
+The first or zero'th vertex
+indicates the infinity vertex at infinity. Its coordinates are
+qh_INFINITE (\-10.101). It indicates unbounded Voronoi regions
+and degenerate Delaunay triangles.
+.PP
+.TP
+Input/Output options:
+.TP
+f
+Print out all facets and all fields of each facet.
+.TP
+G
+Output the hull in Geomview format. For imprecise hulls,
+Geomview displays the inner and outer hull. Geomview can also
+display points, ridges, vertices, coplanar points, and
+facet intersections. See below for a list of options.
+
+For Delaunay triangulations, 'G' displays the
+corresponding paraboloid. For halfspace intersection, 'G' displays the
+dual polytope.
+.TP
+i
+Output the incident vertices for each facet.
+Qhull prints the number of facets followed by the
+vertices of each facet. One facet is printed per line. The numbers
+are the 0\[hy]relative indices of the corresponding input points.
+The facets
+are oriented.
+
+In 4d and higher,
+Qhull triangulates non\[hy]simplicial facets. Each apex (the first vertex) is
+a created point that corresponds to the facet's centrum. Its index is greater
+than the indices of the input points. Each base
+corresponds to a simplicial ridge between two facets.
+To print the vertices without triangulation, use option 'Fv'.
+.TP
+m
+Output the hull in Mathematica format. Qhull writes a Mathematica file for 2\[hy]d and 3\[hy]d
+convex hulls and for 2\[hy]d Delaunay triangulations. Qhull produces a list of objects
+that you can assign to a variable in Mathematica, for example:
+"list= << <outputfilename> ". If the object is 2\[hy]d, it can be
+visualized by "Show[Graphics[list]] ". For 3\[hy]d objects the command is
+"Show[Graphics3D[list]]".
+.TP
+n
+Output the normal equation for each facet.
+Qhull prints the dimension (plus one), the number of facets,
+and the normals for each facet. The facet's offset follows its
+normal coefficients.
+.TP
+o
+Output the facets in OFF file format.
+Qhull prints the dimension, number of points, number
+of facets, and number of ridges. Then it prints the coordinates of
+the input points and the vertices for each facet. Each facet is on
+a separate line. The first number is the number of vertices. The
+remainder are the indices of the corresponding points. The vertices are
+oriented in 2\[hy]d, 3\[hy]d, and in simplicial facets.
+
+For 2\[hy]d Voronoi diagrams,
+the vertices are sorted by adjacency, but not oriented. In 3\[hy]d and higher,
+the Voronoi vertices are sorted by index.
+See the 'v' option for more information.
+.TP
+p
+Output the coordinates of each vertex point.
+Qhull prints the dimension, the number of points,
+and the coordinates for each vertex.
+With the 'Gc' and 'Gi' options, it also prints coplanar
+and interior points. For Voronoi diagrams, it prints the coordinates
+of each Voronoi vertex.
+.TP
+s
+Print a summary to stderr. If no output options
+are specified at all, a summary goes to stdout. The summary lists
+the number of input points, the dimension, the number of vertices
+in the convex hull, the number of facets in the convex hull, the
+number of good facets (if 'Pg'), and statistics.
+
+The last two statistics (if needed) measure the maximum distance
+from a point or vertex to a
+facet. The number in parenthesis (e.g., 2.1x) is the ratio between the
+maximum distance and the worst\[hy]case distance due to merging
+two simplicial facets.
+.PP
+.TP
+Precision options
+.TP
+An
+Maximum angle given as a cosine. If the angle between a pair of facet
+normals
+is greater than n, Qhull merges one of the facets into a neighbor.
+If 'n' is negative, Qhull tests angles after adding
+each point to the hull (pre\[hy]merging).
+If 'n' is positive, Qhull tests angles after
+constructing the hull (post\[hy]merging).
+Both pre\[hy] and post\[hy]merging can be defined.
+
+Option 'C0' or 'C\-0' is set if the corresponding 'Cn' or 'C\-n'
+is not set. If 'Qx'
+is set, then 'A\-n' and 'C\-n' are checked after the hull is constructed
+and before 'An' and 'Cn' are checked.
+.TP
+Cn
+Centrum radius.
+If a centrum is less than n below a neighboring facet, Qhull merges one
+of the facets.
+If 'n' is negative or '\-0', Qhull tests and merges facets after adding
+each point to the hull. This is called "pre\[hy]merging". If 'n' is positive,
+Qhull tests for convexity after constructing the hull ("post\[hy]merging").
+Both pre\[hy] and post\[hy]merging can be defined.
+
+For 5\[hy]d and higher, 'Qx' should be used
+instead of 'C\-n'. Otherwise, most or all facets may be merged
+together.
+.TP
+En
+Maximum roundoff error for distance computations.
+.TP
+Rn
+Randomly perturb distance computations up to +/\- n * max_coord.
+This option perturbs every distance, hyperplane, and angle computation.
+To use time as the random number seed, use option 'QR\-1'.
+.TP
+Vn
+Minimum distance for a facet to be visible.
+A facet is visible if the distance from the point to the
+facet is greater than 'Vn'.
+
+Without merging, the default value for 'Vn' is the round\[hy]off error ('En').
+With merging, the default value is the pre\[hy]merge centrum ('C\-n') in 2\[hy]d or
+3\[hy]d, or three times that in other dimensions. If the outside width
+is specified ('Wn'), the maximum, default value for 'Vn' is 'Wn'.
+.TP
+Un
+Maximum distance below a facet for a point to be coplanar to the facet. The
+default value is 'Vn'.
+.TP
+Wn
+Minimum outside width of the hull. Points are added to the convex hull
+only if they are clearly outside of a facet. A point is outside of a
+facet if its distance to the facet is greater than 'Wn'. The normal
+value for 'Wn' is 'En'. If the user specifies pre\[hy]merging and
+does not set 'Wn', than 'Wn' is set
+to the premerge 'Cn' and maxcoord*(1\-An).
+.PP
+.TP
+Additional input/output formats
+.TP
+Fa
+Print area for each facet.
+For Delaunay triangulations, the area is the area of the triangle.
+For Voronoi diagrams, the area is the area of the dual facet.
+Use 'PAn' for printing the n largest facets, and option 'PFn' for
+printing facets larger than 'n'.
+
+The area for non\[hy]simplicial facets is the sum of the
+areas for each ridge to the centrum. Vertices far below
+the facet's hyperplane are ignored.
+The reported area may be significantly less than the actual area.
+.TP
+FA
+Compute the total area and volume for option 's'. It is an approximation
+for non\[hy]simplicial facets (see 'Fa').
+.TP
+Fc
+Print coplanar points for each facet. The output starts with the
+number of facets. Then each facet is printed one per line. Each line
+is the number of coplanar points followed by the point ids.
+Option 'Qi' includes the interior points. Each coplanar point (interior point) is
+assigned to the facet it is furthest above (resp., least below).
+.TP
+FC
+Print centrums for each facet. The output starts with the
+dimension followed by the number of facets.
+Then each facet centrum is printed, one per line.
+.TP
+Fd
+Read input in cdd format with homogeneous points.
+The input starts with comments. The first comment is reported in
+the summary.
+Data starts after a "begin" line. The next line is the number of points
+followed by the dimension+1 and "real" or "integer". Then the points
+are listed with a leading "1" or "1.0". The data ends with an "end" line.
+
+For halfspaces ('Fd Hn,n,...'), the input format is the same. Each halfspace
+starts with its offset. The sign of the offset is the opposite of Qhull's
+convention.
+.TP
+FD
+Print normals ('n', 'Fo', 'Fi') or points ('p') in cdd format.
+The first line is the command line that invoked Qhull.
+Data starts with a "begin" line. The next line is the number of normals or points
+followed by the dimension+1 and "real". Then the normals or points
+are listed with the offset before the coefficients. The offset for points is
+1.0. The offset for normals has the opposite sign.
+The data ends with an "end" line.
+.TP
+FF
+Print facets (as in 'f') without printing the ridges.
+.TP
+Fi
+Print inner planes for each facet. The inner plane is below all vertices.
+.TP
+Fi
+Print separating hyperplanes for bounded, inner regions of the Voronoi
+diagram. The first line is the number
+of ridges. Then each hyperplane is printed, one per line. A line starts
+with the number of indices and floats. The first pair lists
+adjacent input
+sites, the next d floats are the normalized coefficients for the hyperplane,
+and the last float is the offset. The hyperplane is oriented toward 'QVn'
+(if defined), or the first input site of the pair. Use 'Tv' to
+verify that the hyperplanes are perpendicular bisectors. Use 'Fo' for
+unbounded regions, and 'Fv' for the corresponding Voronoi vertices.
+.TP
+FI
+Print facet identifiers.
+.TP
+Fm
+Print number of merges for each facet. At most 511 merges are reported for
+a facet. See 'PMn' for printing the facets with the most merges.
+.TP
+FM
+Output the hull in Maple format. Qhull writes a Maple
+file for 2\[hy]d and 3\[hy]d
+convex hulls and for 2\[hy]d Delaunay triangulations. Qhull produces a '.mpl'
+file for displaying with display3d().
+.TP
+Fn
+Print neighbors for each facet. The output starts with the number of facets.
+Then each facet is printed one per line. Each line
+is the number of neighbors followed by an index for each neighbor. The indices
+match the other facet output formats.
+
+A negative index indicates an unprinted
+facet due to printing only good facets ('Pg'). It is the negation of the facet's
+ID (option 'FI').
+For example, negative indices are used for facets
+"at infinity" in the Delaunay triangulation.
+.TP
+FN
+Print vertex neighbors or coplanar facet for each point.
+The first line is the number
+of points. Then each point is printed, one per line. If the
+point is coplanar, the line is "1" followed by the facet's ID.
+If the point is
+not a selected vertex, the line is "0".
+Otherwise, each line is the number of
+neighbors followed by the corresponding facet indices (see 'Fn').
+.TP
+Fo
+Print outer planes for each facet in the same format as 'n'.
+The outer plane is above all points.
+.TP
+Fo
+Print separating hyperplanes for unbounded, outer regions of the Voronoi
+diagram. The first line is the number
+of ridges. Then each hyperplane is printed, one per line. A line starts
+with the number of indices and floats. The first pair lists
+adjacent input
+sites, the next d floats are the normalized coefficients for the hyperplane,
+and the last float is the offset. The hyperplane is oriented toward 'QVn'
+(if defined), or the first input site of the pair. Use 'Tv' to
+verify that the hyperplanes are perpendicular bisectors. Use 'Fi' for
+bounded regions, and 'Fv' for the corresponding Voronoi vertices.
+.TP
+FO
+List all options to stderr, including the default values. Additional 'FO's
+are printed to stdout.
+.TP
+Fp
+Print points for halfspace intersections (option 'Hn,n,...'). Each
+intersection corresponds to a facet of the dual polytope.
+The "infinity" point [\-10.101,\-10.101,...]
+indicates an unbounded intersection.
+.TP
+FP
+For each coplanar point ('Qc') print the point ID of the nearest vertex,
+the point ID, the facet ID, and the distance.
+.TP
+FQ
+Print command used for qhull and input.
+.TP
+Fs
+Print a summary. The first line consists of the number of integers ("8"),
+followed by the dimension, the number of points, the number of vertices,
+the number of facets, the number of vertices selected for output, the
+number of facets selected for output, the number of coplanar points selected
+for output, number of simplicial, unmerged facets in output
+
+The second line consists of the number of reals ("2"),
+followed by the maxmimum offset to an outer plane and and minimum offset to
+an inner plane. Roundoff is included. Later
+versions of Qhull may produce additional integers or reals.
+.TP
+FS
+Print the size of the hull. The first line consists of the number of integers ("0").
+The second line consists of the number of reals ("2"),
+followed by the total facet area, and the total volume.
+Later
+versions of Qhull may produce additional integers or reals.
+
+The total volume measures the volume
+of the intersection of the halfspaces defined by each facet.
+Both area and volume are
+approximations for non\[hy]simplicial facets. See option 'Fa'.
+.TP
+Ft
+Print a triangulation with added points for non\[hy]simplicial
+facets. The first line is the dimension and the second line is the
+number of points and the number of facets. The points follow, one
+per line, then the facets follow as a list of point indices. With option 'Qz', the
+points include the point\[hy]at\[hy]infinity.
+.TP
+Fv
+Print vertices for each facet. The first line is the number
+of facets. Then each facet is printed, one per line. Each line is
+the number of vertices followed by the corresponding point ids. Vertices
+are listed in the order they were added to the hull (the last one is first).
+.TP
+Fv
+Print all ridges of a Voronoi diagram. The first line is the number
+of ridges. Then each ridge is printed, one per line. A line starts
+with the number of indices. The first pair lists adjacent input
+sites, the remaining indices list Voronoi vertices. Vertex '0' indicates
+the vertex\[hy]at\[hy]infinity (i.e., an unbounded ray). In 3\[hy]d, the vertices
+are listed in order. See 'Fi' and 'Fo' for separating hyperplanes.
+.TP
+FV
+Print average vertex. The average vertex is a feasible point
+for halfspace intersection.
+.TP
+Fx
+List extreme points (vertices) of the convex hull. The first line
+is the number of points. The other lines give the indices of the
+corresponding points. The first point is '0'. In 2\[hy]d, the points
+occur in counter\[hy]clockwise order; otherwise they occur in input order.
+For Delaunay triangulations, 'Fx' lists the extreme points of the
+input sites. The points are unordered.
+.PP
+.TP
+Geomview options
+.TP
+G
+Produce a file for viewing with Geomview. Without other options,
+Qhull displays edges in 2\[hy]d, outer planes in 3\[hy]d, and ridges in 4\[hy]d.
+A ridge can be
+explicit or implicit. An explicit ridge is a dim\-1 dimensional simplex
+between two facets.
+In 4\[hy]d, the explicit ridges are triangles.
+When displaying a ridge in 4\[hy]d, Qhull projects the ridge's vertices to
+one of its facets' hyperplanes.
+Use 'Gh' to
+project ridges to the intersection of both hyperplanes.
+.TP
+Ga
+Display all input points as dots.
+.TP
+Gc
+Display the centrum for each facet in 3\[hy]d. The centrum is defined by a
+green radius sitting on a blue plane. The plane corresponds to the
+facet's hyperplane.
+The radius is defined by 'C\-n' or 'Cn'.
+.TP
+GDn
+Drop dimension n in 3\[hy]d or 4\[hy]d. The result is a 2\[hy]d or 3\[hy]d object.
+.TP
+Gh
+Display hyperplane intersections in 3\[hy]d and 4\[hy]d. In 3\[hy]d, the
+intersection is a black line. It lies on two neighboring hyperplanes
+(c.f., the blue squares associated with centrums ('Gc')). In 4\[hy]d,
+the ridges are projected to the intersection of both hyperplanes.
+.TP
+Gi
+Display inner planes in 2\[hy]d and 3\[hy]d. The inner plane of a facet
+is below all of its vertices. It is parallel to the facet's hyperplane.
+The inner plane's color is the opposite (1\-r,1\-g,1\-b) of the outer
+plane. Its edges are determined by the vertices.
+.TP
+Gn
+Do not display inner or outer planes. By default,
+Geomview displays the precise plane (no merging) or both
+inner and output planes (merging). Under merging, Geomview does
+not display the inner plane if the
+the difference between inner and outer is too small.
+.TP
+Go
+Display outer planes in 2\[hy]d and 3\[hy]d. The outer plane of a facet
+is above all input points. It is parallel to the facet's hyperplane.
+Its color is determined by the facet's normal, and its
+edges are determined by the vertices.
+.TP
+Gp
+Display coplanar points and vertices as radii. A radius defines a ball
+which corresponds to the imprecision of the point. The imprecision is
+the maximum of the roundoff error, the centrum radius, and maxcoord *
+(1\-An). It is at least 1/20'th of the maximum coordinate,
+and ignores post\[hy]merging if pre\[hy]merging is done.
+.TP
+Gr
+Display ridges in 3\[hy]d. A ridge connects the two vertices that are shared
+by neighboring facets. Ridges are always displayed in 4\[hy]d.
+.TP
+Gt
+A 3\[hy]d Delaunay triangulation looks like a convex hull with interior
+facets. Option 'Gt' removes the outside ridges to reveal the outermost
+facets. It automatically sets options 'Gr' and 'GDn'.
+.TP
+Gv
+Display vertices as spheres. The radius of the sphere corresponds to
+the imprecision of the data. See 'Gp' for determining the radius.
+.PP
+.TP
+Print options
+.TP
+PAn
+Only the n largest facets are marked good for printing.
+Unless 'PG' is set, 'Pg' is automatically set.
+.TP
+Pdk:n
+Drop facet from output if normal[k] <= n. The option 'Pdk' uses the
+default value of 0 for n.
+.TP
+PDk:n
+Drop facet from output if normal[k] >= n. The option 'PDk' uses the
+default value of 0 for n.
+.TP
+PFn
+Only facets with area at least 'n' are marked good for printing.
+Unless 'PG' is set, 'Pg' is automatically set.
+.TP
+Pg
+Print only good facets. A good facet is either visible from a point
+(the 'QGn' option) or includes a point (the 'QVn' option). It also meets the
+requirements of 'Pdk' and 'PDk' options. Option 'Pg' is automatically
+set for options 'PAn' and 'PFn'.
+.TP
+PG
+Print neighbors of good facets.
+.TP
+PMn
+Only the n facets with the most merges are marked good for printing.
+Unless 'PG' is set, 'Pg' is automatically set.
+.TP
+Po
+Force output despite precision problems. Verify ('Tv') does not check
+coplanar points.
+Flipped facets are reported and concave facets are counted.
+If 'Po' is used, points are not
+partitioned into flipped facets and a flipped facet is always visible
+to a point.
+Also, if an error occurs before the completion of Qhull and tracing is
+not active, 'Po' outputs a neighborhood of the erroneous facets
+(if any).
+.TP
+Pp
+Do not report precision problems.
+.PP
+.TP
+Qhull control options
+.TP
+Qbk:0Bk:0
+Drop dimension k from the input points. This allows the user to
+take convex hulls of sub\[hy]dimensional objects. It happens before
+the Delaunay and Voronoi transformation.
+.TP
+QbB
+Scale the input points to fit the unit cube. After scaling, the lower
+bound will be \-0.5 and the upper bound +0.5 in all dimensions.
+For Delaunay and
+Voronoi diagrams, scaling happens after projection to the paraboloid.
+Under precise
+arithmetic, scaling does not change the topology of the convex hull.
+.TP
+Qbb
+Scale the last coordinate to [0, m] where m is the maximum absolute
+value of the other coordinates. For Delaunay and
+Voronoi diagrams, scaling happens after projection to the paraboloid.
+It reduces roundoff error for inputs with integer coordinates.
+Under precise
+arithmetic, scaling does not change the topology of the convex hull.
+.TP
+Qbk:n
+Scale the k'th coordinate of the input points. After scaling, the lower
+bound of the input points will be n. 'Qbk' scales to \-0.5.
+.TP
+QBk:n
+Scale the k'th coordinate of the input points. After scaling, the upper
+bound will be n. 'QBk' scales to +0.5.
+.TP
+Qc
+Keep coplanar points with the nearest facet. Output
+formats 'p', 'f', 'Gp', 'Fc', 'FN', and 'FP' will print the points.
+.TP
+Qf
+Partition points to the furthest outside facet.
+.TP
+Qg
+Only build good facets. With the 'Qg' option, Qhull will only build
+those facets that it needs to determine the good facets in the output.
+See 'QGn', 'QVn', and 'PdD' for defining good facets, and 'Pg' and 'PG'
+for printing good facets and their neighbors.
+.TP
+QGn
+A facet is good (see 'Qg' and 'Pg') if it is visible from point n. If n < 0, a facet is
+good if it is not visible from point n. Point n is not added to the
+hull (unless 'TCn' or 'TPn').
+With rbox, use the 'Pn,m,r' option to define your point; it
+will be point 0 (QG0).
+.TP
+Qi
+Keep interior points with the nearest facet.
+Output formats 'p', 'f', 'Gp', 'FN', 'FP', and 'Fc' will print the points.
+.TP
+QJn
+Joggle each input coordinate by adding a random number in [\-n,n]. If a
+precision error occurs, then qhull increases n and tries again. It does
+not increase n beyond a certain value, and it stops after a certain number
+of attempts [see user.h]. Option 'QJ'
+selects a default value for n. The output will be simplicial. For
+Delaunay triangulations, 'QJn' sets 'Qbb' to scale the last coordinate
+(not if 'Qbk:n' or 'QBk:n' is set).
+\'QJn' is deprecated for Voronoi diagrams. See also 'Qt'.
+.TP
+Qm
+Only process points that would otherwise increase max_outside. Other
+points are treated as coplanar or interior points.
+.TP
+Qr
+Process random outside points instead of furthest ones. This makes
+Qhull equivalent to the randomized incremental algorithms. CPU time
+is not reported since the randomization is inefficient.
+.TP
+QRn
+Randomly rotate the input points. If n=0, use time as the random number seed.
+If n>0, use n as the random number seed. If n=\-1, don't rotate but use
+time as the random number seed. For Delaunay triangulations ('d' and 'v'),
+rotate about the last axis.
+.TP
+Qs
+Search all points for the initial simplex.
+.TP
+Qt
+Triangulated output. Triangulate all non\[hy]simplicial facets.
+\'Qt' is deprecated for Voronoi diagrams. See also 'Qt'.
+.TP
+Qv
+Test vertex neighbors for convexity after post\[hy]merging.
+To use the 'Qv' option, you also need to set a merge option
+(e.g., 'Qx' or 'C\-0').
+.TP
+QVn
+A good facet (see 'Qg' and 'Pg') includes point n. If n<0, then a good facet does not
+include point n. The point is either in the initial simplex or it
+is the first point added to the hull. Option 'QVn' may not be used with merging.
+.TP
+Qx
+Perform exact merges while building the hull. The "exact" merges
+are merging a point into a coplanar facet (defined by 'Vn', 'Un',
+and 'C\-n'), merging concave facets, merging duplicate ridges, and
+merging flipped facets. Coplanar merges and angle coplanar merges ('A\-n')
+are not performed. Concavity testing is delayed until a merge occurs.
+
+After
+the hull is built, all coplanar merges are performed (defined by 'C\-n'
+and 'A\-n'), then post\[hy]merges are performed
+(defined by 'Cn' and 'An').
+.TP
+Qz
+Add a point "at infinity" that is above the paraboloid for Delaunay triangulations
+and Voronoi diagrams. This reduces precision problems and allows the triangulation
+of cospherical points.
+.PP
+.TP
+Qhull experiments and speedups
+.TP
+Q0
+Turn off pre\[hy]merging as a default option.
+With 'Q0'/'Qx' and without explicit pre\[hy]merge options, Qhull
+ignores precision issues while constructing the convex hull. This
+may lead to precision errors. If so, a descriptive warning is
+generated.
+.TP
+Q1
+With 'Q1', Qhull sorts merges by type (coplanar, angle coplanar, concave)
+instead of by angle.
+.TP
+Q2
+With 'Q2', Qhull merges all facets at once instead of using
+independent sets of merges and then retesting.
+.TP
+Q3
+With 'Q3', Qhull does not remove redundant vertices.
+.TP
+Q4
+With 'Q4', Qhull avoids merges of an old facet into a new facet.
+.TP
+Q5
+With 'Q5', Qhull does not correct outer planes at the end. The
+maximum outer plane is used instead.
+.TP
+Q6
+With 'Q6', Qhull does not pre\[hy]merge concave or coplanar facets.
+.TP
+Q7
+With 'Q7', Qhull processes facets in depth\[hy]first order instead of
+breadth\[hy]first order.
+.TP
+Q8
+With 'Q8' and merging, Qhull does not retain near\[hy]interior points for adjusting
+outer planes. 'Qc' will probably retain
+all points that adjust outer planes.
+.TP
+Q9
+With 'Q9', Qhull processes the furthest of all outside sets at each iteration.
+.TP
+Q10
+With 'Q10', Qhull does not use special processing for narrow distributions.
+.TP
+Q11
+With 'Q11', Qhull copies normals and recompute centrums for tricoplanar facets.
+.TP
+Q12
+With 'Q12', Qhull does not report a very wide merge due to a duplicated ridge with nearly coincident vertices
+.PP
+.TP
+Trace options
+.TP
+Tn
+Trace at level n. Qhull includes full execution tracing. 'T\-1'
+traces events. 'T1' traces
+the overall execution of the program. 'T2' and 'T3' trace overall
+execution and geometric and topological events. 'T4' traces the
+algorithm. 'T5' includes information about memory allocation and
+Gaussian elimination.
+.TP
+Ta
+Annotate output with codes that identify the
+corresponding qh_fprintf() statement.
+.TP
+Tc
+Check frequently during execution. This will catch most inconsistency
+errors.
+.TP
+TCn
+Stop Qhull after building the cone of new facets for point n. The
+output for 'f' includes the cone and the old hull.
+See also 'TVn'.
+.TP
+TFn
+Report progress whenever more than n facets are created
+During post\[hy]merging, 'TFn'
+reports progress after more than n/2 merges.
+.TP
+TI file
+Input data from 'file'. The filename may not include spaces or
+quotes.
+.TP
+TO file
+Output results to 'file'. The name may be enclosed in single
+quotes.
+.TP
+TPn
+Turn on tracing when point n is added to the hull. Trace
+partitions of point n. If used with TWn, turn off
+tracing after adding point n to the hull.
+.TP
+TRn
+Rerun qhull n times. Usually used with 'QJn' to determine the
+probability that a given joggle will fail.
+.TP
+Ts
+Collect statistics and print to stderr at the end of execution.
+.TP
+Tv
+Verify the convex hull. This checks the topological structure, facet
+convexity, and point inclusion.
+If precision problems occurred, facet convexity is tested whether or
+not 'Tv' is selected.
+Option 'Tv' does not check point inclusion if forcing output with 'Po',
+or if 'Q5' is set.
+
+For point inclusion testing, Qhull verifies that all points are below
+all outer planes (facet\->maxoutside). Point inclusion is exhaustive
+if merging or if the facet\[hy]point product is small enough;
+otherwise Qhull verifies each point with a directed
+search (qh_findbest).
+
+Point inclusion testing occurs after producing output. It prints
+a message to stderr unless option 'Pp' is used. This
+allows the user to interrupt Qhull without changing the output.
+.TP
+TVn
+Stop Qhull after adding point n. If n < 0, stop Qhull before adding
+point n. Output shows the hull at this time. See also 'TCn'
+.TP
+TMn
+Turn on tracing at n'th merge.
+.TP
+TWn
+Trace merge facets when the width is greater than n.
+.TP
+Tz
+Redirect stderr to stdout.
+.PP
+.SH BUGS
+Please report bugs to Brad Barber at qhull_bug@qhull.org.
+
+If Qhull does not compile, it is due to an incompatibility between your
+system and ours. The first thing to check is that your compiler is
+ANSI standard. If it is, check the man page for the best options, or
+find someone to help you. If you locate the cause of your problem,
+please send email since it might help others.
+
+If Qhull compiles but crashes on the test case (rbox D4), there's
+still incompatibility between your system and ours. Typically it's
+been due to mem.c and memory alignment. You can use qh_NOmem in mem.h
+to turn off memory management. Please let us know if you figure out
+how to fix these problems.
+
+If you do find a problem, try to simplify it before reporting the
+error. Try different size inputs to locate the smallest one that
+causes an error. You're welcome to hunt through the code using the
+execution trace as a guide. This is especially true if you're
+incorporating Qhull into your own program.
+
+When you do report an error, please attach a data set to the
+end of your message. This allows us to see the error for ourselves.
+Qhull is maintained part\[hy]time.
+.PP
+.SH E\[hy]MAIL
+Please send correspondence to qhull@qhull.org and report bugs to
+qhull_bug@qhull.org. Let us know how you use Qhull. If you
+mention it in a paper, please send the reference and an abstract.
+
+If you would like to get Qhull announcements (e.g., a new version)
+and news (any bugs that get fixed, etc.), let us know and we will add you to
+our mailing list. If you would like to communicate with other
+Qhull users, we will add you to the qhull_users alias.
+For Internet news about geometric algorithms and convex hulls, look at
+comp.graphics.algorithms and sci.math.num\-analysis
+
+.SH SEE ALSO
+rbox(1)
+
+Barber, C. B., D.P. Dobkin, and H.T. Huhdanpaa,
+"The Quickhull Algorithm for Convex Hulls," ACM
+Trans. on Mathematical Software, 22(4):469\[en]483, Dec. 1996.
+http://portal.acm.org/citation.cfm?doid=235815.235821
+http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.117.405
+
+Clarkson, K.L., K. Mehlhorn, and R. Seidel, "Four results on randomized
+incremental construction," Computational Geometry: Theory and Applications,
+vol. 3, p. 185\[en]211, 1993.
+
+Preparata, F. and M. Shamos, Computational
+Geometry, Springer\[hy]Verlag, New York, 1985.
+
+.PP
+.SH AUTHORS
+.nf
+ C. Bradford Barber Hannu Huhdanpaa
+ bradb@shore.net hannu@qhull.org
+
+ .fi
+
+.SH ACKNOWLEDGEMENTS
+
+A special thanks to Albert Marden, Victor Milenkovic, the Geometry Center,
+Harvard University, and Endocardial Solutions, Inc. for supporting this work.
+
+Qhull 1.0 and 2.0 were developed under National Science Foundation
+grants NSF/DMS\[hy]8920161 and NSF\[hy]CCR\[hy]91\[hy]15793 750\[hy]7504. David Dobkin
+guided the original work at Princeton University.
+If you find it useful, please let us know.
+
+The Geometry Center is supported by grant DMS\[hy]8920161 from the National
+Science Foundation, by grant DOE/DE\[hy]FG02\[hy]92ER25137 from the Department
+of Energy, by the University of Minnesota, and by Minnesota Technology, Inc.
+
+Qhull is available from http://www.qhull.org
diff --git a/xs/src/qhull/html/qhull.txt b/xs/src/qhull/html/qhull.txt
new file mode 100644
index 000000000..03753547e
--- /dev/null
+++ b/xs/src/qhull/html/qhull.txt
@@ -0,0 +1,1263 @@
+
+
+
+qhull(1) qhull(1)
+
+
+NAME
+ qhull - convex hull, Delaunay triangulation, Voronoi dia-
+ gram, halfspace intersection about a point, hull volume, facet area
+
+SYNOPSIS
+ qhull- compute convex hulls and related structures
+ input (stdin): dimension, #points, point coordinates
+ first comment (non-numeric) is listed in the summary
+ halfspace: use dim plus one with offsets after coefficients
+
+ options (qh-quick.htm):
+ d - Delaunay triangulation by lifting points to a paraboloid
+ v - Voronoi diagram via the Delaunay triangulation
+ H1,1 - Halfspace intersection about [1,1,0,...]
+ d Qu - Furthest-site Delaunay triangulation (upper convex hull)
+ v Qu - Furthest-site Voronoi diagram
+ QJ - Joggle the input to avoid precision problems
+ . - concise list of all options
+ - - one-line description of all options
+
+ Output options (subset):
+ FA - compute total area and volume
+ Fx - extreme points (convex hull vertices)
+ G - Geomview output (2-d, 3-d and 4-d)
+ Fp - halfspace intersection coordinates
+ m - Mathematica output (2-d and 3-d)
+ n - normals with offsets
+ o - OFF file format (if Voronoi, outputs regions)
+ TO file- output results to file, may be enclosed in single quotes
+ f - print all fields of all facets
+ s - summary of results (default)
+ Tv - verify result: structure, convexity, and point inclusion
+ p - vertex coordinates
+ i - vertices incident to each facet
+
+ example:
+ rbox 1000 s | qhull Tv s FA
+
+ - html manual: index.htm
+ - installation: README.txt
+ - see also: COPYING.txt, REGISTER.txt, Changes.txt
+ - WWW: <http://www.qhull.org>
+ - GIT: <git@github.com:qhull/qhull.git>
+ - mirror: <http://www6.uniovi.es/ftp/pub/mirrors/geom.umn.edu/software/ghindex.html>
+ - news: <http://www.qhull.org/news>
+ - Geomview: <http://www.geomview.org>
+ - news group: <news:comp.graphics.algorithms>
+ - FAQ: <http://www.faqs.org/faqs/graphics/algorithms-faq/>
+ - email: qhull@qhull.org
+ - bug reports: qhull_bug@qhull.org
+
+
+
+
+Geometry Center 2003/12/30 1
+
+
+
+
+
+qhull(1) qhull(1)
+
+
+ The sections are:
+ - INTRODUCTION
+ - DESCRIPTION, a description of Qhull
+ - IMPRECISION, how Qhull handles imprecision
+ - OPTIONS
+ - Input and output options
+ - Additional input/output formats
+ - Precision options
+ - Geomview options
+ - Print options
+ - Qhull options
+ - Trace options
+ - BUGS
+ - E-MAIL
+ - SEE ALSO
+ - AUTHORS
+ - ACKNOWLEGEMENTS
+
+ This man page briefly describes all Qhull options. Please
+ report any mismatches with Qhull's html manual (qh-
+ man.htm).
+
+
+
+INTRODUCTION
+ Qhull is a general dimension code for computing convex
+ hulls, Delaunay triangulations, Voronoi diagram, furthest-
+ site Voronoi diagram, furthest-site Delaunay triangula-
+ tions, and halfspace intersections about a point. It
+ implements the Quickhull algorithm for computing the con-
+ vex hull. Qhull handles round-off errors from floating
+ point arithmetic. It can approximate a convex hull.
+
+ The program includes options for hull volume, facet area,
+ partial hulls, input transformations, randomization, trac-
+ ing, multiple output formats, and execution statistics.
+ The program can be called from within your application.
+ You can view the results in 2-d, 3-d and 4-d with
+ Geomview.
+
+
+DESCRIPTION
+ The format of input is the following: first line contains
+ the dimension, second line contains the number of input
+ points, and point coordinates follow. The dimension and
+ number of points can be reversed. Comments and line
+ breaks are ignored. A comment starts with a non-numeric
+ character and continues to the end of line. The first
+ comment is reported in summaries and statistics. Error
+ reporting is better if there is one point per line.
+
+ The default printout option is a short summary. There are
+ many other output formats.
+
+
+
+
+Geometry Center 2003/12/30 2
+
+
+
+
+
+qhull(1) qhull(1)
+
+
+ Qhull implements the Quickhull algorithm for convex hull.
+ This algorithm combines the 2-d Quickhull algorithm with
+ the n-d beneath-beyond algorithm [c.f., Preparata & Shamos
+ '85]. It is similar to the randomized algorithms of
+ Clarkson and others [Clarkson et al. '93]. The main
+ advantages of Quickhull are output sensitive performance,
+ reduced space requirements, and automatic handling of pre-
+ cision problems.
+
+ The data structure produced by Qhull consists of vertices,
+ ridges, and facets. A vertex is a point of the input set.
+ A ridge is a set of d vertices and two neighboring facets.
+ For example in 3-d, a ridge is an edge of the polyhedron.
+ A facet is a set of ridges, a set of neighboring facets, a
+ set of incident vertices, and a hyperplane equation. For
+ simplicial facets, the ridges are defined by the vertices
+ and neighboring facets. When Qhull merges two facets, it
+ produces a non-simplicial facet. A non-simplicial facet
+ has more than d neighbors and may share more than one
+ ridge with a neighbor.
+
+
+IMPRECISION
+ Since Qhull uses floating point arithmetic, roundoff error
+ may occur for each calculation. This causes problems for
+ most geometric algorithms.
+
+ Qhull automatically sets option 'C-0' in 2-d, 3-d, and
+ 4-d, or option 'Qx' in 5-d and higher. These options han-
+ dle precision problems by merging facets. Alternatively,
+ use option 'QJ' to joggle the input.
+
+ With 'C-0', Qhull merges non-convex facets while con-
+ structing the hull. The remaining facets are clearly con-
+ vex. With 'Qx', Qhull merges coplanar horizon facets,
+ flipped facets, concave facets and duplicated ridges. It
+ merges coplanar facets after constructing the hull. With
+ 'Qx', coplanar points may be missed, but it appears to be
+ unlikely.
+
+ To guarantee triangular output, joggle the input with
+ option 'QJ'. Facet merging will not occur.
+
+OPTIONS
+ To get a list of the most important options, execute
+ 'qhull' by itself. To get a complete list of options,
+ execute 'qhull -'. To get a complete, concise list of
+ options, execute 'qhull .'.
+
+ Options can be in any order. Capitalized options take an
+ argument (except 'PG' and 'F' options). Single letters
+ are used for output formats and precision constants. The
+ other options are grouped into menus for other output for-
+ mats ('F'), Geomview output ('G'), printing ('P'), Qhull
+
+
+
+Geometry Center 2003/12/30 3
+
+
+
+
+
+qhull(1) qhull(1)
+
+
+ control ('Q'), and tracing ('T').
+
+ Main options:
+
+ default
+ Compute the convex hull of the input points.
+ Report a summary of the result.
+
+ d Compute the Delaunay triangulation by lifting the
+ input points to a paraboloid. The 'o' option
+ prints the input points and facets. The 'QJ'
+ option guarantees triangular output. The 'Ft'
+ option prints a triangulation. It adds points (the
+ centrums) to non-simplicial facets.
+
+ v Compute the Voronoi diagram from the Delaunay tri-
+ angulation. The 'p' option prints the Voronoi ver-
+ tices. The 'o' option prints the Voronoi vertices
+ and the vertices in each Voronoi region. It lists
+ regions in site id order. The 'Fv' option prints
+ each ridge of the Voronoi diagram. The first or
+ zero'th vertex indicates the infinity vertex. Its
+ coordinates are qh_INFINITE (-10.101). It indi-
+ cates unbounded Voronoi regions or degenerate
+ Delaunay triangles.
+
+ Hn,n,...
+ Compute halfspace intersection about [n,n,0,...].
+ The input is a set of halfspaces defined in the
+ same format as 'n', 'Fo', and 'Fi'. Use 'Fp' to
+ print the intersection points. Use 'Fv' to list
+ the intersection points for each halfspace. The
+ other output formats display the dual convex hull.
+
+ The point [n,n,n,...] is a feasible point for the
+ halfspaces, i.e., a point that is inside all of the
+ halfspaces (Hx+b <= 0). The default coordinate
+ value is 0.
+
+ The input may start with a feasible point. If so,
+ use 'H' by itself. The input starts with a feasi-
+ ble point when the first number is the dimension,
+ the second number is "1", and the coordinates com-
+ plete a line. The 'FV' option produces a feasible
+ point for a convex hull.
+
+ d Qu Compute the furthest-site Delaunay triangulation
+ from the upper convex hull. The 'o' option prints
+ the input points and facets. The 'QJ' option guar-
+ antees triangular otuput. You can also use facets.
+
+ v Qu Compute the furthest-site Voronoi diagram. The 'p'
+ option prints the Voronoi vertices. The 'o' option
+ prints the Voronoi vertices and the vertices in
+
+
+
+Geometry Center 2003/12/30 4
+
+
+
+
+
+qhull(1) qhull(1)
+
+
+ each Voronoi region. The 'Fv' option prints each
+ ridge of the Voronoi diagram. The first or zero'th
+ vertex indicates the infinity vertex at infinity.
+ Its coordinates are qh_INFINITE (-10.101). It
+ indicates unbounded Voronoi regions and degenerate
+ Delaunay triangles.
+
+ Qt Triangulated output.
+
+
+ Input/Output options:
+
+ f Print out all facets and all fields of each facet.
+
+ G Output the hull in Geomview format. For imprecise
+ hulls, Geomview displays the inner and outer hull.
+ Geomview can also display points, ridges, vertices,
+ coplanar points, and facet intersections. See
+ below for a list of options.
+
+ For Delaunay triangulations, 'G' displays the cor-
+ responding paraboloid. For halfspace intersection,
+ 'G' displays the dual polytope.
+
+ i Output the incident vertices for each facet. Qhull
+ prints the number of facets followed by the ver-
+ tices of each facet. One facet is printed per
+ line. The numbers are the 0-relative indices of
+ the corresponding input points. The facets are
+ oriented.
+
+ In 4-d and higher, Qhull triangulates non-simpli-
+ cial facets. Each apex (the first vertex) is a
+ created point that corresponds to the facet's cen-
+ trum. Its index is greater than the indices of the
+ input points. Each base corresponds to a simpli-
+ cial ridge between two facets. To print the ver-
+ tices without triangulation, use option 'Fv'.
+
+ m Output the hull in Mathematica format. Qhull
+ writes a Mathematica file for 2-d and 3-d convex
+ hulls and for 2-d Delaunay triangulations. Qhull
+ produces a list of objects that you can assign to a
+ variable in Mathematica, for example: "list= <<
+ <outputfilename> ". If the object is 2-d, it can be
+ visualized by "Show[Graphics[list]] ". For 3-d
+ objects the command is "Show[Graphics3D[list]]".
+
+ n Output the normal equation for each facet. Qhull
+ prints the dimension (plus one), the number of
+ facets, and the normals for each facet. The
+ facet's offset follows its normal coefficients.
+
+ o Output the facets in OFF file format. Qhull prints
+ the dimension, number of points, number of facets,
+ and number of ridges. Then it prints the
+
+
+
+Geometry Center 2003/12/30 5
+
+
+
+
+
+qhull(1) qhull(1)
+
+
+ coordinates of the input points and the vertices
+ for each facet. Each facet is on a separate line.
+ The first number is the number of vertices. The
+ remainder are the indices of the corresponding
+ points. The vertices are oriented in 2-d, 3-d, and
+ in simplicial facets.
+
+ For 2-d Voronoi diagrams, the vertices are sorted
+ by adjacency, but not oriented. In 3-d and higher,
+ the Voronoi vertices are sorted by index. See the
+ 'v' option for more information.
+
+ p Output the coordinates of each vertex point. Qhull
+ prints the dimension, the number of points, and the
+ coordinates for each vertex. With the 'Gc' and
+ 'Gi' options, it also prints coplanar and interior
+ points. For Voronoi diagrams, it prints the coor-
+ dinates of each Voronoi vertex.
+
+ s Print a summary to stderr. If no output options
+ are specified at all, a summary goes to stdout.
+ The summary lists the number of input points, the
+ dimension, the number of vertices in the convex
+ hull, the number of facets in the convex hull, the
+ number of good facets (if 'Pg'), and statistics.
+
+ The last two statistics (if needed) measure the
+ maximum distance from a point or vertex to a facet.
+ The number in parenthesis (e.g., 2.1x) is the ratio
+ between the maximum distance and the worst-case
+ distance due to merging two simplicial facets.
+
+
+ Precision options
+
+ An Maximum angle given as a cosine. If the angle
+ between a pair of facet normals is greater than n, Qhull
+ merges one of the facets into a neighbor. If 'n'
+ is negative, Qhull tests angles after adding each
+ point to the hull (pre-merging). If 'n' is posi-
+ tive, Qhull tests angles after constructing the
+ hull (post-merging). Both pre- and post-merging
+ can be defined.
+
+ Option 'C0' or 'C-0' is set if the corresponding
+ 'Cn' or 'C-n' is not set. If 'Qx' is set, then 'A-
+ n' and 'C-n' are checked after the hull is con-
+ structed and before 'An' and 'Cn' are checked.
+
+ Cn Centrum radius. If a centrum is less than n below
+ a neighboring facet, Qhull merges one of the
+ facets. If 'n' is negative or '-0', Qhull tests
+ and merges facets after adding each point to the
+ hull. This is called "pre-merging". If 'n' is
+
+
+
+Geometry Center 2003/12/30 6
+
+
+
+
+
+qhull(1) qhull(1)
+
+
+ positive, Qhull tests for convexity after con-
+ structing the hull ("post-merging"). Both pre- and
+ post-merging can be defined.
+
+ For 5-d and higher, 'Qx' should be used instead of
+ 'C-n'. Otherwise, most or all facets may be merged
+ together.
+
+ En Maximum roundoff error for distance computations.
+
+ Rn Randomly perturb distance computations up to +/- n
+ * max_coord. This option perturbs every distance,
+ hyperplane, and angle computation. To use time as
+ the random number seed, use option 'QR-1'.
+
+ Vn Minimum distance for a facet to be visible. A
+ facet is visible if the distance from the point to
+ the facet is greater than 'Vn'.
+
+ Without merging, the default value for 'Vn' is the
+ round-off error ('En'). With merging, the default
+ value is the pre-merge centrum ('C-n') in 2-d or
+ 3--d, or three times that in other dimensions. If
+ the outside width is specified ('Wn'), the maximum,
+ default value for 'Vn' is 'Wn'.
+
+ Un Maximum distance below a facet for a point to be
+ coplanar to the facet. The default value is 'Vn'.
+
+ Wn Minimum outside width of the hull. Points are
+ added to the convex hull only if they are clearly
+ outside of a facet. A point is outside of a facet
+ if its distance to the facet is greater than 'Wn'.
+ The normal value for 'Wn' is 'En'. If the user
+ specifies pre-merging and does not set 'Wn', than
+ 'Wn' is set to the premerge 'Cn' and maxco-
+ ord*(1-An).
+
+
+ Additional input/output formats
+
+ Fa Print area for each facet. For Delaunay triangula-
+ tions, the area is the area of the triangle. For
+ Voronoi diagrams, the area is the area of the dual
+ facet. Use 'PAn' for printing the n largest
+ facets, and option 'PFn' for printing facets larger
+ than 'n'.
+
+ The area for non-simplicial facets is the sum of
+ the areas for each ridge to the centrum. Vertices
+ far below the facet's hyperplane are ignored. The
+ reported area may be significantly less than the
+ actual area.
+
+
+
+
+Geometry Center 2003/12/30 7
+
+
+
+
+
+qhull(1) qhull(1)
+
+
+ FA Compute the total area and volume for option 's'.
+ It is an approximation for non-simplicial facets
+ (see 'Fa').
+
+ Fc Print coplanar points for each facet. The output
+ starts with the number of facets. Then each facet
+ is printed one per line. Each line is the number
+ of coplanar points followed by the point ids.
+ Option 'Qi' includes the interior points. Each
+ coplanar point (interior point) is assigned to the
+ facet it is furthest above (resp., least below).
+
+ FC Print centrums for each facet. The output starts
+ with the dimension followed by the number of
+ facets. Then each facet centrum is printed, one
+ per line.
+
+ Fd Read input in cdd format with homogeneous points.
+ The input starts with comments. The first comment
+ is reported in the summary. Data starts after a
+ "begin" line. The next line is the number of
+ points followed by the dimension+1 and "real" or
+ "integer". Then the points are listed with a
+ leading "1" or "1.0". The data ends with an "end"
+ line.
+
+ For halfspaces ('Fd Hn,n,...'), the input format is
+ the same. Each halfspace starts with its offset.
+ The sign of the offset is the opposite of Qhull's
+ convention.
+
+ FD Print normals ('n', 'Fo', 'Fi') or points ('p') in
+ cdd format. The first line is the command line
+ that invoked Qhull. Data starts with a "begin"
+ line. The next line is the number of normals or
+ points followed by the dimension+1 and "real".
+ Then the normals or points are listed with the
+ offset before the coefficients. The offset for
+ points is 1.0. The offset for normals has the
+ opposite sign. The data ends with an "end" line.
+
+ FF Print facets (as in 'f') without printing the
+ ridges.
+
+ Fi Print inner planes for each facet. The inner plane
+ is below all vertices.
+
+ Fi Print separating hyperplanes for bounded, inner
+ regions of the Voronoi diagram. The first line is
+ the number of ridges. Then each hyperplane is
+ printed, one per line. A line starts with the num-
+ ber of indices and floats. The first pair lists
+ adjacent input sites, the next d floats are the
+ normalized coefficients for the hyperplane, and the
+
+
+
+Geometry Center 2003/12/30 8
+
+
+
+
+
+qhull(1) qhull(1)
+
+
+ last float is the offset. The hyperplane is ori-
+ ented toward verify that the hyperplanes are per-
+ pendicular bisectors. Use 'Fo' for unbounded
+ regions, and 'Fv' for the corresponding Voronoi
+ vertices.
+
+ FI Print facet identifiers.
+
+ Fm Print number of merges for each facet. At most 511
+ merges are reported for a facet. See 'PMn' for
+ printing the facets with the most merges.
+
+ FM Output the hull in Maple format. See 'm'
+
+ Fn Print neighbors for each facet. The output starts
+ with the number of facets. Then each facet is
+ printed one per line. Each line is the number of
+ neighbors followed by an index for each neighbor.
+ The indices match the other facet output formats.
+
+ A negative index indicates an unprinted facet due
+ to printing only good facets ('Pg'). It is the
+ negation of the facet's id (option 'FI'). For
+ example, negative indices are used for facets "at
+ infinity" in the Delaunay triangulation.
+
+ FN Print vertex neighbors or coplanar facet for each
+ point. The first line is the number of points.
+ Then each point is printed, one per line. If the
+ point is coplanar, the line is "1" followed by the
+ facet's id. If the point is not a selected vertex,
+ the line is "0". Otherwise, each line is the num-
+ ber of neighbors followed by the corresponding
+ facet indices (see 'Fn').
+
+ Fo Print outer planes for each facet in the same for-
+ mat as 'n'. The outer plane is above all points.
+
+ Fo Print separating hyperplanes for unbounded, outer
+ regions of the Voronoi diagram. The first line is
+ the number of ridges. Then each hyperplane is
+ printed, one per line. A line starts with the num-
+ ber of indices and floats. The first pair lists
+ adjacent input sites, the next d floats are the
+ normalized coefficients for the hyperplane, and the
+ last float is the offset. The hyperplane is ori-
+ ented toward verify that the hyperplanes are per-
+ pendicular bisectors. Use 'Fi' for bounded
+ regions, and 'Fv' for the corresponding Voronoi
+ vertices.
+
+ FO List all options to stderr, including the default
+ values. Additional 'FO's are printed to stdout.
+
+ Fp Print points for halfspace intersections (option
+ 'Hn,n,...'). Each intersection corresponds to a
+
+
+
+Geometry Center 2003/12/30 9
+
+
+
+qhull(1) qhull(1)
+
+
+ facet of the dual polytope. The "infinity" point
+ [-10.101,-10.101,...] indicates an unbounded
+ intersection.
+
+ FP For each coplanar point ('Qc') print the point id
+ of the nearest vertex, the point id, the facet id,
+ and the distance.
+
+ FQ Print command used for qhull and input.
+
+ Fs Print a summary. The first line consists of the
+ number of integers ("7"), followed by the dimen-
+ sion, the number of points, the number of vertices,
+ the number of facets, the number of vertices
+ selected for output, the number of facets selected
+ for output, the number of coplanar points selected
+ for output.
+
+ The second line consists of the number of reals
+ ("2"), followed by the maxmimum offset to an outer
+ plane and and minimum offset to an inner plane.
+ Roundoff is included. Later versions of Qhull may
+ produce additional integers or reals.
+
+ FS Print the size of the hull. The first line con-
+ sists of the number of integers ("0"). The second
+ line consists of the number of reals ("2"), fol-
+ lowed by the total facet area, and the total vol-
+ ume. Later versions of Qhull may produce addi-
+ tional integers or reals.
+
+ The total volume measures the volume of the inter-
+ section of the halfspaces defined by each facet.
+ Both area and volume are approximations for non-
+ simplicial facets. See option 'Fa'.
+
+ Ft Print a triangulation with added points for non-
+ simplicial facets. The first line is the dimension
+ and the second line is the number of points and the
+ number of facets. The points follow, one per line,
+ then the facets follow as a list of point indices.
+ With option points include the point-at-infinity.
+
+ Fv Print vertices for each facet. The first line is
+ the number of facets. Then each facet is printed,
+ one per line. Each line is the number of vertices
+ followed by the corresponding point ids. Vertices
+ are listed in the order they were added to the hull
+ (the last one is first).
+
+ Fv Print all ridges of a Voronoi diagram. The first
+ line is the number of ridges. Then each ridge is
+ printed, one per line. A line starts with the num-
+ ber of indices. The first pair lists adjacent
+
+
+
+Geometry Center 2003/12/30 10
+
+
+
+
+
+qhull(1) qhull(1)
+
+
+ input sites, the remaining indices list Voronoi
+ vertices. Vertex '0' indicates the vertex-at-
+ infinity (i.e., an unbounded ray). In 3-d, the
+ vertices are listed in order. See 'Fi' and 'Fo'
+ for separating hyperplanes.
+
+ FV Print average vertex. The average vertex is a fea-
+ sible point for halfspace intersection.
+
+ Fx List extreme points (vertices) of the convex hull.
+ The first line is the number of points. The other
+ lines give the indices of the corresponding points.
+ The first point is '0'. In 2-d, the points occur
+ in counter-clockwise order; otherwise they occur in
+ input order. For Delaunay triangulations, 'Fx'
+ lists the extreme points of the input sites. The
+ points are unordered.
+
+
+ Geomview options
+
+ G Produce a file for viewing with Geomview. Without
+ other options, Qhull displays edges in 2-d, outer
+ planes in 3-d, and ridges in 4-d. A ridge can be
+ explicit or implicit. An explicit ridge is a dim-1
+ dimensional simplex between two facets. In 4-d,
+ the explicit ridges are triangles. When displaying
+ a ridge in 4-d, Qhull projects the ridge's vertices
+ to one of its facets' hyperplanes. Use 'Gh' to
+ project ridges to the intersection of both hyper-
+ planes.
+
+ Ga Display all input points as dots.
+
+ Gc Display the centrum for each facet in 3-d. The
+ centrum is defined by a green radius sitting on a
+ blue plane. The plane corresponds to the facet's
+ hyperplane. The radius is defined by 'C-n' or
+ 'Cn'.
+
+ GDn Drop dimension n in 3-d or 4-d. The result is a
+ 2-d or 3-d object.
+
+ Gh Display hyperplane intersections in 3-d and 4-d.
+ In 3-d, the intersection is a black line. It lies
+ on two neighboring hyperplanes (c.f., the blue
+ squares associated with centrums ('Gc')). In 4-d,
+ the ridges are projected to the intersection of
+ both hyperplanes.
+
+ Gi Display inner planes in 2-d and 3-d. The inner
+ plane of a facet is below all of its vertices. It
+ is parallel to the facet's hyperplane. The inner
+ plane's color is the opposite (1-r,1-g,1-b) of the
+
+
+
+Geometry Center 2003/12/30 11
+
+
+
+
+
+qhull(1) qhull(1)
+
+
+ outer plane. Its edges are determined by the ver-
+ tices.
+
+ Gn Do not display inner or outer planes. By default,
+ Geomview displays the precise plane (no merging) or
+ both inner and output planes (merging). Under
+ merging, Geomview does not display the inner plane
+ if the the difference between inner and outer is
+ too small.
+
+ Go Display outer planes in 2-d and 3-d. The outer
+ plane of a facet is above all input points. It is
+ parallel to the facet's hyperplane. Its color is
+ determined by the facet's normal, and its edges are
+ determined by the vertices.
+
+ Gp Display coplanar points and vertices as radii. A
+ radius defines a ball which corresponds to the
+ imprecision of the point. The imprecision is the
+ maximum of the roundoff error, the centrum radius,
+ and maxcoord * (1-An). It is at least 1/20'th of
+ the maximum coordinate, and ignores post-merging if
+ pre-merging is done.
+
+ Gr Display ridges in 3-d. A ridge connects the two
+ vertices that are shared by neighboring facets.
+ Ridges are always displayed in 4-d.
+
+ Gt A 3-d Delaunay triangulation looks like a convex
+ hull with interior facets. Option 'Gt' removes the
+ outside ridges to reveal the outermost facets. It
+ automatically sets options 'Gr' and 'GDn'.
+
+ Gv Display vertices as spheres. The radius of the
+ sphere corresponds to the imprecision of the data.
+ See 'Gp' for determining the radius.
+
+
+ Print options
+
+ PAn Only the n largest facets are marked good for
+ printing. Unless 'PG' is set, 'Pg' is automati-
+ cally set.
+
+ Pdk:n Drop facet from output if normal[k] <= n. The
+ option 'Pdk' uses the default value of 0 for n.
+
+ PDk:n Drop facet from output if normal[k] >= n. The
+ option 'PDk' uses the default value of 0 for n.
+
+ PFn Only facets with area at least 'n' are marked good
+ for printing. Unless 'PG' is set, 'Pg' is automat-
+ ically set.
+
+
+
+
+Geometry Center 2003/12/30 12
+
+
+
+
+
+qhull(1) qhull(1)
+
+
+ Pg Print only good facets. A good facet is either
+ visible from a point (the 'QGn' option) or includes
+ a point (the 'QVn' option). It also meets the
+ requirements of 'Pdk' and 'PDk' options. Option
+ 'Pg' is automatically set for options 'PAn' and
+ 'PFn'.
+
+ PG Print neighbors of good facets.
+
+ PMn Only the n facets with the most merges are marked
+ good for printing. Unless 'PG' is set, 'Pg' is
+ automatically set.
+
+ Po Force output despite precision problems. Verify ('Tv') does not check
+ coplanar points. Flipped facets are reported and
+ concave facets are counted. If 'Po' is used,
+ points are not partitioned into flipped facets and
+ a flipped facet is always visible to a point.
+ Also, if an error occurs before the completion of
+ Qhull and tracing is not active, 'Po' outputs a
+ neighborhood of the erroneous facets (if any).
+
+ Pp Do not report precision problems.
+
+
+ Qhull control options
+
+ Qbk:0Bk:0
+ Drop dimension k from the input points. This
+ allows the user to take convex hulls of sub-dimen-
+ sional objects. It happens before the Delaunay and
+ Voronoi transformation.
+
+ QbB Scale the input points to fit the unit cube. After
+ scaling, the lower bound will be -0.5 and the upper
+ bound +0.5 in all dimensions. For Delaunay and
+ Voronoi diagrams, scaling happens after projection
+ to the paraboloid. Under precise arithmetic, scal-
+ ing does not change the topology of the convex
+ hull.
+
+ Qbb Scale the last coordinate to [0, m] where m is the
+ maximum absolute value of the other coordinates.
+ For Delaunay and Voronoi diagrams, scaling happens
+ after projection to the paraboloid. It reduces
+ roundoff error for inputs with integer coordinates.
+ Under precise arithmetic, scaling does not change
+ the topology of the convex hull.
+
+ Qbk:n Scale the k'th coordinate of the input points.
+ After scaling, the lower bound of the input points
+ will be n. 'Qbk' scales to -0.5.
+
+
+
+Geometry Center 2003/12/30 13
+
+
+
+
+
+qhull(1) qhull(1)
+
+
+ QBk:n Scale the k'th coordinate of the input points.
+ After scaling, the upper bound will be n. 'QBk'
+ scales to +0.5.
+
+ Qc Keep coplanar points with the nearest facet. Out-
+ put formats 'p', 'f', 'Gp', 'Fc', 'FN', and 'FP'
+ will print the points.
+
+ Qf Partition points to the furthest outside facet.
+
+ Qg Only build good facets. With the 'Qg' option,
+ Qhull will only build those facets that it needs to
+ determine the good facets in the output. See
+ 'QGn', 'QVn', and 'PdD' for defining good facets,
+ and 'Pg' and 'PG' for printing good facets and
+ their neighbors.
+
+ QGn A facet is good (see 'Qg' and 'Pg') if it is visi-
+ ble from point n. If n < 0, a facet is good if it
+ is not visible from point n. Point n is not added
+ to the hull (unless 'TCn' or 'TPn'). With rbox,
+ use the 'Pn,m,r' option to define your point; it
+ will be point 0 (QG0).
+
+ Qi Keep interior points with the nearest facet. Out-
+ put formats 'p', 'f', 'Gp', 'FN', 'FP', and 'Fc'
+ will print the points.
+
+ QJn Joggle each input coordinate by adding a random
+ number in [-n,n]. If a precision error occurs,
+ then qhull increases n and tries again. It does
+ not increase n beyond a certain value, and it stops
+ after a certain number of attempts [see user.h].
+ Option 'QJ' selects a default value for n. The
+ output will be simplicial. For Delaunay triangula-
+ tions, 'QJn' sets 'Qbb' to scale the last coordi-
+ nate (not if 'Qbk:n' or 'QBk:n' is set). 'QJn' is
+ deprecated for Voronoi diagrams. See also 'Qt'.
+
+ Qm Only process points that would otherwise increase
+ max_outside. Other points are treated as coplanar
+ or interior points.
+
+ Qr Process random outside points instead of furthest
+ ones. This makes Qhull equivalent to the random-
+ ized incremental algorithms. CPU time is not
+ reported since the randomization is inefficient.
+
+ QRn Randomly rotate the input points. If n=0, use time
+ as the random number seed. If n>0, use n as the
+ random number seed. If n=-1, don't rotate but use
+ time as the random number seed. For Delaunay tri-
+ angulations ('d' and 'v'), rotate about the last
+ axis.
+
+
+
+
+Geometry Center 2003/12/30 14
+
+
+
+
+
+qhull(1) qhull(1)
+
+
+ Qs Search all points for the initial simplex.
+
+ Qt Triangulated output. Triangulate non-simplicial
+ facets. 'Qt' is deprecated for Voronoi diagrams.
+ See also 'QJn'
+
+ Qv Test vertex neighbors for convexity after post-
+ merging. To use the 'Qv' option, you also need to
+ set a merge option (e.g., 'Qx' or 'C-0').
+
+ QVn A good facet (see 'Qg' and 'Pg') includes point n.
+ If n<0, then a good facet does not include point n.
+ The point is either in the initial simplex or it is
+ the first point added to the hull. Option 'QVn'
+ may not be used with merging.
+
+ Qx Perform exact merges while building the hull. The
+ "exact" merges are merging a point into a coplanar
+ facet (defined by 'Vn', 'Un', and 'C-n'), merging
+ concave facets, merging duplicate ridges, and merg-
+ ing flipped facets. Coplanar merges and angle
+ coplanar merges ('A-n') are not performed. Concav-
+ ity testing is delayed until a merge occurs.
+
+ After the hull is built, all coplanar merges are
+ performed (defined by 'C-n' and 'A-n'), then post-
+ merges are performed (defined by 'Cn' and 'An').
+
+ Qz Add a point "at infinity" that is above the
+ paraboloid for Delaunay triangulations and Voronoi
+ diagrams. This reduces precision problems and
+ allows the triangulation of cospherical points.
+
+
+ Qhull experiments and speedups
+
+ Q0 Turn off pre-merging as a default option. With
+ 'Q0'/'Qx' and without explicit pre-merge options,
+ Qhull ignores precision issues while constructing
+ the convex hull. This may lead to precision
+ errors. If so, a descriptive warning is generated.
+
+ Q1 With 'Q1', Qhull sorts merges by type (coplanar,
+ angle coplanar, concave) instead of by angle.
+
+ Q2 With 'Q2', Qhull merges all facets at once instead
+ of using independent sets of merges and then
+ retesting.
+
+ Q3 With 'Q3', Qhull does not remove redundant ver-
+ tices.
+
+ Q4 With 'Q4', Qhull avoids merges of an old facet into
+ a new facet.
+
+ Q5 With 'Q5', Qhull does not correct outer planes at
+ the end. The maximum outer plane is used instead.
+
+
+
+
+Geometry Center 2003/12/30 15
+
+
+
+
+
+qhull(1) qhull(1)
+
+
+ Q6 With 'Q6', Qhull does not pre-merge concave or
+ coplanar facets.
+
+ Q7 With 'Q7', Qhull processes facets in depth-first
+ order instead of breadth-first order.
+
+ Q8 With 'Q8' and merging, Qhull does not retain near-
+ interior points for adjusting outer planes. 'Qc'
+ will probably retain all points that adjust outer
+ planes.
+
+ Q9 With 'Q9', Qhull processes the furthest of all out-
+ side sets at each iteration.
+
+ Q10 With 'Q10', Qhull does not use special processing
+ for narrow distributions.
+
+ Q11 With 'Q11', Qhull copies normals and recomputes
+ centrums for tricoplanar facets.
+
+ Q12 With 'Q12', Qhull does not report a very wide merge due
+ to a duplicated ridge with nearly coincident vertices
+
+ Trace options
+
+ Tn Trace at level n. Qhull includes full execution
+ tracing. 'T-1' traces events. 'T1' traces the
+ overall execution of the program. 'T2' and 'T3'
+ trace overall execution and geometric and topologi-
+ cal events. 'T4' traces the algorithm. 'T5'
+ includes information about memory allocation and
+ Gaussian elimination.
+
+ Ta Annotate output with codes that identify the
+ corresponding qh_fprintf() statement.
+
+ Tc Check frequently during execution. This will catch
+ most inconsistency errors.
+
+ TCn Stop Qhull after building the cone of new facets
+ for point n. The output for 'f' includes the cone
+ and the old hull. See also 'TVn'.
+
+ TFn Report progress whenever more than n facets are
+ created During post-merging, 'TFn' reports progress
+ after more than n/2 merges.
+
+ TI file
+ Input data from 'file'. The filename may not include
+ spaces or quotes.
+
+ TO file
+ Output results to 'file'. The name may be enclosed
+ in single quotes.
+
+ TPn Turn on tracing when point n is added to the hull.
+ Trace partitions of point n. If used with TWn, turn off
+ tracing after adding point n to the hull.
+
+ TRn Rerun qhull n times. Usually used with 'QJn' to
+ determine the probability that a given joggle will
+ fail.
+
+ Ts Collect statistics and print to stderr at the end
+ of execution.
+
+ Tv Verify the convex hull. This checks the topologi-
+ cal structure, facet convexity, and point inclu-
+ sion. If precision problems occurred, facet con-
+ vexity is tested whether or not 'Tv' is selected.
+ Option 'Tv' does not check point inclusion if
+
+
+
+Geometry Center 2003/12/30 16
+
+
+
+
+
+qhull(1) qhull(1)
+
+
+ forcing output with 'Po', or if 'Q5' is set.
+
+ For point inclusion testing, Qhull verifies that
+ all points are below all outer planes (facet->max-
+ outside). Point inclusion is exhaustive if merging
+ or if the facet-point product is small enough; oth-
+ erwise Qhull verifies each point with a directed
+ search (qh_findbest).
+
+ Point inclusion testing occurs after producing out-
+ put. It prints a message to stderr unless option
+ 'Pp' is used. This allows the user to interrupt
+ Qhull without changing the output.
+
+ TVn Stop Qhull after adding point n. If n < 0, stop
+ Qhull before adding point n. Output shows the hull
+ at this time. See also 'TCn'
+
+ TMn Turn on tracing at n'th merge.
+
+ TWn Trace merge facets when the width is greater than
+ n.
+
+ Tz Redirect stderr to stdout.
+
+
+BUGS
+ Please report bugs to Brad Barber at
+ qhull_bug@qhull.org.
+
+ If Qhull does not compile, it is due to an incompatibility
+ between your system and ours. The first thing to check is
+ that your compiler is ANSI standard. If it is, check the
+ man page for the best options, or find someone to help
+ you. If you locate the cause of your problem, please send
+ email since it might help others.
+
+ If Qhull compiles but crashes on the test case (rbox D4),
+ there's still incompatibility between your system and
+ ours. Typically it's been due to mem.c and memory align-
+ ment. You can use qh_NOmem in mem.h to turn off memory
+ management. Please let us know if you figure out how to
+ fix these problems.
+
+ If you do find a problem, try to simplify it before
+ reporting the error. Try different size inputs to locate
+ the smallest one that causes an error. You're welcome to
+ hunt through the code using the execution trace as a
+ guide. This is especially true if you're incorporating
+ Qhull into your own program.
+
+ When you do report an error, please attach a data set to
+ the end of your message. This allows us to see the error
+ for ourselves. Qhull is maintained part-time.
+
+
+
+Geometry Center 2003/12/30 17
+
+
+
+
+
+qhull(1) qhull(1)
+
+
+E-MAIL
+ Please send correspondence to qhull@qhull.org and
+ report bugs to qhull_bug@qhull.org. Let us know how
+ you use Qhull. If you mention it in a paper, please send
+ the reference and an abstract.
+
+ If you would like to get Qhull announcements (e.g., a new
+ version) and news (any bugs that get fixed, etc.), let us
+ know and we will add you to our mailing list. If you
+ would like to communicate with other Qhull users, we will
+ add you to the qhull_users alias. For Internet news about
+ geometric algorithms and convex hulls, look at comp.graph-
+ ics.algorithms and sci.math.num-analysis
+
+
+SEE ALSO
+ rbox(1)
+
+ Barber, C. B., D.P. Dobkin, and H.T. Huhdanpaa, "The
+ Quickhull Algorithm for Convex Hulls," ACM Trans. on Math-
+ ematical Software, 22(4):469-483, Dec. 1996.
+ http://portal.acm.org/citation.cfm?doid=235815.235821
+ http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.117.405
+
+
+ Clarkson, K.L., K. Mehlhorn, and R. Seidel, "Four results
+ on randomized incremental construction," Computational
+ Geometry: Theory and Applications, vol. 3, p. 185-211,
+ 1993.
+
+ Preparata, F. and M. Shamos, Computational Geometry,
+ Springer-Verlag, New York, 1985.
+
+
+
+AUTHORS
+ C. Bradford Barber Hannu Huhdanpaa
+ bradb@shore.net hannu@qhull.org
+
+
+
+ACKNOWLEDGEMENTS
+ A special thanks to Albert Marden, Victor Milenkovic, the
+ Geometry Center, Harvard University, and Endocardial Solu-
+ tions, Inc. for supporting this work.
+
+ Qhull 1.0 and 2.0 were developed under National Science Foundation
+ grants NSF/DMS-8920161 and NSF-CCR-91-15793 750-7504. David Dobkin
+
+
+
+Geometry Center 2003/12/30 18
+
+
+
+
+
+qhull(1) qhull(1)
+
+
+ guided the original work at Princeton University. If you find it
+ useful, please let us know.
+
+ The Geometry Center was supported by grant DMS-8920161 from the National
+ Science Foundation, by grant DOE/DE-FG02-92ER25137 from the Department
+ of Energy, by the University of Minnesota, and by Minnesota Technology, Inc.
+
+ Qhull is available from http://www.qhull.org
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Geometry Center 2003/12/30 19
+
+
diff --git a/xs/src/qhull/html/qvoron_f.htm b/xs/src/qhull/html/qvoron_f.htm
new file mode 100644
index 000000000..db538b5ab
--- /dev/null
+++ b/xs/src/qhull/html/qvoron_f.htm
@@ -0,0 +1,396 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+
+<head>
+<title>qvoronoi Qu -- furthest-site Voronoi diagram</title>
+</head>
+
+<body>
+<!-- Navigation links -->
+<a name="TOP"><b>Up</b></a><b>:</b>
+<a href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b> <a href="index.htm#TOC">Qhull manual</a>: Table of Contents<br>
+<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
+&#149; <a href="qh-quick.htm#options">Options</a>
+&#149; <a href="qh-opto.htm#output">Output</a>
+&#149; <a href="qh-optf.htm#format">Formats</a>
+&#149; <a href="qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="qh-optp.htm#print">Print</a>
+&#149; <a href="qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="qh-optc.htm#prec">Precision</a>
+&#149; <a href="qh-optt.htm#trace">Trace</a>
+&#149; <a href="../src/libqhull_r/index.htm">Functions</a><br>
+<b>To:</b> <a href="#synopsis">sy</a>nopsis
+&#149; <a href="#input">in</a>put &#149; <a href="#outputs">ou</a>tputs
+&#149; <a href="#controls">co</a>ntrols &#149; <a href="#graphics">gr</a>aphics
+&#149; <a href="#notes">no</a>tes &#149; <a href="#conventions">co</a>nventions
+&#149; <a href="#options">op</a>tions
+
+<hr>
+<!-- Main text of document -->
+<h1><a
+href="http://www.geom.uiuc.edu/graphics/pix/Special_Topics/Computational_Geometry/delaunay.html"><img
+src="qh--dt.gif" alt="[delaunay]" align="middle" width="100"
+height="100"></a>qvoronoi Qu -- furthest-site Voronoi diagram</h1>
+
+<p>The furthest-site Voronoi diagram is the furthest-neighbor map for a set of
+points. Each region contains those points that are further
+from one input site than any other input site. See the
+survey article by Aurenhammer [<a href="index.htm#aure91">'91</a>]
+and the brief introduction by O'Rourke [<a
+href="index.htm#orou94">'94</a>]. The furthest-site Voronoi diagram is the dual of the <a
+href="qdelau_f.htm">furthest-site Delaunay triangulation</a>.
+</p>
+
+<blockquote>
+<dl>
+ <dt><b>Example:</b> rbox 10 D2 | qvoronoi <a
+ href="qh-optq.htm#Qu">Qu</a> <a href="qh-opto.htm#s">s</a>
+ <a href="qh-opto.htm#o">o</a> <a href="qh-optt.htm#TO">TO
+ result</a></dt>
+ <dd>Compute the 2-d, furthest-site Voronoi diagram of 10
+ random points. Write a summary to the console and the Voronoi
+ regions and vertices to 'result'. The first vertex of the
+ result indicates unbounded regions. Almost all regions
+ are unbounded.</dd>
+</dl>
+
+<dl>
+ <dt><b>Example:</b> rbox r y c G1 D2 | qvoronoi <a
+ href="qh-optq.htm#Qu">Qu</a>
+ <a href="qh-opto.htm#s">s</a>
+ <a href="qh-optf.htm#Fn">Fn</a> <a href="qh-optt.htm#TO">TO
+ result</a></dt>
+ <dd>Compute the 2-d furthest-site Voronoi diagram of a square
+ and a small triangle. Write a summary to the console and the Voronoi
+ vertices for each input site to 'result'.
+ The origin is the only furthest-site Voronoi vertex. The
+ negative indices indicate vertices-at-infinity.</dd>
+</dl>
+</blockquote>
+
+<p>
+Qhull computes the furthest-site Voronoi diagram via the <a href="qdelau_f.htm">
+furthest-site Delaunay triangulation</a>.
+Each furthest-site Voronoi vertex is the circumcenter of an upper
+facet of the Delaunay triangulation. Each furthest-site Voronoi
+region corresponds to a vertex of the Delaunay triangulation
+(i.e., an input site).</p>
+
+<p>See <a href="http://www.qhull.org/html/qh-faq.htm#TOC">Qhull FAQ</a> - Delaunay and
+Voronoi diagram questions.</p>
+
+<p>The 'qvonoroi' program is equivalent to
+'<a href=qhull.htm#outputs>qhull v</a> <a href=qh-optq.htm#Qbb>Qbb</a>' in 2-d to 3-d, and
+'<a href=qhull.htm#outputs>qhull v</a> <a href=qh-optq.htm#Qbb>Qbb</a> <a href=qh-optq.htm#Qx>Qx</a>'
+in 4-d and higher. It disables the following Qhull
+<a href=qh-quick.htm#options>options</a>: <i>d n m v H U Qb
+QB Qc Qf Qg Qi Qm Qr QR Qv Qx TR E V Fa FA FC Fp FS Ft FV Gt Q0,etc</i>.
+
+
+<p><b>Copyright &copy; 1995-2015 C.B. Barber</b></p>
+
+<hr>
+<h3><a href="#TOP">&#187;</a><a name="synopsis">furthest-site qvoronoi synopsis</a></h3>
+<blockquote>
+
+See <a href="qvoronoi.htm#synopsis">qvoronoi synopsis</a>. The same
+program is used for both constructions. Use option '<a href="qh-optq.htm#Qu">Qu</a>'
+for furthest-site Voronoi diagrams.
+
+
+</blockquote>
+<h3><a href="#TOP">&#187;</a><a name="input">furthest-site qvoronoi
+input</a></h3>
+<blockquote>
+<p>The input data on <tt>stdin</tt> consists of:</p>
+<ul>
+ <li>dimension
+ <li>number of points</li>
+ <li>point coordinates</li>
+</ul>
+
+<p>Use I/O redirection (e.g., qvoronoi Qu &lt; data.txt), a pipe (e.g., rbox 10 | qvoronoi Qu),
+or the '<a href=qh-optt.htm#TI>TI</a>' option (e.g., qvoronoi TI data.txt Qu).
+
+<p>For example, this is a square containing four random points.
+Its furthest-site Voronoi diagram has on vertex and four unbounded,
+separating hyperplanes (i.e., the coordinate axes)
+<p>
+<blockquote>
+<tt>rbox c 4 D2 &gt; data</tt>
+<blockquote><pre>
+2 RBOX c 4 D2
+8
+-0.4999921736307369 -0.3684622117955817
+0.2556053225468894 -0.0413498678629751
+0.0327672376602583 -0.2810408135699488
+-0.452955383763607 0.17886471718444
+ -0.5 -0.5
+ -0.5 0.5
+ 0.5 -0.5
+ 0.5 0.5
+</pre></blockquote>
+
+<p><tt>qvoronoi Qu s Fo &lt; data</tt>
+<blockquote><pre>
+
+Furthest-site Voronoi vertices by the convex hull of 8 points in 3-d:
+
+ Number of Voronoi regions: 8
+ Number of Voronoi vertices: 1
+ Number of non-simplicial Voronoi vertices: 1
+
+Statistics for: RBOX c 4 D2 | QVORONOI Qu s Fo
+
+ Number of points processed: 8
+ Number of hyperplanes created: 20
+ Number of facets in hull: 11
+ Number of distance tests for qhull: 34
+ Number of merged facets: 1
+ Number of distance tests for merging: 107
+ CPU seconds to compute hull (after input): 0
+
+4
+5 4 5 0 1 0
+5 4 6 1 0 0
+5 5 7 1 0 0
+5 6 7 0 1 0
+</pre></blockquote>
+</blockquote>
+
+</blockquote>
+<h3><a href="#TOP">&#187;</a> <a name="outputs">furthest-site qvoronoi
+outputs</a></h3>
+<blockquote>
+
+<p>These options control the output of furthest-site Voronoi diagrams.</p>
+<blockquote>
+
+<dl compact>
+ <dt>&nbsp;</dt>
+ <dd><b>furthest-site Voronoi vertices</b></dd>
+ <dt><a href="qh-opto.htm#p">p</a></dt>
+ <dd>print the coordinates of the furthest-site Voronoi vertices. The first line
+ is the dimension. The second line is the number of vertices. Each
+ remaining line is a furthest-site Voronoi vertex. The points-in-square example
+ has one furthest-site Voronoi vertex at the origin.</dd>
+ <dt><a href="qh-optf.htm#Fn">Fn</a></dt>
+ <dd>list the neighboring furthest-site Voronoi vertices for each furthest-site Voronoi
+ vertex. The first line is the number of Voronoi vertices. Each
+ remaining line starts with the number of neighboring vertices. Negative indices (e.g., <em>-1</em>) indicate vertices
+ outside of the Voronoi diagram. In the points-in-square example, the
+ Voronoi vertex at the origin has four neighbors-at-infinity.</dd>
+ <dt><a href="qh-optf.htm#FN">FN</a></dt>
+ <dd>list the furthest-site Voronoi vertices for each furthest-site Voronoi region. The first line is
+ the number of Voronoi regions. Each remaining line starts with the
+ number of Voronoi vertices. Negative indices (e.g., <em>-1</em>) indicate vertices
+ outside of the Voronoi diagram.
+ In the points-in-square example, all regions share the Voronoi vertex
+ at the origin.</dd>
+
+ <dt>&nbsp;</dt>
+ <dt>&nbsp;</dt>
+ <dd><b>furthest-site Voronoi regions</b></dd>
+ <dt><a href="qh-opto.htm#o">o</a></dt>
+ <dd>print the furthest-site Voronoi regions in OFF format. The first line is the
+ dimension. The second line is the number of vertices, the number
+ of input sites, and "1". The third line represents the vertex-at-infinity.
+ Its coordinates are "-10.101". The next lines are the coordinates
+ of the furthest-site Voronoi vertices. Each remaining line starts with the number
+ of Voronoi vertices in a Voronoi region. In 2-d, the vertices are
+listed in adjacency order (unoriented). In 3-d and higher, the
+vertices are listed in numeric order. In the points-in-square
+ example, each unbounded region includes the Voronoi vertex at
+ the origin. Lines consisting of <em>0</em> indicate
+ interior input sites. </dd>
+ <dt><a href="qh-optf.htm#Fi2">Fi</a></dt>
+ <dd>print separating hyperplanes for inner, bounded furthest-site Voronoi
+ regions. The first number is the number of separating
+ hyperplanes. Each remaining line starts with <i>3+dim</i>. The
+ next two numbers are adjacent input sites. The next <i>dim</i>
+ numbers are the coefficients of the separating hyperplane. The
+ last number is its offset. The are no bounded, separating hyperplanes
+ for the points-in-square example.</dd>
+ <dt><a href="qh-optf.htm#Fo2">Fo</a></dt>
+ <dd>print separating hyperplanes for outer, unbounded furthest-site Voronoi
+ regions. The first number is the number of separating
+ hyperplanes. Each remaining line starts with <i>3+dim</i>. The
+ next two numbers are adjacent input sites on the convex hull. The
+ next <i>dim</i>
+ numbers are the coefficients of the separating hyperplane. The
+ last number is its offset. The points-in-square example has four
+ unbounded, separating hyperplanes.</dd>
+ <dt>&nbsp;</dt>
+ <dt>&nbsp;</dt>
+ <dd><b>Input sites</b></dd>
+ <dt><a href="qh-optf.htm#Fv2">Fv</a></dt>
+ <dd>list ridges of furthest-site Voronoi vertices for pairs of input sites. The
+ first line is the number of ridges. Each remaining line starts with
+ two plus the number of Voronoi vertices in the ridge. The next
+ two numbers are two adjacent input sites. The remaining numbers list
+ the Voronoi vertices. As with option 'o', a <em>0</em> indicates
+ the vertex-at-infinity
+ and an unbounded, separating hyperplane.
+ The perpendicular bisector (separating hyperplane)
+ of the input sites is a flat through these vertices.
+ In the points-in-square example, the ridge for each edge of the square
+ is unbounded.</dd>
+ <dt>&nbsp;</dt>
+ <dt>&nbsp;</dt>
+ <dd><b>General</b></dd>
+ <dt><a href="qh-opto.htm#s">s</a></dt>
+ <dd>print summary of the furthest-site Voronoi diagram. Use '<a
+ href="qh-optf.htm#Fs">Fs</a>' for numeric data.</dd>
+ <dt><a href="qh-opto.htm#i">i</a></dt>
+ <dd>list input sites for each <a href=qdelau_f.htm>furthest-site Delaunay region</a>. Use option '<a href="qh-optp.htm#Pp">Pp</a>'
+ to avoid the warning. The first line is the number of regions. The
+ remaining lines list the input sites for each region. The regions are
+ oriented. In the points-in-square example, the square region has four
+ input sites. In 3-d and higher, report cospherical sites by adding extra points.
+ </dd>
+ <dt><a href="qh-optg.htm#G">G</a></dt>
+ <dd>Geomview output for 2-d furthest-site Voronoi diagrams.</dd>
+ </dl>
+</blockquote>
+
+</blockquote>
+<h3><a href="#TOP">&#187;</a> <a name="controls">furthest-site qvoronoi
+controls</a></h3>
+<blockquote>
+
+<p>These options provide additional control:</p>
+<blockquote>
+
+<dl compact>
+ <dt><a href="qh-optq.htm#Qu">Qu</a></dt>
+ <dd>must be used.</dd>
+ <dt><a href="qh-optq.htm#QVn">QVn</a></dt>
+ <dd>select furthest-site Voronoi vertices for input site <em>n</em> </dd>
+ <dt><a href="qh-optt.htm#Tv">Tv</a></dt>
+ <dd>verify result</dd>
+ <dt><a href="qh-optt.htm#TO">TI file</a></dt>
+ <dd>input data from file. The filename may not use spaces or quotes.</dd>
+ <dt><a href="qh-optt.htm#TO">TO file</a></dt>
+ <dd>output results to file. Use single quotes if the filename
+ contains spaces (e.g., <tt>TO 'file with spaces.txt'</tt></dd>
+ <dt><a href="qh-optt.htm#TFn">TFn</a></dt>
+ <dd>report progress after constructing <em>n</em> facets</dd>
+ <dt><a href="qh-optp.htm#PDk">PDk:1</a></dt>
+ <dd>include upper and lower facets in the output. Set <em>k</em>
+ to the last dimension (e.g., 'PD2:1' for 2-d inputs). </dd>
+ <dt><a href="qh-opto.htm#f">f </a></dt>
+ <dd>facet dump. Print the data structure for each facet (i.e.,
+ furthest-site Voronoi vertex).</dd>
+</dl>
+
+</blockquote>
+</blockquote>
+<h3><a href="#TOP">&#187;</a> <a name="graphics">furthest-site qvoronoi
+graphics</a></h3>
+<blockquote>
+<p>In 2-d, Geomview output ('<a href="qh-optg.htm#G">G</a>')
+displays a furthest-site Voronoi diagram with extra edges to
+close the unbounded furthest-site Voronoi regions. All regions
+will be unbounded. Since the points-in-box example has only
+one furthest-site Voronoi vertex, the Geomview output is one
+point.</p>
+
+<p>See the <a href="qh-eg.htm#delaunay">Delaunay and Voronoi
+examples</a> for a 2-d example. Turn off normalization (on
+Geomview's 'obscure' menu) when comparing the furthest-site
+Voronoi diagram with the corresponding Voronoi diagram. </p>
+
+</blockquote>
+<h3><a href="#TOP">&#187;</a><a name="notes">furthest-site qvoronoi
+notes</a></h3>
+<blockquote>
+
+<p>See <a href="qvoronoi.htm#notes">Voronoi notes</a>.</p>
+
+</blockquote>
+<h3><a href="#TOP">&#187;</a><a name="conventions">furthest-site qvoronoi conventions</a></h3>
+<blockquote>
+
+<p>The following terminology is used for furthest-site Voronoi
+diagrams in Qhull. The underlying structure is a furthest-site
+Delaunay triangulation from a convex hull in one higher
+dimension. Upper facets of the Delaunay triangulation correspond
+to vertices of the furthest-site Voronoi diagram. Vertices of the
+furthest-site Delaunay triangulation correspond to input sites.
+They also define regions of the furthest-site Voronoi diagram.
+All vertices are extreme points of the input sites. See <a
+href="qconvex.htm#conventions">qconvex conventions</a>, <a
+href="qdelau_f.htm#conventions">furthest-site delaunay
+conventions</a>, and <a href="index.htm#structure">Qhull's data structures</a>.</p>
+
+<ul>
+ <li><em>input site</em> - a point in the input (one dimension
+ lower than a point on the convex hull)</li>
+ <li><em>point</em> - a point has <i>d+1</i> coordinates. The
+ last coordinate is the sum of the squares of the input
+ site's coordinates</li>
+ <li><em>vertex</em> - a point on the upper facets of the
+ paraboloid. It corresponds to a unique input site. </li>
+ <li><em>furthest-site Delaunay facet</em> - an upper facet of the
+ paraboloid. The last coefficient of its normal is
+ clearly positive.</li>
+ <li><em>furthest-site Voronoi vertex</em> - the circumcenter
+ of a furthest-site Delaunay facet</li>
+ <li><em>furthest-site Voronoi region</em> - the region of
+ Euclidean space further from an input site than any other
+ input site. Qhull lists the furthest-site Voronoi
+ vertices that define each furthest-site Voronoi region.</li>
+ <li><em>furthest-site Voronoi diagram</em> - the graph of the
+ furthest-site Voronoi regions with the ridges (edges)
+ between the regions.</li>
+ <li><em>infinity vertex</em> - the Voronoi vertex for
+ unbounded furthest-site Voronoi regions in '<a
+ href="qh-opto.htm#o">o</a>' output format. Its
+ coordinates are <em>-10.101</em>.</li>
+ <li><em>good facet</em> - an furthest-site Voronoi vertex with
+ optional restrictions by '<a href="qh-optq.htm#QVn">QVn</a>',
+ etc.</li>
+</ul>
+
+</blockquote>
+<h3><a href="#TOP">&#187;</a><a name="options">furthest-site qvoronoi options</a></h3>
+<blockquote>
+
+See <a href="qvoronoi.htm#options">qvoronoi options</a>. The same
+program is used for both constructions. Use option '<a href="qh-optq.htm#Qu">Qu</a>'
+for furthest-site Voronoi diagrams.
+</blockquote>
+
+<!-- Navigation links -->
+<hr>
+
+<p><b>Up:</b> <a href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b> <a href="index.htm#TOC">Qhull manual</a>: Table of Contents<br>
+<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
+&#149; <a href="qh-quick.htm#options">Options</a>
+&#149; <a href="qh-opto.htm#output">Output</a>
+&#149; <a href="qh-optf.htm#format">Formats</a>
+&#149; <a href="qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="qh-optp.htm#print">Print</a>
+&#149; <a href="qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="qh-optc.htm#prec">Precision</a>
+&#149; <a href="qh-optt.htm#trace">Trace</a>
+&#149; <a href="../src/libqhull_r/index.htm">Functions</a><br>
+<b>To:</b> <a href="#synopsis">sy</a>nopsis
+&#149; <a href="#input">in</a>put &#149; <a href="#outputs">ou</a>tputs
+&#149; <a href="#controls">co</a>ntrols &#149; <a href="#graphics">gr</a>aphics
+&#149; <a href="#notes">no</a>tes &#149; <a href="#conventions">co</a>nventions
+&#149; <a href="#options">op</a>tions
+<!-- GC common information -->
+<hr>
+
+<p><a href="http://www.geom.uiuc.edu/"><img src="qh--geom.gif"
+align="middle" width="40" height="40"></a><i>The Geometry Center
+Home Page </i></p>
+
+<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a>
+</a><br>
+Created: Sept. 25, 1995 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p>
+</body>
+</html>
diff --git a/xs/src/qhull/html/qvoronoi.htm b/xs/src/qhull/html/qvoronoi.htm
new file mode 100644
index 000000000..6d81d48c1
--- /dev/null
+++ b/xs/src/qhull/html/qvoronoi.htm
@@ -0,0 +1,667 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+
+<head>
+<title>qvoronoi -- Voronoi diagram</title>
+</head>
+
+<body>
+<!-- Navigation links -->
+<a name="TOP"><b>Up</b></a><b>:</b>
+<a href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b> <a href="index.htm#TOC">Qhull manual</a>: Table of Contents<br>
+<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
+&#149; <a href="qh-quick.htm#options">Options</a>
+&#149; <a href="qh-opto.htm#output">Output</a>
+&#149; <a href="qh-optf.htm#format">Formats</a>
+&#149; <a href="qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="qh-optp.htm#print">Print</a>
+&#149; <a href="qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="qh-optc.htm#prec">Precision</a>
+&#149; <a href="qh-optt.htm#trace">Trace</a>
+&#149; <a href="../src/libqhull_r/index.htm">Functions</a><br>
+<b>To:</b> <a href="#synopsis">sy</a>nopsis
+&#149; <a href="#input">in</a>put &#149; <a href="#outputs">ou</a>tputs
+&#149; <a href="#controls">co</a>ntrols &#149; <a href="#graphics">gr</a>aphics
+&#149; <a href="#notes">no</a>tes &#149; <a href="#conventions">co</a>nventions
+&#149; <a href="#options">op</a>tions
+
+<hr>
+<!-- Main text of document -->
+<h1><a
+href="http://www.archinect.com/gallery/displayimage.php?pos=-4658"><img
+src="normal_voronoi_knauss_oesterle.jpg" alt="[voronoi]" align="middle"
+height="100"></a>qvoronoi -- Voronoi diagram</h1>
+
+<p>The Voronoi diagram is the nearest-neighbor map for a set of
+points. Each region contains those points that are nearer
+one input site than any other input site. It has many useful properties and applications. See the
+survey article by Aurenhammer [<a href="index.htm#aure91">'91</a>]
+and the detailed introduction by O'Rourke [<a
+href="index.htm#orou94">'94</a>]. The Voronoi diagram is the
+dual of the <a href=qdelaun.htm>Delaunay triangulation</a>. </p>
+
+<blockquote>
+<dl>
+ <dt><b>Example:</b> rbox 10 D3 | qvoronoi <a href="qh-opto.htm#s">s</a>
+ <a href="qh-opto.htm#o">o</a> <a href="qh-optt.htm#TO">TO
+ result</a></dt>
+ <dd>Compute the 3-d Voronoi diagram of 10 random points. Write a
+ summary to the console and the Voronoi vertices and
+ regions to 'result'. The first vertex of the result
+ indicates unbounded regions.</dd>
+
+ <dt>&nbsp;</dt>
+ <dt><b>Example:</b> rbox r y c G0.1 D2 | qvoronoi
+ <a href="qh-opto.htm#s">s</a>
+ <a href="qh-opto.htm#o">o</a> <a href="qh-optt.htm#TO">TO
+ result</a></dt>
+ <dd>Compute the 2-d Voronoi diagram of a triangle and a small
+ square. Write a
+ summary to the console and Voronoi vertices and regions
+ to 'result'. Report a single Voronoi vertex for
+ cocircular input sites. The first vertex of the result
+ indicates unbounded regions. The origin is the Voronoi
+ vertex for the square.</dd>
+
+ <dt>&nbsp;</dt>
+ <dt><b>Example:</b> rbox r y c G0.1 D2 | qvoronoi <a href="qh-optf.htm#Fv2">Fv</a>
+ <a href="qh-optt.htm#TO">TO result</a></dt>
+ <dd>Compute the 2-d Voronoi diagram of a triangle and a small
+ square. Write a
+ summary to the console and the Voronoi ridges to
+ 'result'. Each ridge is the perpendicular bisector of a
+ pair of input sites. Vertex &quot;0&quot; indicates
+ unbounded ridges. Vertex &quot;8&quot; is the Voronoi
+ vertex for the square.</dd>
+
+ <dt>&nbsp;</dt>
+ <dt><b>Example:</b> rbox r y c G0.1 D2 | qvoronoi <a href="qh-optf.htm#Fi2">Fi</a></dt>
+ <dd>Print the bounded, separating hyperplanes for the 2-d Voronoi diagram of a
+ triangle and a small
+ square. Note the four hyperplanes (i.e., lines) for Voronoi vertex
+ &quot;8&quot;. It is at the origin.
+ </dd>
+</dl>
+</blockquote>
+
+<p>Qhull computes the Voronoi diagram via the <a href="qdelaun.htm">Delaunay
+triangulation</a>. Each Voronoi
+vertex is the circumcenter of a facet of the Delaunay
+triangulation. Each Voronoi region corresponds to a vertex (i.e., input site) of the
+Delaunay triangulation. </p>
+
+<p>Qhull outputs the Voronoi vertices for each Voronoi region. With
+option '<a href="qh-optf.htm#Fv2">Fv</a>',
+it lists all ridges of the Voronoi diagram with the corresponding
+pairs of input sites. With
+options '<a href="qh-optf.htm#Fi2">Fi</a>' and '<a href="qh-optf.htm#Fo2">Fo</a>',
+it lists the bounded and unbounded separating hyperplanes.
+You can also output a single Voronoi region
+for further processing [see <a href="#graphics">graphics</a>].</p>
+
+<p>Use option '<a href="qh-optq.htm#Qz">Qz</a>' if the input is circular, cospherical, or
+nearly so. It improves precision by adding a point "at infinity," above the corresponding paraboloid.
+
+<p>See <a href="http://www.qhull.org/html/qh-faq.htm#TOC">Qhull FAQ</a> - Delaunay and
+Voronoi diagram questions.</p>
+
+<p>The 'qvonoroi' program is equivalent to
+'<a href=qhull.htm#outputs>qhull v</a> <a href=qh-optq.htm#Qbb>Qbb</a>' in 2-d to 3-d, and
+'<a href=qhull.htm#outputs>qhull v</a> <a href=qh-optq.htm#Qbb>Qbb</a> <a href=qh-optq.htm#Qx>Qx</a>'
+in 4-d and higher. It disables the following Qhull
+<a href=qh-quick.htm#options>options</a>: <i>d n v Qbb QbB Qf Qg Qm
+Qr QR Qv Qx Qz TR E V Fa FA FC FD FS Ft FV Gt Q0,etc</i>.
+
+<p><b>Copyright &copy; 1995-2015 C.B. Barber</b></p>
+
+<p>Voronoi image by KOOK Architecture, Silvan Oesterle and Michael Knauss.
+
+<hr>
+<h3><a href="#TOP">&#187;</a><a name="synopsis">qvoronoi synopsis</a></h3>
+
+<pre>
+qvoronoi- compute the Voronoi diagram.
+ input (stdin): dimension, number of points, point coordinates
+ comments start with a non-numeric character
+
+options (qh-voron.htm):
+ Qu - compute furthest-site Voronoi diagram
+ Tv - verify result: structure, convexity, and in-circle test
+ . - concise list of all options
+ - - one-line description of all options
+
+output options (subset):
+ s - summary of results (default)
+ p - Voronoi vertices
+ o - OFF file format (dim, Voronoi vertices, and Voronoi regions)
+ FN - count and Voronoi vertices for each Voronoi region
+ Fv - Voronoi diagram as Voronoi vertices between adjacent input sites
+ Fi - separating hyperplanes for bounded regions, 'Fo' for unbounded
+ G - Geomview output (2-d only)
+ QVn - Voronoi vertices for input point n, -n if not
+ TO file- output results to file, may be enclosed in single quotes
+
+examples:
+rbox c P0 D2 | qvoronoi s o rbox c P0 D2 | qvoronoi Fi
+rbox c P0 D2 | qvoronoi Fo rbox c P0 D2 | qvoronoi Fv
+rbox c P0 D2 | qvoronoi s Qu Fv rbox c P0 D2 | qvoronoi Qu Fo
+rbox c G1 d D2 | qvoronoi s p rbox c P0 D2 | qvoronoi s Fv QV0
+</pre>
+
+<h3><a href="#TOP">&#187;</a><a name="input">qvoronoi input</a></h3>
+<blockquote>
+The input data on <tt>stdin</tt> consists of:
+<ul>
+ <li>dimension
+ <li>number of points</li>
+ <li>point coordinates</li>
+</ul>
+
+<p>Use I/O redirection (e.g., qvoronoi &lt; data.txt), a pipe (e.g., rbox 10 | qvoronoi),
+or the '<a href=qh-optt.htm#TI>TI</a>' option (e.g., qvoronoi TI data.txt).
+
+<p>For example, this is four cocircular points inside a square. Their Voronoi
+diagram has nine vertices and eight regions. Notice the Voronoi vertex
+at the origin, and the Voronoi vertices (on each axis) for the four
+sides of the square.
+<p>
+<blockquote>
+<tt>rbox s 4 W0 c G1 D2 &gt; data</tt>
+<blockquote><pre>
+2 RBOX s 4 W0 c D2
+8
+-0.4941988586954018 -0.07594397977563715
+-0.06448037284989526 0.4958248496365813
+0.4911154367094632 0.09383830681375946
+-0.348353580869097 -0.3586778257652367
+ -1 -1
+ -1 1
+ 1 -1
+ 1 1
+</pre></blockquote>
+
+<p><tt>qvoronoi s p &lt; data</tt>
+<blockquote><pre>
+
+Voronoi diagram by the convex hull of 8 points in 3-d:
+
+ Number of Voronoi regions: 8
+ Number of Voronoi vertices: 9
+ Number of non-simplicial Voronoi vertices: 1
+
+Statistics for: RBOX s 4 W0 c D2 | QVORONOI s p
+
+ Number of points processed: 8
+ Number of hyperplanes created: 18
+ Number of facets in hull: 10
+ Number of distance tests for qhull: 33
+ Number of merged facets: 2
+ Number of distance tests for merging: 102
+ CPU seconds to compute hull (after input): 0.094
+
+2
+9
+4.217546450968612e-17 1.735507986399734
+-8.402566836762659e-17 -1.364368854147395
+0.3447488772716865 -0.6395484723719818
+1.719446929853986 2.136555906154247e-17
+0.4967882915039657 0.68662371396699
+-1.729928876283549 1.343733067524222e-17
+-0.8906163241424728 -0.4594150543829102
+-0.6656840313875723 0.5003013793414868
+-7.318364664277155e-19 -1.188217818408333e-16
+</pre></blockquote>
+</blockquote>
+
+</blockquote>
+<h3><a href="#TOP">&#187;</a> <a name="outputs">qvoronoi
+outputs</a></h3>
+<blockquote>
+
+<p>These options control the output of Voronoi diagrams.</p>
+<blockquote>
+
+<dl compact>
+ <dt>&nbsp;</dt>
+ <dd><b>Voronoi vertices</b></dd>
+ <dt><a href="qh-opto.htm#p">p</a></dt>
+ <dd>print the coordinates of the Voronoi vertices. The first line
+ is the dimension. The second line is the number of vertices. Each
+ remaining line is a Voronoi vertex.</dd>
+ <dt><a href="qh-optf.htm#Fn">Fn</a></dt>
+ <dd>list the neighboring Voronoi vertices for each Voronoi
+ vertex. The first line is the number of Voronoi vertices. Each
+ remaining line starts with the number of neighboring vertices.
+ Negative vertices (e.g., <em>-1</em>) indicate vertices
+ outside of the Voronoi diagram.
+ In the circle-in-box example, the
+ Voronoi vertex at the origin has four neighbors.</dd>
+ <dt><a href="qh-optf.htm#FN">FN</a></dt>
+ <dd>list the Voronoi vertices for each Voronoi region. The first line is
+ the number of Voronoi regions. Each remaining line starts with the
+ number of Voronoi vertices. Negative indices (e.g., <em>-1</em>) indicate vertices
+ outside of the Voronoi diagram.
+ In the circle-in-box example, the four bounded regions are defined by four
+ Voronoi vertices.</dd>
+
+ <dt>&nbsp;</dt>
+ <dt>&nbsp;</dt>
+ <dd><b>Voronoi regions</b></dd>
+ <dt><a href="qh-opto.htm#o">o</a></dt>
+ <dd>print the Voronoi regions in OFF format. The first line is the
+ dimension. The second line is the number of vertices, the number
+ of input sites, and "1". The third line represents the vertex-at-infinity.
+ Its coordinates are "-10.101". The next lines are the coordinates
+ of the Voronoi vertices. Each remaining line starts with the number
+ of Voronoi vertices in a Voronoi region. In 2-d, the vertices are
+listed in adjacency order (unoriented). In 3-d and higher, the
+vertices are listed in numeric order. In the circle-in-square
+ example, each bounded region includes the Voronoi vertex at
+ the origin. Lines consisting of <em>0</em> indicate
+ coplanar input sites or '<a href="qh-optq.htm#Qz">Qz</a>'. </dd>
+ <dt><a href="qh-optf.htm#Fi2">Fi</a></dt>
+ <dd>print separating hyperplanes for inner, bounded Voronoi
+ regions. The first number is the number of separating
+ hyperplanes. Each remaining line starts with <i>3+dim</i>. The
+ next two numbers are adjacent input sites. The next <i>dim</i>
+ numbers are the coefficients of the separating hyperplane. The
+ last number is its offset. Use '<a href="qh-optt.htm#Tv">Tv</a>' to verify that the
+hyperplanes are perpendicular bisectors. It will list relevant
+statistics to stderr. </dd>
+ <dt><a href="qh-optf.htm#Fo2">Fo</a></dt>
+ <dd>print separating hyperplanes for outer, unbounded Voronoi
+ regions. The first number is the number of separating
+ hyperplanes. Each remaining line starts with <i>3+dim</i>. The
+ next two numbers are adjacent input sites on the convex hull. The
+ next <i>dim</i>
+ numbers are the coefficients of the separating hyperplane. The
+ last number is its offset. Use '<a href="qh-optt.htm#Tv">Tv</a>' to verify that the
+hyperplanes are perpendicular bisectors. It will list relevant
+statistics to stderr,</dd>
+ <dt>&nbsp;</dt>
+ <dt>&nbsp;</dt>
+ <dd><b>Input sites</b></dd>
+ <dt><a href="qh-optf.htm#Fv2">Fv</a></dt>
+ <dd>list ridges of Voronoi vertices for pairs of input sites. The
+ first line is the number of ridges. Each remaining line starts with
+ two plus the number of Voronoi vertices in the ridge. The next
+ two numbers are two adjacent input sites. The remaining numbers list
+ the Voronoi vertices. As with option 'o', a <em>0</em> indicates
+ the vertex-at-infinity
+ and an unbounded, separating hyperplane.
+ The perpendicular bisector (separating hyperplane)
+ of the input sites is a flat through these vertices.
+ In the circle-in-square example, the ridge for each edge of the square
+ is unbounded.</dd>
+ <dt><a href="qh-optf.htm#Fc">Fc</a></dt>
+ <dd>list coincident input sites for each Voronoi vertex.
+ The first line is the number of vertices. The remaining lines start with
+ the number of coincident sites and deleted vertices. Deleted vertices
+ indicate highly degenerate input (see'<a href="qh-optf.htm#Fs">Fs</a>').
+ A coincident site is assigned to one Voronoi
+ vertex. Do not use '<a href="qh-optq.htm#QJn">QJ</a>' with 'Fc'; the joggle will separate
+ coincident sites.</dd>
+ <dt><a href="qh-optf.htm#FP">FP</a></dt>
+ <dd>print coincident input sites with distance to
+ nearest site (i.e., vertex). The first line is the
+ number of coincident sites. Each remaining line starts with the point ID of
+ an input site, followed by the point ID of a coincident point, its vertex, and distance.
+ Includes deleted vertices which
+ indicate highly degenerate input (see'<a href="qh-optf.htm#Fs">Fs</a>').
+ Do not use '<a href="qh-optq.htm#QJn">QJ</a>' with 'FP'; the joggle will separate
+ coincident sites.</dd>
+ <dt>&nbsp;</dt>
+ <dt>&nbsp;</dt>
+ <dd><b>General</b></dd>
+ <dt><a href="qh-opto.htm#s">s</a></dt>
+ <dd>print summary of the Voronoi diagram. Use '<a
+ href="qh-optf.htm#Fs">Fs</a>' for numeric data.</dd>
+ <dt><a href="qh-opto.htm#i">i</a></dt>
+ <dd>list input sites for each <a href=qdelaun.htm>Delaunay region</a>. Use option '<a href="qh-optp.htm#Pp">Pp</a>'
+ to avoid the warning. The first line is the number of regions. The
+ remaining lines list the input sites for each region. The regions are
+ oriented. In the circle-in-square example, the cocircular region has four
+ edges. In 3-d and higher, report cospherical sites by adding extra points.
+ </dd>
+ <dt><a href="qh-optg.htm#G">G</a></dt>
+ <dd>Geomview output for 2-d Voronoi diagrams.</dd>
+ </dl>
+</blockquote>
+</blockquote>
+<h3><a href="#TOP">&#187;</a> <a name="controls">qvoronoi
+controls</a></h3>
+<blockquote>
+
+<p>These options provide additional control:</p>
+<blockquote>
+
+<dl compact>
+ <dt><a href="qh-optq.htm#Qu">Qu</a></dt>
+ <dd>compute the <a href="qvoron_f.htm">furthest-site Voronoi diagram</a>.</dd>
+ <dt><a href="qh-optq.htm#QVn">QVn</a></dt>
+ <dd>select Voronoi vertices for input site <em>n</em> </dd>
+ <dt><a href="qh-optq.htm#Qz">Qz</a></dt>
+ <dd>add a point above the paraboloid to reduce precision
+ errors. Use it for nearly cocircular/cospherical input
+ (e.g., 'rbox c | qvoronoi Qz').</dd>
+ <dt><a href="qh-optt.htm#Tv">Tv</a></dt>
+ <dd>verify result</dd>
+ <dt><a href="qh-optt.htm#TO">TI file</a></dt>
+ <dd>input data from file. The filename may not use spaces or quotes.</dd>
+ <dt><a href="qh-optt.htm#TO">TO file</a></dt>
+ <dd>output results to file. Use single quotes if the filename
+ contains spaces (e.g., <tt>TO 'file with spaces.txt'</tt></dd>
+ <dt><a href="qh-optt.htm#TFn">TFn</a></dt>
+ <dd>report progress after constructing <em>n</em> facets</dd>
+ <dt><a href="qh-optp.htm#PDk">PDk:1</a></dt>
+ <dd>include upper and lower facets in the output. Set <em>k</em>
+ to the last dimension (e.g., 'PD2:1' for 2-d inputs). </dd>
+ <dt><a href="qh-opto.htm#f">f </a></dt>
+ <dd>facet dump. Print the data structure for each facet (i.e.,
+ Voronoi vertex).</dd>
+</dl>
+
+</blockquote>
+</blockquote>
+<h3><a href="#TOP">&#187;</a> <a name="graphics">qvoronoi
+graphics</a></h3>
+<blockquote>
+
+<p>In 2-d, Geomview output ('<a href="qh-optg.htm#G">G</a>')
+displays a Voronoi diagram with extra edges to close the
+unbounded Voronoi regions. To view the unbounded rays, enclose
+the input points in a square.</p>
+
+<p>You can also view <i>individual</i> Voronoi regions in 3-d. To
+view the Voronoi region for site 3 in Geomview, execute</p>
+
+<blockquote>
+ <p>qvoronoi &lt;data <a href="qh-optq.htm#QVn">QV3</a> <a
+ href="qh-opto.htm#p">p</a> | qconvex s G &gt;output</p>
+</blockquote>
+
+<p>The <tt>qvoronoi</tt> command returns the Voronoi vertices
+for input site 3. The <tt>qconvex</tt> command computes their convex hull.
+This is the Voronoi region for input site 3. Its
+hyperplane normals (qconvex 'n') are the same as the separating hyperplanes
+from options '<a href="qh-optf.htm#Fi">Fi</a>'
+and '<a href="qh-optf.htm#Fo">Fo</a>' (up to roundoff error).
+
+<p>See the <a href="qh-eg.htm#delaunay">Delaunay and Voronoi
+examples</a> for 2-d and 3-d examples. Turn off normalization (on
+Geomview's 'obscure' menu) when comparing the Voronoi diagram
+with the corresponding Delaunay triangulation. </p>
+
+</blockquote>
+<h3><a href="#TOP">&#187;</a><a name="notes">qvoronoi
+notes</a></h3>
+<blockquote>
+
+<p>You can simplify the Voronoi diagram by enclosing the input
+sites in a large square or cube. This is particularly recommended
+for cocircular or cospherical input data.</p>
+
+<p>See <a href="#graphics">Voronoi graphics</a> for computing
+the convex hull of a Voronoi region. </p>
+
+<p>Voronoi diagrams do not include facets that are
+coplanar with the convex hull of the input sites. A facet is
+coplanar if the last coefficient of its normal is
+nearly zero (see <a href="../src/user.h#ZEROdelaunay">qh_ZEROdelaunay</a>).
+
+<p>Unbounded regions can be confusing. For example, '<tt>rbox c |
+qvoronoi Qz o</tt>' produces the Voronoi regions for the vertices
+of a cube centered at the origin. All regions are unbounded. The
+output is </p>
+
+<blockquote>
+ <pre>3
+2 9 1
+-10.101 -10.101 -10.101
+ 0 0 0
+2 0 1
+2 0 1
+2 0 1
+2 0 1
+2 0 1
+2 0 1
+2 0 1
+2 0 1
+0
+</pre>
+</blockquote>
+
+<p>The first line is the dimension. The second line is the number
+of vertices and the number of regions. There is one region per
+input point plus a region for the point-at-infinity added by
+option '<a href="qh-optq.htm#Qz">Qz</a>'. The next two lines
+lists the Voronoi vertices. The first vertex is the infinity
+vertex. It is indicate by the coordinates <em>-10.101</em>. The
+second vertex is the origin. The next nine lines list the
+regions. Each region lists two vertices -- the infinity vertex
+and the origin. The last line is &quot;0&quot; because no region
+is associated with the point-at-infinity. A &quot;0&quot; would
+also be listed for nearly incident input sites. </p>
+
+<p>To use option '<a href="qh-optf.htm#Fv">Fv</a>', add an
+interior point. For example, </p>
+
+<blockquote>
+ <pre>
+rbox c P0 | qvoronoi Fv
+20
+5 0 7 1 3 5
+5 0 3 1 4 5
+5 0 5 1 2 3
+5 0 1 1 2 4
+5 0 6 2 3 6
+5 0 2 2 4 6
+5 0 4 4 5 6
+5 0 8 5 3 6
+5 1 2 0 2 4
+5 1 3 0 1 4
+5 1 5 0 1 2
+5 2 4 0 4 6
+5 2 6 0 2 6
+5 3 4 0 4 5
+5 3 7 0 1 5
+5 4 8 0 6 5
+5 5 6 0 2 3
+5 5 7 0 1 3
+5 6 8 0 6 3
+5 7 8 0 3 5
+</pre>
+</blockquote>
+
+<p>The output consists of 20 ridges and each ridge lists a pair
+of input sites and a triplet of Voronoi vertices. The first eight
+ridges connect the origin ('P0'). The remainder list the edges of
+the cube. Each edge generates an unbounded ray through the
+midpoint. The corresponding separating planes ('Fo') follow each
+pair of coordinate axes. </p>
+
+<p>Options '<a href="qh-optq.htm#Qt">Qt</a>' (triangulated output)
+and '<a href="qh-optq.htm#QJn">QJ</a>' (joggled input) are deprecated. They may produce
+unexpected results. If you use these options, cocircular and cospherical input sites will
+produce duplicate or nearly duplicate Voronoi vertices. See also <a
+href="qh-impre.htm#joggle">Merged facets or joggled input</a>. </p>
+
+</blockquote>
+<h3><a href="#TOP">&#187;</a><a name="conventions">qvoronoi conventions</a></h3>
+<blockquote>
+
+<p>The following terminology is used for Voronoi diagrams in
+Qhull. The underlying structure is a Delaunay triangulation from
+a convex hull in one higher dimension. Facets of the Delaunay
+triangulation correspond to vertices of the Voronoi diagram.
+Vertices of the Delaunay triangulation correspond to input sites.
+They also correspond to regions of the Voronoi diagram. See <a
+href="qconvex.htm#conventions">convex hull conventions</a>, <a
+href="qdelaun.htm#conventions">Delaunay conventions</a>, and
+<a href="index.htm#structure">Qhull's data structures</a>.</p>
+<blockquote>
+
+<ul>
+ <li><em>input site</em> - a point in the input (one dimension
+ lower than a point on the convex hull)</li>
+ <li><em>point</em> - a point has <i>d+1</i> coordinates. The
+ last coordinate is the sum of the squares of the input
+ site's coordinates</li>
+ <li><em>coplanar point</em> - a <em>nearly incident</em>
+ input site</li>
+ <li><em>vertex</em> - a point on the paraboloid. It
+ corresponds to a unique input site. </li>
+ <li><em>point-at-infinity</em> - a point added above the
+ paraboloid by option '<a href="qh-optq.htm#Qz">Qz</a>'</li>
+ <li><em>Delaunay facet</em> - a lower facet of the
+ paraboloid. The last coefficient of its normal is
+ clearly negative.</li>
+ <li><em>Voronoi vertex</em> - the circumcenter of a Delaunay
+ facet</li>
+ <li><em>Voronoi region</em> - the Voronoi vertices for an
+ input site. The region of Euclidean space nearest to an
+ input site.</li>
+ <li><em>Voronoi diagram</em> - the graph of the Voronoi
+ regions. It includes the ridges (i.e., edges) between the
+ regions.</li>
+ <li><em>vertex-at-infinity</em> - the Voronoi vertex that
+ indicates unbounded Voronoi regions in '<a
+ href="qh-opto.htm#o">o</a>' output format. Its
+ coordinates are <em>-10.101</em>.</li>
+ <li><em>good facet</em> - a Voronoi vertex with optional
+ restrictions by '<a href="qh-optq.htm#QVn">QVn</a>', etc.</li>
+</ul>
+
+</blockquote>
+</blockquote>
+<h3><a href="#TOP">&#187;</a><a name="options">qvoronoi options</a></h3>
+
+<pre>
+qvoronoi- compute the Voronoi diagram
+ http://www.qhull.org
+
+input (stdin):
+ first lines: dimension and number of points (or vice-versa).
+ other lines: point coordinates, best if one point per line
+ comments: start with a non-numeric character
+
+options:
+ Qu - compute furthest-site Voronoi diagram
+
+Qhull control options:
+ QJn - randomly joggle input in range [-n,n]
+ Qs - search all points for the initial simplex
+ Qz - add point-at-infinity to Voronoi diagram
+ QGn - Voronoi vertices if visible from point n, -n if not
+ QVn - Voronoi vertices for input point n, -n if not
+
+Trace options:
+ T4 - trace at level n, 4=all, 5=mem/gauss, -1= events
+ Tc - check frequently during execution
+ Ts - statistics
+ Tv - verify result: structure, convexity, and in-circle test
+ Tz - send all output to stdout
+ TFn - report summary when n or more facets created
+ TI file - input data from file, no spaces or single quotes
+ TO file - output results to file, may be enclosed in single quotes
+ TPn - turn on tracing when point n added to hull
+ TMn - turn on tracing at merge n
+ TWn - trace merge facets when width > n
+ TVn - stop qhull after adding point n, -n for before (see TCn)
+ TCn - stop qhull after building cone for point n (see TVn)
+
+Precision options:
+ Cn - radius of centrum (roundoff added). Merge facets if non-convex
+ An - cosine of maximum angle. Merge facets if cosine > n or non-convex
+ C-0 roundoff, A-0.99/C-0.01 pre-merge, A0.99/C0.01 post-merge
+ Rn - randomly perturb computations by a factor of [1-n,1+n]
+ Wn - min facet width for non-coincident point (before roundoff)
+
+Output formats (may be combined; if none, produces a summary to stdout):
+ s - summary to stderr
+ p - Voronoi vertices
+ o - OFF format (dim, Voronoi vertices, and Voronoi regions)
+ i - Delaunay regions (use 'Pp' to avoid warning)
+ f - facet dump
+
+More formats:
+ Fc - count plus coincident points (by Voronoi vertex)
+ Fd - use cdd format for input (homogeneous with offset first)
+ FD - use cdd format for output (offset first)
+ FF - facet dump without ridges
+ Fi - separating hyperplanes for bounded Voronoi regions
+ FI - ID for each Voronoi vertex
+ Fm - merge count for each Voronoi vertex (511 max)
+ Fn - count plus neighboring Voronoi vertices for each Voronoi vertex
+ FN - count and Voronoi vertices for each Voronoi region
+ Fo - separating hyperplanes for unbounded Voronoi regions
+ FO - options and precision constants
+ FP - nearest point and distance for each coincident point
+ FQ - command used for qvoronoi
+ Fs - summary: #int (8), dimension, #points, tot vertices, tot facets,
+ for output: #Voronoi regions, #Voronoi vertices,
+ #coincident points, #non-simplicial regions
+ #real (2), max outer plane and min vertex
+ Fv - Voronoi diagram as Voronoi vertices between adjacent input sites
+ Fx - extreme points of Delaunay triangulation (on convex hull)
+
+Geomview options (2-d only)
+ Ga - all points as dots
+ Gp - coplanar points and vertices as radii
+ Gv - vertices as spheres
+ Gi - inner planes only
+ Gn - no planes
+ Go - outer planes only
+ Gc - centrums
+ Gh - hyperplane intersections
+ Gr - ridges
+ GDn - drop dimension n in 3-d and 4-d output
+
+Print options:
+ PAn - keep n largest Voronoi vertices by 'area'
+ Pdk:n - drop facet if normal[k] &lt;= n (default 0.0)
+ PDk:n - drop facet if normal[k] >= n
+ Pg - print good Voronoi vertices (needs 'QGn' or 'QVn')
+ PFn - keep Voronoi vertices whose 'area' is at least n
+ PG - print neighbors of good Voronoi vertices
+ PMn - keep n Voronoi vertices with most merges
+ Po - force output. If error, output neighborhood of facet
+ Pp - do not report precision problems
+
+ . - list of all options
+ - - one line descriptions of all options
+</pre>
+
+<!-- Navigation links -->
+<hr>
+
+<p><b>Up:</b> <a href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b> <a href="index.htm#TOC">Qhull manual</a>: Table of Contents<br>
+<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
+&#149; <a href="qh-quick.htm#options">Options</a>
+&#149; <a href="qh-opto.htm#output">Output</a>
+&#149; <a href="qh-optf.htm#format">Formats</a>
+&#149; <a href="qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="qh-optp.htm#print">Print</a>
+&#149; <a href="qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="qh-optc.htm#prec">Precision</a>
+&#149; <a href="qh-optt.htm#trace">Trace</a>
+&#149; <a href="../src/libqhull_r/index.htm">Functions</a><br>
+<b>To:</b> <a href="#synopsis">sy</a>nopsis
+&#149; <a href="#input">in</a>put &#149; <a href="#outputs">ou</a>tputs
+&#149; <a href="#controls">co</a>ntrols &#149; <a href="#graphics">gr</a>aphics
+&#149; <a href="#notes">no</a>tes &#149; <a href="#conventions">co</a>nventions
+&#149; <a href="#options">op</a>tions
+<!-- GC common information -->
+<hr>
+
+<p><a href="http://www.geom.uiuc.edu/"><img src="qh--geom.gif"
+align="middle" width="40" height="40"></a><i>The Geometry Center
+Home Page </i></p>
+
+<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a>
+</a><br>
+Created: Sept. 25, 1995 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p>
+</body>
+</html>
diff --git a/xs/src/qhull/html/rbox.htm b/xs/src/qhull/html/rbox.htm
new file mode 100644
index 000000000..9c28face5
--- /dev/null
+++ b/xs/src/qhull/html/rbox.htm
@@ -0,0 +1,277 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+
+<head>
+<title>rbox -- generate point distributions</title>
+</head>
+
+<body>
+<!-- Navigation links -->
+<p><b><a name="TOP">Up:</a></b> <a href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b> <a href="index.htm#TOC">Qhull manual</a>: Table of Contents<br>
+<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
+&#149; <a href="qh-quick.htm#options">Options</a>
+&#149; <a href="qh-opto.htm#output">Output</a>
+&#149; <a href="qh-optf.htm#format">Formats</a>
+&#149; <a href="qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="qh-optp.htm#print">Print</a>
+&#149; <a href="qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="qh-optc.htm#prec">Precision</a>
+&#149; <a href="qh-optt.htm#trace">Trace</a>
+&#149; <a href="../src/libqhull_r/index.htm">Functions</a><br>
+<b>To:</b> <a href="#synopsis">sy</a>nopsis &#149; <a href="#outputs">ou</a>tputs
+&#149; <a href="#examples">ex</a>amples &#149; <a href="#notes">no</a>tes
+&#149; <a href="#options">op</a>tions
+<hr>
+<!-- Main text of document -->
+<h1><a
+href="http://www.geom.uiuc.edu/graphics/pix/Special_Topics/Computational_Geometry/cone.html"><img
+src="qh--cone.gif" alt="[CONE]" align="middle" width="100"
+height="100"></a>rbox -- generate point distributions</h1>
+
+<blockquote>
+ rbox generates random or regular points according to the
+ options given, and outputs the points to stdout. The
+ points are generated in a cube, unless 's', 'x', or 'y'
+ are given.
+
+</blockquote>
+<h3><a href="#TOP">&#187;</a><a name="synopsis">rbox synopsis</a></h3>
+<pre>
+rbox- generate various point distributions. Default is random in cube.
+
+args (any order, space separated):
+ 3000 number of random points in cube, lens, spiral, sphere or grid
+ D3 dimension 3-d
+ c add a unit cube to the output ('c G2.0' sets size)
+ d add a unit diamond to the output ('d G2.0' sets size)
+ l generate a regular 3-d spiral
+ r generate a regular polygon, ('r s Z1 G0.1' makes a cone)
+ s generate cospherical points
+ x generate random points in simplex, may use 'r' or 'Wn'
+ y same as 'x', plus simplex
+ Cn,r,m add n nearly coincident points within radius r of m points
+ Pn,m,r add point [n,m,r] first, pads with 0
+
+ Ln lens distribution of radius n. Also 's', 'r', 'G', 'W'.
+ Mn,m,r lattice (Mesh) rotated by [n,-m,0], [m,n,0], [0,0,r], ...
+ '27 M1,0,1' is {0,1,2} x {0,1,2} x {0,1,2}. Try 'M3,4 z'.
+ W0.1 random distribution within 0.1 of the cube's or sphere's surface
+ Z0.5 s random points in a 0.5 disk projected to a sphere
+ Z0.5 s G0.6 same as Z0.5 within a 0.6 gap
+
+ Bn bounding box coordinates, default 0.5
+ h output as homogeneous coordinates for cdd
+ n remove command line from the first line of output
+ On offset coordinates by n
+ t use time as the random number seed (default is command line)
+ tn use n as the random number seed
+ z print integer coordinates, default 'Bn' is 1e+06
+</pre>
+
+<h3><a href="#TOP">&#187;</a><a name="outputs">rbox outputs</a></h3>
+<blockquote>
+
+The format of the output is the following: first line contains
+ the dimension and a comment, second line contains the
+ number of points, and the following lines contain the points,
+ one point per line. Points are represented by their coordinate values.
+
+<p>For example, <tt>rbox c 10 D2</tt> generates
+<blockquote>
+<pre>
+2 RBOX c 10 D2
+14
+-0.4999921736307369 -0.3684622117955817
+0.2556053225468894 -0.0413498678629751
+0.0327672376602583 -0.2810408135699488
+-0.452955383763607 0.17886471718444
+0.1792964061529342 0.4346928963760779
+-0.1164979223315585 0.01941637230982666
+0.3309653464993139 -0.4654278894564396
+-0.4465383649305798 0.02970019358182344
+0.1711493843897706 -0.4923018137852678
+-0.1165843490665633 -0.433157762450313
+ -0.5 -0.5
+ -0.5 0.5
+ 0.5 -0.5
+ 0.5 0.5
+</pre>
+
+</blockquote>
+
+</blockquote>
+<h3><a href="#TOP">&#187;</a><a name="examples">rbox examples</a></h3>
+
+<pre>
+ rbox 10
+ 10 random points in the unit cube centered at the
+ origin.
+
+ rbox 10 s D2
+ 10 random points on a 2-d circle.
+
+ rbox 100 W0
+ 100 random points on the surface of a cube.
+
+ rbox 1000 s D4
+ 1000 random points on a 4-d sphere.
+
+ rbox c D5 O0.5
+ a 5-d hypercube with one corner at the origin.
+
+ rbox d D10
+ a 10-d diamond.
+
+ rbox x 1000 r W0
+ 100 random points on the surface of a fixed simplex
+
+ rbox y D12
+ a 12-d simplex.
+
+ rbox l 10
+ 10 random points along a spiral
+
+ rbox l 10 r
+ 10 regular points along a spiral plus two end
+ points
+
+ rbox 1000 L10000 D4 s
+ 1000 random points on the surface of a narrow lens.
+
+ rbox 1000 L100000 s G1e-6
+ 1000 random points near the edge of a narrow lens
+
+ rbox c G2 d G3
+ a cube with coordinates +2/-2 and a diamond with
+ coordinates +3/-3.
+
+ rbox 64 M3,4 z
+ a rotated, {0,1,2,3} x {0,1,2,3} x {0,1,2,3} lat-
+ tice (Mesh) of integer points.
+
+ rbox P0 P0 P0 P0 P0
+ 5 copies of the origin in 3-d. Try 'rbox P0 P0 P0
+ P0 P0 | qhull QJ'.
+
+ r 100 s Z1 G0.1
+ two cospherical 100-gons plus another cospherical
+ point.
+
+ 100 s Z1
+ a cone of points.
+
+ 100 s Z1e-7
+ a narrow cone of points with many precision errors.
+</pre>
+
+<h3><a href="#TOP">&#187;</a><a name="notes">rbox notes</a></h3>
+<blockquote>
+Some combinations of arguments generate odd results.
+
+</blockquote>
+<h3><a href="#TOP">&#187;</a><a name="options">rbox options</a></h3>
+
+<pre>
+ n number of points
+
+ Dn dimension n-d (default 3-d)
+
+ Bn bounding box coordinates (default 0.5)
+
+ l spiral distribution, available only in 3-d
+
+ Ln lens distribution of radius n. May be used with
+ 's', 'r', 'G', and 'W'.
+
+ Mn,m,r lattice (Mesh) rotated by {[n,-m,0], [m,n,0],
+ [0,0,r], ...}. Use 'Mm,n' for a rigid rotation
+ with r = sqrt(n^2+m^2). 'M1,0' is an orthogonal
+ lattice. For example, '27 M1,0' is {0,1,2} x
+ {0,1,2} x {0,1,2}.
+
+ s cospherical points randomly generated in a cube and
+ projected to the unit sphere
+
+ x simplicial distribution. It is fixed for option
+ 'r'. May be used with 'W'.
+
+ y simplicial distribution plus a simplex. Both 'x'
+ and 'y' generate the same points.
+
+ Wn restrict points to distance n of the surface of a
+ sphere or a cube
+
+ c add a unit cube to the output
+
+ c Gm add a cube with all combinations of +m and -m to
+ the output
+
+ d add a unit diamond to the output.
+
+ d Gm add a diamond made of 0, +m and -m to the output
+
+ Cn,r,m add n nearly coincident points within radius r of m points
+
+ Pn,m,r add point [n,m,r] to the output first. Pad coordi-
+ nates with 0.0.
+
+ n Remove the command line from the first line of out-
+ put.
+
+ On offset the data by adding n to each coordinate.
+
+ t use time in seconds as the random number seed
+ (default is command line).
+
+ tn set the random number seed to n.
+
+ z generate integer coordinates. Use 'Bn' to change
+ the range. The default is 'B1e6' for six-digit
+ coordinates. In R^4, seven-digit coordinates will
+ overflow hyperplane normalization.
+
+ Zn s restrict points to a disk about the z+ axis and the
+ sphere (default Z1.0). Includes the opposite pole.
+ 'Z1e-6' generates degenerate points under single
+ precision.
+
+ Zn Gm s
+ same as Zn with an empty center (default G0.5).
+
+ r s D2 generate a regular polygon
+
+ r s Z1 G0.1
+ generate a regular cone
+</pre>
+
+<!-- Navigation links -->
+<hr>
+
+<p><b>Up:</b> <a href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b> <a href="index.htm#TOC">Qhull manual</a>: Table of Contents<br>
+<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
+&#149; <a href="qh-quick.htm#options">Options</a>
+&#149; <a href="qh-opto.htm#output">Output</a>
+&#149; <a href="qh-optf.htm#format">Formats</a>
+&#149; <a href="qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="qh-optp.htm#print">Print</a>
+&#149; <a href="qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="qh-optc.htm#prec">Precision</a>
+&#149; <a href="qh-optt.htm#trace">Trace</a>
+&#149; <a href="../src/libqhull_r/index.htm">Functions</a><br>
+<b>To:</b> <a href="#synopsis">sy</a>nopsis &#149; <a href="#outputs">ou</a>tputs
+&#149; <a href="#examples">ex</a>amples &#149; <a href="#notes">no</a>tes
+&#149; <a href="#options">op</a>tions
+<!-- GC common information -->
+<hr>
+
+<p><a href="http://www.geom.uiuc.edu/"><img src="qh--geom.gif"
+align="middle" width="40" height="40"></a><i>The Geometry Center
+Home Page </i></p>
+
+<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a>
+<br>
+Created: Sept. 25, 1995 --- <!-- hhmts start --> Last modified: August 12, 1998 <!-- hhmts end --> </p>
+</body>
+</html>
diff --git a/xs/src/qhull/html/rbox.man b/xs/src/qhull/html/rbox.man
new file mode 100644
index 000000000..3ea6395e6
--- /dev/null
+++ b/xs/src/qhull/html/rbox.man
@@ -0,0 +1,176 @@
+.\" This is the Unix manual page for rbox, written in nroff, the standard
+.\" manual formatter for Unix systems. To format it, type
+.\"
+.\" nroff -man rbox.man
+.\"
+.\" This will print a formatted copy to standard output. If you want
+.\" to ensure that the output is plain ascii, free of any control
+.\" characters that nroff uses for underlining etc, pipe the output
+.\" through "col -b":
+.\"
+.\" nroff -man rbox.man | col -b
+.\"
+.TH rbox 1 "August 10, 1998" "Geometry Center"
+.SH NAME
+rbox \- generate point distributions for qhull
+.SH SYNOPSIS
+Command "rbox" (w/o arguments) lists the options.
+.SH DESCRIPTION
+.PP
+rbox generates random or regular points according to the options given, and
+outputs
+the points to stdout. The points are generated in a cube, unless 's' or 'k'
+option is
+given. The format of the output is the following: first line
+contains the dimension and a comment,
+second line contains the number of points, and the
+following lines contain the points, one point per line. Points are represented
+by their coordinate values.
+.SH EXAMPLES
+.TP
+rbox 10
+10 random points in the unit cube centered at the origin.
+.TP
+rbox 10 s D2
+10 random points on a 2\[hy]d circle.
+.TP
+rbox 100 W0
+100 random points on the surface of a cube.
+.TP
+rbox 1000 s D4
+1000 random points on a 4\[hy]d sphere.
+.TP
+rbox c D5 O0.5
+a 5\[hy]d hypercube with one corner at the origin.
+.TP
+rbox d D10
+a 10\[hy]d diamond.
+.TP
+rbox x 1000 r W0
+100 random points on the surface of a fixed simplex
+.TP
+rbox y D12
+a 12\[hy]d simplex.
+.TP
+rbox l 10
+10 random points along a spiral
+.TP
+rbox l 10 r
+10 regular points along a spiral plus two end points
+.TP
+rbox 1000 L10000 D4 s
+1000 random points on the surface of a narrow lens.
+.TP
+rbox c G2 d G3
+a cube with coordinates +2/\-2 and a diamond with coordinates +3/\-3.
+.TP
+rbox 64 M3,4 z
+a rotated, {0,1,2,3} x {0,1,2,3} x {0,1,2,3} lattice (Mesh) of integer
+points. 'rbox 64 M1,0' is orthogonal.
+.TP
+rbox P0 P0 P0 P0 P0
+5 copies of the origin in 3\-d. Try 'rbox P0 P0 P0 P0 P0 | qhull QJ'.
+.TP
+r 100 s Z1 G0.1
+two cospherical 100\-gons plus another cospherical point.
+.TP
+100 s Z1
+a cone of points.
+.TP
+100 s Z1e\-7
+a narrow cone of points with many precision errors.
+.SH OPTIONS
+.TP
+n
+number of points
+.TP
+Dn
+dimension n\[hy]d (default 3\[hy]d)
+.TP
+Bn
+bounding box coordinates (default 0.5)
+.TP
+l
+spiral distribution, available only in 3\[hy]d
+.TP
+Ln
+lens distribution of radius n. May be used with 's', 'r', 'G', and 'W'.
+.TP
+Mn,m,r
+lattice (Mesh) rotated by {[n,\-m,0], [m,n,0], [0,0,r], ...}.
+Use 'Mm,n' for a rigid rotation with r = sqrt(n^2+m^2). 'M1,0' is an
+orthogonal lattice. For example, '27 M1,0' is {0,1,2} x {0,1,2} x
+{0,1,2}. '27 M3,4 z' is a rotated integer lattice.
+.TP
+s
+cospherical points randomly generated in a cube and projected to the unit sphere
+.TP
+x
+simplicial distribution. It is fixed for option 'r'. May be used with 'W'.
+.TP
+y
+simplicial distribution plus a simplex. Both 'x' and 'y' generate the same points.
+.TP
+Wn
+restrict points to distance n of the surface of a sphere or a cube
+.TP
+c
+add a unit cube to the output
+.TP
+c Gm
+add a cube with all combinations of +m and \-m to the output
+.TP
+d
+add a unit diamond to the output.
+.TP
+d Gm
+add a diamond made of 0, +m and \-m to the output
+.TP
+Cn,r,m
+add n nearly coincident points within radius r of m points
+.TP
+Pn,m,r
+add point [n,m,r] to the output first. Pad coordinates with 0.0.
+.TP
+n
+Remove the command line from the first line of output.
+.TP
+On
+offset the data by adding n to each coordinate.
+.TP
+t
+use time in seconds as the random number seed (default is command line).
+.TP
+tn
+set the random number seed to n.
+.TP
+z
+generate integer coordinates. Use 'Bn' to change the range.
+The default is 'B1e6' for six\[hy]digit coordinates. In R^4, seven\[hy]digit
+coordinates will overflow hyperplane normalization.
+.TP
+Zn s
+restrict points to a disk about the z+ axis and the sphere (default Z1.0).
+Includes the opposite pole. 'Z1e\-6' generates degenerate points under
+single precision.
+.TP
+Zn Gm s
+same as Zn with an empty center (default G0.5).
+.TP
+r s D2
+generate a regular polygon
+.TP
+r s Z1 G0.1
+generate a regular cone
+.SH BUGS
+Some combinations of arguments generate odd results.
+
+Report bugs to qhull_bug@qhull.org, other correspondence to qhull@qhull.org
+.SH SEE ALSO
+qhull(1)
+.SH AUTHOR
+.nf
+C. Bradford Barber
+bradb@shore.net
+.fi
+
diff --git a/xs/src/qhull/html/rbox.txt b/xs/src/qhull/html/rbox.txt
new file mode 100644
index 000000000..e3cf72189
--- /dev/null
+++ b/xs/src/qhull/html/rbox.txt
@@ -0,0 +1,195 @@
+
+
+
+rbox(1) rbox(1)
+
+
+NAME
+ rbox - generate point distributions for qhull
+
+SYNOPSIS
+ Command "rbox" (w/o arguments) lists the options.
+
+DESCRIPTION
+ rbox generates random or regular points according to the
+ options given, and outputs the points to stdout. The
+ points are generated in a cube, unless 's' or given. The
+ format of the output is the following: first line contains
+ the dimension and a comment, second line contains the num-
+ ber of points, and the following lines contain the points,
+ one point per line. Points are represented by their coor-
+ dinate values.
+
+EXAMPLES
+ rbox 10
+ 10 random points in the unit cube centered at the
+ origin.
+
+ rbox 10 s D2
+ 10 random points on a 2-d circle.
+
+ rbox 100 W0
+ 100 random points on the surface of a cube.
+
+ rbox 1000 s D4
+ 1000 random points on a 4-d sphere.
+
+ rbox c D5 O0.5
+ a 5-d hypercube with one corner at the origin.
+
+ rbox d D10
+ a 10-d diamond.
+
+ rbox x 1000 r W0
+ 100 random points on the surface of a fixed simplex
+
+ rbox y D12
+ a 12-d simplex.
+
+ rbox l 10
+ 10 random points along a spiral
+
+ rbox l 10 r
+ 10 regular points along a spiral plus two end
+ points
+
+ rbox 1000 L10000 D4 s
+ 1000 random points on the surface of a narrow lens.
+
+ rbox c G2 d G3
+ a cube with coordinates +2/-2 and a diamond with
+
+
+
+Geometry Center August 10, 1998 1
+
+
+
+
+
+rbox(1) rbox(1)
+
+
+ coordinates +3/-3.
+
+ rbox 64 M3,4 z
+ a rotated, {0,1,2,3} x {0,1,2,3} x {0,1,2,3} lat-
+ tice (Mesh) of integer points.
+
+ rbox P0 P0 P0 P0 P0
+ 5 copies of the origin in 3-d. Try 'rbox P0 P0 P0
+ P0 P0 | qhull QJ'.
+
+ r 100 s Z1 G0.1
+ two cospherical 100-gons plus another cospherical
+ point.
+
+ 100 s Z1
+ a cone of points.
+
+ 100 s Z1e-7
+ a narrow cone of points with many precision errors.
+
+OPTIONS
+ n number of points
+
+ Dn dimension n-d (default 3-d)
+
+ Bn bounding box coordinates (default 0.5)
+
+ l spiral distribution, available only in 3-d
+
+ Ln lens distribution of radius n. May be used with
+ 's', 'r', 'G', and 'W'.
+
+ Mn,m,r lattice (Mesh) rotated by {[n,-m,0], [m,n,0],
+ [0,0,r], ...}. Use 'Mm,n' for a rigid rotation
+ with r = sqrt(n^2+m^2). 'M1,0' is an orthogonal
+ lattice. For example, '27 M1,0' is {0,1,2} x
+ {0,1,2} x {0,1,2}.
+
+ s cospherical points randomly generated in a cube and
+ projected to the unit sphere
+
+ x simplicial distribution. It is fixed for option
+ 'r'. May be used with 'W'.
+
+ y simplicial distribution plus a simplex. Both 'x'
+ and 'y' generate the same points.
+
+ Wn restrict points to distance n of the surface of a
+ sphere or a cube
+
+ c add a unit cube to the output
+
+ c Gm add a cube with all combinations of +m and -m to
+ the output
+
+
+
+Geometry Center August 10, 1998 2
+
+
+
+
+
+rbox(1) rbox(1)
+
+
+ d add a unit diamond to the output.
+
+ d Gm add a diamond made of 0, +m and -m to the output
+
+ Cn,r,m add n nearly coincident points within radius r of m points
+
+ Pn,m,r add point [n,m,r] to the output first. Pad coordi-
+ nates with 0.0.
+
+ n Remove the command line from the first line of out-
+ put.
+
+ On offset the data by adding n to each coordinate.
+
+ t use time in seconds as the random number seed
+ (default is command line).
+
+ tn set the random number seed to n.
+
+ z generate integer coordinates. Use 'Bn' to change
+ the range. The default is 'B1e6' for six-digit
+ coordinates. In R^4, seven-digit coordinates will
+ overflow hyperplane normalization.
+
+ Zn s restrict points to a disk about the z+ axis and the
+ sphere (default Z1.0). Includes the opposite pole.
+ 'Z1e-6' generates degenerate points under single
+ precision.
+
+ Zn Gm s
+ same as Zn with an empty center (default G0.5).
+
+ r s D2 generate a regular polygon
+
+ r s Z1 G0.1
+ generate a regular cone
+
+BUGS
+ Some combinations of arguments generate odd results.
+
+ Report bugs to qhull_bug@qhull.org, other correspon-
+ dence to qhull@qhull.org
+
+SEE ALSO
+ qhull(1)
+
+AUTHOR
+ C. Bradford Barber
+ bradb@shore.net
+
+
+
+
+
+Geometry Center August 10, 1998 3
+
+
diff --git a/xs/src/qhull/index.htm b/xs/src/qhull/index.htm
new file mode 100644
index 000000000..4ea7806c9
--- /dev/null
+++ b/xs/src/qhull/index.htm
@@ -0,0 +1,284 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+
+<head>
+<title>Qhull code for Convex Hull, Delaunay Triangulation, Voronoi Diagram, and Halfspace Intersection about a Point</title>
+</head>
+
+<body>
+<!-- Navigation links -->
+<b>URL:</b> <a href="http://www.qhull.org">http://www.qhull.org</a>
+<br><b>To:</b>
+<a href="http://www.qhull.org/news">News</a>
+&#149; <a href="http://www.qhull.org/download">Download</a>
+&#149; <a href="http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.117.405">CiteSeer</a>
+&#149; <a href=http://images.google.com/images?q=qhull&num=100>Images</a>
+&#149; <a href="html/index.htm#TOC">Manual</a>
+&#149; <a href="http://www.qhull.org/html/qh-faq.htm">FAQ</a>
+&#149; <a href="html/qh-quick.htm#programs">Programs</a>
+&#149; <a href="html/qh-quick.htm#options">Options</a>
+</p>
+
+<hr>
+<!-- Main text of document -->
+<table>
+<tr><td valign=top>
+ <h1>Qhull</h1>
+ <a
+ href="http://www.geom.uiuc.edu/graphics/pix/Special_Topics/Computational_Geometry/cone.html"><img
+ src="html/qh--cone.gif" alt="[CONE]" align="middle" width="100"
+ height="100"></a>
+</td><td>
+Qhull computes the convex hull, Delaunay triangulation, Voronoi diagram,
+halfspace intersection about a point, furthest-site Delaunay
+triangulation, and furthest-site Voronoi diagram. The source code runs in
+2-d, 3-d, 4-d, and higher dimensions. Qhull implements the Quickhull
+algorithm for computing the convex hull. It handles roundoff
+errors from floating point arithmetic. It computes volumes,
+surface areas, and approximations to the convex hull.</p>
+
+<!-- duplicated in index.htm and html/index.htm -->
+<p>Qhull does <i>not</i> support triangulation of non-convex surfaces, mesh
+generation of non-convex objects, medium-sized inputs in 9-D
+and higher, alpha shapes, weighted Voronoi diagrams, Voronoi volumes, or
+constrained Delaunay triangulations, </p>
+
+<p>Qhull 2015.2 introduces reentrant Qhull. It allows concurrent Qhull runs and simplifies the C++ interface to Qhull.
+If you call Qhull from your program, you should use reentrant Qhull (libqhull_r) instead of qh_QHpointer (libqhull).
+If you use Qhull 2003.1. please upgrade or apply <a href="http://www.qhull.org/download/poly.c-qh_gethash.patch">poly.c-qh_gethash.patch</a>.
+</p>
+</td></tr></table>
+
+<hr>
+<form method=get action=http://www.google.com/search>
+<input type=hidden name=sitesearch value=www.qhull.org>
+<input type=hidden name=num value=100>
+<ul>
+ <li><a href="http://www.qhull.org/news">News</a> and
+ <a href="http://www.qhull.org/news/qhull-news.html#bugs">Bugs</a>
+ about Qhull 2015.2 2016/01/18</li>
+ <li><a href="http://www.qhull.org/download">Download</a> Qhull (<a href="http://www.qhull.org/src/Changes.txt">changes</a>)</li>
+ <li><a href=http://github.com/qhull/qhull/wiki>GitHub</a> C++ interface to Qhull
+ <li><a href="html/index.htm">Manual</a> for Qhull and rbox
+ <li><a href="html/index.htm#geomview">Geomview</a> for 3-D and 4-D visualization of Qhull output
+ <li><input name=as_q size=10 value="">
+ <input type="submit" value="Search">
+ www.qhull.org
+ <p>
+ <li><a href="http://www.qhull.org/news/qhull-news.html#users">How</a> is Qhull used?</li>
+ <li><a href="http://scholar.google.com/scholar?cites=13151392091060773178&as_sdt=40000005">Google Scholar</a>,
+ <a href="http://libra.msra.cn/Publication/232063/the-quickhull-algorithm-for-convex-hulls">Microsoft Academic</a>,
+ and <a href="http://citeseerx.ist.psu.edu/showciting?doi=10.1.1.117.405&sort=cite">CiteSeer</a>
+ references to Qhull
+ <li>
+ <a href=http://www.google.com/search?as_q=qhull+-debian+-cvs+-gentoo+-pool+-mirrors&num=100>Google</a> Qhull,
+ <a href="http://images.google.com/images?q=qhull&num=100">Images</a>,
+ <a href="http://www.google.com/#q=qhull&tbm=bks">Books</a>,
+ <a href="http://www.google.com/search?q=qhull&tbm=pts">Patents</a>,
+ <a href="http://groups.google.com/groups?as_q=qhull&num=100&as_scoring=d">Newsgroups</a>,
+ <a href="http://www.google.com/search?q=qhull&tbm=blg">Blogs</a>,
+ and <a href=http://www.googlism.com/who_is/q/qhull/>Who is</a> Qhull?
+
+
+ <p>
+ <li><a href=http://www.mathworks.com/>MATLAB</a> uses Qhull for their n-d computational geometry functions:
+ <a href=http://www.mathworks.com/help/techdoc/ref/convhulln.html>convhulln</a>
+ <a href=http://www.mathworks.com/help/techdoc/ref/delaunayn.html>delaunayn</a>
+ <a href=http://www.mathworks.com/help/techdoc/ref/griddatan.html>griddatan</a>
+ <a href=http://www.mathworks.com/help/techdoc/ref/voronoin.html>voronoin</a>.
+ </li>
+ <li>The <a href="http://cran.r-project.org/web/packages/geometry/geometry.pdf">geometry</a> package of <a href="http://www.r-project.org/">R</a> provides <a href="http://geometry.r-forge.r-project.org/">Qhull in R</a>.
+ <li>The <a href="http://packages.debian.org/sid/octave3.2">Debian build</a> of
+ <a href=http://www.octave.org/>GNU Octave</a> includes Qhull for <a href="http://www.gnu.org/software/octave/doc/interpreter/Geometry.html">computational geometry<a>.
+ <li><a href=http://www.wolfram.com/products/mathematica/>Mathematica</a>'s Delaunay interface <a href=http://library.wolfram.com/infocenter/MathSource/1160/>qh-math</a>
+ and <a href="http://portal.uni-freiburg.de/imteksimulation/downloads/ims">QHullInterface</a>
+ <li>The <a href="http://docs.scipy.org/doc/scipy/reference/tutorial/spatial.html">scipy.spatial</a> of <a href="http://scipy.org/">SciPy</a> is implemented with Qhull. It includes
+ support for Delaunay triangulations,
+</ul>
+</form>
+
+<p><b>Introduction</b>
+<ul>
+ <li><a
+ href="http://www.cs.mcgill.ca/~fukuda/soft/polyfaq/polyfaq.html"
+ >Fukuda's introduction</a> to convex hulls, Delaunay
+ triangulations, Voronoi diagrams, and linear programming</li>
+ <li><a
+ href="http://www.cse.unsw.edu.au/~lambert/java/3d/hull.html"
+ >Lambert's Java</a> visualization of convex hull algorithms </li>
+ <li><a
+ href="http://www.algorithmic-solutions.info/leda_guide/geometryalgorithms.html"
+ >LEDA Guide</a> to geometry algorithms
+ <li><a
+ href="http://mathworld.wolfram.com/ComputationalGeometry.html"
+ >MathWorld's</a> Computational Geometry from Wolfram Research
+ <li><a
+ href="http://www.cs.sunysb.edu/~algorith/major_section/1.6.shtml"
+ >Skiena's</a> Computational Geometry from his <i>Algorithm Design Manual</i>.
+ <li><a
+ href="http://www.cs.sunysb.edu/~algorith/major_section/1.6.shtml"
+ >Stony Brook</a> Algorithm Repository, computational geometry</li>
+</ul>
+
+<p><b>Qhull Documentation and Support</b>
+<ul>
+ <li><a href="html/index.htm">Manual</a> for Qhull and rbox
+ <table><tr><td>
+ <ul>
+ <li><a href="html/index.htm#description">Description</a> of Qhull
+ <li><a href="html/qh-impre.htm">Imprecision</a> in Qhull
+ <li><a href="html/qh-quick.htm#programs">Programs</a> and <a href="html/qh-quick.htm#options">Options</a>
+ quick reference
+ <li><a href="html/qconvex.htm">qconvex</a> -- convex hull
+ <li><a href="html/qdelaun.htm">qdelaunay</a> -- Delaunay triangulation
+ <li><a href="html/qvoronoi.htm">qvoronoi</a> -- Voronoi diagram
+ <li><a href="html/qhalf.htm">qhalf</a> -- halfspace intersection about a point
+ <li><a href="html/rbox.htm">rbox</a> -- generate point distributions
+ </ul></td><td><ul>
+ <li><a href="http://www.qhull.org/html/qh-faq.htm">Frequently</a> asked
+ questions about Qhull</li>
+ <li><a href="html/index.htm#geomview">Geomview</a> for visualizing Qhull
+ <li><a href="COPYING.txt">COPYING.txt</a> - copyright notice<br>
+ <li><a href="REGISTER.txt">REGISTER.txt</a> - registration<br>
+ <li><a href="README.txt">README.txt</a> - installation
+ instructions<br>
+ <li><a href="src/Changes.txt">Changes.txt</a> - change history <br>
+ <li><a href="html/qh-code.htm">Calling Qhull</a> from your program
+ <li><a href="src/libqhull_r/index.htm">Reentrant</a> Qhull functions, macros, and data structures with source
+ </ul>
+ </td></tr></table>
+ <li>Send e-mail to <a href=mailto:qhull@qhull.org>qhull@qhull.org</a> </li>
+ <li>Report bugs to <a
+ href="mailto:qhull_bug@qhull.org">qhull_bug@qhull.org</a>
+</ul>
+
+<p><b>Related URLs</b>
+<ul>
+ <li><a href="http://www.geom.uiuc.edu/software/cglist">Amenta's directory</a> of
+ computational geometry software </li>
+
+ <li><a href=http://www.boost.org/libs/graph/doc/table_of_contents.html>BGL</a>
+ Boost Graph Library provides C++ classes for graph data structures
+and algorithms,
+ <li><a
+ href="http://www.netlib.org/voronoi/hull.html">Clarkson's
+ hull </a>program with exact arithmetic for convex hulls, Delaunay triangulations,
+ Voronoi volumes, and alpha shapes. </li>
+ <li><a href="http://compgeom.cs.uiuc.edu/~jeffe/compgeom/compgeom.html">Erickson's
+ Computational</a> Geometry Pages and
+ <a href="http://compgeom.cs.uiuc.edu/~jeffe/compgeom/code.html">Software</a>
+ <li><a
+ href="http://www.cs.mcgill.ca/~fukuda/soft/cdd_home/cdd.html">Fukuda's
+ cdd</a> program for halfspace intersection and convex hulls (<a
+ href="http://www.csb.ethz.ch/tools/polco">Polco/Java</a>)</li>
+ <li><a href="http://www.inf.ethz.ch/personal/gaertner/miniball.html">Gartner's
+ Miniball</a> for fast and robust smallest enclosing balls (up to 20-d)
+
+ <li><a href=http://www.algorithmic-solutions.com/enleda.htm>Leda</a>
+and <a href=http://www.cgal.org/>CGAL</a> libraries for writing computational
+geometry programs and other combinatorial algorithms
+ <li><a href=http://www.mathtools.net/>Mathtools.net</a> of scientific and engineering
+ software
+ <li><a href="http://www.imr.sandia.gov/papers/topics.html">Owen's International Meshing</a> Roundtable
+ <li><a
+ href="http://www.robertschneiders.de/meshgeneration/meshgeneration.html">Schneiders'
+ Finite Element</a> Mesh Generation page</li>
+ <li><a href="http://www.cs.cmu.edu/~quake/triangle.html">Shewchuk's
+ triangle </a>program for 2-d Delaunay</li>
+ <li><a href=http://www.voronoi.com>Voronoi Web Site</a> for all things Voronoi
+ <li>Young's <a href="http://homepage.usask.ca/~ijm451/finite/fe_resources/">Internet Finite Element Resources</a>
+ <li><a href="http://www.uic.nnov.ru/~zny/skeleton/">Zolotykh's Skeleton</a> generates all extreme rays of a polyhedral cone using the Double Description Method</li>
+ <li><a href="https://github.com/tomilov/quickhull/blob/master/include/quickhull.hpp">Tomilov's quickhull.hpp</a> (<a href="http://habrahabr.ru/post/245221/">doc-ru</a>) implements the Quickhull algorithm for points in general position.
+</ul>
+
+<p><b>FAQs and Newsgroups</b>
+<ul>
+ <li><a
+ href="http://www.faqs.org/faqs/graphics/algorithms-faq/">FAQ</a>
+ for computer graphics algorithms
+ (Exaflop.org: <a href="http://exaflop.org/docs/cgafaq/cga6.html">geometric</a> structures)
+ </li>
+ <li><a
+ href="http://www-unix.mcs.anl.gov/otc/Guide/faq/linear-programming-faq.html">FAQ
+ </a>for linear programming </li>
+ <li><a href="news:comp.graphics.algorithms">Newsgroup</a>:
+ comp.graphics.algorithms </li>
+ <li><a href="news:comp.soft-sys.matlab">Newsgroup</a>:
+ comp.soft-sys.matlab</li>
+ <li><a href="news:sci.math.num-analysis">Newsgroup</a>:
+ sci.math.num-analysis </li>
+ <li><a href="news:sci.op-research">Newsgroup</a>:
+ sci.op-research </li>
+</ul>
+</blockquote>
+<hr>
+
+<p>The program includes options for input transformations,
+randomization, tracing, multiple output formats, and execution
+statistics. The program can be called from within your
+application. </p>
+
+<p>You can view the results in 2-d, 3-d and 4-d with <a
+href="http://www.geomview.org">Geomview</a>. An alternative
+is <a href=http://www.vtk.org/>VTK</a>.</p>
+
+<p>For an article about Qhull, download from
+ <a href="http://dl.acm.org/authorize?89250">ACM</a> or <a
+ href="http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.117.405">CiteSeer</a>:
+</p>
+
+<blockquote>
+ <p>Barber, C.B., Dobkin, D.P., and Huhdanpaa, H.T., &quot;The
+ Quickhull algorithm for convex hulls,&quot; <i>ACM Trans. on
+ Mathematical Software</i>, 22(4):469-483, Dec 1996, http://www.qhull.org</p>
+</blockquote>
+
+<p>Abstract: </p>
+
+<blockquote>
+ <p>The convex hull of a set of points is the smallest convex
+ set that contains the points. This article presents a
+ practical convex hull algorithm that combines the
+ two-dimensional Quickhull Algorithm with the general
+ dimension Beneath-Beyond Algorithm. It is similar to the
+ randomized, incremental algorithms for convex hull and
+ Delaunay triangulation. We provide empirical evidence that
+ the algorithm runs faster when the input contains non-extreme
+ points, and that it uses less memory. </p>
+ <p>Computational geometry algorithms have traditionally
+ assumed that input sets are well behaved. When an algorithm
+ is implemented with floating point arithmetic, this
+ assumption can lead to serious errors. We briefly describe a
+ solution to this problem when computing the convex hull in
+ two, three, or four dimensions. The output is a set of
+ "thick" facets that contain all possible exact convex hulls
+ of the input. A variation is effective in five or more
+ dimensions. </p>
+</blockquote>
+<!-- Navigation links -->
+<hr>
+
+<p><b>Up:</b> <a href="http://www.geom.uiuc.edu/software/past-projects.html"><i>Past Software
+Projects of the Geometry Center</i></a> <br>
+<b>URL:</b> <a href="http://www.qhull.org">http://www.qhull.org</a>
+<br><b>To:</b>
+<a href="http://www.qhull.org/news">News</a>
+&#149; <a href="http://www.qhull.org/download">Download</a>
+&#149; <a href="http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.117.405">CiteSeer</a>
+&#149; <a href=http://images.google.com/images?q=qhull&num=100>Images</a>
+&#149; <a href="html/index.htm#TOC">Manual</a>
+&#149; <a href="http://www.qhull.org/html/qh-faq.htm">FAQ</a>
+&#149; <a href="html/qh-quick.htm#programs">Programs</a>
+&#149; <a href="html/qh-quick.htm#options">Options</a>
+<!-- GC common information --></p>
+
+<hr>
+
+<p><a href="http://www.geom.uiuc.edu/"><img src="html/qh--geom.gif" alt="[HOME]"
+align="middle"></a> <i>The Geometry Center Home Page</i> </p>
+
+<p>Comments to: <a href="mailto:qhull@qhull.org">qhull@qhull.org</a>
+<br>
+Created: May 17 1995 --- <!-- hhmts start -->
+</body>
+</html>
diff --git a/xs/src/qhull/origCMakeLists.txt b/xs/src/qhull/origCMakeLists.txt
new file mode 100644
index 000000000..1034d1dea
--- /dev/null
+++ b/xs/src/qhull/origCMakeLists.txt
@@ -0,0 +1,426 @@
+# CMakeLists.txt -- CMake configuration file for qhull, qhull6, and related programs
+#
+# To install CMake
+# Download from http://www.cmake.org/download/
+#
+# To find the available targets for CMake -G "..."
+# cmake --help
+#
+# To build with MSYS/mingw
+# cd build && cmake -G "MSYS Makefiles" .. && cmake ..
+# make
+# make install
+#
+# To uninstall on unix or MSYS/mingw
+# xargs rm <build/install_manifest.txt
+#
+# To build Qhull with Visual Studio projects, run cmake twice
+# To install bin/doc/include/lib in the current directory
+# mkdir -p build-cmake && cd build-cmake && cmake -G "Visual Studio 11 2012" .. && cmake -DCMAKE_INSTALL_PREFIX=.. ..
+# mkdir -p build-cmake && cd build-cmake && cmake -G "Visual Studio 11 2012 Win64" .. && cmake -DCMAKE_INSTALL_PREFIX=.. ..
+# To install into Program Files/qhull
+# mkdir -p build-cmake && cd build-cmake && cmake -G "Visual Studio 11 2012" .. && cmake ..
+# mkdir -p build-cmake && cd build-cmake && cmake -G "Visual Studio 11 2012 Win64" .. && cmake ..
+# To build for Visual Studio 2005 and install into Program Files/qhull
+# mkdir -p build-cmake && cd build-cmake && cmake -G "Visual Studio 8 2005" .. && cmake ..
+# mkdir -p build-cmake && cd build-cmake && cmake -G "Visual Studio 8 2005 Win64" .. && cmake ..
+# Double click build-cmake/qhull-all.sln
+# Build INSTALL to copy files into C:/Program Files/qhull
+#
+# Additional build targets
+# qhullp -- Same as qhull using qh_QHpointer and deprecated libqhull_p
+# user_egp -- Same as user_eg using qh_QHpointer and deprecated libqhull_p
+#
+# Notes on Visual Studio projects
+# You may need to copy bin/msvcr80.dll into C:/Program Files/qhull/bin
+# If using library debug targets, please rename with '_d' (e.g., qhullstatic_d.lib)
+#
+# Troubleshooting
+# "No CMAKE_C_COMPILER could be found"
+# cmake was not able to find the build environment specified (e.g., Visual Studio 11)
+#
+# To uninstall on Windows
+# Delete C:/Program Files/qhull
+#
+# If creating a qhull package, please include a pkg-config file based on build/qhull*.pc.in
+#
+# For qhulltest, use the Qt build (src/qhull-all.pro)
+#
+# Qhull ships with cmake-derived sln and proj files for DevStudio 8 2005
+# See eg/make-vcproj.sh
+# Change to relative paths
+# Remove ZERO_CHECK, ALL_BUILD, and INSTALL projects
+# Change targets to bin/ and lib/ directories
+# Disable incremental linking and ilk files (LinkIncremental="1")
+# Disable Run-Time Type Info (rtti)
+# Remove src/libqhullcpp from most of the AdditionalIncludeDirectories
+# Remove CMAKE_INTDIR from PreprocessorDefinitions
+# Adjust target names and destinations (e.g., lib/libqhullstatic_rd.a)
+#
+# $Id: //main/2015/qhull/CMakeLists.txt#8 $$Change: 2066 $
+# $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
+
+project(qhull)
+cmake_minimum_required(VERSION 2.6)
+
+# Define qhull_VERSION in CMakeLists.txt, Makefile, qhull-exports.def, qhull_p-exports.def, qhull_r-exports.def, qhull-warn.pri
+set(qhull_VERSION2 "2015.2 2016/01/18") # not used, See global.c, global_r.c, rbox.c, rbox_r.c
+set(qhull_VERSION "7.2.0") # Advance every release
+
+# SOVERSION -- qhull 2003 = empty, 2009 = 5, 2010-2012 = 6, 2015 (reentrant) = 7
+set(qhull_SOVERSION 7) # For SOVERSION
+
+include(CMakeModules/CheckLFS.cmake)
+option(WITH_LFS "Enable Large File Support" ON)
+check_lfs(WITH_LFS)
+
+if(INCLUDE_INSTALL_DIR)
+else()
+set(INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/include)
+endif()
+if(LIB_INSTALL_DIR)
+else()
+set(LIB_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/lib)
+endif()
+if(BIN_INSTALL_DIR)
+else()
+set(BIN_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/bin)
+endif()
+if(MAN_INSTALL_DIR)
+else()
+ if(WIN32)
+ set(MAN_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/man/man1)
+ else()
+ set(MAN_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/share/man/man1)
+ endif()
+endif()
+if(DOC_INSTALL_DIR)
+else()
+ if(WIN32)
+ set(DOC_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/doc)
+ else()
+ set(DOC_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/share/doc/qhull)
+ endif()
+endif()
+message(STATUS)
+message(STATUS "========== qhull Build Information ==========")
+message(STATUS "Build Version: ${qhull_VERSION}")
+message(STATUS "Install Prefix (CMAKE_INSTALL_PREFIX): ${CMAKE_INSTALL_PREFIX}")
+message(STATUS "Binary Directory (BIN_INSTALL_DIR): ${BIN_INSTALL_DIR}")
+message(STATUS "Library Directory (LIB_INSTALL_DIR): ${LIB_INSTALL_DIR}")
+message(STATUS "Include Directory (INCLUDE_INSTALL_DIR): ${INCLUDE_INSTALL_DIR}")
+message(STATUS "Documentation Directory (DOC_INSTALL_DIR): ${DOC_INSTALL_DIR}")
+message(STATUS "Man Pages Directory (MAN_INSTALL_DIR): ${MAN_INSTALL_DIR}")
+message(STATUS "Build Type (CMAKE_BUILD_TYPE): ${CMAKE_BUILD_TYPE}")
+message(STATUS "To override these options, add -D{OPTION_NAME}=... to the cmake command")
+message(STATUS " Build the debug targets -DCMAKE_BUILD_TYPE=Debug")
+message(STATUS)
+message(STATUS "To build and install qhull, enter \"make\" and \"make install\"")
+message(STATUS "To smoketest qhull, enter \"ctest\"")
+message(STATUS)
+
+
+# ---------------------------------------
+# Define library source files and variables
+#
+# Files for individual targets are defined with the target
+# ---------------------------------------
+
+# Order libqhull object files by frequency of execution. Small files at end.
+
+# Non-reentrant Qhull
+set(
+ libqhull_HEADERS
+ src/libqhull/libqhull.h
+ src/libqhull/geom.h
+ src/libqhull/io.h
+ src/libqhull/mem.h
+ src/libqhull/merge.h
+ src/libqhull/poly.h
+ src/libqhull/qhull_a.h
+ src/libqhull/qset.h
+ src/libqhull/random.h
+ src/libqhull/stat.h
+ src/libqhull/user.h
+)
+set(
+ libqhull_SOURCES
+ src/libqhull/global.c
+ src/libqhull/stat.c
+ src/libqhull/geom2.c
+ src/libqhull/poly2.c
+ src/libqhull/merge.c
+ src/libqhull/libqhull.c
+ src/libqhull/geom.c
+ src/libqhull/poly.c
+ src/libqhull/qset.c
+ src/libqhull/mem.c
+ src/libqhull/random.c
+ src/libqhull/usermem.c
+ src/libqhull/userprintf.c
+ src/libqhull/io.c
+ src/libqhull/user.c
+ src/libqhull/rboxlib.c
+ src/libqhull/userprintf_rbox.c
+ ${libqhull_HEADERS}
+)
+
+set(
+ libqhull_DOC
+ src/libqhull/index.htm
+ src/libqhull/qh-geom.htm
+ src/libqhull/qh-globa.htm
+ src/libqhull/qh-io.htm
+ src/libqhull/qh-mem.htm
+ src/libqhull/qh-merge.htm
+ src/libqhull/qh-poly.htm
+ src/libqhull/qh-qhull.htm
+ src/libqhull/qh-set.htm
+ src/libqhull/qh-stat.htm
+ src/libqhull/qh-user.htm
+ src/libqhull/DEPRECATED.txt
+)
+
+set(
+ testqset_HEADERS
+ src/libqhull/mem.h
+ src/libqhull/qset.h
+)
+set(
+ testqset_SOURCES
+ src/libqhull/qset.c
+ src/libqhull/mem.c
+ src/libqhull/usermem.c
+ src/testqset/testqset.c
+ ${testqset_HEADERS}
+)
+
+# Reeentrant Qhull
+
+set(
+ libqhullr_HEADERS
+ src/libqhull_r/libqhull_r.h
+ src/libqhull_r/geom_r.h
+ src/libqhull_r/io_r.h
+ src/libqhull_r/mem_r.h
+ src/libqhull_r/merge_r.h
+ src/libqhull_r/poly_r.h
+ src/libqhull_r/qhull_ra.h
+ src/libqhull_r/qset_r.h
+ src/libqhull_r/random_r.h
+ src/libqhull_r/stat_r.h
+ src/libqhull_r/user_r.h
+)
+set(
+ libqhullr_SOURCES
+ src/libqhull_r/global_r.c
+ src/libqhull_r/stat_r.c
+ src/libqhull_r/geom2_r.c
+ src/libqhull_r/poly2_r.c
+ src/libqhull_r/merge_r.c
+ src/libqhull_r/libqhull_r.c
+ src/libqhull_r/geom_r.c
+ src/libqhull_r/poly_r.c
+ src/libqhull_r/qset_r.c
+ src/libqhull_r/mem_r.c
+ src/libqhull_r/random_r.c
+ src/libqhull_r/usermem_r.c
+ src/libqhull_r/userprintf_r.c
+ src/libqhull_r/io_r.c
+ src/libqhull_r/user_r.c
+ src/libqhull_r/rboxlib_r.c
+ src/libqhull_r/userprintf_rbox_r.c
+ ${libqhullr_HEADERS}
+)
+
+set(
+ libqhullr_DOC
+ src/libqhull_r/index.htm
+ src/libqhull_r/qh-geom_r.htm
+ src/libqhull_r/qh-globa_r.htm
+ src/libqhull_r/qh-io_r.htm
+ src/libqhull_r/qh-mem_r.htm
+ src/libqhull_r/qh-merge_r.htm
+ src/libqhull_r/qh-poly_r.htm
+ src/libqhull_r/qh-qhull_r.htm
+ src/libqhull_r/qh-set_r.htm
+ src/libqhull_r/qh-stat_r.htm
+ src/libqhull_r/qh-user_r.htm
+)
+
+set(
+ testqsetr_HEADERS
+ src/libqhull_r/mem_r.h
+ src/libqhull_r/qset_r.h
+)
+set(
+ testqsetr_SOURCES
+ src/libqhull_r/qset_r.c
+ src/libqhull_r/mem_r.c
+ src/libqhull_r/usermem_r.c
+ src/testqset_r/testqset_r.c
+ ${testqsetr_HEADERS}
+)
+
+# C++ interface to reentrant Qhull
+
+set(
+ libqhullcpp_HEADERS
+ src/libqhullcpp/Coordinates.h
+ src/libqhullcpp/functionObjects.h
+ src/libqhullcpp/PointCoordinates.h
+ src/libqhullcpp/Qhull.h
+ src/libqhullcpp/QhullError.h
+ src/libqhullcpp/QhullFacet.h
+ src/libqhullcpp/QhullFacetList.h
+ src/libqhullcpp/QhullFacetSet.h
+ src/libqhullcpp/QhullHyperplane.h
+ src/libqhullcpp/QhullIterator.h
+ src/libqhullcpp/QhullLinkedList.h
+ src/libqhullcpp/QhullPoint.h
+ src/libqhullcpp/QhullPoints.h
+ src/libqhullcpp/QhullPointSet.h
+ src/libqhullcpp/QhullQh.h
+ src/libqhullcpp/QhullRidge.h
+ src/libqhullcpp/QhullSet.h
+ src/libqhullcpp/QhullSets.h
+ src/libqhullcpp/QhullStat.h
+ src/libqhullcpp/QhullVertex.h
+ src/libqhullcpp/QhullVertexSet.h
+ src/libqhullcpp/RboxPoints.h
+ src/libqhullcpp/RoadError.h
+ src/libqhullcpp/RoadLogEvent.h
+ src/qhulltest/RoadTest.h
+)
+
+set(
+ libqhullcpp_SOURCES
+ src/libqhullcpp/Coordinates.cpp
+ src/libqhullcpp/PointCoordinates.cpp
+ src/libqhullcpp/Qhull.cpp
+ src/libqhullcpp/QhullFacet.cpp
+ src/libqhullcpp/QhullFacetList.cpp
+ src/libqhullcpp/QhullFacetSet.cpp
+ src/libqhullcpp/QhullHyperplane.cpp
+ src/libqhullcpp/QhullPoint.cpp
+ src/libqhullcpp/QhullPointSet.cpp
+ src/libqhullcpp/QhullPoints.cpp
+ src/libqhullcpp/QhullQh.cpp
+ src/libqhullcpp/QhullRidge.cpp
+ src/libqhullcpp/QhullSet.cpp
+ src/libqhullcpp/QhullStat.cpp
+ src/libqhullcpp/QhullVertex.cpp
+ src/libqhullcpp/QhullVertexSet.cpp
+ src/libqhullcpp/RboxPoints.cpp
+ src/libqhullcpp/RoadError.cpp
+ src/libqhullcpp/RoadLogEvent.cpp
+ ${libqhullcpp_HEADERS}
+)
+
+# Documentation files (index.htm refers to html/...)
+
+set(
+ doc_FILES
+ README.txt
+ REGISTER.txt
+ Announce.txt
+ COPYING.txt
+ index.htm
+)
+
+include_directories(${CMAKE_SOURCE_DIR}/src)
+
+if(CMAKE_BUILD_TYPE MATCHES "[dD]ebug")
+ set(qhull_CPP qhullcpp_d)
+ set(qhull_SHARED qhull_d)
+ set(qhull_SHAREDP qhull_pd)
+ set(qhull_SHAREDR qhull_rd)
+ set(qhull_STATIC qhullstatic_d)
+ set(qhull_STATICR qhullstatic_rd)
+else()
+ set(qhull_CPP qhullcpp)
+ set(qhull_SHARED libqhull) # Temporarily avoid name conflict with qhull executable
+ set(qhull_SHAREDP qhull_p)
+ set(qhull_SHAREDR qhull_r)
+ set(qhull_STATIC qhullstatic)
+ set(qhull_STATICR qhullstatic_r)
+endif()
+
+
+
+# ---------------------------------------
+# Define static libraries qhullstatic (non-reentrant) and qhullstatic_r (reentrant)
+# ---------------------------------------
+
+add_library(${qhull_STATIC} STATIC ${libqhull_SOURCES})
+set_target_properties(${qhull_STATIC} PROPERTIES
+ VERSION ${qhull_VERSION})
+
+add_library(${qhull_STATICR} STATIC ${libqhullr_SOURCES})
+set_target_properties(${qhull_STATICR} PROPERTIES
+ VERSION ${qhull_VERSION})
+
+if(UNIX)
+ target_link_libraries(${qhull_STATIC} m)
+ target_link_libraries(${qhull_STATICR} m)
+endif(UNIX)
+
+# ---------------------------------------
+# Define C++ static library qhullcpp
+# Do not create libqhullcpp as a shared library. Qhull C++ classes may change layout and size.
+# ---------------------------------------
+
+add_library(${qhull_CPP} STATIC ${libqhullcpp_SOURCES})
+set_target_properties(${qhull_CPP} PROPERTIES
+ VERSION ${qhull_VERSION})
+
+# ---------------------------------------
+# Define qhull executables linked to qhullstatic library
+# qhull is linked to reentrant qhull (more flexible)
+# the others are linked to non-reentrant qhull (somewhat faster)
+# ---------------------------------------
+
+set(qhull_SOURCES src/qhull/unix_r.c)
+set(rbox_SOURCES src/rbox/rbox.c)
+set(qconvex_SOURCES src/qconvex/qconvex.c)
+set(qdelaunay_SOURCES src/qdelaunay/qdelaun.c)
+set(qvoronoi_SOURCES src/qvoronoi/qvoronoi.c)
+set(qhalf_SOURCES src/qhalf/qhalf.c)
+
+add_executable(qhull ${qhull_SOURCES})
+target_link_libraries(qhull ${qhull_STATICR})
+
+add_executable(rbox ${rbox_SOURCES})
+target_link_libraries(rbox ${qhull_STATIC})
+
+add_executable(qconvex ${qconvex_SOURCES})
+target_link_libraries(qconvex ${qhull_STATIC})
+
+add_executable(qdelaunay ${qdelaunay_SOURCES})
+target_link_libraries(qdelaunay ${qhull_STATIC})
+
+add_executable(qvoronoi ${qvoronoi_SOURCES})
+target_link_libraries(qvoronoi ${qhull_STATIC})
+
+add_executable(qhalf ${qhalf_SOURCES})
+target_link_libraries(qhalf ${qhull_STATIC})
+
+
+# ---------------------------------------
+# Define install
+# ---------------------------------------
+
+install(TARGETS ${qhull_TARGETS_INSTALL}
+ RUNTIME DESTINATION ${BIN_INSTALL_DIR}
+ LIBRARY DESTINATION ${LIB_INSTALL_DIR}
+ ARCHIVE DESTINATION ${LIB_INSTALL_DIR})
+
+install(FILES ${libqhull_HEADERS} DESTINATION ${INCLUDE_INSTALL_DIR}/libqhull)
+install(FILES ${libqhull_DOC} DESTINATION ${INCLUDE_INSTALL_DIR}/libqhull)
+install(FILES ${libqhullr_HEADERS} DESTINATION ${INCLUDE_INSTALL_DIR}/libqhull_r)
+install(FILES ${libqhullr_DOC} DESTINATION ${INCLUDE_INSTALL_DIR}/libqhull_r)
+install(FILES ${libqhullcpp_HEADERS} DESTINATION ${INCLUDE_INSTALL_DIR}/libqhullcpp)
+install(FILES html/qhull.man DESTINATION ${MAN_INSTALL_DIR} RENAME qhull.1)
+install(FILES html/rbox.man DESTINATION ${MAN_INSTALL_DIR} RENAME rbox.1)
+install(FILES ${doc_FILES} DESTINATION ${DOC_INSTALL_DIR})
+install(DIRECTORY html/ DESTINATION ${DOC_INSTALL_DIR})
diff --git a/xs/src/qhull/src/Changes.txt b/xs/src/qhull/src/Changes.txt
new file mode 100644
index 000000000..2dbf41e39
--- /dev/null
+++ b/xs/src/qhull/src/Changes.txt
@@ -0,0 +1,2129 @@
+$Id: //main/2015/qhull/src/Changes.txt#29 $
+
+.............This file lists all changes to qhull and rbox.....................
+
+------------
+Need help
+ - Write an incremental addPoint using bucketed inputs and facet location search (see CGAL)
+ - Compute hyperplanes in parallel (cf. qh_setfactplane)
+ - Create Voronoi volumes and topology using a parallel implementation
+ - Produce conformant triangulations for merged facets using option 'Qt'
+ - Integrate 4dview with Geomview 1.9.5
+ - Qhull needs RPM and Debian builds (CMake's CPackRMP and CPackDeb).
+ - Qhull needs a mirror/archive site for old and new builds
+ - Constrained delaunay triangulation via Shewchuk's algorithm (ACM Symp. Comp. Geo., 1998)
+ - More enhancements -- http://www.qhull.org/html/qh-code.htm#enhance
+ - The C++ interface needs work. Give it a try and make it better.
+ http://github.com/qhull/qhull/wiki
+
+Octave creates endpoints for the ridges that would extend to infinity to be able to draw the diagram [M. Voss]
+
+------
+Qhull 2016.1
+ - Move 'extern "C" {}' logic from C++ to C headers [C. Atkins, github]
+
+ - Explanation of clobbered warning for Qhull::runQhull and qh_new_qhull
+ - html/index.htm: Add "Nearly coincident points" to "When to use Qhull"
+ - html/index.htm: Add Tomilov's qhullhull.hpp for general position input.
+ - index.htm: Add Tomilov's qhullhull.hpp for general position input.
+ - qh-faq.htm: Add a note about merging slivers in a Delaunay triangulation [M. Treacy]
+
+------------
+Qhull 2015.2 2016/01/18 (7.2.0)
+ - Fixed memory leak in ~QhullQh [M. Sandim]
+ QhullQh.cpp: call checkAndFreeQhullMemory() in the destructor. Otherwise memT is not freed.
+ Remove checkAndFreeQhullMemory() from Qhull.h. It is not needed.
+ Remove calls to checkAndFreeQhullMemory in qhulltest. It is called by ~QhullQh()
+ libqhull_r.h: Document qh_ASvornoi and facetT.center
+ qh_freeqhull: if qh_NOmem, use qh_ALL
+ qh_memalloc: short memory is freed by qh_memfreeshort unless qh_NOmem
+ qh_memstatistics (mem.c): call qh_memcheck() as done in mem_r.c
+ qh_new_qhull calls qh_memcheck
+ qh_newvertex: free vertex on error
+ qh_projectinput: Free memory allocations on error
+ qh_rboxpoints: free simplex on error
+ qh_sethalfspace_all: Fixed memory leak on error QH8032 feasible not inside halfspace
+ qh_triangulate_facet: For TRInormals ('Q11') replace qh_copypoints with qh_memalloc
+ qh_triangulate_facet: Document qh.TRInormals
+ qh_voronoi_center: Free center on error
+ qhulltest: Fixed memory leak of s_testcases by calling RoadTest::deleteTests()
+ qhulltest: The 'add_*_test' functions append the test object to RoadTest::s_testcases
+ ~RoadTest: declare virtual for Q_OBJECT, removeAll not needed
+ user_eg2: Check memory at end of each run
+ user_r.h: Add QHULL_CRTDBG for invoking Microsoft's memory leak detector
+ use _MSC_VER instead of QHULL_OS_WIN for QHULL_CRTDBG
+ Call qh_freeqhull with qh_ALL/!qh_ALL instead of 'True/False'
+ Include user_r.h with RoadError,h for QHULL_CRTDBG
+ Invoke _CrtSetDbgFlag... at beginning of program
+ Moved user_r.h/libqhull_r.h/qhull_ra.h as first include (for QHULL_CRTDBG)
+ Moved QHULL_OS_WIN from qhull_ra.h to user_r.h
+ Removed __CYGWIN__ from QHULL_OS_WIN (same as Qt's qglobal.h)
+
+ - check_dupridge: A bounding box is not sufficient to avoid dupridge errors
+ - qh_findbestneighor: Error if qh.CENTERtype is qh_ASvoronoi (i.e., no merging now)
+ - qh_printstatlevel: Remove unused parameter, 'start'
+ - QhullLinkedList::last() and back(): Return T instead of T& (T is computed)
+
+ - qh-code.htm: "How to convert code to reentrant Qhull"
+ Update "Nearly coincident points on an edge"
+ Add 2012 size of data structures to "Qhull on 64-bit computers"
+ - html/index.htm: Add CGAL to "When to use"
+ - qh-optq.htm: Add documentation for option 'Q12'
+ - Move suggestions from Changes.txt to qh-code.htm#enhance
+ - user_r.h: Fixed qh-us_r.html links
+ - Fixed links in html pages
+
+ - QhullIterator and QhullLinkedList: Include <iterator> [B. Boeckel]
+ - Moved include file for each C++ source file to the top of the includes
+ - Prepend cpp includes with "libqhullcpp/"
+ - RoadLogEvent includes RoadLogEvent.h
+ - QhullIterator.h: Only QHULL_DECLARE_SEQUENTIAL_ITERATOR is used.
+
+ - Compared src/libqhull/* to src/libqhull_r/* and resolved differences
+ - qh_printpoint in io.c skips qh_IDnone like io_r.c
+ - qhull_p-exports.def: Added three missing exports
+ - set_r.h: Removed countT. Too many issues
+
+ - libqhull_r/Makefile: Add help prompts to 'make qtest'
+ - libqhull.pro: Add '../libqhull/' to sources and headers
+ - libqhull/Makefile: Fixed -I,./,,/src
+
+ - qhull-zip.sh: Add CMakeModules to tarball [C. Rosenvik]
+ - CMakeLists.txt: Add targets qhullp and user_egp for qh_QHpointer and libqhull_p
+ - Reorganized 'make help'
+ - Makefile cleanall: Delete testqset and qhulltest from bin/
+ - Fix filetype of Unix-only files
+ - Fix Unix line endings for Makefile and check in qhull-zip.sh
+ - Fix Windows line-endings and check in qhull-zip.sh
+ - qhull-zip.sh: Check for Unix text files
+
+ ------------
+Qhull 2015.1 2016/01/03 (7.1.0)
+ - Add Rbox option 'Cn,r,m' to add nearly coincident points. Trigger for duplicate ridges
+ - Add Qhull option 'Q12' to ignore error on wide merge due to duplicate ridge
+
+ - qh_findbestlower: Call qh_findfacet_all to fix rare "flipped or upper Delaunay" error QH6228.
+ QH6228 input provided by J. Metz. Reported (date order): L. Fiaschi, N. Bowler, A. Liebscher, V. Vieira, N. Rhinehart, N. Vance, P. Shafer
+ - qh_check_dupridge: Check if wide merge due to duplicate ridge from nearly coincident points
+ - qh_initialhull: Fix error messages for initial simplex is flat
+ - qh_determinant: increased 2-d and 3-d nearzero by 10x due to a counter-example
+ - rbox: Add qh_outcoord() to output coordinates w/ or w/o iscdd
+ - qh_meminit (mem.c): Add call to qh_memcheck
+ - Compare libqhull/... to libqhull_r/... and resolve differences
+ - Update builds for DevStudio (qhull.sln for msdev 2005..2009, qhull-32.sln and qhull-64.sln for recent releases)
+
+ - qh-impre.htm: Add a section about precision errors for 'Nearly coincident points on an edge'
+ - html/index.htm#geomview: Document how to install, build, and use Geomview.
+ - html/index.htm: Emphasize program links and move related urls to end
+ - qhull/index.htm: Emphasize manual, geomview, and imprecision
+ - Fix documentation links in libqhull_r/index.htm
+ - Add 'Functions' link to documentation headers
+ - Change '<A>...</A>' to '<a>...</a>'
+ - libqhull_r/index.htm -- Add instructions for configuring web browsers for source links.
+ - libqhull_r/ -- Fix source links for ..._r.htm files
+
+------------
+Qhull 2015.0.7 2015/11/09 (7.0.7)
+ - Fix return type of operator-> in QhullLinkedList and other collection classes [F. Jares]
+ - Fix return types for QhullLinkedList
+ - Fix return types for QhullPoints
+ - Simplify return type for Coordinates::operator[] (same as QList)
+ - Add const to operators for QhullSet::iterator and add documentation
+ - Coordinates.h: Fix return types for operations of iterator and const_iterator
+ - Drop use of Perforce changelist number in qhull_VERSION of CMakeLists.txt
+ - Rename the md5sum files as *.tgz.md5sum instead of *-tgz.md5sum
+ - Fix build dependency for testqset_r [asekez]
+ - rbox.c depends on Qhull due to qh_lib_check which uses qh_version2 for error messages
+ - QhullFacet_test.cpp: Annotate Qhull invocations. Allows their repetition.
+ - QhullFacet_test.cpp: Adjust epsilon on distance tests
+ - Do not create libqhullcpp as a shared library. Qhull C++ classes may change layout and size.
+ - qhull-cpp.xml: Make a relative path to road-faq.xsl
+
+------------
+Qhull 2015.0.6 2015/10/20 (7.0.6.2013)
+ - In the libraries, exit() is only called from qh_exit(). qh_exit may be redefined.
+ - Add qh_fprintf_stderr to usermem.c. May be overridden to avoid use of stderr [D. Sterratt]
+ Add usermem to testqset builds
+ Used by qh_fprintf_rbox
+ - Remove most instances of stderr/stdout from libqhull, libqhull_r, and libqhullcpp [D. Sterratt]
+ qh_fprintf_stderr may be redefined. qh_meminit and qh_new_qhull use stderr as the default ferr
+ - qh_initflags: Use qh.fout instead of stdout for 'TO file'. A library caller may define a different qh.fout.
+ - qh_settemppush: Call qh_fprintf() instead of fprintf() on error.
+ - Rename qh_call_qhull as "Qhull-template" from user.c. Updated its references.
+
+ - qh-code.htm: "How to avoid</a> exit(), fprintf(), stderr, and stdout"
+ - html/index.htm: Fix table of contents for qh-code
+ - libqhull_r/index.htm: Rewrite introduction to Reentrant Qhull
+ - qh-faq.htm: Rewrite "Can Qhull use coordinates without placing them in a data file?"
+ - qh-get.html: Link to github
+ - Remove qhull_interface.cpp from the documentation
+
+------------
+Qhull 2015.0.5 2015/10/12 (7.0.5.1995)
+- qh_new_qhull: default 'errfile' is 'stderr'. outfile and errfile are optional [B. Pearlmutter]
+- qh_new_qhull: returns qh_ERRinput instead of exit() if qhull_cmd is not "qhull ..." [B. Pearlmutter]
+- qhalf_r.c,etc: Add clear of qh.NOerrexit
+- global.c: gcc 4.4.0 mingw32 segfault cleared by adding comment
+- usermem_r-cpp.cpp: Optional file to redefine qh_exit() as throw "QH10003.." [B. Pearlmutter]
+ qh_exit() is called by qhull_r when qh_errexit() is not available.
+
+- html/index.htm: Add bibliographic reference to Golub & van Loan and other references [R. Gaul]
+- qhalf.htm: A halfspace is the points on or below a hyperplane [D. Strawn]
+- qh-opto.htm#n: Defined inside, outside, on, above, and below a hyperplane [D. Strawn]
+- qhalf.htm#notes: Recast the linear program using negative halfspaces (as used by Qhull) [D. Strawn]
+- qhull_a.h: Fix comment '#include "libqhull/qhull_a.h" [fe rew]
+
+- build/qhull*.pc.in: Templates for pkg-config (derived from Fedorra) [P. McMunn]
+ https://bitbucket.org/mgorny/pkg-config-spec/src/c1bf12afe0df6d95f2fe3f5e1ffb4c50f018825d/pkg-config-spec.txt?at=master&fileviewer=file-view-default
+- Makefile: Remove user_eg3.o from LIBQHULLCPP_OBJS
+- Makefile: Add .h dependencies for unix_r.o, etc.
+- libqhull/Makefile: Fix build of rbox
+- libqhull_r/Makefile: Fix build -I
+- qhull.sln/user_eg3: Add dependency on libcpp
+- Removed bin/libqhull_r.dll (should be qhull_r.dll)
+- Removed build/qhulltest.vcproj (see build/qhulltest/qhulltest.vcproj)
+
+------------
+Qhull 2015.0.4 2015/9/30 (7.0.4.1984)
+ - qh-get.htm: Unix tarball includes version number (e.g., qhull-2015-src-7.1.0.1940.tgz) [Hauptman]
+ - qglobal.c: Add qh_version2 with Unix version for "-V" option [Hauptman]
+ - build/qhull-32.sln, *-32.vcxproj: Add Visual Studio 32-bit build for 2010+
+ - build/qhull-64.sln, *-64.vcxproj: Add Visual Studio 64-bit build for 2010+ [G. Lodron]
+ - make-vcproj.sh: Restore to eg/... It is required for Visual Studio builds
+ - README.txt: updated builds and reentrant Qhull
+ - Add documentation for QHULL_LIB_CHECK
+ - qh_lib_check: Check for unknown QHULL_LIB_TYPE
+ - qh-code.htm: Add memory requirements for 32- and 64-bit
+
+------------
+Qhull 2015.0.3 2015/9/22
+ - qh_mem, qh_merge: Log before 'delete' instead of afterwards [Coverity, K. Schwehr]
+ - qh_merge: Test for NULL horizon in qh_checkzero [Coverity, K. Schwehr]
+ - qh_matchneighbor: Check for matchfacet not a neighbor of facet [Coverity, K. Schwehr]
+ - qh_triangulate: Explicit check for visible==NULL [Coverity, K. Schwehr]
+ - qh_findbestfacet (unused by qhull): Fix test of isoutside [Coverity, K. Schwehr]
+ - qh_check_maxout: Check bestfacet!=0 for logging its id [Coverity, K. Schwehr]
+ - qh_nearvertex: Check for bestvertex not found [Coverity, K. Schwehr]
+ - qh_checkfacet: Check for missing neighbors of simplicial facets [Coverity, K. Schwehr]
+ - qh_setdelnth: Check 'nth' before using it [Coverity, K. Schwehr]
+ - Annotate code for Coverity warnings (most of these protected by qh_errexit) [K. Schwehr]
+
+ - qh_printfacet3math: explicit format string (duplicates change to io.c) [B. Pearlmutter]
+ - libqhull_r.h: fix spelling error (duplicates change to libqhull.h) [B. Pearlmutter]
+ - unix_r.c: fix spelling error (duplicates change to unix.c) [B. Pearlmutter]
+ - qhull_a.h: define qhullUnused() only if defined(__cplusplus) [R. Stogner]
+ - qh_version: Use const char str[]= "string" instead of const char * str= "string" [U. Drepper, p. 27]
+ - qh_newvertex: Use UINT_MAX instead of 0xFFFFFFFF
+ - qh_newridge: Use UINT_MAX instead of 0xFFFFFFFF
+ - Reviewed FIXUP notes
+
+ - QhullRidge_test: t_foreach use 'foreach(const QhullVertex &v, vertices)
+ - Made '#include "RoadTest.h" consistent across all C++ tests
+
+ - qh-code.htm: May also use libqhull_r (e.g., FOREACHfacet_(...))
+ - qh-get.htm: Add list of download build repositories
+
+ - Add CMakeModules/CheckLFS.cmake: Enables Large File Support [B. Pearlmutter]
+ - Makefile: Use -fpic at all times instead of -fPIC, [U. Drepper p. 15]
+
+------------
+Qhull 2015.0.2 2015/9/1
+ - global_r.c: Fixed spelling of /* duplicated in...qh_clear_outputflags */ [K. Schwehr]
+ - Replaced Gitorious with GitHub
+ - Moved 'to do' comments into Changes.txt
+
+------------
+Qhull 2015.0.1 2015/8/31
+
+ Source code changes
+ - Increased size of vertexT.id and ridgeT.id to 2^32 [H. Strandenes, C. Carson, K. Nguyen]
+ Reworded the warning message for ridgeT.id overflow. It does not affect Qhull output
+ - Add qh_lib_check to check for a compatible Qhull library.
+ Programs should call QHULL_LIB_CHECK before calling Qhull.
+ - Include headers prefixed with libqhull/, libqhull_r/, or libqhullcpp/
+ - Renamed debugging routines dfacet/dvertex to qh_dfacet/qh_dvertex
+ - Rewrote user_eg, user_eg2, and user_eg3 as reentrant code
+ - Renamed 'qh_rand_seed' to 'qh_last_random'. Declare it as DATA
+ - qh_initqhull_start2 sets qh->NOerrexit on initialization
+ User must clear NOerrexit after setjmp()
+
+ Other source code changes
+ - Define ptr_intT as 'long long' for __MINGW64__ [A. Voskov]
+ - poly_r.c: initialize horizon_skip [K. Schwehr]
+ - Removed vertexT.dim and MAX_vdim. It is not used by reentrant Qhull.
+ - Removed qhull_inuse. Not used by C++
+ - Removed old __MWERKS__/__POWERPC__ code that speed up SIOUX I/O
+ - Moved #include libqhull/... before system includes (e.g., <stdio.h>
+ - Comment-out _isatty declaration. Avoids "C4273 ... inconsistent dll linkage"
+ - Add random.h/random_r.h as an include file to random.c/random_r.c
+ - Rename rbox routines to qh_roundi/qh_out1/qh_out2n/qh_out3n
+ - Rename dfacet and dvertex to qh_dfacet and qh_dvertex
+ - Replace 'qhmem .zzz' with 'qhmem.zzz'
+ - Removed spaces between function name and parentheses
+ - Rename 'enum statistics' to 'enum qh_statistics'
+ - Declare rbox as DATA in qhull-exports.def and qhull_p-exports.def
+ - In comments, use 'qh.zzz' to reference qhT fields
+ - In qh_fprintf, use qhmem.ferr to report errors
+ - qh_fprintf may be called for errors in qh_initstatistics and qh_meminit
+ - qh_pointid returns qh_IDnone, qh_IDinterior, qh_IDunknown in place of -3, -2, -1 resp.
+ - getid_() returns qh_IDunknown in place of -1
+ - After qh_meminit, qhmem.ferr is non-zero (stderr is the default)
+ - Update qh_MEMalign in testqset.c to user.h (with realT and void*)
+ - Split rboxT into a header file
+ - Add rboxlib.h to libqhull_a.h
+ - Rename PI to qh_PI and extend to 30 digits
+ - Rename MAXdim to qh_MAXdim
+ - Change spacing for type annotations '*' and '&' in C++ header files
+ - Test for !rbox_output/cpp_object in qh_fprintf_rbox
+ - Remove 'inline' annotation from explicit inline declarations
+ - Column 25 formatting for iterators, etc.
+ - Use '#//!\name' for section headers
+ - QhullFacet.cpp: zinc_(Zdistio);
+ - Clear qhT.ALLOWrestart in qh_errexit
+ - Replace longjmp with qh_errexit_rbox in qh_rboxpoints
+ - Add jmpExtra after rbox_errexit to protect against compiler errors
+ - Add qh.ISqhullQh to indicate initialization by QhullQh()
+ - Add library warnings to 'rbox D4', user_eg, user_eg2, user_eg3
+ - Add headers to q_eg, q_egtest, and q_test
+ - Check that qh.NOerrexit is cleared before call to qh_initflags
+
+Qhull documentation
+ - README.txt: Added references to qh-code.htm
+ - README.txt: Added section 'Calling Qhull from C programs'
+ - qh-code.htm: Moved Performance after C++ and C interface
+ - qh-code.htm: Moved Cpp Questions to end of the C++ section
+ - qh-code.htm: Fixed documentation for 'include' path. It should be include/libqhull
+ - qconvex.htm: Fixed documentation for 'i'. It triangulates in 4-d and higher [ref]
+ - Clarified qhalf space documentation for the interior point [J. Santos]
+ - rbox.c: Version is same date as qh_version in global.c
+ - gobal_r.c: Version includes a '.r' suffix to indicate 'reentrant'
+
+Qhull builds
+ - Development moved to http://github.com/qhull/qhull
+ git clone git@github.com:qhull/qhull.git
+ - Exchanged make targets for testing.
+ 'make test' is a quick test of qhull programs.
+ 'make testall' is a thorough test
+ - Added 'make help' and 'make test' to libqhull and libqhull_r Makefiles
+ - CMakeLists.txt: Remove libqhull, libqhull_r, and libqhullcpp from include_directories
+ - CMakeLists.txt: Add qhull_SHAREDR for qhull_r
+ - CMakeLists.txt: Retain qhull_SHARED and qhull_SHAREDP (qh_QHpointer)
+ - CMakeLists.txt: Move qhull_SHARED and qhull_SHAREDP (qh_QHpointer) to qhull_TARGETS_OLD
+ Drop qhull_STATICP (use qhull_SHAREDP or qhull_STATIC)
+ Set SOVERSION and VERSION for shared libraries
+ - Move qhull_p-exports.def back to libqhull
+ - Switched to mingw-w64-install for gcc
+ - Improved prompts for 'make'
+ - qhull-all.pro: Remove user_eg3.cpp from OTHER_FILES
+ - libqhull.pro: Ordered object files by frequency of execution, as done before
+ - Add the folder name to C++ includes and remove libqhullcpp from INCLUDEPATH
+ - Changed CONFIG+=qtestlib to QT+=testlib
+ - Changed Makefile to gcc -O3 (was -O2)
+ - Changed libqhull/libqhull_r Makefiles to both produce rbox, qhull, ..., user_eg, and user_eg2
+ - Removed Debian 'config/...'. It was needed for Qhull 2012.
+
+libqhull_r (reentrant Qhull)
+ - Replaced qh_qh with a parameter to each procedure [P. Klosterman]
+ No more globally defined data structures in Qhull
+ Simplified multithreading and C++ user interface
+ All functions are reentrant (Qt: "A reentrant function can ... be called simultaneously from multiple threads, but only if each invocation uses its own data.")
+ No more qh_QHpointer.
+ See user_eg3 and qhulltest
+ New libraries
+ libqhull_r -- Shared library with reentrant sources (e.g., poly_r.h and poly_r.c which replace libqhull's poly.h and poly.c)
+ libqhullstatic_r -- Static library with the same sources as libqhull_r
+ libqhullcpp -- The C++ interface using libqhullstatic_r (further notes below)
+ New executables
+ testqset_r -- Test qset_r.c (the reentrant version of qset.c
+
+ Source code changes for libqhull_r
+ - Add qh_zero() to initialize and zero memory for qh_new_qhull
+ - Remove qh_save_qhull(), qh_restore_qhull(), and qh.old_qhstat from global_r.c
+ - Remove qh_freeqhull2() (global_r.c)
+ - Remove qh_freestatistics() (stat_r.c)
+ - Remove qh_compare_vertexpoint (qhT is not available, unused code)
+ - Remove conditional code for __POWERPC__ from unix_r.c and rbox_r.c
+ - Move qh_last_random into qh->last_random (random_r.c)
+ - Rename sources files with a '_r' suffix. qhull_a.h becomes qhull_ra.h
+ - Replace 'qh' macro with 'qh->'
+ - Replace global qhT with parameter-0
+ - Add qhmemT to beginning of qhT. It may not be used standalone.
+ - Add qhstatT to end of qhT
+ - Remove qhull_inuse
+ - Change qhmem.zzz to qh->qhmem.zzz
+ - Replace qh_qhstat with qh->qhstat
+ - Remove qh_freestatistics
+ - Replace qh_last_random with qh->last_random
+ - Replace rboxT with qh->rbox_errexit, rbox_isinteger, rbox_out_offset
+ - Replace rbox.ferr/fout with qh->ferr/fout
+ - No qh for qh_exit, qh_free, qh_malloc, qh_strtod, qh_strtol, qh_stddev
+ - New qmake include files qhull-app-c_r.pri, qhull-app-shared_r.pri, qhull-libqhull-src_r.pri
+ - Replace 'int' with 'countT' and 'COUNTmax' for large counts and identifiers
+ - qhset converted to countT
+ - Removed vertexT.dim -- No longer needed by cpp
+ Removed MAX_vdim
+ - Guarantee that qh->run_id!=0. Old code assumed that qh_RANDOMint was 31 bits
+
+Changes to libqhullcpp
+ - Added QhullVertexSet.h to libqhullcpp.pro and libqhullpcpp.pro
+ - QhullVertexSet: error if qhsettemp_defined at copy constructor/assignment (otherwise double free)
+ - Enable QhullSet.operator=. Copy constructor and assignment only copies pointers
+ - Changed QhullPoint.operator==() to sqrt(distanceEpsilon)
+ - Added assignment of base class QhullPoints to PointCoordinates.operator=
+ - Enable QhullPoints.operator=
+ - Rename PointCoordinates.point_comment to describe_points
+ - Add 'typename T' to definition of QhullSet<T>::value()
+
+C++ interface
+ - Reimplemented C++ interface on reentrant libqhull_r instead of libqhull
+ - Prepend include files with libqhullcpp/
+ - Replaced UsingLibQhull with QhullQh and macro QH_TRY
+ Removed UsingLibQhull.currentAngleEpsilon and related routines
+ Removed UsingLibQhull_test.cpp
+ Replaced globalDistanceEpsilon with QhullQh.distanceEpsilon
+ Replaced globalAngleEpsilon with QhullQh.angleEpsilon
+ Moved UsingQhullLib.checkQhullMemoryEmpty to QhullQh.checkAndFreeQhullMemory
+ Replaced FACTORepsilon=10 with QhullQh.factor_epsilon=1.0
+ - To avoid -Wshadow for QhullQh*, use 'qqh' for parameters and 'qh()' for methods
+ - Moved messaging from Qhull to QhullQh
+ - Add check of RboxPoints* in qh_fprintf_rbox
+ - Renamed Qhull.initializeQhull to Qhull.allocateQhullQh
+ Added qh_freeqhull(!qh_ALL) as done by unix.c and other programs
+ - Moved QhullPoints.extraCoordinatesCount into QhullPoints.cpp
+ - Replaced section tags with '#//!\name ...'
+ - Removed qhRunId from print() to ostream.
+ - Removed print() to ostream. Use '<< qhullPoint' or '<< qhullPoint.print("message")'
+
+C++ interface for most classes
+ - Remove qhRunId
+ - Add QhullQh *qh_qh to all types
+ Pointer comparisons of facetT,etc. do not test corresponding qh_qh
+ Added to end of type for debugging information, unless wasteful alignment
+ - Add QhullQh * to all constructors
+ - All constructors may use Qhull & instead of QhullQh *
+ - For inherited QhullQh types, change to 'protected'
+ - Renamed 'o' to 'other' except where used extensively in iterators
+ - Except for conditional code, merged the Conversion section into GetSet
+ - Removed empty(). Use isEmpty() instead
+ - Add operator= instead of keeping it private
+ - print_message=0 not allowed. Use "" instead.
+ - Rename isDefined() to isValid() to match Qt conventions
+
+C++ interface by class
+ - Coordinates
+ Removed empty(). Use isEmpty() instead
+ Added append(dim, coordT*)
+ Reformated the iterators
+ Convert to countT
+ - PointCoordinates
+ Added constructors for Qhull or QhullQh* (provides access to QhullPoint.operator==)
+ Removed PointCoordinates(int pointDimension) since PointCoordinates should have a comment. Also, it is ambiguous with PointCoordinates(QhullQh*)
+ Renamed point_comment to describe_points
+ Convert to countT
+ - Qhull
+ Remove qhull_run_i
+ Remove qh_active
+ Replace property feasiblePoint with field feasible_point and methods setFeasiblePoint/feasiblePoint
+ Returns qh.feasible_point if defined
+ Moved useOutputStream to QhullQh use_output_stream
+ Renamed useOutputStream() to hasOutputStream()
+ Replaced qhull_dimension with qh->input_dim //! Dimension of result (qh.hull_dim or one less for Delaunay/Voronoi)
+ Removed global s_qhull_output= 0;
+ Move qhull_status, qhull_message, error_stream, output_stream to QhullQh
+ Renamed qhullQh() to qh()
+ Added check of base address to allocateQhullQh(), Was not needed for qhullpcpp
+ - QhullFacet
+ Constructor requires Qhull or QhullQh* pointer
+ Convert to countT
+ Dropped implicit conversion from facetT
+ Dropped runId
+ Add print("message") to replace print()
+ - QhullFacetList
+ Constructor requires Qhull or QhullQh* pointer
+ Convert to countT
+ Dropped runId
+ - QhullFacetSet
+ Removed empty(). Use isEmpty() instead
+ Constructor requires Qhull or QhullQh* pointer
+ Convert to countT
+ Dropped runId
+ Add operator=
+ Implement print("message")
+ - QhullHyperplane
+ Add hyperplaneAngle() method
+ Rewrite operator== to use hyperplaneAngle()
+ Reorganize fields to keep pointers aligned
+ Except for default constructor requires Qhull or QhullQh* pointer
+ Enable copy assignment
+ Reorganized header
+ - QhullLinkedList
+ Add operator=
+ Removed empty(). Use isEmpty() instead
+ Convert to countT
+ iterator(T) made iterator(const T &)
+ const_iterator(T) made const_iterator(const T &)
+ const_iterator(iterator) made const_iterator(const iterator &)
+ - QhullPoint
+ Add constructors for Qhull or QhullQh* pointer (for id() and operator==)
+ Add defineAs(coordT*)
+ Add getBaseT() and base_type for QhullSet<QhullPoint>
+ Added checks for point_coordinates==0
+ Removed static QhullPoint::id(), use QhullPoint.id() instead
+ distance() throws an error if dimension doesn't agree or if a point is undefined
+ Convert to countT
+ If !qh_qh, operator==() requires equal coordinates
+ Use cout<<p instead of cout<<p.print()
+ Reorganized
+ - QhullPoints
+ Add constructors for Qhull and QhullQh* (for qh.hull_dim, QhullPoint::operator==)
+ Remove QhullPoints(int pointDimension) since it is ambiguous with QhullPoints(QhullQh *qqh)
+ Add operator=
+ Removed empty(). Use isEmpty() instead
+ Convert to countT
+ operator==() tests if pointers are the same. Ituses distanceEpsilon if qh_qh is defined
+ Reorganized
+ - QhullPoints::Iterator and ConstIterator
+ Removed default constructors
+ Add constructors for Qhull and QhullQh* (for qh.hull_dim, QhullPoint::operator==)
+ Moved test of dimension from QHULL_ASSERT to operator==
+ Added QHULL_ASSERT of qh_qh
+ Convert to countT
+ - QhullPointSet
+ Constructor requires Qhull or QhullQh* instead of dimension()
+ Add operator=
+ Removed empty(). Use isEmpty() instead
+ Convert to countT
+ Always print print_message
+ Drop print(). Replace with print("")
+ - QhullQh
+ Added methods hasOutputStream(), disableOutputStream(), and enableOutputStream() (was Qhull UseOutputStream)
+ Add test of qh.NOerrexit to maybeThrowQhullMessage()
+ Add qhull_status, qhull_message, error_stream, output_stream from Qhull
+ Add factor_epsilon
+ - QhullRidge
+ Constructor requires Qhull or QhullQh* pointer
+ Dropped implicit conversion from ridgeT
+ Converted otherFacet() to 'const &'
+ Converted nextRidge3d() to 'const &'
+ Message for '<< QhullRidge' replaces " - " instead of preceding it
+ - QhullSet
+ Removed empty(). Use isEmpty() instead
+ Constructor requires Qhull or QhullQh* pointer
+ Convert to countT
+ Add operator=
+ - QhullVertex
+ Constructor requires Qhull or QhullQh* pointer
+ Convert to countT
+ Dropped implicit conversion from vertexT
+ Add message to '<< QhullVertex'
+ - QhullVertexSet
+ Removed empty(). Use isEmpty() instead
+ Constructor requires Qhull or QhullQh* pointer
+ Convert to countT
+ - UsingQhullLib
+ Removed
+ Replace setGlobalDistanceEpsilon with setFactorEpsilon
+ Replace globalDistanceEpsilon with distanceEpsilon
+
+------------
+Qhull 2012.1 2012/02/18 6.3.1.1494
+ - Fix CMakeLists for libqhull with MATCHES [P. Gajdos]
+
+------------
+Qhull 2012.1 2012/02/18 6.3.1.1490
+
+Code changes
+ - Require option 'Qz' for Delaunay triangulation/Voronoi diagram
+ of cocircular/cospherical points [D. Sheehy]
+ - qh_errexit: Do not call qh_printsummary or qh_printstats on qh_ERRinput
+ - Change error QH6227 (all degenerate) from qh_ERRinput to qh_ERRprec
+ - Change error QH6159 (ID overflow) from qh_ERRinput to qh_ERRqhull
+ - eg/q_eg, q_egtest, q_test: Run if qconvex is in $PATH [M. Atzeri]
+
+Build changes [M. Atzeri]
+ - Install to share/doc/qhull instead of share/doc/packages/qhull
+ - On Unix systems, install to share/man/man1 instead of man/man1
+ - CMakeLists: Remove the installation of user_eg* and testqset
+ - CMakeLists: Remove VERSION from qhull executables and libraries
+ - CMakeLists: Define qhull_SOVERSION instead of qhull_MAJOR
+ - CMakeLists: Set SOVERSION for shared libraries
+ - Rename libraries to qhull, qhull_d, qhull_p, and qhull_pd
+ libqhull6_p.vcproj is now libqhull_p.vcproj
+ mingw builds as libqhull.dll
+ cygwin builds as cygqhull-6.dll
+ linux builds as libqhull.so.6.3.1 with symbolic link as libqhull.so
+ - Developers using qhull 2011:
+ libqhull6.so is now libqhull_p.so. Do not use libqhull.so.
+ qhull6.dll is now qhull_p.dll. Do not use qhull.dll.
+ - Merged road/ into libqhullcpp/ and qhulltest/
+ Moved RoadLogEvent.* and RoadError.* to libqhullcpp
+ Moved RoadTest.* to qhulltest (requires Qt)
+ Installed RoadTest.h in libqhullcpp
+
+Doc changes
+ - index.htm: Mathworks uses qhull for n-d
+ - qhull.htm: Fix qhull for qconvex
+ - qdelaun.htm/qvoronoi.htm: Use option 'Qz' for circular/cospherical inputs
+ - make help: Display targets
+ - Makefile: Better messaging
+
+------------
+Qhull 2012.1 2012/02/02 6.3.0.1483
+
+Bug fixes
+ - Fixed qset.c for -fno-strict-aliasing. This gcc option is no longer needed
+ (disallow two pointers of differing types to the same memory location)
+ - Fixed error in qh_setappend_set if first set full and second set empty
+ - qh_setdelnth, qh_setdelnth_sorted: fixed wording of error message
+ - qh_setcheck: error message listed size and max backwards.
+ - qh_setequal: Allow NULL set as documented
+ - qh_setindex: Allow NULL set as documented
+ - qh_settemppush: report error if NULL
+
+Code changes
+ - Add testqset: low level test of qset.c with mem.c
+ - qh_setendpointer: Implements QSet::endPointer()
+ - Assigned unique error code for qh_gethash
+
+Build changes
+ - Added qhull.dll(.so) for Octave and other Debian builds
+ The global data structure qh_qh is statically defined (no qh_QHpointer)
+ Linked user_eg2 with qhull.dll (libqhull.so) instead of qhullstatic
+ Added qh_dllimport to libqhull.h for qhull.dll with MSVC
+ Changed qhull-app-shared.pri to use libqhull (without qh_QHpointer)
+ - Renamed libqhull6.so to libqhull6_p.so
+ Renamed qhull6.dll to qhull6_p.dll
+ The _p libraries (e.g., libqhull6_p.so) require -Dqh_QHpointer
+ Renamed qhull6.vcproj to libqhull6_p.vcproj
+ Added libqhullp/libqhullp.pro for shared library (libqhull6_p.so)
+ Added qhull-app-sharedp.pri for shared libraries with qh_QHpointer
+ - Install libqhull/*.htm files into include/libqhull
+ - Removed libqhull/qhull.h-deprecated [J. Eaton]
+ - Other changes to Makefile builds
+ Added 'make qtestall' as a smoketest of each qhull program
+ src/libqhull/Makefile: Use 'ar -rs ...' instead of ranlib
+ src/libqhull/Makefile: Fixed targets for cleanall
+ - Other changes to DevStudio builds
+ Moved pdb files for qhull libraries to lib/
+ AdditionalIncludeDirectories: Removed ../src/libqhullcpp
+ Use build-cmake/ for the DevStudio CMake build
+ - Other changes to Qt builds
+ Renamed qhull-libsrc.pri to qhull-libqhull-src.pri
+ - Added explicit d2u conversions to qhull-zip.sh
+ - Fixed \n vs. \r\n issues for Windows source files
+
+ Draft of Debian/AutoConf build (untested)
+ - Adjusted the Makefile.am's for the new directory structure.
+ - Added testqset to bin_PROGRAMS
+ - config/bootstrap.sh copies program sources into src/libqhull
+ - Kept qh_QHpointer=0 (static global data structure, qh_qh). It is faster.
+ Planning a new interface (qhull7?) which passes qh_qh as a parameter
+ - Added config/changelog from the 2003.1 Debian build
+ - Moved the debian/patches directory to config/
+ Optional patches to change smoketest message and turn on qh_QHpointer
+ - Deleted the debian directory. It was the old Debian build from 2003.1
+ Rafael Laboissiere's config directory replaced this build..
+ - Deleted Make-conf.sh (also the old Debian build)
+
+Doc changes
+ - FAQ: Updated notes on computing volume of a Voronoi region
+ - Added direct link to ACM Digital Library for downloading the qhull paper
+ - Added link to Qhull in R
+ - qset.c: Updated notes about NULL sets
+ - qh_setappend: clarify qh_setappend for NULL newelem
+ - qh_setdellast: Fix head note
+ - Add build/README.txt
+ - Add uninstall instructions to README.txt and CMakeLists.txt
+ - Added instructions to create build/*.vcproj to CMakeLists.txt
+ - Update copyright to 2012
+ - Updated page links. Added Google books, patents, and blogs.
+
+-----------
+Qhull 2009.1.3 2011/12/06
+ configure: Add -fno-strict-aliasing if $GCC, Required for gcc 4.1+
+
+------------
+Qhull 2011.2 2011/11/29 6.2.1.1446
+
+Bug fixes
+ - qh_new_qhull: Call qh_prepare_output if !outfile [A. Aldoma]
+ No effect on qhull users since qh_prepare_output is always called.
+ - Replace Qhull-go.pif with Qhull-go.lnk for Windows 7 64-bit [lots]
+ - Error if qh_newhashtable, qh_setnew, or qh_memalloc overflows [X. Cheng]
+ For example, 'rbox 64 D32' overflows hash table for qh_matchnewfacets
+ Qhull uses 32-bit ints for identifiers, counts, and sizes. See "WARN64"
+ - q_eg, q_test: change tail +3 to tail -n +3 [N. Dubray, M. Atzeri]
+ - Qhull-go.bat: Changed 'cmd' to '%comspec%'
+
+Build changes
+ - Added src/libqhull/Makefile for simple gcc build of executables and lib
+ - qhulltest.vcproj: Replaced full path to QT with $QTDIR (e.g., c:/qt/4.7.4)
+ - Split userprintf_rbox.c from userprintf.c,
+ Otherwise qhull brings in rboxlib and rbox brings in libqhull
+ - Makefile: qhullx target must be after LIBQHULLS_OBJS
+ - Makefile: Explicitly list rbox dependencies for qhullx target
+ - MBorland: Fixed tabs
+ - Placed $LIBQHULLS_OBJS in same order. Frequently called ones together.
+ - Update file lists for Make-config.sh [O. Lahaye]
+ - CMakeLists: add README.txt,etc. to DOC_INSTALL_DIR [M. Atzeri]
+ - Restored qhull.h-deprecated.
+ qhull.h conflicts with Qhull.h on Windows systems [C. Abela]
+ - make-config.sh: Add warning that it is out-of-date
+ - Remove extra space in '#! /bin/sh' in q_eg, etc. [P. Cheeseman]
+
+Source changes
+ - libqhull.h: Added qh_True and qh_False for True/False [A. Mutzel]
+ Did not remove or replace True/False since it is used everywhere
+ - Moved error message from qh_argv_to_command to caller. Avoids dependency.
+ - user_eg3.c: Use '10 D2' as default rbox (e.g., 'user_eg3 rbox qhull d')
+ - user.c, user_eg2.c: Add test of qh_qh as done in user_eg.c
+ - q_test: Removed duplicate test of qhull C-0.02
+
+Documentation
+ - index.html: Added ACM Authorizer link to ACM Trans. Math. Software
+ - Split Delaunay and Voronoi FAQs
+ - FAQ: How to compute the volume of a Voronoi region [C, Brinch]
+ - Add 'FS' to qconvex prompt (total area and volume)
+ - Add clarification to 'Fv' about corner input sites [O. Can]
+ - Qhull-go.bat: Removed out-of-date advice. Added title.
+ - qh-code.htm: Updated the discussion of multi-threading for C++ [I. Pirwani]
+
+Qhull 2009.1.2 2011/11/21
+ - Revert to LF line endings [P. Cheeseman]
+ - Remove out-of-date material from qhull-go.bat
+ - Replaced QHULL-GO with a lnk file
+
+Qhull 2011.1 2011/05/23 6.2.0.1385 (exe/dll files unchanged)
+ - delaunay.vcproj: Fixed qhullstatic_d.lib for debug and minrelsize builds
+ - Did not redate the distribution
+
+Qhull 2011.1 2011/05/18 6.2.0.1385 (exe/dll files unchanged)
+ - Add 'm' library to shared and static targets on Unix [A. Bouchard]
+
+Qhull 2011.1 2011/05/14 6.2.0.1383 (exe/dll files unchanged)
+ - PointCoordinates.cpp: Add #include <iterator> [R. Richter, S. Pasko]
+ - Remove deprecated libqhull/qhull.h
+ Use libqhull/libqhull.h instead. Avoids confusion with libqhullcpp/Qhull.h
+ - Makefile: Add LIBDIR, INCDIR, and DESTDIR to install [L.H. de Mello]
+ Separate MAN install from DOC install
+ Create install directories
+ Installs headers to include/libqhull, include/libqhullcpp, include/road
+ - CMakeLists.txt: Add MAN_INSTALL_DIR for qhull.1 and rbox.1 man pages
+ Add RoadTest.h to include/road for Qt users (road_HEADERS)
+ - Renamed md5sum files to avoid two extensions
+ - qh-get.htm: Add Readme links and 2009.1 note.
+ - qh-optf.htm: Fix link
+ - index.htm: Updated Google Scholar link
+ - qhull-zip.sh: Improved error message.
+
+------------
+Qhull 2011.1 2011/04/17 6.2.0.1373
+
+Changes to deliverables
+ - qvoronoi: Deprecated 'Qt' and 'QJn'. Removed from documentation and prompts.
+ These options produced duplicate Voronoi vertices for cospherical data.
+ - Removed doskey from Qhull-go.bat. It is incompatible with Windows 7
+ - Added 'facets' argument to user_eg3.cpp
+ - user_eg links with shared library
+ - qhulltest.cpp: Add closing prompt.
+
+Changes to build system
+ - Reorganized source directories
+ - Moved executables to bin directory
+ - Add CMake build for all targets (CMakeFiles.txt) [M. Moll assisted]
+ - Add gcc build for all targets (Makefile)
+ - Fixed location of qhull.man and rbox.man [M. Moll]
+ - Add DevStudio builds for all targets (build/*.vcproj)
+ - Added shared library (lib/qhull6.dll)
+ Added qh_QHpointer_dllimport to work around problems with MSVC
+ - Added static libraries with and without qh_QHpointer (lib/qhullstatic.lib)
+ - Added eg/make-vcproj.sh to create vcproj/sln files from cmake and qmake
+ - Document location of qh_QHpointer
+ - Use shadow build directory
+ - Made -fno-strict-aliasing conditional on gcc version
+ - Added src/qhull-app-cpp.pri, src/qhull-app-c.pri, etc. for common settings
+ - Add .gitignore with ignored files and directories.
+ - Use .git/info/exclude for locally excluded files.
+ - Fixed MBorland for new directory structure
+ - cleanall (Makefile): Delete 'linked' programs due to libqhull_r and libqhull/Makefile
+
+Changes to documentation
+ - qvoronoi.htm: Remove quotes from qvoronoi example
+ - qhull-cpp.xml: Add naming conventions
+ - index.htm: Add Google Scholar references
+ - qh-optf.htm: Add note about order of 'Fn' matching 'Fv' order [Q. Pan]
+ - Add patch for old builds in qh-get.htm
+ - Added C++ compiling instructions to README.txt
+ - Add instructions for fixing the DOS window
+ - Changed DOS window to command window
+ - Fixed html links
+ - qh-get.htm: Dropped the Spanish mirror site. It was disabled.
+
+Changes to C code
+ - mem.h: Define ptr_intT as 'long long' for Microsoft Windows _win64 builds.
+ On Linux and Mac, 'long' is 64-bits on a 64-bit host
+ - Added qh_QHpointer_dllimport to work around MSVC problem
+ - qconvex.c,etc.: Define prototype for _isatty
+ - Define MSG_QHULL_ERROR in user.h
+ - Move MSG_FIXUP to 11000 and updated FIXUP QH11...
+
+Changes to test code
+ - Add note to q_test than R1e-3 may error (qh-code.htm, Enhancements)
+ - Add test for executables to q_eg, etc.
+ - Fixed Qhull-go.bat. QHULL-GO invokes it with command.com,
+
+Changes to C++ interface
+ - QhullFacet: Added isSimplicial, isTopOrient, isTriCoplanar, isUpperDelaunay
+ - Added Qhull::defineVertexFacetNeighbors() for facetNeighbors of vertices.
+ Automatically called for facet merging and Voronoi diagrams
+ Do not print QhullVertex::facetNeighbors is !facetNeighborsDefined()
+ - Assigned FIXUP identifiers
+ - QhullError: Add copy constructor, assignment operator, and destructor
+ - Add throw() specifiers to RoadError and QhullError
+ - Renamed RoadError::defined() to RoadError::isDefined()
+ - Add #error to Qhull.h if qh_QHpointer is not defined
+
+Changes to C++ code
+ - Fixed bug reported by renangms. Vertex output throws error QH10034
+ and defineVertexNeighbors() does not exist.
+ - Define QHULL_USES_QT for qt-qhull.cpp [renangms]
+ - Reviewed all copy constructors and copy assignments. Updated comments.
+ Defined Qhull copy constructor and copy assignment [G. Rivet-Sabourin]
+ Disabled UsingQhullLib default constructor, copy construct, and copy assign
+ - Merged changes from J. Obermayr in gitorious/jobermayrs-qhull:next
+ - Fix strncat limit in rboxlib.c and global.c
+ - Changes to CMakeLists.txt for openSUSE
+ - Fixed additional uses of strncat
+ - Fixed QhullFacet::PrintRidges to check hasNextRidge3d()
+ - Removed gcc warnings for shadowing from code (src/qhull-warn.pri)
+ - Removed semicolon after extern "C" {...}
+ - Removed experimental QhullEvent/QhullLog
+ - Use fabs() instead of abs() to avoid accidental conversions to int
+ - Fixed type of vertex->neighbors in qh_printvoronoi [no effect on results]
+ - Removed unnecessary if statement in qh_printvoronoi
+
+------------
+qhull 2010.1 2010/01/14
+- Fixed quote for #include in qhull.h [U.Hergenhahn, K.Roland]
+- Add qt-qhull.cpp with Qt conditional code
+- Add libqhullp.proj
+- Add libqhull5 to Readme, Announce, download
+- Reviewed #pragma
+- Reviewed FIXUP and assigned QH tags
+- All projects compile with warnings enabled
+- Replaced 'up' glyphs with &#187;
+- Moved cpp questions to qh-code.htm#questions-cpp
+- Moved suggestions to qh-code.htm#enhance
+- Moved documentation requests to qh-code.htm#enhance
+- Add md5sum file to distributions
+- Switched to DevStudio builds to avoid dependent libraries, 10% slower
+ Removed user_eg3.exe and qhullcpp.dll from Windows build
+ Fix qhull.sln and project files for qh_QHpointer
+- Add eg/qhull-zip.sh to build qhull distribution files
+
+------------
+qhull 2010.1 2010/01/10
+- Test for NULL fp in qh_eachvoronoi [D. Szczerba]
+
+qhull 2010.1 2010/01/09
+
+Changes to build and distribution
+- Use qh_QHpointer=0 for libqhull.a, qhull, rbox, etc.
+ Use -Dqh_QHpointer for libqhullp.a, qhullcpp.dll, etc.
+ qh_QHpointer [2010, gcc] 4% time 4% space, [2003, msvc] 8% time 2% space
+- Add config/ and project/debian/ for Autoconf build [R. Laboissiere]
+ from debian branch in git and http://savannah.nongnu.org/cvs/?group=qhull
+- Add CMakeLists.txt [kwilliams]
+- Fix tabs in Makefile.txt [mschamschula]
+- Add -fno-strict-aliasing to Makefile for gcc 4.1, 4.2, and 4.3 qset segfault
+- Remove user_eg.exe and user_eg2.exe from Windows distribution
+- Order object files by frequency of execution for better locality.
+
+Changes to source
+- Remove ptr_intT from qh_matchvertices. It was int since the beginning.
+- user.h requires <time.h> for CLOCKS_PER_SEC
+- Move ostream<<QhullFacetList from inline to compiled.
+- Removed ConvexHull/ from git. Not used.
+
+------------
+qhull 2009.1.1 2010/01/09
+- Patch release of 2009.1.
+ qh_gethash allowed a negative result, causing overwrite or segfault
+ See git:qhull/project/patch/qhull-2003.1/poly.c-qh_gethash.patch
+ Compared results of q_test, q_eg, q_egtest with patched poly.c, qhull-2003.1
+
+------------
+qhull 2010.1 2010/01/07
+- Assign type to qh.old_qhstat and memT.tempstack [amorilia]
+- Replace tabs with spaces.
+- Fix qh_pointid in case ptr_intT is unsigned
+
+qhull 2010.1 2010/01/06
+- Fixed serious bug in qh_gethash [poly.c]
+- Documentation and build system are incomplete (see above)
+- First release of C++ interface [qh-code.htm]
+- Development moved to http://gitorious.org/qhull
+ git clone git@gitorious.org:qhull/qhull.git
+- Did not fix conformant tesselations for 'Qt'.
+ For details, see http://www.qhull.org/news#bugs of May 2007 and Dec 2006.
+- Use g++ builds for Windows distribution (10% faster than msvc2005)
+ Combined vcproj/ and qtproj/ into project/
+ vcproj will be replaced by qmake generated files
+
+------------
+qhull 2010.0.3 2010/01/05
+Fixed bugs
+- 'QJn': Fix qh.STOPcone in qh_build_withrestart(). It was not cleared.
+- qh_initqhull_outputflags [global.c]: warn about Qc only if QHULLfinished
+ otherwise set if needed
+
+qhull 2010.0.2 2010/01/04
+
+Fixed bugs
+- qh_gethash [poly.c]: fix sign conversion.
+ Previously, the result may be negative, leading to a segfault.
+ The bug is more likely with large address spaces
+ Reviewed all uses of %(modulo) for remainder with negative arguments
+- Reviewed output of q_test and compared to results from 2003.1
+
+Breaking code changes
+- Return type of qh_gethash changed from unsigned to int. Matches 'size'
+- addhash takes a signed hash
+ qh_addhash( newelem, hashtable, hashsize, hash )
+
+Code changes
+- Test for qh_qh in qh_printf
+- Makefile.txt corrected for libqhull build [amorilia]
+- Renamed index to idx to avoid shadowing BSD strings.h [kwilliams]
+
+qhull 2010.0.1 2010/01/03
+
+New Features:
+ - Added option 'Ta' to annotate output with message codes
+
+Preliminary C++ support:
+ - C++ declarations may change without warning
+ - Preliminary documentation for Qhull's C++ interface [qh-code.htm#cpp, qhull-cpp.xml]
+ - Added user_eg3 as an example of Qhull.cpp
+ - Removed qhull_interface.cpp. Use Qhull.cpp instead.
+ If math.h breaks '#include qhull_a.h', preceed it with '#include math.h'
+
+Changes to qhull options and results
+ - Allow 'd' and 'v' as the filename for 'TO ..' and 'TI ...' in qdelaunay [M. Jambon]
+ - 'rbox tN' requires an integer (previously allowed floats)
+ - Allow quoted filenames for 'TO ...' and 'TI ...'
+ - Prefix error messages and warnings with a message code (e.g., QH6012)
+ - Fixed rbox ignoring flags that were not separated by spaces
+ - Report all hidden options before exiting in qh_checkflags()
+ - Defined qh_OPTIONline [user.h] as max length of option line ('FO')
+ - Report error if negative arguments to rbox 'G', 'L', 'Z'
+ - Unknown rbox flag changed from a warning to an error
+ - Set error status 4 qh_ERRmem if rbox runs out of memory
+ - Removed extra spaces at end of line
+
+Breaking Code Changes:
+ - Renamed qh.coplanarset to coplanarfacetset. Avoids conflict with facetT.coplanarset
+ - qh_restore_qhull() zeroes out qh.old_qhstat and qh.old_tempstack. Ownership moved.
+ - Rewrote save_qhull/restore_qhull
+ - Add Ztotcheck to zzdef_ [R. Gardener]
+ - Changed qh_malloc to size_t (was unsigned long)
+ - Declare qh_PRINT instead of int [kwilliams]
+ - In qh_printafacet(), changed error output to 'qh ferr'
+
+Bug fixes to C code:
+ - Use gcc 4.4.0 or later. gcc 4.2.1, 4.2.2, and 4.3.2 -O2 segfaults in qset.c . gcc 4.1.1 was OK
+ See bug report http://gcc.gnu.org/ml/gcc-bugs/2007-09/msg00474.html
+ - Rewrite qh_setappend to avoid g++ 4.1, 4.2, and 4.3 strict_aliasing error.
+ Orion Poplawski (orion@cora.nwra.com)
+ http://www.rpmfind.net/linux/RPM/fedora/12/ppc/qhull-devel-2003.1-13.fc12.ppc64.html
+ - Fixed qh_findfacet_all(), "REALmin" should be "-REALmax" [L.A. Taylor].
+ Effects library users for convex hulls and halfspace intersections.
+ - qh_printfacet [io.c] Removed extra space for neighboring facets
+ - Report error if d points, Delaunay, and not Qz
+ - Fixed double-free of facet->centrum for triangulated facets
+ - Fixed mindist initialization if !testcentrum in io.c findbest_test [Ratcliff]
+ - Fixed parentheses around warning for missing 'Qc' [qh_initqhull_outputflags]
+ - Fixed rbox buffer overflow of 'command' when appending seedbuf
+ - Fixed option string for 'rbox t t999'. Although seed was correctly set to 999,
+ a random seed was appended to the rbox comment (e.g., 'rbox t t999 t32343')
+ - Fixed upper bound of sanity check for qh_RANDOMmax in qh_initqhull_globals()
+
+Changes to C code
+ - Reordered #include from specific to general. Move up .h for module.
+ - Removed qh.old_stat -- never used
+ - Removed qh_clearcenters from qh_freeqhull. Duplicated by qh_delfacet
+ - qh_printcenter [io.c] removed unreachable fprintf argument
+ - qh_getarea() [geom2.c] ignored on multiple calls (qh.hasAreaVolume)
+ - qh_getarea() [geom2.c] checks facet->isarea. Set by QhullFacet.facetArea()
+ - qh_triangulate() [poly2.c] ignored on multiple calls (qh.hasTriangulation)
+ - Add statistics for vertex_visit and visit_id to buildtracing
+ - Defined scale and offset parameters for qh_randomfactor
+
+Bug fixes and changes to mem.c/mem.h
+ - Fixed qhmem.totshort (total short memory in use)
+ - Memory tracing (T5) redone for sort order by object
+ - Added full tracing for short memory allocations.
+ - Added qhmem.totfree (total short memory on freelists)
+ Increases size of qh_memalloc_ and qh_memfree_
+ - Added qhmem.totdropped (leftover freesize at end of each short buffer)
+ - Added qhmem.totunused (short size - request size)
+ - Added qhmem.totbuffer (total short memory buffer w/o links)
+ - Added memory statistics to qh_NOmem;
+ - Added qh_memtotal to track allocated memory
+ - Renamed qh_memfree parameter to 'insize' for consistency with qh_memalloc
+ - Removed qhmem.curlong. qa_memfreeshort computes curlong from cntlong and cntfree
+ - In mem.h, changed ptr_intT to long. qh_meminit() checks that it holds a 'void*'
+
+Fixed g++ and devstudio warnings
+ - Except for bit field conversions, compiles cleanly with
+ -Wall -Wextra -Wshadow -Wcast-qual -Wwrite-strings -Wno-sign-conversion -Wconversion
+ - Fixed warnings at VC8, level 4
+ - Fix data types to remove conversion warnings [kwilliams]
+ - Use size_t for calls to malloc,etc [kwilliams]
+ Retained int sizes for qset.h and mem.h. Follows Qt convention
+ and is easier to work with. int can be 64-bits if 2 billion facets
+ - Change literal strings to const char* [kwilliams]
+ - Added type casts to SETfirst and SETsecond [amorilia+alphax]
+ - getid_() returns an int [kwilliams]
+ - Add missing const annotations [kwilliams]
+ - Fixed 64-bit warnings (marked with "WARN64")
+ - Convert sizeof to (int) for int parameters
+ - In libqhull.c, added explicit casts from long to float, Avoids warning
+ - In global.c, cast time() to int for QRandom-seed. Avoids warning
+
+Changes to C code for C++ support
+ - Add sln, vcproj, and qtpro files for building Qhull -- add to README notes
+ - Added dim to vertexT for cpp interface. Reduced size of qh.vertex_visit
+ - qh_produce_output [io.c] may be called multiple times (C++ interface)
+ - Moved SETsizeaddr_() to qset.h for use by QhullSet.cpp
+ - Option 'Tz' sets flag qh.USEstdout for QhullPoints.cpp
+ - Added support for multiple output runs from QhullPoints.outputQhull
+ - qh_clear_outputflags() resets the output flags
+ - qh_initqhull_outputflags split from qh_initqhull_globals
+ - Added qh.run_id, a random identifier for this instance of Qhull (QhullPoints)
+ - For qh.run_id, initqhull_start initializes qh_RANDOMseed to time instead of 1
+ - Extracted qh_argv_to_command (random.c) from qh_init_qhull_command and fixed a buffer overflow
+ - Moved qh_strtod/qh_strtol from global.c to random.c for use in rboxlib.c
+ - Split out random functions into random.c
+ - Added message codes to qh_fprintf(). See its definition in user.c
+ - Replaced exit, malloc, free, fprintf, and fputs with qh_malloc,...[J.W. Ratcliff]
+ - Added qh_fprintf, qh_malloc, qh_free, ph_printhelp_narrowhull to user.c
+ - Moved qh_printhelp_degenerate and qh_printhelp_singular from io.c to user.c
+ - Clear qh.ERREXITcalled at end of qh_errexit().
+
+Documentation:
+ - Fixed out-of-date CiteSeer references
+ - Renamed html/qh-in.htm to html/qh-code.htm
+ - Add reference to 'Qt' to 'i'
+ - Add reference to 'FS' to 'FA'
+ - qh-impre.htm discusses precision issues for halfspace intersection
+ - Add cross references between options 'FA' and 'FS'
+ - Added link to Wolfram Research's MathWorld site
+ - Updated Fukuda's links
+ - Changed copyright to C.B. Barber for C++, documentation, and merge.c
+ - Updated Qhull citation with page numbers.
+ - Proposed project: constructing Voronoi diagram
+ - Proposed project: computing Voronoi volumes
+ - Replaced tabs with spaces in qhull.txt and rbox.txt
+
+------------
+qhull 2009.1 2009/6/11
+
+This is a maintenance release done by Rafael Laboissiere <rafael@debian.org>.
+ - src/rbox.c (main): Avoid problems of evaluation order when
+ pre-incrementing arguments of strtod
+ - src/io.c (qh_produce_output), src/stat.c (qh_initstatistics): Use %lu
+ instead of %d in the format string for arguments of type size_t
+ - html/qhull.man, html/rbox.man: Fix several syntax, macros, and hyphen
+ problems in man pages
+ - The Autotools files have been generated with modern version of autoconf (2.63),
+ automake/aclocal (1.10.2), and libtool (2.2.6)
+ - Some character issues in the man pages are fixed
+
+------------
+qhull 2003.1 2003/12/30
+
+New Features:
+ - Add Maple output ('FM') for 2-d and 3-d convex hulls [T. Abraham]
+
+Breaking Code Changes:
+ - Annotate C code with 'const'. An ANSI compatible compiler is required.
+
+Bug Fixes and Code Changes:
+ - Fixed qh_findbest() for upperdelaunay facets w/o better, lower neighbors
+ For library users and some qhull users [A. Cutti, E. Milloti, K. Sun]
+ - Preserved qhmem.ferr in qh_memfreeshort() for library users
+ - Removed 'static' from qh_compare... for io.h and merge.h [V. Brumberg]
+ - Split out qh_initqhull_start2() to avoid allocating qh_qh
+ - Split out qh_freeqhull2() to avoid freeing qh_qh
+ - Split out qh_produce_output2() and qh_prepare_output()
+ - qh_initstatistics() frees a previously existing qh_qhstat
+ - qh_initqhull_start2() checks that qh_initstatistics() called first
+
+Documentation:
+ - Add warning to findDelaunay() and qh_in.htm about tricoplanar facets
+ - Noted Edelsbrunner's Geometry & Topology for Mesh Generation [qh-impre.htm]
+ - Noted Gartner's Miniball algorithm [qh_impre.htm]
+ - Noted Veron and Leon's shape preserving simplification [qh_impre.htm]
+
+qhull 2003.1 2003/12/19
+
+Bug Fixes:
+ - Reversed coordinate order for qh.ATinfinity in qh_projectinput [V. Brumberg]
+ This effects:
+ Qhull library 'd' or 'v' users with 'Qz' and unequal coordinate ranges.
+ qdelaunay/qvoronoi users with 'Qbk:0Bk:0', 'Qz', and unequal coordinate ranges
+
+Changes to code:
+ - Replaced qh_VERSION with qh_version in global.c [B. Pearlmutter]
+ The previous techniques were either clumsy or caused compiler errors
+ - Removed unused variables from qh_findbest and qh_findbestnew [B. Pearlmutter]
+ - Note that qh.TESTpoints was added in 2002.1 for tsearch implementation
+
+Changes to distribution:
+ - Added Unix distribution including Debian files [R. Laboissiere]
+ The previous Unix distribution is now the source distribution
+ - Added rpm distribution [L. Mazet]
+ - Investigated generation of Win32 dll. Need to define a C++ interface.
+
+Changes to documentation:
+ - Moved Qhull to www.qhull.org (geom.umn.edu is not available)
+ - The Geometry Center is archived at http://www.geom.uiuc.edu
+ - Reviewed introduction to each program
+ Triangulated output ('Qt') is more accurate than joggled input ('QJ')
+ qdelaunay is 'qhull d Qbb' [C. Ulbrich]
+ qvoronoi is 'qhull v Qbb'
+ Added example of non-simplicial intersection to halfspace intersections
+ - Added warning about using the Qhull library.
+ - Added qhull timings to When to use Qhull [C. Ulbrich]
+ - Reorganized the home page index and the manual index
+ - Moved qh-home.htm to index.htm
+
+Changes to examples
+ - Fixed options for eg/eg.t23.voronoi.imprecise [B. Pearlmutter]
+
+
+------------
+qhull 2002.1 2002/8/20
+
+Changes to distribution:
+ - Set up savannah.nongnu.org/projects/qhull/ [R. Laboissiere]
+ - Set up www.thesa.com as a backup
+ - Added qh-get.htm, a local copy of the download page
+ - Added Visual C++ interface to Qhull, qhull_interface.cpp [K. Erleben]
+ - Use HTTP instead of FTP for downloading Qhull
+ - Renamed qhull-1.0.sit.hqx
+
+Bug fixes:
+ - Fixed sign of coefficients for cdd halfspaces ('FD','Fd') [T. Abraham]
+
+Changes to code:
+ - Replace qh_version with qh_VERSION in libqhull.h.
+ Allows shared libraries and single point of definition
+ - Added qh.TESTpoints for future implementation of tsearch
+
+Changes to build
+ - Makefile.txt works under cygwin
+ - Added Make-config.sh to create a Debian build [R. Laboissiere]
+ - Added .exe to Makefile.txt#clean.
+ - In README, use -fno-strict-aliasing with gcc-2.95.1 [Karas, Krishnaswami]
+ - Fixed chmod in Makefile.txt [B. Karas]
+
+Documentation updates
+ - Documented input options for each program [A. Montesinos]
+ - FAQ: "How to get the radii of the empty spheres for Voronoi vertices"
+
+URL updates:
+ - Changed official URL from locate/qhull to software/qhull
+ - Changed URLs from relative to absolute in qh-home.htm and qh-get.htm
+ - Added URL for Newsgroup: comp.soft-sys.matlab
+ - Added URL for GNU Octave
+ - Added URLs for Google and Google Groups
+ - Replaced qhull_mail.html and qhull_bug.html with mailto links.
+ - Removed URL for Computational Geometry Tribune
+ - Changed URL for locate/cglist to software/cglist
+ - Used site relative links for qh-home.htm
+
+------------
+qhull 3.1 2001/10/04
+
+New features
+ - Added option 'Qt' to triangulate non-simplicial facets
+ - Added option 'TI file' to input data from file
+ - Added option 'Q10' to prevent special processing for narrow distributions
+ e.g., RBOX 1000 L100000 s G1e-6 t1001803691 | QHULL Tv Q10
+ - Tried to compute Voronoi volumes ('Pv'). Requires dual face graph--not easy
+ See Clarkson's hull program for code.
+
+Changes to options
+ - Added numtricoplanars to 'Fs'. Number of good, triangulated facets for 'Qt'
+ - Added Zdelvertextot to 'Fs'. If non-zero and Delaunay, input is degenerate
+ - Qhull command ('FQ') may be repeated.
+ - If 'TPn' and 'TWn' defined, trace the addition of point 'n'
+ otherwise continue tracing (previously it stopped in 4-d)
+ - Removed 'Ft' from qdelaunay. Use 'Qt o' or 'qhull d QJ Qt' instead.
+ For non-simplicial regions, 'Ft' does not satisify the Delaunay property.
+ - If 'Po' or 'TVn', qhull checks outer planes. Use 'Q5' to turn off.
+ - If 'T4', print facet lists and check polygon after adding each point
+
+Corrections to code
+ - rbox: allow 'c' and 'd' with 's r', meshes, etc.
+ - qh_findbest: redesigned as directed search. qh_findbesthorizon for coplanar
+ qh_findbest is faster for many distributions
+ - qh_findbestnew: redesigned to search horizon of coplanar best newfacets
+ needed for distributions with a sharp edge,
+ e.g., rbox 1000 s Z1 G1e-13 | qhull Tv
+ - qh_findbest/qh_findbestnew: search neighbors of better horizon facets
+ was needed for RBOX 1000 s Z1 G1e-13 t996564279 | qhull Tv
+ and RBOX 1000 s W1e-13 P0 t996547055 | QHULL d Qbb Qc Tv
+ - qh_findbest with noupper: could return an upperdelaunay facet if dist>qh.MINoutside.
+ - qh_findbestnew: allow facet->upperdelaunay if dist > qh.MINoutside
+ - qh_partitioncoplanar: call qh_partitionpoint if outside and perpendicular
+ for distributions with a sharp edge
+ - qh_partitionvisible: report precision error if all newfacets degenerate.
+ was needed for RBOX 1000 s W1e-13 t995138628 | QHULL d
+ - qh_createsimplex: clears qh.num_visible, may be non-zero with 'TRn QJ'
+
+Changes to prompts, warnings, and statistics
+ - For Delaunay & Voronoi, 's' reports deleted vertices due to facet merging.
+ They were incorrectly reported as nearly incident points.
+ - Warn if recompute centrum after constructing hull
+ - Simplified narrow hull warning and print all digits of cosine.
+ A narrow hull may lead to a point outside of the hull.
+ - Print total vertices deleted instead of ave. per iteration (Zdelvertextot)
+ - Improved tracing for qh_partitionpoint and qh_partitioncoplanar
+ - Added number of distance tests for checking outer planes (qh_check_maxout)
+ - Simplified "qhull precision error: Only n facets remain."
+ - Included 'TRn' in the causes of a premature exit
+
+Changes to documentation
+ - README.txt: Added quickstart instructions for Visual C++
+ - rbox: Added example of edge of narrow lens, rbox 1000 L100000 s G1e-6
+ - Added cross references between options 'o' and 'p'.
+ - qh-eg.html: added examples comparing 'Qt', 'QJ', and neither 'Qt' nor 'QJ'
+ eg.15a.surface, eg.15b.triangle, eg.17a.delaunay.2, etc.
+ - Reorganized and enhanced discussion of precision problems in qh_impre.htm
+ - Fixed spelling errors [K. Briggs]
+ - Fixed link errors, validated HTML, and spell checked [HomeSite]
+ - Removed unnecessary #TOP links
+ - Added source links to the qh-quick.htm's header and footer
+ - qh-geom.htm, qh-poly.htm: add links to Voronoi functions in io.c
+ - src/index.htm: Added how to search libqhull.h for qhull options
+ - qvoronoi.htm/qdelaun.htm: 'Fc' and 'FN' includes deleted vertices
+
+Changes to URLs
+ - Added http://www.voronoi.com and http://www.magic-software.com
+
+Changes to code
+ - qh_qhull: if 'TVn' or 'TCn' do not call qh_check_maxout and qh_nearcoplanar
+ - reviewed time profile. Qhull is slower. Optimized qh_findbestnew()
+ - qh_addpoint: Added warning note about avoiding a local minimum
+ - qh_checkpolygon: report qh.facet_next error if NARROWhull & dist>MINoutside
+ - qh_findbest: renamed "newfacets" parameter to "isnewfacets" since it is boolT
+ - qh_findbest/qh_findbestnew: testhorizon even if !MERGING
+ Otherwise qhull c D6 | qhull Q0 Tv assigns coplanar points
+ - qh_resetlists: add qh_RESETvisible for qh_triangulate
+ - qh_findbest: search better facets first. Rewritten.
+ - qh_findbest: increased minminsearch, always check coplanar facets.
+ See: RBOX 1000 s Z1 G1e-13 t996564279 | QHULL Tv
+ - qh_findbestnew: report precision error for deleted cones [rare event]
+ e.g.: RBOX 1000 s W1e-13 P0 t1001034076 | QHULL d Qbb Qc Tv
+ - qh_findbesthorizon: search horizon of qh.coplanarset. New.
+ - qh_findbestsharp: replaced with qh_sharpnewfacets followed by qh_findbestnew
+ - qh_partitionpoint, Delaunay sites can not be inside. Otherwise points may
+ be outside upperDelaunay facets yet not near-inside Delaunay facets
+ See: RBOX s 1000 t993602376 | QHULL C-1e-3 d Qbb FA Tv
+ - qh_partitioncoplanar: call qh_findbest/qh_findbestnew with qh DELAUNAY
+ - qh_printlists: format long lines
+ - qh_printvertex: format long lines
+ - user.h: tightened qh_WARNnarrow and qh_MAXnarrow. Do not see problems
+ until they are -1.0.
+ - user.h: defined qh_DISToutside, qh_SEARCHdist, and qh_USEfindbestnew
+ - qh_checkfacet: in 3-d, allow #ridges > #vertices. Can get a vertex twice
+ in a ridge list, e.g, RBOX 1000 s W1e-13 t995849315 D2 | QHULL d Tc Tv
+
+Changes to FAQ
+ - Recommended use of triangulated output ('Qt')
+
+Changes to distribution
+ - Recompiled in Visual C++ 5.0 with optimization (as was version 2.6)
+ - q_test: Added bad cases for Qhull and tests for new features
+
+Changes to Qhull library
+ - Added qh_triangulate() to poly2.c. It triangulates the output.
+ - Added option 'Q11' to copy normals and recompute centrums for tricoplanar facets
+ 'FP' may not print the nearest vertex for coplanar points
+ Use option 'Q11' when adding points after qh_triangulate()
+
+------------
+qhull 3.0 2001/02/11
+
+Changes to distribution
+ - added qconvex, qdelaunay, qhalf, and qvoronoi
+ - added qhull-interface.cpp on calling Qhull from C++ [K. Erleben]
+ - renamed to qhull3.0/.
+ - added eg/, html/, and src/ directories
+
+Changes to URLs
+ - MathLab6 qhull: convhulln, delaunayn, griddatan, tsearchn, vororoin [Z. You]
+ - Wolfram Research wrote a Mathematica interface for qdelaunay [Hinton]
+ - Geomview moved from www.geom.umn.edu to www.geomview.org [M. Phillips}
+ - added URLs for tcsh and cygwin to README.txt
+
+Changes to documentation
+ - reorganized table of contents and renamed qh-man.htm to index.htm
+ - wrote program documentation, dropped qh-opt.htm and qh-optv.htm
+ - added quick reference, qh-quick.htm
+ - reorganized qh-rbox.htm and renamed it to rbox.htm
+ - split up qh-c.htm for quick navigation
+
+Corrections to code
+ - fixed type of arg for error message in qh_initqhull_globals [N. Max]
+ - fixed incorrect initialization of qh MINdenom_1 for scalepoints
+ - fixed drop dim for 'H Qbk:0Bk:0'. Added qh.feasible_point to qh_projectinput
+ - qh_WARNnarrow is angle between facet normals. Negate for warning message.
+ - statistics for Wangle/etc. concerns facet normals. Reworded [E. Voth]
+ - fixed error message for 'FC v'
+ - report cospherical points if Delaunay and error in qh_scalelast()
+
+Changes to code
+ - turn on Pg if (QVn or QGn) and not (Qg or PG)
+ - turn on Qc if format option 'Fc', 'FP', or 'Gp' (removes warning)
+ - removed last good facet unless ONLYgood ('Qg').
+ - added count of non-simplicial or merged facets to 'FS'
+ - added count of non-simplicial facets to 's' (OK if #vertices==dim)
+ - added Znonsimplicial and Znowsimplicial to 'Ts'
+ - allow Mathematica output of dual polytope for halfspace intersection
+ - added qh_checkflags to globals.c for multiple front ends
+ - qh_initqhull_globals sets qh.MERGING if qh.MERGEexact
+ - removed hashentryT. It is no longer used.
+
+Changes to prompts and warnings
+ - reorganized prompts
+ - reorganized synopsis for rbox.c
+ - print warning if 'Fc' or 'FP' with 'd QJ'. Coincident points are unlikely.
+ - ignore warning if options 'v i Pp'. qvoronoi users may need Delaunay tri.
+ - reworded warning if 'Pg' and 'QVn' is not a vertex.
+ - reworded warning for 'Qx Tv', qh_check_points() in poly2.c
+ - if 'Pp', turn off warning for 'Qbb' without 'd' or 'v'
+ - in qh_printsummary() of Voronoi and Delaunay, distinguish QVn, QGn, Pdn, PDn
+
+Changes to FAQ
+ - added FAQ item for nearly flat Delaunay triangles [Z. You]
+ - added FAQ item for area and volume [R. Konatham]
+ - added FAQ item for Voronoi diagram of a square [J. Yong]
+ - edited FAQ item on point location in Delaunay triangles [Z. You]
+ - added FAQ item on nearly flat Delaunay triangles [Dehghani]
+ - added FAQ item about halfspace intersection [V. Tyberghein]
+ - added FAQ item for missing ridges [M. Schmidt]
+ - added FAQ item about qh_call_qhull [R. Snyder]
+ - added FAQ item about memory statistics [Masi]
+ - added FAQ item on meshing non-convex objects
+ - added FAQ item on MATLAB and Mathematica interface
+
+------------
+qhull 2.6 1999/04/19
+ - fixed memory overwrite (no effect) in qh_initstatistics() [K. Ford]
+ - added zdoc11 to qh-stat.h#ZZdef for !qh_KEEPstatistics [K. Ford]
+ - enhanced qh_initqhull_globals() test of qh_RANDOMint and qh_RANDOMmax
+ - added debugging option to always return qh_RANDOMmax from qh_rand()
+ - fixed option 'Qr', qh_initialvertices(), to allow a broken qh_rand()
+ fixed option 'Qr', qh_nextfurthest(), to allow narrow hulls
+ Option 'Qr' simulates the random incremental algorithm for convex hulls
+ - added note that qh.num_outside includes coplanar points for narrow hulls
+ - added FAQ items for triangles/ridges of 3-d Delaunay triangulation[P. Kumar]
+ - added FAQ item about on-line processing with the Qhull library [O. Skare]
+ - changed name of option 'Error-roundoff' to 'Distance-roundoff'
+
+------------
+qhull 2.6 1998/12/30
+ - for the unbounded rays of the Voronoi diagram, use a large box [Schmidt]
+ e.g., 'rbox P4,4,4 P4,2,4 P2,4,4 P4,4,2 10 | qhull v Fv' fails for point 0
+ while 'rbox P4,4,4 P4,2,4 P2,4,4 P4,4,2 10 c G5 | qhull v Fv' is OK.
+ - fixed qh_new_qhull() to use outfile/errfile instead of stdout/stderr [Ford]
+ - clarified COPYING.txt for use in other programs [Archer]
+ - improved qh_readpoints() error message for incorrect point count.
+ - added FAQ item for closest triangle to a point [Sminchisescu & Heijting]
+ - added FAQ item for is a point outside of a facet [Beardsley]
+ - added FAQ item for visiting facets of the Delaunay triangulation [Heijting]
+ - added link to Erickson's Computational Geometry Software
+ - added link to Sharman's HTML version of the comp.graphics.algorithms FAQ
+ - added link to Owen's Meshing Research Corner
+ - added note to 'd' about quadratic size of 'rbox 100 l | qhull d' [Kumar]
+ - added 'about a point' to mentions of halfspace intersection
+ - added request to qh-code.htm to compute largest empty circle [Hase]
+ - the DOS window in Windows NT is better than the DOS window in Windows 95
+ - removed obsolete qh_findfacet() from qh-c.htm [Sminchisescu]
+
+------------
+qhull 2.6 1998/8/12
+ new and modified features
+ - rbox: added option Mn,m,r to generate a rotated lattice or mesh
+ - rbox: report error if more than one of 'l', 'x', 'L', 's', or 'M'
+
+ Qhull library changes
+ - added qh_new_qhull() to user.c for calling qhull() from a program [D. Zwick]
+ rewrote user_eg.c to use qh_new_qhull(). Added qh_QHpointer example.
+ - added qh_CLOCKtype 2 in user.h to call times() for CPU time [B. Hemkemeier]
+ - renamed set.c/set.h to avoid conflict with STL's set.h [G. van den Bergen]
+ can also use '#include <qhull/qhull_a.h>' for Qhull library
+
+ fixed errors
+ - fixed qh_init_B() to call qh_initqhull_mem() once only [D. Zwick]
+ This only effects Qhull library users of qh_QHpointer.
+ - fixed qh_mergecycle_all() to check for redundant vertices [J. Nagle]
+ e.g., 'rbox M3,4 64 | qhull Qc' should report 8 vertices & 48 coplanars
+ - fixed gmcoord initialization in qh_setfacetplane() for qh.RANDOMdist
+ - turn off qh.RANDOMdist during qh_printfacetheader()
+ - fixed error messages for unknown option flags
+
+ documentation changes
+ - added note to FAQ on the Voronoi diagram of cospherical points [H. Hase]
+ - added note to 'Qu' on furthest-site Delaunay triangulation via convex hull
+ - added note to 'QJ' about coplanar points on the convex hull of the input
+ - added note that 'o' and 'FN' list Voronoi regions in site id order [Arvind]
+ - added links to Fukuda's introduction to convex hulls, etc. [H. Hase]
+ - added .c and .h source files to web distribution and qh-c.htm [J. Sands]
+ - documented qh_ZEROdelaunay. Delaunay and Voronoi diagrams do not include
+ facets that are coplanar with the convex hull of the input sites.
+
+ modified code
+ - replaced computed minnorm in qh_sethyperplane_det with distance test
+ - removed 'qh rand_seed' since random number generator is independent of qhull
+ - dropt 'qhull-PPC.sit.bin' from the distribution (out-of-date) [M. Harris]
+
+------------
+qhull 2.5 1998/5/4
+
+ fixed errors
+ - removed zero-area Delaunay triangles and furthest-site triangles [I. Beichl]
+ Zero-area triangles occur for points coplanar with the input's convex hull.
+ - replaced qh.MINnorm with computed minnorm in qh_sethyperplane_det [J. Nagle]
+ qh.MINnorm was incorrect for the convex hull of the "teapot" example.
+ Qhull runs 0-20% slower in 3-d and 4-d.
+ - allow 'Qg' with furthest-site Delaunay triangulation (may be faster)
+ - removed extra space in definition of FOREACHmerge_() for Alpha cc [R. LeRoy]
+ - fixed innerouter type in qh_printvdiagram2 [does not effect code, R. Adams]
+
+ documentation changes
+ - to browse qh-c.htm, set MIME type of .c and .h files to text/html
+ - added example of 3-d Delaunay triangulation to q-faq.htm
+ - added Delaunay and Voronoi examples to qh-optv.htm
+
+------------
+qhull 2.5 1998/2/4
+ - added option 'v Fi' for separating hyperplanes of bounded Voronoi regions
+ - added option 'v Fo' for separating hyperplanes of unbounded Voronoi regions
+ - option 'Pp' turns off the warning, "initial hull is narrow"
+ - fixed partial, 3-d Voronoi diagrams (i.e., 'v Fv QVn Tc')
+ - fixed missing statistics in qh_allstat* [T. Johnson]
+ - rearranged qh_printvdiagram. Use qh_eachvoronoi to iterate Voronoi ridges.
+ - added qh_check_points to qh-math.c
+
+qhull 2.5 1998/1/28
+ - added option 'Fv' to print the Voronoi diagram
+ - added rbox option 'x' to generate random points in a simplex
+ - added rbox option 'y' to generate a simplex and random points
+ - added rbox option 'On' to offset the output
+ - add unpacking instructions to README.txt
+ - updated discussion of forced output, 'Po'
+ - sorted the options alphabetically
+ - removed __STDC__ check from libqhull.h for VisualC++
+ - moved qh_markvoronoi from qh_printvoronoi and cleared facet->seen flags
+ - added facet->seen2 flag for 'Fv'
+
+qhull 2.5 1998/1/16
+ - fixed initialization of qh.last_low on restart of 'd QJ'
+ - renamed qh_JOGGLEmax to qh_JOGGLEmaxincrease
+ - updated URL for Fukuda's cdd program
+
+qhull 2.5 1998/1/12
+
+New or modified features
+ - added option 'Fx' to print vertices by point id [G. Harris, T. McClendon]
+ in 2-d, the vertices are printed in counter-clockwise order
+ for Delaunay triangl., 'Fx' lists the extreme points of the input sites
+ - added option 'QJ' to randomly joggle input instead of merging facets
+ - added option 'TO file' to output results to a file. Needed for Windows95.
+ - added option 'TRn' to rerun Qhull n times. Use to collect joggle statistics
+
+Corrections
+ - fixed 2-d facet orientation for 'i' and 'o' outputs
+ - for Mathematica 2.2 ('m') changed %10.8g to %16.8f [A. Zhaxybayev]
+ - fixed incorrect warning for 'QV0 Qg' in qh_initbuild [B. Wythoff]
+ - fixed unaccessible statistic if !qh_KEEPstatistics for Wnewvertexmax
+ - fixed overestimate of qh ONEmerge for point sets outside of
+ first quadrant and far from the origin
+ - fixed overestimate of 'Qbb' for point sets far from the origin
+ - fixed potential overestimate of qh DISTround under 'Qbb'
+ - fixed 'p' printing coplanar points of unselected facets
+ - fixed 'Ft' printing centrums unnecessarily in 2-d
+
+Changes to documentation
+ - wrote internal design documentation (qh-c.htm)
+ - started frequently asked questions (qh-faq.htm)
+ - added a section on joggled input to qh-impre.htm
+ - added joggle example to qh-eg.htm (eg.15.joggle)
+ - changed q_eg to use 'QJ' instead of 'Q0' were appropriate
+ - added an example to each of the main options
+ - added examples to rbox.htm
+ - added examples to the synopsis
+ - added a reference to Mucke, et al ['96], Fast randomized point location ...
+ - added code for printing Delaunay triangles to qh-code.htm [A. Tsui]
+ - options 'Pdk' and 'PDk' do not drop on equality
+
+Improvements to the code
+ - reviewed warning messages for Qhull options in qh_initflags
+ - added warning to 's' if premature exit from 'TCn' or 'TVn'
+ - 's' prints max distance above/below only if qh.MERGING
+ - reduced maxoutside in qh_check_bestdist/qh_check_points for 'Rn'
+ - in post-merging, rebuild centrums of large facets that become small
+ - lowered cutoff for coplanar facets for ischeckmax/qh_findbest
+ - removed qh_check_maxout for 'Wn Q0'
+ - reset tracing after 'TPn' adds point in 4-d and higher
+
+Changes for the Qhull library
+ - changed qh_setdelaunay to call qh_scalelast if used with 'Qbb' or 'QJ'
+ Delaunay callers to qh_findbestfacet, qh_addpoint, or qh_findfacet_all
+ should always use qh_setdelaunay as in user_eg.c
+ - defined qh.outside_err to check points against a given epsilon [E. Voth]
+ - added header to user_eg.c to avoid its incorporation into qhull [C. Begnis]
+ - added qh_nearcoplanar() calls to user_eg.c
+ only needed if use 'QJ'
+ - expanded __STDC__ warning message in libqhull.h [C. Begnis]
+ - renamed qh maxmaxcoord to qh MAXabs_coord
+ - replaced qh MAXlowcoord with qh MAXabs_coord
+ - random seed ('QR-n') is reset in qh_initqhull_globals after testing
+ - replaced 'FO' options "_max-coord/_min-coord" with "_max-width"/qh.MAXwidth
+
+Other changes to Qhull functions
+ - report error for !bestfacet in qh_findbest (it shouldn't happen) [H. Meuret]
+ - set newbest in qh_findbest only if bestfacet updated
+ - renamed facet parameter for qh_findbest
+ - added maxdist argument to qh_checkpoint
+ - moved 'FO' output after qh_partitionall
+ - separated qh_initbuild from qh_qhull
+ - reinitialize globals modified by qh_buildhull
+ - moved initialization of qh.GOODvertexp & qh.GOODpointp to qh_initbuild
+ - separated qh_nearcoplanar from qh_check_maxout
+ - separated qh_geomplanes from qh_printfacet2geom, etc.
+ - separated qh_freebuild from qh_freeqhull
+ - separated qh_outerinner from io.c to return outer and inner planes
+ - separated qh_distround and qh_detroundoff from qh_maxmin
+
+
+------------
+qhull 2.4 97/4/2
+
+New or modified features
+ - made 'C-0' and 'Qx' default options. Use 'Q0' to not handle roundoff errors
+ - removed point-at-infinity from Delaunay/Voronoi.
+ you no longer need to use 'Qu PDk'
+ - added 'Qz' to add a point-at-infinity to Delaunay and Voronoi constructions
+ - added published version of qhull article in ACM Trans Math Software
+ - ported qhull to Windows95 under Visual C++ and Borland C++
+ - added 'Ft' for triangulation by adding the centrums of non-simplicial facets
+ - added 'Gt' to display 3-d Delaunay triangulations with a transparent hull
+ - changed definition of coplanar point in output to qh min_vertex (see 'Qc')
+ it was qh MAXcoplanar ('Un') [could make vertices non-coplanar]
+ - automatically set 'Qi' for options 'd Qc' and 'v Qc'.
+ - reworded summary ('s') for Delaunay/Voronoi/halfspace.
+ use 'Fs' and 'FS' for summary statistics.
+ - for summary 's' of Delaunay/Voronoi, display number of coplanars for facets
+ if none, display total number of coplanars (i.e., non-vertices)
+ - input comment is first non-numeric text (previously limited to header)
+ - added input warning if use 'Qbb' without 'd' or 'v'
+ - 'Q3' can not be followed with a numeric option
+
+Corrections
+ - fixed qh_partitioncoplanar() to not drop interior points if 'Qi' is used
+ - fixed 'FP d' to report distance in point set instead of paraboloid
+ - fixed qh_findbest() to search all coplanar facets for qh_check_maxout()
+
+Changes to documentation
+ - added example eg.17f.delaunay.3 to show a triangulation of cospherical sites
+ - split up qh-opt.htm into multiple pieces
+ - split off qh-code.htm for Qhull code
+ - renamed .html files to .htm for Windows95
+ - rewrote qh-optv.htm on Delaunay triangulation and Voronoi vertices
+ - added 'man' pages qhull.txt and rbox.txt. These list all the options
+ - removed 'txt' versions of html files
+ - added note to 'PDk' about avoiding a 'd' option immediately after a float
+ - under option 'd', display the triangulation with 'GrD3', not 'GnrD3'
+
+Changes to the Qhull library
+ - added 'format' argument to qh_printfacetNvertex_nonsimplicial() in io.c
+ - removed C++ type errors [J. Stern, S. Marino]
+ - created SETelemt, SETfirstt, etc. for specifying data types.
+ - use SETelem,etc. for assignments.
+ - changed setT.maxsize to 'int' to prevent type conversion warnings
+ - changed FOREACH.. to a comma expression for better code and less warning
+ - changed qh.vertex_visit and .visit_id to unsigned int to prevent warnings
+ - changed clock() to qh_CPUclock (user.h)
+ - qh_init_B() and qh_readpoints() do not set qh.ATinfinity for Delaunay tri.
+ - qh_setvoronoi_all() sets upper Delaunay facets if qh.UPPERdelaunay is set
+ - qh_nearvertex() returns distance in dim-1 for qh.DELAUNAY
+ - removed MSDOS path from qhull_command. Spaces in Win95 tripped qh_setflags
+ - removed memory.h from qhull_a.h. memset,etc. should be in strings.h
+ - split qh_prompt into pieces for Visual C++
+ - added note to qh_addpoint that coordinates can not be deallocated
+ - added Baker '89 to constrained Delaunay diagrams under Enhancements
+ please let me know if you try this
+ - added request for unbounded Voronoi rays to Enhancements
+ please let me know if you try this
+
+------------
+qhull V2.3 96/6/5
+ - fixed total area of Delaunay triangulation. [A. Enge]
+ It should ignore facets on the upper-envelope.
+ - if 'd Qu FA', the total area is summed over the upper-Delaunay triangles
+ - fixed sign of area for Delaunay triangulations.
+ - fixed cdd input format for Delaunay triangulation. [A. Enge]
+ - for cdd input, allow feasible point for halfspaces.
+ - warning if cdd output format for centrums, halfspace intersections, or OFF.
+ - print '0' for area ('Fa') if area is not computed for a facet
+ - option 'QR-n' sets random number seed to n without rotating input
+ - fixed qh_findbest() to retest coplanar and flipped facets after a restart
+ - for 'd Qu Ts' collects angle statistics for upper Delaunay facets
+
+ Changes to the Qhull library
+ - expanded user_eg.c for incremental constructions and Delaunay triangulation
+ - added discussion of incremental construction to qh_man.html#library
+ - WARNING: The default value of qh ATinfinity was changed from True to False.
+ A new flag, qh UPPERdelaunay, was defined for 'Qu'.
+ Please set qh ATinfinity if you explicitly add the point "at-infinity"
+ Please set qh ATinfinity if you explicitly call qh_projectinput.
+ Please set qh UPPERdelaunay if you explicitly cleared qh ATinfinity.
+ Other users do not need to change their code.
+ Now you can build a Delaunay triangulation without creating a point
+ "at-infinity". This removes a potential, hard-to-understand error.
+ qh_readpoints sets qh ATinfinity for options 'd' or 'v' without 'Qu'.
+ qh_initB sets qh ATinfinity for qh PROJECTdelaunay w/o qh UPPERdelaunay.
+ qh_projectinput adds a point "at-infinity" only if qh ATinfinity is set.
+ - added qh_setdelaunay to geom2.c to project points to paraboloid
+ - added qh_findbestfacet() to poly2.c to replace qh_findfacet()
+ - removed procedure qh_findfacet(). It does not always work.
+ - removed NULL option for facet in qh_addpoint(). It does not always work.
+ - added noupper parameter to qh_findbest.
+ - added search of upperdelaunay facets to qh_findbest()
+ - allowed qh_findbest() to start with a flipped or upperdelaunay facet
+ - removed qh_nonupper() -- it is no longer needed
+ - allow space at end of options
+ - fixed qh_projectinput for furthest-site Delaunay (qh PROJECTdelaunay 'd Qu')
+ - added voids to procedure declarations with empty parameter lists
+
+------------
+qhull V2.3 96/3/25
+ - fixed missing qh_check_maxout when coplanar points and no merged facets.
+ - fixed qh_freeqhull/allmem (e.g., if qh_NOmem) [only custom code] [E. Voth]
+ - fixed qh_freeqhull to free qh interior_point [E. Voth]
+ - fixed main() to free all of memory if qh_NOmem. Include "mem.h" [E. Voth]
+ - reset f.newcycle link in qh_mergecycle_all [E. Voth]
+ - fixed false error if 'Tc', coplanar points, and a narrow hull
+ - turn off 'Rn' when computing statistics, checking code, or tracing code
+ - removed ={0} from global.c and stat.c to reduce compiler warnings
+ - changed Makefile dependences to $(HFILES) for all but unix.o, set.o, mem.o
+ - pulled out qh_printpointid and reordered qh_pointid [E. Voth]
+ - removed some compiler warnings
+ - moved 'FO' print of options to just before qh_buildhull
+ - changed 'FO' to list difference from -1 for _narrow-hull
+
+------------
+qhull V2.2 96/2/15
+ - detect narrow initial hulls (cosine of min angle < qh_MAXnarrow in user.h).
+ Write warning if cosine < qh_WARNnarrow in user.h. If narrow (qh NARROWhull),
+ partition coplanar points as outside points and delay coplanar test to end.
+ Needed for RBOX 1000 L100000 s G1e-6 t995127886 | QHULL Tv
+ See 'limitations' in qh-impre.html for further discussion.
+ - corrected some confusions between 'FV' and 'Fv' in qh-opt.html
+ - fixed initialization error for small Voronoi diagrams (e.g., [0,0], [1,0], [0,1]) [J. Velez]
+ - fixed bad return from qh_mindiff in geom2.c
+ - for initial hull, first process facet with furthest outside point (qh_furthestnext)
+ - added facet->notfurthest flag for recomputing furthest point
+ - added check for __STDC__ (needs ANSI C) [E. Voth]
+ - reduced warning messages from [E. Voth]. e[1] in setT still reports a warning.
+ - added a cube to the discussion of option 'v' (Voronoi) in qh-opt.html [J. Velez]
+ - added notes about adjacent vertices to "Calling Qhull" in qh-man.html [R. Lewis & J. Velez]
+ - added note about 'lines closer' when viewing 3-d Delaunay triangulations [P. Kallberg]
+ - added option 'Q9' to always process furthest of furthest outside points.
+ - removed option 'Pp' from q_eg and qh-eg.html.
+
+------------
+qhull V2.2 95/12/28
+ - added option 'Qbb' to scale the last coordinate to [0,m] (max prev coord).
+ This reduces roundoff errors for Delaunay triangulations with integer coordinates.
+ - changed option 'Qu d' to print out the furthest-site Delaunay triangulation
+ Use options 'Qu d PD2' to compute the normal 2-d Delaunay triangulation without
+ the point at infinity.
+ - added notes to the documentation of option 'd'
+ - added notes to limitations of how Qhull handles imprecision
+ - added warning if options 'FP', 'Fc', or 'Gp' without option 'Qc' or 'Qi'
+ - added note to options 'PDk:n' and 'Pdk:n' that closest facet is returned if none match
+ - added check that 'Qbk' and 'QBk' does not invert paraboloid for 'd'
+ - added notes that qh_findfacet and qh_addpoint require lifted points for Delaunay triangulations
+ - fixed 'rbox s 5000 W1e-13 D2 | qhull d Qcu C-0 Qbb'
+ - fixed option 'QbB' (qh SCALEpoints was not set)
+ - fixed minor confusion between 'Gc' (print centrums) and 'Gp' (print points)
+ - rewrote qh_findbestnew for upper convex hull, Delaunay facets
+ - changed option name for 'Gp' from 'Gcoplanar' to 'Gpoints'
+ - changed "nearest" facet for 'Pdk' to threshold - normal
+ - reworked qh GOODclosest for 'Qg'
+ - added note that 'Qg' does not always work
+ - recorded option 'C-0' as "_zero-merge" in option 'FO'
+ - refined qh DISTround in qh_maxmin/geom2.c for Delaunay triangulations
+
+qhull V2.2 95/12/4
+ - Version 2.2 fixes an important bug in computing Delaunay triangulations
+ and convex hulls with edges sharper than ninety degrees. The problem
+ occurs when processing a point at a sharp edge. A directed search can
+ not be used for partitioning because one side may hide facets from the
+ other side. The new lens-shaped distribution for rbox demonstrates the
+ problem. For example, 'rbox 100 L3 G0.5 s | qhull Tv' fails for Version 2.1.
+ O. Schramm found the bug when computing the Delaunay triangulation of points
+ near an outside edge.
+
+ I rewrote qh_findbest and related functions. Qh_findbest
+ uses an exhaustive test for sharp edges (qh_findbest_sharp).
+ Qh_findbest avoids the upper convex hull of Delaunay triangulations.
+
+ Options 'd' and 'v' no longer assign coplanar points to the upper convex hull.
+
+ Qh_check_maxout tests near-inside points. It ignores fully inside points.
+ When done, it removes near-inside points from the coplanar sets.
+
+ If you use qh_addpoint or qh_findbest, please review the function headers.
+ They do not work for lens-shaped hulls for arbitrary facets. They do work for
+ Delaunay triangulations.
+
+ Changes to options for V2.2
+ - added 'Qu' for computing the furthest-site Delaunay triangulation (upper hull)
+ and furthest-site Voronoi vertices.
+ - added 'FP' to print nearest vertex for coplanar points
+ - added coplanar count to 'Fs' and 's'
+ - added number of similar points to summary for Delaunay/Voronoi
+ - Option 'Qc' is no longer necessary when merging.
+ - 'o' format for Voronoi vertices ('v') generates "0" lines for similar points
+ - 'QRn' for Delaunay ('d' or 'v') now rotates about the Z axis (see qh_init_B).
+ Otherwise Qhull does not identify the upper hull
+ - removed option 'Qa' for "all points outside". In V2.1 it was automatically
+ set for 'd'. Even though it was a bad idea, it revealed the above bug.
+ - for option list ('FO'), added version, values for one-merge, maxpos, maxneg,
+ and near-inside, and flags for zero-centrum
+ - optimized 'C-0' and 'Qx'. These options ("zero-centrum") test vertices
+ instead of centrums for adjacent simplicial facets.
+ - if 'Tv', report number of points that are not verified due to qh_findbest
+ - Option 'Q8' ignores near-inside points.
+
+ rbox 95/12/3
+ - added lens distribution ('Ln') It may be used with 's', 'r', 'Wn', and 'Gn'
+ - removed default point count except for the test case, 'Dn'
+ - divided main() of rbox.c into sections
+
+ Documentation changes for V2.2
+ - added examples for lens distribution and furthest-site Delaunay triangulation
+ and renumbered the examples for q_eg
+ - described facet orientation in 'data structure' section [P. Soikkonen]
+ - added notes to qh-man.html/"What to do if something goes wrong"
+ - added note about using 'Tv' to verify the results [O. Schramm]
+ - expanded definition of f_r in Performance section [S. Grundmann]
+ - noted that Geomview display of a Voronoi diagram adds extra edges
+ for unbounded Voronoi cells
+ - rewrote error "convexity constraints may be too strong" [D. Newland]
+ - added limitations section to "Imprecision in Qhull"
+ - added note about zero-area facets to 'Imprecise convex hulls' in qh-impre.html
+ - added note to 'Qi' that it does not retain coplanar points
+ - added note that option 'Q5' may be used if outer planes are not needed
+ - added note to README.txt about Irix 5.1 [C. Goudeseune]
+ - added code fragment for visiting Voronoi vertices to "Calling Qhull" [M. Downes]
+ - added note about qh_addpoint() to "Calling Qhull" [M. Downes]
+
+ Errors fixed for V2.2
+ - use qh_sethyperplane_gauss if 3-d or 4-d norm is less than qh MINnorm
+ - count Zcentrumtests if qh_NOstat
+ - reversed sign convention for qh_sethyperplane_gauss
+ it was opposite to qh_sethyperplane_det
+ this effects qh_determinant and qh_detsimplex
+ - fixed error in qh_findgood_all with multiple restrictions, e.g., 'QVn Pdk'
+ - fixed call to qh_clearcenters for qh_produce_output
+ - in qh_errexit, report p0 if last furthest point
+
+ Changes for users of the Qhull library
+ - user code needs to define qh_version (see user_eg.c)
+ - merged initialization code into qh_init_A and qh_init_B [M. Mazzario]
+ old code works as before.
+ qh_initflags also sets qh qhull_command for qh_initthresholds
+ redid initialization for user_eg.c
+ - simplified user_eg.c. It computes the convex hull of a cube.
+ - added qh_setvoronoi_all in poly2.c to compute Voronoi centers
+ added related note to call_qhull
+ - added qh_findfacet to use in place of qh_findbest
+ - added qh_nearvertex to return nearest vertex in facet to point
+ - redid notes on multiple, concurrent calls in call_qhull/user.c
+ - changed BoolT to unsigned int (prevent implicit enum conversion warnings)
+ - added upperdelaunay to facetT. It indicates a facet of the upper convex hull.
+ - converted qhull-PPC.sit for CodeWarrior 7
+
+ Code changes for V2.2
+ - simplified calls to setjmp() for Cray J916 [Salazar & Velez]
+ - replaced fprintf(fp,string) with fputs in io.c
+ - 'qh num_coplanar' removed (too expensive and not used).
+ - added numcoplanars to qh_countfacets()
+ - added min norm to qh_normalize2(). qh_normalize() wasn't changed
+ - removed type casts from qh_point and qh_pointid [Salazar & Velez]
+ - account for roundoff error in detecting upper convex hull (qh ANGLEround).
+ - post merging uses qh_findbestnew for partitioning
+ - qh_findbestnew for qh_partitioncoplanar goes to best facet
+ - 'Qm' always processes points above the upper hull of a Delaunay triangulation
+ - GOODvertex initialized with qh_findbestnew instead of qh_findbest
+ - for 'v', qh_facetcenter returns furthest-neighbor vertex if 'Qu'
+ - added test for qh feasible_point if use 'Fp' and qh_sethalfspace_all
+ - reviewed Sugihara's algorithm for topologically robust beneath-beyond
+ - on error, report if qhull in post-merging or has completed
+ - if tracing, option 'FO' and qhull command always printed
+ - added current furthest point ("during p%d") to 'T-1' events
+ - added 'TWn' tracing for vertices of new facets (qh_setfacetplane)
+ - added 'TWn' tracing for vertices in an output facet (qh_check_maxout)
+ - reorganized split between poly/poly2.c and geom/geom2.c
+ - reordered object files in Makefile
+
+------------
+qhull V2.1 95/9/25
+ - converted qhull.man to html format, many edits
+ - referenced Shewchuk's triangle program and Schneiders' Mesh Generation page
+ - added option 'Qa' to force all points outside
+ automatically set for "precise" Delaunay or Voronoi [Salazar & Velez]
+ it is turned off by merging, 'Wn', 'Qc' or 'Qi'
+ - added coplanar points to option 'FN'
+ - moved option 'FO' to include default precision options
+ - changed default random number generator to qh_rand in geom2.c (user.h)
+
+ other code changes
+ - fixed option comment Pdrop-facets-dim-less, Qbound-dim-low, QbBound-unit-box
+ - defined ptr_intT for converting 64-bit ptrs to 'unsigned long' [D. Bremner]
+ - defined setelemT to distinguish actual size from pointers [D. Bremner]
+ use either e[...].p or e[...].i (user-level code should use set.h macros)
+ - changed %x to %p in format statements for pointers [D. Bremner]
+ - added test of REALmax,etc. to qh_maxmin [H. Poulard]
+ - added type parameter to qh_memalloc_() macro for type conversion
+ - added type conversion to qh_memalloc() calls where missing
+ - added type conversion to calls into set.c that return void*
+
+ other documentation changes:
+ - new URLs for graphics images
+ - fixed comment for facetT.neighbors in libqhull.h [P. Soikkonen]
+ - changed recommendations for precision errors in qh_printhelp_degenerate()
+ - added recommendation for 'V0' (facet is visible if distance >= 0)
+ - added note about 'long double' to user.h [S. Grundmann]
+ - added note about zero area Delaunay triangles for the 'v' option
+ - added note about locating Delaunay triangles to option 'd' [A. Curtis]
+ - added note that coplanar vertices correspond to duplicate points for 'd'
+ - added note about option 'd' automatically setting 'PDk' (lower convex hull)
+ - added note about precision errors to option 'd' [many users]
+ - added note about qh_findbest() to the Qhull library section [L. Lai]
+ - 'make install' renames qhull.man to qhull.1 for Unix [M. Phillips]
+ - renamed README, etc. to *.txt to match WWW conventions [D. Cervone]
+
+------------
+qhull V2.1 7/10/95
+ - in 2-d, 'v o' lists the vertex at infinity in order [R. Critchlow]
+ - it used to always list the vertex at infinity ("0") first
+ - rewrote description of 'v' option (Voronoi vertices and 2-d diagrams)
+ - added 'PFn' for printing facets whose area is at least 'n' [D. Holland]
+ - prefixed 'Q',etc. to the 'Q',etc. options in the long help prompt
+ - fixed references to 'Fv' and 'FV' options under option 'Hn,n,n'
+ - updated reference to cdd, <ftp://ifor13.ethz.ch/pub/fukuda/cdd/>
+ - in set.c, added some missing type coercions for qhmem.tempstack
+
+qhull V2.1 6/12/95
+ - replaced qhull.ps with qhull-2.ps (paper submitted to ACM TOMS)
+ - use BUFSIZ for setvbuf for Power Macintosh
+ - number of good facets printed if QVn, QGn, Pd, or PD
+ - added Makefile for Borland C++ 4.02 with Win32 [D. Zwick]
+ - added note to Enhancements section of qhull.1 about constrained
+ Delaunay triangulations [T. Rasanen]
+
+qhull V2.1 6/7/95
+ - fixed qh_facetarea_simplex() for non-simplicial facets [L. Schramm]
+ - fixed cast in qh_point and qh_pointid for 64-bit architectures
+ - fixed URL for Amenta's list of computational geometry software
+ - fixed cast in qh_meminitbuffers for qhmem.freelists
+ - added test for !qh half_space in qh readpoints
+ - clarified options for qh_printhelp_singular()
+ - discussed non-simplicial facet area under option 'Fa' in qhull.1
+
+qhull V2.1 6/3/95
+ - home page for Qhull and new descriptions for the Qhull examples
+ http://www.qhull.org
+ - changed SIOUX buffering for Power Macintosh. It runs fast now.
+ added a project file for Metrowerk's C
+ - note in README about compiling qhull on the PC
+
+qhull V2.1 beta 5/15/95
+
+ ======= main changes ========
+ - added halfspace intersection ('Hn,n,n')
+ - facet merging is better, especially for high dimensions
+ - added 'Qx' for exact merges of coplanar points and precision faults
+ - facet merging is faster, especially for high dimensions.
+ e.g., convex hull of the 8-d hypercube is seven times faster
+ - added 'F' output formats for printing each aspect of the convex hull
+ - format 'Fa' computes facet areas, total area, and total volume
+ - format 'FO' writes a descriptive list of selected options to stderr
+ - moved all customization options to user.h
+ - changed the default precision to 'double' since it's needed for Delaunay.
+ using 'float' is faster and takes less space (REALfloat in user.h)
+ - option 'Qm' is no longer important for facet merging
+ - removed need for 'Qs' by selecting initial simplex with pos. determinants
+ - renamed 'Qv' to 'Q7' since virtual memory does not work well for qhull
+ - Qhull is available for the Power Mac (no graphical output)
+
+ ====== other new and modified options ===========
+ - changed default MINvisible ('Vn') to a multiple of premerge_centrum ('C-n')
+ - added 'Un' option to set width of facet for coplanar points.
+ This replaces the previous rules for determining coplanar points.
+ - changed default MINoutside ('Wn') to twice MINvisible ('Vn')
+ - Geomview output adjusts point radii for MINvisible 'Vn'
+ - the input format allows the number of points to precede the dimension
+ - options 'v' 'd' 'FAn' and 'FMn' record good facets ('Pg')
+ - added 'Fd' and 'FD' options for homogeneous coordinates in cdd format
+ - in rbox, added 'h' flag to generate homogeneous coordinates in cdd format
+ - option 'PAn' prints out the n facets with the largest area
+ - option 'PMn' prints out the n facets with the most merges
+ - option 'Po' under tracing ('Tn') no longer tries to write erroneous facets
+ - option 'TCn' only prints the old 'visible' facets for 'f'
+ - 'TFn' reports intermediate results when post-merging
+ - option 'Ts' with option 'TFn' prints intermediate statistics
+ - the message for 'Tv' reports if it is checking outer planes
+ - 'Tz' sends stderr output to stdout
+ - added 'Q1' to ignore angle when sorting merges (merges are worse)
+ - added 'Q2' to not perform merges in independent sets (merges are worse)
+ - added 'Q3' to not remove redundant vertices (faster)
+ - added 'Q4' to avoid merges of old facets into new facets (does worse)
+ - added 'Q5' to skip qh_check_maxout (faster, but less accurate)
+ - added 'Q6' to skip pre-merge of concave and coplanar facets
+ - added 'Qv' for testing vertex neighbors for convexity (needs merge option)
+ - added warning if mix Geomview output with other outputs ('Po' turns off)
+ - options 'o v' for 3-d and higher sort the Voronoi vertices by index
+
+ ======= documentation =======
+ - rewrote the introduction and precision sections
+ - added a section on performance
+ - added an example on halfspace intersection
+ - installed examples of Qhull in
+ <http://www.geom.uiuc.edu/graphics/pix/Special_Topics/Computational_Geometry/>
+
+ ======= Makefile, user.h, and messages =======
+ - Makefile calls ./qhull, ./rbox, and prints short prompt for qhull
+ - added new statistics, e.g., for buildhull
+ - changed default qh_RANDOMtype to RAND_MAX with rand()
+ - added comment about numeric overflow to printhelp_singular
+ - reorganized the code to improve locality of reference
+ - option in mem.h (qh_NOmem) to turn off memory management in qhull
+ - option in user.h (qh_NOtrace) to turn off tracing in qhull
+ - option in user.h (qh_NOmerge) to turn off merging in qhull.
+ - use this instead of redefining qh_merge_nonconvex in user.c
+ - simplified user_eg.c. See qh_call_qhull() in user.c for the full version
+
+ ======== bug fixes ============
+ - fixed error in number of points for 'rbox 100 r' (odd distribution)
+ - fixed performance error in qh_degen_redundant_neighbors
+ - qh_partitionpoint now sets facet->maxoutside for first outside point
+ - fixed performance error in partitioning when merging a large, regular cone
+ - removed memory leak in qh_appendmergeset
+ - removed double free of qh line under errors in qh_readinput()
+ - forcing output on error ('Po') fixed for options 'n' 'o' 'i' 's'
+ - fixed optimization error on HP machines [fprintf(... *p++)]
+
+ ======== changes to libqhull.h for user code =======
+ - qh_collectstatistics and qh_printstatistics removed from libqhull.h.
+ should use qh_printallstatistics instead
+ - qh_findbest uses boolT for newfacets
+ - added qh_findbestnew for non-simplicial facets. qh_findbest is
+ too slow in this case since it needs to look at many nearly coplanar
+ facets.
+ - renamed qh_voronoi/qh_centrum to qh_ASvoronoi, qh_AScentrum
+ - changed facet->id to 32-bits, added new flags for merging
+ - added facet->f for facet pointers while merging and for facet area
+ - added dfacet/dvertex for printing facets/vertices while debugging
+ - added qh_produce_output and qh_printsummary
+
+ ======== changes to code ==========
+ - moved qh_setfacetplane from qh_makenewfacets to qh_makenewplanes
+ - added qh_setfree2, qh_setcompact, and qh_setduplicate to set.c
+ - qh_findgooddist returns list of visible facets instead of setting global
+ - in qh_check_maxout, inside points may be added to coplanar list.
+ - qh_findbestnew used for qh_partitionall. It is faster.
+ - in qh_findbest, changed searchdist to MINvisible+max_outside+DISTround.
+ MINvisible is the default MAXcoplanar.
+ - cleaned up list management via qh_resetlists
+ - uses facet->dupridge to indicate duplicated ridges instead of ->seen
+ - qh_buildtracing records CPU time relative to qh hulltime instead of 0
+
+ ========== changes to merging =======
+ - many performance improvements, especially in high-d.
+ - when merging, qh_findbest and qh_findbestnew stops search at qh_DISToutside
+ - vertex neighbors delayed until first merge
+ - post merges reported every TFn/2 merges
+ - vertex merging turned off in 7-d and higher (lots of work, no benefit).
+ vertex merging moved to qh_qhull_postmerging in 6-d.
+ - replaced qh min_vertex with MAXcoplanar for determining coplanarity
+ - pick closest facets to merge in duplicate ridge instead of flip/flip
+ (see qh_matchduplicates in poly2.c)
+ - optimize merge of simplex into a facet
+ - optimize merge of a "samecycle" of facets into a coplanar horizon facet
+ - cleaned up qh_forcedmerges/qh_flippedmerges and removed facet->newmerge
+ - rewrote qh_merge_degenredundant with separate queue
+ - qh_facetdegen replaced by facet->degenredun
+ - flipped neighbors no longer merged in preference to flip/non-flip pairs
+ - removed angle argument from qh_merge_degenredundant and qh_mergefacet
+ only used for tracing
+ - getmergeset_initial had extra test of neighbor->simplicial
+ - ridge->nonconvex is now set on only one ridge between non-convex facets
+ - moved centrum deletion to qh_updatetested
+ - qh_isnewmerge(facet) changed to facet->newmerge (removed NEWmerges)
+ - qh_findbestneighbor reports correct distance even if testcentrum
+ - added hull_dim factor to qh_BESTcentrum
+ - removed horizon preference in qh_merge_nonconvex (qh AVOIDold)
+ - facet->keepcentrum if qh WIDEfacet or qh_MAXnewcentrum extra vertices
+
+------------
+qhull V2.02 1/25/95
+ - rbox 'z' prints integer coordinates, use 'Bn' to change range
+ - fixed rare bug in qh_check_maxout when qh_bestfacet returns NULL
+ - fixed performance bug in findbestneighbor, should be + BESTnonconvex
+ - renamed 'notgood' flag in 'f' option to 'notG' flag (caused confusion)
+ - changed qh.hulltime to (unsigned) to prevent negative CPU times
+ - added random perturbations to qh_getangle under the 'Rn' option
+ - reviewed the documentation and enhancement list
+ - added discussion of how to intersect halfspaces using qhull
+ - replaced expression that caused incorrect code under an old version of gcc
+ - added buffer after qh.errexit in case 'jmp_buf' has the wrong size
+ - rewrote qh_printhelp_singular for lower-dimensional inputs
+ - rewrote qh_printhelp_degenerate
+ - added options for qh_RANDOMint in qhull_a.h
+ - changed time format for 'TFn' to %02d
+
+------------
+qhull V2.01 6/20/94
+ - fixed bug in qh_matchnewfacets that occured when memory alignment makes
+ facet->neighbors larger than necessary.
+ - fixed bug in computing worst-case simplicial merge of angle coplanar
+ facets (ONEmerge). This decreases (...x) in printsummary.
+
+qhull V2.01 6/17/94
+ - added recommendation for 'Qcm' to documentation and help prompts
+ - added an input warning to qh_check_points ('Tv') if coplanars and no 'Qc'
+ - qh_partitionpoint: always counts coplanar partitions (Zcoplanarpart)
+ - rewrote qh_printhelp_degenerate to emphasize option 'C-0'
+ - For Geomview output, roundoff is not needed when printing the inner and
+ outer planes. This improves Geomview output for the 'Rn' option.
+ - For Geomview output without coplanar points or vertices, qh_GEOMepislon
+ is not needed. This removes the edge gap when displaying a Voronoi cell.
+ - For Geomview output 'Gp', direct vertices to the interior point
+ instead of the arithmetic center of the displayed vertices.
+
+qhull V2.01 6/11/94
+ - if pre-merge, 'Qf' is automatically set. Otherwise an outside point may
+ be dropt by qh_findbest(). This slows down partitioning.
+ - always use 'Qc' if merging and all facet->maxoutside's must be right.
+ Otherwise distributions with many coplanar points may occassionally
+ miss a coplanar point for a facet. This is because qh_findbest, when
+ called by qh_check_maxout, can become stuck at a local maximum if
+ the search is started at an arbitrary facet. With 'Qc', the search
+ is started from a coplanar facet. For example,
+ rbox 1000 W8e-6 t | qhull C-0 Tv
+ will (rarely) report that a facet->minoutside is incorrect
+ - option 'Pp' turns off "Verifying" message for 'Tv'
+ - added qh_copynonconvex to qh_renameridgevertex (fixes rare error)
+ - 'rbox tn' sets random seed to n
+ - 'rbox t' reports random seed in comment line
+ - qh_errexit reports rbox_command | qhull_command and 'QR' random seed
+ - added additional tracing to bestdist and setfacetplane
+ - in qh_checkconvex, need to test coplanar against 0 instead of -DISTround
+ - in qh_checkconvex, always test centrums if merging. The opposite
+ vertex of a simplicial facet may be coplanar since a vertex of
+ a simplicial facet may be above the facet's hyperplane.
+ - fixed error handling in qh_checkconvex when merging
+ - in qh_printsummary, one merge ratio not printed if less than 'Wn'
+ - documented that 'Tv' verifies all facet->maxoutside
+
+qhull V2.01 6/2/94
+ - 's' prints summary to stderr
+ - multiple output formats printed in order to stdout
+ - added statistic for worst-case distance for merging simplicial facets
+ can not hope for a better "max distance above/below facet"
+ print factor for "max distance.."/"merge simplicial" in printsummary
+ - fixed error in scaling input with min/max reversed ('Qb0:1B0:-1')
+ - fixed error in scaling if project & Delaunay & scale ('d Qb0:0B1:0b2:0')
+ - user_eg.c: qh_delpoint removed since it does not always work
+ - user_eg.c now works for either convex hull or Delaunay triangulation
+ - added PROJECTdelaunay for Delaunay triangulations and Voronoi diagrams
+ with libqhull.a and user_eg.c
+ - user_eg.c: if project or scale input, need to copy points
+ - user_eg.c: default just defines main, added fprintf's for qh_errprint etc.
+ - qh_gausselim: a 0 pivot no longer zeros the rest of the array,
+ need the remaining elements for area computation
+ - qh_qhull: restore cos_max, centrum_radius at end of POSTmerging
+ - qh_checkflipped with !allerror is >=0.0 instead of >0.0
+ - removed -Wall from gcc due to unnecesssary "warning: implicit declaration"
+ - renamed 'new' variables and fields to allow compilation by g++
+ - added README notes on C++, and "size isn't known"
+ - updated manual on 'Qg' with coplanar facets and no merging ('rbox c D7')
+ 'Qg Pg' and 'Pg' produce different results because of precision problems
+
+------------
+Converting from qhull 1.01 to qhull 2.00
+ - 'qhull An' is now 'qhull Wn'
+ option 'Wn Po' is faster but it doesn't check coplanars
+ - 'qhull g' is now 'qhull G', and the output formats are different
+ - 'qhull c' is now 'qhull Tc'
+ - 'qhull f' is now 'qhull Qf'
+ - 'qhull o' is now 'qhull Po'
+ - 'qhull b' is now always done
+ - qhull and rbox now use floats, change REALfloat in libqhull.h for doubles
+ - qhull 2.00 fixes several initialization errors and performanace errors
+ e.g., "singular input" on data with lots of 0 coordinates
+ - 'rbox b' is now 'rbox c G0.48'
+ - all rbox distributions are now scaled to a 0.5 box (use 'Bn' to change)
+ - rbox now adds a comment line. This may be removed by 'rbox n'
+ - 'rbox r s Z G' no longer includes the positive pole
+ - no changes to the Macintosh version
+
+qhull V2.00 5/23/94
+ - if force output ('Po'), facet->maxoutside= 'Wn' since coplanars not updated
+ convexity checked if precision problems or verify ('Tv')
+ - if merging, new facets always checked for flipped orientation
+ - a facet is visible (findhorizon) under 'Qm' if distance > max_vertex
+ - if using 'Qm' then min. outside is max_vertex instead of max_outside
+ - default is random()/srandom() in qhull_a.h, checked in initqhull_globals
+ - created internal strtod since strtod may skip spacing after number
+ - removed lower bound (1.0) for qh maxmaxcoord
+ - divzero needs to handle 0/0 and large/small
+ - decreased size of precise vertices
+ - need to initialize qh MINdenom_1 for scalepoints
+ - added factor of qh maxmaxcoord into DISTround (needed for offset)
+ - 'Rn' perturbs distance computations by random +/-n
+ - check_points needs an extra DISTround to get from precise point to computed
+ - rewrote some of the IMPRECISION section in qhull.man
+ - added the input format to the qhull prompt
+ - moved include files to qhull_a.h since some compilers do not use float.h
+ - added qhull.1 and rbox.1 since SGI does not ship nroff
+ - reduced cutoff for printpointvect
+ - use time for qhull random seed only if QR0 or QR-1
+ - radius of vertices and coplanar points determined by pre-merge values
+
+qhull V2.00 5/12/94
+ - facet2point (io.c) used vertex0 instead of vertex1
+ - can't print visible facets after cone is attached
+ - shouldn't check output after STOPcone (TCn)
+ - statistic 'Wminvertex' and 'Wmaxoutside' only if MERGING or APPROXhull
+ - 'make doc' uses lineprinter format for paging
+ - warning if Gpv in 4-d
+
+qhull V2.b05 5/9/94
+ - decreased size of precise vertices
+ - precise facets in 2-d print hyperplanes
+ - accounted for GOODpoint in printsummary
+ - added IMPRECISION section to qhull.man
+ - 'Qg' does work in 7-d unless there's many coplanar facets
+
diff --git a/xs/src/qhull/src/libqhull/DEPRECATED.txt b/xs/src/qhull/src/libqhull/DEPRECATED.txt
new file mode 100644
index 000000000..617275c14
--- /dev/null
+++ b/xs/src/qhull/src/libqhull/DEPRECATED.txt
@@ -0,0 +1,29 @@
+
+qhull/src/libqhull
+
+ This directory contains the non-reentrant version of qhull, libqhull.
+
+ New code should use the reentrant version of qhull (libqhull_r).
+ It allows multiple instances of qhull to run at the same time. On
+ modern architectures, it is nearly as fast as libqhull.
+
+ Qhull programs may be built with either library. Each program has a
+ reentrant version (e.g., qconvex_r.c) and a non-reentrant
+ version (qconvex.c). The programs, rbox, qconvex, qdelaunay, qhalf,
+ and qvoronoi, are built with libqhull. The qhull program is built
+ with libqhull_r.
+
+ Qhull's C++ interface requires libqhull_r. If you previously used the
+ C++ interface, you will need to update your code. See Changes.txt for
+ suggestions.
+
+ The C code in libqhull looks unusual because of the 'qh' macro. The 'qh'
+ macro controls access to Qhull's global data structure, qhT. If
+ 'qh_QHpointer' is defined, 'qh' is 'qh_qh->' and 'qh_qh' is defined as
+ 'qhT *qh_qh', otherwise 'qh' is 'qh_qh.' and 'qh_qh' is defined as
+ 'qhT qh_qh'.
+
+ libqhull will be supported indefinitely. The qh_QHpointer variation
+ of libqhull will be not be retested each release. It is replaced by
+ libqhull_r.
+
diff --git a/xs/src/qhull/src/libqhull/Makefile b/xs/src/qhull/src/libqhull/Makefile
new file mode 100644
index 000000000..bcad65777
--- /dev/null
+++ b/xs/src/qhull/src/libqhull/Makefile
@@ -0,0 +1,240 @@
+# Simple gcc Makefile for non-reentrant qhull and rbox (default gcc/g++)
+#
+# make help
+# See README.txt and ../../Makefile
+#
+# Variables
+# BINDIR directory where to copy executables
+# DESTDIR destination directory for 'make install'
+# DOCDIR directory where to copy html documentation
+# INCDIR directory where to copy headers
+# LIBDIR directory where to copy libraries
+# MANDIR directory where to copy manual pages
+# PRINTMAN command for printing manual pages
+# PRINTC command for printing C files
+# CC ANSI C or C++ compiler
+# CC_OPTS1 options used to compile .c files
+# CC_OPTS2 options used to link .o files
+# CC_OPTS3 options to build shared libraries
+#
+# LIBQHULL_OBJS .o files for linking
+# LIBQHULL_HDRS .h files for printing
+# CFILES .c files for printing
+# DOCFILES documentation files
+# FILES miscellaneous files for printing
+# TFILES .txt versions of html files
+# FILES all other files
+# LIBQHULL_OBJS specifies the object files of libqhullstatic_r.a
+#
+# Results
+# rbox Generates points sets for qhull, qconvex, etc.
+# qhull Computes convex hulls and related structures
+# qconvex, qdelaunay, qhalf, qvoronoi
+# Specializations of qhull for each geometric structure
+# libqhullstatic_r.a Static library for non-reentrant qhull
+# testqset_r Standalone test of non-reentrant qset_r.c with mem_r.c
+# user_eg An example of using qhull (non-reentrant)
+# user_eg2 An example of using qhull (non-reentrant)
+#
+# Make targets
+# make Build results using gcc or another compiler
+# make clean Remove object files
+# make cleanall Remove generated files
+# make doc Print documentation
+# make help
+# make install Copy qhull, rbox, qhull.1, rbox.1 to BINDIR, MANDIR
+# make new Rebuild qhull and rbox from source
+# make printall Print all files
+# make qtest Quick test of qset, rbox, and qhull
+# make test Quck test of qhull, qconvex, etc.
+#
+# Do not replace tabs with spaces. Needed for build rules
+# Unix line endings (\n)
+# $Id: //main/2015/qhull/src/libqhull/Makefile#8 $
+
+DESTDIR = /usr/local
+BINDIR = $(DESTDIR)/bin
+INCDIR = $(DESTDIR)/include
+LIBDIR = $(DESTDIR)/lib
+DOCDIR = $(DESTDIR)/share/doc/qhull
+MANDIR = $(DESTDIR)/share/man/man1
+
+# if you do not have enscript, try a2ps or just use lpr. The files are text.
+PRINTMAN = enscript -2rl
+PRINTC = enscript -2r
+# PRINTMAN = lpr
+# PRINTC = lpr
+
+#for Gnu's gcc compiler, -O3 for optimization, -g for debugging, -pg for profiling
+# -fpic needed for gcc x86_64-linux-gnu. Not needed for mingw
+CC = gcc
+CC_OPTS1 = -O3 -ansi -I../../src -fpic $(CC_WARNINGS)
+
+# for Sun's cc compiler, -fast or O2 for optimization, -g for debugging, -Xc for ANSI
+#CC = cc
+#CC_OPTS1 = -Xc -v -fast -I../../src
+
+# for Silicon Graphics cc compiler, -O2 for optimization, -g for debugging
+#CC = cc
+#CC_OPTS1 = -ansi -O2 -I../../src
+
+# for Next cc compiler with fat executable
+#CC = cc
+#CC_OPTS1 = -ansi -O2 -I../../src -arch m68k -arch i386 -arch hppa
+
+# For loader, ld,
+CC_OPTS2 = $(CC_OPTS1)
+
+# Default targets for make
+
+all: qhull_links qhull_all qtest
+
+help:
+ head -n 50 Makefile
+
+clean:
+ rm -f *.o
+ # Delete linked files from other directories [qhull_links]
+ rm -f qconvex.c unix.c qdelaun.c qhalf.c qvoronoi.c rbox.c
+ rm -f user_eg.c user_eg2.c testqset.c
+
+cleanall: clean
+ rm -f qconvex qdelaunay qhalf qvoronoi qhull *.exe
+ rm -f core user_eg user_eg2 testqset libqhullstatic.a
+
+doc:
+ $(PRINTMAN) $(TXTFILES) $(DOCFILES)
+
+install:
+ mkdir -p $(BINDIR)
+ mkdir -p $(DOCDIR)
+ mkdir -p $(INCDIR)/libqhull
+ mkdir -p $(MANDIR)
+ cp -p qconvex qdelaunay qhalf qhull qvoronoi rbox $(BINDIR)
+ cp -p libqhullstatic.a $(LIBDIR)
+ cp -p ../../html/qhull.man $(MANDIR)/qhull.1
+ cp -p ../../html/rbox.man $(MANDIR)/rbox.1
+ cp -p ../../html/* $(DOCDIR)
+ cp *.h $(INCDIR)/libqhull
+
+new: cleanall all
+
+printall: doc printh printc printf
+
+printh:
+ $(PRINTC) $(LIBQHULL_HDRS)
+
+printc:
+ $(PRINTC) $(CFILES)
+
+# LIBQHULL_OBJS_1 ordered by frequency of execution with small files at end. Better locality.
+# Same definitions as ../../Makefile
+
+LIBQHULLS_OBJS_1= global.o stat.o geom2.o poly2.o merge.o \
+ libqhull.o geom.o poly.o qset.o mem.o random.o
+
+LIBQHULLS_OBJS_2= $(LIBQHULLS_OBJS_1) usermem.o userprintf.o io.o user.o
+
+LIBQHULLS_OBJS= $(LIBQHULLS_OBJS_2) rboxlib.o userprintf_rbox.o
+
+LIBQHULL_HDRS= user.h libqhull.h qhull_a.h geom.h \
+ io.h mem.h merge.h poly.h random.h \
+ qset.h stat.h
+
+# CFILES ordered alphabetically after libqhull.c
+CFILES= ../qhull/unix.c libqhull.c geom.c geom2.c global.c io.c \
+ mem.c merge.c poly.c poly2.c random.c rboxlib.c \
+ qset.c stat.c user.c usermem.c userprintf.c \
+ ../qconvex/qconvex.c ../qdelaunay/qdelaun.c ../qhalf/qhalf.c ../qvoronoi/qvoronoi.c
+
+TXTFILES= ../../Announce.txt ../../REGISTER.txt ../../COPYING.txt ../../README.txt ../Changes.txt
+DOCFILES= ../../html/rbox.txt ../../html/qhull.txt
+
+.c.o:
+ $(CC) -c $(CC_OPTS1) -o $@ $<
+
+# Work around problems with ../ in Red Hat Linux
+qhull_links:
+ # On MINSYS, 'ln -s' may create a copy instead of a symbolic link
+ [ -f qconvex.c ] || ln -s ../qconvex/qconvex.c
+ [ -f qdelaun.c ] || ln -s ../qdelaunay/qdelaun.c
+ [ -f qhalf.c ] || ln -s ../qhalf/qhalf.c
+ [ -f qvoronoi.c ] || ln -s ../qvoronoi/qvoronoi.c
+ [ -f rbox.c ] || ln -s ../rbox/rbox.c
+ [ -f testqset.c ] || ln -s ../testqset/testqset.c
+ [ -f unix.c ] || ln -s ../qhull/unix.c
+ [ -f user_eg.c ] || ln -s ../user_eg/user_eg.c
+ [ -f user_eg2.c ] || ln -s ../user_eg2/user_eg2.c
+
+# compile qhull without using bin/libqhullstatic.a
+qhull_all: qconvex.o qdelaun.o qhalf.o qvoronoi.o unix.o user_eg.o user_eg2.o rbox.o testqset.o $(LIBQHULLS_OBJS)
+ $(CC) -o qconvex $(CC_OPTS2) -lm $(LIBQHULLS_OBJS_2) qconvex.o
+ $(CC) -o qdelaunay $(CC_OPTS2) -lm $(LIBQHULLS_OBJS_2) qdelaun.o
+ $(CC) -o qhalf $(CC_OPTS2) -lm $(LIBQHULLS_OBJS_2) qhalf.o
+ $(CC) -o qvoronoi $(CC_OPTS2) -lm $(LIBQHULLS_OBJS_2) qvoronoi.o
+ $(CC) -o qhull $(CC_OPTS2) -lm $(LIBQHULLS_OBJS_2) unix.o
+ $(CC) -o rbox $(CC_OPTS2) -lm $(LIBQHULLS_OBJS) rbox.o
+ $(CC) -o user_eg $(CC_OPTS2) -lm $(LIBQHULLS_OBJS_2) user_eg.o
+ $(CC) -o user_eg2 $(CC_OPTS2) -lm $(LIBQHULLS_OBJS_1) user_eg2.o usermem.o userprintf.o io.o
+ $(CC) -o testqset $(CC_OPTS2) -lm mem.o qset.o usermem.o testqset.o
+ -ar -rs libqhullstatic.a $(LIBQHULLS_OBJS)
+ #libqhullstatic.a is not needed for qhull
+ #If 'ar -rs' fails try using 'ar -s' with 'ranlib'
+ #ranlib libqhullstatic.a
+
+qtest:
+ @echo ============================================
+ @echo == make qtest ==============================
+ @echo ============================================
+ @echo -n "== "
+ @date
+ @echo
+ @echo Testing qset.c and mem.c with testqset
+ ./testqset 10000
+ @echo Run the qhull smoketest
+ ./rbox D4 | ./qhull
+ @echo ============================================
+ @echo == To smoketest qhull programs
+ @echo '== make test'
+ @echo ============================================
+ @echo
+ @echo ============================================
+ @echo == For all make targets
+ @echo '== make help'
+ @echo ============================================
+ @echo
+
+test: qtest
+ @echo ==============================
+ @echo ========= qconvex ============
+ @echo ==============================
+ -./rbox 10 | ./qconvex Tv
+ @echo
+ @echo ==============================
+ @echo ========= qdelaunay ==========
+ @echo ==============================
+ -./rbox 10 | ./qdelaunay Tv
+ @echo
+ @echo ==============================
+ @echo ========= qhalf ==============
+ @echo ==============================
+ -./rbox 10 | ./qconvex FQ FV n Tv | ./qhalf Tv
+ @echo
+ @echo ==============================
+ @echo ========= qvoronoi ===========
+ @echo ==============================
+ -./rbox 10 | ./qvoronoi Tv
+ @echo
+ @echo ==============================
+ @echo ========= user_eg ============
+ @echo == w/o shared library ========
+ @echo ==============================
+ -./user_eg
+ @echo
+ @echo ==============================
+ @echo ========= user_eg2 ===========
+ @echo ==============================
+ -./user_eg2
+ @echo
+
+# end of Makefile
diff --git a/xs/src/qhull/src/libqhull/Mborland b/xs/src/qhull/src/libqhull/Mborland
new file mode 100644
index 000000000..46d88dbd4
--- /dev/null
+++ b/xs/src/qhull/src/libqhull/Mborland
@@ -0,0 +1,206 @@
+#########################################################################
+# Borland C++ 4.02 for Win32 and DOS Power Pack #
+# Makefile for qhull and rbox #
+# #
+# make -fMborland all to produce qconvex, qhull, and rbox #
+# make -fMborland user_eg to produce user_eg #
+# make -fMborland user_eg2 to produce user_eg2 #
+# make -fMborland new to rebuild qhull and rbox from source #
+# make -fMborland clean to remove object files #
+# make -fMborland cleanall to remove all generated files #
+# make -fMborland test to test rbox and qhull #
+# #
+# Author: D. Zwick of Germany, C.B. Barber #
+#########################################################################
+
+CC = bcc32 # 32 bit compiler for DOS
+ # bcc32i - Intel's compiler
+LINKER = $(CC) # bcc calls tlink32 with needed options
+CFLAGS = -w- -A -O2
+ # -w- no warnings, bcc doesn't handle assigns in conditions
+ # -A Ansi standard
+ # -X no auto-dependency outputs
+ # -v debugging, use CCOPTS for both
+ # -O2 optimization
+!if $d(_DPMI)
+LFLAGS = -WX -w- # -WX loads DPMI library
+!else
+LFLAGS = -lap -lx -lc
+ # -lap 32-bit console application
+ # -lx no map file
+ # -lc case is significant
+!endif
+
+EXERB = rbox
+EXEQH = qhull
+EXEQC = qconvex
+EXEQD = qdelaunay
+EXEQV = qvoronoi
+EXEQF = qhalf
+EXEEG = user_eg
+EXEEG2 = user_eg2
+
+TMPFILE = BCC32tmp.cfg
+
+OBJS1 = global.obj stat.obj geom2.obj poly2.obj merge.obj
+OBJS2 = libqhull.obj geom.obj poly.obj qset.obj mem.obj
+OBJS3 = random.obj usermem.obj userprintf.obj io.obj user.obj
+OBJS4 = rboxlib.obj random.obj usermem.obj userprintf_rbox.obj
+
+HFILES1 = libqhull.h stat.h qhull_a.h user.h
+
+
+# General rules
+
+.c.obj:
+ $(CC) -c $(CFLAGS) $<
+
+# Default
+
+all: $(EXERB) $(EXEQH) $(EXEQC) $(EXEQD) $(EXEQV) $(EXEQF) test
+
+help:
+ @echo USAGE:
+ @echo "make all to produce qhull, rbox, qconvex, qdelaun, qvoronoi, qhalf"
+ @echo "make user_eg to produce user_eg"
+ @echo "make user_eg2 to produce user_eg2"
+ @echo "make new to rebuild qhull and rbox from source"
+ @echo "make clean to remove object files"
+ @echo "make cleanall to remove all generated file"
+ @echo "make test to test rbox and qhull"
+ @echo OPTIONS (default is 32-bit console app):
+ @echo "-D_DPMI for C++ 4.01 and DOS Power Pack"
+
+# Executables
+
+$(EXEQH): ..\..\bin\$(EXEQH).exe
+ @echo Made ..\..\bin\$(EXEQH).exe
+
+unix.obj: ..\qhull\unix.c
+..\..\bin\$(EXEQH).exe: unix.obj $(OBJS1) $(OBJS2) $(OBJS3)
+ @echo unix.obj > $(TMPFILE)
+ @echo $(OBJS1) >> $(TMPFILE)
+ @echo $(OBJS2) >> $(TMPFILE)
+ @echo $(OBJS3) >> $(TMPFILE)
+ $(LINKER) -e$@ $(CFLAGS) $(LFLAGS) @$(TMPFILE)
+
+$(EXEQC): ..\..\bin\$(EXEQC).exe
+ @echo Made ..\..\bin\$(EXEQC).exe
+
+qconvex.obj: ..\qconvex\qconvex.c
+..\..\bin\$(EXEQC).exe: qconvex.obj $(OBJS1) $(OBJS2) $(OBJS3)
+ @echo qconvex.obj > $(TMPFILE)
+ @echo $(OBJS1) >> $(TMPFILE)
+ @echo $(OBJS2) >> $(TMPFILE)
+ @echo $(OBJS3) >> $(TMPFILE)
+ $(LINKER) -e$@ $(CFLAGS) $(LFLAGS) @$(TMPFILE)
+
+$(EXEQD): ..\..\bin\$(EXEQD).exe
+ @echo Made ..\..\bin\$(EXEQD).exe
+
+qdelaun.obj: ..\qdelaunay\qdelaun.c
+..\..\bin\$(EXEQD).exe: qdelaun.obj $(OBJS1) $(OBJS2) $(OBJS3)
+ @echo qdelaun.obj > $(TMPFILE)
+ @echo $(OBJS1) >> $(TMPFILE)
+ @echo $(OBJS2) >> $(TMPFILE)
+ @echo $(OBJS3) >> $(TMPFILE)
+ $(LINKER) -e$@ $(CFLAGS) $(LFLAGS) @$(TMPFILE)
+
+$(EXEQV): ..\..\bin\$(EXEQV).exe
+ @echo Made ..\..\bin\$(EXEQV).exe
+
+qvoronoi.obj: ..\qvoronoi\qvoronoi.c
+..\..\bin\$(EXEQV).exe: qvoronoi.obj $(OBJS1) $(OBJS2) $(OBJS3)
+ @echo qvoronoi.obj > $(TMPFILE)
+ @echo $(OBJS1) >> $(TMPFILE)
+ @echo $(OBJS2) >> $(TMPFILE)
+ @echo $(OBJS3) >> $(TMPFILE)
+ $(LINKER) -e$@ $(CFLAGS) $(LFLAGS) @$(TMPFILE)
+
+$(EXEQF): ..\..\bin\$(EXEQF).exe
+ @echo Made ..\..\bin\$(EXEQF).exe
+
+qhalf.obj: ..\qhalf\qhalf.c
+..\..\bin\$(EXEQF).exe: qhalf.obj $(OBJS1) $(OBJS2) $(OBJS3)
+ @echo qhalf.obj > $(TMPFILE)
+ @echo $(OBJS1) >> $(TMPFILE)
+ @echo $(OBJS2) >> $(TMPFILE)
+ @echo $(OBJS3) >> $(TMPFILE)
+ $(LINKER) -e$@ $(CFLAGS) $(LFLAGS) @$(TMPFILE)
+
+$(EXEEG): ..\..\bin\$(EXEEG).exe
+ @echo Made ..\..\bin\$(EXEEG).exe
+
+user_eg.obj: ..\user_eg\user_eg.c
+..\..\bin\$(EXEEG).exe: user_eg.obj $(OBJS1) $(OBJS2) $(OBJS3)
+ @echo user_eg.obj > $(TMPFILE)
+ @echo $(OBJS1) >> $(TMPFILE)
+ @echo $(OBJS2) >> $(TMPFILE)
+ @echo $(OBJS3) >> $(TMPFILE)
+ $(LINKER) -e$@ $(CFLAGS) $(LFLAGS) @$(TMPFILE)
+
+$(EXEEG2): ..\..\bin\$(EXEEG2).exe
+ @echo Made ..\..\bin\$(EXEEG2).exe
+
+user_eg2.obj: ..\user_eg2\user_eg2.c
+..\..\bin\$(EXEEG2).exe: user_eg2.obj $(OBJS1) $(OBJS2) $(OBJS3)
+ @echo user_eg2.obj > $(TMPFILE)
+ @echo $(OBJS1) >> $(TMPFILE)
+ @echo $(OBJS2) >> $(TMPFILE)
+ @echo $(OBJS3) >> $(TMPFILE)
+ $(LINKER) -e$@ $(CFLAGS) $(LFLAGS) @$(TMPFILE)
+
+$(EXERB): ..\..\bin\$(EXERB).exe
+ @echo Made ..\..\bin\$(EXERB).exe
+
+rbox.obj: ..\rbox\rbox.c
+..\..\bin\$(EXERB).exe: rbox.obj $(OBJS4)
+ @echo rbox.obj > $(TMPFILE)
+ @echo $(OBJS4) >> $(TMPFILE)
+ $(LINKER) -e$@ $(CFLAGS) $(LFLAGS) @$(TMPFILE)
+
+# Test rbox and qhull
+
+test:
+ @..\..\bin\rbox D4 > test.x
+ @..\..\bin\qhull <test.x
+ @del test.x
+
+# Clean up
+
+clean:
+ @del *.obj
+ @del $(TMPFILE)
+
+cleanall: clean
+ @del ..\..\bin\$(EXERB).exe
+ @del ..\..\bin\$(EXEQC).exe
+ @del ..\..\bin\$(EXEQD).exe
+ @del ..\..\bin\$(EXEQF).exe
+ @del ..\..\bin\$(EXEQH).exe
+ @del ..\..\bin\$(EXEQV).exe
+ @del ..\..\bin\$(EXEEG).exe
+ @del ..\..\bin\$(EXEEG2).exe
+ @del ..\q_test.x
+ @del ..\q_test.log.1
+
+# Clean up and rebuild all
+
+new: cleanall all
+
+# Header file dependencies
+
+libqhull.obj stat.obj user.obj global.obj usermem.obj userprintf.obj: $(HFILES1)
+random.obj: libqhull.h random.h
+geom.obj geom2.obj: $(HFILES1) geom.h
+poly.obj poly2.obj: $(HFILES1) poly.h
+io.obj: $(HFILES1) io.h
+merge.obj: $(HFILES1) merge.h
+mem.obj: mem.h
+qset.obj: qset.h mem.h
+unix.obj: libqhull.h user.h
+qconvex.obj: libqhull.h user.h
+qdelaun.obj: libqhull.h user.h
+qhalf.obj: libqhull.h user.h
+qvoronoi.obj: libqhull.h user.h
+rbox.obj: user.h
diff --git a/xs/src/qhull/src/libqhull/geom.c b/xs/src/qhull/src/libqhull/geom.c
new file mode 100644
index 000000000..71444f05a
--- /dev/null
+++ b/xs/src/qhull/src/libqhull/geom.c
@@ -0,0 +1,1234 @@
+/*<html><pre> -<a href="qh-geom.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ geom.c
+ geometric routines of qhull
+
+ see qh-geom.htm and geom.h
+
+ Copyright (c) 1993-2015 The Geometry Center.
+ $Id: //main/2015/qhull/src/libqhull/geom.c#2 $$Change: 1995 $
+ $DateTime: 2015/10/13 21:59:42 $$Author: bbarber $
+
+ infrequent code goes into geom2.c
+*/
+
+#include "qhull_a.h"
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="distplane">-</a>
+
+ qh_distplane( point, facet, dist )
+ return distance from point to facet
+
+ returns:
+ dist
+ if qh.RANDOMdist, joggles result
+
+ notes:
+ dist > 0 if point is above facet (i.e., outside)
+ does not error (for qh_sortfacets, qh_outerinner)
+
+ see:
+ qh_distnorm in geom2.c
+ qh_distplane [geom.c], QhullFacet::distance, and QhullHyperplane::distance are copies
+*/
+void qh_distplane(pointT *point, facetT *facet, realT *dist) {
+ coordT *normal= facet->normal, *coordp, randr;
+ int k;
+
+ switch (qh hull_dim){
+ case 2:
+ *dist= facet->offset + point[0] * normal[0] + point[1] * normal[1];
+ break;
+ case 3:
+ *dist= facet->offset + point[0] * normal[0] + point[1] * normal[1] + point[2] * normal[2];
+ break;
+ case 4:
+ *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3];
+ break;
+ case 5:
+ *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3]+point[4]*normal[4];
+ break;
+ case 6:
+ *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3]+point[4]*normal[4]+point[5]*normal[5];
+ break;
+ case 7:
+ *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3]+point[4]*normal[4]+point[5]*normal[5]+point[6]*normal[6];
+ break;
+ case 8:
+ *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3]+point[4]*normal[4]+point[5]*normal[5]+point[6]*normal[6]+point[7]*normal[7];
+ break;
+ default:
+ *dist= facet->offset;
+ coordp= point;
+ for (k=qh hull_dim; k--; )
+ *dist += *coordp++ * *normal++;
+ break;
+ }
+ zinc_(Zdistplane);
+ if (!qh RANDOMdist && qh IStracing < 4)
+ return;
+ if (qh RANDOMdist) {
+ randr= qh_RANDOMint;
+ *dist += (2.0 * randr / qh_RANDOMmax - 1.0) *
+ qh RANDOMfactor * qh MAXabs_coord;
+ }
+ if (qh IStracing >= 4) {
+ qh_fprintf(qh ferr, 8001, "qh_distplane: ");
+ qh_fprintf(qh ferr, 8002, qh_REAL_1, *dist);
+ qh_fprintf(qh ferr, 8003, "from p%d to f%d\n", qh_pointid(point), facet->id);
+ }
+ return;
+} /* distplane */
+
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="findbest">-</a>
+
+ qh_findbest( point, startfacet, bestoutside, qh_ISnewfacets, qh_NOupper, dist, isoutside, numpart )
+ find facet that is furthest below a point
+ for upperDelaunay facets
+ returns facet only if !qh_NOupper and clearly above
+
+ input:
+ starts search at 'startfacet' (can not be flipped)
+ if !bestoutside(qh_ALL), stops at qh.MINoutside
+
+ returns:
+ best facet (reports error if NULL)
+ early out if isoutside defined and bestdist > qh.MINoutside
+ dist is distance to facet
+ isoutside is true if point is outside of facet
+ numpart counts the number of distance tests
+
+ see also:
+ qh_findbestnew()
+
+ notes:
+ If merging (testhorizon), searches horizon facets of coplanar best facets because
+ after qh_distplane, this and qh_partitionpoint are the most expensive in 3-d
+ avoid calls to distplane, function calls, and real number operations.
+ caller traces result
+ Optimized for outside points. Tried recording a search set for qh_findhorizon.
+ Made code more complicated.
+
+ when called by qh_partitionvisible():
+ indicated by qh_ISnewfacets
+ qh.newfacet_list is list of simplicial, new facets
+ qh_findbestnew set if qh_sharpnewfacets returns True (to use qh_findbestnew)
+ qh.bestfacet_notsharp set if qh_sharpnewfacets returns False
+
+ when called by qh_findfacet(), qh_partitionpoint(), qh_partitioncoplanar(),
+ qh_check_bestdist(), qh_addpoint()
+ indicated by !qh_ISnewfacets
+ returns best facet in neighborhood of given facet
+ this is best facet overall if dist > - qh.MAXcoplanar
+ or hull has at least a "spherical" curvature
+
+ design:
+ initialize and test for early exit
+ repeat while there are better facets
+ for each neighbor of facet
+ exit if outside facet found
+ test for better facet
+ if point is inside and partitioning
+ test for new facets with a "sharp" intersection
+ if so, future calls go to qh_findbestnew()
+ test horizon facets
+*/
+facetT *qh_findbest(pointT *point, facetT *startfacet,
+ boolT bestoutside, boolT isnewfacets, boolT noupper,
+ realT *dist, boolT *isoutside, int *numpart) {
+ realT bestdist= -REALmax/2 /* avoid underflow */;
+ facetT *facet, *neighbor, **neighborp;
+ facetT *bestfacet= NULL, *lastfacet= NULL;
+ int oldtrace= qh IStracing;
+ unsigned int visitid= ++qh visit_id;
+ int numpartnew=0;
+ boolT testhorizon = True; /* needed if precise, e.g., rbox c D6 | qhull Q0 Tv */
+
+ zinc_(Zfindbest);
+ if (qh IStracing >= 3 || (qh TRACElevel && qh TRACEpoint >= 0 && qh TRACEpoint == qh_pointid(point))) {
+ if (qh TRACElevel > qh IStracing)
+ qh IStracing= qh TRACElevel;
+ qh_fprintf(qh ferr, 8004, "qh_findbest: point p%d starting at f%d isnewfacets? %d, unless %d exit if > %2.2g\n",
+ qh_pointid(point), startfacet->id, isnewfacets, bestoutside, qh MINoutside);
+ qh_fprintf(qh ferr, 8005, " testhorizon? %d noupper? %d", testhorizon, noupper);
+ qh_fprintf(qh ferr, 8006, " Last point added was p%d.", qh furthest_id);
+ qh_fprintf(qh ferr, 8007, " Last merge was #%d. max_outside %2.2g\n", zzval_(Ztotmerge), qh max_outside);
+ }
+ if (isoutside)
+ *isoutside= True;
+ if (!startfacet->flipped) { /* test startfacet */
+ *numpart= 1;
+ qh_distplane(point, startfacet, dist); /* this code is duplicated below */
+ if (!bestoutside && *dist >= qh MINoutside
+ && (!startfacet->upperdelaunay || !noupper)) {
+ bestfacet= startfacet;
+ goto LABELreturn_best;
+ }
+ bestdist= *dist;
+ if (!startfacet->upperdelaunay) {
+ bestfacet= startfacet;
+ }
+ }else
+ *numpart= 0;
+ startfacet->visitid= visitid;
+ facet= startfacet;
+ while (facet) {
+ trace4((qh ferr, 4001, "qh_findbest: neighbors of f%d, bestdist %2.2g f%d\n",
+ facet->id, bestdist, getid_(bestfacet)));
+ lastfacet= facet;
+ FOREACHneighbor_(facet) {
+ if (!neighbor->newfacet && isnewfacets)
+ continue;
+ if (neighbor->visitid == visitid)
+ continue;
+ neighbor->visitid= visitid;
+ if (!neighbor->flipped) { /* code duplicated above */
+ (*numpart)++;
+ qh_distplane(point, neighbor, dist);
+ if (*dist > bestdist) {
+ if (!bestoutside && *dist >= qh MINoutside
+ && (!neighbor->upperdelaunay || !noupper)) {
+ bestfacet= neighbor;
+ goto LABELreturn_best;
+ }
+ if (!neighbor->upperdelaunay) {
+ bestfacet= neighbor;
+ bestdist= *dist;
+ break; /* switch to neighbor */
+ }else if (!bestfacet) {
+ bestdist= *dist;
+ break; /* switch to neighbor */
+ }
+ } /* end of *dist>bestdist */
+ } /* end of !flipped */
+ } /* end of FOREACHneighbor */
+ facet= neighbor; /* non-NULL only if *dist>bestdist */
+ } /* end of while facet (directed search) */
+ if (isnewfacets) {
+ if (!bestfacet) {
+ bestdist= -REALmax/2;
+ bestfacet= qh_findbestnew(point, startfacet->next, &bestdist, bestoutside, isoutside, &numpartnew);
+ testhorizon= False; /* qh_findbestnew calls qh_findbesthorizon */
+ }else if (!qh findbest_notsharp && bestdist < - qh DISTround) {
+ if (qh_sharpnewfacets()) {
+ /* seldom used, qh_findbestnew will retest all facets */
+ zinc_(Zfindnewsharp);
+ bestfacet= qh_findbestnew(point, bestfacet, &bestdist, bestoutside, isoutside, &numpartnew);
+ testhorizon= False; /* qh_findbestnew calls qh_findbesthorizon */
+ qh findbestnew= True;
+ }else
+ qh findbest_notsharp= True;
+ }
+ }
+ if (!bestfacet)
+ bestfacet= qh_findbestlower(lastfacet, point, &bestdist, numpart);
+ if (testhorizon)
+ bestfacet= qh_findbesthorizon(!qh_IScheckmax, point, bestfacet, noupper, &bestdist, &numpartnew);
+ *dist= bestdist;
+ if (isoutside && bestdist < qh MINoutside)
+ *isoutside= False;
+LABELreturn_best:
+ zadd_(Zfindbesttot, *numpart);
+ zmax_(Zfindbestmax, *numpart);
+ (*numpart) += numpartnew;
+ qh IStracing= oldtrace;
+ return bestfacet;
+} /* findbest */
+
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="findbesthorizon">-</a>
+
+ qh_findbesthorizon( qh_IScheckmax, point, startfacet, qh_NOupper, &bestdist, &numpart )
+ search coplanar and better horizon facets from startfacet/bestdist
+ ischeckmax turns off statistics and minsearch update
+ all arguments must be initialized
+ returns(ischeckmax):
+ best facet
+ returns(!ischeckmax):
+ best facet that is not upperdelaunay
+ allows upperdelaunay that is clearly outside
+ returns:
+ bestdist is distance to bestfacet
+ numpart -- updates number of distance tests
+
+ notes:
+ no early out -- use qh_findbest() or qh_findbestnew()
+ Searches coplanar or better horizon facets
+
+ when called by qh_check_maxout() (qh_IScheckmax)
+ startfacet must be closest to the point
+ Otherwise, if point is beyond and below startfacet, startfacet may be a local minimum
+ even though other facets are below the point.
+ updates facet->maxoutside for good, visited facets
+ may return NULL
+
+ searchdist is qh.max_outside + 2 * DISTround
+ + max( MINvisible('Vn'), MAXcoplanar('Un'));
+ This setting is a guess. It must be at least max_outside + 2*DISTround
+ because a facet may have a geometric neighbor across a vertex
+
+ design:
+ for each horizon facet of coplanar best facets
+ continue if clearly inside
+ unless upperdelaunay or clearly outside
+ update best facet
+*/
+facetT *qh_findbesthorizon(boolT ischeckmax, pointT* point, facetT *startfacet, boolT noupper, realT *bestdist, int *numpart) {
+ facetT *bestfacet= startfacet;
+ realT dist;
+ facetT *neighbor, **neighborp, *facet;
+ facetT *nextfacet= NULL; /* optimize last facet of coplanarfacetset */
+ int numpartinit= *numpart, coplanarfacetset_size;
+ unsigned int visitid= ++qh visit_id;
+ boolT newbest= False; /* for tracing */
+ realT minsearch, searchdist; /* skip facets that are too far from point */
+
+ if (!ischeckmax) {
+ zinc_(Zfindhorizon);
+ }else {
+#if qh_MAXoutside
+ if ((!qh ONLYgood || startfacet->good) && *bestdist > startfacet->maxoutside)
+ startfacet->maxoutside= *bestdist;
+#endif
+ }
+ searchdist= qh_SEARCHdist; /* multiple of qh.max_outside and precision constants */
+ minsearch= *bestdist - searchdist;
+ if (ischeckmax) {
+ /* Always check coplanar facets. Needed for RBOX 1000 s Z1 G1e-13 t996564279 | QHULL Tv */
+ minimize_(minsearch, -searchdist);
+ }
+ coplanarfacetset_size= 0;
+ facet= startfacet;
+ while (True) {
+ trace4((qh ferr, 4002, "qh_findbesthorizon: neighbors of f%d bestdist %2.2g f%d ischeckmax? %d noupper? %d minsearch %2.2g searchdist %2.2g\n",
+ facet->id, *bestdist, getid_(bestfacet), ischeckmax, noupper,
+ minsearch, searchdist));
+ FOREACHneighbor_(facet) {
+ if (neighbor->visitid == visitid)
+ continue;
+ neighbor->visitid= visitid;
+ if (!neighbor->flipped) {
+ qh_distplane(point, neighbor, &dist);
+ (*numpart)++;
+ if (dist > *bestdist) {
+ if (!neighbor->upperdelaunay || ischeckmax || (!noupper && dist >= qh MINoutside)) {
+ bestfacet= neighbor;
+ *bestdist= dist;
+ newbest= True;
+ if (!ischeckmax) {
+ minsearch= dist - searchdist;
+ if (dist > *bestdist + searchdist) {
+ zinc_(Zfindjump); /* everything in qh.coplanarfacetset at least searchdist below */
+ coplanarfacetset_size= 0;
+ }
+ }
+ }
+ }else if (dist < minsearch)
+ continue; /* if ischeckmax, dist can't be positive */
+#if qh_MAXoutside
+ if (ischeckmax && dist > neighbor->maxoutside)
+ neighbor->maxoutside= dist;
+#endif
+ } /* end of !flipped */
+ if (nextfacet) {
+ if (!coplanarfacetset_size++) {
+ SETfirst_(qh coplanarfacetset)= nextfacet;
+ SETtruncate_(qh coplanarfacetset, 1);
+ }else
+ qh_setappend(&qh coplanarfacetset, nextfacet); /* Was needed for RBOX 1000 s W1e-13 P0 t996547055 | QHULL d Qbb Qc Tv
+ and RBOX 1000 s Z1 G1e-13 t996564279 | qhull Tv */
+ }
+ nextfacet= neighbor;
+ } /* end of EACHneighbor */
+ facet= nextfacet;
+ if (facet)
+ nextfacet= NULL;
+ else if (!coplanarfacetset_size)
+ break;
+ else if (!--coplanarfacetset_size) {
+ facet= SETfirstt_(qh coplanarfacetset, facetT);
+ SETtruncate_(qh coplanarfacetset, 0);
+ }else
+ facet= (facetT*)qh_setdellast(qh coplanarfacetset);
+ } /* while True, for each facet in qh.coplanarfacetset */
+ if (!ischeckmax) {
+ zadd_(Zfindhorizontot, *numpart - numpartinit);
+ zmax_(Zfindhorizonmax, *numpart - numpartinit);
+ if (newbest)
+ zinc_(Zparthorizon);
+ }
+ trace4((qh ferr, 4003, "qh_findbesthorizon: newbest? %d bestfacet f%d bestdist %2.2g\n", newbest, getid_(bestfacet), *bestdist));
+ return bestfacet;
+} /* findbesthorizon */
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="findbestnew">-</a>
+
+ qh_findbestnew( point, startfacet, dist, isoutside, numpart )
+ find best newfacet for point
+ searches all of qh.newfacet_list starting at startfacet
+ searches horizon facets of coplanar best newfacets
+ searches all facets if startfacet == qh.facet_list
+ returns:
+ best new or horizon facet that is not upperdelaunay
+ early out if isoutside and not 'Qf'
+ dist is distance to facet
+ isoutside is true if point is outside of facet
+ numpart is number of distance tests
+
+ notes:
+ Always used for merged new facets (see qh_USEfindbestnew)
+ Avoids upperdelaunay facet unless (isoutside and outside)
+
+ Uses qh.visit_id, qh.coplanarfacetset.
+ If share visit_id with qh_findbest, coplanarfacetset is incorrect.
+
+ If merging (testhorizon), searches horizon facets of coplanar best facets because
+ a point maybe coplanar to the bestfacet, below its horizon facet,
+ and above a horizon facet of a coplanar newfacet. For example,
+ rbox 1000 s Z1 G1e-13 | qhull
+ rbox 1000 s W1e-13 P0 t992110337 | QHULL d Qbb Qc
+
+ qh_findbestnew() used if
+ qh_sharpnewfacets -- newfacets contains a sharp angle
+ if many merges, qh_premerge found a merge, or 'Qf' (qh.findbestnew)
+
+ see also:
+ qh_partitionall() and qh_findbest()
+
+ design:
+ for each new facet starting from startfacet
+ test distance from point to facet
+ return facet if clearly outside
+ unless upperdelaunay and a lowerdelaunay exists
+ update best facet
+ test horizon facets
+*/
+facetT *qh_findbestnew(pointT *point, facetT *startfacet,
+ realT *dist, boolT bestoutside, boolT *isoutside, int *numpart) {
+ realT bestdist= -REALmax/2;
+ facetT *bestfacet= NULL, *facet;
+ int oldtrace= qh IStracing, i;
+ unsigned int visitid= ++qh visit_id;
+ realT distoutside= 0.0;
+ boolT isdistoutside; /* True if distoutside is defined */
+ boolT testhorizon = True; /* needed if precise, e.g., rbox c D6 | qhull Q0 Tv */
+
+ if (!startfacet) {
+ if (qh MERGING)
+ qh_fprintf(qh ferr, 6001, "qhull precision error (qh_findbestnew): merging has formed and deleted a cone of new facets. Can not continue.\n");
+ else
+ qh_fprintf(qh ferr, 6002, "qhull internal error (qh_findbestnew): no new facets for point p%d\n",
+ qh furthest_id);
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ zinc_(Zfindnew);
+ if (qh BESToutside || bestoutside)
+ isdistoutside= False;
+ else {
+ isdistoutside= True;
+ distoutside= qh_DISToutside; /* multiple of qh.MINoutside & qh.max_outside, see user.h */
+ }
+ if (isoutside)
+ *isoutside= True;
+ *numpart= 0;
+ if (qh IStracing >= 3 || (qh TRACElevel && qh TRACEpoint >= 0 && qh TRACEpoint == qh_pointid(point))) {
+ if (qh TRACElevel > qh IStracing)
+ qh IStracing= qh TRACElevel;
+ qh_fprintf(qh ferr, 8008, "qh_findbestnew: point p%d facet f%d. Stop? %d if dist > %2.2g\n",
+ qh_pointid(point), startfacet->id, isdistoutside, distoutside);
+ qh_fprintf(qh ferr, 8009, " Last point added p%d visitid %d.", qh furthest_id, visitid);
+ qh_fprintf(qh ferr, 8010, " Last merge was #%d.\n", zzval_(Ztotmerge));
+ }
+ /* visit all new facets starting with startfacet, maybe qh facet_list */
+ for (i=0, facet=startfacet; i < 2; i++, facet= qh newfacet_list) {
+ FORALLfacet_(facet) {
+ if (facet == startfacet && i)
+ break;
+ facet->visitid= visitid;
+ if (!facet->flipped) {
+ qh_distplane(point, facet, dist);
+ (*numpart)++;
+ if (*dist > bestdist) {
+ if (!facet->upperdelaunay || *dist >= qh MINoutside) {
+ bestfacet= facet;
+ if (isdistoutside && *dist >= distoutside)
+ goto LABELreturn_bestnew;
+ bestdist= *dist;
+ }
+ }
+ } /* end of !flipped */
+ } /* FORALLfacet from startfacet or qh newfacet_list */
+ }
+ if (testhorizon || !bestfacet) /* testhorizon is always True. Keep the same code as qh_findbest */
+ bestfacet= qh_findbesthorizon(!qh_IScheckmax, point, bestfacet ? bestfacet : startfacet,
+ !qh_NOupper, &bestdist, numpart);
+ *dist= bestdist;
+ if (isoutside && *dist < qh MINoutside)
+ *isoutside= False;
+LABELreturn_bestnew:
+ zadd_(Zfindnewtot, *numpart);
+ zmax_(Zfindnewmax, *numpart);
+ trace4((qh ferr, 4004, "qh_findbestnew: bestfacet f%d bestdist %2.2g\n", getid_(bestfacet), *dist));
+ qh IStracing= oldtrace;
+ return bestfacet;
+} /* findbestnew */
+
+/* ============ hyperplane functions -- keep code together [?] ============ */
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="backnormal">-</a>
+
+ qh_backnormal( rows, numrow, numcol, sign, normal, nearzero )
+ given an upper-triangular rows array and a sign,
+ solve for normal equation x using back substitution over rows U
+
+ returns:
+ normal= x
+
+ if will not be able to divzero() when normalized(qh.MINdenom_2 and qh.MINdenom_1_2),
+ if fails on last row
+ this means that the hyperplane intersects [0,..,1]
+ sets last coordinate of normal to sign
+ otherwise
+ sets tail of normal to [...,sign,0,...], i.e., solves for b= [0...0]
+ sets nearzero
+
+ notes:
+ assumes numrow == numcol-1
+
+ see Golub & van Loan, 1983, Eq. 4.4-9 for "Gaussian elimination with complete pivoting"
+
+ solves Ux=b where Ax=b and PA=LU
+ b= [0,...,0,sign or 0] (sign is either -1 or +1)
+ last row of A= [0,...,0,1]
+
+ 1) Ly=Pb == y=b since P only permutes the 0's of b
+
+ design:
+ for each row from end
+ perform back substitution
+ if near zero
+ use qh_divzero for division
+ if zero divide and not last row
+ set tail of normal to 0
+*/
+void qh_backnormal(realT **rows, int numrow, int numcol, boolT sign,
+ coordT *normal, boolT *nearzero) {
+ int i, j;
+ coordT *normalp, *normal_tail, *ai, *ak;
+ realT diagonal;
+ boolT waszero;
+ int zerocol= -1;
+
+ normalp= normal + numcol - 1;
+ *normalp--= (sign ? -1.0 : 1.0);
+ for (i=numrow; i--; ) {
+ *normalp= 0.0;
+ ai= rows[i] + i + 1;
+ ak= normalp+1;
+ for (j=i+1; j < numcol; j++)
+ *normalp -= *ai++ * *ak++;
+ diagonal= (rows[i])[i];
+ if (fabs_(diagonal) > qh MINdenom_2)
+ *(normalp--) /= diagonal;
+ else {
+ waszero= False;
+ *normalp= qh_divzero(*normalp, diagonal, qh MINdenom_1_2, &waszero);
+ if (waszero) {
+ zerocol= i;
+ *(normalp--)= (sign ? -1.0 : 1.0);
+ for (normal_tail= normalp+2; normal_tail < normal + numcol; normal_tail++)
+ *normal_tail= 0.0;
+ }else
+ normalp--;
+ }
+ }
+ if (zerocol != -1) {
+ zzinc_(Zback0);
+ *nearzero= True;
+ trace4((qh ferr, 4005, "qh_backnormal: zero diagonal at column %d.\n", i));
+ qh_precision("zero diagonal on back substitution");
+ }
+} /* backnormal */
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="gausselim">-</a>
+
+ qh_gausselim( rows, numrow, numcol, sign )
+ Gaussian elimination with partial pivoting
+
+ returns:
+ rows is upper triangular (includes row exchanges)
+ flips sign for each row exchange
+ sets nearzero if pivot[k] < qh.NEARzero[k], else clears it
+
+ notes:
+ if nearzero, the determinant's sign may be incorrect.
+ assumes numrow <= numcol
+
+ design:
+ for each row
+ determine pivot and exchange rows if necessary
+ test for near zero
+ perform gaussian elimination step
+*/
+void qh_gausselim(realT **rows, int numrow, int numcol, boolT *sign, boolT *nearzero) {
+ realT *ai, *ak, *rowp, *pivotrow;
+ realT n, pivot, pivot_abs= 0.0, temp;
+ int i, j, k, pivoti, flip=0;
+
+ *nearzero= False;
+ for (k=0; k < numrow; k++) {
+ pivot_abs= fabs_((rows[k])[k]);
+ pivoti= k;
+ for (i=k+1; i < numrow; i++) {
+ if ((temp= fabs_((rows[i])[k])) > pivot_abs) {
+ pivot_abs= temp;
+ pivoti= i;
+ }
+ }
+ if (pivoti != k) {
+ rowp= rows[pivoti];
+ rows[pivoti]= rows[k];
+ rows[k]= rowp;
+ *sign ^= 1;
+ flip ^= 1;
+ }
+ if (pivot_abs <= qh NEARzero[k]) {
+ *nearzero= True;
+ if (pivot_abs == 0.0) { /* remainder of column == 0 */
+ if (qh IStracing >= 4) {
+ qh_fprintf(qh ferr, 8011, "qh_gausselim: 0 pivot at column %d. (%2.2g < %2.2g)\n", k, pivot_abs, qh DISTround);
+ qh_printmatrix(qh ferr, "Matrix:", rows, numrow, numcol);
+ }
+ zzinc_(Zgauss0);
+ qh_precision("zero pivot for Gaussian elimination");
+ goto LABELnextcol;
+ }
+ }
+ pivotrow= rows[k] + k;
+ pivot= *pivotrow++; /* signed value of pivot, and remainder of row */
+ for (i=k+1; i < numrow; i++) {
+ ai= rows[i] + k;
+ ak= pivotrow;
+ n= (*ai++)/pivot; /* divzero() not needed since |pivot| >= |*ai| */
+ for (j= numcol - (k+1); j--; )
+ *ai++ -= n * *ak++;
+ }
+ LABELnextcol:
+ ;
+ }
+ wmin_(Wmindenom, pivot_abs); /* last pivot element */
+ if (qh IStracing >= 5)
+ qh_printmatrix(qh ferr, "qh_gausselem: result", rows, numrow, numcol);
+} /* gausselim */
+
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="getangle">-</a>
+
+ qh_getangle( vect1, vect2 )
+ returns the dot product of two vectors
+ if qh.RANDOMdist, joggles result
+
+ notes:
+ the angle may be > 1.0 or < -1.0 because of roundoff errors
+
+*/
+realT qh_getangle(pointT *vect1, pointT *vect2) {
+ realT angle= 0, randr;
+ int k;
+
+ for (k=qh hull_dim; k--; )
+ angle += *vect1++ * *vect2++;
+ if (qh RANDOMdist) {
+ randr= qh_RANDOMint;
+ angle += (2.0 * randr / qh_RANDOMmax - 1.0) *
+ qh RANDOMfactor;
+ }
+ trace4((qh ferr, 4006, "qh_getangle: %2.2g\n", angle));
+ return(angle);
+} /* getangle */
+
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="getcenter">-</a>
+
+ qh_getcenter( vertices )
+ returns arithmetic center of a set of vertices as a new point
+
+ notes:
+ allocates point array for center
+*/
+pointT *qh_getcenter(setT *vertices) {
+ int k;
+ pointT *center, *coord;
+ vertexT *vertex, **vertexp;
+ int count= qh_setsize(vertices);
+
+ if (count < 2) {
+ qh_fprintf(qh ferr, 6003, "qhull internal error (qh_getcenter): not defined for %d points\n", count);
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ center= (pointT *)qh_memalloc(qh normal_size);
+ for (k=0; k < qh hull_dim; k++) {
+ coord= center+k;
+ *coord= 0.0;
+ FOREACHvertex_(vertices)
+ *coord += vertex->point[k];
+ *coord /= count; /* count>=2 by QH6003 */
+ }
+ return(center);
+} /* getcenter */
+
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="getcentrum">-</a>
+
+ qh_getcentrum( facet )
+ returns the centrum for a facet as a new point
+
+ notes:
+ allocates the centrum
+*/
+pointT *qh_getcentrum(facetT *facet) {
+ realT dist;
+ pointT *centrum, *point;
+
+ point= qh_getcenter(facet->vertices);
+ zzinc_(Zcentrumtests);
+ qh_distplane(point, facet, &dist);
+ centrum= qh_projectpoint(point, facet, dist);
+ qh_memfree(point, qh normal_size);
+ trace4((qh ferr, 4007, "qh_getcentrum: for f%d, %d vertices dist= %2.2g\n",
+ facet->id, qh_setsize(facet->vertices), dist));
+ return centrum;
+} /* getcentrum */
+
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="getdistance">-</a>
+
+ qh_getdistance( facet, neighbor, mindist, maxdist )
+ returns the maxdist and mindist distance of any vertex from neighbor
+
+ returns:
+ the max absolute value
+
+ design:
+ for each vertex of facet that is not in neighbor
+ test the distance from vertex to neighbor
+*/
+realT qh_getdistance(facetT *facet, facetT *neighbor, realT *mindist, realT *maxdist) {
+ vertexT *vertex, **vertexp;
+ realT dist, maxd, mind;
+
+ FOREACHvertex_(facet->vertices)
+ vertex->seen= False;
+ FOREACHvertex_(neighbor->vertices)
+ vertex->seen= True;
+ mind= 0.0;
+ maxd= 0.0;
+ FOREACHvertex_(facet->vertices) {
+ if (!vertex->seen) {
+ zzinc_(Zbestdist);
+ qh_distplane(vertex->point, neighbor, &dist);
+ if (dist < mind)
+ mind= dist;
+ else if (dist > maxd)
+ maxd= dist;
+ }
+ }
+ *mindist= mind;
+ *maxdist= maxd;
+ mind= -mind;
+ if (maxd > mind)
+ return maxd;
+ else
+ return mind;
+} /* getdistance */
+
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="normalize">-</a>
+
+ qh_normalize( normal, dim, toporient )
+ normalize a vector and report if too small
+ does not use min norm
+
+ see:
+ qh_normalize2
+*/
+void qh_normalize(coordT *normal, int dim, boolT toporient) {
+ qh_normalize2( normal, dim, toporient, NULL, NULL);
+} /* normalize */
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="normalize2">-</a>
+
+ qh_normalize2( normal, dim, toporient, minnorm, ismin )
+ normalize a vector and report if too small
+ qh.MINdenom/MINdenom1 are the upper limits for divide overflow
+
+ returns:
+ normalized vector
+ flips sign if !toporient
+ if minnorm non-NULL,
+ sets ismin if normal < minnorm
+
+ notes:
+ if zero norm
+ sets all elements to sqrt(1.0/dim)
+ if divide by zero (divzero())
+ sets largest element to +/-1
+ bumps Znearlysingular
+
+ design:
+ computes norm
+ test for minnorm
+ if not near zero
+ normalizes normal
+ else if zero norm
+ sets normal to standard value
+ else
+ uses qh_divzero to normalize
+ if nearzero
+ sets norm to direction of maximum value
+*/
+void qh_normalize2(coordT *normal, int dim, boolT toporient,
+ realT *minnorm, boolT *ismin) {
+ int k;
+ realT *colp, *maxp, norm= 0, temp, *norm1, *norm2, *norm3;
+ boolT zerodiv;
+
+ norm1= normal+1;
+ norm2= normal+2;
+ norm3= normal+3;
+ if (dim == 2)
+ norm= sqrt((*normal)*(*normal) + (*norm1)*(*norm1));
+ else if (dim == 3)
+ norm= sqrt((*normal)*(*normal) + (*norm1)*(*norm1) + (*norm2)*(*norm2));
+ else if (dim == 4) {
+ norm= sqrt((*normal)*(*normal) + (*norm1)*(*norm1) + (*norm2)*(*norm2)
+ + (*norm3)*(*norm3));
+ }else if (dim > 4) {
+ norm= (*normal)*(*normal) + (*norm1)*(*norm1) + (*norm2)*(*norm2)
+ + (*norm3)*(*norm3);
+ for (k=dim-4, colp=normal+4; k--; colp++)
+ norm += (*colp) * (*colp);
+ norm= sqrt(norm);
+ }
+ if (minnorm) {
+ if (norm < *minnorm)
+ *ismin= True;
+ else
+ *ismin= False;
+ }
+ wmin_(Wmindenom, norm);
+ if (norm > qh MINdenom) {
+ if (!toporient)
+ norm= -norm;
+ *normal /= norm;
+ *norm1 /= norm;
+ if (dim == 2)
+ ; /* all done */
+ else if (dim == 3)
+ *norm2 /= norm;
+ else if (dim == 4) {
+ *norm2 /= norm;
+ *norm3 /= norm;
+ }else if (dim >4) {
+ *norm2 /= norm;
+ *norm3 /= norm;
+ for (k=dim-4, colp=normal+4; k--; )
+ *colp++ /= norm;
+ }
+ }else if (norm == 0.0) {
+ temp= sqrt(1.0/dim);
+ for (k=dim, colp=normal; k--; )
+ *colp++ = temp;
+ }else {
+ if (!toporient)
+ norm= -norm;
+ for (k=dim, colp=normal; k--; colp++) { /* k used below */
+ temp= qh_divzero(*colp, norm, qh MINdenom_1, &zerodiv);
+ if (!zerodiv)
+ *colp= temp;
+ else {
+ maxp= qh_maxabsval(normal, dim);
+ temp= ((*maxp * norm >= 0.0) ? 1.0 : -1.0);
+ for (k=dim, colp=normal; k--; colp++)
+ *colp= 0.0;
+ *maxp= temp;
+ zzinc_(Znearlysingular);
+ trace0((qh ferr, 1, "qh_normalize: norm=%2.2g too small during p%d\n",
+ norm, qh furthest_id));
+ return;
+ }
+ }
+ }
+} /* normalize */
+
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="projectpoint">-</a>
+
+ qh_projectpoint( point, facet, dist )
+ project point onto a facet by dist
+
+ returns:
+ returns a new point
+
+ notes:
+ if dist= distplane(point,facet)
+ this projects point to hyperplane
+ assumes qh_memfree_() is valid for normal_size
+*/
+pointT *qh_projectpoint(pointT *point, facetT *facet, realT dist) {
+ pointT *newpoint, *np, *normal;
+ int normsize= qh normal_size;
+ int k;
+ void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */
+
+ qh_memalloc_(normsize, freelistp, newpoint, pointT);
+ np= newpoint;
+ normal= facet->normal;
+ for (k=qh hull_dim; k--; )
+ *(np++)= *point++ - dist * *normal++;
+ return(newpoint);
+} /* projectpoint */
+
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="setfacetplane">-</a>
+
+ qh_setfacetplane( facet )
+ sets the hyperplane for a facet
+ if qh.RANDOMdist, joggles hyperplane
+
+ notes:
+ uses global buffers qh.gm_matrix and qh.gm_row
+ overwrites facet->normal if already defined
+ updates Wnewvertex if PRINTstatistics
+ sets facet->upperdelaunay if upper envelope of Delaunay triangulation
+
+ design:
+ copy vertex coordinates to qh.gm_matrix/gm_row
+ compute determinate
+ if nearzero
+ recompute determinate with gaussian elimination
+ if nearzero
+ force outside orientation by testing interior point
+*/
+void qh_setfacetplane(facetT *facet) {
+ pointT *point;
+ vertexT *vertex, **vertexp;
+ int normsize= qh normal_size;
+ int k,i, oldtrace= 0;
+ realT dist;
+ void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */
+ coordT *coord, *gmcoord;
+ pointT *point0= SETfirstt_(facet->vertices, vertexT)->point;
+ boolT nearzero= False;
+
+ zzinc_(Zsetplane);
+ if (!facet->normal)
+ qh_memalloc_(normsize, freelistp, facet->normal, coordT);
+ if (facet == qh tracefacet) {
+ oldtrace= qh IStracing;
+ qh IStracing= 5;
+ qh_fprintf(qh ferr, 8012, "qh_setfacetplane: facet f%d created.\n", facet->id);
+ qh_fprintf(qh ferr, 8013, " Last point added to hull was p%d.", qh furthest_id);
+ if (zzval_(Ztotmerge))
+ qh_fprintf(qh ferr, 8014, " Last merge was #%d.", zzval_(Ztotmerge));
+ qh_fprintf(qh ferr, 8015, "\n\nCurrent summary is:\n");
+ qh_printsummary(qh ferr);
+ }
+ if (qh hull_dim <= 4) {
+ i= 0;
+ if (qh RANDOMdist) {
+ gmcoord= qh gm_matrix;
+ FOREACHvertex_(facet->vertices) {
+ qh gm_row[i++]= gmcoord;
+ coord= vertex->point;
+ for (k=qh hull_dim; k--; )
+ *(gmcoord++)= *coord++ * qh_randomfactor(qh RANDOMa, qh RANDOMb);
+ }
+ }else {
+ FOREACHvertex_(facet->vertices)
+ qh gm_row[i++]= vertex->point;
+ }
+ qh_sethyperplane_det(qh hull_dim, qh gm_row, point0, facet->toporient,
+ facet->normal, &facet->offset, &nearzero);
+ }
+ if (qh hull_dim > 4 || nearzero) {
+ i= 0;
+ gmcoord= qh gm_matrix;
+ FOREACHvertex_(facet->vertices) {
+ if (vertex->point != point0) {
+ qh gm_row[i++]= gmcoord;
+ coord= vertex->point;
+ point= point0;
+ for (k=qh hull_dim; k--; )
+ *(gmcoord++)= *coord++ - *point++;
+ }
+ }
+ qh gm_row[i]= gmcoord; /* for areasimplex */
+ if (qh RANDOMdist) {
+ gmcoord= qh gm_matrix;
+ for (i=qh hull_dim-1; i--; ) {
+ for (k=qh hull_dim; k--; )
+ *(gmcoord++) *= qh_randomfactor(qh RANDOMa, qh RANDOMb);
+ }
+ }
+ qh_sethyperplane_gauss(qh hull_dim, qh gm_row, point0, facet->toporient,
+ facet->normal, &facet->offset, &nearzero);
+ if (nearzero) {
+ if (qh_orientoutside(facet)) {
+ trace0((qh ferr, 2, "qh_setfacetplane: flipped orientation after testing interior_point during p%d\n", qh furthest_id));
+ /* this is part of using Gaussian Elimination. For example in 5-d
+ 1 1 1 1 0
+ 1 1 1 1 1
+ 0 0 0 1 0
+ 0 1 0 0 0
+ 1 0 0 0 0
+ norm= 0.38 0.38 -0.76 0.38 0
+ has a determinate of 1, but g.e. after subtracting pt. 0 has
+ 0's in the diagonal, even with full pivoting. It does work
+ if you subtract pt. 4 instead. */
+ }
+ }
+ }
+ facet->upperdelaunay= False;
+ if (qh DELAUNAY) {
+ if (qh UPPERdelaunay) { /* matches qh_triangulate_facet and qh.lower_threshold in qh_initbuild */
+ if (facet->normal[qh hull_dim -1] >= qh ANGLEround * qh_ZEROdelaunay)
+ facet->upperdelaunay= True;
+ }else {
+ if (facet->normal[qh hull_dim -1] > -qh ANGLEround * qh_ZEROdelaunay)
+ facet->upperdelaunay= True;
+ }
+ }
+ if (qh PRINTstatistics || qh IStracing || qh TRACElevel || qh JOGGLEmax < REALmax) {
+ qh old_randomdist= qh RANDOMdist;
+ qh RANDOMdist= False;
+ FOREACHvertex_(facet->vertices) {
+ if (vertex->point != point0) {
+ boolT istrace= False;
+ zinc_(Zdiststat);
+ qh_distplane(vertex->point, facet, &dist);
+ dist= fabs_(dist);
+ zinc_(Znewvertex);
+ wadd_(Wnewvertex, dist);
+ if (dist > wwval_(Wnewvertexmax)) {
+ wwval_(Wnewvertexmax)= dist;
+ if (dist > qh max_outside) {
+ qh max_outside= dist; /* used by qh_maxouter() */
+ if (dist > qh TRACEdist)
+ istrace= True;
+ }
+ }else if (-dist > qh TRACEdist)
+ istrace= True;
+ if (istrace) {
+ qh_fprintf(qh ferr, 8016, "qh_setfacetplane: ====== vertex p%d(v%d) increases max_outside to %2.2g for new facet f%d last p%d\n",
+ qh_pointid(vertex->point), vertex->id, dist, facet->id, qh furthest_id);
+ qh_errprint("DISTANT", facet, NULL, NULL, NULL);
+ }
+ }
+ }
+ qh RANDOMdist= qh old_randomdist;
+ }
+ if (qh IStracing >= 3) {
+ qh_fprintf(qh ferr, 8017, "qh_setfacetplane: f%d offset %2.2g normal: ",
+ facet->id, facet->offset);
+ for (k=0; k < qh hull_dim; k++)
+ qh_fprintf(qh ferr, 8018, "%2.2g ", facet->normal[k]);
+ qh_fprintf(qh ferr, 8019, "\n");
+ }
+ if (facet == qh tracefacet)
+ qh IStracing= oldtrace;
+} /* setfacetplane */
+
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="sethyperplane_det">-</a>
+
+ qh_sethyperplane_det( dim, rows, point0, toporient, normal, offset, nearzero )
+ given dim X dim array indexed by rows[], one row per point,
+ toporient(flips all signs),
+ and point0 (any row)
+ set normalized hyperplane equation from oriented simplex
+
+ returns:
+ normal (normalized)
+ offset (places point0 on the hyperplane)
+ sets nearzero if hyperplane not through points
+
+ notes:
+ only defined for dim == 2..4
+ rows[] is not modified
+ solves det(P-V_0, V_n-V_0, ..., V_1-V_0)=0, i.e. every point is on hyperplane
+ see Bower & Woodworth, A programmer's geometry, Butterworths 1983.
+
+ derivation of 3-d minnorm
+ Goal: all vertices V_i within qh.one_merge of hyperplane
+ Plan: exactly translate the facet so that V_0 is the origin
+ exactly rotate the facet so that V_1 is on the x-axis and y_2=0.
+ exactly rotate the effective perturbation to only effect n_0
+ this introduces a factor of sqrt(3)
+ n_0 = ((y_2-y_0)*(z_1-z_0) - (z_2-z_0)*(y_1-y_0)) / norm
+ Let M_d be the max coordinate difference
+ Let M_a be the greater of M_d and the max abs. coordinate
+ Let u be machine roundoff and distround be max error for distance computation
+ The max error for n_0 is sqrt(3) u M_a M_d / norm. n_1 is approx. 1 and n_2 is approx. 0
+ The max error for distance of V_1 is sqrt(3) u M_a M_d M_d / norm. Offset=0 at origin
+ Then minnorm = 1.8 u M_a M_d M_d / qh.ONEmerge
+ Note that qh.one_merge is approx. 45.5 u M_a and norm is usually about M_d M_d
+
+ derivation of 4-d minnorm
+ same as above except rotate the facet so that V_1 on x-axis and w_2, y_3, w_3=0
+ [if two vertices fixed on x-axis, can rotate the other two in yzw.]
+ n_0 = det3_(...) = y_2 det2_(z_1, w_1, z_3, w_3) = - y_2 w_1 z_3
+ [all other terms contain at least two factors nearly zero.]
+ The max error for n_0 is sqrt(4) u M_a M_d M_d / norm
+ Then minnorm = 2 u M_a M_d M_d M_d / qh.ONEmerge
+ Note that qh.one_merge is approx. 82 u M_a and norm is usually about M_d M_d M_d
+*/
+void qh_sethyperplane_det(int dim, coordT **rows, coordT *point0,
+ boolT toporient, coordT *normal, realT *offset, boolT *nearzero) {
+ realT maxround, dist;
+ int i;
+ pointT *point;
+
+
+ if (dim == 2) {
+ normal[0]= dY(1,0);
+ normal[1]= dX(0,1);
+ qh_normalize2(normal, dim, toporient, NULL, NULL);
+ *offset= -(point0[0]*normal[0]+point0[1]*normal[1]);
+ *nearzero= False; /* since nearzero norm => incident points */
+ }else if (dim == 3) {
+ normal[0]= det2_(dY(2,0), dZ(2,0),
+ dY(1,0), dZ(1,0));
+ normal[1]= det2_(dX(1,0), dZ(1,0),
+ dX(2,0), dZ(2,0));
+ normal[2]= det2_(dX(2,0), dY(2,0),
+ dX(1,0), dY(1,0));
+ qh_normalize2(normal, dim, toporient, NULL, NULL);
+ *offset= -(point0[0]*normal[0] + point0[1]*normal[1]
+ + point0[2]*normal[2]);
+ maxround= qh DISTround;
+ for (i=dim; i--; ) {
+ point= rows[i];
+ if (point != point0) {
+ dist= *offset + (point[0]*normal[0] + point[1]*normal[1]
+ + point[2]*normal[2]);
+ if (dist > maxround || dist < -maxround) {
+ *nearzero= True;
+ break;
+ }
+ }
+ }
+ }else if (dim == 4) {
+ normal[0]= - det3_(dY(2,0), dZ(2,0), dW(2,0),
+ dY(1,0), dZ(1,0), dW(1,0),
+ dY(3,0), dZ(3,0), dW(3,0));
+ normal[1]= det3_(dX(2,0), dZ(2,0), dW(2,0),
+ dX(1,0), dZ(1,0), dW(1,0),
+ dX(3,0), dZ(3,0), dW(3,0));
+ normal[2]= - det3_(dX(2,0), dY(2,0), dW(2,0),
+ dX(1,0), dY(1,0), dW(1,0),
+ dX(3,0), dY(3,0), dW(3,0));
+ normal[3]= det3_(dX(2,0), dY(2,0), dZ(2,0),
+ dX(1,0), dY(1,0), dZ(1,0),
+ dX(3,0), dY(3,0), dZ(3,0));
+ qh_normalize2(normal, dim, toporient, NULL, NULL);
+ *offset= -(point0[0]*normal[0] + point0[1]*normal[1]
+ + point0[2]*normal[2] + point0[3]*normal[3]);
+ maxround= qh DISTround;
+ for (i=dim; i--; ) {
+ point= rows[i];
+ if (point != point0) {
+ dist= *offset + (point[0]*normal[0] + point[1]*normal[1]
+ + point[2]*normal[2] + point[3]*normal[3]);
+ if (dist > maxround || dist < -maxround) {
+ *nearzero= True;
+ break;
+ }
+ }
+ }
+ }
+ if (*nearzero) {
+ zzinc_(Zminnorm);
+ trace0((qh ferr, 3, "qh_sethyperplane_det: degenerate norm during p%d.\n", qh furthest_id));
+ zzinc_(Znearlysingular);
+ }
+} /* sethyperplane_det */
+
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="sethyperplane_gauss">-</a>
+
+ qh_sethyperplane_gauss( dim, rows, point0, toporient, normal, offset, nearzero )
+ given(dim-1) X dim array of rows[i]= V_{i+1} - V_0 (point0)
+ set normalized hyperplane equation from oriented simplex
+
+ returns:
+ normal (normalized)
+ offset (places point0 on the hyperplane)
+
+ notes:
+ if nearzero
+ orientation may be incorrect because of incorrect sign flips in gausselim
+ solves [V_n-V_0,...,V_1-V_0, 0 .. 0 1] * N == [0 .. 0 1]
+ or [V_n-V_0,...,V_1-V_0, 0 .. 0 1] * N == [0]
+ i.e., N is normal to the hyperplane, and the unnormalized
+ distance to [0 .. 1] is either 1 or 0
+
+ design:
+ perform gaussian elimination
+ flip sign for negative values
+ perform back substitution
+ normalize result
+ compute offset
+*/
+void qh_sethyperplane_gauss(int dim, coordT **rows, pointT *point0,
+ boolT toporient, coordT *normal, coordT *offset, boolT *nearzero) {
+ coordT *pointcoord, *normalcoef;
+ int k;
+ boolT sign= toporient, nearzero2= False;
+
+ qh_gausselim(rows, dim-1, dim, &sign, nearzero);
+ for (k=dim-1; k--; ) {
+ if ((rows[k])[k] < 0)
+ sign ^= 1;
+ }
+ if (*nearzero) {
+ zzinc_(Znearlysingular);
+ trace0((qh ferr, 4, "qh_sethyperplane_gauss: nearly singular or axis parallel hyperplane during p%d.\n", qh furthest_id));
+ qh_backnormal(rows, dim-1, dim, sign, normal, &nearzero2);
+ }else {
+ qh_backnormal(rows, dim-1, dim, sign, normal, &nearzero2);
+ if (nearzero2) {
+ zzinc_(Znearlysingular);
+ trace0((qh ferr, 5, "qh_sethyperplane_gauss: singular or axis parallel hyperplane at normalization during p%d.\n", qh furthest_id));
+ }
+ }
+ if (nearzero2)
+ *nearzero= True;
+ qh_normalize2(normal, dim, True, NULL, NULL);
+ pointcoord= point0;
+ normalcoef= normal;
+ *offset= -(*pointcoord++ * *normalcoef++);
+ for (k=dim-1; k--; )
+ *offset -= *pointcoord++ * *normalcoef++;
+} /* sethyperplane_gauss */
+
+
+
diff --git a/xs/src/qhull/src/libqhull/geom.h b/xs/src/qhull/src/libqhull/geom.h
new file mode 100644
index 000000000..16ef48d2d
--- /dev/null
+++ b/xs/src/qhull/src/libqhull/geom.h
@@ -0,0 +1,176 @@
+/*<html><pre> -<a href="qh-geom.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ geom.h
+ header file for geometric routines
+
+ see qh-geom.htm and geom.c
+
+ Copyright (c) 1993-2015 The Geometry Center.
+ $Id: //main/2015/qhull/src/libqhull/geom.h#1 $$Change: 1981 $
+ $DateTime: 2015/09/28 20:26:32 $$Author: bbarber $
+*/
+
+#ifndef qhDEFgeom
+#define qhDEFgeom 1
+
+#include "libqhull.h"
+
+/* ============ -macros- ======================== */
+
+/*-<a href="qh-geom.htm#TOC"
+ >--------------------------------</a><a name="fabs_">-</a>
+
+ fabs_(a)
+ returns the absolute value of a
+*/
+#define fabs_( a ) ((( a ) < 0 ) ? -( a ):( a ))
+
+/*-<a href="qh-geom.htm#TOC"
+ >--------------------------------</a><a name="fmax_">-</a>
+
+ fmax_(a,b)
+ returns the maximum value of a and b
+*/
+#define fmax_( a,b ) ( ( a ) < ( b ) ? ( b ) : ( a ) )
+
+/*-<a href="qh-geom.htm#TOC"
+ >--------------------------------</a><a name="fmin_">-</a>
+
+ fmin_(a,b)
+ returns the minimum value of a and b
+*/
+#define fmin_( a,b ) ( ( a ) > ( b ) ? ( b ) : ( a ) )
+
+/*-<a href="qh-geom.htm#TOC"
+ >--------------------------------</a><a name="maximize_">-</a>
+
+ maximize_(maxval, val)
+ set maxval to val if val is greater than maxval
+*/
+#define maximize_( maxval, val ) { if (( maxval ) < ( val )) ( maxval )= ( val ); }
+
+/*-<a href="qh-geom.htm#TOC"
+ >--------------------------------</a><a name="minimize_">-</a>
+
+ minimize_(minval, val)
+ set minval to val if val is less than minval
+*/
+#define minimize_( minval, val ) { if (( minval ) > ( val )) ( minval )= ( val ); }
+
+/*-<a href="qh-geom.htm#TOC"
+ >--------------------------------</a><a name="det2_">-</a>
+
+ det2_(a1, a2,
+ b1, b2)
+
+ compute a 2-d determinate
+*/
+#define det2_( a1,a2,b1,b2 ) (( a1 )*( b2 ) - ( a2 )*( b1 ))
+
+/*-<a href="qh-geom.htm#TOC"
+ >--------------------------------</a><a name="det3_">-</a>
+
+ det3_(a1, a2, a3,
+ b1, b2, b3,
+ c1, c2, c3)
+
+ compute a 3-d determinate
+*/
+#define det3_( a1,a2,a3,b1,b2,b3,c1,c2,c3 ) ( ( a1 )*det2_( b2,b3,c2,c3 ) \
+ - ( b1 )*det2_( a2,a3,c2,c3 ) + ( c1 )*det2_( a2,a3,b2,b3 ) )
+
+/*-<a href="qh-geom.htm#TOC"
+ >--------------------------------</a><a name="dX">-</a>
+
+ dX( p1, p2 )
+ dY( p1, p2 )
+ dZ( p1, p2 )
+
+ given two indices into rows[],
+
+ compute the difference between X, Y, or Z coordinates
+*/
+#define dX( p1,p2 ) ( *( rows[p1] ) - *( rows[p2] ))
+#define dY( p1,p2 ) ( *( rows[p1]+1 ) - *( rows[p2]+1 ))
+#define dZ( p1,p2 ) ( *( rows[p1]+2 ) - *( rows[p2]+2 ))
+#define dW( p1,p2 ) ( *( rows[p1]+3 ) - *( rows[p2]+3 ))
+
+/*============= prototypes in alphabetical order, infrequent at end ======= */
+
+void qh_backnormal(realT **rows, int numrow, int numcol, boolT sign, coordT *normal, boolT *nearzero);
+void qh_distplane(pointT *point, facetT *facet, realT *dist);
+facetT *qh_findbest(pointT *point, facetT *startfacet,
+ boolT bestoutside, boolT isnewfacets, boolT noupper,
+ realT *dist, boolT *isoutside, int *numpart);
+facetT *qh_findbesthorizon(boolT ischeckmax, pointT *point,
+ facetT *startfacet, boolT noupper, realT *bestdist, int *numpart);
+facetT *qh_findbestnew(pointT *point, facetT *startfacet, realT *dist,
+ boolT bestoutside, boolT *isoutside, int *numpart);
+void qh_gausselim(realT **rows, int numrow, int numcol, boolT *sign, boolT *nearzero);
+realT qh_getangle(pointT *vect1, pointT *vect2);
+pointT *qh_getcenter(setT *vertices);
+pointT *qh_getcentrum(facetT *facet);
+realT qh_getdistance(facetT *facet, facetT *neighbor, realT *mindist, realT *maxdist);
+void qh_normalize(coordT *normal, int dim, boolT toporient);
+void qh_normalize2(coordT *normal, int dim, boolT toporient,
+ realT *minnorm, boolT *ismin);
+pointT *qh_projectpoint(pointT *point, facetT *facet, realT dist);
+
+void qh_setfacetplane(facetT *newfacets);
+void qh_sethyperplane_det(int dim, coordT **rows, coordT *point0,
+ boolT toporient, coordT *normal, realT *offset, boolT *nearzero);
+void qh_sethyperplane_gauss(int dim, coordT **rows, pointT *point0,
+ boolT toporient, coordT *normal, coordT *offset, boolT *nearzero);
+boolT qh_sharpnewfacets(void);
+
+/*========= infrequently used code in geom2.c =============*/
+
+coordT *qh_copypoints(coordT *points, int numpoints, int dimension);
+void qh_crossproduct(int dim, realT vecA[3], realT vecB[3], realT vecC[3]);
+realT qh_determinant(realT **rows, int dim, boolT *nearzero);
+realT qh_detjoggle(pointT *points, int numpoints, int dimension);
+void qh_detroundoff(void);
+realT qh_detsimplex(pointT *apex, setT *points, int dim, boolT *nearzero);
+realT qh_distnorm(int dim, pointT *point, pointT *normal, realT *offsetp);
+realT qh_distround(int dimension, realT maxabs, realT maxsumabs);
+realT qh_divzero(realT numer, realT denom, realT mindenom1, boolT *zerodiv);
+realT qh_facetarea(facetT *facet);
+realT qh_facetarea_simplex(int dim, coordT *apex, setT *vertices,
+ vertexT *notvertex, boolT toporient, coordT *normal, realT *offset);
+pointT *qh_facetcenter(setT *vertices);
+facetT *qh_findgooddist(pointT *point, facetT *facetA, realT *distp, facetT **facetlist);
+void qh_getarea(facetT *facetlist);
+boolT qh_gram_schmidt(int dim, realT **rows);
+boolT qh_inthresholds(coordT *normal, realT *angle);
+void qh_joggleinput(void);
+realT *qh_maxabsval(realT *normal, int dim);
+setT *qh_maxmin(pointT *points, int numpoints, int dimension);
+realT qh_maxouter(void);
+void qh_maxsimplex(int dim, setT *maxpoints, pointT *points, int numpoints, setT **simplex);
+realT qh_minabsval(realT *normal, int dim);
+int qh_mindiff(realT *vecA, realT *vecB, int dim);
+boolT qh_orientoutside(facetT *facet);
+void qh_outerinner(facetT *facet, realT *outerplane, realT *innerplane);
+coordT qh_pointdist(pointT *point1, pointT *point2, int dim);
+void qh_printmatrix(FILE *fp, const char *string, realT **rows, int numrow, int numcol);
+void qh_printpoints(FILE *fp, const char *string, setT *points);
+void qh_projectinput(void);
+void qh_projectpoints(signed char *project, int n, realT *points,
+ int numpoints, int dim, realT *newpoints, int newdim);
+void qh_rotateinput(realT **rows);
+void qh_rotatepoints(realT *points, int numpoints, int dim, realT **rows);
+void qh_scaleinput(void);
+void qh_scalelast(coordT *points, int numpoints, int dim, coordT low,
+ coordT high, coordT newhigh);
+void qh_scalepoints(pointT *points, int numpoints, int dim,
+ realT *newlows, realT *newhighs);
+boolT qh_sethalfspace(int dim, coordT *coords, coordT **nextp,
+ coordT *normal, coordT *offset, coordT *feasible);
+coordT *qh_sethalfspace_all(int dim, int count, coordT *halfspaces, pointT *feasible);
+pointT *qh_voronoi_center(int dim, setT *points);
+
+#endif /* qhDEFgeom */
+
+
+
diff --git a/xs/src/qhull/src/libqhull/geom2.c b/xs/src/qhull/src/libqhull/geom2.c
new file mode 100644
index 000000000..82ec4936e
--- /dev/null
+++ b/xs/src/qhull/src/libqhull/geom2.c
@@ -0,0 +1,2094 @@
+/*<html><pre> -<a href="qh-geom.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+
+ geom2.c
+ infrequently used geometric routines of qhull
+
+ see qh-geom.htm and geom.h
+
+ Copyright (c) 1993-2015 The Geometry Center.
+ $Id: //main/2015/qhull/src/libqhull/geom2.c#6 $$Change: 2065 $
+ $DateTime: 2016/01/18 13:51:04 $$Author: bbarber $
+
+ frequently used code goes into geom.c
+*/
+
+#include "qhull_a.h"
+
+/*================== functions in alphabetic order ============*/
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="copypoints">-</a>
+
+ qh_copypoints( points, numpoints, dimension)
+ return qh_malloc'd copy of points
+ notes:
+ qh_free the returned points to avoid a memory leak
+*/
+coordT *qh_copypoints(coordT *points, int numpoints, int dimension) {
+ int size;
+ coordT *newpoints;
+
+ size= numpoints * dimension * (int)sizeof(coordT);
+ if (!(newpoints=(coordT*)qh_malloc((size_t)size))) {
+ qh_fprintf(qh ferr, 6004, "qhull error: insufficient memory to copy %d points\n",
+ numpoints);
+ qh_errexit(qh_ERRmem, NULL, NULL);
+ }
+ memcpy((char *)newpoints, (char *)points, (size_t)size); /* newpoints!=0 by QH6004 */
+ return newpoints;
+} /* copypoints */
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="crossproduct">-</a>
+
+ qh_crossproduct( dim, vecA, vecB, vecC )
+ crossproduct of 2 dim vectors
+ C= A x B
+
+ notes:
+ from Glasner, Graphics Gems I, p. 639
+ only defined for dim==3
+*/
+void qh_crossproduct(int dim, realT vecA[3], realT vecB[3], realT vecC[3]){
+
+ if (dim == 3) {
+ vecC[0]= det2_(vecA[1], vecA[2],
+ vecB[1], vecB[2]);
+ vecC[1]= - det2_(vecA[0], vecA[2],
+ vecB[0], vecB[2]);
+ vecC[2]= det2_(vecA[0], vecA[1],
+ vecB[0], vecB[1]);
+ }
+} /* vcross */
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="determinant">-</a>
+
+ qh_determinant( rows, dim, nearzero )
+ compute signed determinant of a square matrix
+ uses qh.NEARzero to test for degenerate matrices
+
+ returns:
+ determinant
+ overwrites rows and the matrix
+ if dim == 2 or 3
+ nearzero iff determinant < qh NEARzero[dim-1]
+ (!quite correct, not critical)
+ if dim >= 4
+ nearzero iff diagonal[k] < qh NEARzero[k]
+*/
+realT qh_determinant(realT **rows, int dim, boolT *nearzero) {
+ realT det=0;
+ int i;
+ boolT sign= False;
+
+ *nearzero= False;
+ if (dim < 2) {
+ qh_fprintf(qh ferr, 6005, "qhull internal error (qh_determinate): only implemented for dimension >= 2\n");
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }else if (dim == 2) {
+ det= det2_(rows[0][0], rows[0][1],
+ rows[1][0], rows[1][1]);
+ if (fabs_(det) < 10*qh NEARzero[1]) /* not really correct, what should this be? */
+ *nearzero= True;
+ }else if (dim == 3) {
+ det= det3_(rows[0][0], rows[0][1], rows[0][2],
+ rows[1][0], rows[1][1], rows[1][2],
+ rows[2][0], rows[2][1], rows[2][2]);
+ if (fabs_(det) < 10*qh NEARzero[2]) /* what should this be? det 5.5e-12 was flat for qh_maxsimplex of qdelaunay 0,0 27,27 -36,36 -9,63 */
+ *nearzero= True;
+ }else {
+ qh_gausselim(rows, dim, dim, &sign, nearzero); /* if nearzero, diagonal still ok*/
+ det= 1.0;
+ for (i=dim; i--; )
+ det *= (rows[i])[i];
+ if (sign)
+ det= -det;
+ }
+ return det;
+} /* determinant */
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="detjoggle">-</a>
+
+ qh_detjoggle( points, numpoints, dimension )
+ determine default max joggle for point array
+ as qh_distround * qh_JOGGLEdefault
+
+ returns:
+ initial value for JOGGLEmax from points and REALepsilon
+
+ notes:
+ computes DISTround since qh_maxmin not called yet
+ if qh SCALElast, last dimension will be scaled later to MAXwidth
+
+ loop duplicated from qh_maxmin
+*/
+realT qh_detjoggle(pointT *points, int numpoints, int dimension) {
+ realT abscoord, distround, joggle, maxcoord, mincoord;
+ pointT *point, *pointtemp;
+ realT maxabs= -REALmax;
+ realT sumabs= 0;
+ realT maxwidth= 0;
+ int k;
+
+ for (k=0; k < dimension; k++) {
+ if (qh SCALElast && k == dimension-1)
+ abscoord= maxwidth;
+ else if (qh DELAUNAY && k == dimension-1) /* will qh_setdelaunay() */
+ abscoord= 2 * maxabs * maxabs; /* may be low by qh hull_dim/2 */
+ else {
+ maxcoord= -REALmax;
+ mincoord= REALmax;
+ FORALLpoint_(points, numpoints) {
+ maximize_(maxcoord, point[k]);
+ minimize_(mincoord, point[k]);
+ }
+ maximize_(maxwidth, maxcoord-mincoord);
+ abscoord= fmax_(maxcoord, -mincoord);
+ }
+ sumabs += abscoord;
+ maximize_(maxabs, abscoord);
+ } /* for k */
+ distround= qh_distround(qh hull_dim, maxabs, sumabs);
+ joggle= distround * qh_JOGGLEdefault;
+ maximize_(joggle, REALepsilon * qh_JOGGLEdefault);
+ trace2((qh ferr, 2001, "qh_detjoggle: joggle=%2.2g maxwidth=%2.2g\n", joggle, maxwidth));
+ return joggle;
+} /* detjoggle */
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="detroundoff">-</a>
+
+ qh_detroundoff()
+ determine maximum roundoff errors from
+ REALepsilon, REALmax, REALmin, qh.hull_dim, qh.MAXabs_coord,
+ qh.MAXsumcoord, qh.MAXwidth, qh.MINdenom_1
+
+ accounts for qh.SETroundoff, qh.RANDOMdist, qh MERGEexact
+ qh.premerge_cos, qh.postmerge_cos, qh.premerge_centrum,
+ qh.postmerge_centrum, qh.MINoutside,
+ qh_RATIOnearinside, qh_COPLANARratio, qh_WIDEcoplanar
+
+ returns:
+ sets qh.DISTround, etc. (see below)
+ appends precision constants to qh.qhull_options
+
+ see:
+ qh_maxmin() for qh.NEARzero
+
+ design:
+ determine qh.DISTround for distance computations
+ determine minimum denominators for qh_divzero
+ determine qh.ANGLEround for angle computations
+ adjust qh.premerge_cos,... for roundoff error
+ determine qh.ONEmerge for maximum error due to a single merge
+ determine qh.NEARinside, qh.MAXcoplanar, qh.MINvisible,
+ qh.MINoutside, qh.WIDEfacet
+ initialize qh.max_vertex and qh.minvertex
+*/
+void qh_detroundoff(void) {
+
+ qh_option("_max-width", NULL, &qh MAXwidth);
+ if (!qh SETroundoff) {
+ qh DISTround= qh_distround(qh hull_dim, qh MAXabs_coord, qh MAXsumcoord);
+ if (qh RANDOMdist)
+ qh DISTround += qh RANDOMfactor * qh MAXabs_coord;
+ qh_option("Error-roundoff", NULL, &qh DISTround);
+ }
+ qh MINdenom= qh MINdenom_1 * qh MAXabs_coord;
+ qh MINdenom_1_2= sqrt(qh MINdenom_1 * qh hull_dim) ; /* if will be normalized */
+ qh MINdenom_2= qh MINdenom_1_2 * qh MAXabs_coord;
+ /* for inner product */
+ qh ANGLEround= 1.01 * qh hull_dim * REALepsilon;
+ if (qh RANDOMdist)
+ qh ANGLEround += qh RANDOMfactor;
+ if (qh premerge_cos < REALmax/2) {
+ qh premerge_cos -= qh ANGLEround;
+ if (qh RANDOMdist)
+ qh_option("Angle-premerge-with-random", NULL, &qh premerge_cos);
+ }
+ if (qh postmerge_cos < REALmax/2) {
+ qh postmerge_cos -= qh ANGLEround;
+ if (qh RANDOMdist)
+ qh_option("Angle-postmerge-with-random", NULL, &qh postmerge_cos);
+ }
+ qh premerge_centrum += 2 * qh DISTround; /*2 for centrum and distplane()*/
+ qh postmerge_centrum += 2 * qh DISTround;
+ if (qh RANDOMdist && (qh MERGEexact || qh PREmerge))
+ qh_option("Centrum-premerge-with-random", NULL, &qh premerge_centrum);
+ if (qh RANDOMdist && qh POSTmerge)
+ qh_option("Centrum-postmerge-with-random", NULL, &qh postmerge_centrum);
+ { /* compute ONEmerge, max vertex offset for merging simplicial facets */
+ realT maxangle= 1.0, maxrho;
+
+ minimize_(maxangle, qh premerge_cos);
+ minimize_(maxangle, qh postmerge_cos);
+ /* max diameter * sin theta + DISTround for vertex to its hyperplane */
+ qh ONEmerge= sqrt((realT)qh hull_dim) * qh MAXwidth *
+ sqrt(1.0 - maxangle * maxangle) + qh DISTround;
+ maxrho= qh hull_dim * qh premerge_centrum + qh DISTround;
+ maximize_(qh ONEmerge, maxrho);
+ maxrho= qh hull_dim * qh postmerge_centrum + qh DISTround;
+ maximize_(qh ONEmerge, maxrho);
+ if (qh MERGING)
+ qh_option("_one-merge", NULL, &qh ONEmerge);
+ }
+ qh NEARinside= qh ONEmerge * qh_RATIOnearinside; /* only used if qh KEEPnearinside */
+ if (qh JOGGLEmax < REALmax/2 && (qh KEEPcoplanar || qh KEEPinside)) {
+ realT maxdist; /* adjust qh.NEARinside for joggle */
+ qh KEEPnearinside= True;
+ maxdist= sqrt((realT)qh hull_dim) * qh JOGGLEmax + qh DISTround;
+ maxdist= 2*maxdist; /* vertex and coplanar point can joggle in opposite directions */
+ maximize_(qh NEARinside, maxdist); /* must agree with qh_nearcoplanar() */
+ }
+ if (qh KEEPnearinside)
+ qh_option("_near-inside", NULL, &qh NEARinside);
+ if (qh JOGGLEmax < qh DISTround) {
+ qh_fprintf(qh ferr, 6006, "qhull error: the joggle for 'QJn', %.2g, is below roundoff for distance computations, %.2g\n",
+ qh JOGGLEmax, qh DISTround);
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ if (qh MINvisible > REALmax/2) {
+ if (!qh MERGING)
+ qh MINvisible= qh DISTround;
+ else if (qh hull_dim <= 3)
+ qh MINvisible= qh premerge_centrum;
+ else
+ qh MINvisible= qh_COPLANARratio * qh premerge_centrum;
+ if (qh APPROXhull && qh MINvisible > qh MINoutside)
+ qh MINvisible= qh MINoutside;
+ qh_option("Visible-distance", NULL, &qh MINvisible);
+ }
+ if (qh MAXcoplanar > REALmax/2) {
+ qh MAXcoplanar= qh MINvisible;
+ qh_option("U-coplanar-distance", NULL, &qh MAXcoplanar);
+ }
+ if (!qh APPROXhull) { /* user may specify qh MINoutside */
+ qh MINoutside= 2 * qh MINvisible;
+ if (qh premerge_cos < REALmax/2)
+ maximize_(qh MINoutside, (1- qh premerge_cos) * qh MAXabs_coord);
+ qh_option("Width-outside", NULL, &qh MINoutside);
+ }
+ qh WIDEfacet= qh MINoutside;
+ maximize_(qh WIDEfacet, qh_WIDEcoplanar * qh MAXcoplanar);
+ maximize_(qh WIDEfacet, qh_WIDEcoplanar * qh MINvisible);
+ qh_option("_wide-facet", NULL, &qh WIDEfacet);
+ if (qh MINvisible > qh MINoutside + 3 * REALepsilon
+ && !qh BESToutside && !qh FORCEoutput)
+ qh_fprintf(qh ferr, 7001, "qhull input warning: minimum visibility V%.2g is greater than \nminimum outside W%.2g. Flipped facets are likely.\n",
+ qh MINvisible, qh MINoutside);
+ qh max_vertex= qh DISTround;
+ qh min_vertex= -qh DISTround;
+ /* numeric constants reported in printsummary */
+} /* detroundoff */
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="detsimplex">-</a>
+
+ qh_detsimplex( apex, points, dim, nearzero )
+ compute determinant of a simplex with point apex and base points
+
+ returns:
+ signed determinant and nearzero from qh_determinant
+
+ notes:
+ uses qh.gm_matrix/qh.gm_row (assumes they're big enough)
+
+ design:
+ construct qm_matrix by subtracting apex from points
+ compute determinate
+*/
+realT qh_detsimplex(pointT *apex, setT *points, int dim, boolT *nearzero) {
+ pointT *coorda, *coordp, *gmcoord, *point, **pointp;
+ coordT **rows;
+ int k, i=0;
+ realT det;
+
+ zinc_(Zdetsimplex);
+ gmcoord= qh gm_matrix;
+ rows= qh gm_row;
+ FOREACHpoint_(points) {
+ if (i == dim)
+ break;
+ rows[i++]= gmcoord;
+ coordp= point;
+ coorda= apex;
+ for (k=dim; k--; )
+ *(gmcoord++)= *coordp++ - *coorda++;
+ }
+ if (i < dim) {
+ qh_fprintf(qh ferr, 6007, "qhull internal error (qh_detsimplex): #points %d < dimension %d\n",
+ i, dim);
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ det= qh_determinant(rows, dim, nearzero);
+ trace2((qh ferr, 2002, "qh_detsimplex: det=%2.2g for point p%d, dim %d, nearzero? %d\n",
+ det, qh_pointid(apex), dim, *nearzero));
+ return det;
+} /* detsimplex */
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="distnorm">-</a>
+
+ qh_distnorm( dim, point, normal, offset )
+ return distance from point to hyperplane at normal/offset
+
+ returns:
+ dist
+
+ notes:
+ dist > 0 if point is outside of hyperplane
+
+ see:
+ qh_distplane in geom.c
+*/
+realT qh_distnorm(int dim, pointT *point, pointT *normal, realT *offsetp) {
+ coordT *normalp= normal, *coordp= point;
+ realT dist;
+ int k;
+
+ dist= *offsetp;
+ for (k=dim; k--; )
+ dist += *(coordp++) * *(normalp++);
+ return dist;
+} /* distnorm */
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="distround">-</a>
+
+ qh_distround(dimension, maxabs, maxsumabs )
+ compute maximum round-off error for a distance computation
+ to a normalized hyperplane
+ maxabs is the maximum absolute value of a coordinate
+ maxsumabs is the maximum possible sum of absolute coordinate values
+
+ returns:
+ max dist round for REALepsilon
+
+ notes:
+ calculate roundoff error according to Golub & van Loan, 1983, Lemma 3.2-1, "Rounding Errors"
+ use sqrt(dim) since one vector is normalized
+ or use maxsumabs since one vector is < 1
+*/
+realT qh_distround(int dimension, realT maxabs, realT maxsumabs) {
+ realT maxdistsum, maxround;
+
+ maxdistsum= sqrt((realT)dimension) * maxabs;
+ minimize_( maxdistsum, maxsumabs);
+ maxround= REALepsilon * (dimension * maxdistsum * 1.01 + maxabs);
+ /* adds maxabs for offset */
+ trace4((qh ferr, 4008, "qh_distround: %2.2g maxabs %2.2g maxsumabs %2.2g maxdistsum %2.2g\n",
+ maxround, maxabs, maxsumabs, maxdistsum));
+ return maxround;
+} /* distround */
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="divzero">-</a>
+
+ qh_divzero( numer, denom, mindenom1, zerodiv )
+ divide by a number that's nearly zero
+ mindenom1= minimum denominator for dividing into 1.0
+
+ returns:
+ quotient
+ sets zerodiv and returns 0.0 if it would overflow
+
+ design:
+ if numer is nearly zero and abs(numer) < abs(denom)
+ return numer/denom
+ else if numer is nearly zero
+ return 0 and zerodiv
+ else if denom/numer non-zero
+ return numer/denom
+ else
+ return 0 and zerodiv
+*/
+realT qh_divzero(realT numer, realT denom, realT mindenom1, boolT *zerodiv) {
+ realT temp, numerx, denomx;
+
+
+ if (numer < mindenom1 && numer > -mindenom1) {
+ numerx= fabs_(numer);
+ denomx= fabs_(denom);
+ if (numerx < denomx) {
+ *zerodiv= False;
+ return numer/denom;
+ }else {
+ *zerodiv= True;
+ return 0.0;
+ }
+ }
+ temp= denom/numer;
+ if (temp > mindenom1 || temp < -mindenom1) {
+ *zerodiv= False;
+ return numer/denom;
+ }else {
+ *zerodiv= True;
+ return 0.0;
+ }
+} /* divzero */
+
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="facetarea">-</a>
+
+ qh_facetarea( facet )
+ return area for a facet
+
+ notes:
+ if non-simplicial,
+ uses centrum to triangulate facet and sums the projected areas.
+ if (qh DELAUNAY),
+ computes projected area instead for last coordinate
+ assumes facet->normal exists
+ projecting tricoplanar facets to the hyperplane does not appear to make a difference
+
+ design:
+ if simplicial
+ compute area
+ else
+ for each ridge
+ compute area from centrum to ridge
+ negate area if upper Delaunay facet
+*/
+realT qh_facetarea(facetT *facet) {
+ vertexT *apex;
+ pointT *centrum;
+ realT area= 0.0;
+ ridgeT *ridge, **ridgep;
+
+ if (facet->simplicial) {
+ apex= SETfirstt_(facet->vertices, vertexT);
+ area= qh_facetarea_simplex(qh hull_dim, apex->point, facet->vertices,
+ apex, facet->toporient, facet->normal, &facet->offset);
+ }else {
+ if (qh CENTERtype == qh_AScentrum)
+ centrum= facet->center;
+ else
+ centrum= qh_getcentrum(facet);
+ FOREACHridge_(facet->ridges)
+ area += qh_facetarea_simplex(qh hull_dim, centrum, ridge->vertices,
+ NULL, (boolT)(ridge->top == facet), facet->normal, &facet->offset);
+ if (qh CENTERtype != qh_AScentrum)
+ qh_memfree(centrum, qh normal_size);
+ }
+ if (facet->upperdelaunay && qh DELAUNAY)
+ area= -area; /* the normal should be [0,...,1] */
+ trace4((qh ferr, 4009, "qh_facetarea: f%d area %2.2g\n", facet->id, area));
+ return area;
+} /* facetarea */
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="facetarea_simplex">-</a>
+
+ qh_facetarea_simplex( dim, apex, vertices, notvertex, toporient, normal, offset )
+ return area for a simplex defined by
+ an apex, a base of vertices, an orientation, and a unit normal
+ if simplicial or tricoplanar facet,
+ notvertex is defined and it is skipped in vertices
+
+ returns:
+ computes area of simplex projected to plane [normal,offset]
+ returns 0 if vertex too far below plane (qh WIDEfacet)
+ vertex can't be apex of tricoplanar facet
+
+ notes:
+ if (qh DELAUNAY),
+ computes projected area instead for last coordinate
+ uses qh gm_matrix/gm_row and qh hull_dim
+ helper function for qh_facetarea
+
+ design:
+ if Notvertex
+ translate simplex to apex
+ else
+ project simplex to normal/offset
+ translate simplex to apex
+ if Delaunay
+ set last row/column to 0 with -1 on diagonal
+ else
+ set last row to Normal
+ compute determinate
+ scale and flip sign for area
+*/
+realT qh_facetarea_simplex(int dim, coordT *apex, setT *vertices,
+ vertexT *notvertex, boolT toporient, coordT *normal, realT *offset) {
+ pointT *coorda, *coordp, *gmcoord;
+ coordT **rows, *normalp;
+ int k, i=0;
+ realT area, dist;
+ vertexT *vertex, **vertexp;
+ boolT nearzero;
+
+ gmcoord= qh gm_matrix;
+ rows= qh gm_row;
+ FOREACHvertex_(vertices) {
+ if (vertex == notvertex)
+ continue;
+ rows[i++]= gmcoord;
+ coorda= apex;
+ coordp= vertex->point;
+ normalp= normal;
+ if (notvertex) {
+ for (k=dim; k--; )
+ *(gmcoord++)= *coordp++ - *coorda++;
+ }else {
+ dist= *offset;
+ for (k=dim; k--; )
+ dist += *coordp++ * *normalp++;
+ if (dist < -qh WIDEfacet) {
+ zinc_(Znoarea);
+ return 0.0;
+ }
+ coordp= vertex->point;
+ normalp= normal;
+ for (k=dim; k--; )
+ *(gmcoord++)= (*coordp++ - dist * *normalp++) - *coorda++;
+ }
+ }
+ if (i != dim-1) {
+ qh_fprintf(qh ferr, 6008, "qhull internal error (qh_facetarea_simplex): #points %d != dim %d -1\n",
+ i, dim);
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ rows[i]= gmcoord;
+ if (qh DELAUNAY) {
+ for (i=0; i < dim-1; i++)
+ rows[i][dim-1]= 0.0;
+ for (k=dim; k--; )
+ *(gmcoord++)= 0.0;
+ rows[dim-1][dim-1]= -1.0;
+ }else {
+ normalp= normal;
+ for (k=dim; k--; )
+ *(gmcoord++)= *normalp++;
+ }
+ zinc_(Zdetsimplex);
+ area= qh_determinant(rows, dim, &nearzero);
+ if (toporient)
+ area= -area;
+ area *= qh AREAfactor;
+ trace4((qh ferr, 4010, "qh_facetarea_simplex: area=%2.2g for point p%d, toporient %d, nearzero? %d\n",
+ area, qh_pointid(apex), toporient, nearzero));
+ return area;
+} /* facetarea_simplex */
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="facetcenter">-</a>
+
+ qh_facetcenter( vertices )
+ return Voronoi center (Voronoi vertex) for a facet's vertices
+
+ returns:
+ return temporary point equal to the center
+
+ see:
+ qh_voronoi_center()
+*/
+pointT *qh_facetcenter(setT *vertices) {
+ setT *points= qh_settemp(qh_setsize(vertices));
+ vertexT *vertex, **vertexp;
+ pointT *center;
+
+ FOREACHvertex_(vertices)
+ qh_setappend(&points, vertex->point);
+ center= qh_voronoi_center(qh hull_dim-1, points);
+ qh_settempfree(&points);
+ return center;
+} /* facetcenter */
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="findgooddist">-</a>
+
+ qh_findgooddist( point, facetA, dist, facetlist )
+ find best good facet visible for point from facetA
+ assumes facetA is visible from point
+
+ returns:
+ best facet, i.e., good facet that is furthest from point
+ distance to best facet
+ NULL if none
+
+ moves good, visible facets (and some other visible facets)
+ to end of qh facet_list
+
+ notes:
+ uses qh visit_id
+
+ design:
+ initialize bestfacet if facetA is good
+ move facetA to end of facetlist
+ for each facet on facetlist
+ for each unvisited neighbor of facet
+ move visible neighbors to end of facetlist
+ update best good neighbor
+ if no good neighbors, update best facet
+*/
+facetT *qh_findgooddist(pointT *point, facetT *facetA, realT *distp,
+ facetT **facetlist) {
+ realT bestdist= -REALmax, dist;
+ facetT *neighbor, **neighborp, *bestfacet=NULL, *facet;
+ boolT goodseen= False;
+
+ if (facetA->good) {
+ zzinc_(Zcheckpart); /* calls from check_bestdist occur after print stats */
+ qh_distplane(point, facetA, &bestdist);
+ bestfacet= facetA;
+ goodseen= True;
+ }
+ qh_removefacet(facetA);
+ qh_appendfacet(facetA);
+ *facetlist= facetA;
+ facetA->visitid= ++qh visit_id;
+ FORALLfacet_(*facetlist) {
+ FOREACHneighbor_(facet) {
+ if (neighbor->visitid == qh visit_id)
+ continue;
+ neighbor->visitid= qh visit_id;
+ if (goodseen && !neighbor->good)
+ continue;
+ zzinc_(Zcheckpart);
+ qh_distplane(point, neighbor, &dist);
+ if (dist > 0) {
+ qh_removefacet(neighbor);
+ qh_appendfacet(neighbor);
+ if (neighbor->good) {
+ goodseen= True;
+ if (dist > bestdist) {
+ bestdist= dist;
+ bestfacet= neighbor;
+ }
+ }
+ }
+ }
+ }
+ if (bestfacet) {
+ *distp= bestdist;
+ trace2((qh ferr, 2003, "qh_findgooddist: p%d is %2.2g above good facet f%d\n",
+ qh_pointid(point), bestdist, bestfacet->id));
+ return bestfacet;
+ }
+ trace4((qh ferr, 4011, "qh_findgooddist: no good facet for p%d above f%d\n",
+ qh_pointid(point), facetA->id));
+ return NULL;
+} /* findgooddist */
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="getarea">-</a>
+
+ qh_getarea( facetlist )
+ set area of all facets in facetlist
+ collect statistics
+ nop if hasAreaVolume
+
+ returns:
+ sets qh totarea/totvol to total area and volume of convex hull
+ for Delaunay triangulation, computes projected area of the lower or upper hull
+ ignores upper hull if qh ATinfinity
+
+ notes:
+ could compute outer volume by expanding facet area by rays from interior
+ the following attempt at perpendicular projection underestimated badly:
+ qh.totoutvol += (-dist + facet->maxoutside + qh DISTround)
+ * area/ qh hull_dim;
+ design:
+ for each facet on facetlist
+ compute facet->area
+ update qh.totarea and qh.totvol
+*/
+void qh_getarea(facetT *facetlist) {
+ realT area;
+ realT dist;
+ facetT *facet;
+
+ if (qh hasAreaVolume)
+ return;
+ if (qh REPORTfreq)
+ qh_fprintf(qh ferr, 8020, "computing area of each facet and volume of the convex hull\n");
+ else
+ trace1((qh ferr, 1001, "qh_getarea: computing volume and area for each facet\n"));
+ qh totarea= qh totvol= 0.0;
+ FORALLfacet_(facetlist) {
+ if (!facet->normal)
+ continue;
+ if (facet->upperdelaunay && qh ATinfinity)
+ continue;
+ if (!facet->isarea) {
+ facet->f.area= qh_facetarea(facet);
+ facet->isarea= True;
+ }
+ area= facet->f.area;
+ if (qh DELAUNAY) {
+ if (facet->upperdelaunay == qh UPPERdelaunay)
+ qh totarea += area;
+ }else {
+ qh totarea += area;
+ qh_distplane(qh interior_point, facet, &dist);
+ qh totvol += -dist * area/ qh hull_dim;
+ }
+ if (qh PRINTstatistics) {
+ wadd_(Wareatot, area);
+ wmax_(Wareamax, area);
+ wmin_(Wareamin, area);
+ }
+ }
+ qh hasAreaVolume= True;
+} /* getarea */
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="gram_schmidt">-</a>
+
+ qh_gram_schmidt( dim, row )
+ implements Gram-Schmidt orthogonalization by rows
+
+ returns:
+ false if zero norm
+ overwrites rows[dim][dim]
+
+ notes:
+ see Golub & van Loan, 1983, Algorithm 6.2-2, "Modified Gram-Schmidt"
+ overflow due to small divisors not handled
+
+ design:
+ for each row
+ compute norm for row
+ if non-zero, normalize row
+ for each remaining rowA
+ compute inner product of row and rowA
+ reduce rowA by row * inner product
+*/
+boolT qh_gram_schmidt(int dim, realT **row) {
+ realT *rowi, *rowj, norm;
+ int i, j, k;
+
+ for (i=0; i < dim; i++) {
+ rowi= row[i];
+ for (norm= 0.0, k= dim; k--; rowi++)
+ norm += *rowi * *rowi;
+ norm= sqrt(norm);
+ wmin_(Wmindenom, norm);
+ if (norm == 0.0) /* either 0 or overflow due to sqrt */
+ return False;
+ for (k=dim; k--; )
+ *(--rowi) /= norm;
+ for (j=i+1; j < dim; j++) {
+ rowj= row[j];
+ for (norm= 0.0, k=dim; k--; )
+ norm += *rowi++ * *rowj++;
+ for (k=dim; k--; )
+ *(--rowj) -= *(--rowi) * norm;
+ }
+ }
+ return True;
+} /* gram_schmidt */
+
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="inthresholds">-</a>
+
+ qh_inthresholds( normal, angle )
+ return True if normal within qh.lower_/upper_threshold
+
+ returns:
+ estimate of angle by summing of threshold diffs
+ angle may be NULL
+ smaller "angle" is better
+
+ notes:
+ invalid if qh.SPLITthresholds
+
+ see:
+ qh.lower_threshold in qh_initbuild()
+ qh_initthresholds()
+
+ design:
+ for each dimension
+ test threshold
+*/
+boolT qh_inthresholds(coordT *normal, realT *angle) {
+ boolT within= True;
+ int k;
+ realT threshold;
+
+ if (angle)
+ *angle= 0.0;
+ for (k=0; k < qh hull_dim; k++) {
+ threshold= qh lower_threshold[k];
+ if (threshold > -REALmax/2) {
+ if (normal[k] < threshold)
+ within= False;
+ if (angle) {
+ threshold -= normal[k];
+ *angle += fabs_(threshold);
+ }
+ }
+ if (qh upper_threshold[k] < REALmax/2) {
+ threshold= qh upper_threshold[k];
+ if (normal[k] > threshold)
+ within= False;
+ if (angle) {
+ threshold -= normal[k];
+ *angle += fabs_(threshold);
+ }
+ }
+ }
+ return within;
+} /* inthresholds */
+
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="joggleinput">-</a>
+
+ qh_joggleinput()
+ randomly joggle input to Qhull by qh.JOGGLEmax
+ initial input is qh.first_point/qh.num_points of qh.hull_dim
+ repeated calls use qh.input_points/qh.num_points
+
+ returns:
+ joggles points at qh.first_point/qh.num_points
+ copies data to qh.input_points/qh.input_malloc if first time
+ determines qh.JOGGLEmax if it was zero
+ if qh.DELAUNAY
+ computes the Delaunay projection of the joggled points
+
+ notes:
+ if qh.DELAUNAY, unnecessarily joggles the last coordinate
+ the initial 'QJn' may be set larger than qh_JOGGLEmaxincrease
+
+ design:
+ if qh.DELAUNAY
+ set qh.SCALElast for reduced precision errors
+ if first call
+ initialize qh.input_points to the original input points
+ if qh.JOGGLEmax == 0
+ determine default qh.JOGGLEmax
+ else
+ increase qh.JOGGLEmax according to qh.build_cnt
+ joggle the input by adding a random number in [-qh.JOGGLEmax,qh.JOGGLEmax]
+ if qh.DELAUNAY
+ sets the Delaunay projection
+*/
+void qh_joggleinput(void) {
+ int i, seed, size;
+ coordT *coordp, *inputp;
+ realT randr, randa, randb;
+
+ if (!qh input_points) { /* first call */
+ qh input_points= qh first_point;
+ qh input_malloc= qh POINTSmalloc;
+ size= qh num_points * qh hull_dim * sizeof(coordT);
+ if (!(qh first_point=(coordT*)qh_malloc((size_t)size))) {
+ qh_fprintf(qh ferr, 6009, "qhull error: insufficient memory to joggle %d points\n",
+ qh num_points);
+ qh_errexit(qh_ERRmem, NULL, NULL);
+ }
+ qh POINTSmalloc= True;
+ if (qh JOGGLEmax == 0.0) {
+ qh JOGGLEmax= qh_detjoggle(qh input_points, qh num_points, qh hull_dim);
+ qh_option("QJoggle", NULL, &qh JOGGLEmax);
+ }
+ }else { /* repeated call */
+ if (!qh RERUN && qh build_cnt > qh_JOGGLEretry) {
+ if (((qh build_cnt-qh_JOGGLEretry-1) % qh_JOGGLEagain) == 0) {
+ realT maxjoggle= qh MAXwidth * qh_JOGGLEmaxincrease;
+ if (qh JOGGLEmax < maxjoggle) {
+ qh JOGGLEmax *= qh_JOGGLEincrease;
+ minimize_(qh JOGGLEmax, maxjoggle);
+ }
+ }
+ }
+ qh_option("QJoggle", NULL, &qh JOGGLEmax);
+ }
+ if (qh build_cnt > 1 && qh JOGGLEmax > fmax_(qh MAXwidth/4, 0.1)) {
+ qh_fprintf(qh ferr, 6010, "qhull error: the current joggle for 'QJn', %.2g, is too large for the width\nof the input. If possible, recompile Qhull with higher-precision reals.\n",
+ qh JOGGLEmax);
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ /* for some reason, using qh ROTATErandom and qh_RANDOMseed does not repeat the run. Use 'TRn' instead */
+ seed= qh_RANDOMint;
+ qh_option("_joggle-seed", &seed, NULL);
+ trace0((qh ferr, 6, "qh_joggleinput: joggle input by %2.2g with seed %d\n",
+ qh JOGGLEmax, seed));
+ inputp= qh input_points;
+ coordp= qh first_point;
+ randa= 2.0 * qh JOGGLEmax/qh_RANDOMmax;
+ randb= -qh JOGGLEmax;
+ size= qh num_points * qh hull_dim;
+ for (i=size; i--; ) {
+ randr= qh_RANDOMint;
+ *(coordp++)= *(inputp++) + (randr * randa + randb);
+ }
+ if (qh DELAUNAY) {
+ qh last_low= qh last_high= qh last_newhigh= REALmax;
+ qh_setdelaunay(qh hull_dim, qh num_points, qh first_point);
+ }
+} /* joggleinput */
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="maxabsval">-</a>
+
+ qh_maxabsval( normal, dim )
+ return pointer to maximum absolute value of a dim vector
+ returns NULL if dim=0
+*/
+realT *qh_maxabsval(realT *normal, int dim) {
+ realT maxval= -REALmax;
+ realT *maxp= NULL, *colp, absval;
+ int k;
+
+ for (k=dim, colp= normal; k--; colp++) {
+ absval= fabs_(*colp);
+ if (absval > maxval) {
+ maxval= absval;
+ maxp= colp;
+ }
+ }
+ return maxp;
+} /* maxabsval */
+
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="maxmin">-</a>
+
+ qh_maxmin( points, numpoints, dimension )
+ return max/min points for each dimension
+ determine max and min coordinates
+
+ returns:
+ returns a temporary set of max and min points
+ may include duplicate points. Does not include qh.GOODpoint
+ sets qh.NEARzero, qh.MAXabs_coord, qh.MAXsumcoord, qh.MAXwidth
+ qh.MAXlastcoord, qh.MINlastcoord
+ initializes qh.max_outside, qh.min_vertex, qh.WAScoplanar, qh.ZEROall_ok
+
+ notes:
+ loop duplicated in qh_detjoggle()
+
+ design:
+ initialize global precision variables
+ checks definition of REAL...
+ for each dimension
+ for each point
+ collect maximum and minimum point
+ collect maximum of maximums and minimum of minimums
+ determine qh.NEARzero for Gaussian Elimination
+*/
+setT *qh_maxmin(pointT *points, int numpoints, int dimension) {
+ int k;
+ realT maxcoord, temp;
+ pointT *minimum, *maximum, *point, *pointtemp;
+ setT *set;
+
+ qh max_outside= 0.0;
+ qh MAXabs_coord= 0.0;
+ qh MAXwidth= -REALmax;
+ qh MAXsumcoord= 0.0;
+ qh min_vertex= 0.0;
+ qh WAScoplanar= False;
+ if (qh ZEROcentrum)
+ qh ZEROall_ok= True;
+ if (REALmin < REALepsilon && REALmin < REALmax && REALmin > -REALmax
+ && REALmax > 0.0 && -REALmax < 0.0)
+ ; /* all ok */
+ else {
+ qh_fprintf(qh ferr, 6011, "qhull error: floating point constants in user.h are wrong\n\
+REALepsilon %g REALmin %g REALmax %g -REALmax %g\n",
+ REALepsilon, REALmin, REALmax, -REALmax);
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ set= qh_settemp(2*dimension);
+ for (k=0; k < dimension; k++) {
+ if (points == qh GOODpointp)
+ minimum= maximum= points + dimension;
+ else
+ minimum= maximum= points;
+ FORALLpoint_(points, numpoints) {
+ if (point == qh GOODpointp)
+ continue;
+ if (maximum[k] < point[k])
+ maximum= point;
+ else if (minimum[k] > point[k])
+ minimum= point;
+ }
+ if (k == dimension-1) {
+ qh MINlastcoord= minimum[k];
+ qh MAXlastcoord= maximum[k];
+ }
+ if (qh SCALElast && k == dimension-1)
+ maxcoord= qh MAXwidth;
+ else {
+ maxcoord= fmax_(maximum[k], -minimum[k]);
+ if (qh GOODpointp) {
+ temp= fmax_(qh GOODpointp[k], -qh GOODpointp[k]);
+ maximize_(maxcoord, temp);
+ }
+ temp= maximum[k] - minimum[k];
+ maximize_(qh MAXwidth, temp);
+ }
+ maximize_(qh MAXabs_coord, maxcoord);
+ qh MAXsumcoord += maxcoord;
+ qh_setappend(&set, maximum);
+ qh_setappend(&set, minimum);
+ /* calculation of qh NEARzero is based on Golub & van Loan, 1983,
+ Eq. 4.4-13 for "Gaussian elimination with complete pivoting".
+ Golub & van Loan say that n^3 can be ignored and 10 be used in
+ place of rho */
+ qh NEARzero[k]= 80 * qh MAXsumcoord * REALepsilon;
+ }
+ if (qh IStracing >=1)
+ qh_printpoints(qh ferr, "qh_maxmin: found the max and min points(by dim):", set);
+ return(set);
+} /* maxmin */
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="maxouter">-</a>
+
+ qh_maxouter()
+ return maximum distance from facet to outer plane
+ normally this is qh.max_outside+qh.DISTround
+ does not include qh.JOGGLEmax
+
+ see:
+ qh_outerinner()
+
+ notes:
+ need to add another qh.DISTround if testing actual point with computation
+
+ for joggle:
+ qh_setfacetplane() updated qh.max_outer for Wnewvertexmax (max distance to vertex)
+ need to use Wnewvertexmax since could have a coplanar point for a high
+ facet that is replaced by a low facet
+ need to add qh.JOGGLEmax if testing input points
+*/
+realT qh_maxouter(void) {
+ realT dist;
+
+ dist= fmax_(qh max_outside, qh DISTround);
+ dist += qh DISTround;
+ trace4((qh ferr, 4012, "qh_maxouter: max distance from facet to outer plane is %2.2g max_outside is %2.2g\n", dist, qh max_outside));
+ return dist;
+} /* maxouter */
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="maxsimplex">-</a>
+
+ qh_maxsimplex( dim, maxpoints, points, numpoints, simplex )
+ determines maximum simplex for a set of points
+ starts from points already in simplex
+ skips qh.GOODpointp (assumes that it isn't in maxpoints)
+
+ returns:
+ simplex with dim+1 points
+
+ notes:
+ assumes at least pointsneeded points in points
+ maximizes determinate for x,y,z,w, etc.
+ uses maxpoints as long as determinate is clearly non-zero
+
+ design:
+ initialize simplex with at least two points
+ (find points with max or min x coordinate)
+ for each remaining dimension
+ add point that maximizes the determinate
+ (use points from maxpoints first)
+*/
+void qh_maxsimplex(int dim, setT *maxpoints, pointT *points, int numpoints, setT **simplex) {
+ pointT *point, **pointp, *pointtemp, *maxpoint, *minx=NULL, *maxx=NULL;
+ boolT nearzero, maxnearzero= False;
+ int k, sizinit;
+ realT maxdet= -REALmax, det, mincoord= REALmax, maxcoord= -REALmax;
+
+ sizinit= qh_setsize(*simplex);
+ if (sizinit < 2) {
+ if (qh_setsize(maxpoints) >= 2) {
+ FOREACHpoint_(maxpoints) {
+ if (maxcoord < point[0]) {
+ maxcoord= point[0];
+ maxx= point;
+ }
+ if (mincoord > point[0]) {
+ mincoord= point[0];
+ minx= point;
+ }
+ }
+ }else {
+ FORALLpoint_(points, numpoints) {
+ if (point == qh GOODpointp)
+ continue;
+ if (maxcoord < point[0]) {
+ maxcoord= point[0];
+ maxx= point;
+ }
+ if (mincoord > point[0]) {
+ mincoord= point[0];
+ minx= point;
+ }
+ }
+ }
+ qh_setunique(simplex, minx);
+ if (qh_setsize(*simplex) < 2)
+ qh_setunique(simplex, maxx);
+ sizinit= qh_setsize(*simplex);
+ if (sizinit < 2) {
+ qh_precision("input has same x coordinate");
+ if (zzval_(Zsetplane) > qh hull_dim+1) {
+ qh_fprintf(qh ferr, 6012, "qhull precision error (qh_maxsimplex for voronoi_center):\n%d points with the same x coordinate.\n",
+ qh_setsize(maxpoints)+numpoints);
+ qh_errexit(qh_ERRprec, NULL, NULL);
+ }else {
+ qh_fprintf(qh ferr, 6013, "qhull input error: input is less than %d-dimensional since it has the same x coordinate\n", qh hull_dim);
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ }
+ }
+ for (k=sizinit; k < dim+1; k++) {
+ maxpoint= NULL;
+ maxdet= -REALmax;
+ FOREACHpoint_(maxpoints) {
+ if (!qh_setin(*simplex, point)) {
+ det= qh_detsimplex(point, *simplex, k, &nearzero);
+ if ((det= fabs_(det)) > maxdet) {
+ maxdet= det;
+ maxpoint= point;
+ maxnearzero= nearzero;
+ }
+ }
+ }
+ if (!maxpoint || maxnearzero) {
+ zinc_(Zsearchpoints);
+ if (!maxpoint) {
+ trace0((qh ferr, 7, "qh_maxsimplex: searching all points for %d-th initial vertex.\n", k+1));
+ }else {
+ trace0((qh ferr, 8, "qh_maxsimplex: searching all points for %d-th initial vertex, better than p%d det %2.2g\n",
+ k+1, qh_pointid(maxpoint), maxdet));
+ }
+ FORALLpoint_(points, numpoints) {
+ if (point == qh GOODpointp)
+ continue;
+ if (!qh_setin(*simplex, point)) {
+ det= qh_detsimplex(point, *simplex, k, &nearzero);
+ if ((det= fabs_(det)) > maxdet) {
+ maxdet= det;
+ maxpoint= point;
+ maxnearzero= nearzero;
+ }
+ }
+ }
+ } /* !maxpoint */
+ if (!maxpoint) {
+ qh_fprintf(qh ferr, 6014, "qhull internal error (qh_maxsimplex): not enough points available\n");
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ qh_setappend(simplex, maxpoint);
+ trace1((qh ferr, 1002, "qh_maxsimplex: selected point p%d for %d`th initial vertex, det=%2.2g\n",
+ qh_pointid(maxpoint), k+1, maxdet));
+ } /* k */
+} /* maxsimplex */
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="minabsval">-</a>
+
+ qh_minabsval( normal, dim )
+ return minimum absolute value of a dim vector
+*/
+realT qh_minabsval(realT *normal, int dim) {
+ realT minval= 0;
+ realT maxval= 0;
+ realT *colp;
+ int k;
+
+ for (k=dim, colp=normal; k--; colp++) {
+ maximize_(maxval, *colp);
+ minimize_(minval, *colp);
+ }
+ return fmax_(maxval, -minval);
+} /* minabsval */
+
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="mindiff">-</a>
+
+ qh_mindif( vecA, vecB, dim )
+ return index of min abs. difference of two vectors
+*/
+int qh_mindiff(realT *vecA, realT *vecB, int dim) {
+ realT mindiff= REALmax, diff;
+ realT *vecAp= vecA, *vecBp= vecB;
+ int k, mink= 0;
+
+ for (k=0; k < dim; k++) {
+ diff= *vecAp++ - *vecBp++;
+ diff= fabs_(diff);
+ if (diff < mindiff) {
+ mindiff= diff;
+ mink= k;
+ }
+ }
+ return mink;
+} /* mindiff */
+
+
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="orientoutside">-</a>
+
+ qh_orientoutside( facet )
+ make facet outside oriented via qh.interior_point
+
+ returns:
+ True if facet reversed orientation.
+*/
+boolT qh_orientoutside(facetT *facet) {
+ int k;
+ realT dist;
+
+ qh_distplane(qh interior_point, facet, &dist);
+ if (dist > 0) {
+ for (k=qh hull_dim; k--; )
+ facet->normal[k]= -facet->normal[k];
+ facet->offset= -facet->offset;
+ return True;
+ }
+ return False;
+} /* orientoutside */
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="outerinner">-</a>
+
+ qh_outerinner( facet, outerplane, innerplane )
+ if facet and qh.maxoutdone (i.e., qh_check_maxout)
+ returns outer and inner plane for facet
+ else
+ returns maximum outer and inner plane
+ accounts for qh.JOGGLEmax
+
+ see:
+ qh_maxouter(), qh_check_bestdist(), qh_check_points()
+
+ notes:
+ outerplaner or innerplane may be NULL
+ facet is const
+ Does not error (QhullFacet)
+
+ includes qh.DISTround for actual points
+ adds another qh.DISTround if testing with floating point arithmetic
+*/
+void qh_outerinner(facetT *facet, realT *outerplane, realT *innerplane) {
+ realT dist, mindist;
+ vertexT *vertex, **vertexp;
+
+ if (outerplane) {
+ if (!qh_MAXoutside || !facet || !qh maxoutdone) {
+ *outerplane= qh_maxouter(); /* includes qh.DISTround */
+ }else { /* qh_MAXoutside ... */
+#if qh_MAXoutside
+ *outerplane= facet->maxoutside + qh DISTround;
+#endif
+
+ }
+ if (qh JOGGLEmax < REALmax/2)
+ *outerplane += qh JOGGLEmax * sqrt((realT)qh hull_dim);
+ }
+ if (innerplane) {
+ if (facet) {
+ mindist= REALmax;
+ FOREACHvertex_(facet->vertices) {
+ zinc_(Zdistio);
+ qh_distplane(vertex->point, facet, &dist);
+ minimize_(mindist, dist);
+ }
+ *innerplane= mindist - qh DISTround;
+ }else
+ *innerplane= qh min_vertex - qh DISTround;
+ if (qh JOGGLEmax < REALmax/2)
+ *innerplane -= qh JOGGLEmax * sqrt((realT)qh hull_dim);
+ }
+} /* outerinner */
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="pointdist">-</a>
+
+ qh_pointdist( point1, point2, dim )
+ return distance between two points
+
+ notes:
+ returns distance squared if 'dim' is negative
+*/
+coordT qh_pointdist(pointT *point1, pointT *point2, int dim) {
+ coordT dist, diff;
+ int k;
+
+ dist= 0.0;
+ for (k= (dim > 0 ? dim : -dim); k--; ) {
+ diff= *point1++ - *point2++;
+ dist += diff * diff;
+ }
+ if (dim > 0)
+ return(sqrt(dist));
+ return dist;
+} /* pointdist */
+
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="printmatrix">-</a>
+
+ qh_printmatrix( fp, string, rows, numrow, numcol )
+ print matrix to fp given by row vectors
+ print string as header
+
+ notes:
+ print a vector by qh_printmatrix(fp, "", &vect, 1, len)
+*/
+void qh_printmatrix(FILE *fp, const char *string, realT **rows, int numrow, int numcol) {
+ realT *rowp;
+ realT r; /*bug fix*/
+ int i,k;
+
+ qh_fprintf(fp, 9001, "%s\n", string);
+ for (i=0; i < numrow; i++) {
+ rowp= rows[i];
+ for (k=0; k < numcol; k++) {
+ r= *rowp++;
+ qh_fprintf(fp, 9002, "%6.3g ", r);
+ }
+ qh_fprintf(fp, 9003, "\n");
+ }
+} /* printmatrix */
+
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="printpoints">-</a>
+
+ qh_printpoints( fp, string, points )
+ print pointids to fp for a set of points
+ if string, prints string and 'p' point ids
+*/
+void qh_printpoints(FILE *fp, const char *string, setT *points) {
+ pointT *point, **pointp;
+
+ if (string) {
+ qh_fprintf(fp, 9004, "%s", string);
+ FOREACHpoint_(points)
+ qh_fprintf(fp, 9005, " p%d", qh_pointid(point));
+ qh_fprintf(fp, 9006, "\n");
+ }else {
+ FOREACHpoint_(points)
+ qh_fprintf(fp, 9007, " %d", qh_pointid(point));
+ qh_fprintf(fp, 9008, "\n");
+ }
+} /* printpoints */
+
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="projectinput">-</a>
+
+ qh_projectinput()
+ project input points using qh.lower_bound/upper_bound and qh DELAUNAY
+ if qh.lower_bound[k]=qh.upper_bound[k]= 0,
+ removes dimension k
+ if halfspace intersection
+ removes dimension k from qh.feasible_point
+ input points in qh first_point, num_points, input_dim
+
+ returns:
+ new point array in qh first_point of qh hull_dim coordinates
+ sets qh POINTSmalloc
+ if qh DELAUNAY
+ projects points to paraboloid
+ lowbound/highbound is also projected
+ if qh ATinfinity
+ adds point "at-infinity"
+ if qh POINTSmalloc
+ frees old point array
+
+ notes:
+ checks that qh.hull_dim agrees with qh.input_dim, PROJECTinput, and DELAUNAY
+
+
+ design:
+ sets project[k] to -1 (delete), 0 (keep), 1 (add for Delaunay)
+ determines newdim and newnum for qh hull_dim and qh num_points
+ projects points to newpoints
+ projects qh.lower_bound to itself
+ projects qh.upper_bound to itself
+ if qh DELAUNAY
+ if qh ATINFINITY
+ projects points to paraboloid
+ computes "infinity" point as vertex average and 10% above all points
+ else
+ uses qh_setdelaunay to project points to paraboloid
+*/
+void qh_projectinput(void) {
+ int k,i;
+ int newdim= qh input_dim, newnum= qh num_points;
+ signed char *project;
+ int projectsize= (qh input_dim+1)*sizeof(*project);
+ pointT *newpoints, *coord, *infinity;
+ realT paraboloid, maxboloid= 0;
+
+ project= (signed char*)qh_memalloc(projectsize);
+ memset((char*)project, 0, (size_t)projectsize);
+ for (k=0; k < qh input_dim; k++) { /* skip Delaunay bound */
+ if (qh lower_bound[k] == 0 && qh upper_bound[k] == 0) {
+ project[k]= -1;
+ newdim--;
+ }
+ }
+ if (qh DELAUNAY) {
+ project[k]= 1;
+ newdim++;
+ if (qh ATinfinity)
+ newnum++;
+ }
+ if (newdim != qh hull_dim) {
+ qh_memfree(project, projectsize);
+ qh_fprintf(qh ferr, 6015, "qhull internal error (qh_projectinput): dimension after projection %d != hull_dim %d\n", newdim, qh hull_dim);
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ if (!(newpoints= qh temp_malloc= (coordT*)qh_malloc(newnum*newdim*sizeof(coordT)))){
+ qh_memfree(project, projectsize);
+ qh_fprintf(qh ferr, 6016, "qhull error: insufficient memory to project %d points\n",
+ qh num_points);
+ qh_errexit(qh_ERRmem, NULL, NULL);
+ }
+ /* qh_projectpoints throws error if mismatched dimensions */
+ qh_projectpoints(project, qh input_dim+1, qh first_point,
+ qh num_points, qh input_dim, newpoints, newdim);
+ trace1((qh ferr, 1003, "qh_projectinput: updating lower and upper_bound\n"));
+ qh_projectpoints(project, qh input_dim+1, qh lower_bound,
+ 1, qh input_dim+1, qh lower_bound, newdim+1);
+ qh_projectpoints(project, qh input_dim+1, qh upper_bound,
+ 1, qh input_dim+1, qh upper_bound, newdim+1);
+ if (qh HALFspace) {
+ if (!qh feasible_point) {
+ qh_memfree(project, projectsize);
+ qh_fprintf(qh ferr, 6017, "qhull internal error (qh_projectinput): HALFspace defined without qh.feasible_point\n");
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ qh_projectpoints(project, qh input_dim, qh feasible_point,
+ 1, qh input_dim, qh feasible_point, newdim);
+ }
+ qh_memfree(project, projectsize);
+ if (qh POINTSmalloc)
+ qh_free(qh first_point);
+ qh first_point= newpoints;
+ qh POINTSmalloc= True;
+ qh temp_malloc= NULL;
+ if (qh DELAUNAY && qh ATinfinity) {
+ coord= qh first_point;
+ infinity= qh first_point + qh hull_dim * qh num_points;
+ for (k=qh hull_dim-1; k--; )
+ infinity[k]= 0.0;
+ for (i=qh num_points; i--; ) {
+ paraboloid= 0.0;
+ for (k=0; k < qh hull_dim-1; k++) {
+ paraboloid += *coord * *coord;
+ infinity[k] += *coord;
+ coord++;
+ }
+ *(coord++)= paraboloid;
+ maximize_(maxboloid, paraboloid);
+ }
+ /* coord == infinity */
+ for (k=qh hull_dim-1; k--; )
+ *(coord++) /= qh num_points;
+ *(coord++)= maxboloid * 1.1;
+ qh num_points++;
+ trace0((qh ferr, 9, "qh_projectinput: projected points to paraboloid for Delaunay\n"));
+ }else if (qh DELAUNAY) /* !qh ATinfinity */
+ qh_setdelaunay( qh hull_dim, qh num_points, qh first_point);
+} /* projectinput */
+
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="projectpoints">-</a>
+
+ qh_projectpoints( project, n, points, numpoints, dim, newpoints, newdim )
+ project points/numpoints/dim to newpoints/newdim
+ if project[k] == -1
+ delete dimension k
+ if project[k] == 1
+ add dimension k by duplicating previous column
+ n is size of project
+
+ notes:
+ newpoints may be points if only adding dimension at end
+
+ design:
+ check that 'project' and 'newdim' agree
+ for each dimension
+ if project == -1
+ skip dimension
+ else
+ determine start of column in newpoints
+ determine start of column in points
+ if project == +1, duplicate previous column
+ copy dimension (column) from points to newpoints
+*/
+void qh_projectpoints(signed char *project, int n, realT *points,
+ int numpoints, int dim, realT *newpoints, int newdim) {
+ int testdim= dim, oldk=0, newk=0, i,j=0,k;
+ realT *newp, *oldp;
+
+ for (k=0; k < n; k++)
+ testdim += project[k];
+ if (testdim != newdim) {
+ qh_fprintf(qh ferr, 6018, "qhull internal error (qh_projectpoints): newdim %d should be %d after projection\n",
+ newdim, testdim);
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ for (j=0; j<n; j++) {
+ if (project[j] == -1)
+ oldk++;
+ else {
+ newp= newpoints+newk++;
+ if (project[j] == +1) {
+ if (oldk >= dim)
+ continue;
+ oldp= points+oldk;
+ }else
+ oldp= points+oldk++;
+ for (i=numpoints; i--; ) {
+ *newp= *oldp;
+ newp += newdim;
+ oldp += dim;
+ }
+ }
+ if (oldk >= dim)
+ break;
+ }
+ trace1((qh ferr, 1004, "qh_projectpoints: projected %d points from dim %d to dim %d\n",
+ numpoints, dim, newdim));
+} /* projectpoints */
+
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="rotateinput">-</a>
+
+ qh_rotateinput( rows )
+ rotate input using row matrix
+ input points given by qh first_point, num_points, hull_dim
+ assumes rows[dim] is a scratch buffer
+ if qh POINTSmalloc, overwrites input points, else mallocs a new array
+
+ returns:
+ rotated input
+ sets qh POINTSmalloc
+
+ design:
+ see qh_rotatepoints
+*/
+void qh_rotateinput(realT **rows) {
+
+ if (!qh POINTSmalloc) {
+ qh first_point= qh_copypoints(qh first_point, qh num_points, qh hull_dim);
+ qh POINTSmalloc= True;
+ }
+ qh_rotatepoints(qh first_point, qh num_points, qh hull_dim, rows);
+} /* rotateinput */
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="rotatepoints">-</a>
+
+ qh_rotatepoints( points, numpoints, dim, row )
+ rotate numpoints points by a d-dim row matrix
+ assumes rows[dim] is a scratch buffer
+
+ returns:
+ rotated points in place
+
+ design:
+ for each point
+ for each coordinate
+ use row[dim] to compute partial inner product
+ for each coordinate
+ rotate by partial inner product
+*/
+void qh_rotatepoints(realT *points, int numpoints, int dim, realT **row) {
+ realT *point, *rowi, *coord= NULL, sum, *newval;
+ int i,j,k;
+
+ if (qh IStracing >= 1)
+ qh_printmatrix(qh ferr, "qh_rotatepoints: rotate points by", row, dim, dim);
+ for (point= points, j= numpoints; j--; point += dim) {
+ newval= row[dim];
+ for (i=0; i < dim; i++) {
+ rowi= row[i];
+ coord= point;
+ for (sum= 0.0, k= dim; k--; )
+ sum += *rowi++ * *coord++;
+ *(newval++)= sum;
+ }
+ for (k=dim; k--; )
+ *(--coord)= *(--newval);
+ }
+} /* rotatepoints */
+
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="scaleinput">-</a>
+
+ qh_scaleinput()
+ scale input points using qh low_bound/high_bound
+ input points given by qh first_point, num_points, hull_dim
+ if qh POINTSmalloc, overwrites input points, else mallocs a new array
+
+ returns:
+ scales coordinates of points to low_bound[k], high_bound[k]
+ sets qh POINTSmalloc
+
+ design:
+ see qh_scalepoints
+*/
+void qh_scaleinput(void) {
+
+ if (!qh POINTSmalloc) {
+ qh first_point= qh_copypoints(qh first_point, qh num_points, qh hull_dim);
+ qh POINTSmalloc= True;
+ }
+ qh_scalepoints(qh first_point, qh num_points, qh hull_dim,
+ qh lower_bound, qh upper_bound);
+} /* scaleinput */
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="scalelast">-</a>
+
+ qh_scalelast( points, numpoints, dim, low, high, newhigh )
+ scale last coordinate to [0,m] for Delaunay triangulations
+ input points given by points, numpoints, dim
+
+ returns:
+ changes scale of last coordinate from [low, high] to [0, newhigh]
+ overwrites last coordinate of each point
+ saves low/high/newhigh in qh.last_low, etc. for qh_setdelaunay()
+
+ notes:
+ when called by qh_setdelaunay, low/high may not match actual data
+
+ design:
+ compute scale and shift factors
+ apply to last coordinate of each point
+*/
+void qh_scalelast(coordT *points, int numpoints, int dim, coordT low,
+ coordT high, coordT newhigh) {
+ realT scale, shift;
+ coordT *coord;
+ int i;
+ boolT nearzero= False;
+
+ trace4((qh ferr, 4013, "qh_scalelast: scale last coordinate from [%2.2g, %2.2g] to [0,%2.2g]\n",
+ low, high, newhigh));
+ qh last_low= low;
+ qh last_high= high;
+ qh last_newhigh= newhigh;
+ scale= qh_divzero(newhigh, high - low,
+ qh MINdenom_1, &nearzero);
+ if (nearzero) {
+ if (qh DELAUNAY)
+ qh_fprintf(qh ferr, 6019, "qhull input error: can not scale last coordinate. Input is cocircular\n or cospherical. Use option 'Qz' to add a point at infinity.\n");
+ else
+ qh_fprintf(qh ferr, 6020, "qhull input error: can not scale last coordinate. New bounds [0, %2.2g] are too wide for\nexisting bounds [%2.2g, %2.2g] (width %2.2g)\n",
+ newhigh, low, high, high-low);
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ shift= - low * newhigh / (high-low);
+ coord= points + dim - 1;
+ for (i=numpoints; i--; coord += dim)
+ *coord= *coord * scale + shift;
+} /* scalelast */
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="scalepoints">-</a>
+
+ qh_scalepoints( points, numpoints, dim, newlows, newhighs )
+ scale points to new lowbound and highbound
+ retains old bound when newlow= -REALmax or newhigh= +REALmax
+
+ returns:
+ scaled points
+ overwrites old points
+
+ design:
+ for each coordinate
+ compute current low and high bound
+ compute scale and shift factors
+ scale all points
+ enforce new low and high bound for all points
+*/
+void qh_scalepoints(pointT *points, int numpoints, int dim,
+ realT *newlows, realT *newhighs) {
+ int i,k;
+ realT shift, scale, *coord, low, high, newlow, newhigh, mincoord, maxcoord;
+ boolT nearzero= False;
+
+ for (k=0; k < dim; k++) {
+ newhigh= newhighs[k];
+ newlow= newlows[k];
+ if (newhigh > REALmax/2 && newlow < -REALmax/2)
+ continue;
+ low= REALmax;
+ high= -REALmax;
+ for (i=numpoints, coord=points+k; i--; coord += dim) {
+ minimize_(low, *coord);
+ maximize_(high, *coord);
+ }
+ if (newhigh > REALmax/2)
+ newhigh= high;
+ if (newlow < -REALmax/2)
+ newlow= low;
+ if (qh DELAUNAY && k == dim-1 && newhigh < newlow) {
+ qh_fprintf(qh ferr, 6021, "qhull input error: 'Qb%d' or 'QB%d' inverts paraboloid since high bound %.2g < low bound %.2g\n",
+ k, k, newhigh, newlow);
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ scale= qh_divzero(newhigh - newlow, high - low,
+ qh MINdenom_1, &nearzero);
+ if (nearzero) {
+ qh_fprintf(qh ferr, 6022, "qhull input error: %d'th dimension's new bounds [%2.2g, %2.2g] too wide for\nexisting bounds [%2.2g, %2.2g]\n",
+ k, newlow, newhigh, low, high);
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ shift= (newlow * high - low * newhigh)/(high-low);
+ coord= points+k;
+ for (i=numpoints; i--; coord += dim)
+ *coord= *coord * scale + shift;
+ coord= points+k;
+ if (newlow < newhigh) {
+ mincoord= newlow;
+ maxcoord= newhigh;
+ }else {
+ mincoord= newhigh;
+ maxcoord= newlow;
+ }
+ for (i=numpoints; i--; coord += dim) {
+ minimize_(*coord, maxcoord); /* because of roundoff error */
+ maximize_(*coord, mincoord);
+ }
+ trace0((qh ferr, 10, "qh_scalepoints: scaled %d'th coordinate [%2.2g, %2.2g] to [%.2g, %.2g] for %d points by %2.2g and shifted %2.2g\n",
+ k, low, high, newlow, newhigh, numpoints, scale, shift));
+ }
+} /* scalepoints */
+
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="setdelaunay">-</a>
+
+ qh_setdelaunay( dim, count, points )
+ project count points to dim-d paraboloid for Delaunay triangulation
+
+ dim is one more than the dimension of the input set
+ assumes dim is at least 3 (i.e., at least a 2-d Delaunay triangulation)
+
+ points is a dim*count realT array. The first dim-1 coordinates
+ are the coordinates of the first input point. array[dim] is
+ the first coordinate of the second input point. array[2*dim] is
+ the first coordinate of the third input point.
+
+ if qh.last_low defined (i.e., 'Qbb' called qh_scalelast)
+ calls qh_scalelast to scale the last coordinate the same as the other points
+
+ returns:
+ for each point
+ sets point[dim-1] to sum of squares of coordinates
+ scale points to 'Qbb' if needed
+
+ notes:
+ to project one point, use
+ qh_setdelaunay(qh hull_dim, 1, point)
+
+ Do not use options 'Qbk', 'QBk', or 'QbB' since they scale
+ the coordinates after the original projection.
+
+*/
+void qh_setdelaunay(int dim, int count, pointT *points) {
+ int i, k;
+ coordT *coordp, coord;
+ realT paraboloid;
+
+ trace0((qh ferr, 11, "qh_setdelaunay: project %d points to paraboloid for Delaunay triangulation\n", count));
+ coordp= points;
+ for (i=0; i < count; i++) {
+ coord= *coordp++;
+ paraboloid= coord*coord;
+ for (k=dim-2; k--; ) {
+ coord= *coordp++;
+ paraboloid += coord*coord;
+ }
+ *coordp++ = paraboloid;
+ }
+ if (qh last_low < REALmax/2)
+ qh_scalelast(points, count, dim, qh last_low, qh last_high, qh last_newhigh);
+} /* setdelaunay */
+
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="sethalfspace">-</a>
+
+ qh_sethalfspace( dim, coords, nextp, normal, offset, feasible )
+ set point to dual of halfspace relative to feasible point
+ halfspace is normal coefficients and offset.
+
+ returns:
+ false and prints error if feasible point is outside of hull
+ overwrites coordinates for point at dim coords
+ nextp= next point (coords)
+ does not call qh_errexit
+
+ design:
+ compute distance from feasible point to halfspace
+ divide each normal coefficient by -dist
+*/
+boolT qh_sethalfspace(int dim, coordT *coords, coordT **nextp,
+ coordT *normal, coordT *offset, coordT *feasible) {
+ coordT *normp= normal, *feasiblep= feasible, *coordp= coords;
+ realT dist;
+ realT r; /*bug fix*/
+ int k;
+ boolT zerodiv;
+
+ dist= *offset;
+ for (k=dim; k--; )
+ dist += *(normp++) * *(feasiblep++);
+ if (dist > 0)
+ goto LABELerroroutside;
+ normp= normal;
+ if (dist < -qh MINdenom) {
+ for (k=dim; k--; )
+ *(coordp++)= *(normp++) / -dist;
+ }else {
+ for (k=dim; k--; ) {
+ *(coordp++)= qh_divzero(*(normp++), -dist, qh MINdenom_1, &zerodiv);
+ if (zerodiv)
+ goto LABELerroroutside;
+ }
+ }
+ *nextp= coordp;
+ if (qh IStracing >= 4) {
+ qh_fprintf(qh ferr, 8021, "qh_sethalfspace: halfspace at offset %6.2g to point: ", *offset);
+ for (k=dim, coordp=coords; k--; ) {
+ r= *coordp++;
+ qh_fprintf(qh ferr, 8022, " %6.2g", r);
+ }
+ qh_fprintf(qh ferr, 8023, "\n");
+ }
+ return True;
+LABELerroroutside:
+ feasiblep= feasible;
+ normp= normal;
+ qh_fprintf(qh ferr, 6023, "qhull input error: feasible point is not clearly inside halfspace\nfeasible point: ");
+ for (k=dim; k--; )
+ qh_fprintf(qh ferr, 8024, qh_REAL_1, r=*(feasiblep++));
+ qh_fprintf(qh ferr, 8025, "\n halfspace: ");
+ for (k=dim; k--; )
+ qh_fprintf(qh ferr, 8026, qh_REAL_1, r=*(normp++));
+ qh_fprintf(qh ferr, 8027, "\n at offset: ");
+ qh_fprintf(qh ferr, 8028, qh_REAL_1, *offset);
+ qh_fprintf(qh ferr, 8029, " and distance: ");
+ qh_fprintf(qh ferr, 8030, qh_REAL_1, dist);
+ qh_fprintf(qh ferr, 8031, "\n");
+ return False;
+} /* sethalfspace */
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="sethalfspace_all">-</a>
+
+ qh_sethalfspace_all( dim, count, halfspaces, feasible )
+ generate dual for halfspace intersection with feasible point
+ array of count halfspaces
+ each halfspace is normal coefficients followed by offset
+ the origin is inside the halfspace if the offset is negative
+ feasible is a point inside all halfspaces (http://www.qhull.org/html/qhalf.htm#notes)
+
+ returns:
+ malloc'd array of count X dim-1 points
+
+ notes:
+ call before qh_init_B or qh_initqhull_globals
+ free memory when done
+ unused/untested code: please email bradb@shore.net if this works ok for you
+ if using option 'Fp', qh->feasible_point must be set (e.g., to 'feasible')
+ qh->feasible_point is a malloc'd array that is freed by qh_freebuffers.
+
+ design:
+ see qh_sethalfspace
+*/
+coordT *qh_sethalfspace_all(int dim, int count, coordT *halfspaces, pointT *feasible) {
+ int i, newdim;
+ pointT *newpoints;
+ coordT *coordp, *normalp, *offsetp;
+
+ trace0((qh ferr, 12, "qh_sethalfspace_all: compute dual for halfspace intersection\n"));
+ newdim= dim - 1;
+ if (!(newpoints=(coordT*)qh_malloc(count*newdim*sizeof(coordT)))){
+ qh_fprintf(qh ferr, 6024, "qhull error: insufficient memory to compute dual of %d halfspaces\n",
+ count);
+ qh_errexit(qh_ERRmem, NULL, NULL);
+ }
+ coordp= newpoints;
+ normalp= halfspaces;
+ for (i=0; i < count; i++) {
+ offsetp= normalp + newdim;
+ if (!qh_sethalfspace(newdim, coordp, &coordp, normalp, offsetp, feasible)) {
+ qh_free(newpoints); /* feasible is not inside halfspace as reported by qh_sethalfspace */
+ qh_fprintf(qh ferr, 8032, "The halfspace was at index %d\n", i);
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ normalp= offsetp + 1;
+ }
+ return newpoints;
+} /* sethalfspace_all */
+
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="sharpnewfacets">-</a>
+
+ qh_sharpnewfacets()
+
+ returns:
+ true if could be an acute angle (facets in different quadrants)
+
+ notes:
+ for qh_findbest
+
+ design:
+ for all facets on qh.newfacet_list
+ if two facets are in different quadrants
+ set issharp
+*/
+boolT qh_sharpnewfacets(void) {
+ facetT *facet;
+ boolT issharp = False;
+ int *quadrant, k;
+
+ quadrant= (int*)qh_memalloc(qh hull_dim * sizeof(int));
+ FORALLfacet_(qh newfacet_list) {
+ if (facet == qh newfacet_list) {
+ for (k=qh hull_dim; k--; )
+ quadrant[ k]= (facet->normal[ k] > 0);
+ }else {
+ for (k=qh hull_dim; k--; ) {
+ if (quadrant[ k] != (facet->normal[ k] > 0)) {
+ issharp= True;
+ break;
+ }
+ }
+ }
+ if (issharp)
+ break;
+ }
+ qh_memfree( quadrant, qh hull_dim * sizeof(int));
+ trace3((qh ferr, 3001, "qh_sharpnewfacets: %d\n", issharp));
+ return issharp;
+} /* sharpnewfacets */
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="voronoi_center">-</a>
+
+ qh_voronoi_center( dim, points )
+ return Voronoi center for a set of points
+ dim is the orginal dimension of the points
+ gh.gm_matrix/qh.gm_row are scratch buffers
+
+ returns:
+ center as a temporary point (qh_memalloc)
+ if non-simplicial,
+ returns center for max simplex of points
+
+ notes:
+ only called by qh_facetcenter
+ from Bowyer & Woodwark, A Programmer's Geometry, 1983, p. 65
+
+ design:
+ if non-simplicial
+ determine max simplex for points
+ translate point0 of simplex to origin
+ compute sum of squares of diagonal
+ compute determinate
+ compute Voronoi center (see Bowyer & Woodwark)
+*/
+pointT *qh_voronoi_center(int dim, setT *points) {
+ pointT *point, **pointp, *point0;
+ pointT *center= (pointT*)qh_memalloc(qh center_size);
+ setT *simplex;
+ int i, j, k, size= qh_setsize(points);
+ coordT *gmcoord;
+ realT *diffp, sum2, *sum2row, *sum2p, det, factor;
+ boolT nearzero, infinite;
+
+ if (size == dim+1)
+ simplex= points;
+ else if (size < dim+1) {
+ qh_memfree(center, qh center_size);
+ qh_fprintf(qh ferr, 6025, "qhull internal error (qh_voronoi_center):\n need at least %d points to construct a Voronoi center\n",
+ dim+1);
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ simplex= points; /* never executed -- avoids warning */
+ }else {
+ simplex= qh_settemp(dim+1);
+ qh_maxsimplex(dim, points, NULL, 0, &simplex);
+ }
+ point0= SETfirstt_(simplex, pointT);
+ gmcoord= qh gm_matrix;
+ for (k=0; k < dim; k++) {
+ qh gm_row[k]= gmcoord;
+ FOREACHpoint_(simplex) {
+ if (point != point0)
+ *(gmcoord++)= point[k] - point0[k];
+ }
+ }
+ sum2row= gmcoord;
+ for (i=0; i < dim; i++) {
+ sum2= 0.0;
+ for (k=0; k < dim; k++) {
+ diffp= qh gm_row[k] + i;
+ sum2 += *diffp * *diffp;
+ }
+ *(gmcoord++)= sum2;
+ }
+ det= qh_determinant(qh gm_row, dim, &nearzero);
+ factor= qh_divzero(0.5, det, qh MINdenom, &infinite);
+ if (infinite) {
+ for (k=dim; k--; )
+ center[k]= qh_INFINITE;
+ if (qh IStracing)
+ qh_printpoints(qh ferr, "qh_voronoi_center: at infinity for ", simplex);
+ }else {
+ for (i=0; i < dim; i++) {
+ gmcoord= qh gm_matrix;
+ sum2p= sum2row;
+ for (k=0; k < dim; k++) {
+ qh gm_row[k]= gmcoord;
+ if (k == i) {
+ for (j=dim; j--; )
+ *(gmcoord++)= *sum2p++;
+ }else {
+ FOREACHpoint_(simplex) {
+ if (point != point0)
+ *(gmcoord++)= point[k] - point0[k];
+ }
+ }
+ }
+ center[i]= qh_determinant(qh gm_row, dim, &nearzero)*factor + point0[i];
+ }
+#ifndef qh_NOtrace
+ if (qh IStracing >= 3) {
+ qh_fprintf(qh ferr, 8033, "qh_voronoi_center: det %2.2g factor %2.2g ", det, factor);
+ qh_printmatrix(qh ferr, "center:", &center, 1, dim);
+ if (qh IStracing >= 5) {
+ qh_printpoints(qh ferr, "points", simplex);
+ FOREACHpoint_(simplex)
+ qh_fprintf(qh ferr, 8034, "p%d dist %.2g, ", qh_pointid(point),
+ qh_pointdist(point, center, dim));
+ qh_fprintf(qh ferr, 8035, "\n");
+ }
+ }
+#endif
+ }
+ if (simplex != points)
+ qh_settempfree(&simplex);
+ return center;
+} /* voronoi_center */
+
diff --git a/xs/src/qhull/src/libqhull/global.c b/xs/src/qhull/src/libqhull/global.c
new file mode 100644
index 000000000..0328fea7b
--- /dev/null
+++ b/xs/src/qhull/src/libqhull/global.c
@@ -0,0 +1,2217 @@
+
+/*<html><pre> -<a href="qh-globa.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ global.c
+ initializes all the globals of the qhull application
+
+ see README
+
+ see libqhull.h for qh.globals and function prototypes
+
+ see qhull_a.h for internal functions
+
+ Copyright (c) 1993-2015 The Geometry Center.
+ $Id: //main/2015/qhull/src/libqhull/global.c#17 $$Change: 2066 $
+ $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
+ */
+
+#include "qhull_a.h"
+
+/*========= qh definition -- globals defined in libqhull.h =======================*/
+
+#if qh_QHpointer
+qhT *qh_qh= NULL; /* pointer to all global variables */
+#else
+qhT qh_qh; /* all global variables.
+ Add "= {0}" if this causes a compiler error.
+ Also qh_qhstat in stat.c and qhmem in mem.c. */
+#endif
+
+/*-<a href ="qh-globa.htm#TOC"
+ >--------------------------------</a><a name="qh_version">-</a>
+
+ qh_version
+ version string by year and date
+ qh_version2 for Unix users and -V
+
+ the revision increases on code changes only
+
+ notes:
+ change date: Changes.txt, Announce.txt, index.htm, README.txt,
+ qhull-news.html, Eudora signatures, CMakeLists.txt
+ change version: README.txt, qh-get.htm, File_id.diz, Makefile.txt, CMakeLists.txt
+ check that CmakeLists @version is the same as qh_version2
+ change year: Copying.txt
+ check download size
+ recompile user_eg.c, rbox.c, libqhull.c, qconvex.c, qdelaun.c qvoronoi.c, qhalf.c, testqset.c
+*/
+
+const char qh_version[]= "2015.2 2016/01/18";
+const char qh_version2[]= "qhull 7.2.0 (2015.2 2016/01/18)";
+
+/*-<a href="qh-globa.htm#TOC"
+ >-------------------------------</a><a name="appendprint">-</a>
+
+ qh_appendprint( printFormat )
+ append printFormat to qh.PRINTout unless already defined
+*/
+void qh_appendprint(qh_PRINT format) {
+ int i;
+
+ for (i=0; i < qh_PRINTEND; i++) {
+ if (qh PRINTout[i] == format && format != qh_PRINTqhull)
+ break;
+ if (!qh PRINTout[i]) {
+ qh PRINTout[i]= format;
+ break;
+ }
+ }
+} /* appendprint */
+
+/*-<a href="qh-globa.htm#TOC"
+ >-------------------------------</a><a name="checkflags">-</a>
+
+ qh_checkflags( commandStr, hiddenFlags )
+ errors if commandStr contains hiddenFlags
+ hiddenFlags starts and ends with a space and is space delimited (checked)
+
+ notes:
+ ignores first word (e.g., "qconvex i")
+ use qh_strtol/strtod since strtol/strtod may or may not skip trailing spaces
+
+ see:
+ qh_initflags() initializes Qhull according to commandStr
+*/
+void qh_checkflags(char *command, char *hiddenflags) {
+ char *s= command, *t, *chkerr; /* qh_skipfilename is non-const */
+ char key, opt, prevopt;
+ char chkkey[]= " ";
+ char chkopt[]= " ";
+ char chkopt2[]= " ";
+ boolT waserr= False;
+
+ if (*hiddenflags != ' ' || hiddenflags[strlen(hiddenflags)-1] != ' ') {
+ qh_fprintf(qh ferr, 6026, "qhull error (qh_checkflags): hiddenflags must start and end with a space: \"%s\"", hiddenflags);
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ if (strpbrk(hiddenflags, ",\n\r\t")) {
+ qh_fprintf(qh ferr, 6027, "qhull error (qh_checkflags): hiddenflags contains commas, newlines, or tabs: \"%s\"", hiddenflags);
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ while (*s && !isspace(*s)) /* skip program name */
+ s++;
+ while (*s) {
+ while (*s && isspace(*s))
+ s++;
+ if (*s == '-')
+ s++;
+ if (!*s)
+ break;
+ key = *s++;
+ chkerr = NULL;
+ if (key == 'T' && (*s == 'I' || *s == 'O')) { /* TI or TO 'file name' */
+ s= qh_skipfilename(++s);
+ continue;
+ }
+ chkkey[1]= key;
+ if (strstr(hiddenflags, chkkey)) {
+ chkerr= chkkey;
+ }else if (isupper(key)) {
+ opt= ' ';
+ prevopt= ' ';
+ chkopt[1]= key;
+ chkopt2[1]= key;
+ while (!chkerr && *s && !isspace(*s)) {
+ opt= *s++;
+ if (isalpha(opt)) {
+ chkopt[2]= opt;
+ if (strstr(hiddenflags, chkopt))
+ chkerr= chkopt;
+ if (prevopt != ' ') {
+ chkopt2[2]= prevopt;
+ chkopt2[3]= opt;
+ if (strstr(hiddenflags, chkopt2))
+ chkerr= chkopt2;
+ }
+ }else if (key == 'Q' && isdigit(opt) && prevopt != 'b'
+ && (prevopt == ' ' || islower(prevopt))) {
+ chkopt[2]= opt;
+ if (strstr(hiddenflags, chkopt))
+ chkerr= chkopt;
+ }else {
+ qh_strtod(s-1, &t);
+ if (s < t)
+ s= t;
+ }
+ prevopt= opt;
+ }
+ }
+ if (chkerr) {
+ *chkerr= '\'';
+ chkerr[strlen(chkerr)-1]= '\'';
+ qh_fprintf(qh ferr, 6029, "qhull error: option %s is not used with this program.\n It may be used with qhull.\n", chkerr);
+ waserr= True;
+ }
+ }
+ if (waserr)
+ qh_errexit(qh_ERRinput, NULL, NULL);
+} /* checkflags */
+
+/*-<a href="qh-globa.htm#TOC"
+ >-------------------------------</a><a name="qh_clear_outputflags">-</a>
+
+ qh_clear_outputflags()
+ Clear output flags for QhullPoints
+*/
+void qh_clear_outputflags(void) {
+ int i,k;
+
+ qh ANNOTATEoutput= False;
+ qh DOintersections= False;
+ qh DROPdim= -1;
+ qh FORCEoutput= False;
+ qh GETarea= False;
+ qh GOODpoint= 0;
+ qh GOODpointp= NULL;
+ qh GOODthreshold= False;
+ qh GOODvertex= 0;
+ qh GOODvertexp= NULL;
+ qh IStracing= 0;
+ qh KEEParea= False;
+ qh KEEPmerge= False;
+ qh KEEPminArea= REALmax;
+ qh PRINTcentrums= False;
+ qh PRINTcoplanar= False;
+ qh PRINTdots= False;
+ qh PRINTgood= False;
+ qh PRINTinner= False;
+ qh PRINTneighbors= False;
+ qh PRINTnoplanes= False;
+ qh PRINToptions1st= False;
+ qh PRINTouter= False;
+ qh PRINTprecision= True;
+ qh PRINTridges= False;
+ qh PRINTspheres= False;
+ qh PRINTstatistics= False;
+ qh PRINTsummary= False;
+ qh PRINTtransparent= False;
+ qh SPLITthresholds= False;
+ qh TRACElevel= 0;
+ qh TRInormals= False;
+ qh USEstdout= False;
+ qh VERIFYoutput= False;
+ for (k=qh input_dim+1; k--; ) { /* duplicated in qh_initqhull_buffers and qh_clear_outputflags */
+ qh lower_threshold[k]= -REALmax;
+ qh upper_threshold[k]= REALmax;
+ qh lower_bound[k]= -REALmax;
+ qh upper_bound[k]= REALmax;
+ }
+
+ for (i=0; i < qh_PRINTEND; i++) {
+ qh PRINTout[i]= qh_PRINTnone;
+ }
+
+ if (!qh qhull_commandsiz2)
+ qh qhull_commandsiz2= (int)strlen(qh qhull_command); /* WARN64 */
+ else {
+ qh qhull_command[qh qhull_commandsiz2]= '\0';
+ }
+ if (!qh qhull_optionsiz2)
+ qh qhull_optionsiz2= (int)strlen(qh qhull_options); /* WARN64 */
+ else {
+ qh qhull_options[qh qhull_optionsiz2]= '\0';
+ qh qhull_optionlen= qh_OPTIONline; /* start a new line */
+ }
+} /* clear_outputflags */
+
+/*-<a href="qh-globa.htm#TOC"
+ >-------------------------------</a><a name="clock">-</a>
+
+ qh_clock()
+ return user CPU time in 100ths (qh_SECtick)
+ only defined for qh_CLOCKtype == 2
+
+ notes:
+ use first value to determine time 0
+ from Stevens '92 8.15
+*/
+unsigned long qh_clock(void) {
+
+#if (qh_CLOCKtype == 2)
+ struct tms time;
+ static long clktck; /* initialized first call and never updated */
+ double ratio, cpu;
+ unsigned long ticks;
+
+ if (!clktck) {
+ if ((clktck= sysconf(_SC_CLK_TCK)) < 0) {
+ qh_fprintf(qh ferr, 6030, "qhull internal error (qh_clock): sysconf() failed. Use qh_CLOCKtype 1 in user.h\n");
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ }
+ if (times(&time) == -1) {
+ qh_fprintf(qh ferr, 6031, "qhull internal error (qh_clock): times() failed. Use qh_CLOCKtype 1 in user.h\n");
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ ratio= qh_SECticks / (double)clktck;
+ ticks= time.tms_utime * ratio;
+ return ticks;
+#else
+ qh_fprintf(qh ferr, 6032, "qhull internal error (qh_clock): use qh_CLOCKtype 2 in user.h\n");
+ qh_errexit(qh_ERRqhull, NULL, NULL); /* never returns */
+ return 0;
+#endif
+} /* clock */
+
+/*-<a href="qh-globa.htm#TOC"
+ >-------------------------------</a><a name="freebuffers">-</a>
+
+ qh_freebuffers()
+ free up global memory buffers
+
+ notes:
+ must match qh_initbuffers()
+*/
+void qh_freebuffers(void) {
+
+ trace5((qh ferr, 5001, "qh_freebuffers: freeing up global memory buffers\n"));
+ /* allocated by qh_initqhull_buffers */
+ qh_memfree(qh NEARzero, qh hull_dim * sizeof(realT));
+ qh_memfree(qh lower_threshold, (qh input_dim+1) * sizeof(realT));
+ qh_memfree(qh upper_threshold, (qh input_dim+1) * sizeof(realT));
+ qh_memfree(qh lower_bound, (qh input_dim+1) * sizeof(realT));
+ qh_memfree(qh upper_bound, (qh input_dim+1) * sizeof(realT));
+ qh_memfree(qh gm_matrix, (qh hull_dim+1) * qh hull_dim * sizeof(coordT));
+ qh_memfree(qh gm_row, (qh hull_dim+1) * sizeof(coordT *));
+ qh NEARzero= qh lower_threshold= qh upper_threshold= NULL;
+ qh lower_bound= qh upper_bound= NULL;
+ qh gm_matrix= NULL;
+ qh gm_row= NULL;
+ qh_setfree(&qh other_points);
+ qh_setfree(&qh del_vertices);
+ qh_setfree(&qh coplanarfacetset);
+ if (qh line) /* allocated by qh_readinput, freed if no error */
+ qh_free(qh line);
+ if (qh half_space)
+ qh_free(qh half_space);
+ if (qh temp_malloc)
+ qh_free(qh temp_malloc);
+ if (qh feasible_point) /* allocated by qh_readfeasible */
+ qh_free(qh feasible_point);
+ if (qh feasible_string) /* allocated by qh_initflags */
+ qh_free(qh feasible_string);
+ qh line= qh feasible_string= NULL;
+ qh half_space= qh feasible_point= qh temp_malloc= NULL;
+ /* usually allocated by qh_readinput */
+ if (qh first_point && qh POINTSmalloc) {
+ qh_free(qh first_point);
+ qh first_point= NULL;
+ }
+ if (qh input_points && qh input_malloc) { /* set by qh_joggleinput */
+ qh_free(qh input_points);
+ qh input_points= NULL;
+ }
+ trace5((qh ferr, 5002, "qh_freebuffers: finished\n"));
+} /* freebuffers */
+
+
+/*-<a href="qh-globa.htm#TOC"
+ >-------------------------------</a><a name="freebuild">-</a>
+
+ qh_freebuild( allmem )
+ free global memory used by qh_initbuild and qh_buildhull
+ if !allmem,
+ does not free short memory (e.g., facetT, freed by qh_memfreeshort)
+
+ design:
+ free centrums
+ free each vertex
+ mark unattached ridges
+ for each facet
+ free ridges
+ free outside set, coplanar set, neighbor set, ridge set, vertex set
+ free facet
+ free hash table
+ free interior point
+ free merge set
+ free temporary sets
+*/
+void qh_freebuild(boolT allmem) {
+ facetT *facet;
+ vertexT *vertex;
+ ridgeT *ridge, **ridgep;
+ mergeT *merge, **mergep;
+
+ trace1((qh ferr, 1005, "qh_freebuild: free memory from qh_inithull and qh_buildhull\n"));
+ if (qh del_vertices)
+ qh_settruncate(qh del_vertices, 0);
+ if (allmem) {
+ while ((vertex= qh vertex_list)) {
+ if (vertex->next)
+ qh_delvertex(vertex);
+ else {
+ qh_memfree(vertex, (int)sizeof(vertexT));
+ qh newvertex_list= qh vertex_list= NULL;
+ }
+ }
+ }else if (qh VERTEXneighbors) {
+ FORALLvertices
+ qh_setfreelong(&(vertex->neighbors));
+ }
+ qh VERTEXneighbors= False;
+ qh GOODclosest= NULL;
+ if (allmem) {
+ FORALLfacets {
+ FOREACHridge_(facet->ridges)
+ ridge->seen= False;
+ }
+ FORALLfacets {
+ if (facet->visible) {
+ FOREACHridge_(facet->ridges) {
+ if (!otherfacet_(ridge, facet)->visible)
+ ridge->seen= True; /* an unattached ridge */
+ }
+ }
+ }
+ while ((facet= qh facet_list)) {
+ FOREACHridge_(facet->ridges) {
+ if (ridge->seen) {
+ qh_setfree(&(ridge->vertices));
+ qh_memfree(ridge, (int)sizeof(ridgeT));
+ }else
+ ridge->seen= True;
+ }
+ qh_setfree(&(facet->outsideset));
+ qh_setfree(&(facet->coplanarset));
+ qh_setfree(&(facet->neighbors));
+ qh_setfree(&(facet->ridges));
+ qh_setfree(&(facet->vertices));
+ if (facet->next)
+ qh_delfacet(facet);
+ else {
+ qh_memfree(facet, (int)sizeof(facetT));
+ qh visible_list= qh newfacet_list= qh facet_list= NULL;
+ }
+ }
+ }else {
+ FORALLfacets {
+ qh_setfreelong(&(facet->outsideset));
+ qh_setfreelong(&(facet->coplanarset));
+ if (!facet->simplicial) {
+ qh_setfreelong(&(facet->neighbors));
+ qh_setfreelong(&(facet->ridges));
+ qh_setfreelong(&(facet->vertices));
+ }
+ }
+ }
+ qh_setfree(&(qh hash_table));
+ qh_memfree(qh interior_point, qh normal_size);
+ qh interior_point= NULL;
+ FOREACHmerge_(qh facet_mergeset) /* usually empty */
+ qh_memfree(merge, (int)sizeof(mergeT));
+ qh facet_mergeset= NULL; /* temp set */
+ qh degen_mergeset= NULL; /* temp set */
+ qh_settempfree_all();
+} /* freebuild */
+
+/*-<a href="qh-globa.htm#TOC"
+ >-------------------------------</a><a name="freeqhull">-</a>
+
+ qh_freeqhull( allmem )
+ see qh_freeqhull2
+ if qh_QHpointer, frees qh_qh
+*/
+void qh_freeqhull(boolT allmem) {
+ qh_freeqhull2(allmem);
+#if qh_QHpointer
+ qh_free(qh_qh);
+ qh_qh= NULL;
+#endif
+}
+
+/*-<a href="qh-globa.htm#TOC"
+>-------------------------------</a><a name="freeqhull2">-</a>
+
+qh_freeqhull2( allmem )
+ free global memory and set qhT to 0
+ if !allmem,
+ does not free short memory (freed by qh_memfreeshort unless qh_NOmem)
+
+notes:
+ sets qh.NOerrexit in case caller forgets to
+ Does not throw errors
+
+see:
+ see qh_initqhull_start2()
+
+design:
+ free global and temporary memory from qh_initbuild and qh_buildhull
+ free buffers
+ free statistics
+*/
+void qh_freeqhull2(boolT allmem) {
+
+ qh NOerrexit= True; /* no more setjmp since called at exit and ~QhullQh */
+ trace1((qh ferr, 1006, "qh_freeqhull: free global memory\n"));
+ qh_freebuild(allmem);
+ qh_freebuffers();
+ qh_freestatistics();
+#if qh_QHpointer
+ memset((char *)qh_qh, 0, sizeof(qhT));
+ /* qh_qh freed by caller, qh_freeqhull() */
+#else
+ memset((char *)&qh_qh, 0, sizeof(qhT));
+#endif
+ qh NOerrexit= True;
+} /* freeqhull2 */
+
+/*-<a href="qh-globa.htm#TOC"
+ >-------------------------------</a><a name="init_A">-</a>
+
+ qh_init_A( infile, outfile, errfile, argc, argv )
+ initialize memory and stdio files
+ convert input options to option string (qh.qhull_command)
+
+ notes:
+ infile may be NULL if qh_readpoints() is not called
+
+ errfile should always be defined. It is used for reporting
+ errors. outfile is used for output and format options.
+
+ argc/argv may be 0/NULL
+
+ called before error handling initialized
+ qh_errexit() may not be used
+*/
+void qh_init_A(FILE *infile, FILE *outfile, FILE *errfile, int argc, char *argv[]) {
+ qh_meminit(errfile);
+ qh_initqhull_start(infile, outfile, errfile);
+ qh_init_qhull_command(argc, argv);
+} /* init_A */
+
+/*-<a href="qh-globa.htm#TOC"
+ >-------------------------------</a><a name="init_B">-</a>
+
+ qh_init_B( points, numpoints, dim, ismalloc )
+ initialize globals for points array
+
+ points has numpoints dim-dimensional points
+ points[0] is the first coordinate of the first point
+ points[1] is the second coordinate of the first point
+ points[dim] is the first coordinate of the second point
+
+ ismalloc=True
+ Qhull will call qh_free(points) on exit or input transformation
+ ismalloc=False
+ Qhull will allocate a new point array if needed for input transformation
+
+ qh.qhull_command
+ is the option string.
+ It is defined by qh_init_B(), qh_qhull_command(), or qh_initflags
+
+ returns:
+ if qh.PROJECTinput or (qh.DELAUNAY and qh.PROJECTdelaunay)
+ projects the input to a new point array
+
+ if qh.DELAUNAY,
+ qh.hull_dim is increased by one
+ if qh.ATinfinity,
+ qh_projectinput adds point-at-infinity for Delaunay tri.
+
+ if qh.SCALEinput
+ changes the upper and lower bounds of the input, see qh_scaleinput()
+
+ if qh.ROTATEinput
+ rotates the input by a random rotation, see qh_rotateinput()
+ if qh.DELAUNAY
+ rotates about the last coordinate
+
+ notes:
+ called after points are defined
+ qh_errexit() may be used
+*/
+void qh_init_B(coordT *points, int numpoints, int dim, boolT ismalloc) {
+ qh_initqhull_globals(points, numpoints, dim, ismalloc);
+ if (qhmem.LASTsize == 0)
+ qh_initqhull_mem();
+ /* mem.c and qset.c are initialized */
+ qh_initqhull_buffers();
+ qh_initthresholds(qh qhull_command);
+ if (qh PROJECTinput || (qh DELAUNAY && qh PROJECTdelaunay))
+ qh_projectinput();
+ if (qh SCALEinput)
+ qh_scaleinput();
+ if (qh ROTATErandom >= 0) {
+ qh_randommatrix(qh gm_matrix, qh hull_dim, qh gm_row);
+ if (qh DELAUNAY) {
+ int k, lastk= qh hull_dim-1;
+ for (k=0; k < lastk; k++) {
+ qh gm_row[k][lastk]= 0.0;
+ qh gm_row[lastk][k]= 0.0;
+ }
+ qh gm_row[lastk][lastk]= 1.0;
+ }
+ qh_gram_schmidt(qh hull_dim, qh gm_row);
+ qh_rotateinput(qh gm_row);
+ }
+} /* init_B */
+
+/*-<a href="qh-globa.htm#TOC"
+ >-------------------------------</a><a name="init_qhull_command">-</a>
+
+ qh_init_qhull_command( argc, argv )
+ build qh.qhull_command from argc/argv
+ Calls qh_exit if qhull_command is too short
+
+ returns:
+ a space-delimited string of options (just as typed)
+
+ notes:
+ makes option string easy to input and output
+
+ argc/argv may be 0/NULL
+*/
+void qh_init_qhull_command(int argc, char *argv[]) {
+
+ if (!qh_argv_to_command(argc, argv, qh qhull_command, (int)sizeof(qh qhull_command))){
+ /* Assumes qh.ferr is defined. */
+ qh_fprintf(qh ferr, 6033, "qhull input error: more than %d characters in command line\n",
+ (int)sizeof(qh qhull_command));
+ qh_exit(qh_ERRinput); /* error reported, can not use qh_errexit */
+ }
+} /* init_qhull_command */
+
+/*-<a href="qh-globa.htm#TOC"
+ >-------------------------------</a><a name="initflags">-</a>
+
+ qh_initflags( commandStr )
+ set flags and initialized constants from commandStr
+ calls qh_exit() if qh->NOerrexit
+
+ returns:
+ sets qh.qhull_command to command if needed
+
+ notes:
+ ignores first word (e.g., "qhull d")
+ use qh_strtol/strtod since strtol/strtod may or may not skip trailing spaces
+
+ see:
+ qh_initthresholds() continues processing of 'Pdn' and 'PDn'
+ 'prompt' in unix.c for documentation
+
+ design:
+ for each space-delimited option group
+ if top-level option
+ check syntax
+ append appropriate option to option string
+ set appropriate global variable or append printFormat to print options
+ else
+ for each sub-option
+ check syntax
+ append appropriate option to option string
+ set appropriate global variable or append printFormat to print options
+*/
+void qh_initflags(char *command) {
+ int k, i, lastproject;
+ char *s= command, *t, *prev_s, *start, key;
+ boolT isgeom= False, wasproject;
+ realT r;
+
+ if(qh NOerrexit){/* without this comment, segfault in gcc 4.4.0 mingw32 */
+ qh_fprintf(qh ferr, 6245, "qhull initflags error: qh.NOerrexit was not cleared before calling qh_initflags(). It should be cleared after setjmp(). Exit qhull.");
+ qh_exit(6245);
+ }
+ if (command <= &qh qhull_command[0] || command > &qh qhull_command[0] + sizeof(qh qhull_command)) {
+ if (command != &qh qhull_command[0]) {
+ *qh qhull_command= '\0';
+ strncat(qh qhull_command, command, sizeof(qh qhull_command)-strlen(qh qhull_command)-1);
+ }
+ while (*s && !isspace(*s)) /* skip program name */
+ s++;
+ }
+ while (*s) {
+ while (*s && isspace(*s))
+ s++;
+ if (*s == '-')
+ s++;
+ if (!*s)
+ break;
+ prev_s= s;
+ switch (*s++) {
+ case 'd':
+ qh_option("delaunay", NULL, NULL);
+ qh DELAUNAY= True;
+ break;
+ case 'f':
+ qh_option("facets", NULL, NULL);
+ qh_appendprint(qh_PRINTfacets);
+ break;
+ case 'i':
+ qh_option("incidence", NULL, NULL);
+ qh_appendprint(qh_PRINTincidences);
+ break;
+ case 'm':
+ qh_option("mathematica", NULL, NULL);
+ qh_appendprint(qh_PRINTmathematica);
+ break;
+ case 'n':
+ qh_option("normals", NULL, NULL);
+ qh_appendprint(qh_PRINTnormals);
+ break;
+ case 'o':
+ qh_option("offFile", NULL, NULL);
+ qh_appendprint(qh_PRINToff);
+ break;
+ case 'p':
+ qh_option("points", NULL, NULL);
+ qh_appendprint(qh_PRINTpoints);
+ break;
+ case 's':
+ qh_option("summary", NULL, NULL);
+ qh PRINTsummary= True;
+ break;
+ case 'v':
+ qh_option("voronoi", NULL, NULL);
+ qh VORONOI= True;
+ qh DELAUNAY= True;
+ break;
+ case 'A':
+ if (!isdigit(*s) && *s != '.' && *s != '-')
+ qh_fprintf(qh ferr, 7002, "qhull warning: no maximum cosine angle given for option 'An'. Ignored.\n");
+ else {
+ if (*s == '-') {
+ qh premerge_cos= -qh_strtod(s, &s);
+ qh_option("Angle-premerge-", NULL, &qh premerge_cos);
+ qh PREmerge= True;
+ }else {
+ qh postmerge_cos= qh_strtod(s, &s);
+ qh_option("Angle-postmerge", NULL, &qh postmerge_cos);
+ qh POSTmerge= True;
+ }
+ qh MERGING= True;
+ }
+ break;
+ case 'C':
+ if (!isdigit(*s) && *s != '.' && *s != '-')
+ qh_fprintf(qh ferr, 7003, "qhull warning: no centrum radius given for option 'Cn'. Ignored.\n");
+ else {
+ if (*s == '-') {
+ qh premerge_centrum= -qh_strtod(s, &s);
+ qh_option("Centrum-premerge-", NULL, &qh premerge_centrum);
+ qh PREmerge= True;
+ }else {
+ qh postmerge_centrum= qh_strtod(s, &s);
+ qh_option("Centrum-postmerge", NULL, &qh postmerge_centrum);
+ qh POSTmerge= True;
+ }
+ qh MERGING= True;
+ }
+ break;
+ case 'E':
+ if (*s == '-')
+ qh_fprintf(qh ferr, 7004, "qhull warning: negative maximum roundoff given for option 'An'. Ignored.\n");
+ else if (!isdigit(*s))
+ qh_fprintf(qh ferr, 7005, "qhull warning: no maximum roundoff given for option 'En'. Ignored.\n");
+ else {
+ qh DISTround= qh_strtod(s, &s);
+ qh_option("Distance-roundoff", NULL, &qh DISTround);
+ qh SETroundoff= True;
+ }
+ break;
+ case 'H':
+ start= s;
+ qh HALFspace= True;
+ qh_strtod(s, &t);
+ while (t > s) {
+ if (*t && !isspace(*t)) {
+ if (*t == ',')
+ t++;
+ else
+ qh_fprintf(qh ferr, 7006, "qhull warning: origin for Halfspace intersection should be 'Hn,n,n,...'\n");
+ }
+ s= t;
+ qh_strtod(s, &t);
+ }
+ if (start < t) {
+ if (!(qh feasible_string= (char*)calloc((size_t)(t-start+1), (size_t)1))) {
+ qh_fprintf(qh ferr, 6034, "qhull error: insufficient memory for 'Hn,n,n'\n");
+ qh_errexit(qh_ERRmem, NULL, NULL);
+ }
+ strncpy(qh feasible_string, start, (size_t)(t-start));
+ qh_option("Halfspace-about", NULL, NULL);
+ qh_option(qh feasible_string, NULL, NULL);
+ }else
+ qh_option("Halfspace", NULL, NULL);
+ break;
+ case 'R':
+ if (!isdigit(*s))
+ qh_fprintf(qh ferr, 7007, "qhull warning: missing random perturbation for option 'Rn'. Ignored\n");
+ else {
+ qh RANDOMfactor= qh_strtod(s, &s);
+ qh_option("Random_perturb", NULL, &qh RANDOMfactor);
+ qh RANDOMdist= True;
+ }
+ break;
+ case 'V':
+ if (!isdigit(*s) && *s != '-')
+ qh_fprintf(qh ferr, 7008, "qhull warning: missing visible distance for option 'Vn'. Ignored\n");
+ else {
+ qh MINvisible= qh_strtod(s, &s);
+ qh_option("Visible", NULL, &qh MINvisible);
+ }
+ break;
+ case 'U':
+ if (!isdigit(*s) && *s != '-')
+ qh_fprintf(qh ferr, 7009, "qhull warning: missing coplanar distance for option 'Un'. Ignored\n");
+ else {
+ qh MAXcoplanar= qh_strtod(s, &s);
+ qh_option("U-coplanar", NULL, &qh MAXcoplanar);
+ }
+ break;
+ case 'W':
+ if (*s == '-')
+ qh_fprintf(qh ferr, 7010, "qhull warning: negative outside width for option 'Wn'. Ignored.\n");
+ else if (!isdigit(*s))
+ qh_fprintf(qh ferr, 7011, "qhull warning: missing outside width for option 'Wn'. Ignored\n");
+ else {
+ qh MINoutside= qh_strtod(s, &s);
+ qh_option("W-outside", NULL, &qh MINoutside);
+ qh APPROXhull= True;
+ }
+ break;
+ /************ sub menus ***************/
+ case 'F':
+ while (*s && !isspace(*s)) {
+ switch (*s++) {
+ case 'a':
+ qh_option("Farea", NULL, NULL);
+ qh_appendprint(qh_PRINTarea);
+ qh GETarea= True;
+ break;
+ case 'A':
+ qh_option("FArea-total", NULL, NULL);
+ qh GETarea= True;
+ break;
+ case 'c':
+ qh_option("Fcoplanars", NULL, NULL);
+ qh_appendprint(qh_PRINTcoplanars);
+ break;
+ case 'C':
+ qh_option("FCentrums", NULL, NULL);
+ qh_appendprint(qh_PRINTcentrums);
+ break;
+ case 'd':
+ qh_option("Fd-cdd-in", NULL, NULL);
+ qh CDDinput= True;
+ break;
+ case 'D':
+ qh_option("FD-cdd-out", NULL, NULL);
+ qh CDDoutput= True;
+ break;
+ case 'F':
+ qh_option("FFacets-xridge", NULL, NULL);
+ qh_appendprint(qh_PRINTfacets_xridge);
+ break;
+ case 'i':
+ qh_option("Finner", NULL, NULL);
+ qh_appendprint(qh_PRINTinner);
+ break;
+ case 'I':
+ qh_option("FIDs", NULL, NULL);
+ qh_appendprint(qh_PRINTids);
+ break;
+ case 'm':
+ qh_option("Fmerges", NULL, NULL);
+ qh_appendprint(qh_PRINTmerges);
+ break;
+ case 'M':
+ qh_option("FMaple", NULL, NULL);
+ qh_appendprint(qh_PRINTmaple);
+ break;
+ case 'n':
+ qh_option("Fneighbors", NULL, NULL);
+ qh_appendprint(qh_PRINTneighbors);
+ break;
+ case 'N':
+ qh_option("FNeighbors-vertex", NULL, NULL);
+ qh_appendprint(qh_PRINTvneighbors);
+ break;
+ case 'o':
+ qh_option("Fouter", NULL, NULL);
+ qh_appendprint(qh_PRINTouter);
+ break;
+ case 'O':
+ if (qh PRINToptions1st) {
+ qh_option("FOptions", NULL, NULL);
+ qh_appendprint(qh_PRINToptions);
+ }else
+ qh PRINToptions1st= True;
+ break;
+ case 'p':
+ qh_option("Fpoint-intersect", NULL, NULL);
+ qh_appendprint(qh_PRINTpointintersect);
+ break;
+ case 'P':
+ qh_option("FPoint-nearest", NULL, NULL);
+ qh_appendprint(qh_PRINTpointnearest);
+ break;
+ case 'Q':
+ qh_option("FQhull", NULL, NULL);
+ qh_appendprint(qh_PRINTqhull);
+ break;
+ case 's':
+ qh_option("Fsummary", NULL, NULL);
+ qh_appendprint(qh_PRINTsummary);
+ break;
+ case 'S':
+ qh_option("FSize", NULL, NULL);
+ qh_appendprint(qh_PRINTsize);
+ qh GETarea= True;
+ break;
+ case 't':
+ qh_option("Ftriangles", NULL, NULL);
+ qh_appendprint(qh_PRINTtriangles);
+ break;
+ case 'v':
+ /* option set in qh_initqhull_globals */
+ qh_appendprint(qh_PRINTvertices);
+ break;
+ case 'V':
+ qh_option("FVertex-average", NULL, NULL);
+ qh_appendprint(qh_PRINTaverage);
+ break;
+ case 'x':
+ qh_option("Fxtremes", NULL, NULL);
+ qh_appendprint(qh_PRINTextremes);
+ break;
+ default:
+ s--;
+ qh_fprintf(qh ferr, 7012, "qhull warning: unknown 'F' output option %c, rest ignored\n", (int)s[0]);
+ while (*++s && !isspace(*s));
+ break;
+ }
+ }
+ break;
+ case 'G':
+ isgeom= True;
+ qh_appendprint(qh_PRINTgeom);
+ while (*s && !isspace(*s)) {
+ switch (*s++) {
+ case 'a':
+ qh_option("Gall-points", NULL, NULL);
+ qh PRINTdots= True;
+ break;
+ case 'c':
+ qh_option("Gcentrums", NULL, NULL);
+ qh PRINTcentrums= True;
+ break;
+ case 'h':
+ qh_option("Gintersections", NULL, NULL);
+ qh DOintersections= True;
+ break;
+ case 'i':
+ qh_option("Ginner", NULL, NULL);
+ qh PRINTinner= True;
+ break;
+ case 'n':
+ qh_option("Gno-planes", NULL, NULL);
+ qh PRINTnoplanes= True;
+ break;
+ case 'o':
+ qh_option("Gouter", NULL, NULL);
+ qh PRINTouter= True;
+ break;
+ case 'p':
+ qh_option("Gpoints", NULL, NULL);
+ qh PRINTcoplanar= True;
+ break;
+ case 'r':
+ qh_option("Gridges", NULL, NULL);
+ qh PRINTridges= True;
+ break;
+ case 't':
+ qh_option("Gtransparent", NULL, NULL);
+ qh PRINTtransparent= True;
+ break;
+ case 'v':
+ qh_option("Gvertices", NULL, NULL);
+ qh PRINTspheres= True;
+ break;
+ case 'D':
+ if (!isdigit(*s))
+ qh_fprintf(qh ferr, 6035, "qhull input error: missing dimension for option 'GDn'\n");
+ else {
+ if (qh DROPdim >= 0)
+ qh_fprintf(qh ferr, 7013, "qhull warning: can only drop one dimension. Previous 'GD%d' ignored\n",
+ qh DROPdim);
+ qh DROPdim= qh_strtol(s, &s);
+ qh_option("GDrop-dim", &qh DROPdim, NULL);
+ }
+ break;
+ default:
+ s--;
+ qh_fprintf(qh ferr, 7014, "qhull warning: unknown 'G' print option %c, rest ignored\n", (int)s[0]);
+ while (*++s && !isspace(*s));
+ break;
+ }
+ }
+ break;
+ case 'P':
+ while (*s && !isspace(*s)) {
+ switch (*s++) {
+ case 'd': case 'D': /* see qh_initthresholds() */
+ key= s[-1];
+ i= qh_strtol(s, &s);
+ r= 0;
+ if (*s == ':') {
+ s++;
+ r= qh_strtod(s, &s);
+ }
+ if (key == 'd')
+ qh_option("Pdrop-facets-dim-less", &i, &r);
+ else
+ qh_option("PDrop-facets-dim-more", &i, &r);
+ break;
+ case 'g':
+ qh_option("Pgood-facets", NULL, NULL);
+ qh PRINTgood= True;
+ break;
+ case 'G':
+ qh_option("PGood-facet-neighbors", NULL, NULL);
+ qh PRINTneighbors= True;
+ break;
+ case 'o':
+ qh_option("Poutput-forced", NULL, NULL);
+ qh FORCEoutput= True;
+ break;
+ case 'p':
+ qh_option("Pprecision-ignore", NULL, NULL);
+ qh PRINTprecision= False;
+ break;
+ case 'A':
+ if (!isdigit(*s))
+ qh_fprintf(qh ferr, 6036, "qhull input error: missing facet count for keep area option 'PAn'\n");
+ else {
+ qh KEEParea= qh_strtol(s, &s);
+ qh_option("PArea-keep", &qh KEEParea, NULL);
+ qh GETarea= True;
+ }
+ break;
+ case 'F':
+ if (!isdigit(*s))
+ qh_fprintf(qh ferr, 6037, "qhull input error: missing facet area for option 'PFn'\n");
+ else {
+ qh KEEPminArea= qh_strtod(s, &s);
+ qh_option("PFacet-area-keep", NULL, &qh KEEPminArea);
+ qh GETarea= True;
+ }
+ break;
+ case 'M':
+ if (!isdigit(*s))
+ qh_fprintf(qh ferr, 6038, "qhull input error: missing merge count for option 'PMn'\n");
+ else {
+ qh KEEPmerge= qh_strtol(s, &s);
+ qh_option("PMerge-keep", &qh KEEPmerge, NULL);
+ }
+ break;
+ default:
+ s--;
+ qh_fprintf(qh ferr, 7015, "qhull warning: unknown 'P' print option %c, rest ignored\n", (int)s[0]);
+ while (*++s && !isspace(*s));
+ break;
+ }
+ }
+ break;
+ case 'Q':
+ lastproject= -1;
+ while (*s && !isspace(*s)) {
+ switch (*s++) {
+ case 'b': case 'B': /* handled by qh_initthresholds */
+ key= s[-1];
+ if (key == 'b' && *s == 'B') {
+ s++;
+ r= qh_DEFAULTbox;
+ qh SCALEinput= True;
+ qh_option("QbBound-unit-box", NULL, &r);
+ break;
+ }
+ if (key == 'b' && *s == 'b') {
+ s++;
+ qh SCALElast= True;
+ qh_option("Qbbound-last", NULL, NULL);
+ break;
+ }
+ k= qh_strtol(s, &s);
+ r= 0.0;
+ wasproject= False;
+ if (*s == ':') {
+ s++;
+ if ((r= qh_strtod(s, &s)) == 0.0) {
+ t= s; /* need true dimension for memory allocation */
+ while (*t && !isspace(*t)) {
+ if (toupper(*t++) == 'B'
+ && k == qh_strtol(t, &t)
+ && *t++ == ':'
+ && qh_strtod(t, &t) == 0.0) {
+ qh PROJECTinput++;
+ trace2((qh ferr, 2004, "qh_initflags: project dimension %d\n", k));
+ qh_option("Qb-project-dim", &k, NULL);
+ wasproject= True;
+ lastproject= k;
+ break;
+ }
+ }
+ }
+ }
+ if (!wasproject) {
+ if (lastproject == k && r == 0.0)
+ lastproject= -1; /* doesn't catch all possible sequences */
+ else if (key == 'b') {
+ qh SCALEinput= True;
+ if (r == 0.0)
+ r= -qh_DEFAULTbox;
+ qh_option("Qbound-dim-low", &k, &r);
+ }else {
+ qh SCALEinput= True;
+ if (r == 0.0)
+ r= qh_DEFAULTbox;
+ qh_option("QBound-dim-high", &k, &r);
+ }
+ }
+ break;
+ case 'c':
+ qh_option("Qcoplanar-keep", NULL, NULL);
+ qh KEEPcoplanar= True;
+ break;
+ case 'f':
+ qh_option("Qfurthest-outside", NULL, NULL);
+ qh BESToutside= True;
+ break;
+ case 'g':
+ qh_option("Qgood-facets-only", NULL, NULL);
+ qh ONLYgood= True;
+ break;
+ case 'i':
+ qh_option("Qinterior-keep", NULL, NULL);
+ qh KEEPinside= True;
+ break;
+ case 'm':
+ qh_option("Qmax-outside-only", NULL, NULL);
+ qh ONLYmax= True;
+ break;
+ case 'r':
+ qh_option("Qrandom-outside", NULL, NULL);
+ qh RANDOMoutside= True;
+ break;
+ case 's':
+ qh_option("Qsearch-initial-simplex", NULL, NULL);
+ qh ALLpoints= True;
+ break;
+ case 't':
+ qh_option("Qtriangulate", NULL, NULL);
+ qh TRIangulate= True;
+ break;
+ case 'T':
+ qh_option("QTestPoints", NULL, NULL);
+ if (!isdigit(*s))
+ qh_fprintf(qh ferr, 6039, "qhull input error: missing number of test points for option 'QTn'\n");
+ else {
+ qh TESTpoints= qh_strtol(s, &s);
+ qh_option("QTestPoints", &qh TESTpoints, NULL);
+ }
+ break;
+ case 'u':
+ qh_option("QupperDelaunay", NULL, NULL);
+ qh UPPERdelaunay= True;
+ break;
+ case 'v':
+ qh_option("Qvertex-neighbors-convex", NULL, NULL);
+ qh TESTvneighbors= True;
+ break;
+ case 'x':
+ qh_option("Qxact-merge", NULL, NULL);
+ qh MERGEexact= True;
+ break;
+ case 'z':
+ qh_option("Qz-infinity-point", NULL, NULL);
+ qh ATinfinity= True;
+ break;
+ case '0':
+ qh_option("Q0-no-premerge", NULL, NULL);
+ qh NOpremerge= True;
+ break;
+ case '1':
+ if (!isdigit(*s)) {
+ qh_option("Q1-no-angle-sort", NULL, NULL);
+ qh ANGLEmerge= False;
+ break;
+ }
+ switch (*s++) {
+ case '0':
+ qh_option("Q10-no-narrow", NULL, NULL);
+ qh NOnarrow= True;
+ break;
+ case '1':
+ qh_option("Q11-trinormals Qtriangulate", NULL, NULL);
+ qh TRInormals= True;
+ qh TRIangulate= True;
+ break;
+ case '2':
+ qh_option("Q12-no-wide-dup", NULL, NULL);
+ qh NOwide= True;
+ break;
+ default:
+ s--;
+ qh_fprintf(qh ferr, 7016, "qhull warning: unknown 'Q' qhull option 1%c, rest ignored\n", (int)s[0]);
+ while (*++s && !isspace(*s));
+ break;
+ }
+ break;
+ case '2':
+ qh_option("Q2-no-merge-independent", NULL, NULL);
+ qh MERGEindependent= False;
+ goto LABELcheckdigit;
+ break; /* no warnings */
+ case '3':
+ qh_option("Q3-no-merge-vertices", NULL, NULL);
+ qh MERGEvertices= False;
+ LABELcheckdigit:
+ if (isdigit(*s))
+ qh_fprintf(qh ferr, 7017, "qhull warning: can not follow '1', '2', or '3' with a digit. '%c' skipped.\n",
+ *s++);
+ break;
+ case '4':
+ qh_option("Q4-avoid-old-into-new", NULL, NULL);
+ qh AVOIDold= True;
+ break;
+ case '5':
+ qh_option("Q5-no-check-outer", NULL, NULL);
+ qh SKIPcheckmax= True;
+ break;
+ case '6':
+ qh_option("Q6-no-concave-merge", NULL, NULL);
+ qh SKIPconvex= True;
+ break;
+ case '7':
+ qh_option("Q7-no-breadth-first", NULL, NULL);
+ qh VIRTUALmemory= True;
+ break;
+ case '8':
+ qh_option("Q8-no-near-inside", NULL, NULL);
+ qh NOnearinside= True;
+ break;
+ case '9':
+ qh_option("Q9-pick-furthest", NULL, NULL);
+ qh PICKfurthest= True;
+ break;
+ case 'G':
+ i= qh_strtol(s, &t);
+ if (qh GOODpoint)
+ qh_fprintf(qh ferr, 7018, "qhull warning: good point already defined for option 'QGn'. Ignored\n");
+ else if (s == t)
+ qh_fprintf(qh ferr, 7019, "qhull warning: missing good point id for option 'QGn'. Ignored\n");
+ else if (i < 0 || *s == '-') {
+ qh GOODpoint= i-1;
+ qh_option("QGood-if-dont-see-point", &i, NULL);
+ }else {
+ qh GOODpoint= i+1;
+ qh_option("QGood-if-see-point", &i, NULL);
+ }
+ s= t;
+ break;
+ case 'J':
+ if (!isdigit(*s) && *s != '-')
+ qh JOGGLEmax= 0.0;
+ else {
+ qh JOGGLEmax= (realT) qh_strtod(s, &s);
+ qh_option("QJoggle", NULL, &qh JOGGLEmax);
+ }
+ break;
+ case 'R':
+ if (!isdigit(*s) && *s != '-')
+ qh_fprintf(qh ferr, 7020, "qhull warning: missing random seed for option 'QRn'. Ignored\n");
+ else {
+ qh ROTATErandom= i= qh_strtol(s, &s);
+ if (i > 0)
+ qh_option("QRotate-id", &i, NULL );
+ else if (i < -1)
+ qh_option("QRandom-seed", &i, NULL );
+ }
+ break;
+ case 'V':
+ i= qh_strtol(s, &t);
+ if (qh GOODvertex)
+ qh_fprintf(qh ferr, 7021, "qhull warning: good vertex already defined for option 'QVn'. Ignored\n");
+ else if (s == t)
+ qh_fprintf(qh ferr, 7022, "qhull warning: no good point id given for option 'QVn'. Ignored\n");
+ else if (i < 0) {
+ qh GOODvertex= i - 1;
+ qh_option("QV-good-facets-not-point", &i, NULL);
+ }else {
+ qh_option("QV-good-facets-point", &i, NULL);
+ qh GOODvertex= i + 1;
+ }
+ s= t;
+ break;
+ default:
+ s--;
+ qh_fprintf(qh ferr, 7023, "qhull warning: unknown 'Q' qhull option %c, rest ignored\n", (int)s[0]);
+ while (*++s && !isspace(*s));
+ break;
+ }
+ }
+ break;
+ case 'T':
+ while (*s && !isspace(*s)) {
+ if (isdigit(*s) || *s == '-')
+ qh IStracing= qh_strtol(s, &s);
+ else switch (*s++) {
+ case 'a':
+ qh_option("Tannotate-output", NULL, NULL);
+ qh ANNOTATEoutput= True;
+ break;
+ case 'c':
+ qh_option("Tcheck-frequently", NULL, NULL);
+ qh CHECKfrequently= True;
+ break;
+ case 's':
+ qh_option("Tstatistics", NULL, NULL);
+ qh PRINTstatistics= True;
+ break;
+ case 'v':
+ qh_option("Tverify", NULL, NULL);
+ qh VERIFYoutput= True;
+ break;
+ case 'z':
+ if (qh ferr == qh_FILEstderr) {
+ /* The C++ interface captures the output in qh_fprint_qhull() */
+ qh_option("Tz-stdout", NULL, NULL);
+ qh USEstdout= True;
+ }else if (!qh fout)
+ qh_fprintf(qh ferr, 7024, "qhull warning: output file undefined(stdout). Option 'Tz' ignored.\n");
+ else {
+ qh_option("Tz-stdout", NULL, NULL);
+ qh USEstdout= True;
+ qh ferr= qh fout;
+ qhmem.ferr= qh fout;
+ }
+ break;
+ case 'C':
+ if (!isdigit(*s))
+ qh_fprintf(qh ferr, 7025, "qhull warning: missing point id for cone for trace option 'TCn'. Ignored\n");
+ else {
+ i= qh_strtol(s, &s);
+ qh_option("TCone-stop", &i, NULL);
+ qh STOPcone= i + 1;
+ }
+ break;
+ case 'F':
+ if (!isdigit(*s))
+ qh_fprintf(qh ferr, 7026, "qhull warning: missing frequency count for trace option 'TFn'. Ignored\n");
+ else {
+ qh REPORTfreq= qh_strtol(s, &s);
+ qh_option("TFacet-log", &qh REPORTfreq, NULL);
+ qh REPORTfreq2= qh REPORTfreq/2; /* for tracemerging() */
+ }
+ break;
+ case 'I':
+ if (!isspace(*s))
+ qh_fprintf(qh ferr, 7027, "qhull warning: missing space between 'TI' and filename, %s\n", s);
+ while (isspace(*s))
+ s++;
+ t= qh_skipfilename(s);
+ {
+ char filename[qh_FILENAMElen];
+
+ qh_copyfilename(filename, (int)sizeof(filename), s, (int)(t-s)); /* WARN64 */
+ s= t;
+ if (!freopen(filename, "r", stdin)) {
+ qh_fprintf(qh ferr, 6041, "qhull error: could not open file \"%s\".", filename);
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }else {
+ qh_option("TInput-file", NULL, NULL);
+ qh_option(filename, NULL, NULL);
+ }
+ }
+ break;
+ case 'O':
+ if (!isspace(*s))
+ qh_fprintf(qh ferr, 7028, "qhull warning: missing space between 'TO' and filename, %s\n", s);
+ while (isspace(*s))
+ s++;
+ t= qh_skipfilename(s);
+ {
+ char filename[qh_FILENAMElen];
+
+ qh_copyfilename(filename, (int)sizeof(filename), s, (int)(t-s)); /* WARN64 */
+ s= t;
+ if (!qh fout) {
+ qh_fprintf(qh ferr, 6266, "qhull input warning: qh.fout was not set by caller. Cannot use option 'TO' to redirect output. Ignoring option 'TO'\n");
+ }else if (!freopen(filename, "w", qh fout)) {
+ qh_fprintf(qh ferr, 6044, "qhull error: could not open file \"%s\".", filename);
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }else {
+ qh_option("TOutput-file", NULL, NULL);
+ qh_option(filename, NULL, NULL);
+ }
+ }
+ break;
+ case 'P':
+ if (!isdigit(*s))
+ qh_fprintf(qh ferr, 7029, "qhull warning: missing point id for trace option 'TPn'. Ignored\n");
+ else {
+ qh TRACEpoint= qh_strtol(s, &s);
+ qh_option("Trace-point", &qh TRACEpoint, NULL);
+ }
+ break;
+ case 'M':
+ if (!isdigit(*s))
+ qh_fprintf(qh ferr, 7030, "qhull warning: missing merge id for trace option 'TMn'. Ignored\n");
+ else {
+ qh TRACEmerge= qh_strtol(s, &s);
+ qh_option("Trace-merge", &qh TRACEmerge, NULL);
+ }
+ break;
+ case 'R':
+ if (!isdigit(*s))
+ qh_fprintf(qh ferr, 7031, "qhull warning: missing rerun count for trace option 'TRn'. Ignored\n");
+ else {
+ qh RERUN= qh_strtol(s, &s);
+ qh_option("TRerun", &qh RERUN, NULL);
+ }
+ break;
+ case 'V':
+ i= qh_strtol(s, &t);
+ if (s == t)
+ qh_fprintf(qh ferr, 7032, "qhull warning: missing furthest point id for trace option 'TVn'. Ignored\n");
+ else if (i < 0) {
+ qh STOPpoint= i - 1;
+ qh_option("TV-stop-before-point", &i, NULL);
+ }else {
+ qh STOPpoint= i + 1;
+ qh_option("TV-stop-after-point", &i, NULL);
+ }
+ s= t;
+ break;
+ case 'W':
+ if (!isdigit(*s))
+ qh_fprintf(qh ferr, 7033, "qhull warning: missing max width for trace option 'TWn'. Ignored\n");
+ else {
+ qh TRACEdist= (realT) qh_strtod(s, &s);
+ qh_option("TWide-trace", NULL, &qh TRACEdist);
+ }
+ break;
+ default:
+ s--;
+ qh_fprintf(qh ferr, 7034, "qhull warning: unknown 'T' trace option %c, rest ignored\n", (int)s[0]);
+ while (*++s && !isspace(*s));
+ break;
+ }
+ }
+ break;
+ default:
+ qh_fprintf(qh ferr, 7035, "qhull warning: unknown flag %c(%x)\n", (int)s[-1],
+ (int)s[-1]);
+ break;
+ }
+ if (s-1 == prev_s && *s && !isspace(*s)) {
+ qh_fprintf(qh ferr, 7036, "qhull warning: missing space after flag %c(%x); reserved for menu. Skipped.\n",
+ (int)*prev_s, (int)*prev_s);
+ while (*s && !isspace(*s))
+ s++;
+ }
+ }
+ if (qh STOPcone && qh JOGGLEmax < REALmax/2)
+ qh_fprintf(qh ferr, 7078, "qhull warning: 'TCn' (stopCone) ignored when used with 'QJn' (joggle)\n");
+ if (isgeom && !qh FORCEoutput && qh PRINTout[1])
+ qh_fprintf(qh ferr, 7037, "qhull warning: additional output formats are not compatible with Geomview\n");
+ /* set derived values in qh_initqhull_globals */
+} /* initflags */
+
+
+/*-<a href="qh-globa.htm#TOC"
+ >-------------------------------</a><a name="initqhull_buffers">-</a>
+
+ qh_initqhull_buffers()
+ initialize global memory buffers
+
+ notes:
+ must match qh_freebuffers()
+*/
+void qh_initqhull_buffers(void) {
+ int k;
+
+ qh TEMPsize= (qhmem.LASTsize - sizeof(setT))/SETelemsize;
+ if (qh TEMPsize <= 0 || qh TEMPsize > qhmem.LASTsize)
+ qh TEMPsize= 8; /* e.g., if qh_NOmem */
+ qh other_points= qh_setnew(qh TEMPsize);
+ qh del_vertices= qh_setnew(qh TEMPsize);
+ qh coplanarfacetset= qh_setnew(qh TEMPsize);
+ qh NEARzero= (realT *)qh_memalloc(qh hull_dim * sizeof(realT));
+ qh lower_threshold= (realT *)qh_memalloc((qh input_dim+1) * sizeof(realT));
+ qh upper_threshold= (realT *)qh_memalloc((qh input_dim+1) * sizeof(realT));
+ qh lower_bound= (realT *)qh_memalloc((qh input_dim+1) * sizeof(realT));
+ qh upper_bound= (realT *)qh_memalloc((qh input_dim+1) * sizeof(realT));
+ for (k=qh input_dim+1; k--; ) { /* duplicated in qh_initqhull_buffers and qh_clear_outputflags */
+ qh lower_threshold[k]= -REALmax;
+ qh upper_threshold[k]= REALmax;
+ qh lower_bound[k]= -REALmax;
+ qh upper_bound[k]= REALmax;
+ }
+ qh gm_matrix= (coordT *)qh_memalloc((qh hull_dim+1) * qh hull_dim * sizeof(coordT));
+ qh gm_row= (coordT **)qh_memalloc((qh hull_dim+1) * sizeof(coordT *));
+} /* initqhull_buffers */
+
+/*-<a href="qh-globa.htm#TOC"
+ >-------------------------------</a><a name="initqhull_globals">-</a>
+
+ qh_initqhull_globals( points, numpoints, dim, ismalloc )
+ initialize globals
+ if ismalloc
+ points were malloc'd and qhull should free at end
+
+ returns:
+ sets qh.first_point, num_points, input_dim, hull_dim and others
+ seeds random number generator (seed=1 if tracing)
+ modifies qh.hull_dim if ((qh.DELAUNAY and qh.PROJECTdelaunay) or qh.PROJECTinput)
+ adjust user flags as needed
+ also checks DIM3 dependencies and constants
+
+ notes:
+ do not use qh_point() since an input transformation may move them elsewhere
+
+ see:
+ qh_initqhull_start() sets default values for non-zero globals
+
+ design:
+ initialize points array from input arguments
+ test for qh.ZEROcentrum
+ (i.e., use opposite vertex instead of cetrum for convexity testing)
+ initialize qh.CENTERtype, qh.normal_size,
+ qh.center_size, qh.TRACEpoint/level,
+ initialize and test random numbers
+ qh_initqhull_outputflags() -- adjust and test output flags
+*/
+void qh_initqhull_globals(coordT *points, int numpoints, int dim, boolT ismalloc) {
+ int seed, pointsneeded, extra= 0, i, randi, k;
+ realT randr;
+ realT factorial;
+
+ time_t timedata;
+
+ trace0((qh ferr, 13, "qh_initqhull_globals: for %s | %s\n", qh rbox_command,
+ qh qhull_command));
+ qh POINTSmalloc= ismalloc;
+ qh first_point= points;
+ qh num_points= numpoints;
+ qh hull_dim= qh input_dim= dim;
+ if (!qh NOpremerge && !qh MERGEexact && !qh PREmerge && qh JOGGLEmax > REALmax/2) {
+ qh MERGING= True;
+ if (qh hull_dim <= 4) {
+ qh PREmerge= True;
+ qh_option("_pre-merge", NULL, NULL);
+ }else {
+ qh MERGEexact= True;
+ qh_option("Qxact_merge", NULL, NULL);
+ }
+ }else if (qh MERGEexact)
+ qh MERGING= True;
+ if (!qh NOpremerge && qh JOGGLEmax > REALmax/2) {
+#ifdef qh_NOmerge
+ qh JOGGLEmax= 0.0;
+#endif
+ }
+ if (qh TRIangulate && qh JOGGLEmax < REALmax/2 && qh PRINTprecision)
+ qh_fprintf(qh ferr, 7038, "qhull warning: joggle('QJ') always produces simplicial output. Triangulated output('Qt') does nothing.\n");
+ if (qh JOGGLEmax < REALmax/2 && qh DELAUNAY && !qh SCALEinput && !qh SCALElast) {
+ qh SCALElast= True;
+ qh_option("Qbbound-last-qj", NULL, NULL);
+ }
+ if (qh MERGING && !qh POSTmerge && qh premerge_cos > REALmax/2
+ && qh premerge_centrum == 0) {
+ qh ZEROcentrum= True;
+ qh ZEROall_ok= True;
+ qh_option("_zero-centrum", NULL, NULL);
+ }
+ if (qh JOGGLEmax < REALmax/2 && REALepsilon > 2e-8 && qh PRINTprecision)
+ qh_fprintf(qh ferr, 7039, "qhull warning: real epsilon, %2.2g, is probably too large for joggle('QJn')\nRecompile with double precision reals(see user.h).\n",
+ REALepsilon);
+#ifdef qh_NOmerge
+ if (qh MERGING) {
+ qh_fprintf(qh ferr, 6045, "qhull input error: merging not installed(qh_NOmerge + 'Qx', 'Cn' or 'An')\n");
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+#endif
+ if (qh DELAUNAY && qh KEEPcoplanar && !qh KEEPinside) {
+ qh KEEPinside= True;
+ qh_option("Qinterior-keep", NULL, NULL);
+ }
+ if (qh DELAUNAY && qh HALFspace) {
+ qh_fprintf(qh ferr, 6046, "qhull input error: can not use Delaunay('d') or Voronoi('v') with halfspace intersection('H')\n");
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ if (!qh DELAUNAY && (qh UPPERdelaunay || qh ATinfinity)) {
+ qh_fprintf(qh ferr, 6047, "qhull input error: use upper-Delaunay('Qu') or infinity-point('Qz') with Delaunay('d') or Voronoi('v')\n");
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ if (qh UPPERdelaunay && qh ATinfinity) {
+ qh_fprintf(qh ferr, 6048, "qhull input error: can not use infinity-point('Qz') with upper-Delaunay('Qu')\n");
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ if (qh SCALElast && !qh DELAUNAY && qh PRINTprecision)
+ qh_fprintf(qh ferr, 7040, "qhull input warning: option 'Qbb' (scale-last-coordinate) is normally used with 'd' or 'v'\n");
+ qh DOcheckmax= (!qh SKIPcheckmax && qh MERGING );
+ qh KEEPnearinside= (qh DOcheckmax && !(qh KEEPinside && qh KEEPcoplanar)
+ && !qh NOnearinside);
+ if (qh MERGING)
+ qh CENTERtype= qh_AScentrum;
+ else if (qh VORONOI)
+ qh CENTERtype= qh_ASvoronoi;
+ if (qh TESTvneighbors && !qh MERGING) {
+ qh_fprintf(qh ferr, 6049, "qhull input error: test vertex neighbors('Qv') needs a merge option\n");
+ qh_errexit(qh_ERRinput, NULL ,NULL);
+ }
+ if (qh PROJECTinput || (qh DELAUNAY && qh PROJECTdelaunay)) {
+ qh hull_dim -= qh PROJECTinput;
+ if (qh DELAUNAY) {
+ qh hull_dim++;
+ if (qh ATinfinity)
+ extra= 1;
+ }
+ }
+ if (qh hull_dim <= 1) {
+ qh_fprintf(qh ferr, 6050, "qhull error: dimension %d must be > 1\n", qh hull_dim);
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ for (k=2, factorial=1.0; k < qh hull_dim; k++)
+ factorial *= k;
+ qh AREAfactor= 1.0 / factorial;
+ trace2((qh ferr, 2005, "qh_initqhull_globals: initialize globals. dim %d numpoints %d malloc? %d projected %d to hull_dim %d\n",
+ dim, numpoints, ismalloc, qh PROJECTinput, qh hull_dim));
+ qh normal_size= qh hull_dim * sizeof(coordT);
+ qh center_size= qh normal_size - sizeof(coordT);
+ pointsneeded= qh hull_dim+1;
+ if (qh hull_dim > qh_DIMmergeVertex) {
+ qh MERGEvertices= False;
+ qh_option("Q3-no-merge-vertices-dim-high", NULL, NULL);
+ }
+ if (qh GOODpoint)
+ pointsneeded++;
+#ifdef qh_NOtrace
+ if (qh IStracing) {
+ qh_fprintf(qh ferr, 6051, "qhull input error: tracing is not installed(qh_NOtrace in user.h)");
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+#endif
+ if (qh RERUN > 1) {
+ qh TRACElastrun= qh IStracing; /* qh_build_withrestart duplicates next conditional */
+ if (qh IStracing != -1)
+ qh IStracing= 0;
+ }else if (qh TRACEpoint != qh_IDunknown || qh TRACEdist < REALmax/2 || qh TRACEmerge) {
+ qh TRACElevel= (qh IStracing? qh IStracing : 3);
+ qh IStracing= 0;
+ }
+ if (qh ROTATErandom == 0 || qh ROTATErandom == -1) {
+ seed= (int)time(&timedata);
+ if (qh ROTATErandom == -1) {
+ seed= -seed;
+ qh_option("QRandom-seed", &seed, NULL );
+ }else
+ qh_option("QRotate-random", &seed, NULL);
+ qh ROTATErandom= seed;
+ }
+ seed= qh ROTATErandom;
+ if (seed == INT_MIN) /* default value */
+ seed= 1;
+ else if (seed < 0)
+ seed= -seed;
+ qh_RANDOMseed_(seed);
+ randr= 0.0;
+ for (i=1000; i--; ) {
+ randi= qh_RANDOMint;
+ randr += randi;
+ if (randi > qh_RANDOMmax) {
+ qh_fprintf(qh ferr, 8036, "\
+qhull configuration error (qh_RANDOMmax in user.h):\n\
+ random integer %d > qh_RANDOMmax(%.8g)\n",
+ randi, qh_RANDOMmax);
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ }
+ qh_RANDOMseed_(seed);
+ randr = randr/1000;
+ if (randr < qh_RANDOMmax * 0.1
+ || randr > qh_RANDOMmax * 0.9)
+ qh_fprintf(qh ferr, 8037, "\
+qhull configuration warning (qh_RANDOMmax in user.h):\n\
+ average of 1000 random integers (%.2g) is much different than expected (%.2g).\n\
+ Is qh_RANDOMmax (%.2g) wrong?\n",
+ randr, qh_RANDOMmax * 0.5, qh_RANDOMmax);
+ qh RANDOMa= 2.0 * qh RANDOMfactor/qh_RANDOMmax;
+ qh RANDOMb= 1.0 - qh RANDOMfactor;
+ if (qh_HASHfactor < 1.1) {
+ qh_fprintf(qh ferr, 6052, "qhull internal error (qh_initqhull_globals): qh_HASHfactor %d must be at least 1.1. Qhull uses linear hash probing\n",
+ qh_HASHfactor);
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ if (numpoints+extra < pointsneeded) {
+ qh_fprintf(qh ferr, 6214, "qhull input error: not enough points(%d) to construct initial simplex (need %d)\n",
+ numpoints, pointsneeded);
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ qh_initqhull_outputflags();
+} /* initqhull_globals */
+
+/*-<a href="qh-globa.htm#TOC"
+ >-------------------------------</a><a name="initqhull_mem">-</a>
+
+ qh_initqhull_mem( )
+ initialize mem.c for qhull
+ qh.hull_dim and qh.normal_size determine some of the allocation sizes
+ if qh.MERGING,
+ includes ridgeT
+ calls qh_user_memsizes() to add up to 10 additional sizes for quick allocation
+ (see numsizes below)
+
+ returns:
+ mem.c already for qh_memalloc/qh_memfree (errors if called beforehand)
+
+ notes:
+ qh_produceoutput() prints memsizes
+
+*/
+void qh_initqhull_mem(void) {
+ int numsizes;
+ int i;
+
+ numsizes= 8+10;
+ qh_meminitbuffers(qh IStracing, qh_MEMalign, numsizes,
+ qh_MEMbufsize,qh_MEMinitbuf);
+ qh_memsize((int)sizeof(vertexT));
+ if (qh MERGING) {
+ qh_memsize((int)sizeof(ridgeT));
+ qh_memsize((int)sizeof(mergeT));
+ }
+ qh_memsize((int)sizeof(facetT));
+ i= sizeof(setT) + (qh hull_dim - 1) * SETelemsize; /* ridge.vertices */
+ qh_memsize(i);
+ qh_memsize(qh normal_size); /* normal */
+ i += SETelemsize; /* facet.vertices, .ridges, .neighbors */
+ qh_memsize(i);
+ qh_user_memsizes();
+ qh_memsetup();
+} /* initqhull_mem */
+
+/*-<a href="qh-globa.htm#TOC"
+ >-------------------------------</a><a name="initqhull_outputflags">-</a>
+
+ qh_initqhull_outputflags
+ initialize flags concerned with output
+
+ returns:
+ adjust user flags as needed
+
+ see:
+ qh_clear_outputflags() resets the flags
+
+ design:
+ test for qh.PRINTgood (i.e., only print 'good' facets)
+ check for conflicting print output options
+*/
+void qh_initqhull_outputflags(void) {
+ boolT printgeom= False, printmath= False, printcoplanar= False;
+ int i;
+
+ trace3((qh ferr, 3024, "qh_initqhull_outputflags: %s\n", qh qhull_command));
+ if (!(qh PRINTgood || qh PRINTneighbors)) {
+ if (qh KEEParea || qh KEEPminArea < REALmax/2 || qh KEEPmerge || qh DELAUNAY
+ || (!qh ONLYgood && (qh GOODvertex || qh GOODpoint))) {
+ qh PRINTgood= True;
+ qh_option("Pgood", NULL, NULL);
+ }
+ }
+ if (qh PRINTtransparent) {
+ if (qh hull_dim != 4 || !qh DELAUNAY || qh VORONOI || qh DROPdim >= 0) {
+ qh_fprintf(qh ferr, 6215, "qhull input error: transparent Delaunay('Gt') needs 3-d Delaunay('d') w/o 'GDn'\n");
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ qh DROPdim = 3;
+ qh PRINTridges = True;
+ }
+ for (i=qh_PRINTEND; i--; ) {
+ if (qh PRINTout[i] == qh_PRINTgeom)
+ printgeom= True;
+ else if (qh PRINTout[i] == qh_PRINTmathematica || qh PRINTout[i] == qh_PRINTmaple)
+ printmath= True;
+ else if (qh PRINTout[i] == qh_PRINTcoplanars)
+ printcoplanar= True;
+ else if (qh PRINTout[i] == qh_PRINTpointnearest)
+ printcoplanar= True;
+ else if (qh PRINTout[i] == qh_PRINTpointintersect && !qh HALFspace) {
+ qh_fprintf(qh ferr, 6053, "qhull input error: option 'Fp' is only used for \nhalfspace intersection('Hn,n,n').\n");
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }else if (qh PRINTout[i] == qh_PRINTtriangles && (qh HALFspace || qh VORONOI)) {
+ qh_fprintf(qh ferr, 6054, "qhull input error: option 'Ft' is not available for Voronoi vertices or halfspace intersection\n");
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }else if (qh PRINTout[i] == qh_PRINTcentrums && qh VORONOI) {
+ qh_fprintf(qh ferr, 6055, "qhull input error: option 'FC' is not available for Voronoi vertices('v')\n");
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }else if (qh PRINTout[i] == qh_PRINTvertices) {
+ if (qh VORONOI)
+ qh_option("Fvoronoi", NULL, NULL);
+ else
+ qh_option("Fvertices", NULL, NULL);
+ }
+ }
+ if (printcoplanar && qh DELAUNAY && qh JOGGLEmax < REALmax/2) {
+ if (qh PRINTprecision)
+ qh_fprintf(qh ferr, 7041, "qhull input warning: 'QJ' (joggle) will usually prevent coincident input sites for options 'Fc' and 'FP'\n");
+ }
+ if (printmath && (qh hull_dim > 3 || qh VORONOI)) {
+ qh_fprintf(qh ferr, 6056, "qhull input error: Mathematica and Maple output is only available for 2-d and 3-d convex hulls and 2-d Delaunay triangulations\n");
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ if (printgeom) {
+ if (qh hull_dim > 4) {
+ qh_fprintf(qh ferr, 6057, "qhull input error: Geomview output is only available for 2-d, 3-d and 4-d\n");
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ if (qh PRINTnoplanes && !(qh PRINTcoplanar + qh PRINTcentrums
+ + qh PRINTdots + qh PRINTspheres + qh DOintersections + qh PRINTridges)) {
+ qh_fprintf(qh ferr, 6058, "qhull input error: no output specified for Geomview\n");
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ if (qh VORONOI && (qh hull_dim > 3 || qh DROPdim >= 0)) {
+ qh_fprintf(qh ferr, 6059, "qhull input error: Geomview output for Voronoi diagrams only for 2-d\n");
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ /* can not warn about furthest-site Geomview output: no lower_threshold */
+ if (qh hull_dim == 4 && qh DROPdim == -1 &&
+ (qh PRINTcoplanar || qh PRINTspheres || qh PRINTcentrums)) {
+ qh_fprintf(qh ferr, 7042, "qhull input warning: coplanars, vertices, and centrums output not\n\
+available for 4-d output(ignored). Could use 'GDn' instead.\n");
+ qh PRINTcoplanar= qh PRINTspheres= qh PRINTcentrums= False;
+ }
+ }
+ if (!qh KEEPcoplanar && !qh KEEPinside && !qh ONLYgood) {
+ if ((qh PRINTcoplanar && qh PRINTspheres) || printcoplanar) {
+ if (qh QHULLfinished) {
+ qh_fprintf(qh ferr, 7072, "qhull output warning: ignoring coplanar points, option 'Qc' was not set for the first run of qhull.\n");
+ }else {
+ qh KEEPcoplanar = True;
+ qh_option("Qcoplanar", NULL, NULL);
+ }
+ }
+ }
+ qh PRINTdim= qh hull_dim;
+ if (qh DROPdim >=0) { /* after Geomview checks */
+ if (qh DROPdim < qh hull_dim) {
+ qh PRINTdim--;
+ if (!printgeom || qh hull_dim < 3)
+ qh_fprintf(qh ferr, 7043, "qhull input warning: drop dimension 'GD%d' is only available for 3-d/4-d Geomview\n", qh DROPdim);
+ }else
+ qh DROPdim= -1;
+ }else if (qh VORONOI) {
+ qh DROPdim= qh hull_dim-1;
+ qh PRINTdim= qh hull_dim-1;
+ }
+} /* qh_initqhull_outputflags */
+
+/*-<a href="qh-globa.htm#TOC"
+ >-------------------------------</a><a name="initqhull_start">-</a>
+
+ qh_initqhull_start( infile, outfile, errfile )
+ allocate memory if needed and call qh_initqhull_start2()
+*/
+void qh_initqhull_start(FILE *infile, FILE *outfile, FILE *errfile) {
+
+#if qh_QHpointer
+ if (qh_qh) {
+ qh_fprintf(errfile, 6205, "qhull error (qh_initqhull_start): qh_qh already defined. Call qh_save_qhull() first\n");
+ qh_exit(qh_ERRqhull); /* no error handler */
+ }
+ if (!(qh_qh= (qhT *)qh_malloc(sizeof(qhT)))) {
+ qh_fprintf(errfile, 6060, "qhull error (qh_initqhull_start): insufficient memory\n");
+ qh_exit(qh_ERRmem); /* no error handler */
+ }
+#endif
+ qh_initstatistics();
+ qh_initqhull_start2(infile, outfile, errfile);
+} /* initqhull_start */
+
+/*-<a href="qh-globa.htm#TOC"
+ >-------------------------------</a><a name="initqhull_start2">-</a>
+
+ qh_initqhull_start2( infile, outfile, errfile )
+ start initialization of qhull
+ initialize statistics, stdio, default values for global variables
+ assumes qh_qh is defined
+ notes:
+ report errors elsewhere, error handling and g_qhull_output [Qhull.cpp, QhullQh()] not in initialized
+ see:
+ qh_maxmin() determines the precision constants
+ qh_freeqhull2()
+*/
+void qh_initqhull_start2(FILE *infile, FILE *outfile, FILE *errfile) {
+ time_t timedata;
+ int seed;
+
+ qh_CPUclock; /* start the clock(for qh_clock). One-shot. */
+#if qh_QHpointer
+ memset((char *)qh_qh, 0, sizeof(qhT)); /* every field is 0, FALSE, NULL */
+#else
+ memset((char *)&qh_qh, 0, sizeof(qhT));
+#endif
+ qh ANGLEmerge= True;
+ qh DROPdim= -1;
+ qh ferr= errfile;
+ qh fin= infile;
+ qh fout= outfile;
+ qh furthest_id= qh_IDunknown;
+ qh JOGGLEmax= REALmax;
+ qh KEEPminArea = REALmax;
+ qh last_low= REALmax;
+ qh last_high= REALmax;
+ qh last_newhigh= REALmax;
+ qh max_outside= 0.0;
+ qh max_vertex= 0.0;
+ qh MAXabs_coord= 0.0;
+ qh MAXsumcoord= 0.0;
+ qh MAXwidth= -REALmax;
+ qh MERGEindependent= True;
+ qh MINdenom_1= fmax_(1.0/REALmax, REALmin); /* used by qh_scalepoints */
+ qh MINoutside= 0.0;
+ qh MINvisible= REALmax;
+ qh MAXcoplanar= REALmax;
+ qh outside_err= REALmax;
+ qh premerge_centrum= 0.0;
+ qh premerge_cos= REALmax;
+ qh PRINTprecision= True;
+ qh PRINTradius= 0.0;
+ qh postmerge_cos= REALmax;
+ qh postmerge_centrum= 0.0;
+ qh ROTATErandom= INT_MIN;
+ qh MERGEvertices= True;
+ qh totarea= 0.0;
+ qh totvol= 0.0;
+ qh TRACEdist= REALmax;
+ qh TRACEpoint= qh_IDunknown; /* recompile or use 'TPn' */
+ qh tracefacet_id= UINT_MAX; /* recompile to trace a facet */
+ qh tracevertex_id= UINT_MAX; /* recompile to trace a vertex */
+ seed= (int)time(&timedata);
+ qh_RANDOMseed_(seed);
+ qh run_id= qh_RANDOMint;
+ if(!qh run_id)
+ qh run_id++; /* guarantee non-zero */
+ qh_option("run-id", &qh run_id, NULL);
+ strcat(qh qhull, "qhull");
+} /* initqhull_start2 */
+
+/*-<a href="qh-globa.htm#TOC"
+ >-------------------------------</a><a name="initthresholds">-</a>
+
+ qh_initthresholds( commandString )
+ set thresholds for printing and scaling from commandString
+
+ returns:
+ sets qh.GOODthreshold or qh.SPLITthreshold if 'Pd0D1' used
+
+ see:
+ qh_initflags(), 'Qbk' 'QBk' 'Pdk' and 'PDk'
+ qh_inthresholds()
+
+ design:
+ for each 'Pdn' or 'PDn' option
+ check syntax
+ set qh.lower_threshold or qh.upper_threshold
+ set qh.GOODthreshold if an unbounded threshold is used
+ set qh.SPLITthreshold if a bounded threshold is used
+*/
+void qh_initthresholds(char *command) {
+ realT value;
+ int idx, maxdim, k;
+ char *s= command; /* non-const due to strtol */
+ char key;
+
+ maxdim= qh input_dim;
+ if (qh DELAUNAY && (qh PROJECTdelaunay || qh PROJECTinput))
+ maxdim++;
+ while (*s) {
+ if (*s == '-')
+ s++;
+ if (*s == 'P') {
+ s++;
+ while (*s && !isspace(key= *s++)) {
+ if (key == 'd' || key == 'D') {
+ if (!isdigit(*s)) {
+ qh_fprintf(qh ferr, 7044, "qhull warning: no dimension given for Print option '%c' at: %s. Ignored\n",
+ key, s-1);
+ continue;
+ }
+ idx= qh_strtol(s, &s);
+ if (idx >= qh hull_dim) {
+ qh_fprintf(qh ferr, 7045, "qhull warning: dimension %d for Print option '%c' is >= %d. Ignored\n",
+ idx, key, qh hull_dim);
+ continue;
+ }
+ if (*s == ':') {
+ s++;
+ value= qh_strtod(s, &s);
+ if (fabs((double)value) > 1.0) {
+ qh_fprintf(qh ferr, 7046, "qhull warning: value %2.4g for Print option %c is > +1 or < -1. Ignored\n",
+ value, key);
+ continue;
+ }
+ }else
+ value= 0.0;
+ if (key == 'd')
+ qh lower_threshold[idx]= value;
+ else
+ qh upper_threshold[idx]= value;
+ }
+ }
+ }else if (*s == 'Q') {
+ s++;
+ while (*s && !isspace(key= *s++)) {
+ if (key == 'b' && *s == 'B') {
+ s++;
+ for (k=maxdim; k--; ) {
+ qh lower_bound[k]= -qh_DEFAULTbox;
+ qh upper_bound[k]= qh_DEFAULTbox;
+ }
+ }else if (key == 'b' && *s == 'b')
+ s++;
+ else if (key == 'b' || key == 'B') {
+ if (!isdigit(*s)) {
+ qh_fprintf(qh ferr, 7047, "qhull warning: no dimension given for Qhull option %c. Ignored\n",
+ key);
+ continue;
+ }
+ idx= qh_strtol(s, &s);
+ if (idx >= maxdim) {
+ qh_fprintf(qh ferr, 7048, "qhull warning: dimension %d for Qhull option %c is >= %d. Ignored\n",
+ idx, key, maxdim);
+ continue;
+ }
+ if (*s == ':') {
+ s++;
+ value= qh_strtod(s, &s);
+ }else if (key == 'b')
+ value= -qh_DEFAULTbox;
+ else
+ value= qh_DEFAULTbox;
+ if (key == 'b')
+ qh lower_bound[idx]= value;
+ else
+ qh upper_bound[idx]= value;
+ }
+ }
+ }else {
+ while (*s && !isspace(*s))
+ s++;
+ }
+ while (isspace(*s))
+ s++;
+ }
+ for (k=qh hull_dim; k--; ) {
+ if (qh lower_threshold[k] > -REALmax/2) {
+ qh GOODthreshold= True;
+ if (qh upper_threshold[k] < REALmax/2) {
+ qh SPLITthresholds= True;
+ qh GOODthreshold= False;
+ break;
+ }
+ }else if (qh upper_threshold[k] < REALmax/2)
+ qh GOODthreshold= True;
+ }
+} /* initthresholds */
+
+/*-<a href="qh-globa.htm#TOC"
+ >-------------------------------</a><a name="lib_check">-</a>
+
+ qh_lib_check( qhullLibraryType, qhTsize, vertexTsize, ridgeTsize, facetTsize, setTsize, qhmemTsize )
+ Report error if library does not agree with caller
+
+ notes:
+ NOerrors -- qh_lib_check can not call qh_errexit()
+*/
+void qh_lib_check(int qhullLibraryType, int qhTsize, int vertexTsize, int ridgeTsize, int facetTsize, int setTsize, int qhmemTsize) {
+ boolT iserror= False;
+
+#if defined(_MSC_VER) && defined(_DEBUG) && defined(QHULL_CRTDBG) /* user_r.h */
+ // _CrtSetBreakAlloc(744); /* Break at memalloc {744}, or 'watch' _crtBreakAlloc */
+ _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_DELAY_FREE_MEM_DF | _CRTDBG_LEAK_CHECK_DF | _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) );
+ _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG );
+ _CrtSetReportFile( _CRT_ERROR, _CRTDBG_FILE_STDERR );
+ _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG );
+ _CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDERR );
+ _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG );
+ _CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDERR );
+#endif
+
+ if (qhullLibraryType==QHULL_NON_REENTRANT) { /* 0 */
+ if (qh_QHpointer) {
+ qh_fprintf_stderr(6246, "qh_lib_check: Incorrect qhull library called. Caller uses a static qhT while library uses a dynamic qhT via qh_QHpointer. Both caller and library are non-reentrant.\n");
+ iserror= True;
+ }
+ }else if (qhullLibraryType==QHULL_QH_POINTER) { /* 1 */
+ if (!qh_QHpointer) {
+ qh_fprintf_stderr(6247, "qh_lib_check: Incorrect qhull library called. Caller uses a dynamic qhT via qh_QHpointer while library uses a static qhT. Both caller and library are non-reentrant.\n");
+ iserror= True;
+ }
+ }else if (qhullLibraryType==QHULL_REENTRANT) { /* 2 */
+ qh_fprintf_stderr(6248, "qh_lib_check: Incorrect qhull library called. Caller uses reentrant Qhull while library is non-reentrant\n");
+ iserror= True;
+ }else{
+ qh_fprintf_stderr(6262, "qh_lib_check: Expecting qhullLibraryType QHULL_NON_REENTRANT(0), QHULL_QH_POINTER(1), or QHULL_REENTRANT(2). Got %d\n", qhullLibraryType);
+ iserror= True;
+ }
+ if (qhTsize != sizeof(qhT)) {
+ qh_fprintf_stderr(6249, "qh_lib_check: Incorrect qhull library called. Size of qhT for caller is %d, but for library is %d.\n", qhTsize, sizeof(qhT));
+ iserror= True;
+ }
+ if (vertexTsize != sizeof(vertexT)) {
+ qh_fprintf_stderr(6250, "qh_lib_check: Incorrect qhull library called. Size of vertexT for caller is %d, but for library is %d.\n", vertexTsize, sizeof(vertexT));
+ iserror= True;
+ }
+ if (ridgeTsize != sizeof(ridgeT)) {
+ qh_fprintf_stderr(6251, "qh_lib_check: Incorrect qhull library called. Size of ridgeT for caller is %d, but for library is %d.\n", ridgeTsize, sizeof(ridgeT));
+ iserror= True;
+ }
+ if (facetTsize != sizeof(facetT)) {
+ qh_fprintf_stderr(6252, "qh_lib_check: Incorrect qhull library called. Size of facetT for caller is %d, but for library is %d.\n", facetTsize, sizeof(facetT));
+ iserror= True;
+ }
+ if (setTsize && setTsize != sizeof(setT)) {
+ qh_fprintf_stderr(6253, "qh_lib_check: Incorrect qhull library called. Size of setT for caller is %d, but for library is %d.\n", setTsize, sizeof(setT));
+ iserror= True;
+ }
+ if (qhmemTsize && qhmemTsize != sizeof(qhmemT)) {
+ qh_fprintf_stderr(6254, "qh_lib_check: Incorrect qhull library called. Size of qhmemT for caller is %d, but for library is %d.\n", qhmemTsize, sizeof(qhmemT));
+ iserror= True;
+ }
+ if (iserror) {
+ if(qh_QHpointer){
+ qh_fprintf_stderr(6255, "qh_lib_check: Cannot continue. Library '%s' uses a dynamic qhT via qh_QHpointer (e.g., qhull_p.so)\n", qh_version2);
+ }else{
+ qh_fprintf_stderr(6256, "qh_lib_check: Cannot continue. Library '%s' uses a static qhT (e.g., libqhull.so)\n", qh_version2);
+ }
+ qh_exit(qh_ERRqhull); /* can not use qh_errexit() */
+ }
+} /* lib_check */
+
+/*-<a href="qh-globa.htm#TOC"
+ >-------------------------------</a><a name="option">-</a>
+
+ qh_option( option, intVal, realVal )
+ add an option description to qh.qhull_options
+
+ notes:
+ NOerrors -- qh_option can not call qh_errexit() [qh_initqhull_start2]
+ will be printed with statistics ('Ts') and errors
+ strlen(option) < 40
+*/
+void qh_option(const char *option, int *i, realT *r) {
+ char buf[200];
+ int len, maxlen;
+
+ sprintf(buf, " %s", option);
+ if (i)
+ sprintf(buf+strlen(buf), " %d", *i);
+ if (r)
+ sprintf(buf+strlen(buf), " %2.2g", *r);
+ len= (int)strlen(buf); /* WARN64 */
+ qh qhull_optionlen += len;
+ maxlen= sizeof(qh qhull_options) - len -1;
+ maximize_(maxlen, 0);
+ if (qh qhull_optionlen >= qh_OPTIONline && maxlen > 0) {
+ qh qhull_optionlen= len;
+ strncat(qh qhull_options, "\n", (size_t)(maxlen--));
+ }
+ strncat(qh qhull_options, buf, (size_t)maxlen);
+} /* option */
+
+#if qh_QHpointer
+/*-<a href="qh-globa.htm#TOC"
+ >-------------------------------</a><a name="restore_qhull">-</a>
+
+ qh_restore_qhull( oldqh )
+ restores a previously saved qhull
+ also restores qh_qhstat and qhmem.tempstack
+ Sets *oldqh to NULL
+ notes:
+ errors if current qhull hasn't been saved or freed
+ uses qhmem for error reporting
+
+ NOTE 1998/5/11:
+ Freeing memory after qh_save_qhull and qh_restore_qhull
+ is complicated. The procedures will be redesigned.
+
+ see:
+ qh_save_qhull(), UsingLibQhull
+*/
+void qh_restore_qhull(qhT **oldqh) {
+
+ if (*oldqh && strcmp((*oldqh)->qhull, "qhull")) {
+ qh_fprintf(qhmem.ferr, 6061, "qhull internal error (qh_restore_qhull): %p is not a qhull data structure\n",
+ *oldqh);
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ if (qh_qh) {
+ qh_fprintf(qhmem.ferr, 6062, "qhull internal error (qh_restore_qhull): did not save or free existing qhull\n");
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ if (!*oldqh || !(*oldqh)->old_qhstat) {
+ qh_fprintf(qhmem.ferr, 6063, "qhull internal error (qh_restore_qhull): did not previously save qhull %p\n",
+ *oldqh);
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ qh_qh= *oldqh;
+ *oldqh= NULL;
+ qh_qhstat= qh old_qhstat;
+ qhmem.tempstack= qh old_tempstack;
+ qh old_qhstat= 0;
+ qh old_tempstack= 0;
+ trace1((qh ferr, 1007, "qh_restore_qhull: restored qhull from %p\n", *oldqh));
+} /* restore_qhull */
+
+/*-<a href="qh-globa.htm#TOC"
+ >-------------------------------</a><a name="save_qhull">-</a>
+
+ qh_save_qhull( )
+ saves qhull for a later qh_restore_qhull
+ also saves qh_qhstat and qhmem.tempstack
+
+ returns:
+ qh_qh=NULL
+
+ notes:
+ need to initialize qhull or call qh_restore_qhull before continuing
+
+ NOTE 1998/5/11:
+ Freeing memory after qh_save_qhull and qh_restore_qhull
+ is complicated. The procedures will be redesigned.
+
+ see:
+ qh_restore_qhull()
+*/
+qhT *qh_save_qhull(void) {
+ qhT *oldqh;
+
+ trace1((qhmem.ferr, 1045, "qh_save_qhull: save qhull %p\n", qh_qh));
+ if (!qh_qh) {
+ qh_fprintf(qhmem.ferr, 6064, "qhull internal error (qh_save_qhull): qhull not initialized\n");
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ qh old_qhstat= qh_qhstat;
+ qh_qhstat= NULL;
+ qh old_tempstack= qhmem.tempstack;
+ qhmem.tempstack= NULL;
+ oldqh= qh_qh;
+ qh_qh= NULL;
+ return oldqh;
+} /* save_qhull */
+
+#endif
+
diff --git a/xs/src/qhull/src/libqhull/index.htm b/xs/src/qhull/src/libqhull/index.htm
new file mode 100644
index 000000000..62b9d9970
--- /dev/null
+++ b/xs/src/qhull/src/libqhull/index.htm
@@ -0,0 +1,264 @@
+<!-- Do not edit with Front Page, it adds too many spaces -->
+<html>
+<head>
+<meta http-equiv="Content-Type"
+content="text/html; charset=iso-8859-1">
+<title>Qhull functions, macros, and data structures</title>
+</head>
+
+<body>
+<!-- Navigation links -->
+<p><a name="TOP"><b>Up:</b></a> <a
+href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual</a>: Table of Contents <br>
+<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a>
+&#149; <a href="../../html/qh-quick.htm#options">Options</a>
+&#149; <a href="../../html/qh-opto.htm#output">Output</a>
+&#149; <a href="../../html/qh-optf.htm#format">Formats</a>
+&#149; <a href="../../html/qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="../../html/qh-optp.htm#print">Print</a>
+&#149; <a href="../../html/qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="../../html/qh-optc.htm#prec">Precision</a>
+&#149; <a href="../../html/qh-optt.htm#trace">Trace</a>
+&#149; <a href="index.htm">Functions</a><br>
+<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code</a><br>
+<b>To:</b> <a href="#TOC">Qhull files</a><br>
+<b>To:</b> <a href="qh-geom.htm">Geom</a> &#149; <a href="qh-globa.htm">Global</a>
+&#149; <a href="qh-io.htm">Io</a> &#149; <a href="qh-mem.htm">Mem</a>
+&#149; <a href="qh-merge.htm">Merge</a> &#149; <a href="qh-poly.htm">Poly</a>
+&#149; <a href="qh-qhull.htm">Qhull</a> &#149; <a href="qh-set.htm">Set</a>
+&#149; <a href="qh-stat.htm">Stat</a> &#149; <a href="qh-user.htm">User</a>
+
+<hr>
+<!-- Main text of document. -->
+
+<h1>Qhull functions, macros, and data structures</h1>
+<blockquote>
+<p>The following sections provide an overview and index to
+Qhull's functions, macros, and data structures.
+Each section starts with an introduction.
+See also <a href=../../html/qh-code.htm#library>Calling
+Qhull from C programs</a> and <a href="../../html/qh-code.htm#cpp">Calling Qhull from C++ programs</a>.</p>
+
+<p>Qhull uses the following conventions:</p>
+<blockquote>
+
+<ul>
+<li>in code, global variables start with &quot;qh &quot;
+<li>in documentation, global variables start with 'qh.'
+<li>constants start with an upper case word
+<li>important globals include an '_'
+<li>functions, macros, and constants start with &quot;qh_&quot;</li>
+<li>data types end in &quot;T&quot;</li>
+<li>macros with arguments end in &quot;_&quot;</li>
+<li>iterators are macros that use local variables</li>
+<li>iterators for sets start with &quot;FOREACH&quot;</li>
+<li>iterators for lists start with &quot;FORALL&quot;</li>
+<li>qhull options are in single quotes (e.g., 'Pdn')</li>
+<li>lists are sorted alphabetically</li>
+<li>preprocessor directives on left margin for older compilers</li>
+</ul>
+</blockquote>
+<p>
+When reading the code, please note that the
+global data structure, 'qh', is a macro. It
+either expands to &quot;qh_qh.&quot; or to
+&quot;qh_qh-&gt;&quot;. The later is used for
+applications which run concurrent calls to qh_qhull().
+<p>
+When reading code with an editor, a search for
+'<i>&quot;function</i>'
+will locate the header of <i>qh_function</i>. A search for '<i>* function</i>'
+will locate the tail of <i>qh_function</i>.
+
+<p>A useful starting point is <a href="libqhull.h">libqhull.h</a>. It defines most
+of Qhull data structures and top-level functions. Search for <i>'PFn'</i> to
+determine the corresponding constant in Qhull. Search for <i>'Fp'</i> to
+determine the corresponding <a href="libqhull.h#qh_PRINT">qh_PRINT...</a> constant.
+Search <a href="io.c">io.c</a> to learn how the print function is implemented.</p>
+
+<p>If your web browser is configured for .c and .h files, the function, macro, and data type links
+go to the corresponding source location. To configure your web browser for .c and .h files.
+<ul>
+<li>In the Download Preferences or Options panel, add file extensions 'c' and 'h' to mime type 'text/html'.
+<li>Opera 12.10
+<ol>
+<li>In Tools > Preferences > Advanced > Downloads
+<li>Uncheck 'Hide file types opened with Opera'
+<li>Quick find 'html'
+<li>Select 'text/html' > Edit
+<li>Add File extensions 'c,h,'
+<li>Click 'OK'
+</ol>
+<li>Internet Explorer -- Mime types are not available from 'Internet Options'. Is there a registry key for these settings?
+<li>Firefox -- Mime types are not available from 'Preferences'. Is there an add-on to change the file extensions for a mime type?
+<li>Chrome -- Can Chrome be configured?
+</ul>
+
+<p>
+Please report documentation and link errors
+to <a href="mailto:qhull-bug@qhull.org">qhull-bug@qhull.org</a>.
+</blockquote>
+
+<p><b>Copyright &copy; 1997-2015 C.B. Barber</b></p>
+
+<hr>
+
+<h2><a href="#TOP">&#187;</a><a name="TOC">Qhull files</a> </h2>
+<blockquote>
+
+<p>This sections lists the .c and .h files for Qhull. Please
+refer to these files for detailed information.</p>
+<blockquote>
+
+<dl>
+<dt><a href="../../Makefile"><b>Makefile</b></a><b>, </b><a href="../../CMakeLists.txt"><b>CMakeLists.txt</b></a></dt>
+<dd><tt>Makefile</tt> is preconfigured for gcc. <tt>CMakeLists.txt</tt> supports multiple
+platforms with <a href=http://www.cmake.org/>CMake</a>.
+Qhull includes project files for Visual Studio and Qt.
+</dd>
+
+<dt>&nbsp;</dt>
+<dt><a href="libqhull.h"><b>libqhull.h</b></a> </dt>
+<dd>Include file for the Qhull library (<tt>libqhull.so</tt>, <tt>qhull.dll</tt>, <tt>libqhullstatic.a</tt>).
+Data structures are documented under <a href="qh-poly.htm">Poly</a>.
+Global variables are documented under <a href="qh-globa.htm">Global</a>.
+Other data structures and variables are documented under
+<a href="qh-qhull.htm#TOC">Qhull</a> or <a href="qh-geom.htm"><b>Geom</b></a><b>.</b></dd>
+
+<dt>&nbsp;</dt>
+<dt><a href="qh-geom.htm"><b>Geom</b></a><b>, </b>
+<a href="geom.h"><b>geom.h</b></a><b>, </b>
+<a href="geom.c"><b>geom.c</b></a><b>, </b>
+<a href="geom2.c"><b>geom2.c</b></a><b>, </b>
+<a href="random.c"><b>random.c</b></a><b>, </b>
+<a href="random.h"><b>random.h</b></a></dt>
+<dd>Geometric routines. These routines implement mathematical
+functions such as Gaussian elimination and geometric
+routines needed for Qhull. Frequently used routines are
+in <tt>geom.c</tt> while infrequent ones are in <tt>geom2.c</tt>.
+</dd>
+
+<dt>&nbsp;</dt>
+<dt><a href="qh-globa.htm"><b>Global</b></a><b>, </b>
+<a href="global.c"><b>global.c</b></a><b>, </b>
+<a href="libqhull.h"><b>libqhull.h</b></a> </dt>
+<dd>Global routines. Qhull uses a global data structure, <tt>qh</tt>,
+to store globally defined constants, lists, sets, and
+variables.
+<tt>global.c</tt> initializes and frees these
+structures. </dd>
+
+<dt>&nbsp;</dt>
+<dt><a href="qh-io.htm"><b>Io</b></a><b>, </b><a href="io.h"><b>io.h</b></a><b>,
+</b><a href="io.c"><b>io.c</b></a> </dt>
+<dd>Input and output routines. Qhull provides a wide range of
+input and output options.</dd>
+
+<dt>&nbsp;</dt>
+<dt><a href="qh-mem.htm"><b>Mem</b></a><b>, </b>
+<a href="mem.h"><b>mem.h</b></a><b>, </b>
+<a href="mem.c"><b>mem.c</b></a> </dt>
+<dd>Memory routines. Qhull provides memory allocation and
+deallocation. It uses quick-fit allocation.</dd>
+
+<dt>&nbsp;</dt>
+<dt><a href="qh-merge.htm"><b>Merge</b></a><b>, </b>
+<a href="merge.h"><b>merge.h</b></a><b>, </b>
+<a href="merge.c"><b>merge.c</b></a> </dt>
+<dd>Merge routines. Qhull handles precision problems by
+merged facets or joggled input. These routines merge simplicial facets,
+merge non-simplicial facets, merge cycles of facets, and
+rename redundant vertices.</dd>
+
+<dt>&nbsp;</dt>
+<dt><a href="qh-poly.htm"><b>Poly</b></a><b>, </b>
+<a href="poly.h"><b>poly.h</b></a><b>, </b>
+<a href="poly.c"><b>poly.c</b></a><b>, </b>
+<a href="poly2.c"><b>poly2.c</b></a><b>, </b>
+<a href="libqhull.h"><b>libqhull.h</b></a> </dt>
+<dd>Polyhedral routines. Qhull produces a polyhedron as a
+list of facets with vertices, neighbors, ridges, and
+geometric information. <tt>libqhull.h</tt> defines the main
+data structures. Frequently used routines are in <tt>poly.c</tt>
+while infrequent ones are in <tt>poly2.c</tt>.</dd>
+
+<dt>&nbsp;</dt>
+<dt><a href="qh-qhull.htm#TOC"><b>Qhull</b></a><b>, </b>
+<a href="libqhull.c"><b>libqhull.c</b></a><b>, </b>
+<a href="libqhull.h"><b>libqhull.h</b></a><b>, </b>
+<a href="qhull_a.h"><b>qhull_a.h</b></a><b>, </b>
+<a href="../qhullo/unix.c"><b>unix.c</b></a> <b>, </b>
+<a href="../qconvex/qconvex.c"><b>qconvex.c</b></a> <b>, </b>
+<a href="../qdelaunay/qdelaun.c"><b>qdelaun.c</b></a> <b>, </b>
+<a href="../qhalf/qhalf.c"><b>qhalf.c</b></a> <b>, </b>
+<a href="../qvoronoi/qvoronoi.c"><b>qvoronoi.c</b></a> </dt>
+<dd>Top-level routines. The Quickhull algorithm is
+implemented by <tt>libqhull.c</tt>. <tt>qhull_a.h</tt>
+includes all header files. </dd>
+
+<dt>&nbsp;</dt>
+<dt><a href="qh-set.htm"><b>Set</b></a><b>, </b>
+<a href="qset.h"><b>qset.h</b></a><b>, </b>
+<a href="qset.c"><b>qset.c</b></a> </dt>
+<dd>Set routines. Qhull implements its data structures as
+sets. A set is an array of pointers that is expanded as
+needed. This is a separate package that may be used in
+other applications. </dd>
+
+<dt>&nbsp;</dt>
+<dt><a href="qh-stat.htm"><b>Stat</b></a><b>, </b>
+<a href="stat.h"><b>stat.h</b></a><b>, </b>
+<a href="stat.c"><b>stat.c</b></a> </dt>
+<dd>Statistical routines. Qhull maintains statistics about
+its implementation. </dd>
+
+<dt>&nbsp;</dt>
+<dt><a href="qh-user.htm"><b>User</b></a><b>, </b>
+<a href="user.h"><b>user.h</b></a><b>, </b>
+<a href="user.c"><b>user.c</b></a><b>, </b>
+<a href="../user_eg/user_eg.c"><b>user_eg.c</b></a><b>, </b>
+<a href="../user_eg2/user_eg2.c"><b>user_eg2.c</b></a><b>, </b>
+</dt>
+<dd>User-defined routines. Qhull allows the user to configure
+the code with defined constants and specialized routines.
+</dd>
+</dl>
+</blockquote>
+
+</blockquote>
+<p><!-- Navigation links --> </p>
+<hr>
+<p><b>Up:</b>
+<a href="http://www.qhull.org">Home page for
+Qhull</a> <br>
+<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual: Table of Contents</a> <br>
+<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a>
+&#149; <a href="../../html/qh-quick.htm#options">Options</a>
+&#149; <a href="../../html/qh-opto.htm#output">Output</a>
+&#149; <a href="../../html/qh-optf.htm#format">Formats</a>
+&#149; <a href="../../html/qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="../../html/qh-optp.htm#print">Print</a>
+&#149; <a href="../../html/qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="../../html/qh-optc.htm#prec">Precision</a>
+&#149; <a href="../../html/qh-optt.htm#trace">Trace</a>
+&#149; <a href="index.htm">Functions</a><br>
+<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a> <br>
+<b>To:</b> <a href="#TOC">Qhull files</a><br>
+<b>To:</b> <a href="qh-geom.htm">Geom</a> &#149;
+<a href="qh-globa.htm">Global</a> &#149; <a href="qh-io.htm">Io</a>
+&#149; <a href="qh-mem.htm">Mem</a> &#149; <a href="qh-merge.htm">Merge</a>
+&#149; <a href="qh-poly.htm">Poly</a> &#149; <a href="qh-qhull.htm#TOC">Qhull</a>
+&#149; <a href="qh-set.htm">Set</a> &#149; <a href="qh-stat.htm">Stat</a>
+&#149; <a href="qh-user.htm">User</a><br>
+
+<p><!-- GC common information --> </p>
+<hr>
+<p><a href="http://www.geom.uiuc.edu/"><img
+src="../../html/qh--geom.gif" align="middle" width="40" height="40"></a><i>The
+Geometry Center Home Page </i></p>
+<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a>
+</a><br>
+Created: May 2, 1997 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p>
+</body>
+</html>
diff --git a/xs/src/qhull/src/libqhull/io.c b/xs/src/qhull/src/libqhull/io.c
new file mode 100644
index 000000000..401987ec0
--- /dev/null
+++ b/xs/src/qhull/src/libqhull/io.c
@@ -0,0 +1,4062 @@
+/*<html><pre> -<a href="qh-io.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ io.c
+ Input/Output routines of qhull application
+
+ see qh-io.htm and io.h
+
+ see user.c for qh_errprint and qh_printfacetlist
+
+ unix.c calls qh_readpoints and qh_produce_output
+
+ unix.c and user.c are the only callers of io.c functions
+ This allows the user to avoid loading io.o from qhull.a
+
+ Copyright (c) 1993-2015 The Geometry Center.
+ $Id: //main/2015/qhull/src/libqhull/io.c#5 $$Change: 2064 $
+ $DateTime: 2016/01/18 12:36:08 $$Author: bbarber $
+*/
+
+#include "qhull_a.h"
+
+/*========= -functions in alphabetical order after qh_produce_output() =====*/
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="produce_output">-</a>
+
+ qh_produce_output()
+ qh_produce_output2()
+ prints out the result of qhull in desired format
+ qh_produce_output2() does not call qh_prepare_output()
+ if qh.GETarea
+ computes and prints area and volume
+ qh.PRINTout[] is an array of output formats
+
+ notes:
+ prints output in qh.PRINTout order
+*/
+void qh_produce_output(void) {
+ int tempsize= qh_setsize(qhmem.tempstack);
+
+ qh_prepare_output();
+ qh_produce_output2();
+ if (qh_setsize(qhmem.tempstack) != tempsize) {
+ qh_fprintf(qh ferr, 6206, "qhull internal error (qh_produce_output): temporary sets not empty(%d)\n",
+ qh_setsize(qhmem.tempstack));
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+} /* produce_output */
+
+
+void qh_produce_output2(void) {
+ int i, tempsize= qh_setsize(qhmem.tempstack), d_1;
+
+ if (qh PRINTsummary)
+ qh_printsummary(qh ferr);
+ else if (qh PRINTout[0] == qh_PRINTnone)
+ qh_printsummary(qh fout);
+ for (i=0; i < qh_PRINTEND; i++)
+ qh_printfacets(qh fout, qh PRINTout[i], qh facet_list, NULL, !qh_ALL);
+ qh_allstatistics();
+ if (qh PRINTprecision && !qh MERGING && (qh JOGGLEmax > REALmax/2 || qh RERUN))
+ qh_printstats(qh ferr, qhstat precision, NULL);
+ if (qh VERIFYoutput && (zzval_(Zridge) > 0 || zzval_(Zridgemid) > 0))
+ qh_printstats(qh ferr, qhstat vridges, NULL);
+ if (qh PRINTstatistics) {
+ qh_printstatistics(qh ferr, "");
+ qh_memstatistics(qh ferr);
+ d_1= sizeof(setT) + (qh hull_dim - 1) * SETelemsize;
+ qh_fprintf(qh ferr, 8040, "\
+ size in bytes: merge %d ridge %d vertex %d facet %d\n\
+ normal %d ridge vertices %d facet vertices or neighbors %d\n",
+ (int)sizeof(mergeT), (int)sizeof(ridgeT),
+ (int)sizeof(vertexT), (int)sizeof(facetT),
+ qh normal_size, d_1, d_1 + SETelemsize);
+ }
+ if (qh_setsize(qhmem.tempstack) != tempsize) {
+ qh_fprintf(qh ferr, 6065, "qhull internal error (qh_produce_output2): temporary sets not empty(%d)\n",
+ qh_setsize(qhmem.tempstack));
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+} /* produce_output2 */
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="dfacet">-</a>
+
+ qh_dfacet( id )
+ print facet by id, for debugging
+
+*/
+void qh_dfacet(unsigned id) {
+ facetT *facet;
+
+ FORALLfacets {
+ if (facet->id == id) {
+ qh_printfacet(qh fout, facet);
+ break;
+ }
+ }
+} /* dfacet */
+
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="dvertex">-</a>
+
+ qh_dvertex( id )
+ print vertex by id, for debugging
+*/
+void qh_dvertex(unsigned id) {
+ vertexT *vertex;
+
+ FORALLvertices {
+ if (vertex->id == id) {
+ qh_printvertex(qh fout, vertex);
+ break;
+ }
+ }
+} /* dvertex */
+
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="compare_facetarea">-</a>
+
+ qh_compare_facetarea( p1, p2 )
+ used by qsort() to order facets by area
+*/
+int qh_compare_facetarea(const void *p1, const void *p2) {
+ const facetT *a= *((facetT *const*)p1), *b= *((facetT *const*)p2);
+
+ if (!a->isarea)
+ return -1;
+ if (!b->isarea)
+ return 1;
+ if (a->f.area > b->f.area)
+ return 1;
+ else if (a->f.area == b->f.area)
+ return 0;
+ return -1;
+} /* compare_facetarea */
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="compare_facetmerge">-</a>
+
+ qh_compare_facetmerge( p1, p2 )
+ used by qsort() to order facets by number of merges
+*/
+int qh_compare_facetmerge(const void *p1, const void *p2) {
+ const facetT *a= *((facetT *const*)p1), *b= *((facetT *const*)p2);
+
+ return(a->nummerge - b->nummerge);
+} /* compare_facetvisit */
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="compare_facetvisit">-</a>
+
+ qh_compare_facetvisit( p1, p2 )
+ used by qsort() to order facets by visit id or id
+*/
+int qh_compare_facetvisit(const void *p1, const void *p2) {
+ const facetT *a= *((facetT *const*)p1), *b= *((facetT *const*)p2);
+ int i,j;
+
+ if (!(i= a->visitid))
+ i= 0 - a->id; /* do not convert to int, sign distinguishes id from visitid */
+ if (!(j= b->visitid))
+ j= 0 - b->id;
+ return(i - j);
+} /* compare_facetvisit */
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="compare_vertexpoint">-</a>
+
+ qh_compare_vertexpoint( p1, p2 )
+ used by qsort() to order vertices by point id
+
+ Not used. Not available in libqhull_r.h since qh_pointid depends on qh
+*/
+int qh_compare_vertexpoint(const void *p1, const void *p2) {
+ const vertexT *a= *((vertexT *const*)p1), *b= *((vertexT *const*)p2);
+
+ return((qh_pointid(a->point) > qh_pointid(b->point)?1:-1));
+} /* compare_vertexpoint */
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="copyfilename">-</a>
+
+ qh_copyfilename( dest, size, source, length )
+ copy filename identified by qh_skipfilename()
+
+ notes:
+ see qh_skipfilename() for syntax
+*/
+void qh_copyfilename(char *filename, int size, const char* source, int length) {
+ char c= *source;
+
+ if (length > size + 1) {
+ qh_fprintf(qh ferr, 6040, "qhull error: filename is more than %d characters, %s\n", size-1, source);
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ strncpy(filename, source, length);
+ filename[length]= '\0';
+ if (c == '\'' || c == '"') {
+ char *s= filename + 1;
+ char *t= filename;
+ while (*s) {
+ if (*s == c) {
+ if (s[-1] == '\\')
+ t[-1]= c;
+ }else
+ *t++= *s;
+ s++;
+ }
+ *t= '\0';
+ }
+} /* copyfilename */
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="countfacets">-</a>
+
+ qh_countfacets( facetlist, facets, printall,
+ numfacets, numsimplicial, totneighbors, numridges, numcoplanar, numtricoplanars )
+ count good facets for printing and set visitid
+ if allfacets, ignores qh_skipfacet()
+
+ notes:
+ qh_printsummary and qh_countfacets must match counts
+
+ returns:
+ numfacets, numsimplicial, total neighbors, numridges, coplanars
+ each facet with ->visitid indicating 1-relative position
+ ->visitid==0 indicates not good
+
+ notes
+ numfacets >= numsimplicial
+ if qh.NEWfacets,
+ does not count visible facets (matches qh_printafacet)
+
+ design:
+ for all facets on facetlist and in facets set
+ unless facet is skipped or visible (i.e., will be deleted)
+ mark facet->visitid
+ update counts
+*/
+void qh_countfacets(facetT *facetlist, setT *facets, boolT printall,
+ int *numfacetsp, int *numsimplicialp, int *totneighborsp, int *numridgesp, int *numcoplanarsp, int *numtricoplanarsp) {
+ facetT *facet, **facetp;
+ int numfacets= 0, numsimplicial= 0, numridges= 0, totneighbors= 0, numcoplanars= 0, numtricoplanars= 0;
+
+ FORALLfacet_(facetlist) {
+ if ((facet->visible && qh NEWfacets)
+ || (!printall && qh_skipfacet(facet)))
+ facet->visitid= 0;
+ else {
+ facet->visitid= ++numfacets;
+ totneighbors += qh_setsize(facet->neighbors);
+ if (facet->simplicial) {
+ numsimplicial++;
+ if (facet->keepcentrum && facet->tricoplanar)
+ numtricoplanars++;
+ }else
+ numridges += qh_setsize(facet->ridges);
+ if (facet->coplanarset)
+ numcoplanars += qh_setsize(facet->coplanarset);
+ }
+ }
+
+ FOREACHfacet_(facets) {
+ if ((facet->visible && qh NEWfacets)
+ || (!printall && qh_skipfacet(facet)))
+ facet->visitid= 0;
+ else {
+ facet->visitid= ++numfacets;
+ totneighbors += qh_setsize(facet->neighbors);
+ if (facet->simplicial){
+ numsimplicial++;
+ if (facet->keepcentrum && facet->tricoplanar)
+ numtricoplanars++;
+ }else
+ numridges += qh_setsize(facet->ridges);
+ if (facet->coplanarset)
+ numcoplanars += qh_setsize(facet->coplanarset);
+ }
+ }
+ qh visit_id += numfacets+1;
+ *numfacetsp= numfacets;
+ *numsimplicialp= numsimplicial;
+ *totneighborsp= totneighbors;
+ *numridgesp= numridges;
+ *numcoplanarsp= numcoplanars;
+ *numtricoplanarsp= numtricoplanars;
+} /* countfacets */
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="detvnorm">-</a>
+
+ qh_detvnorm( vertex, vertexA, centers, offset )
+ compute separating plane of the Voronoi diagram for a pair of input sites
+ centers= set of facets (i.e., Voronoi vertices)
+ facet->visitid= 0 iff vertex-at-infinity (i.e., unbounded)
+
+ assumes:
+ qh_ASvoronoi and qh_vertexneighbors() already set
+
+ returns:
+ norm
+ a pointer into qh.gm_matrix to qh.hull_dim-1 reals
+ copy the data before reusing qh.gm_matrix
+ offset
+ if 'QVn'
+ sign adjusted so that qh.GOODvertexp is inside
+ else
+ sign adjusted so that vertex is inside
+
+ qh.gm_matrix= simplex of points from centers relative to first center
+
+ notes:
+ in io.c so that code for 'v Tv' can be removed by removing io.c
+ returns pointer into qh.gm_matrix to avoid tracking of temporary memory
+
+ design:
+ determine midpoint of input sites
+ build points as the set of Voronoi vertices
+ select a simplex from points (if necessary)
+ include midpoint if the Voronoi region is unbounded
+ relocate the first vertex of the simplex to the origin
+ compute the normalized hyperplane through the simplex
+ orient the hyperplane toward 'QVn' or 'vertex'
+ if 'Tv' or 'Ts'
+ if bounded
+ test that hyperplane is the perpendicular bisector of the input sites
+ test that Voronoi vertices not in the simplex are still on the hyperplane
+ free up temporary memory
+*/
+pointT *qh_detvnorm(vertexT *vertex, vertexT *vertexA, setT *centers, realT *offsetp) {
+ facetT *facet, **facetp;
+ int i, k, pointid, pointidA, point_i, point_n;
+ setT *simplex= NULL;
+ pointT *point, **pointp, *point0, *midpoint, *normal, *inpoint;
+ coordT *coord, *gmcoord, *normalp;
+ setT *points= qh_settemp(qh TEMPsize);
+ boolT nearzero= False;
+ boolT unbounded= False;
+ int numcenters= 0;
+ int dim= qh hull_dim - 1;
+ realT dist, offset, angle, zero= 0.0;
+
+ midpoint= qh gm_matrix + qh hull_dim * qh hull_dim; /* last row */
+ for (k=0; k < dim; k++)
+ midpoint[k]= (vertex->point[k] + vertexA->point[k])/2;
+ FOREACHfacet_(centers) {
+ numcenters++;
+ if (!facet->visitid)
+ unbounded= True;
+ else {
+ if (!facet->center)
+ facet->center= qh_facetcenter(facet->vertices);
+ qh_setappend(&points, facet->center);
+ }
+ }
+ if (numcenters > dim) {
+ simplex= qh_settemp(qh TEMPsize);
+ qh_setappend(&simplex, vertex->point);
+ if (unbounded)
+ qh_setappend(&simplex, midpoint);
+ qh_maxsimplex(dim, points, NULL, 0, &simplex);
+ qh_setdelnth(simplex, 0);
+ }else if (numcenters == dim) {
+ if (unbounded)
+ qh_setappend(&points, midpoint);
+ simplex= points;
+ }else {
+ qh_fprintf(qh ferr, 6216, "qhull internal error (qh_detvnorm): too few points(%d) to compute separating plane\n", numcenters);
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ i= 0;
+ gmcoord= qh gm_matrix;
+ point0= SETfirstt_(simplex, pointT);
+ FOREACHpoint_(simplex) {
+ if (qh IStracing >= 4)
+ qh_printmatrix(qh ferr, "qh_detvnorm: Voronoi vertex or midpoint",
+ &point, 1, dim);
+ if (point != point0) {
+ qh gm_row[i++]= gmcoord;
+ coord= point0;
+ for (k=dim; k--; )
+ *(gmcoord++)= *point++ - *coord++;
+ }
+ }
+ qh gm_row[i]= gmcoord; /* does not overlap midpoint, may be used later for qh_areasimplex */
+ normal= gmcoord;
+ qh_sethyperplane_gauss(dim, qh gm_row, point0, True,
+ normal, &offset, &nearzero);
+ if (qh GOODvertexp == vertexA->point)
+ inpoint= vertexA->point;
+ else
+ inpoint= vertex->point;
+ zinc_(Zdistio);
+ dist= qh_distnorm(dim, inpoint, normal, &offset);
+ if (dist > 0) {
+ offset= -offset;
+ normalp= normal;
+ for (k=dim; k--; ) {
+ *normalp= -(*normalp);
+ normalp++;
+ }
+ }
+ if (qh VERIFYoutput || qh PRINTstatistics) {
+ pointid= qh_pointid(vertex->point);
+ pointidA= qh_pointid(vertexA->point);
+ if (!unbounded) {
+ zinc_(Zdiststat);
+ dist= qh_distnorm(dim, midpoint, normal, &offset);
+ if (dist < 0)
+ dist= -dist;
+ zzinc_(Zridgemid);
+ wwmax_(Wridgemidmax, dist);
+ wwadd_(Wridgemid, dist);
+ trace4((qh ferr, 4014, "qh_detvnorm: points %d %d midpoint dist %2.2g\n",
+ pointid, pointidA, dist));
+ for (k=0; k < dim; k++)
+ midpoint[k]= vertexA->point[k] - vertex->point[k]; /* overwrites midpoint! */
+ qh_normalize(midpoint, dim, False);
+ angle= qh_distnorm(dim, midpoint, normal, &zero); /* qh_detangle uses dim+1 */
+ if (angle < 0.0)
+ angle= angle + 1.0;
+ else
+ angle= angle - 1.0;
+ if (angle < 0.0)
+ angle -= angle;
+ trace4((qh ferr, 4015, "qh_detvnorm: points %d %d angle %2.2g nearzero %d\n",
+ pointid, pointidA, angle, nearzero));
+ if (nearzero) {
+ zzinc_(Zridge0);
+ wwmax_(Wridge0max, angle);
+ wwadd_(Wridge0, angle);
+ }else {
+ zzinc_(Zridgeok)
+ wwmax_(Wridgeokmax, angle);
+ wwadd_(Wridgeok, angle);
+ }
+ }
+ if (simplex != points) {
+ FOREACHpoint_i_(points) {
+ if (!qh_setin(simplex, point)) {
+ facet= SETelemt_(centers, point_i, facetT);
+ zinc_(Zdiststat);
+ dist= qh_distnorm(dim, point, normal, &offset);
+ if (dist < 0)
+ dist= -dist;
+ zzinc_(Zridge);
+ wwmax_(Wridgemax, dist);
+ wwadd_(Wridge, dist);
+ trace4((qh ferr, 4016, "qh_detvnorm: points %d %d Voronoi vertex %d dist %2.2g\n",
+ pointid, pointidA, facet->visitid, dist));
+ }
+ }
+ }
+ }
+ *offsetp= offset;
+ if (simplex != points)
+ qh_settempfree(&simplex);
+ qh_settempfree(&points);
+ return normal;
+} /* detvnorm */
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="detvridge">-</a>
+
+ qh_detvridge( vertexA )
+ determine Voronoi ridge from 'seen' neighbors of vertexA
+ include one vertex-at-infinite if an !neighbor->visitid
+
+ returns:
+ temporary set of centers (facets, i.e., Voronoi vertices)
+ sorted by center id
+*/
+setT *qh_detvridge(vertexT *vertex) {
+ setT *centers= qh_settemp(qh TEMPsize);
+ setT *tricenters= qh_settemp(qh TEMPsize);
+ facetT *neighbor, **neighborp;
+ boolT firstinf= True;
+
+ FOREACHneighbor_(vertex) {
+ if (neighbor->seen) {
+ if (neighbor->visitid) {
+ if (!neighbor->tricoplanar || qh_setunique(&tricenters, neighbor->center))
+ qh_setappend(&centers, neighbor);
+ }else if (firstinf) {
+ firstinf= False;
+ qh_setappend(&centers, neighbor);
+ }
+ }
+ }
+ qsort(SETaddr_(centers, facetT), (size_t)qh_setsize(centers),
+ sizeof(facetT *), qh_compare_facetvisit);
+ qh_settempfree(&tricenters);
+ return centers;
+} /* detvridge */
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="detvridge3">-</a>
+
+ qh_detvridge3( atvertex, vertex )
+ determine 3-d Voronoi ridge from 'seen' neighbors of atvertex and vertex
+ include one vertex-at-infinite for !neighbor->visitid
+ assumes all facet->seen2= True
+
+ returns:
+ temporary set of centers (facets, i.e., Voronoi vertices)
+ listed in adjacency order (!oriented)
+ all facet->seen2= True
+
+ design:
+ mark all neighbors of atvertex
+ for each adjacent neighbor of both atvertex and vertex
+ if neighbor selected
+ add neighbor to set of Voronoi vertices
+*/
+setT *qh_detvridge3(vertexT *atvertex, vertexT *vertex) {
+ setT *centers= qh_settemp(qh TEMPsize);
+ setT *tricenters= qh_settemp(qh TEMPsize);
+ facetT *neighbor, **neighborp, *facet= NULL;
+ boolT firstinf= True;
+
+ FOREACHneighbor_(atvertex)
+ neighbor->seen2= False;
+ FOREACHneighbor_(vertex) {
+ if (!neighbor->seen2) {
+ facet= neighbor;
+ break;
+ }
+ }
+ while (facet) {
+ facet->seen2= True;
+ if (neighbor->seen) {
+ if (facet->visitid) {
+ if (!facet->tricoplanar || qh_setunique(&tricenters, facet->center))
+ qh_setappend(&centers, facet);
+ }else if (firstinf) {
+ firstinf= False;
+ qh_setappend(&centers, facet);
+ }
+ }
+ FOREACHneighbor_(facet) {
+ if (!neighbor->seen2) {
+ if (qh_setin(vertex->neighbors, neighbor))
+ break;
+ else
+ neighbor->seen2= True;
+ }
+ }
+ facet= neighbor;
+ }
+ if (qh CHECKfrequently) {
+ FOREACHneighbor_(vertex) {
+ if (!neighbor->seen2) {
+ qh_fprintf(qh ferr, 6217, "qhull internal error (qh_detvridge3): neighbors of vertex p%d are not connected at facet %d\n",
+ qh_pointid(vertex->point), neighbor->id);
+ qh_errexit(qh_ERRqhull, neighbor, NULL);
+ }
+ }
+ }
+ FOREACHneighbor_(atvertex)
+ neighbor->seen2= True;
+ qh_settempfree(&tricenters);
+ return centers;
+} /* detvridge3 */
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="eachvoronoi">-</a>
+
+ qh_eachvoronoi( fp, printvridge, vertex, visitall, innerouter, inorder )
+ if visitall,
+ visit all Voronoi ridges for vertex (i.e., an input site)
+ else
+ visit all unvisited Voronoi ridges for vertex
+ all vertex->seen= False if unvisited
+ assumes
+ all facet->seen= False
+ all facet->seen2= True (for qh_detvridge3)
+ all facet->visitid == 0 if vertex_at_infinity
+ == index of Voronoi vertex
+ >= qh.num_facets if ignored
+ innerouter:
+ qh_RIDGEall-- both inner (bounded) and outer(unbounded) ridges
+ qh_RIDGEinner- only inner
+ qh_RIDGEouter- only outer
+
+ if inorder
+ orders vertices for 3-d Voronoi diagrams
+
+ returns:
+ number of visited ridges (does not include previously visited ridges)
+
+ if printvridge,
+ calls printvridge( fp, vertex, vertexA, centers)
+ fp== any pointer (assumes FILE*)
+ vertex,vertexA= pair of input sites that define a Voronoi ridge
+ centers= set of facets (i.e., Voronoi vertices)
+ ->visitid == index or 0 if vertex_at_infinity
+ ordered for 3-d Voronoi diagram
+ notes:
+ uses qh.vertex_visit
+
+ see:
+ qh_eachvoronoi_all()
+
+ design:
+ mark selected neighbors of atvertex
+ for each selected neighbor (either Voronoi vertex or vertex-at-infinity)
+ for each unvisited vertex
+ if atvertex and vertex share more than d-1 neighbors
+ bump totalcount
+ if printvridge defined
+ build the set of shared neighbors (i.e., Voronoi vertices)
+ call printvridge
+*/
+int qh_eachvoronoi(FILE *fp, printvridgeT printvridge, vertexT *atvertex, boolT visitall, qh_RIDGE innerouter, boolT inorder) {
+ boolT unbounded;
+ int count;
+ facetT *neighbor, **neighborp, *neighborA, **neighborAp;
+ setT *centers;
+ setT *tricenters= qh_settemp(qh TEMPsize);
+
+ vertexT *vertex, **vertexp;
+ boolT firstinf;
+ unsigned int numfacets= (unsigned int)qh num_facets;
+ int totridges= 0;
+
+ qh vertex_visit++;
+ atvertex->seen= True;
+ if (visitall) {
+ FORALLvertices
+ vertex->seen= False;
+ }
+ FOREACHneighbor_(atvertex) {
+ if (neighbor->visitid < numfacets)
+ neighbor->seen= True;
+ }
+ FOREACHneighbor_(atvertex) {
+ if (neighbor->seen) {
+ FOREACHvertex_(neighbor->vertices) {
+ if (vertex->visitid != qh vertex_visit && !vertex->seen) {
+ vertex->visitid= qh vertex_visit;
+ count= 0;
+ firstinf= True;
+ qh_settruncate(tricenters, 0);
+ FOREACHneighborA_(vertex) {
+ if (neighborA->seen) {
+ if (neighborA->visitid) {
+ if (!neighborA->tricoplanar || qh_setunique(&tricenters, neighborA->center))
+ count++;
+ }else if (firstinf) {
+ count++;
+ firstinf= False;
+ }
+ }
+ }
+ if (count >= qh hull_dim - 1) { /* e.g., 3 for 3-d Voronoi */
+ if (firstinf) {
+ if (innerouter == qh_RIDGEouter)
+ continue;
+ unbounded= False;
+ }else {
+ if (innerouter == qh_RIDGEinner)
+ continue;
+ unbounded= True;
+ }
+ totridges++;
+ trace4((qh ferr, 4017, "qh_eachvoronoi: Voronoi ridge of %d vertices between sites %d and %d\n",
+ count, qh_pointid(atvertex->point), qh_pointid(vertex->point)));
+ if (printvridge && fp) {
+ if (inorder && qh hull_dim == 3+1) /* 3-d Voronoi diagram */
+ centers= qh_detvridge3(atvertex, vertex);
+ else
+ centers= qh_detvridge(vertex);
+ (*printvridge)(fp, atvertex, vertex, centers, unbounded);
+ qh_settempfree(&centers);
+ }
+ }
+ }
+ }
+ }
+ }
+ FOREACHneighbor_(atvertex)
+ neighbor->seen= False;
+ qh_settempfree(&tricenters);
+ return totridges;
+} /* eachvoronoi */
+
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="eachvoronoi_all">-</a>
+
+ qh_eachvoronoi_all( fp, printvridge, isUpper, innerouter, inorder )
+ visit all Voronoi ridges
+
+ innerouter:
+ see qh_eachvoronoi()
+
+ if inorder
+ orders vertices for 3-d Voronoi diagrams
+
+ returns
+ total number of ridges
+
+ if isUpper == facet->upperdelaunay (i.e., a Vornoi vertex)
+ facet->visitid= Voronoi vertex index(same as 'o' format)
+ else
+ facet->visitid= 0
+
+ if printvridge,
+ calls printvridge( fp, vertex, vertexA, centers)
+ [see qh_eachvoronoi]
+
+ notes:
+ Not used for qhull.exe
+ same effect as qh_printvdiagram but ridges not sorted by point id
+*/
+int qh_eachvoronoi_all(FILE *fp, printvridgeT printvridge, boolT isUpper, qh_RIDGE innerouter, boolT inorder) {
+ facetT *facet;
+ vertexT *vertex;
+ int numcenters= 1; /* vertex 0 is vertex-at-infinity */
+ int totridges= 0;
+
+ qh_clearcenters(qh_ASvoronoi);
+ qh_vertexneighbors();
+ maximize_(qh visit_id, (unsigned) qh num_facets);
+ FORALLfacets {
+ facet->visitid= 0;
+ facet->seen= False;
+ facet->seen2= True;
+ }
+ FORALLfacets {
+ if (facet->upperdelaunay == isUpper)
+ facet->visitid= numcenters++;
+ }
+ FORALLvertices
+ vertex->seen= False;
+ FORALLvertices {
+ if (qh GOODvertex > 0 && qh_pointid(vertex->point)+1 != qh GOODvertex)
+ continue;
+ totridges += qh_eachvoronoi(fp, printvridge, vertex,
+ !qh_ALL, innerouter, inorder);
+ }
+ return totridges;
+} /* eachvoronoi_all */
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="facet2point">-</a>
+
+ qh_facet2point( facet, point0, point1, mindist )
+ return two projected temporary vertices for a 2-d facet
+ may be non-simplicial
+
+ returns:
+ point0 and point1 oriented and projected to the facet
+ returns mindist (maximum distance below plane)
+*/
+void qh_facet2point(facetT *facet, pointT **point0, pointT **point1, realT *mindist) {
+ vertexT *vertex0, *vertex1;
+ realT dist;
+
+ if (facet->toporient ^ qh_ORIENTclock) {
+ vertex0= SETfirstt_(facet->vertices, vertexT);
+ vertex1= SETsecondt_(facet->vertices, vertexT);
+ }else {
+ vertex1= SETfirstt_(facet->vertices, vertexT);
+ vertex0= SETsecondt_(facet->vertices, vertexT);
+ }
+ zadd_(Zdistio, 2);
+ qh_distplane(vertex0->point, facet, &dist);
+ *mindist= dist;
+ *point0= qh_projectpoint(vertex0->point, facet, dist);
+ qh_distplane(vertex1->point, facet, &dist);
+ minimize_(*mindist, dist);
+ *point1= qh_projectpoint(vertex1->point, facet, dist);
+} /* facet2point */
+
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="facetvertices">-</a>
+
+ qh_facetvertices( facetlist, facets, allfacets )
+ returns temporary set of vertices in a set and/or list of facets
+ if allfacets, ignores qh_skipfacet()
+
+ returns:
+ vertices with qh.vertex_visit
+
+ notes:
+ optimized for allfacets of facet_list
+
+ design:
+ if allfacets of facet_list
+ create vertex set from vertex_list
+ else
+ for each selected facet in facets or facetlist
+ append unvisited vertices to vertex set
+*/
+setT *qh_facetvertices(facetT *facetlist, setT *facets, boolT allfacets) {
+ setT *vertices;
+ facetT *facet, **facetp;
+ vertexT *vertex, **vertexp;
+
+ qh vertex_visit++;
+ if (facetlist == qh facet_list && allfacets && !facets) {
+ vertices= qh_settemp(qh num_vertices);
+ FORALLvertices {
+ vertex->visitid= qh vertex_visit;
+ qh_setappend(&vertices, vertex);
+ }
+ }else {
+ vertices= qh_settemp(qh TEMPsize);
+ FORALLfacet_(facetlist) {
+ if (!allfacets && qh_skipfacet(facet))
+ continue;
+ FOREACHvertex_(facet->vertices) {
+ if (vertex->visitid != qh vertex_visit) {
+ vertex->visitid= qh vertex_visit;
+ qh_setappend(&vertices, vertex);
+ }
+ }
+ }
+ }
+ FOREACHfacet_(facets) {
+ if (!allfacets && qh_skipfacet(facet))
+ continue;
+ FOREACHvertex_(facet->vertices) {
+ if (vertex->visitid != qh vertex_visit) {
+ vertex->visitid= qh vertex_visit;
+ qh_setappend(&vertices, vertex);
+ }
+ }
+ }
+ return vertices;
+} /* facetvertices */
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="geomplanes">-</a>
+
+ qh_geomplanes( facet, outerplane, innerplane )
+ return outer and inner planes for Geomview
+ qh.PRINTradius is size of vertices and points (includes qh.JOGGLEmax)
+
+ notes:
+ assume precise calculations in io.c with roundoff covered by qh_GEOMepsilon
+*/
+void qh_geomplanes(facetT *facet, realT *outerplane, realT *innerplane) {
+ realT radius;
+
+ if (qh MERGING || qh JOGGLEmax < REALmax/2) {
+ qh_outerinner(facet, outerplane, innerplane);
+ radius= qh PRINTradius;
+ if (qh JOGGLEmax < REALmax/2)
+ radius -= qh JOGGLEmax * sqrt((realT)qh hull_dim); /* already accounted for in qh_outerinner() */
+ *outerplane += radius;
+ *innerplane -= radius;
+ if (qh PRINTcoplanar || qh PRINTspheres) {
+ *outerplane += qh MAXabs_coord * qh_GEOMepsilon;
+ *innerplane -= qh MAXabs_coord * qh_GEOMepsilon;
+ }
+ }else
+ *innerplane= *outerplane= 0;
+} /* geomplanes */
+
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="markkeep">-</a>
+
+ qh_markkeep( facetlist )
+ mark good facets that meet qh.KEEParea, qh.KEEPmerge, and qh.KEEPminArea
+ ignores visible facets (!part of convex hull)
+
+ returns:
+ may clear facet->good
+ recomputes qh.num_good
+
+ design:
+ get set of good facets
+ if qh.KEEParea
+ sort facets by area
+ clear facet->good for all but n largest facets
+ if qh.KEEPmerge
+ sort facets by merge count
+ clear facet->good for all but n most merged facets
+ if qh.KEEPminarea
+ clear facet->good if area too small
+ update qh.num_good
+*/
+void qh_markkeep(facetT *facetlist) {
+ facetT *facet, **facetp;
+ setT *facets= qh_settemp(qh num_facets);
+ int size, count;
+
+ trace2((qh ferr, 2006, "qh_markkeep: only keep %d largest and/or %d most merged facets and/or min area %.2g\n",
+ qh KEEParea, qh KEEPmerge, qh KEEPminArea));
+ FORALLfacet_(facetlist) {
+ if (!facet->visible && facet->good)
+ qh_setappend(&facets, facet);
+ }
+ size= qh_setsize(facets);
+ if (qh KEEParea) {
+ qsort(SETaddr_(facets, facetT), (size_t)size,
+ sizeof(facetT *), qh_compare_facetarea);
+ if ((count= size - qh KEEParea) > 0) {
+ FOREACHfacet_(facets) {
+ facet->good= False;
+ if (--count == 0)
+ break;
+ }
+ }
+ }
+ if (qh KEEPmerge) {
+ qsort(SETaddr_(facets, facetT), (size_t)size,
+ sizeof(facetT *), qh_compare_facetmerge);
+ if ((count= size - qh KEEPmerge) > 0) {
+ FOREACHfacet_(facets) {
+ facet->good= False;
+ if (--count == 0)
+ break;
+ }
+ }
+ }
+ if (qh KEEPminArea < REALmax/2) {
+ FOREACHfacet_(facets) {
+ if (!facet->isarea || facet->f.area < qh KEEPminArea)
+ facet->good= False;
+ }
+ }
+ qh_settempfree(&facets);
+ count= 0;
+ FORALLfacet_(facetlist) {
+ if (facet->good)
+ count++;
+ }
+ qh num_good= count;
+} /* markkeep */
+
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="markvoronoi">-</a>
+
+ qh_markvoronoi( facetlist, facets, printall, isLower, numcenters )
+ mark voronoi vertices for printing by site pairs
+
+ returns:
+ temporary set of vertices indexed by pointid
+ isLower set if printing lower hull (i.e., at least one facet is lower hull)
+ numcenters= total number of Voronoi vertices
+ bumps qh.printoutnum for vertex-at-infinity
+ clears all facet->seen and sets facet->seen2
+
+ if selected
+ facet->visitid= Voronoi vertex id
+ else if upper hull (or 'Qu' and lower hull)
+ facet->visitid= 0
+ else
+ facet->visitid >= qh num_facets
+
+ notes:
+ ignores qh.ATinfinity, if defined
+*/
+setT *qh_markvoronoi(facetT *facetlist, setT *facets, boolT printall, boolT *isLowerp, int *numcentersp) {
+ int numcenters=0;
+ facetT *facet, **facetp;
+ setT *vertices;
+ boolT isLower= False;
+
+ qh printoutnum++;
+ qh_clearcenters(qh_ASvoronoi); /* in case, qh_printvdiagram2 called by user */
+ qh_vertexneighbors();
+ vertices= qh_pointvertex();
+ if (qh ATinfinity)
+ SETelem_(vertices, qh num_points-1)= NULL;
+ qh visit_id++;
+ maximize_(qh visit_id, (unsigned) qh num_facets);
+ FORALLfacet_(facetlist) {
+ if (printall || !qh_skipfacet(facet)) {
+ if (!facet->upperdelaunay) {
+ isLower= True;
+ break;
+ }
+ }
+ }
+ FOREACHfacet_(facets) {
+ if (printall || !qh_skipfacet(facet)) {
+ if (!facet->upperdelaunay) {
+ isLower= True;
+ break;
+ }
+ }
+ }
+ FORALLfacets {
+ if (facet->normal && (facet->upperdelaunay == isLower))
+ facet->visitid= 0; /* facetlist or facets may overwrite */
+ else
+ facet->visitid= qh visit_id;
+ facet->seen= False;
+ facet->seen2= True;
+ }
+ numcenters++; /* qh_INFINITE */
+ FORALLfacet_(facetlist) {
+ if (printall || !qh_skipfacet(facet))
+ facet->visitid= numcenters++;
+ }
+ FOREACHfacet_(facets) {
+ if (printall || !qh_skipfacet(facet))
+ facet->visitid= numcenters++;
+ }
+ *isLowerp= isLower;
+ *numcentersp= numcenters;
+ trace2((qh ferr, 2007, "qh_markvoronoi: isLower %d numcenters %d\n", isLower, numcenters));
+ return vertices;
+} /* markvoronoi */
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="order_vertexneighbors">-</a>
+
+ qh_order_vertexneighbors( vertex )
+ order facet neighbors of a 2-d or 3-d vertex by adjacency
+
+ notes:
+ does not orient the neighbors
+
+ design:
+ initialize a new neighbor set with the first facet in vertex->neighbors
+ while vertex->neighbors non-empty
+ select next neighbor in the previous facet's neighbor set
+ set vertex->neighbors to the new neighbor set
+*/
+void qh_order_vertexneighbors(vertexT *vertex) {
+ setT *newset;
+ facetT *facet, *neighbor, **neighborp;
+
+ trace4((qh ferr, 4018, "qh_order_vertexneighbors: order neighbors of v%d for 3-d\n", vertex->id));
+ newset= qh_settemp(qh_setsize(vertex->neighbors));
+ facet= (facetT*)qh_setdellast(vertex->neighbors);
+ qh_setappend(&newset, facet);
+ while (qh_setsize(vertex->neighbors)) {
+ FOREACHneighbor_(vertex) {
+ if (qh_setin(facet->neighbors, neighbor)) {
+ qh_setdel(vertex->neighbors, neighbor);
+ qh_setappend(&newset, neighbor);
+ facet= neighbor;
+ break;
+ }
+ }
+ if (!neighbor) {
+ qh_fprintf(qh ferr, 6066, "qhull internal error (qh_order_vertexneighbors): no neighbor of v%d for f%d\n",
+ vertex->id, facet->id);
+ qh_errexit(qh_ERRqhull, facet, NULL);
+ }
+ }
+ qh_setfree(&vertex->neighbors);
+ qh_settemppop();
+ vertex->neighbors= newset;
+} /* order_vertexneighbors */
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="prepare_output">-</a>
+
+ qh_prepare_output( )
+ prepare for qh_produce_output2() according to
+ qh.KEEPminArea, KEEParea, KEEPmerge, GOODvertex, GOODthreshold, GOODpoint, ONLYgood, SPLITthresholds
+ does not reset facet->good
+
+ notes
+ except for PRINTstatistics, no-op if previously called with same options
+*/
+void qh_prepare_output(void) {
+ if (qh VORONOI) {
+ qh_clearcenters(qh_ASvoronoi); /* must be before qh_triangulate */
+ qh_vertexneighbors();
+ }
+ if (qh TRIangulate && !qh hasTriangulation) {
+ qh_triangulate();
+ if (qh VERIFYoutput && !qh CHECKfrequently)
+ qh_checkpolygon(qh facet_list);
+ }
+ qh_findgood_all(qh facet_list);
+ if (qh GETarea)
+ qh_getarea(qh facet_list);
+ if (qh KEEParea || qh KEEPmerge || qh KEEPminArea < REALmax/2)
+ qh_markkeep(qh facet_list);
+ if (qh PRINTstatistics)
+ qh_collectstatistics();
+}
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="printafacet">-</a>
+
+ qh_printafacet( fp, format, facet, printall )
+ print facet to fp in given output format (see qh.PRINTout)
+
+ returns:
+ nop if !printall and qh_skipfacet()
+ nop if visible facet and NEWfacets and format != PRINTfacets
+ must match qh_countfacets
+
+ notes
+ preserves qh.visit_id
+ facet->normal may be null if PREmerge/MERGEexact and STOPcone before merge
+
+ see
+ qh_printbegin() and qh_printend()
+
+ design:
+ test for printing facet
+ call appropriate routine for format
+ or output results directly
+*/
+void qh_printafacet(FILE *fp, qh_PRINT format, facetT *facet, boolT printall) {
+ realT color[4], offset, dist, outerplane, innerplane;
+ boolT zerodiv;
+ coordT *point, *normp, *coordp, **pointp, *feasiblep;
+ int k;
+ vertexT *vertex, **vertexp;
+ facetT *neighbor, **neighborp;
+
+ if (!printall && qh_skipfacet(facet))
+ return;
+ if (facet->visible && qh NEWfacets && format != qh_PRINTfacets)
+ return;
+ qh printoutnum++;
+ switch (format) {
+ case qh_PRINTarea:
+ if (facet->isarea) {
+ qh_fprintf(fp, 9009, qh_REAL_1, facet->f.area);
+ qh_fprintf(fp, 9010, "\n");
+ }else
+ qh_fprintf(fp, 9011, "0\n");
+ break;
+ case qh_PRINTcoplanars:
+ qh_fprintf(fp, 9012, "%d", qh_setsize(facet->coplanarset));
+ FOREACHpoint_(facet->coplanarset)
+ qh_fprintf(fp, 9013, " %d", qh_pointid(point));
+ qh_fprintf(fp, 9014, "\n");
+ break;
+ case qh_PRINTcentrums:
+ qh_printcenter(fp, format, NULL, facet);
+ break;
+ case qh_PRINTfacets:
+ qh_printfacet(fp, facet);
+ break;
+ case qh_PRINTfacets_xridge:
+ qh_printfacetheader(fp, facet);
+ break;
+ case qh_PRINTgeom: /* either 2 , 3, or 4-d by qh_printbegin */
+ if (!facet->normal)
+ break;
+ for (k=qh hull_dim; k--; ) {
+ color[k]= (facet->normal[k]+1.0)/2.0;
+ maximize_(color[k], -1.0);
+ minimize_(color[k], +1.0);
+ }
+ qh_projectdim3(color, color);
+ if (qh PRINTdim != qh hull_dim)
+ qh_normalize2(color, 3, True, NULL, NULL);
+ if (qh hull_dim <= 2)
+ qh_printfacet2geom(fp, facet, color);
+ else if (qh hull_dim == 3) {
+ if (facet->simplicial)
+ qh_printfacet3geom_simplicial(fp, facet, color);
+ else
+ qh_printfacet3geom_nonsimplicial(fp, facet, color);
+ }else {
+ if (facet->simplicial)
+ qh_printfacet4geom_simplicial(fp, facet, color);
+ else
+ qh_printfacet4geom_nonsimplicial(fp, facet, color);
+ }
+ break;
+ case qh_PRINTids:
+ qh_fprintf(fp, 9015, "%d\n", facet->id);
+ break;
+ case qh_PRINTincidences:
+ case qh_PRINToff:
+ case qh_PRINTtriangles:
+ if (qh hull_dim == 3 && format != qh_PRINTtriangles)
+ qh_printfacet3vertex(fp, facet, format);
+ else if (facet->simplicial || qh hull_dim == 2 || format == qh_PRINToff)
+ qh_printfacetNvertex_simplicial(fp, facet, format);
+ else
+ qh_printfacetNvertex_nonsimplicial(fp, facet, qh printoutvar++, format);
+ break;
+ case qh_PRINTinner:
+ qh_outerinner(facet, NULL, &innerplane);
+ offset= facet->offset - innerplane;
+ goto LABELprintnorm;
+ break; /* prevent warning */
+ case qh_PRINTmerges:
+ qh_fprintf(fp, 9016, "%d\n", facet->nummerge);
+ break;
+ case qh_PRINTnormals:
+ offset= facet->offset;
+ goto LABELprintnorm;
+ break; /* prevent warning */
+ case qh_PRINTouter:
+ qh_outerinner(facet, &outerplane, NULL);
+ offset= facet->offset - outerplane;
+ LABELprintnorm:
+ if (!facet->normal) {
+ qh_fprintf(fp, 9017, "no normal for facet f%d\n", facet->id);
+ break;
+ }
+ if (qh CDDoutput) {
+ qh_fprintf(fp, 9018, qh_REAL_1, -offset);
+ for (k=0; k < qh hull_dim; k++)
+ qh_fprintf(fp, 9019, qh_REAL_1, -facet->normal[k]);
+ }else {
+ for (k=0; k < qh hull_dim; k++)
+ qh_fprintf(fp, 9020, qh_REAL_1, facet->normal[k]);
+ qh_fprintf(fp, 9021, qh_REAL_1, offset);
+ }
+ qh_fprintf(fp, 9022, "\n");
+ break;
+ case qh_PRINTmathematica: /* either 2 or 3-d by qh_printbegin */
+ case qh_PRINTmaple:
+ if (qh hull_dim == 2)
+ qh_printfacet2math(fp, facet, format, qh printoutvar++);
+ else
+ qh_printfacet3math(fp, facet, format, qh printoutvar++);
+ break;
+ case qh_PRINTneighbors:
+ qh_fprintf(fp, 9023, "%d", qh_setsize(facet->neighbors));
+ FOREACHneighbor_(facet)
+ qh_fprintf(fp, 9024, " %d",
+ neighbor->visitid ? neighbor->visitid - 1: 0 - neighbor->id);
+ qh_fprintf(fp, 9025, "\n");
+ break;
+ case qh_PRINTpointintersect:
+ if (!qh feasible_point) {
+ qh_fprintf(qh ferr, 6067, "qhull input error (qh_printafacet): option 'Fp' needs qh feasible_point\n");
+ qh_errexit( qh_ERRinput, NULL, NULL);
+ }
+ if (facet->offset > 0)
+ goto LABELprintinfinite;
+ point= coordp= (coordT*)qh_memalloc(qh normal_size);
+ normp= facet->normal;
+ feasiblep= qh feasible_point;
+ if (facet->offset < -qh MINdenom) {
+ for (k=qh hull_dim; k--; )
+ *(coordp++)= (*(normp++) / - facet->offset) + *(feasiblep++);
+ }else {
+ for (k=qh hull_dim; k--; ) {
+ *(coordp++)= qh_divzero(*(normp++), facet->offset, qh MINdenom_1,
+ &zerodiv) + *(feasiblep++);
+ if (zerodiv) {
+ qh_memfree(point, qh normal_size);
+ goto LABELprintinfinite;
+ }
+ }
+ }
+ qh_printpoint(fp, NULL, point);
+ qh_memfree(point, qh normal_size);
+ break;
+ LABELprintinfinite:
+ for (k=qh hull_dim; k--; )
+ qh_fprintf(fp, 9026, qh_REAL_1, qh_INFINITE);
+ qh_fprintf(fp, 9027, "\n");
+ break;
+ case qh_PRINTpointnearest:
+ FOREACHpoint_(facet->coplanarset) {
+ int id, id2;
+ vertex= qh_nearvertex(facet, point, &dist);
+ id= qh_pointid(vertex->point);
+ id2= qh_pointid(point);
+ qh_fprintf(fp, 9028, "%d %d %d " qh_REAL_1 "\n", id, id2, facet->id, dist);
+ }
+ break;
+ case qh_PRINTpoints: /* VORONOI only by qh_printbegin */
+ if (qh CDDoutput)
+ qh_fprintf(fp, 9029, "1 ");
+ qh_printcenter(fp, format, NULL, facet);
+ break;
+ case qh_PRINTvertices:
+ qh_fprintf(fp, 9030, "%d", qh_setsize(facet->vertices));
+ FOREACHvertex_(facet->vertices)
+ qh_fprintf(fp, 9031, " %d", qh_pointid(vertex->point));
+ qh_fprintf(fp, 9032, "\n");
+ break;
+ default:
+ break;
+ }
+} /* printafacet */
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="printbegin">-</a>
+
+ qh_printbegin( )
+ prints header for all output formats
+
+ returns:
+ checks for valid format
+
+ notes:
+ uses qh.visit_id for 3/4off
+ changes qh.interior_point if printing centrums
+ qh_countfacets clears facet->visitid for non-good facets
+
+ see
+ qh_printend() and qh_printafacet()
+
+ design:
+ count facets and related statistics
+ print header for format
+*/
+void qh_printbegin(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) {
+ int numfacets, numsimplicial, numridges, totneighbors, numcoplanars, numtricoplanars;
+ int i, num;
+ facetT *facet, **facetp;
+ vertexT *vertex, **vertexp;
+ setT *vertices;
+ pointT *point, **pointp, *pointtemp;
+
+ qh printoutnum= 0;
+ qh_countfacets(facetlist, facets, printall, &numfacets, &numsimplicial,
+ &totneighbors, &numridges, &numcoplanars, &numtricoplanars);
+ switch (format) {
+ case qh_PRINTnone:
+ break;
+ case qh_PRINTarea:
+ qh_fprintf(fp, 9033, "%d\n", numfacets);
+ break;
+ case qh_PRINTcoplanars:
+ qh_fprintf(fp, 9034, "%d\n", numfacets);
+ break;
+ case qh_PRINTcentrums:
+ if (qh CENTERtype == qh_ASnone)
+ qh_clearcenters(qh_AScentrum);
+ qh_fprintf(fp, 9035, "%d\n%d\n", qh hull_dim, numfacets);
+ break;
+ case qh_PRINTfacets:
+ case qh_PRINTfacets_xridge:
+ if (facetlist)
+ qh_printvertexlist(fp, "Vertices and facets:\n", facetlist, facets, printall);
+ break;
+ case qh_PRINTgeom:
+ if (qh hull_dim > 4) /* qh_initqhull_globals also checks */
+ goto LABELnoformat;
+ if (qh VORONOI && qh hull_dim > 3) /* PRINTdim == DROPdim == hull_dim-1 */
+ goto LABELnoformat;
+ if (qh hull_dim == 2 && (qh PRINTridges || qh DOintersections))
+ qh_fprintf(qh ferr, 7049, "qhull warning: output for ridges and intersections not implemented in 2-d\n");
+ if (qh hull_dim == 4 && (qh PRINTinner || qh PRINTouter ||
+ (qh PRINTdim == 4 && qh PRINTcentrums)))
+ qh_fprintf(qh ferr, 7050, "qhull warning: output for outer/inner planes and centrums not implemented in 4-d\n");
+ if (qh PRINTdim == 4 && (qh PRINTspheres))
+ qh_fprintf(qh ferr, 7051, "qhull warning: output for vertices not implemented in 4-d\n");
+ if (qh PRINTdim == 4 && qh DOintersections && qh PRINTnoplanes)
+ qh_fprintf(qh ferr, 7052, "qhull warning: 'Gnh' generates no output in 4-d\n");
+ if (qh PRINTdim == 2) {
+ qh_fprintf(fp, 9036, "{appearance {linewidth 3} LIST # %s | %s\n",
+ qh rbox_command, qh qhull_command);
+ }else if (qh PRINTdim == 3) {
+ qh_fprintf(fp, 9037, "{appearance {+edge -evert linewidth 2} LIST # %s | %s\n",
+ qh rbox_command, qh qhull_command);
+ }else if (qh PRINTdim == 4) {
+ qh visit_id++;
+ num= 0;
+ FORALLfacet_(facetlist) /* get number of ridges to be printed */
+ qh_printend4geom(NULL, facet, &num, printall);
+ FOREACHfacet_(facets)
+ qh_printend4geom(NULL, facet, &num, printall);
+ qh ridgeoutnum= num;
+ qh printoutvar= 0; /* counts number of ridges in output */
+ qh_fprintf(fp, 9038, "LIST # %s | %s\n", qh rbox_command, qh qhull_command);
+ }
+
+ if (qh PRINTdots) {
+ qh printoutnum++;
+ num= qh num_points + qh_setsize(qh other_points);
+ if (qh DELAUNAY && qh ATinfinity)
+ num--;
+ if (qh PRINTdim == 4)
+ qh_fprintf(fp, 9039, "4VECT %d %d 1\n", num, num);
+ else
+ qh_fprintf(fp, 9040, "VECT %d %d 1\n", num, num);
+
+ for (i=num; i--; ) {
+ if (i % 20 == 0)
+ qh_fprintf(fp, 9041, "\n");
+ qh_fprintf(fp, 9042, "1 ");
+ }
+ qh_fprintf(fp, 9043, "# 1 point per line\n1 ");
+ for (i=num-1; i--; ) { /* num at least 3 for D2 */
+ if (i % 20 == 0)
+ qh_fprintf(fp, 9044, "\n");
+ qh_fprintf(fp, 9045, "0 ");
+ }
+ qh_fprintf(fp, 9046, "# 1 color for all\n");
+ FORALLpoints {
+ if (!qh DELAUNAY || !qh ATinfinity || qh_pointid(point) != qh num_points-1) {
+ if (qh PRINTdim == 4)
+ qh_printpoint(fp, NULL, point);
+ else
+ qh_printpoint3(fp, point);
+ }
+ }
+ FOREACHpoint_(qh other_points) {
+ if (qh PRINTdim == 4)
+ qh_printpoint(fp, NULL, point);
+ else
+ qh_printpoint3(fp, point);
+ }
+ qh_fprintf(fp, 9047, "0 1 1 1 # color of points\n");
+ }
+
+ if (qh PRINTdim == 4 && !qh PRINTnoplanes)
+ /* 4dview loads up multiple 4OFF objects slowly */
+ qh_fprintf(fp, 9048, "4OFF %d %d 1\n", 3*qh ridgeoutnum, qh ridgeoutnum);
+ qh PRINTcradius= 2 * qh DISTround; /* include test DISTround */
+ if (qh PREmerge) {
+ maximize_(qh PRINTcradius, qh premerge_centrum + qh DISTround);
+ }else if (qh POSTmerge)
+ maximize_(qh PRINTcradius, qh postmerge_centrum + qh DISTround);
+ qh PRINTradius= qh PRINTcradius;
+ if (qh PRINTspheres + qh PRINTcoplanar)
+ maximize_(qh PRINTradius, qh MAXabs_coord * qh_MINradius);
+ if (qh premerge_cos < REALmax/2) {
+ maximize_(qh PRINTradius, (1- qh premerge_cos) * qh MAXabs_coord);
+ }else if (!qh PREmerge && qh POSTmerge && qh postmerge_cos < REALmax/2) {
+ maximize_(qh PRINTradius, (1- qh postmerge_cos) * qh MAXabs_coord);
+ }
+ maximize_(qh PRINTradius, qh MINvisible);
+ if (qh JOGGLEmax < REALmax/2)
+ qh PRINTradius += qh JOGGLEmax * sqrt((realT)qh hull_dim);
+ if (qh PRINTdim != 4 &&
+ (qh PRINTcoplanar || qh PRINTspheres || qh PRINTcentrums)) {
+ vertices= qh_facetvertices(facetlist, facets, printall);
+ if (qh PRINTspheres && qh PRINTdim <= 3)
+ qh_printspheres(fp, vertices, qh PRINTradius);
+ if (qh PRINTcoplanar || qh PRINTcentrums) {
+ qh firstcentrum= True;
+ if (qh PRINTcoplanar&& !qh PRINTspheres) {
+ FOREACHvertex_(vertices)
+ qh_printpointvect2(fp, vertex->point, NULL, qh interior_point, qh PRINTradius);
+ }
+ FORALLfacet_(facetlist) {
+ if (!printall && qh_skipfacet(facet))
+ continue;
+ if (!facet->normal)
+ continue;
+ if (qh PRINTcentrums && qh PRINTdim <= 3)
+ qh_printcentrum(fp, facet, qh PRINTcradius);
+ if (!qh PRINTcoplanar)
+ continue;
+ FOREACHpoint_(facet->coplanarset)
+ qh_printpointvect2(fp, point, facet->normal, NULL, qh PRINTradius);
+ FOREACHpoint_(facet->outsideset)
+ qh_printpointvect2(fp, point, facet->normal, NULL, qh PRINTradius);
+ }
+ FOREACHfacet_(facets) {
+ if (!printall && qh_skipfacet(facet))
+ continue;
+ if (!facet->normal)
+ continue;
+ if (qh PRINTcentrums && qh PRINTdim <= 3)
+ qh_printcentrum(fp, facet, qh PRINTcradius);
+ if (!qh PRINTcoplanar)
+ continue;
+ FOREACHpoint_(facet->coplanarset)
+ qh_printpointvect2(fp, point, facet->normal, NULL, qh PRINTradius);
+ FOREACHpoint_(facet->outsideset)
+ qh_printpointvect2(fp, point, facet->normal, NULL, qh PRINTradius);
+ }
+ }
+ qh_settempfree(&vertices);
+ }
+ qh visit_id++; /* for printing hyperplane intersections */
+ break;
+ case qh_PRINTids:
+ qh_fprintf(fp, 9049, "%d\n", numfacets);
+ break;
+ case qh_PRINTincidences:
+ if (qh VORONOI && qh PRINTprecision)
+ qh_fprintf(qh ferr, 7053, "qhull warning: writing Delaunay. Use 'p' or 'o' for Voronoi centers\n");
+ qh printoutvar= qh vertex_id; /* centrum id for non-simplicial facets */
+ if (qh hull_dim <= 3)
+ qh_fprintf(fp, 9050, "%d\n", numfacets);
+ else
+ qh_fprintf(fp, 9051, "%d\n", numsimplicial+numridges);
+ break;
+ case qh_PRINTinner:
+ case qh_PRINTnormals:
+ case qh_PRINTouter:
+ if (qh CDDoutput)
+ qh_fprintf(fp, 9052, "%s | %s\nbegin\n %d %d real\n", qh rbox_command,
+ qh qhull_command, numfacets, qh hull_dim+1);
+ else
+ qh_fprintf(fp, 9053, "%d\n%d\n", qh hull_dim+1, numfacets);
+ break;
+ case qh_PRINTmathematica:
+ case qh_PRINTmaple:
+ if (qh hull_dim > 3) /* qh_initbuffers also checks */
+ goto LABELnoformat;
+ if (qh VORONOI)
+ qh_fprintf(qh ferr, 7054, "qhull warning: output is the Delaunay triangulation\n");
+ if (format == qh_PRINTmaple) {
+ if (qh hull_dim == 2)
+ qh_fprintf(fp, 9054, "PLOT(CURVES(\n");
+ else
+ qh_fprintf(fp, 9055, "PLOT3D(POLYGONS(\n");
+ }else
+ qh_fprintf(fp, 9056, "{\n");
+ qh printoutvar= 0; /* counts number of facets for notfirst */
+ break;
+ case qh_PRINTmerges:
+ qh_fprintf(fp, 9057, "%d\n", numfacets);
+ break;
+ case qh_PRINTpointintersect:
+ qh_fprintf(fp, 9058, "%d\n%d\n", qh hull_dim, numfacets);
+ break;
+ case qh_PRINTneighbors:
+ qh_fprintf(fp, 9059, "%d\n", numfacets);
+ break;
+ case qh_PRINToff:
+ case qh_PRINTtriangles:
+ if (qh VORONOI)
+ goto LABELnoformat;
+ num = qh hull_dim;
+ if (format == qh_PRINToff || qh hull_dim == 2)
+ qh_fprintf(fp, 9060, "%d\n%d %d %d\n", num,
+ qh num_points+qh_setsize(qh other_points), numfacets, totneighbors/2);
+ else { /* qh_PRINTtriangles */
+ qh printoutvar= qh num_points+qh_setsize(qh other_points); /* first centrum */
+ if (qh DELAUNAY)
+ num--; /* drop last dimension */
+ qh_fprintf(fp, 9061, "%d\n%d %d %d\n", num, qh printoutvar
+ + numfacets - numsimplicial, numsimplicial + numridges, totneighbors/2);
+ }
+ FORALLpoints
+ qh_printpointid(qh fout, NULL, num, point, qh_IDunknown);
+ FOREACHpoint_(qh other_points)
+ qh_printpointid(qh fout, NULL, num, point, qh_IDunknown);
+ if (format == qh_PRINTtriangles && qh hull_dim > 2) {
+ FORALLfacets {
+ if (!facet->simplicial && facet->visitid)
+ qh_printcenter(qh fout, format, NULL, facet);
+ }
+ }
+ break;
+ case qh_PRINTpointnearest:
+ qh_fprintf(fp, 9062, "%d\n", numcoplanars);
+ break;
+ case qh_PRINTpoints:
+ if (!qh VORONOI)
+ goto LABELnoformat;
+ if (qh CDDoutput)
+ qh_fprintf(fp, 9063, "%s | %s\nbegin\n%d %d real\n", qh rbox_command,
+ qh qhull_command, numfacets, qh hull_dim);
+ else
+ qh_fprintf(fp, 9064, "%d\n%d\n", qh hull_dim-1, numfacets);
+ break;
+ case qh_PRINTvertices:
+ qh_fprintf(fp, 9065, "%d\n", numfacets);
+ break;
+ case qh_PRINTsummary:
+ default:
+ LABELnoformat:
+ qh_fprintf(qh ferr, 6068, "qhull internal error (qh_printbegin): can not use this format for dimension %d\n",
+ qh hull_dim);
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+} /* printbegin */
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="printcenter">-</a>
+
+ qh_printcenter( fp, string, facet )
+ print facet->center as centrum or Voronoi center
+ string may be NULL. Don't include '%' codes.
+ nop if qh CENTERtype neither CENTERvoronoi nor CENTERcentrum
+ if upper envelope of Delaunay triangulation and point at-infinity
+ prints qh_INFINITE instead;
+
+ notes:
+ defines facet->center if needed
+ if format=PRINTgeom, adds a 0 if would otherwise be 2-d
+ Same as QhullFacet::printCenter
+*/
+void qh_printcenter(FILE *fp, qh_PRINT format, const char *string, facetT *facet) {
+ int k, num;
+
+ if (qh CENTERtype != qh_ASvoronoi && qh CENTERtype != qh_AScentrum)
+ return;
+ if (string)
+ qh_fprintf(fp, 9066, string);
+ if (qh CENTERtype == qh_ASvoronoi) {
+ num= qh hull_dim-1;
+ if (!facet->normal || !facet->upperdelaunay || !qh ATinfinity) {
+ if (!facet->center)
+ facet->center= qh_facetcenter(facet->vertices);
+ for (k=0; k < num; k++)
+ qh_fprintf(fp, 9067, qh_REAL_1, facet->center[k]);
+ }else {
+ for (k=0; k < num; k++)
+ qh_fprintf(fp, 9068, qh_REAL_1, qh_INFINITE);
+ }
+ }else /* qh.CENTERtype == qh_AScentrum */ {
+ num= qh hull_dim;
+ if (format == qh_PRINTtriangles && qh DELAUNAY)
+ num--;
+ if (!facet->center)
+ facet->center= qh_getcentrum(facet);
+ for (k=0; k < num; k++)
+ qh_fprintf(fp, 9069, qh_REAL_1, facet->center[k]);
+ }
+ if (format == qh_PRINTgeom && num == 2)
+ qh_fprintf(fp, 9070, " 0\n");
+ else
+ qh_fprintf(fp, 9071, "\n");
+} /* printcenter */
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="printcentrum">-</a>
+
+ qh_printcentrum( fp, facet, radius )
+ print centrum for a facet in OOGL format
+ radius defines size of centrum
+ 2-d or 3-d only
+
+ returns:
+ defines facet->center if needed
+*/
+void qh_printcentrum(FILE *fp, facetT *facet, realT radius) {
+ pointT *centrum, *projpt;
+ boolT tempcentrum= False;
+ realT xaxis[4], yaxis[4], normal[4], dist;
+ realT green[3]={0, 1, 0};
+ vertexT *apex;
+ int k;
+
+ if (qh CENTERtype == qh_AScentrum) {
+ if (!facet->center)
+ facet->center= qh_getcentrum(facet);
+ centrum= facet->center;
+ }else {
+ centrum= qh_getcentrum(facet);
+ tempcentrum= True;
+ }
+ qh_fprintf(fp, 9072, "{appearance {-normal -edge normscale 0} ");
+ if (qh firstcentrum) {
+ qh firstcentrum= False;
+ qh_fprintf(fp, 9073, "{INST geom { define centrum CQUAD # f%d\n\
+-0.3 -0.3 0.0001 0 0 1 1\n\
+ 0.3 -0.3 0.0001 0 0 1 1\n\
+ 0.3 0.3 0.0001 0 0 1 1\n\
+-0.3 0.3 0.0001 0 0 1 1 } transform { \n", facet->id);
+ }else
+ qh_fprintf(fp, 9074, "{INST geom { : centrum } transform { # f%d\n", facet->id);
+ apex= SETfirstt_(facet->vertices, vertexT);
+ qh_distplane(apex->point, facet, &dist);
+ projpt= qh_projectpoint(apex->point, facet, dist);
+ for (k=qh hull_dim; k--; ) {
+ xaxis[k]= projpt[k] - centrum[k];
+ normal[k]= facet->normal[k];
+ }
+ if (qh hull_dim == 2) {
+ xaxis[2]= 0;
+ normal[2]= 0;
+ }else if (qh hull_dim == 4) {
+ qh_projectdim3(xaxis, xaxis);
+ qh_projectdim3(normal, normal);
+ qh_normalize2(normal, qh PRINTdim, True, NULL, NULL);
+ }
+ qh_crossproduct(3, xaxis, normal, yaxis);
+ qh_fprintf(fp, 9075, "%8.4g %8.4g %8.4g 0\n", xaxis[0], xaxis[1], xaxis[2]);
+ qh_fprintf(fp, 9076, "%8.4g %8.4g %8.4g 0\n", yaxis[0], yaxis[1], yaxis[2]);
+ qh_fprintf(fp, 9077, "%8.4g %8.4g %8.4g 0\n", normal[0], normal[1], normal[2]);
+ qh_printpoint3(fp, centrum);
+ qh_fprintf(fp, 9078, "1 }}}\n");
+ qh_memfree(projpt, qh normal_size);
+ qh_printpointvect(fp, centrum, facet->normal, NULL, radius, green);
+ if (tempcentrum)
+ qh_memfree(centrum, qh normal_size);
+} /* printcentrum */
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="printend">-</a>
+
+ qh_printend( fp, format )
+ prints trailer for all output formats
+
+ see:
+ qh_printbegin() and qh_printafacet()
+
+*/
+void qh_printend(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) {
+ int num;
+ facetT *facet, **facetp;
+
+ if (!qh printoutnum)
+ qh_fprintf(qh ferr, 7055, "qhull warning: no facets printed\n");
+ switch (format) {
+ case qh_PRINTgeom:
+ if (qh hull_dim == 4 && qh DROPdim < 0 && !qh PRINTnoplanes) {
+ qh visit_id++;
+ num= 0;
+ FORALLfacet_(facetlist)
+ qh_printend4geom(fp, facet,&num, printall);
+ FOREACHfacet_(facets)
+ qh_printend4geom(fp, facet, &num, printall);
+ if (num != qh ridgeoutnum || qh printoutvar != qh ridgeoutnum) {
+ qh_fprintf(qh ferr, 6069, "qhull internal error (qh_printend): number of ridges %d != number printed %d and at end %d\n", qh ridgeoutnum, qh printoutvar, num);
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ }else
+ qh_fprintf(fp, 9079, "}\n");
+ break;
+ case qh_PRINTinner:
+ case qh_PRINTnormals:
+ case qh_PRINTouter:
+ if (qh CDDoutput)
+ qh_fprintf(fp, 9080, "end\n");
+ break;
+ case qh_PRINTmaple:
+ qh_fprintf(fp, 9081, "));\n");
+ break;
+ case qh_PRINTmathematica:
+ qh_fprintf(fp, 9082, "}\n");
+ break;
+ case qh_PRINTpoints:
+ if (qh CDDoutput)
+ qh_fprintf(fp, 9083, "end\n");
+ break;
+ default:
+ break;
+ }
+} /* printend */
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="printend4geom">-</a>
+
+ qh_printend4geom( fp, facet, numridges, printall )
+ helper function for qh_printbegin/printend
+
+ returns:
+ number of printed ridges
+
+ notes:
+ just counts printed ridges if fp=NULL
+ uses facet->visitid
+ must agree with qh_printfacet4geom...
+
+ design:
+ computes color for facet from its normal
+ prints each ridge of facet
+*/
+void qh_printend4geom(FILE *fp, facetT *facet, int *nump, boolT printall) {
+ realT color[3];
+ int i, num= *nump;
+ facetT *neighbor, **neighborp;
+ ridgeT *ridge, **ridgep;
+
+ if (!printall && qh_skipfacet(facet))
+ return;
+ if (qh PRINTnoplanes || (facet->visible && qh NEWfacets))
+ return;
+ if (!facet->normal)
+ return;
+ if (fp) {
+ for (i=0; i < 3; i++) {
+ color[i]= (facet->normal[i]+1.0)/2.0;
+ maximize_(color[i], -1.0);
+ minimize_(color[i], +1.0);
+ }
+ }
+ facet->visitid= qh visit_id;
+ if (facet->simplicial) {
+ FOREACHneighbor_(facet) {
+ if (neighbor->visitid != qh visit_id) {
+ if (fp)
+ qh_fprintf(fp, 9084, "3 %d %d %d %8.4g %8.4g %8.4g 1 # f%d f%d\n",
+ 3*num, 3*num+1, 3*num+2, color[0], color[1], color[2],
+ facet->id, neighbor->id);
+ num++;
+ }
+ }
+ }else {
+ FOREACHridge_(facet->ridges) {
+ neighbor= otherfacet_(ridge, facet);
+ if (neighbor->visitid != qh visit_id) {
+ if (fp)
+ qh_fprintf(fp, 9085, "3 %d %d %d %8.4g %8.4g %8.4g 1 #r%d f%d f%d\n",
+ 3*num, 3*num+1, 3*num+2, color[0], color[1], color[2],
+ ridge->id, facet->id, neighbor->id);
+ num++;
+ }
+ }
+ }
+ *nump= num;
+} /* printend4geom */
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="printextremes">-</a>
+
+ qh_printextremes( fp, facetlist, facets, printall )
+ print extreme points for convex hulls or halfspace intersections
+
+ notes:
+ #points, followed by ids, one per line
+
+ sorted by id
+ same order as qh_printpoints_out if no coplanar/interior points
+*/
+void qh_printextremes(FILE *fp, facetT *facetlist, setT *facets, boolT printall) {
+ setT *vertices, *points;
+ pointT *point;
+ vertexT *vertex, **vertexp;
+ int id;
+ int numpoints=0, point_i, point_n;
+ int allpoints= qh num_points + qh_setsize(qh other_points);
+
+ points= qh_settemp(allpoints);
+ qh_setzero(points, 0, allpoints);
+ vertices= qh_facetvertices(facetlist, facets, printall);
+ FOREACHvertex_(vertices) {
+ id= qh_pointid(vertex->point);
+ if (id >= 0) {
+ SETelem_(points, id)= vertex->point;
+ numpoints++;
+ }
+ }
+ qh_settempfree(&vertices);
+ qh_fprintf(fp, 9086, "%d\n", numpoints);
+ FOREACHpoint_i_(points) {
+ if (point)
+ qh_fprintf(fp, 9087, "%d\n", point_i);
+ }
+ qh_settempfree(&points);
+} /* printextremes */
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="printextremes_2d">-</a>
+
+ qh_printextremes_2d( fp, facetlist, facets, printall )
+ prints point ids for facets in qh_ORIENTclock order
+
+ notes:
+ #points, followed by ids, one per line
+ if facetlist/facets are disjoint than the output includes skips
+ errors if facets form a loop
+ does not print coplanar points
+*/
+void qh_printextremes_2d(FILE *fp, facetT *facetlist, setT *facets, boolT printall) {
+ int numfacets, numridges, totneighbors, numcoplanars, numsimplicial, numtricoplanars;
+ setT *vertices;
+ facetT *facet, *startfacet, *nextfacet;
+ vertexT *vertexA, *vertexB;
+
+ qh_countfacets(facetlist, facets, printall, &numfacets, &numsimplicial,
+ &totneighbors, &numridges, &numcoplanars, &numtricoplanars); /* marks qh visit_id */
+ vertices= qh_facetvertices(facetlist, facets, printall);
+ qh_fprintf(fp, 9088, "%d\n", qh_setsize(vertices));
+ qh_settempfree(&vertices);
+ if (!numfacets)
+ return;
+ facet= startfacet= facetlist ? facetlist : SETfirstt_(facets, facetT);
+ qh vertex_visit++;
+ qh visit_id++;
+ do {
+ if (facet->toporient ^ qh_ORIENTclock) {
+ vertexA= SETfirstt_(facet->vertices, vertexT);
+ vertexB= SETsecondt_(facet->vertices, vertexT);
+ nextfacet= SETfirstt_(facet->neighbors, facetT);
+ }else {
+ vertexA= SETsecondt_(facet->vertices, vertexT);
+ vertexB= SETfirstt_(facet->vertices, vertexT);
+ nextfacet= SETsecondt_(facet->neighbors, facetT);
+ }
+ if (facet->visitid == qh visit_id) {
+ qh_fprintf(qh ferr, 6218, "Qhull internal error (qh_printextremes_2d): loop in facet list. facet %d nextfacet %d\n",
+ facet->id, nextfacet->id);
+ qh_errexit2(qh_ERRqhull, facet, nextfacet);
+ }
+ if (facet->visitid) {
+ if (vertexA->visitid != qh vertex_visit) {
+ vertexA->visitid= qh vertex_visit;
+ qh_fprintf(fp, 9089, "%d\n", qh_pointid(vertexA->point));
+ }
+ if (vertexB->visitid != qh vertex_visit) {
+ vertexB->visitid= qh vertex_visit;
+ qh_fprintf(fp, 9090, "%d\n", qh_pointid(vertexB->point));
+ }
+ }
+ facet->visitid= qh visit_id;
+ facet= nextfacet;
+ }while (facet && facet != startfacet);
+} /* printextremes_2d */
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="printextremes_d">-</a>
+
+ qh_printextremes_d( fp, facetlist, facets, printall )
+ print extreme points of input sites for Delaunay triangulations
+
+ notes:
+ #points, followed by ids, one per line
+
+ unordered
+*/
+void qh_printextremes_d(FILE *fp, facetT *facetlist, setT *facets, boolT printall) {
+ setT *vertices;
+ vertexT *vertex, **vertexp;
+ boolT upperseen, lowerseen;
+ facetT *neighbor, **neighborp;
+ int numpoints=0;
+
+ vertices= qh_facetvertices(facetlist, facets, printall);
+ qh_vertexneighbors();
+ FOREACHvertex_(vertices) {
+ upperseen= lowerseen= False;
+ FOREACHneighbor_(vertex) {
+ if (neighbor->upperdelaunay)
+ upperseen= True;
+ else
+ lowerseen= True;
+ }
+ if (upperseen && lowerseen) {
+ vertex->seen= True;
+ numpoints++;
+ }else
+ vertex->seen= False;
+ }
+ qh_fprintf(fp, 9091, "%d\n", numpoints);
+ FOREACHvertex_(vertices) {
+ if (vertex->seen)
+ qh_fprintf(fp, 9092, "%d\n", qh_pointid(vertex->point));
+ }
+ qh_settempfree(&vertices);
+} /* printextremes_d */
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="printfacet">-</a>
+
+ qh_printfacet( fp, facet )
+ prints all fields of a facet to fp
+
+ notes:
+ ridges printed in neighbor order
+*/
+void qh_printfacet(FILE *fp, facetT *facet) {
+
+ qh_printfacetheader(fp, facet);
+ if (facet->ridges)
+ qh_printfacetridges(fp, facet);
+} /* printfacet */
+
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="printfacet2geom">-</a>
+
+ qh_printfacet2geom( fp, facet, color )
+ print facet as part of a 2-d VECT for Geomview
+
+ notes:
+ assume precise calculations in io.c with roundoff covered by qh_GEOMepsilon
+ mindist is calculated within io.c. maxoutside is calculated elsewhere
+ so a DISTround error may have occurred.
+*/
+void qh_printfacet2geom(FILE *fp, facetT *facet, realT color[3]) {
+ pointT *point0, *point1;
+ realT mindist, innerplane, outerplane;
+ int k;
+
+ qh_facet2point(facet, &point0, &point1, &mindist);
+ qh_geomplanes(facet, &outerplane, &innerplane);
+ if (qh PRINTouter || (!qh PRINTnoplanes && !qh PRINTinner))
+ qh_printfacet2geom_points(fp, point0, point1, facet, outerplane, color);
+ if (qh PRINTinner || (!qh PRINTnoplanes && !qh PRINTouter &&
+ outerplane - innerplane > 2 * qh MAXabs_coord * qh_GEOMepsilon)) {
+ for (k=3; k--; )
+ color[k]= 1.0 - color[k];
+ qh_printfacet2geom_points(fp, point0, point1, facet, innerplane, color);
+ }
+ qh_memfree(point1, qh normal_size);
+ qh_memfree(point0, qh normal_size);
+} /* printfacet2geom */
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="printfacet2geom_points">-</a>
+
+ qh_printfacet2geom_points( fp, point1, point2, facet, offset, color )
+ prints a 2-d facet as a VECT with 2 points at some offset.
+ The points are on the facet's plane.
+*/
+void qh_printfacet2geom_points(FILE *fp, pointT *point1, pointT *point2,
+ facetT *facet, realT offset, realT color[3]) {
+ pointT *p1= point1, *p2= point2;
+
+ qh_fprintf(fp, 9093, "VECT 1 2 1 2 1 # f%d\n", facet->id);
+ if (offset != 0.0) {
+ p1= qh_projectpoint(p1, facet, -offset);
+ p2= qh_projectpoint(p2, facet, -offset);
+ }
+ qh_fprintf(fp, 9094, "%8.4g %8.4g %8.4g\n%8.4g %8.4g %8.4g\n",
+ p1[0], p1[1], 0.0, p2[0], p2[1], 0.0);
+ if (offset != 0.0) {
+ qh_memfree(p1, qh normal_size);
+ qh_memfree(p2, qh normal_size);
+ }
+ qh_fprintf(fp, 9095, "%8.4g %8.4g %8.4g 1.0\n", color[0], color[1], color[2]);
+} /* printfacet2geom_points */
+
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="printfacet2math">-</a>
+
+ qh_printfacet2math( fp, facet, format, notfirst )
+ print 2-d Maple or Mathematica output for a facet
+ may be non-simplicial
+
+ notes:
+ use %16.8f since Mathematica 2.2 does not handle exponential format
+ see qh_printfacet3math
+*/
+void qh_printfacet2math(FILE *fp, facetT *facet, qh_PRINT format, int notfirst) {
+ pointT *point0, *point1;
+ realT mindist;
+ const char *pointfmt;
+
+ qh_facet2point(facet, &point0, &point1, &mindist);
+ if (notfirst)
+ qh_fprintf(fp, 9096, ",");
+ if (format == qh_PRINTmaple)
+ pointfmt= "[[%16.8f, %16.8f], [%16.8f, %16.8f]]\n";
+ else
+ pointfmt= "Line[{{%16.8f, %16.8f}, {%16.8f, %16.8f}}]\n";
+ qh_fprintf(fp, 9097, pointfmt, point0[0], point0[1], point1[0], point1[1]);
+ qh_memfree(point1, qh normal_size);
+ qh_memfree(point0, qh normal_size);
+} /* printfacet2math */
+
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="printfacet3geom_nonsimplicial">-</a>
+
+ qh_printfacet3geom_nonsimplicial( fp, facet, color )
+ print Geomview OFF for a 3-d nonsimplicial facet.
+ if DOintersections, prints ridges to unvisited neighbors(qh visit_id)
+
+ notes
+ uses facet->visitid for intersections and ridges
+*/
+void qh_printfacet3geom_nonsimplicial(FILE *fp, facetT *facet, realT color[3]) {
+ ridgeT *ridge, **ridgep;
+ setT *projectedpoints, *vertices;
+ vertexT *vertex, **vertexp, *vertexA, *vertexB;
+ pointT *projpt, *point, **pointp;
+ facetT *neighbor;
+ realT dist, outerplane, innerplane;
+ int cntvertices, k;
+ realT black[3]={0, 0, 0}, green[3]={0, 1, 0};
+
+ qh_geomplanes(facet, &outerplane, &innerplane);
+ vertices= qh_facet3vertex(facet); /* oriented */
+ cntvertices= qh_setsize(vertices);
+ projectedpoints= qh_settemp(cntvertices);
+ FOREACHvertex_(vertices) {
+ zinc_(Zdistio);
+ qh_distplane(vertex->point, facet, &dist);
+ projpt= qh_projectpoint(vertex->point, facet, dist);
+ qh_setappend(&projectedpoints, projpt);
+ }
+ if (qh PRINTouter || (!qh PRINTnoplanes && !qh PRINTinner))
+ qh_printfacet3geom_points(fp, projectedpoints, facet, outerplane, color);
+ if (qh PRINTinner || (!qh PRINTnoplanes && !qh PRINTouter &&
+ outerplane - innerplane > 2 * qh MAXabs_coord * qh_GEOMepsilon)) {
+ for (k=3; k--; )
+ color[k]= 1.0 - color[k];
+ qh_printfacet3geom_points(fp, projectedpoints, facet, innerplane, color);
+ }
+ FOREACHpoint_(projectedpoints)
+ qh_memfree(point, qh normal_size);
+ qh_settempfree(&projectedpoints);
+ qh_settempfree(&vertices);
+ if ((qh DOintersections || qh PRINTridges)
+ && (!facet->visible || !qh NEWfacets)) {
+ facet->visitid= qh visit_id;
+ FOREACHridge_(facet->ridges) {
+ neighbor= otherfacet_(ridge, facet);
+ if (neighbor->visitid != qh visit_id) {
+ if (qh DOintersections)
+ qh_printhyperplaneintersection(fp, facet, neighbor, ridge->vertices, black);
+ if (qh PRINTridges) {
+ vertexA= SETfirstt_(ridge->vertices, vertexT);
+ vertexB= SETsecondt_(ridge->vertices, vertexT);
+ qh_printline3geom(fp, vertexA->point, vertexB->point, green);
+ }
+ }
+ }
+ }
+} /* printfacet3geom_nonsimplicial */
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="printfacet3geom_points">-</a>
+
+ qh_printfacet3geom_points( fp, points, facet, offset )
+ prints a 3-d facet as OFF Geomview object.
+ offset is relative to the facet's hyperplane
+ Facet is determined as a list of points
+*/
+void qh_printfacet3geom_points(FILE *fp, setT *points, facetT *facet, realT offset, realT color[3]) {
+ int k, n= qh_setsize(points), i;
+ pointT *point, **pointp;
+ setT *printpoints;
+
+ qh_fprintf(fp, 9098, "{ OFF %d 1 1 # f%d\n", n, facet->id);
+ if (offset != 0.0) {
+ printpoints= qh_settemp(n);
+ FOREACHpoint_(points)
+ qh_setappend(&printpoints, qh_projectpoint(point, facet, -offset));
+ }else
+ printpoints= points;
+ FOREACHpoint_(printpoints) {
+ for (k=0; k < qh hull_dim; k++) {
+ if (k == qh DROPdim)
+ qh_fprintf(fp, 9099, "0 ");
+ else
+ qh_fprintf(fp, 9100, "%8.4g ", point[k]);
+ }
+ if (printpoints != points)
+ qh_memfree(point, qh normal_size);
+ qh_fprintf(fp, 9101, "\n");
+ }
+ if (printpoints != points)
+ qh_settempfree(&printpoints);
+ qh_fprintf(fp, 9102, "%d ", n);
+ for (i=0; i < n; i++)
+ qh_fprintf(fp, 9103, "%d ", i);
+ qh_fprintf(fp, 9104, "%8.4g %8.4g %8.4g 1.0 }\n", color[0], color[1], color[2]);
+} /* printfacet3geom_points */
+
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="printfacet3geom_simplicial">-</a>
+
+ qh_printfacet3geom_simplicial( )
+ print Geomview OFF for a 3-d simplicial facet.
+
+ notes:
+ may flip color
+ uses facet->visitid for intersections and ridges
+
+ assume precise calculations in io.c with roundoff covered by qh_GEOMepsilon
+ innerplane may be off by qh DISTround. Maxoutside is calculated elsewhere
+ so a DISTround error may have occurred.
+*/
+void qh_printfacet3geom_simplicial(FILE *fp, facetT *facet, realT color[3]) {
+ setT *points, *vertices;
+ vertexT *vertex, **vertexp, *vertexA, *vertexB;
+ facetT *neighbor, **neighborp;
+ realT outerplane, innerplane;
+ realT black[3]={0, 0, 0}, green[3]={0, 1, 0};
+ int k;
+
+ qh_geomplanes(facet, &outerplane, &innerplane);
+ vertices= qh_facet3vertex(facet);
+ points= qh_settemp(qh TEMPsize);
+ FOREACHvertex_(vertices)
+ qh_setappend(&points, vertex->point);
+ if (qh PRINTouter || (!qh PRINTnoplanes && !qh PRINTinner))
+ qh_printfacet3geom_points(fp, points, facet, outerplane, color);
+ if (qh PRINTinner || (!qh PRINTnoplanes && !qh PRINTouter &&
+ outerplane - innerplane > 2 * qh MAXabs_coord * qh_GEOMepsilon)) {
+ for (k=3; k--; )
+ color[k]= 1.0 - color[k];
+ qh_printfacet3geom_points(fp, points, facet, innerplane, color);
+ }
+ qh_settempfree(&points);
+ qh_settempfree(&vertices);
+ if ((qh DOintersections || qh PRINTridges)
+ && (!facet->visible || !qh NEWfacets)) {
+ facet->visitid= qh visit_id;
+ FOREACHneighbor_(facet) {
+ if (neighbor->visitid != qh visit_id) {
+ vertices= qh_setnew_delnthsorted(facet->vertices, qh hull_dim,
+ SETindex_(facet->neighbors, neighbor), 0);
+ if (qh DOintersections)
+ qh_printhyperplaneintersection(fp, facet, neighbor, vertices, black);
+ if (qh PRINTridges) {
+ vertexA= SETfirstt_(vertices, vertexT);
+ vertexB= SETsecondt_(vertices, vertexT);
+ qh_printline3geom(fp, vertexA->point, vertexB->point, green);
+ }
+ qh_setfree(&vertices);
+ }
+ }
+ }
+} /* printfacet3geom_simplicial */
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="printfacet3math">-</a>
+
+ qh_printfacet3math( fp, facet, notfirst )
+ print 3-d Maple or Mathematica output for a facet
+
+ notes:
+ may be non-simplicial
+ use %16.8f since Mathematica 2.2 does not handle exponential format
+ see qh_printfacet2math
+*/
+void qh_printfacet3math(FILE *fp, facetT *facet, qh_PRINT format, int notfirst) {
+ vertexT *vertex, **vertexp;
+ setT *points, *vertices;
+ pointT *point, **pointp;
+ boolT firstpoint= True;
+ realT dist;
+ const char *pointfmt, *endfmt;
+
+ if (notfirst)
+ qh_fprintf(fp, 9105, ",\n");
+ vertices= qh_facet3vertex(facet);
+ points= qh_settemp(qh_setsize(vertices));
+ FOREACHvertex_(vertices) {
+ zinc_(Zdistio);
+ qh_distplane(vertex->point, facet, &dist);
+ point= qh_projectpoint(vertex->point, facet, dist);
+ qh_setappend(&points, point);
+ }
+ if (format == qh_PRINTmaple) {
+ qh_fprintf(fp, 9106, "[");
+ pointfmt= "[%16.8f, %16.8f, %16.8f]";
+ endfmt= "]";
+ }else {
+ qh_fprintf(fp, 9107, "Polygon[{");
+ pointfmt= "{%16.8f, %16.8f, %16.8f}";
+ endfmt= "}]";
+ }
+ FOREACHpoint_(points) {
+ if (firstpoint)
+ firstpoint= False;
+ else
+ qh_fprintf(fp, 9108, ",\n");
+ qh_fprintf(fp, 9109, pointfmt, point[0], point[1], point[2]);
+ }
+ FOREACHpoint_(points)
+ qh_memfree(point, qh normal_size);
+ qh_settempfree(&points);
+ qh_settempfree(&vertices);
+ qh_fprintf(fp, 9110, "%s", endfmt);
+} /* printfacet3math */
+
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="printfacet3vertex">-</a>
+
+ qh_printfacet3vertex( fp, facet, format )
+ print vertices in a 3-d facet as point ids
+
+ notes:
+ prints number of vertices first if format == qh_PRINToff
+ the facet may be non-simplicial
+*/
+void qh_printfacet3vertex(FILE *fp, facetT *facet, qh_PRINT format) {
+ vertexT *vertex, **vertexp;
+ setT *vertices;
+
+ vertices= qh_facet3vertex(facet);
+ if (format == qh_PRINToff)
+ qh_fprintf(fp, 9111, "%d ", qh_setsize(vertices));
+ FOREACHvertex_(vertices)
+ qh_fprintf(fp, 9112, "%d ", qh_pointid(vertex->point));
+ qh_fprintf(fp, 9113, "\n");
+ qh_settempfree(&vertices);
+} /* printfacet3vertex */
+
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="printfacet4geom_nonsimplicial">-</a>
+
+ qh_printfacet4geom_nonsimplicial( )
+ print Geomview 4OFF file for a 4d nonsimplicial facet
+ prints all ridges to unvisited neighbors (qh.visit_id)
+ if qh.DROPdim
+ prints in OFF format
+
+ notes:
+ must agree with printend4geom()
+*/
+void qh_printfacet4geom_nonsimplicial(FILE *fp, facetT *facet, realT color[3]) {
+ facetT *neighbor;
+ ridgeT *ridge, **ridgep;
+ vertexT *vertex, **vertexp;
+ pointT *point;
+ int k;
+ realT dist;
+
+ facet->visitid= qh visit_id;
+ if (qh PRINTnoplanes || (facet->visible && qh NEWfacets))
+ return;
+ FOREACHridge_(facet->ridges) {
+ neighbor= otherfacet_(ridge, facet);
+ if (neighbor->visitid == qh visit_id)
+ continue;
+ if (qh PRINTtransparent && !neighbor->good)
+ continue;
+ if (qh DOintersections)
+ qh_printhyperplaneintersection(fp, facet, neighbor, ridge->vertices, color);
+ else {
+ if (qh DROPdim >= 0)
+ qh_fprintf(fp, 9114, "OFF 3 1 1 # f%d\n", facet->id);
+ else {
+ qh printoutvar++;
+ qh_fprintf(fp, 9115, "# r%d between f%d f%d\n", ridge->id, facet->id, neighbor->id);
+ }
+ FOREACHvertex_(ridge->vertices) {
+ zinc_(Zdistio);
+ qh_distplane(vertex->point,facet, &dist);
+ point=qh_projectpoint(vertex->point,facet, dist);
+ for (k=0; k < qh hull_dim; k++) {
+ if (k != qh DROPdim)
+ qh_fprintf(fp, 9116, "%8.4g ", point[k]);
+ }
+ qh_fprintf(fp, 9117, "\n");
+ qh_memfree(point, qh normal_size);
+ }
+ if (qh DROPdim >= 0)
+ qh_fprintf(fp, 9118, "3 0 1 2 %8.4g %8.4g %8.4g\n", color[0], color[1], color[2]);
+ }
+ }
+} /* printfacet4geom_nonsimplicial */
+
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="printfacet4geom_simplicial">-</a>
+
+ qh_printfacet4geom_simplicial( fp, facet, color )
+ print Geomview 4OFF file for a 4d simplicial facet
+ prints triangles for unvisited neighbors (qh.visit_id)
+
+ notes:
+ must agree with printend4geom()
+*/
+void qh_printfacet4geom_simplicial(FILE *fp, facetT *facet, realT color[3]) {
+ setT *vertices;
+ facetT *neighbor, **neighborp;
+ vertexT *vertex, **vertexp;
+ int k;
+
+ facet->visitid= qh visit_id;
+ if (qh PRINTnoplanes || (facet->visible && qh NEWfacets))
+ return;
+ FOREACHneighbor_(facet) {
+ if (neighbor->visitid == qh visit_id)
+ continue;
+ if (qh PRINTtransparent && !neighbor->good)
+ continue;
+ vertices= qh_setnew_delnthsorted(facet->vertices, qh hull_dim,
+ SETindex_(facet->neighbors, neighbor), 0);
+ if (qh DOintersections)
+ qh_printhyperplaneintersection(fp, facet, neighbor, vertices, color);
+ else {
+ if (qh DROPdim >= 0)
+ qh_fprintf(fp, 9119, "OFF 3 1 1 # ridge between f%d f%d\n",
+ facet->id, neighbor->id);
+ else {
+ qh printoutvar++;
+ qh_fprintf(fp, 9120, "# ridge between f%d f%d\n", facet->id, neighbor->id);
+ }
+ FOREACHvertex_(vertices) {
+ for (k=0; k < qh hull_dim; k++) {
+ if (k != qh DROPdim)
+ qh_fprintf(fp, 9121, "%8.4g ", vertex->point[k]);
+ }
+ qh_fprintf(fp, 9122, "\n");
+ }
+ if (qh DROPdim >= 0)
+ qh_fprintf(fp, 9123, "3 0 1 2 %8.4g %8.4g %8.4g\n", color[0], color[1], color[2]);
+ }
+ qh_setfree(&vertices);
+ }
+} /* printfacet4geom_simplicial */
+
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="printfacetNvertex_nonsimplicial">-</a>
+
+ qh_printfacetNvertex_nonsimplicial( fp, facet, id, format )
+ print vertices for an N-d non-simplicial facet
+ triangulates each ridge to the id
+*/
+void qh_printfacetNvertex_nonsimplicial(FILE *fp, facetT *facet, int id, qh_PRINT format) {
+ vertexT *vertex, **vertexp;
+ ridgeT *ridge, **ridgep;
+
+ if (facet->visible && qh NEWfacets)
+ return;
+ FOREACHridge_(facet->ridges) {
+ if (format == qh_PRINTtriangles)
+ qh_fprintf(fp, 9124, "%d ", qh hull_dim);
+ qh_fprintf(fp, 9125, "%d ", id);
+ if ((ridge->top == facet) ^ qh_ORIENTclock) {
+ FOREACHvertex_(ridge->vertices)
+ qh_fprintf(fp, 9126, "%d ", qh_pointid(vertex->point));
+ }else {
+ FOREACHvertexreverse12_(ridge->vertices)
+ qh_fprintf(fp, 9127, "%d ", qh_pointid(vertex->point));
+ }
+ qh_fprintf(fp, 9128, "\n");
+ }
+} /* printfacetNvertex_nonsimplicial */
+
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="printfacetNvertex_simplicial">-</a>
+
+ qh_printfacetNvertex_simplicial( fp, facet, format )
+ print vertices for an N-d simplicial facet
+ prints vertices for non-simplicial facets
+ 2-d facets (orientation preserved by qh_mergefacet2d)
+ PRINToff ('o') for 4-d and higher
+*/
+void qh_printfacetNvertex_simplicial(FILE *fp, facetT *facet, qh_PRINT format) {
+ vertexT *vertex, **vertexp;
+
+ if (format == qh_PRINToff || format == qh_PRINTtriangles)
+ qh_fprintf(fp, 9129, "%d ", qh_setsize(facet->vertices));
+ if ((facet->toporient ^ qh_ORIENTclock)
+ || (qh hull_dim > 2 && !facet->simplicial)) {
+ FOREACHvertex_(facet->vertices)
+ qh_fprintf(fp, 9130, "%d ", qh_pointid(vertex->point));
+ }else {
+ FOREACHvertexreverse12_(facet->vertices)
+ qh_fprintf(fp, 9131, "%d ", qh_pointid(vertex->point));
+ }
+ qh_fprintf(fp, 9132, "\n");
+} /* printfacetNvertex_simplicial */
+
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="printfacetheader">-</a>
+
+ qh_printfacetheader( fp, facet )
+ prints header fields of a facet to fp
+
+ notes:
+ for 'f' output and debugging
+ Same as QhullFacet::printHeader()
+*/
+void qh_printfacetheader(FILE *fp, facetT *facet) {
+ pointT *point, **pointp, *furthest;
+ facetT *neighbor, **neighborp;
+ realT dist;
+
+ if (facet == qh_MERGEridge) {
+ qh_fprintf(fp, 9133, " MERGEridge\n");
+ return;
+ }else if (facet == qh_DUPLICATEridge) {
+ qh_fprintf(fp, 9134, " DUPLICATEridge\n");
+ return;
+ }else if (!facet) {
+ qh_fprintf(fp, 9135, " NULLfacet\n");
+ return;
+ }
+ qh old_randomdist= qh RANDOMdist;
+ qh RANDOMdist= False;
+ qh_fprintf(fp, 9136, "- f%d\n", facet->id);
+ qh_fprintf(fp, 9137, " - flags:");
+ if (facet->toporient)
+ qh_fprintf(fp, 9138, " top");
+ else
+ qh_fprintf(fp, 9139, " bottom");
+ if (facet->simplicial)
+ qh_fprintf(fp, 9140, " simplicial");
+ if (facet->tricoplanar)
+ qh_fprintf(fp, 9141, " tricoplanar");
+ if (facet->upperdelaunay)
+ qh_fprintf(fp, 9142, " upperDelaunay");
+ if (facet->visible)
+ qh_fprintf(fp, 9143, " visible");
+ if (facet->newfacet)
+ qh_fprintf(fp, 9144, " new");
+ if (facet->tested)
+ qh_fprintf(fp, 9145, " tested");
+ if (!facet->good)
+ qh_fprintf(fp, 9146, " notG");
+ if (facet->seen)
+ qh_fprintf(fp, 9147, " seen");
+ if (facet->coplanar)
+ qh_fprintf(fp, 9148, " coplanar");
+ if (facet->mergehorizon)
+ qh_fprintf(fp, 9149, " mergehorizon");
+ if (facet->keepcentrum)
+ qh_fprintf(fp, 9150, " keepcentrum");
+ if (facet->dupridge)
+ qh_fprintf(fp, 9151, " dupridge");
+ if (facet->mergeridge && !facet->mergeridge2)
+ qh_fprintf(fp, 9152, " mergeridge1");
+ if (facet->mergeridge2)
+ qh_fprintf(fp, 9153, " mergeridge2");
+ if (facet->newmerge)
+ qh_fprintf(fp, 9154, " newmerge");
+ if (facet->flipped)
+ qh_fprintf(fp, 9155, " flipped");
+ if (facet->notfurthest)
+ qh_fprintf(fp, 9156, " notfurthest");
+ if (facet->degenerate)
+ qh_fprintf(fp, 9157, " degenerate");
+ if (facet->redundant)
+ qh_fprintf(fp, 9158, " redundant");
+ qh_fprintf(fp, 9159, "\n");
+ if (facet->isarea)
+ qh_fprintf(fp, 9160, " - area: %2.2g\n", facet->f.area);
+ else if (qh NEWfacets && facet->visible && facet->f.replace)
+ qh_fprintf(fp, 9161, " - replacement: f%d\n", facet->f.replace->id);
+ else if (facet->newfacet) {
+ if (facet->f.samecycle && facet->f.samecycle != facet)
+ qh_fprintf(fp, 9162, " - shares same visible/horizon as f%d\n", facet->f.samecycle->id);
+ }else if (facet->tricoplanar /* !isarea */) {
+ if (facet->f.triowner)
+ qh_fprintf(fp, 9163, " - owner of normal & centrum is facet f%d\n", facet->f.triowner->id);
+ }else if (facet->f.newcycle)
+ qh_fprintf(fp, 9164, " - was horizon to f%d\n", facet->f.newcycle->id);
+ if (facet->nummerge)
+ qh_fprintf(fp, 9165, " - merges: %d\n", facet->nummerge);
+ qh_printpointid(fp, " - normal: ", qh hull_dim, facet->normal, qh_IDunknown);
+ qh_fprintf(fp, 9166, " - offset: %10.7g\n", facet->offset);
+ if (qh CENTERtype == qh_ASvoronoi || facet->center)
+ qh_printcenter(fp, qh_PRINTfacets, " - center: ", facet);
+#if qh_MAXoutside
+ if (facet->maxoutside > qh DISTround)
+ qh_fprintf(fp, 9167, " - maxoutside: %10.7g\n", facet->maxoutside);
+#endif
+ if (!SETempty_(facet->outsideset)) {
+ furthest= (pointT*)qh_setlast(facet->outsideset);
+ if (qh_setsize(facet->outsideset) < 6) {
+ qh_fprintf(fp, 9168, " - outside set(furthest p%d):\n", qh_pointid(furthest));
+ FOREACHpoint_(facet->outsideset)
+ qh_printpoint(fp, " ", point);
+ }else if (qh_setsize(facet->outsideset) < 21) {
+ qh_printpoints(fp, " - outside set:", facet->outsideset);
+ }else {
+ qh_fprintf(fp, 9169, " - outside set: %d points.", qh_setsize(facet->outsideset));
+ qh_printpoint(fp, " Furthest", furthest);
+ }
+#if !qh_COMPUTEfurthest
+ qh_fprintf(fp, 9170, " - furthest distance= %2.2g\n", facet->furthestdist);
+#endif
+ }
+ if (!SETempty_(facet->coplanarset)) {
+ furthest= (pointT*)qh_setlast(facet->coplanarset);
+ if (qh_setsize(facet->coplanarset) < 6) {
+ qh_fprintf(fp, 9171, " - coplanar set(furthest p%d):\n", qh_pointid(furthest));
+ FOREACHpoint_(facet->coplanarset)
+ qh_printpoint(fp, " ", point);
+ }else if (qh_setsize(facet->coplanarset) < 21) {
+ qh_printpoints(fp, " - coplanar set:", facet->coplanarset);
+ }else {
+ qh_fprintf(fp, 9172, " - coplanar set: %d points.", qh_setsize(facet->coplanarset));
+ qh_printpoint(fp, " Furthest", furthest);
+ }
+ zinc_(Zdistio);
+ qh_distplane(furthest, facet, &dist);
+ qh_fprintf(fp, 9173, " furthest distance= %2.2g\n", dist);
+ }
+ qh_printvertices(fp, " - vertices:", facet->vertices);
+ qh_fprintf(fp, 9174, " - neighboring facets:");
+ FOREACHneighbor_(facet) {
+ if (neighbor == qh_MERGEridge)
+ qh_fprintf(fp, 9175, " MERGE");
+ else if (neighbor == qh_DUPLICATEridge)
+ qh_fprintf(fp, 9176, " DUP");
+ else
+ qh_fprintf(fp, 9177, " f%d", neighbor->id);
+ }
+ qh_fprintf(fp, 9178, "\n");
+ qh RANDOMdist= qh old_randomdist;
+} /* printfacetheader */
+
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="printfacetridges">-</a>
+
+ qh_printfacetridges( fp, facet )
+ prints ridges of a facet to fp
+
+ notes:
+ ridges printed in neighbor order
+ assumes the ridges exist
+ for 'f' output
+ same as QhullFacet::printRidges
+*/
+void qh_printfacetridges(FILE *fp, facetT *facet) {
+ facetT *neighbor, **neighborp;
+ ridgeT *ridge, **ridgep;
+ int numridges= 0;
+
+
+ if (facet->visible && qh NEWfacets) {
+ qh_fprintf(fp, 9179, " - ridges(ids may be garbage):");
+ FOREACHridge_(facet->ridges)
+ qh_fprintf(fp, 9180, " r%d", ridge->id);
+ qh_fprintf(fp, 9181, "\n");
+ }else {
+ qh_fprintf(fp, 9182, " - ridges:\n");
+ FOREACHridge_(facet->ridges)
+ ridge->seen= False;
+ if (qh hull_dim == 3) {
+ ridge= SETfirstt_(facet->ridges, ridgeT);
+ while (ridge && !ridge->seen) {
+ ridge->seen= True;
+ qh_printridge(fp, ridge);
+ numridges++;
+ ridge= qh_nextridge3d(ridge, facet, NULL);
+ }
+ }else {
+ FOREACHneighbor_(facet) {
+ FOREACHridge_(facet->ridges) {
+ if (otherfacet_(ridge,facet) == neighbor) {
+ ridge->seen= True;
+ qh_printridge(fp, ridge);
+ numridges++;
+ }
+ }
+ }
+ }
+ if (numridges != qh_setsize(facet->ridges)) {
+ qh_fprintf(fp, 9183, " - all ridges:");
+ FOREACHridge_(facet->ridges)
+ qh_fprintf(fp, 9184, " r%d", ridge->id);
+ qh_fprintf(fp, 9185, "\n");
+ }
+ FOREACHridge_(facet->ridges) {
+ if (!ridge->seen)
+ qh_printridge(fp, ridge);
+ }
+ }
+} /* printfacetridges */
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="printfacets">-</a>
+
+ qh_printfacets( fp, format, facetlist, facets, printall )
+ prints facetlist and/or facet set in output format
+
+ notes:
+ also used for specialized formats ('FO' and summary)
+ turns off 'Rn' option since want actual numbers
+*/
+void qh_printfacets(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) {
+ int numfacets, numsimplicial, numridges, totneighbors, numcoplanars, numtricoplanars;
+ facetT *facet, **facetp;
+ setT *vertices;
+ coordT *center;
+ realT outerplane, innerplane;
+
+ qh old_randomdist= qh RANDOMdist;
+ qh RANDOMdist= False;
+ if (qh CDDoutput && (format == qh_PRINTcentrums || format == qh_PRINTpointintersect || format == qh_PRINToff))
+ qh_fprintf(qh ferr, 7056, "qhull warning: CDD format is not available for centrums, halfspace\nintersections, and OFF file format.\n");
+ if (format == qh_PRINTnone)
+ ; /* print nothing */
+ else if (format == qh_PRINTaverage) {
+ vertices= qh_facetvertices(facetlist, facets, printall);
+ center= qh_getcenter(vertices);
+ qh_fprintf(fp, 9186, "%d 1\n", qh hull_dim);
+ qh_printpointid(fp, NULL, qh hull_dim, center, qh_IDunknown);
+ qh_memfree(center, qh normal_size);
+ qh_settempfree(&vertices);
+ }else if (format == qh_PRINTextremes) {
+ if (qh DELAUNAY)
+ qh_printextremes_d(fp, facetlist, facets, printall);
+ else if (qh hull_dim == 2)
+ qh_printextremes_2d(fp, facetlist, facets, printall);
+ else
+ qh_printextremes(fp, facetlist, facets, printall);
+ }else if (format == qh_PRINToptions)
+ qh_fprintf(fp, 9187, "Options selected for Qhull %s:\n%s\n", qh_version, qh qhull_options);
+ else if (format == qh_PRINTpoints && !qh VORONOI)
+ qh_printpoints_out(fp, facetlist, facets, printall);
+ else if (format == qh_PRINTqhull)
+ qh_fprintf(fp, 9188, "%s | %s\n", qh rbox_command, qh qhull_command);
+ else if (format == qh_PRINTsize) {
+ qh_fprintf(fp, 9189, "0\n2 ");
+ qh_fprintf(fp, 9190, qh_REAL_1, qh totarea);
+ qh_fprintf(fp, 9191, qh_REAL_1, qh totvol);
+ qh_fprintf(fp, 9192, "\n");
+ }else if (format == qh_PRINTsummary) {
+ qh_countfacets(facetlist, facets, printall, &numfacets, &numsimplicial,
+ &totneighbors, &numridges, &numcoplanars, &numtricoplanars);
+ vertices= qh_facetvertices(facetlist, facets, printall);
+ qh_fprintf(fp, 9193, "10 %d %d %d %d %d %d %d %d %d %d\n2 ", qh hull_dim,
+ qh num_points + qh_setsize(qh other_points),
+ qh num_vertices, qh num_facets - qh num_visible,
+ qh_setsize(vertices), numfacets, numcoplanars,
+ numfacets - numsimplicial, zzval_(Zdelvertextot),
+ numtricoplanars);
+ qh_settempfree(&vertices);
+ qh_outerinner(NULL, &outerplane, &innerplane);
+ qh_fprintf(fp, 9194, qh_REAL_2n, outerplane, innerplane);
+ }else if (format == qh_PRINTvneighbors)
+ qh_printvneighbors(fp, facetlist, facets, printall);
+ else if (qh VORONOI && format == qh_PRINToff)
+ qh_printvoronoi(fp, format, facetlist, facets, printall);
+ else if (qh VORONOI && format == qh_PRINTgeom) {
+ qh_printbegin(fp, format, facetlist, facets, printall);
+ qh_printvoronoi(fp, format, facetlist, facets, printall);
+ qh_printend(fp, format, facetlist, facets, printall);
+ }else if (qh VORONOI
+ && (format == qh_PRINTvertices || format == qh_PRINTinner || format == qh_PRINTouter))
+ qh_printvdiagram(fp, format, facetlist, facets, printall);
+ else {
+ qh_printbegin(fp, format, facetlist, facets, printall);
+ FORALLfacet_(facetlist)
+ qh_printafacet(fp, format, facet, printall);
+ FOREACHfacet_(facets)
+ qh_printafacet(fp, format, facet, printall);
+ qh_printend(fp, format, facetlist, facets, printall);
+ }
+ qh RANDOMdist= qh old_randomdist;
+} /* printfacets */
+
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="printhyperplaneintersection">-</a>
+
+ qh_printhyperplaneintersection( fp, facet1, facet2, vertices, color )
+ print Geomview OFF or 4OFF for the intersection of two hyperplanes in 3-d or 4-d
+*/
+void qh_printhyperplaneintersection(FILE *fp, facetT *facet1, facetT *facet2,
+ setT *vertices, realT color[3]) {
+ realT costheta, denominator, dist1, dist2, s, t, mindenom, p[4];
+ vertexT *vertex, **vertexp;
+ int i, k;
+ boolT nearzero1, nearzero2;
+
+ costheta= qh_getangle(facet1->normal, facet2->normal);
+ denominator= 1 - costheta * costheta;
+ i= qh_setsize(vertices);
+ if (qh hull_dim == 3)
+ qh_fprintf(fp, 9195, "VECT 1 %d 1 %d 1 ", i, i);
+ else if (qh hull_dim == 4 && qh DROPdim >= 0)
+ qh_fprintf(fp, 9196, "OFF 3 1 1 ");
+ else
+ qh printoutvar++;
+ qh_fprintf(fp, 9197, "# intersect f%d f%d\n", facet1->id, facet2->id);
+ mindenom= 1 / (10.0 * qh MAXabs_coord);
+ FOREACHvertex_(vertices) {
+ zadd_(Zdistio, 2);
+ qh_distplane(vertex->point, facet1, &dist1);
+ qh_distplane(vertex->point, facet2, &dist2);
+ s= qh_divzero(-dist1 + costheta * dist2, denominator,mindenom,&nearzero1);
+ t= qh_divzero(-dist2 + costheta * dist1, denominator,mindenom,&nearzero2);
+ if (nearzero1 || nearzero2)
+ s= t= 0.0;
+ for (k=qh hull_dim; k--; )
+ p[k]= vertex->point[k] + facet1->normal[k] * s + facet2->normal[k] * t;
+ if (qh PRINTdim <= 3) {
+ qh_projectdim3(p, p);
+ qh_fprintf(fp, 9198, "%8.4g %8.4g %8.4g # ", p[0], p[1], p[2]);
+ }else
+ qh_fprintf(fp, 9199, "%8.4g %8.4g %8.4g %8.4g # ", p[0], p[1], p[2], p[3]);
+ if (nearzero1+nearzero2)
+ qh_fprintf(fp, 9200, "p%d(coplanar facets)\n", qh_pointid(vertex->point));
+ else
+ qh_fprintf(fp, 9201, "projected p%d\n", qh_pointid(vertex->point));
+ }
+ if (qh hull_dim == 3)
+ qh_fprintf(fp, 9202, "%8.4g %8.4g %8.4g 1.0\n", color[0], color[1], color[2]);
+ else if (qh hull_dim == 4 && qh DROPdim >= 0)
+ qh_fprintf(fp, 9203, "3 0 1 2 %8.4g %8.4g %8.4g 1.0\n", color[0], color[1], color[2]);
+} /* printhyperplaneintersection */
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="printline3geom">-</a>
+
+ qh_printline3geom( fp, pointA, pointB, color )
+ prints a line as a VECT
+ prints 0's for qh.DROPdim
+
+ notes:
+ if pointA == pointB,
+ it's a 1 point VECT
+*/
+void qh_printline3geom(FILE *fp, pointT *pointA, pointT *pointB, realT color[3]) {
+ int k;
+ realT pA[4], pB[4];
+
+ qh_projectdim3(pointA, pA);
+ qh_projectdim3(pointB, pB);
+ if ((fabs(pA[0] - pB[0]) > 1e-3) ||
+ (fabs(pA[1] - pB[1]) > 1e-3) ||
+ (fabs(pA[2] - pB[2]) > 1e-3)) {
+ qh_fprintf(fp, 9204, "VECT 1 2 1 2 1\n");
+ for (k=0; k < 3; k++)
+ qh_fprintf(fp, 9205, "%8.4g ", pB[k]);
+ qh_fprintf(fp, 9206, " # p%d\n", qh_pointid(pointB));
+ }else
+ qh_fprintf(fp, 9207, "VECT 1 1 1 1 1\n");
+ for (k=0; k < 3; k++)
+ qh_fprintf(fp, 9208, "%8.4g ", pA[k]);
+ qh_fprintf(fp, 9209, " # p%d\n", qh_pointid(pointA));
+ qh_fprintf(fp, 9210, "%8.4g %8.4g %8.4g 1\n", color[0], color[1], color[2]);
+}
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="printneighborhood">-</a>
+
+ qh_printneighborhood( fp, format, facetA, facetB, printall )
+ print neighborhood of one or two facets
+
+ notes:
+ calls qh_findgood_all()
+ bumps qh.visit_id
+*/
+void qh_printneighborhood(FILE *fp, qh_PRINT format, facetT *facetA, facetT *facetB, boolT printall) {
+ facetT *neighbor, **neighborp, *facet;
+ setT *facets;
+
+ if (format == qh_PRINTnone)
+ return;
+ qh_findgood_all(qh facet_list);
+ if (facetA == facetB)
+ facetB= NULL;
+ facets= qh_settemp(2*(qh_setsize(facetA->neighbors)+1));
+ qh visit_id++;
+ for (facet= facetA; facet; facet= ((facet == facetA) ? facetB : NULL)) {
+ if (facet->visitid != qh visit_id) {
+ facet->visitid= qh visit_id;
+ qh_setappend(&facets, facet);
+ }
+ FOREACHneighbor_(facet) {
+ if (neighbor->visitid == qh visit_id)
+ continue;
+ neighbor->visitid= qh visit_id;
+ if (printall || !qh_skipfacet(neighbor))
+ qh_setappend(&facets, neighbor);
+ }
+ }
+ qh_printfacets(fp, format, NULL, facets, printall);
+ qh_settempfree(&facets);
+} /* printneighborhood */
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="printpoint">-</a>
+
+ qh_printpoint( fp, string, point )
+ qh_printpointid( fp, string, dim, point, id )
+ prints the coordinates of a point
+
+ returns:
+ if string is defined
+ prints 'string p%d'. Skips p%d if id=qh_IDunknown(-1) or qh_IDnone(-3)
+
+ notes:
+ nop if point is NULL
+ Same as QhullPoint's printPoint
+*/
+void qh_printpoint(FILE *fp, const char *string, pointT *point) {
+ int id= qh_pointid( point);
+
+ qh_printpointid( fp, string, qh hull_dim, point, id);
+} /* printpoint */
+
+void qh_printpointid(FILE *fp, const char *string, int dim, pointT *point, int id) {
+ int k;
+ realT r; /*bug fix*/
+
+ if (!point)
+ return;
+ if (string) {
+ qh_fprintf(fp, 9211, "%s", string);
+ if (id != qh_IDunknown && id != qh_IDnone)
+ qh_fprintf(fp, 9212, " p%d: ", id);
+ }
+ for (k=dim; k--; ) {
+ r= *point++;
+ if (string)
+ qh_fprintf(fp, 9213, " %8.4g", r);
+ else
+ qh_fprintf(fp, 9214, qh_REAL_1, r);
+ }
+ qh_fprintf(fp, 9215, "\n");
+} /* printpointid */
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="printpoint3">-</a>
+
+ qh_printpoint3( fp, point )
+ prints 2-d, 3-d, or 4-d point as Geomview 3-d coordinates
+*/
+void qh_printpoint3(FILE *fp, pointT *point) {
+ int k;
+ realT p[4];
+
+ qh_projectdim3(point, p);
+ for (k=0; k < 3; k++)
+ qh_fprintf(fp, 9216, "%8.4g ", p[k]);
+ qh_fprintf(fp, 9217, " # p%d\n", qh_pointid(point));
+} /* printpoint3 */
+
+/*----------------------------------------
+-printpoints- print pointids for a set of points starting at index
+ see geom.c
+*/
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="printpoints_out">-</a>
+
+ qh_printpoints_out( fp, facetlist, facets, printall )
+ prints vertices, coplanar/inside points, for facets by their point coordinates
+ allows qh.CDDoutput
+
+ notes:
+ same format as qhull input
+ if no coplanar/interior points,
+ same order as qh_printextremes
+*/
+void qh_printpoints_out(FILE *fp, facetT *facetlist, setT *facets, boolT printall) {
+ int allpoints= qh num_points + qh_setsize(qh other_points);
+ int numpoints=0, point_i, point_n;
+ setT *vertices, *points;
+ facetT *facet, **facetp;
+ pointT *point, **pointp;
+ vertexT *vertex, **vertexp;
+ int id;
+
+ points= qh_settemp(allpoints);
+ qh_setzero(points, 0, allpoints);
+ vertices= qh_facetvertices(facetlist, facets, printall);
+ FOREACHvertex_(vertices) {
+ id= qh_pointid(vertex->point);
+ if (id >= 0)
+ SETelem_(points, id)= vertex->point;
+ }
+ if (qh KEEPinside || qh KEEPcoplanar || qh KEEPnearinside) {
+ FORALLfacet_(facetlist) {
+ if (!printall && qh_skipfacet(facet))
+ continue;
+ FOREACHpoint_(facet->coplanarset) {
+ id= qh_pointid(point);
+ if (id >= 0)
+ SETelem_(points, id)= point;
+ }
+ }
+ FOREACHfacet_(facets) {
+ if (!printall && qh_skipfacet(facet))
+ continue;
+ FOREACHpoint_(facet->coplanarset) {
+ id= qh_pointid(point);
+ if (id >= 0)
+ SETelem_(points, id)= point;
+ }
+ }
+ }
+ qh_settempfree(&vertices);
+ FOREACHpoint_i_(points) {
+ if (point)
+ numpoints++;
+ }
+ if (qh CDDoutput)
+ qh_fprintf(fp, 9218, "%s | %s\nbegin\n%d %d real\n", qh rbox_command,
+ qh qhull_command, numpoints, qh hull_dim + 1);
+ else
+ qh_fprintf(fp, 9219, "%d\n%d\n", qh hull_dim, numpoints);
+ FOREACHpoint_i_(points) {
+ if (point) {
+ if (qh CDDoutput)
+ qh_fprintf(fp, 9220, "1 ");
+ qh_printpoint(fp, NULL, point);
+ }
+ }
+ if (qh CDDoutput)
+ qh_fprintf(fp, 9221, "end\n");
+ qh_settempfree(&points);
+} /* printpoints_out */
+
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="printpointvect">-</a>
+
+ qh_printpointvect( fp, point, normal, center, radius, color )
+ prints a 2-d, 3-d, or 4-d point as 3-d VECT's relative to normal or to center point
+*/
+void qh_printpointvect(FILE *fp, pointT *point, coordT *normal, pointT *center, realT radius, realT color[3]) {
+ realT diff[4], pointA[4];
+ int k;
+
+ for (k=qh hull_dim; k--; ) {
+ if (center)
+ diff[k]= point[k]-center[k];
+ else if (normal)
+ diff[k]= normal[k];
+ else
+ diff[k]= 0;
+ }
+ if (center)
+ qh_normalize2(diff, qh hull_dim, True, NULL, NULL);
+ for (k=qh hull_dim; k--; )
+ pointA[k]= point[k]+diff[k] * radius;
+ qh_printline3geom(fp, point, pointA, color);
+} /* printpointvect */
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="printpointvect2">-</a>
+
+ qh_printpointvect2( fp, point, normal, center, radius )
+ prints a 2-d, 3-d, or 4-d point as 2 3-d VECT's for an imprecise point
+*/
+void qh_printpointvect2(FILE *fp, pointT *point, coordT *normal, pointT *center, realT radius) {
+ realT red[3]={1, 0, 0}, yellow[3]={1, 1, 0};
+
+ qh_printpointvect(fp, point, normal, center, radius, red);
+ qh_printpointvect(fp, point, normal, center, -radius, yellow);
+} /* printpointvect2 */
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="printridge">-</a>
+
+ qh_printridge( fp, ridge )
+ prints the information in a ridge
+
+ notes:
+ for qh_printfacetridges()
+ same as operator<< [QhullRidge.cpp]
+*/
+void qh_printridge(FILE *fp, ridgeT *ridge) {
+
+ qh_fprintf(fp, 9222, " - r%d", ridge->id);
+ if (ridge->tested)
+ qh_fprintf(fp, 9223, " tested");
+ if (ridge->nonconvex)
+ qh_fprintf(fp, 9224, " nonconvex");
+ qh_fprintf(fp, 9225, "\n");
+ qh_printvertices(fp, " vertices:", ridge->vertices);
+ if (ridge->top && ridge->bottom)
+ qh_fprintf(fp, 9226, " between f%d and f%d\n",
+ ridge->top->id, ridge->bottom->id);
+} /* printridge */
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="printspheres">-</a>
+
+ qh_printspheres( fp, vertices, radius )
+ prints 3-d vertices as OFF spheres
+
+ notes:
+ inflated octahedron from Stuart Levy earth/mksphere2
+*/
+void qh_printspheres(FILE *fp, setT *vertices, realT radius) {
+ vertexT *vertex, **vertexp;
+
+ qh printoutnum++;
+ qh_fprintf(fp, 9227, "{appearance {-edge -normal normscale 0} {\n\
+INST geom {define vsphere OFF\n\
+18 32 48\n\
+\n\
+0 0 1\n\
+1 0 0\n\
+0 1 0\n\
+-1 0 0\n\
+0 -1 0\n\
+0 0 -1\n\
+0.707107 0 0.707107\n\
+0 -0.707107 0.707107\n\
+0.707107 -0.707107 0\n\
+-0.707107 0 0.707107\n\
+-0.707107 -0.707107 0\n\
+0 0.707107 0.707107\n\
+-0.707107 0.707107 0\n\
+0.707107 0.707107 0\n\
+0.707107 0 -0.707107\n\
+0 0.707107 -0.707107\n\
+-0.707107 0 -0.707107\n\
+0 -0.707107 -0.707107\n\
+\n\
+3 0 6 11\n\
+3 0 7 6 \n\
+3 0 9 7 \n\
+3 0 11 9\n\
+3 1 6 8 \n\
+3 1 8 14\n\
+3 1 13 6\n\
+3 1 14 13\n\
+3 2 11 13\n\
+3 2 12 11\n\
+3 2 13 15\n\
+3 2 15 12\n\
+3 3 9 12\n\
+3 3 10 9\n\
+3 3 12 16\n\
+3 3 16 10\n\
+3 4 7 10\n\
+3 4 8 7\n\
+3 4 10 17\n\
+3 4 17 8\n\
+3 5 14 17\n\
+3 5 15 14\n\
+3 5 16 15\n\
+3 5 17 16\n\
+3 6 13 11\n\
+3 7 8 6\n\
+3 9 10 7\n\
+3 11 12 9\n\
+3 14 8 17\n\
+3 15 13 14\n\
+3 16 12 15\n\
+3 17 10 16\n} transforms { TLIST\n");
+ FOREACHvertex_(vertices) {
+ qh_fprintf(fp, 9228, "%8.4g 0 0 0 # v%d\n 0 %8.4g 0 0\n0 0 %8.4g 0\n",
+ radius, vertex->id, radius, radius);
+ qh_printpoint3(fp, vertex->point);
+ qh_fprintf(fp, 9229, "1\n");
+ }
+ qh_fprintf(fp, 9230, "}}}\n");
+} /* printspheres */
+
+
+/*----------------------------------------------
+-printsummary-
+ see libqhull.c
+*/
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="printvdiagram">-</a>
+
+ qh_printvdiagram( fp, format, facetlist, facets, printall )
+ print voronoi diagram
+ # of pairs of input sites
+ #indices site1 site2 vertex1 ...
+
+ sites indexed by input point id
+ point 0 is the first input point
+ vertices indexed by 'o' and 'p' order
+ vertex 0 is the 'vertex-at-infinity'
+ vertex 1 is the first Voronoi vertex
+
+ see:
+ qh_printvoronoi()
+ qh_eachvoronoi_all()
+
+ notes:
+ if all facets are upperdelaunay,
+ prints upper hull (furthest-site Voronoi diagram)
+*/
+void qh_printvdiagram(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) {
+ setT *vertices;
+ int totcount, numcenters;
+ boolT isLower;
+ qh_RIDGE innerouter= qh_RIDGEall;
+ printvridgeT printvridge= NULL;
+
+ if (format == qh_PRINTvertices) {
+ innerouter= qh_RIDGEall;
+ printvridge= qh_printvridge;
+ }else if (format == qh_PRINTinner) {
+ innerouter= qh_RIDGEinner;
+ printvridge= qh_printvnorm;
+ }else if (format == qh_PRINTouter) {
+ innerouter= qh_RIDGEouter;
+ printvridge= qh_printvnorm;
+ }else {
+ qh_fprintf(qh ferr, 6219, "Qhull internal error (qh_printvdiagram): unknown print format %d.\n", format);
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ vertices= qh_markvoronoi(facetlist, facets, printall, &isLower, &numcenters);
+ totcount= qh_printvdiagram2(NULL, NULL, vertices, innerouter, False);
+ qh_fprintf(fp, 9231, "%d\n", totcount);
+ totcount= qh_printvdiagram2(fp, printvridge, vertices, innerouter, True /* inorder*/);
+ qh_settempfree(&vertices);
+#if 0 /* for testing qh_eachvoronoi_all */
+ qh_fprintf(fp, 9232, "\n");
+ totcount= qh_eachvoronoi_all(fp, printvridge, qh UPPERdelaunay, innerouter, True /* inorder*/);
+ qh_fprintf(fp, 9233, "%d\n", totcount);
+#endif
+} /* printvdiagram */
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="printvdiagram2">-</a>
+
+ qh_printvdiagram2( fp, printvridge, vertices, innerouter, inorder )
+ visit all pairs of input sites (vertices) for selected Voronoi vertices
+ vertices may include NULLs
+
+ innerouter:
+ qh_RIDGEall print inner ridges(bounded) and outer ridges(unbounded)
+ qh_RIDGEinner print only inner ridges
+ qh_RIDGEouter print only outer ridges
+
+ inorder:
+ print 3-d Voronoi vertices in order
+
+ assumes:
+ qh_markvoronoi marked facet->visitid for Voronoi vertices
+ all facet->seen= False
+ all facet->seen2= True
+
+ returns:
+ total number of Voronoi ridges
+ if printvridge,
+ calls printvridge( fp, vertex, vertexA, centers) for each ridge
+ [see qh_eachvoronoi()]
+
+ see:
+ qh_eachvoronoi_all()
+*/
+int qh_printvdiagram2(FILE *fp, printvridgeT printvridge, setT *vertices, qh_RIDGE innerouter, boolT inorder) {
+ int totcount= 0;
+ int vertex_i, vertex_n;
+ vertexT *vertex;
+
+ FORALLvertices
+ vertex->seen= False;
+ FOREACHvertex_i_(vertices) {
+ if (vertex) {
+ if (qh GOODvertex > 0 && qh_pointid(vertex->point)+1 != qh GOODvertex)
+ continue;
+ totcount += qh_eachvoronoi(fp, printvridge, vertex, !qh_ALL, innerouter, inorder);
+ }
+ }
+ return totcount;
+} /* printvdiagram2 */
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="printvertex">-</a>
+
+ qh_printvertex( fp, vertex )
+ prints the information in a vertex
+ Duplicated as operator<< [QhullVertex.cpp]
+*/
+void qh_printvertex(FILE *fp, vertexT *vertex) {
+ pointT *point;
+ int k, count= 0;
+ facetT *neighbor, **neighborp;
+ realT r; /*bug fix*/
+
+ if (!vertex) {
+ qh_fprintf(fp, 9234, " NULLvertex\n");
+ return;
+ }
+ qh_fprintf(fp, 9235, "- p%d(v%d):", qh_pointid(vertex->point), vertex->id);
+ point= vertex->point;
+ if (point) {
+ for (k=qh hull_dim; k--; ) {
+ r= *point++;
+ qh_fprintf(fp, 9236, " %5.2g", r);
+ }
+ }
+ if (vertex->deleted)
+ qh_fprintf(fp, 9237, " deleted");
+ if (vertex->delridge)
+ qh_fprintf(fp, 9238, " ridgedeleted");
+ qh_fprintf(fp, 9239, "\n");
+ if (vertex->neighbors) {
+ qh_fprintf(fp, 9240, " neighbors:");
+ FOREACHneighbor_(vertex) {
+ if (++count % 100 == 0)
+ qh_fprintf(fp, 9241, "\n ");
+ qh_fprintf(fp, 9242, " f%d", neighbor->id);
+ }
+ qh_fprintf(fp, 9243, "\n");
+ }
+} /* printvertex */
+
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="printvertexlist">-</a>
+
+ qh_printvertexlist( fp, string, facetlist, facets, printall )
+ prints vertices used by a facetlist or facet set
+ tests qh_skipfacet() if !printall
+*/
+void qh_printvertexlist(FILE *fp, const char* string, facetT *facetlist,
+ setT *facets, boolT printall) {
+ vertexT *vertex, **vertexp;
+ setT *vertices;
+
+ vertices= qh_facetvertices(facetlist, facets, printall);
+ qh_fprintf(fp, 9244, "%s", string);
+ FOREACHvertex_(vertices)
+ qh_printvertex(fp, vertex);
+ qh_settempfree(&vertices);
+} /* printvertexlist */
+
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="printvertices">-</a>
+
+ qh_printvertices( fp, string, vertices )
+ prints vertices in a set
+ duplicated as printVertexSet [QhullVertex.cpp]
+*/
+void qh_printvertices(FILE *fp, const char* string, setT *vertices) {
+ vertexT *vertex, **vertexp;
+
+ qh_fprintf(fp, 9245, "%s", string);
+ FOREACHvertex_(vertices)
+ qh_fprintf(fp, 9246, " p%d(v%d)", qh_pointid(vertex->point), vertex->id);
+ qh_fprintf(fp, 9247, "\n");
+} /* printvertices */
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="printvneighbors">-</a>
+
+ qh_printvneighbors( fp, facetlist, facets, printall )
+ print vertex neighbors of vertices in facetlist and facets ('FN')
+
+ notes:
+ qh_countfacets clears facet->visitid for non-printed facets
+
+ design:
+ collect facet count and related statistics
+ if necessary, build neighbor sets for each vertex
+ collect vertices in facetlist and facets
+ build a point array for point->vertex and point->coplanar facet
+ for each point
+ list vertex neighbors or coplanar facet
+*/
+void qh_printvneighbors(FILE *fp, facetT* facetlist, setT *facets, boolT printall) {
+ int numfacets, numsimplicial, numridges, totneighbors, numneighbors, numcoplanars, numtricoplanars;
+ setT *vertices, *vertex_points, *coplanar_points;
+ int numpoints= qh num_points + qh_setsize(qh other_points);
+ vertexT *vertex, **vertexp;
+ int vertex_i, vertex_n;
+ facetT *facet, **facetp, *neighbor, **neighborp;
+ pointT *point, **pointp;
+
+ qh_countfacets(facetlist, facets, printall, &numfacets, &numsimplicial,
+ &totneighbors, &numridges, &numcoplanars, &numtricoplanars); /* sets facet->visitid */
+ qh_fprintf(fp, 9248, "%d\n", numpoints);
+ qh_vertexneighbors();
+ vertices= qh_facetvertices(facetlist, facets, printall);
+ vertex_points= qh_settemp(numpoints);
+ coplanar_points= qh_settemp(numpoints);
+ qh_setzero(vertex_points, 0, numpoints);
+ qh_setzero(coplanar_points, 0, numpoints);
+ FOREACHvertex_(vertices)
+ qh_point_add(vertex_points, vertex->point, vertex);
+ FORALLfacet_(facetlist) {
+ FOREACHpoint_(facet->coplanarset)
+ qh_point_add(coplanar_points, point, facet);
+ }
+ FOREACHfacet_(facets) {
+ FOREACHpoint_(facet->coplanarset)
+ qh_point_add(coplanar_points, point, facet);
+ }
+ FOREACHvertex_i_(vertex_points) {
+ if (vertex) {
+ numneighbors= qh_setsize(vertex->neighbors);
+ qh_fprintf(fp, 9249, "%d", numneighbors);
+ if (qh hull_dim == 3)
+ qh_order_vertexneighbors(vertex);
+ else if (qh hull_dim >= 4)
+ qsort(SETaddr_(vertex->neighbors, facetT), (size_t)numneighbors,
+ sizeof(facetT *), qh_compare_facetvisit);
+ FOREACHneighbor_(vertex)
+ qh_fprintf(fp, 9250, " %d",
+ neighbor->visitid ? neighbor->visitid - 1 : 0 - neighbor->id);
+ qh_fprintf(fp, 9251, "\n");
+ }else if ((facet= SETelemt_(coplanar_points, vertex_i, facetT)))
+ qh_fprintf(fp, 9252, "1 %d\n",
+ facet->visitid ? facet->visitid - 1 : 0 - facet->id);
+ else
+ qh_fprintf(fp, 9253, "0\n");
+ }
+ qh_settempfree(&coplanar_points);
+ qh_settempfree(&vertex_points);
+ qh_settempfree(&vertices);
+} /* printvneighbors */
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="printvoronoi">-</a>
+
+ qh_printvoronoi( fp, format, facetlist, facets, printall )
+ print voronoi diagram in 'o' or 'G' format
+ for 'o' format
+ prints voronoi centers for each facet and for infinity
+ for each vertex, lists ids of printed facets or infinity
+ assumes facetlist and facets are disjoint
+ for 'G' format
+ prints an OFF object
+ adds a 0 coordinate to center
+ prints infinity but does not list in vertices
+
+ see:
+ qh_printvdiagram()
+
+ notes:
+ if 'o',
+ prints a line for each point except "at-infinity"
+ if all facets are upperdelaunay,
+ reverses lower and upper hull
+*/
+void qh_printvoronoi(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) {
+ int k, numcenters, numvertices= 0, numneighbors, numinf, vid=1, vertex_i, vertex_n;
+ facetT *facet, **facetp, *neighbor, **neighborp;
+ setT *vertices;
+ vertexT *vertex;
+ boolT isLower;
+ unsigned int numfacets= (unsigned int) qh num_facets;
+
+ vertices= qh_markvoronoi(facetlist, facets, printall, &isLower, &numcenters);
+ FOREACHvertex_i_(vertices) {
+ if (vertex) {
+ numvertices++;
+ numneighbors = numinf = 0;
+ FOREACHneighbor_(vertex) {
+ if (neighbor->visitid == 0)
+ numinf= 1;
+ else if (neighbor->visitid < numfacets)
+ numneighbors++;
+ }
+ if (numinf && !numneighbors) {
+ SETelem_(vertices, vertex_i)= NULL;
+ numvertices--;
+ }
+ }
+ }
+ if (format == qh_PRINTgeom)
+ qh_fprintf(fp, 9254, "{appearance {+edge -face} OFF %d %d 1 # Voronoi centers and cells\n",
+ numcenters, numvertices);
+ else
+ qh_fprintf(fp, 9255, "%d\n%d %d 1\n", qh hull_dim-1, numcenters, qh_setsize(vertices));
+ if (format == qh_PRINTgeom) {
+ for (k=qh hull_dim-1; k--; )
+ qh_fprintf(fp, 9256, qh_REAL_1, 0.0);
+ qh_fprintf(fp, 9257, " 0 # infinity not used\n");
+ }else {
+ for (k=qh hull_dim-1; k--; )
+ qh_fprintf(fp, 9258, qh_REAL_1, qh_INFINITE);
+ qh_fprintf(fp, 9259, "\n");
+ }
+ FORALLfacet_(facetlist) {
+ if (facet->visitid && facet->visitid < numfacets) {
+ if (format == qh_PRINTgeom)
+ qh_fprintf(fp, 9260, "# %d f%d\n", vid++, facet->id);
+ qh_printcenter(fp, format, NULL, facet);
+ }
+ }
+ FOREACHfacet_(facets) {
+ if (facet->visitid && facet->visitid < numfacets) {
+ if (format == qh_PRINTgeom)
+ qh_fprintf(fp, 9261, "# %d f%d\n", vid++, facet->id);
+ qh_printcenter(fp, format, NULL, facet);
+ }
+ }
+ FOREACHvertex_i_(vertices) {
+ numneighbors= 0;
+ numinf=0;
+ if (vertex) {
+ if (qh hull_dim == 3)
+ qh_order_vertexneighbors(vertex);
+ else if (qh hull_dim >= 4)
+ qsort(SETaddr_(vertex->neighbors, facetT),
+ (size_t)qh_setsize(vertex->neighbors),
+ sizeof(facetT *), qh_compare_facetvisit);
+ FOREACHneighbor_(vertex) {
+ if (neighbor->visitid == 0)
+ numinf= 1;
+ else if (neighbor->visitid < numfacets)
+ numneighbors++;
+ }
+ }
+ if (format == qh_PRINTgeom) {
+ if (vertex) {
+ qh_fprintf(fp, 9262, "%d", numneighbors);
+ FOREACHneighbor_(vertex) {
+ if (neighbor->visitid && neighbor->visitid < numfacets)
+ qh_fprintf(fp, 9263, " %d", neighbor->visitid);
+ }
+ qh_fprintf(fp, 9264, " # p%d(v%d)\n", vertex_i, vertex->id);
+ }else
+ qh_fprintf(fp, 9265, " # p%d is coplanar or isolated\n", vertex_i);
+ }else {
+ if (numinf)
+ numneighbors++;
+ qh_fprintf(fp, 9266, "%d", numneighbors);
+ if (vertex) {
+ FOREACHneighbor_(vertex) {
+ if (neighbor->visitid == 0) {
+ if (numinf) {
+ numinf= 0;
+ qh_fprintf(fp, 9267, " %d", neighbor->visitid);
+ }
+ }else if (neighbor->visitid < numfacets)
+ qh_fprintf(fp, 9268, " %d", neighbor->visitid);
+ }
+ }
+ qh_fprintf(fp, 9269, "\n");
+ }
+ }
+ if (format == qh_PRINTgeom)
+ qh_fprintf(fp, 9270, "}\n");
+ qh_settempfree(&vertices);
+} /* printvoronoi */
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="printvnorm">-</a>
+
+ qh_printvnorm( fp, vertex, vertexA, centers, unbounded )
+ print one separating plane of the Voronoi diagram for a pair of input sites
+ unbounded==True if centers includes vertex-at-infinity
+
+ assumes:
+ qh_ASvoronoi and qh_vertexneighbors() already set
+
+ note:
+ parameter unbounded is UNUSED by this callback
+
+ see:
+ qh_printvdiagram()
+ qh_eachvoronoi()
+*/
+void qh_printvnorm(FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded) {
+ pointT *normal;
+ realT offset;
+ int k;
+ QHULL_UNUSED(unbounded);
+
+ normal= qh_detvnorm(vertex, vertexA, centers, &offset);
+ qh_fprintf(fp, 9271, "%d %d %d ",
+ 2+qh hull_dim, qh_pointid(vertex->point), qh_pointid(vertexA->point));
+ for (k=0; k< qh hull_dim-1; k++)
+ qh_fprintf(fp, 9272, qh_REAL_1, normal[k]);
+ qh_fprintf(fp, 9273, qh_REAL_1, offset);
+ qh_fprintf(fp, 9274, "\n");
+} /* printvnorm */
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="printvridge">-</a>
+
+ qh_printvridge( fp, vertex, vertexA, centers, unbounded )
+ print one ridge of the Voronoi diagram for a pair of input sites
+ unbounded==True if centers includes vertex-at-infinity
+
+ see:
+ qh_printvdiagram()
+
+ notes:
+ the user may use a different function
+ parameter unbounded is UNUSED
+*/
+void qh_printvridge(FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded) {
+ facetT *facet, **facetp;
+ QHULL_UNUSED(unbounded);
+
+ qh_fprintf(fp, 9275, "%d %d %d", qh_setsize(centers)+2,
+ qh_pointid(vertex->point), qh_pointid(vertexA->point));
+ FOREACHfacet_(centers)
+ qh_fprintf(fp, 9276, " %d", facet->visitid);
+ qh_fprintf(fp, 9277, "\n");
+} /* printvridge */
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="projectdim3">-</a>
+
+ qh_projectdim3( source, destination )
+ project 2-d 3-d or 4-d point to a 3-d point
+ uses qh.DROPdim and qh.hull_dim
+ source and destination may be the same
+
+ notes:
+ allocate 4 elements to destination just in case
+*/
+void qh_projectdim3(pointT *source, pointT *destination) {
+ int i,k;
+
+ for (k=0, i=0; k < qh hull_dim; k++) {
+ if (qh hull_dim == 4) {
+ if (k != qh DROPdim)
+ destination[i++]= source[k];
+ }else if (k == qh DROPdim)
+ destination[i++]= 0;
+ else
+ destination[i++]= source[k];
+ }
+ while (i < 3)
+ destination[i++]= 0.0;
+} /* projectdim3 */
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="readfeasible">-</a>
+
+ qh_readfeasible( dim, curline )
+ read feasible point from current line and qh.fin
+
+ returns:
+ number of lines read from qh.fin
+ sets qh.feasible_point with malloc'd coordinates
+
+ notes:
+ checks for qh.HALFspace
+ assumes dim > 1
+
+ see:
+ qh_setfeasible
+*/
+int qh_readfeasible(int dim, const char *curline) {
+ boolT isfirst= True;
+ int linecount= 0, tokcount= 0;
+ const char *s;
+ char *t, firstline[qh_MAXfirst+1];
+ coordT *coords, value;
+
+ if (!qh HALFspace) {
+ qh_fprintf(qh ferr, 6070, "qhull input error: feasible point(dim 1 coords) is only valid for halfspace intersection\n");
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ if (qh feasible_string)
+ qh_fprintf(qh ferr, 7057, "qhull input warning: feasible point(dim 1 coords) overrides 'Hn,n,n' feasible point for halfspace intersection\n");
+ if (!(qh feasible_point= (coordT*)qh_malloc(dim* sizeof(coordT)))) {
+ qh_fprintf(qh ferr, 6071, "qhull error: insufficient memory for feasible point\n");
+ qh_errexit(qh_ERRmem, NULL, NULL);
+ }
+ coords= qh feasible_point;
+ while ((s= (isfirst ? curline : fgets(firstline, qh_MAXfirst, qh fin)))) {
+ if (isfirst)
+ isfirst= False;
+ else
+ linecount++;
+ while (*s) {
+ while (isspace(*s))
+ s++;
+ value= qh_strtod(s, &t);
+ if (s == t)
+ break;
+ s= t;
+ *(coords++)= value;
+ if (++tokcount == dim) {
+ while (isspace(*s))
+ s++;
+ qh_strtod(s, &t);
+ if (s != t) {
+ qh_fprintf(qh ferr, 6072, "qhull input error: coordinates for feasible point do not finish out the line: %s\n",
+ s);
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ return linecount;
+ }
+ }
+ }
+ qh_fprintf(qh ferr, 6073, "qhull input error: only %d coordinates. Could not read %d-d feasible point.\n",
+ tokcount, dim);
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ return 0;
+} /* readfeasible */
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="readpoints">-</a>
+
+ qh_readpoints( numpoints, dimension, ismalloc )
+ read points from qh.fin into qh.first_point, qh.num_points
+ qh.fin is lines of coordinates, one per vertex, first line number of points
+ if 'rbox D4',
+ gives message
+ if qh.ATinfinity,
+ adds point-at-infinity for Delaunay triangulations
+
+ returns:
+ number of points, array of point coordinates, dimension, ismalloc True
+ if qh.DELAUNAY & !qh.PROJECTinput, projects points to paraboloid
+ and clears qh.PROJECTdelaunay
+ if qh.HALFspace, reads optional feasible point, reads halfspaces,
+ converts to dual.
+
+ for feasible point in "cdd format" in 3-d:
+ 3 1
+ coordinates
+ comments
+ begin
+ n 4 real/integer
+ ...
+ end
+
+ notes:
+ dimension will change in qh_initqhull_globals if qh.PROJECTinput
+ uses malloc() since qh_mem not initialized
+ FIXUP QH11012: qh_readpoints needs rewriting, too long
+*/
+coordT *qh_readpoints(int *numpoints, int *dimension, boolT *ismalloc) {
+ coordT *points, *coords, *infinity= NULL;
+ realT paraboloid, maxboloid= -REALmax, value;
+ realT *coordp= NULL, *offsetp= NULL, *normalp= NULL;
+ char *s= 0, *t, firstline[qh_MAXfirst+1];
+ int diminput=0, numinput=0, dimfeasible= 0, newnum, k, tempi;
+ int firsttext=0, firstshort=0, firstlong=0, firstpoint=0;
+ int tokcount= 0, linecount=0, maxcount, coordcount=0;
+ boolT islong, isfirst= True, wasbegin= False;
+ boolT isdelaunay= qh DELAUNAY && !qh PROJECTinput;
+
+ if (qh CDDinput) {
+ while ((s= fgets(firstline, qh_MAXfirst, qh fin))) {
+ linecount++;
+ if (qh HALFspace && linecount == 1 && isdigit(*s)) {
+ dimfeasible= qh_strtol(s, &s);
+ while (isspace(*s))
+ s++;
+ if (qh_strtol(s, &s) == 1)
+ linecount += qh_readfeasible(dimfeasible, s);
+ else
+ dimfeasible= 0;
+ }else if (!memcmp(firstline, "begin", (size_t)5) || !memcmp(firstline, "BEGIN", (size_t)5))
+ break;
+ else if (!*qh rbox_command)
+ strncat(qh rbox_command, s, sizeof(qh rbox_command)-1);
+ }
+ if (!s) {
+ qh_fprintf(qh ferr, 6074, "qhull input error: missing \"begin\" for cdd-formated input\n");
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ }
+ while (!numinput && (s= fgets(firstline, qh_MAXfirst, qh fin))) {
+ linecount++;
+ if (!memcmp(s, "begin", (size_t)5) || !memcmp(s, "BEGIN", (size_t)5))
+ wasbegin= True;
+ while (*s) {
+ while (isspace(*s))
+ s++;
+ if (!*s)
+ break;
+ if (!isdigit(*s)) {
+ if (!*qh rbox_command) {
+ strncat(qh rbox_command, s, sizeof(qh rbox_command)-1);
+ firsttext= linecount;
+ }
+ break;
+ }
+ if (!diminput)
+ diminput= qh_strtol(s, &s);
+ else {
+ numinput= qh_strtol(s, &s);
+ if (numinput == 1 && diminput >= 2 && qh HALFspace && !qh CDDinput) {
+ linecount += qh_readfeasible(diminput, s); /* checks if ok */
+ dimfeasible= diminput;
+ diminput= numinput= 0;
+ }else
+ break;
+ }
+ }
+ }
+ if (!s) {
+ qh_fprintf(qh ferr, 6075, "qhull input error: short input file. Did not find dimension and number of points\n");
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ if (diminput > numinput) {
+ tempi= diminput; /* exchange dim and n, e.g., for cdd input format */
+ diminput= numinput;
+ numinput= tempi;
+ }
+ if (diminput < 2) {
+ qh_fprintf(qh ferr, 6220,"qhull input error: dimension %d(first number) should be at least 2\n",
+ diminput);
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ if (isdelaunay) {
+ qh PROJECTdelaunay= False;
+ if (qh CDDinput)
+ *dimension= diminput;
+ else
+ *dimension= diminput+1;
+ *numpoints= numinput;
+ if (qh ATinfinity)
+ (*numpoints)++;
+ }else if (qh HALFspace) {
+ *dimension= diminput - 1;
+ *numpoints= numinput;
+ if (diminput < 3) {
+ qh_fprintf(qh ferr, 6221,"qhull input error: dimension %d(first number, includes offset) should be at least 3 for halfspaces\n",
+ diminput);
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ if (dimfeasible) {
+ if (dimfeasible != *dimension) {
+ qh_fprintf(qh ferr, 6222,"qhull input error: dimension %d of feasible point is not one less than dimension %d for halfspaces\n",
+ dimfeasible, diminput);
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ }else
+ qh_setfeasible(*dimension);
+ }else {
+ if (qh CDDinput)
+ *dimension= diminput-1;
+ else
+ *dimension= diminput;
+ *numpoints= numinput;
+ }
+ qh normal_size= *dimension * sizeof(coordT); /* for tracing with qh_printpoint */
+ if (qh HALFspace) {
+ qh half_space= coordp= (coordT*)qh_malloc(qh normal_size + sizeof(coordT));
+ if (qh CDDinput) {
+ offsetp= qh half_space;
+ normalp= offsetp + 1;
+ }else {
+ normalp= qh half_space;
+ offsetp= normalp + *dimension;
+ }
+ }
+ qh maxline= diminput * (qh_REALdigits + 5);
+ maximize_(qh maxline, 500);
+ qh line= (char*)qh_malloc((qh maxline+1) * sizeof(char));
+ *ismalloc= True; /* use malloc since memory not setup */
+ coords= points= qh temp_malloc= /* numinput and diminput >=2 by QH6220 */
+ (coordT*)qh_malloc((*numpoints)*(*dimension)*sizeof(coordT));
+ if (!coords || !qh line || (qh HALFspace && !qh half_space)) {
+ qh_fprintf(qh ferr, 6076, "qhull error: insufficient memory to read %d points\n",
+ numinput);
+ qh_errexit(qh_ERRmem, NULL, NULL);
+ }
+ if (isdelaunay && qh ATinfinity) {
+ infinity= points + numinput * (*dimension);
+ for (k= (*dimension) - 1; k--; )
+ infinity[k]= 0.0;
+ }
+ maxcount= numinput * diminput;
+ paraboloid= 0.0;
+ while ((s= (isfirst ? s : fgets(qh line, qh maxline, qh fin)))) {
+ if (!isfirst) {
+ linecount++;
+ if (*s == 'e' || *s == 'E') {
+ if (!memcmp(s, "end", (size_t)3) || !memcmp(s, "END", (size_t)3)) {
+ if (qh CDDinput )
+ break;
+ else if (wasbegin)
+ qh_fprintf(qh ferr, 7058, "qhull input warning: the input appears to be in cdd format. If so, use 'Fd'\n");
+ }
+ }
+ }
+ islong= False;
+ while (*s) {
+ while (isspace(*s))
+ s++;
+ value= qh_strtod(s, &t);
+ if (s == t) {
+ if (!*qh rbox_command)
+ strncat(qh rbox_command, s, sizeof(qh rbox_command)-1);
+ if (*s && !firsttext)
+ firsttext= linecount;
+ if (!islong && !firstshort && coordcount)
+ firstshort= linecount;
+ break;
+ }
+ if (!firstpoint)
+ firstpoint= linecount;
+ s= t;
+ if (++tokcount > maxcount)
+ continue;
+ if (qh HALFspace) {
+ if (qh CDDinput)
+ *(coordp++)= -value; /* both coefficients and offset */
+ else
+ *(coordp++)= value;
+ }else {
+ *(coords++)= value;
+ if (qh CDDinput && !coordcount) {
+ if (value != 1.0) {
+ qh_fprintf(qh ferr, 6077, "qhull input error: for cdd format, point at line %d does not start with '1'\n",
+ linecount);
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ coords--;
+ }else if (isdelaunay) {
+ paraboloid += value * value;
+ if (qh ATinfinity) {
+ if (qh CDDinput)
+ infinity[coordcount-1] += value;
+ else
+ infinity[coordcount] += value;
+ }
+ }
+ }
+ if (++coordcount == diminput) {
+ coordcount= 0;
+ if (isdelaunay) {
+ *(coords++)= paraboloid;
+ maximize_(maxboloid, paraboloid);
+ paraboloid= 0.0;
+ }else if (qh HALFspace) {
+ if (!qh_sethalfspace(*dimension, coords, &coords, normalp, offsetp, qh feasible_point)) {
+ qh_fprintf(qh ferr, 8048, "The halfspace was on line %d\n", linecount);
+ if (wasbegin)
+ qh_fprintf(qh ferr, 8049, "The input appears to be in cdd format. If so, you should use option 'Fd'\n");
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ coordp= qh half_space;
+ }
+ while (isspace(*s))
+ s++;
+ if (*s) {
+ islong= True;
+ if (!firstlong)
+ firstlong= linecount;
+ }
+ }
+ }
+ if (!islong && !firstshort && coordcount)
+ firstshort= linecount;
+ if (!isfirst && s - qh line >= qh maxline) {
+ qh_fprintf(qh ferr, 6078, "qhull input error: line %d contained more than %d characters\n",
+ linecount, (int) (s - qh line)); /* WARN64 */
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ isfirst= False;
+ }
+ if (tokcount != maxcount) {
+ newnum= fmin_(numinput, tokcount/diminput);
+ qh_fprintf(qh ferr, 7073,"\
+qhull warning: instead of %d %d-dimensional points, input contains\n\
+%d points and %d extra coordinates. Line %d is the first\npoint",
+ numinput, diminput, tokcount/diminput, tokcount % diminput, firstpoint);
+ if (firsttext)
+ qh_fprintf(qh ferr, 8051, ", line %d is the first comment", firsttext);
+ if (firstshort)
+ qh_fprintf(qh ferr, 8052, ", line %d is the first short\nline", firstshort);
+ if (firstlong)
+ qh_fprintf(qh ferr, 8053, ", line %d is the first long line", firstlong);
+ qh_fprintf(qh ferr, 8054, ". Continue with %d points.\n", newnum);
+ numinput= newnum;
+ if (isdelaunay && qh ATinfinity) {
+ for (k= tokcount % diminput; k--; )
+ infinity[k] -= *(--coords);
+ *numpoints= newnum+1;
+ }else {
+ coords -= tokcount % diminput;
+ *numpoints= newnum;
+ }
+ }
+ if (isdelaunay && qh ATinfinity) {
+ for (k= (*dimension) -1; k--; )
+ infinity[k] /= numinput;
+ if (coords == infinity)
+ coords += (*dimension) -1;
+ else {
+ for (k=0; k < (*dimension) -1; k++)
+ *(coords++)= infinity[k];
+ }
+ *(coords++)= maxboloid * 1.1;
+ }
+ if (qh rbox_command[0]) {
+ qh rbox_command[strlen(qh rbox_command)-1]= '\0';
+ if (!strcmp(qh rbox_command, "./rbox D4"))
+ qh_fprintf(qh ferr, 8055, "\n\
+This is the qhull test case. If any errors or core dumps occur,\n\
+recompile qhull with 'make new'. If errors still occur, there is\n\
+an incompatibility. You should try a different compiler. You can also\n\
+change the choices in user.h. If you discover the source of the problem,\n\
+please send mail to qhull_bug@qhull.org.\n\
+\n\
+Type 'qhull' for a short list of options.\n");
+ }
+ qh_free(qh line);
+ qh line= NULL;
+ if (qh half_space) {
+ qh_free(qh half_space);
+ qh half_space= NULL;
+ }
+ qh temp_malloc= NULL;
+ trace1((qh ferr, 1008,"qh_readpoints: read in %d %d-dimensional points\n",
+ numinput, diminput));
+ return(points);
+} /* readpoints */
+
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="setfeasible">-</a>
+
+ qh_setfeasible( dim )
+ set qh.feasible_point from qh.feasible_string in "n,n,n" or "n n n" format
+
+ notes:
+ "n,n,n" already checked by qh_initflags()
+ see qh_readfeasible()
+ called only once from qh_new_qhull, otherwise leaks memory
+*/
+void qh_setfeasible(int dim) {
+ int tokcount= 0;
+ char *s;
+ coordT *coords, value;
+
+ if (!(s= qh feasible_string)) {
+ qh_fprintf(qh ferr, 6223, "\
+qhull input error: halfspace intersection needs a feasible point.\n\
+Either prepend the input with 1 point or use 'Hn,n,n'. See manual.\n");
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ if (!(qh feasible_point= (pointT*)qh_malloc(dim * sizeof(coordT)))) {
+ qh_fprintf(qh ferr, 6079, "qhull error: insufficient memory for 'Hn,n,n'\n");
+ qh_errexit(qh_ERRmem, NULL, NULL);
+ }
+ coords= qh feasible_point;
+ while (*s) {
+ value= qh_strtod(s, &s);
+ if (++tokcount > dim) {
+ qh_fprintf(qh ferr, 7059, "qhull input warning: more coordinates for 'H%s' than dimension %d\n",
+ qh feasible_string, dim);
+ break;
+ }
+ *(coords++)= value;
+ if (*s)
+ s++;
+ }
+ while (++tokcount <= dim)
+ *(coords++)= 0.0;
+} /* setfeasible */
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="skipfacet">-</a>
+
+ qh_skipfacet( facet )
+ returns 'True' if this facet is not to be printed
+
+ notes:
+ based on the user provided slice thresholds and 'good' specifications
+*/
+boolT qh_skipfacet(facetT *facet) {
+ facetT *neighbor, **neighborp;
+
+ if (qh PRINTneighbors) {
+ if (facet->good)
+ return !qh PRINTgood;
+ FOREACHneighbor_(facet) {
+ if (neighbor->good)
+ return False;
+ }
+ return True;
+ }else if (qh PRINTgood)
+ return !facet->good;
+ else if (!facet->normal)
+ return True;
+ return(!qh_inthresholds(facet->normal, NULL));
+} /* skipfacet */
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="skipfilename">-</a>
+
+ qh_skipfilename( string )
+ returns pointer to character after filename
+
+ notes:
+ skips leading spaces
+ ends with spacing or eol
+ if starts with ' or " ends with the same, skipping \' or \"
+ For qhull, qh_argv_to_command() only uses double quotes
+*/
+char *qh_skipfilename(char *filename) {
+ char *s= filename; /* non-const due to return */
+ char c;
+
+ while (*s && isspace(*s))
+ s++;
+ c= *s++;
+ if (c == '\0') {
+ qh_fprintf(qh ferr, 6204, "qhull input error: filename expected, none found.\n");
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ if (c == '\'' || c == '"') {
+ while (*s !=c || s[-1] == '\\') {
+ if (!*s) {
+ qh_fprintf(qh ferr, 6203, "qhull input error: missing quote after filename -- %s\n", filename);
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ s++;
+ }
+ s++;
+ }
+ else while (*s && !isspace(*s))
+ s++;
+ return s;
+} /* skipfilename */
+
diff --git a/xs/src/qhull/src/libqhull/io.h b/xs/src/qhull/src/libqhull/io.h
new file mode 100644
index 000000000..eca0369d3
--- /dev/null
+++ b/xs/src/qhull/src/libqhull/io.h
@@ -0,0 +1,159 @@
+/*<html><pre> -<a href="qh-io.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ io.h
+ declarations of Input/Output functions
+
+ see README, libqhull.h and io.c
+
+ Copyright (c) 1993-2015 The Geometry Center.
+ $Id: //main/2015/qhull/src/libqhull/io.h#1 $$Change: 1981 $
+ $DateTime: 2015/09/28 20:26:32 $$Author: bbarber $
+*/
+
+#ifndef qhDEFio
+#define qhDEFio 1
+
+#include "libqhull.h"
+
+/*============ constants and flags ==================*/
+
+/*-<a href="qh-io.htm#TOC"
+ >--------------------------------</a><a name="qh_MAXfirst">-</a>
+
+ qh_MAXfirst
+ maximum length of first two lines of stdin
+*/
+#define qh_MAXfirst 200
+
+/*-<a href="qh-io.htm#TOC"
+ >--------------------------------</a><a name="qh_MINradius">-</a>
+
+ qh_MINradius
+ min radius for Gp and Gv, fraction of maxcoord
+*/
+#define qh_MINradius 0.02
+
+/*-<a href="qh-io.htm#TOC"
+ >--------------------------------</a><a name="qh_GEOMepsilon">-</a>
+
+ qh_GEOMepsilon
+ adjust outer planes for 'lines closer' and geomview roundoff.
+ This prevents bleed through.
+*/
+#define qh_GEOMepsilon 2e-3
+
+/*-<a href="qh-io.htm#TOC"
+ >--------------------------------</a><a name="qh_WHITESPACE">-</a>
+
+ qh_WHITESPACE
+ possible values of white space
+*/
+#define qh_WHITESPACE " \n\t\v\r\f"
+
+
+/*-<a href="qh-io.htm#TOC"
+ >--------------------------------</a><a name="RIDGE">-</a>
+
+ qh_RIDGE
+ to select which ridges to print in qh_eachvoronoi
+*/
+typedef enum
+{
+ qh_RIDGEall = 0, qh_RIDGEinner, qh_RIDGEouter
+}
+qh_RIDGE;
+
+/*-<a href="qh-io.htm#TOC"
+ >--------------------------------</a><a name="printvridgeT">-</a>
+
+ printvridgeT
+ prints results of qh_printvdiagram
+
+ see:
+ <a href="io.c#printvridge">qh_printvridge</a> for an example
+*/
+typedef void (*printvridgeT)(FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded);
+
+/*============== -prototypes in alphabetical order =========*/
+
+void qh_dfacet(unsigned id);
+void qh_dvertex(unsigned id);
+int qh_compare_facetarea(const void *p1, const void *p2);
+int qh_compare_facetmerge(const void *p1, const void *p2);
+int qh_compare_facetvisit(const void *p1, const void *p2);
+int qh_compare_vertexpoint(const void *p1, const void *p2); /* not used, not in libqhull_r.h */
+void qh_copyfilename(char *filename, int size, const char* source, int length);
+void qh_countfacets(facetT *facetlist, setT *facets, boolT printall,
+ int *numfacetsp, int *numsimplicialp, int *totneighborsp,
+ int *numridgesp, int *numcoplanarsp, int *numnumtricoplanarsp);
+pointT *qh_detvnorm(vertexT *vertex, vertexT *vertexA, setT *centers, realT *offsetp);
+setT *qh_detvridge(vertexT *vertex);
+setT *qh_detvridge3(vertexT *atvertex, vertexT *vertex);
+int qh_eachvoronoi(FILE *fp, printvridgeT printvridge, vertexT *atvertex, boolT visitall, qh_RIDGE innerouter, boolT inorder);
+int qh_eachvoronoi_all(FILE *fp, printvridgeT printvridge, boolT isUpper, qh_RIDGE innerouter, boolT inorder);
+void qh_facet2point(facetT *facet, pointT **point0, pointT **point1, realT *mindist);
+setT *qh_facetvertices(facetT *facetlist, setT *facets, boolT allfacets);
+void qh_geomplanes(facetT *facet, realT *outerplane, realT *innerplane);
+void qh_markkeep(facetT *facetlist);
+setT *qh_markvoronoi(facetT *facetlist, setT *facets, boolT printall, boolT *isLowerp, int *numcentersp);
+void qh_order_vertexneighbors(vertexT *vertex);
+void qh_prepare_output(void);
+void qh_printafacet(FILE *fp, qh_PRINT format, facetT *facet, boolT printall);
+void qh_printbegin(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall);
+void qh_printcenter(FILE *fp, qh_PRINT format, const char *string, facetT *facet);
+void qh_printcentrum(FILE *fp, facetT *facet, realT radius);
+void qh_printend(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall);
+void qh_printend4geom(FILE *fp, facetT *facet, int *num, boolT printall);
+void qh_printextremes(FILE *fp, facetT *facetlist, setT *facets, boolT printall);
+void qh_printextremes_2d(FILE *fp, facetT *facetlist, setT *facets, boolT printall);
+void qh_printextremes_d(FILE *fp, facetT *facetlist, setT *facets, boolT printall);
+void qh_printfacet(FILE *fp, facetT *facet);
+void qh_printfacet2math(FILE *fp, facetT *facet, qh_PRINT format, int notfirst);
+void qh_printfacet2geom(FILE *fp, facetT *facet, realT color[3]);
+void qh_printfacet2geom_points(FILE *fp, pointT *point1, pointT *point2,
+ facetT *facet, realT offset, realT color[3]);
+void qh_printfacet3math(FILE *fp, facetT *facet, qh_PRINT format, int notfirst);
+void qh_printfacet3geom_nonsimplicial(FILE *fp, facetT *facet, realT color[3]);
+void qh_printfacet3geom_points(FILE *fp, setT *points, facetT *facet, realT offset, realT color[3]);
+void qh_printfacet3geom_simplicial(FILE *fp, facetT *facet, realT color[3]);
+void qh_printfacet3vertex(FILE *fp, facetT *facet, qh_PRINT format);
+void qh_printfacet4geom_nonsimplicial(FILE *fp, facetT *facet, realT color[3]);
+void qh_printfacet4geom_simplicial(FILE *fp, facetT *facet, realT color[3]);
+void qh_printfacetNvertex_nonsimplicial(FILE *fp, facetT *facet, int id, qh_PRINT format);
+void qh_printfacetNvertex_simplicial(FILE *fp, facetT *facet, qh_PRINT format);
+void qh_printfacetheader(FILE *fp, facetT *facet);
+void qh_printfacetridges(FILE *fp, facetT *facet);
+void qh_printfacets(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall);
+void qh_printhyperplaneintersection(FILE *fp, facetT *facet1, facetT *facet2,
+ setT *vertices, realT color[3]);
+void qh_printneighborhood(FILE *fp, qh_PRINT format, facetT *facetA, facetT *facetB, boolT printall);
+void qh_printline3geom(FILE *fp, pointT *pointA, pointT *pointB, realT color[3]);
+void qh_printpoint(FILE *fp, const char *string, pointT *point);
+void qh_printpointid(FILE *fp, const char *string, int dim, pointT *point, int id);
+void qh_printpoint3(FILE *fp, pointT *point);
+void qh_printpoints_out(FILE *fp, facetT *facetlist, setT *facets, boolT printall);
+void qh_printpointvect(FILE *fp, pointT *point, coordT *normal, pointT *center, realT radius, realT color[3]);
+void qh_printpointvect2(FILE *fp, pointT *point, coordT *normal, pointT *center, realT radius);
+void qh_printridge(FILE *fp, ridgeT *ridge);
+void qh_printspheres(FILE *fp, setT *vertices, realT radius);
+void qh_printvdiagram(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall);
+int qh_printvdiagram2(FILE *fp, printvridgeT printvridge, setT *vertices, qh_RIDGE innerouter, boolT inorder);
+void qh_printvertex(FILE *fp, vertexT *vertex);
+void qh_printvertexlist(FILE *fp, const char* string, facetT *facetlist,
+ setT *facets, boolT printall);
+void qh_printvertices(FILE *fp, const char* string, setT *vertices);
+void qh_printvneighbors(FILE *fp, facetT* facetlist, setT *facets, boolT printall);
+void qh_printvoronoi(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall);
+void qh_printvnorm(FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded);
+void qh_printvridge(FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded);
+void qh_produce_output(void);
+void qh_produce_output2(void);
+void qh_projectdim3(pointT *source, pointT *destination);
+int qh_readfeasible(int dim, const char *curline);
+coordT *qh_readpoints(int *numpoints, int *dimension, boolT *ismalloc);
+void qh_setfeasible(int dim);
+boolT qh_skipfacet(facetT *facet);
+char *qh_skipfilename(char *filename);
+
+#endif /* qhDEFio */
diff --git a/xs/src/qhull/src/libqhull/libqhull.c b/xs/src/qhull/src/libqhull/libqhull.c
new file mode 100644
index 000000000..7696a8a9f
--- /dev/null
+++ b/xs/src/qhull/src/libqhull/libqhull.c
@@ -0,0 +1,1403 @@
+/*<html><pre> -<a href="qh-qhull.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ libqhull.c
+ Quickhull algorithm for convex hulls
+
+ qhull() and top-level routines
+
+ see qh-qhull.htm, libqhull.h, unix.c
+
+ see qhull_a.h for internal functions
+
+ Copyright (c) 1993-2015 The Geometry Center.
+ $Id: //main/2015/qhull/src/libqhull/libqhull.c#3 $$Change: 2047 $
+ $DateTime: 2016/01/04 22:03:18 $$Author: bbarber $
+*/
+
+#include "qhull_a.h"
+
+/*============= functions in alphabetic order after qhull() =======*/
+
+/*-<a href="qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="qhull">-</a>
+
+ qh_qhull()
+ compute DIM3 convex hull of qh.num_points starting at qh.first_point
+ qh contains all global options and variables
+
+ returns:
+ returns polyhedron
+ qh.facet_list, qh.num_facets, qh.vertex_list, qh.num_vertices,
+
+ returns global variables
+ qh.hulltime, qh.max_outside, qh.interior_point, qh.max_vertex, qh.min_vertex
+
+ returns precision constants
+ qh.ANGLEround, centrum_radius, cos_max, DISTround, MAXabs_coord, ONEmerge
+
+ notes:
+ unless needed for output
+ qh.max_vertex and qh.min_vertex are max/min due to merges
+
+ see:
+ to add individual points to either qh.num_points
+ use qh_addpoint()
+
+ if qh.GETarea
+ qh_produceoutput() returns qh.totarea and qh.totvol via qh_getarea()
+
+ design:
+ record starting time
+ initialize hull and partition points
+ build convex hull
+ unless early termination
+ update facet->maxoutside for vertices, coplanar, and near-inside points
+ error if temporary sets exist
+ record end time
+*/
+
+void qh_qhull(void) {
+ int numoutside;
+
+ qh hulltime= qh_CPUclock;
+ if (qh RERUN || qh JOGGLEmax < REALmax/2)
+ qh_build_withrestart();
+ else {
+ qh_initbuild();
+ qh_buildhull();
+ }
+ if (!qh STOPpoint && !qh STOPcone) {
+ if (qh ZEROall_ok && !qh TESTvneighbors && qh MERGEexact)
+ qh_checkzero( qh_ALL);
+ if (qh ZEROall_ok && !qh TESTvneighbors && !qh WAScoplanar) {
+ trace2((qh ferr, 2055, "qh_qhull: all facets are clearly convex and no coplanar points. Post-merging and check of maxout not needed.\n"));
+ qh DOcheckmax= False;
+ }else {
+ if (qh MERGEexact || (qh hull_dim > qh_DIMreduceBuild && qh PREmerge))
+ qh_postmerge("First post-merge", qh premerge_centrum, qh premerge_cos,
+ (qh POSTmerge ? False : qh TESTvneighbors));
+ else if (!qh POSTmerge && qh TESTvneighbors)
+ qh_postmerge("For testing vertex neighbors", qh premerge_centrum,
+ qh premerge_cos, True);
+ if (qh POSTmerge)
+ qh_postmerge("For post-merging", qh postmerge_centrum,
+ qh postmerge_cos, qh TESTvneighbors);
+ if (qh visible_list == qh facet_list) { /* i.e., merging done */
+ qh findbestnew= True;
+ qh_partitionvisible(/*qh.visible_list*/ !qh_ALL, &numoutside);
+ qh findbestnew= False;
+ qh_deletevisible(/*qh.visible_list*/);
+ qh_resetlists(False, qh_RESETvisible /*qh.visible_list newvertex_list newfacet_list */);
+ }
+ }
+ if (qh DOcheckmax){
+ if (qh REPORTfreq) {
+ qh_buildtracing(NULL, NULL);
+ qh_fprintf(qh ferr, 8115, "\nTesting all coplanar points.\n");
+ }
+ qh_check_maxout();
+ }
+ if (qh KEEPnearinside && !qh maxoutdone)
+ qh_nearcoplanar();
+ }
+ if (qh_setsize(qhmem.tempstack) != 0) {
+ qh_fprintf(qh ferr, 6164, "qhull internal error (qh_qhull): temporary sets not empty(%d)\n",
+ qh_setsize(qhmem.tempstack));
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ qh hulltime= qh_CPUclock - qh hulltime;
+ qh QHULLfinished= True;
+ trace1((qh ferr, 1036, "Qhull: algorithm completed\n"));
+} /* qhull */
+
+/*-<a href="qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="addpoint">-</a>
+
+ qh_addpoint( furthest, facet, checkdist )
+ add point (usually furthest point) above facet to hull
+ if checkdist,
+ check that point is above facet.
+ if point is not outside of the hull, uses qh_partitioncoplanar()
+ assumes that facet is defined by qh_findbestfacet()
+ else if facet specified,
+ assumes that point is above facet (major damage if below)
+ for Delaunay triangulations,
+ Use qh_setdelaunay() to lift point to paraboloid and scale by 'Qbb' if needed
+ Do not use options 'Qbk', 'QBk', or 'QbB' since they scale the coordinates.
+
+ returns:
+ returns False if user requested an early termination
+ qh.visible_list, newfacet_list, delvertex_list, NEWfacets may be defined
+ updates qh.facet_list, qh.num_facets, qh.vertex_list, qh.num_vertices
+ clear qh.maxoutdone (will need to call qh_check_maxout() for facet->maxoutside)
+ if unknown point, adds a pointer to qh.other_points
+ do not deallocate the point's coordinates
+
+ notes:
+ assumes point is near its best facet and not at a local minimum of a lens
+ distributions. Use qh_findbestfacet to avoid this case.
+ uses qh.visible_list, qh.newfacet_list, qh.delvertex_list, qh.NEWfacets
+
+ see also:
+ qh_triangulate() -- triangulate non-simplicial facets
+
+ design:
+ add point to other_points if needed
+ if checkdist
+ if point not above facet
+ partition coplanar point
+ exit
+ exit if pre STOPpoint requested
+ find horizon and visible facets for point
+ make new facets for point to horizon
+ make hyperplanes for point
+ compute balance statistics
+ match neighboring new facets
+ update vertex neighbors and delete interior vertices
+ exit if STOPcone requested
+ merge non-convex new facets
+ if merge found, many merges, or 'Qf'
+ use qh_findbestnew() instead of qh_findbest()
+ partition outside points from visible facets
+ delete visible facets
+ check polyhedron if requested
+ exit if post STOPpoint requested
+ reset working lists of facets and vertices
+*/
+boolT qh_addpoint(pointT *furthest, facetT *facet, boolT checkdist) {
+ int goodvisible, goodhorizon;
+ vertexT *vertex;
+ facetT *newfacet;
+ realT dist, newbalance, pbalance;
+ boolT isoutside= False;
+ int numpart, numpoints, numnew, firstnew;
+
+ qh maxoutdone= False;
+ if (qh_pointid(furthest) == qh_IDunknown)
+ qh_setappend(&qh other_points, furthest);
+ if (!facet) {
+ qh_fprintf(qh ferr, 6213, "qhull internal error (qh_addpoint): NULL facet. Need to call qh_findbestfacet first\n");
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ if (checkdist) {
+ facet= qh_findbest(furthest, facet, !qh_ALL, !qh_ISnewfacets, !qh_NOupper,
+ &dist, &isoutside, &numpart);
+ zzadd_(Zpartition, numpart);
+ if (!isoutside) {
+ zinc_(Znotmax); /* last point of outsideset is no longer furthest. */
+ facet->notfurthest= True;
+ qh_partitioncoplanar(furthest, facet, &dist);
+ return True;
+ }
+ }
+ qh_buildtracing(furthest, facet);
+ if (qh STOPpoint < 0 && qh furthest_id == -qh STOPpoint-1) {
+ facet->notfurthest= True;
+ return False;
+ }
+ qh_findhorizon(furthest, facet, &goodvisible, &goodhorizon);
+ if (qh ONLYgood && !(goodvisible+goodhorizon) && !qh GOODclosest) {
+ zinc_(Znotgood);
+ facet->notfurthest= True;
+ /* last point of outsideset is no longer furthest. This is ok
+ since all points of the outside are likely to be bad */
+ qh_resetlists(False, qh_RESETvisible /*qh.visible_list newvertex_list newfacet_list */);
+ return True;
+ }
+ zzinc_(Zprocessed);
+ firstnew= qh facet_id;
+ vertex= qh_makenewfacets(furthest /*visible_list, attaches if !ONLYgood */);
+ qh_makenewplanes(/* newfacet_list */);
+ numnew= qh facet_id - firstnew;
+ newbalance= numnew - (realT) (qh num_facets-qh num_visible)
+ * qh hull_dim/qh num_vertices;
+ wadd_(Wnewbalance, newbalance);
+ wadd_(Wnewbalance2, newbalance * newbalance);
+ if (qh ONLYgood
+ && !qh_findgood(qh newfacet_list, goodhorizon) && !qh GOODclosest) {
+ FORALLnew_facets
+ qh_delfacet(newfacet);
+ qh_delvertex(vertex);
+ qh_resetlists(True, qh_RESETvisible /*qh.visible_list newvertex_list newfacet_list */);
+ zinc_(Znotgoodnew);
+ facet->notfurthest= True;
+ return True;
+ }
+ if (qh ONLYgood)
+ qh_attachnewfacets(/*visible_list*/);
+ qh_matchnewfacets();
+ qh_updatevertices();
+ if (qh STOPcone && qh furthest_id == qh STOPcone-1) {
+ facet->notfurthest= True;
+ return False; /* visible_list etc. still defined */
+ }
+ qh findbestnew= False;
+ if (qh PREmerge || qh MERGEexact) {
+ qh_premerge(vertex, qh premerge_centrum, qh premerge_cos);
+ if (qh_USEfindbestnew)
+ qh findbestnew= True;
+ else {
+ FORALLnew_facets {
+ if (!newfacet->simplicial) {
+ qh findbestnew= True; /* use qh_findbestnew instead of qh_findbest*/
+ break;
+ }
+ }
+ }
+ }else if (qh BESToutside)
+ qh findbestnew= True;
+ qh_partitionvisible(/*qh.visible_list*/ !qh_ALL, &numpoints);
+ qh findbestnew= False;
+ qh findbest_notsharp= False;
+ zinc_(Zpbalance);
+ pbalance= numpoints - (realT) qh hull_dim /* assumes all points extreme */
+ * (qh num_points - qh num_vertices)/qh num_vertices;
+ wadd_(Wpbalance, pbalance);
+ wadd_(Wpbalance2, pbalance * pbalance);
+ qh_deletevisible(/*qh.visible_list*/);
+ zmax_(Zmaxvertex, qh num_vertices);
+ qh NEWfacets= False;
+ if (qh IStracing >= 4) {
+ if (qh num_facets < 2000)
+ qh_printlists();
+ qh_printfacetlist(qh newfacet_list, NULL, True);
+ qh_checkpolygon(qh facet_list);
+ }else if (qh CHECKfrequently) {
+ if (qh num_facets < 50)
+ qh_checkpolygon(qh facet_list);
+ else
+ qh_checkpolygon(qh newfacet_list);
+ }
+ if (qh STOPpoint > 0 && qh furthest_id == qh STOPpoint-1)
+ return False;
+ qh_resetlists(True, qh_RESETvisible /*qh.visible_list newvertex_list newfacet_list */);
+ /* qh_triangulate(); to test qh.TRInormals */
+ trace2((qh ferr, 2056, "qh_addpoint: added p%d new facets %d new balance %2.2g point balance %2.2g\n",
+ qh_pointid(furthest), numnew, newbalance, pbalance));
+ return True;
+} /* addpoint */
+
+/*-<a href="qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="build_withrestart">-</a>
+
+ qh_build_withrestart()
+ allow restarts due to qh.JOGGLEmax while calling qh_buildhull()
+ qh_errexit always undoes qh_build_withrestart()
+ qh.FIRSTpoint/qh.NUMpoints is point array
+ it may be moved by qh_joggleinput()
+*/
+void qh_build_withrestart(void) {
+ int restart;
+
+ qh ALLOWrestart= True;
+ while (True) {
+ restart= setjmp(qh restartexit); /* simple statement for CRAY J916 */
+ if (restart) { /* only from qh_precision() */
+ zzinc_(Zretry);
+ wmax_(Wretrymax, qh JOGGLEmax);
+ /* QH7078 warns about using 'TCn' with 'QJn' */
+ qh STOPcone= qh_IDunknown; /* if break from joggle, prevents normal output */
+ }
+ if (!qh RERUN && qh JOGGLEmax < REALmax/2) {
+ if (qh build_cnt > qh_JOGGLEmaxretry) {
+ qh_fprintf(qh ferr, 6229, "qhull precision error: %d attempts to construct a convex hull\n\
+ with joggled input. Increase joggle above 'QJ%2.2g'\n\
+ or modify qh_JOGGLE... parameters in user.h\n",
+ qh build_cnt, qh JOGGLEmax);
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ if (qh build_cnt && !restart)
+ break;
+ }else if (qh build_cnt && qh build_cnt >= qh RERUN)
+ break;
+ qh STOPcone= 0;
+ qh_freebuild(True); /* first call is a nop */
+ qh build_cnt++;
+ if (!qh qhull_optionsiz)
+ qh qhull_optionsiz= (int)strlen(qh qhull_options); /* WARN64 */
+ else {
+ qh qhull_options [qh qhull_optionsiz]= '\0';
+ qh qhull_optionlen= qh_OPTIONline; /* starts a new line */
+ }
+ qh_option("_run", &qh build_cnt, NULL);
+ if (qh build_cnt == qh RERUN) {
+ qh IStracing= qh TRACElastrun; /* duplicated from qh_initqhull_globals */
+ if (qh TRACEpoint != qh_IDunknown || qh TRACEdist < REALmax/2 || qh TRACEmerge) {
+ qh TRACElevel= (qh IStracing? qh IStracing : 3);
+ qh IStracing= 0;
+ }
+ qhmem.IStracing= qh IStracing;
+ }
+ if (qh JOGGLEmax < REALmax/2)
+ qh_joggleinput();
+ qh_initbuild();
+ qh_buildhull();
+ if (qh JOGGLEmax < REALmax/2 && !qh MERGING)
+ qh_checkconvex(qh facet_list, qh_ALGORITHMfault);
+ }
+ qh ALLOWrestart= False;
+} /* qh_build_withrestart */
+
+/*-<a href="qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="buildhull">-</a>
+
+ qh_buildhull()
+ construct a convex hull by adding outside points one at a time
+
+ returns:
+
+ notes:
+ may be called multiple times
+ checks facet and vertex lists for incorrect flags
+ to recover from STOPcone, call qh_deletevisible and qh_resetlists
+
+ design:
+ check visible facet and newfacet flags
+ check newlist vertex flags and qh.STOPcone/STOPpoint
+ for each facet with a furthest outside point
+ add point to facet
+ exit if qh.STOPcone or qh.STOPpoint requested
+ if qh.NARROWhull for initial simplex
+ partition remaining outside points to coplanar sets
+*/
+void qh_buildhull(void) {
+ facetT *facet;
+ pointT *furthest;
+ vertexT *vertex;
+ int id;
+
+ trace1((qh ferr, 1037, "qh_buildhull: start build hull\n"));
+ FORALLfacets {
+ if (facet->visible || facet->newfacet) {
+ qh_fprintf(qh ferr, 6165, "qhull internal error (qh_buildhull): visible or new facet f%d in facet list\n",
+ facet->id);
+ qh_errexit(qh_ERRqhull, facet, NULL);
+ }
+ }
+ FORALLvertices {
+ if (vertex->newlist) {
+ qh_fprintf(qh ferr, 6166, "qhull internal error (qh_buildhull): new vertex f%d in vertex list\n",
+ vertex->id);
+ qh_errprint("ERRONEOUS", NULL, NULL, NULL, vertex);
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ id= qh_pointid(vertex->point);
+ if ((qh STOPpoint>0 && id == qh STOPpoint-1) ||
+ (qh STOPpoint<0 && id == -qh STOPpoint-1) ||
+ (qh STOPcone>0 && id == qh STOPcone-1)) {
+ trace1((qh ferr, 1038,"qh_buildhull: stop point or cone P%d in initial hull\n", id));
+ return;
+ }
+ }
+ qh facet_next= qh facet_list; /* advance facet when processed */
+ while ((furthest= qh_nextfurthest(&facet))) {
+ qh num_outside--; /* if ONLYmax, furthest may not be outside */
+ if (!qh_addpoint(furthest, facet, qh ONLYmax))
+ break;
+ }
+ if (qh NARROWhull) /* move points from outsideset to coplanarset */
+ qh_outcoplanar( /* facet_list */ );
+ if (qh num_outside && !furthest) {
+ qh_fprintf(qh ferr, 6167, "qhull internal error (qh_buildhull): %d outside points were never processed.\n", qh num_outside);
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ trace1((qh ferr, 1039, "qh_buildhull: completed the hull construction\n"));
+} /* buildhull */
+
+
+/*-<a href="qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="buildtracing">-</a>
+
+ qh_buildtracing( furthest, facet )
+ trace an iteration of qh_buildhull() for furthest point and facet
+ if !furthest, prints progress message
+
+ returns:
+ tracks progress with qh.lastreport
+ updates qh.furthest_id (-3 if furthest is NULL)
+ also resets visit_id, vertext_visit on wrap around
+
+ see:
+ qh_tracemerging()
+
+ design:
+ if !furthest
+ print progress message
+ exit
+ if 'TFn' iteration
+ print progress message
+ else if tracing
+ trace furthest point and facet
+ reset qh.visit_id and qh.vertex_visit if overflow may occur
+ set qh.furthest_id for tracing
+*/
+void qh_buildtracing(pointT *furthest, facetT *facet) {
+ realT dist= 0;
+ float cpu;
+ int total, furthestid;
+ time_t timedata;
+ struct tm *tp;
+ vertexT *vertex;
+
+ qh old_randomdist= qh RANDOMdist;
+ qh RANDOMdist= False;
+ if (!furthest) {
+ time(&timedata);
+ tp= localtime(&timedata);
+ cpu= (float)qh_CPUclock - (float)qh hulltime;
+ cpu /= (float)qh_SECticks;
+ total= zzval_(Ztotmerge) - zzval_(Zcyclehorizon) + zzval_(Zcyclefacettot);
+ qh_fprintf(qh ferr, 8118, "\n\
+At %02d:%02d:%02d & %2.5g CPU secs, qhull has created %d facets and merged %d.\n\
+ The current hull contains %d facets and %d vertices. Last point was p%d\n",
+ tp->tm_hour, tp->tm_min, tp->tm_sec, cpu, qh facet_id -1,
+ total, qh num_facets, qh num_vertices, qh furthest_id);
+ return;
+ }
+ furthestid= qh_pointid(furthest);
+ if (qh TRACEpoint == furthestid) {
+ qh IStracing= qh TRACElevel;
+ qhmem.IStracing= qh TRACElevel;
+ }else if (qh TRACEpoint != qh_IDunknown && qh TRACEdist < REALmax/2) {
+ qh IStracing= 0;
+ qhmem.IStracing= 0;
+ }
+ if (qh REPORTfreq && (qh facet_id-1 > qh lastreport+qh REPORTfreq)) {
+ qh lastreport= qh facet_id-1;
+ time(&timedata);
+ tp= localtime(&timedata);
+ cpu= (float)qh_CPUclock - (float)qh hulltime;
+ cpu /= (float)qh_SECticks;
+ total= zzval_(Ztotmerge) - zzval_(Zcyclehorizon) + zzval_(Zcyclefacettot);
+ zinc_(Zdistio);
+ qh_distplane(furthest, facet, &dist);
+ qh_fprintf(qh ferr, 8119, "\n\
+At %02d:%02d:%02d & %2.5g CPU secs, qhull has created %d facets and merged %d.\n\
+ The current hull contains %d facets and %d vertices. There are %d\n\
+ outside points. Next is point p%d(v%d), %2.2g above f%d.\n",
+ tp->tm_hour, tp->tm_min, tp->tm_sec, cpu, qh facet_id -1,
+ total, qh num_facets, qh num_vertices, qh num_outside+1,
+ furthestid, qh vertex_id, dist, getid_(facet));
+ }else if (qh IStracing >=1) {
+ cpu= (float)qh_CPUclock - (float)qh hulltime;
+ cpu /= (float)qh_SECticks;
+ qh_distplane(furthest, facet, &dist);
+ qh_fprintf(qh ferr, 8120, "qh_addpoint: add p%d(v%d) to hull of %d facets(%2.2g above f%d) and %d outside at %4.4g CPU secs. Previous was p%d.\n",
+ furthestid, qh vertex_id, qh num_facets, dist,
+ getid_(facet), qh num_outside+1, cpu, qh furthest_id);
+ }
+ zmax_(Zvisit2max, (int)qh visit_id/2);
+ if (qh visit_id > (unsigned) INT_MAX) { /* 31 bits */
+ zinc_(Zvisit);
+ qh visit_id= 0;
+ FORALLfacets
+ facet->visitid= 0;
+ }
+ zmax_(Zvvisit2max, (int)qh vertex_visit/2);
+ if (qh vertex_visit > (unsigned) INT_MAX) { /* 31 bits */
+ zinc_(Zvvisit);
+ qh vertex_visit= 0;
+ FORALLvertices
+ vertex->visitid= 0;
+ }
+ qh furthest_id= furthestid;
+ qh RANDOMdist= qh old_randomdist;
+} /* buildtracing */
+
+/*-<a href="qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="errexit2">-</a>
+
+ qh_errexit2( exitcode, facet, otherfacet )
+ return exitcode to system after an error
+ report two facets
+
+ returns:
+ assumes exitcode non-zero
+
+ see:
+ normally use qh_errexit() in user.c(reports a facet and a ridge)
+*/
+void qh_errexit2(int exitcode, facetT *facet, facetT *otherfacet) {
+
+ qh_errprint("ERRONEOUS", facet, otherfacet, NULL, NULL);
+ qh_errexit(exitcode, NULL, NULL);
+} /* errexit2 */
+
+
+/*-<a href="qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="findhorizon">-</a>
+
+ qh_findhorizon( point, facet, goodvisible, goodhorizon )
+ given a visible facet, find the point's horizon and visible facets
+ for all facets, !facet-visible
+
+ returns:
+ returns qh.visible_list/num_visible with all visible facets
+ marks visible facets with ->visible
+ updates count of good visible and good horizon facets
+ updates qh.max_outside, qh.max_vertex, facet->maxoutside
+
+ see:
+ similar to qh_delpoint()
+
+ design:
+ move facet to qh.visible_list at end of qh.facet_list
+ for all visible facets
+ for each unvisited neighbor of a visible facet
+ compute distance of point to neighbor
+ if point above neighbor
+ move neighbor to end of qh.visible_list
+ else if point is coplanar with neighbor
+ update qh.max_outside, qh.max_vertex, neighbor->maxoutside
+ mark neighbor coplanar (will create a samecycle later)
+ update horizon statistics
+*/
+void qh_findhorizon(pointT *point, facetT *facet, int *goodvisible, int *goodhorizon) {
+ facetT *neighbor, **neighborp, *visible;
+ int numhorizon= 0, coplanar= 0;
+ realT dist;
+
+ trace1((qh ferr, 1040,"qh_findhorizon: find horizon for point p%d facet f%d\n",qh_pointid(point),facet->id));
+ *goodvisible= *goodhorizon= 0;
+ zinc_(Ztotvisible);
+ qh_removefacet(facet); /* visible_list at end of qh facet_list */
+ qh_appendfacet(facet);
+ qh num_visible= 1;
+ if (facet->good)
+ (*goodvisible)++;
+ qh visible_list= facet;
+ facet->visible= True;
+ facet->f.replace= NULL;
+ if (qh IStracing >=4)
+ qh_errprint("visible", facet, NULL, NULL, NULL);
+ qh visit_id++;
+ FORALLvisible_facets {
+ if (visible->tricoplanar && !qh TRInormals) {
+ qh_fprintf(qh ferr, 6230, "Qhull internal error (qh_findhorizon): does not work for tricoplanar facets. Use option 'Q11'\n");
+ qh_errexit(qh_ERRqhull, visible, NULL);
+ }
+ visible->visitid= qh visit_id;
+ FOREACHneighbor_(visible) {
+ if (neighbor->visitid == qh visit_id)
+ continue;
+ neighbor->visitid= qh visit_id;
+ zzinc_(Znumvisibility);
+ qh_distplane(point, neighbor, &dist);
+ if (dist > qh MINvisible) {
+ zinc_(Ztotvisible);
+ qh_removefacet(neighbor); /* append to end of qh visible_list */
+ qh_appendfacet(neighbor);
+ neighbor->visible= True;
+ neighbor->f.replace= NULL;
+ qh num_visible++;
+ if (neighbor->good)
+ (*goodvisible)++;
+ if (qh IStracing >=4)
+ qh_errprint("visible", neighbor, NULL, NULL, NULL);
+ }else {
+ if (dist > - qh MAXcoplanar) {
+ neighbor->coplanar= True;
+ zzinc_(Zcoplanarhorizon);
+ qh_precision("coplanar horizon");
+ coplanar++;
+ if (qh MERGING) {
+ if (dist > 0) {
+ maximize_(qh max_outside, dist);
+ maximize_(qh max_vertex, dist);
+#if qh_MAXoutside
+ maximize_(neighbor->maxoutside, dist);
+#endif
+ }else
+ minimize_(qh min_vertex, dist); /* due to merge later */
+ }
+ trace2((qh ferr, 2057, "qh_findhorizon: point p%d is coplanar to horizon f%d, dist=%2.7g < qh MINvisible(%2.7g)\n",
+ qh_pointid(point), neighbor->id, dist, qh MINvisible));
+ }else
+ neighbor->coplanar= False;
+ zinc_(Ztothorizon);
+ numhorizon++;
+ if (neighbor->good)
+ (*goodhorizon)++;
+ if (qh IStracing >=4)
+ qh_errprint("horizon", neighbor, NULL, NULL, NULL);
+ }
+ }
+ }
+ if (!numhorizon) {
+ qh_precision("empty horizon");
+ qh_fprintf(qh ferr, 6168, "qhull precision error (qh_findhorizon): empty horizon\n\
+QhullPoint p%d was above all facets.\n", qh_pointid(point));
+ qh_printfacetlist(qh facet_list, NULL, True);
+ qh_errexit(qh_ERRprec, NULL, NULL);
+ }
+ trace1((qh ferr, 1041, "qh_findhorizon: %d horizon facets(good %d), %d visible(good %d), %d coplanar\n",
+ numhorizon, *goodhorizon, qh num_visible, *goodvisible, coplanar));
+ if (qh IStracing >= 4 && qh num_facets < 50)
+ qh_printlists();
+} /* findhorizon */
+
+/*-<a href="qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="nextfurthest">-</a>
+
+ qh_nextfurthest( visible )
+ returns next furthest point and visible facet for qh_addpoint()
+ starts search at qh.facet_next
+
+ returns:
+ removes furthest point from outside set
+ NULL if none available
+ advances qh.facet_next over facets with empty outside sets
+
+ design:
+ for each facet from qh.facet_next
+ if empty outside set
+ advance qh.facet_next
+ else if qh.NARROWhull
+ determine furthest outside point
+ if furthest point is not outside
+ advance qh.facet_next(point will be coplanar)
+ remove furthest point from outside set
+*/
+pointT *qh_nextfurthest(facetT **visible) {
+ facetT *facet;
+ int size, idx;
+ realT randr, dist;
+ pointT *furthest;
+
+ while ((facet= qh facet_next) != qh facet_tail) {
+ if (!facet->outsideset) {
+ qh facet_next= facet->next;
+ continue;
+ }
+ SETreturnsize_(facet->outsideset, size);
+ if (!size) {
+ qh_setfree(&facet->outsideset);
+ qh facet_next= facet->next;
+ continue;
+ }
+ if (qh NARROWhull) {
+ if (facet->notfurthest)
+ qh_furthestout(facet);
+ furthest= (pointT*)qh_setlast(facet->outsideset);
+#if qh_COMPUTEfurthest
+ qh_distplane(furthest, facet, &dist);
+ zinc_(Zcomputefurthest);
+#else
+ dist= facet->furthestdist;
+#endif
+ if (dist < qh MINoutside) { /* remainder of outside set is coplanar for qh_outcoplanar */
+ qh facet_next= facet->next;
+ continue;
+ }
+ }
+ if (!qh RANDOMoutside && !qh VIRTUALmemory) {
+ if (qh PICKfurthest) {
+ qh_furthestnext(/* qh.facet_list */);
+ facet= qh facet_next;
+ }
+ *visible= facet;
+ return((pointT*)qh_setdellast(facet->outsideset));
+ }
+ if (qh RANDOMoutside) {
+ int outcoplanar = 0;
+ if (qh NARROWhull) {
+ FORALLfacets {
+ if (facet == qh facet_next)
+ break;
+ if (facet->outsideset)
+ outcoplanar += qh_setsize( facet->outsideset);
+ }
+ }
+ randr= qh_RANDOMint;
+ randr= randr/(qh_RANDOMmax+1);
+ idx= (int)floor((qh num_outside - outcoplanar) * randr);
+ FORALLfacet_(qh facet_next) {
+ if (facet->outsideset) {
+ SETreturnsize_(facet->outsideset, size);
+ if (!size)
+ qh_setfree(&facet->outsideset);
+ else if (size > idx) {
+ *visible= facet;
+ return((pointT*)qh_setdelnth(facet->outsideset, idx));
+ }else
+ idx -= size;
+ }
+ }
+ qh_fprintf(qh ferr, 6169, "qhull internal error (qh_nextfurthest): num_outside %d is too low\nby at least %d, or a random real %g >= 1.0\n",
+ qh num_outside, idx+1, randr);
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }else { /* VIRTUALmemory */
+ facet= qh facet_tail->previous;
+ if (!(furthest= (pointT*)qh_setdellast(facet->outsideset))) {
+ if (facet->outsideset)
+ qh_setfree(&facet->outsideset);
+ qh_removefacet(facet);
+ qh_prependfacet(facet, &qh facet_list);
+ continue;
+ }
+ *visible= facet;
+ return furthest;
+ }
+ }
+ return NULL;
+} /* nextfurthest */
+
+/*-<a href="qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="partitionall">-</a>
+
+ qh_partitionall( vertices, points, numpoints )
+ partitions all points in points/numpoints to the outsidesets of facets
+ vertices= vertices in qh.facet_list(!partitioned)
+
+ returns:
+ builds facet->outsideset
+ does not partition qh.GOODpoint
+ if qh.ONLYgood && !qh.MERGING,
+ does not partition qh.GOODvertex
+
+ notes:
+ faster if qh.facet_list sorted by anticipated size of outside set
+
+ design:
+ initialize pointset with all points
+ remove vertices from pointset
+ remove qh.GOODpointp from pointset (unless it's qh.STOPcone or qh.STOPpoint)
+ for all facets
+ for all remaining points in pointset
+ compute distance from point to facet
+ if point is outside facet
+ remove point from pointset (by not reappending)
+ update bestpoint
+ append point or old bestpoint to facet's outside set
+ append bestpoint to facet's outside set (furthest)
+ for all points remaining in pointset
+ partition point into facets' outside sets and coplanar sets
+*/
+void qh_partitionall(setT *vertices, pointT *points, int numpoints){
+ setT *pointset;
+ vertexT *vertex, **vertexp;
+ pointT *point, **pointp, *bestpoint;
+ int size, point_i, point_n, point_end, remaining, i, id;
+ facetT *facet;
+ realT bestdist= -REALmax, dist, distoutside;
+
+ trace1((qh ferr, 1042, "qh_partitionall: partition all points into outside sets\n"));
+ pointset= qh_settemp(numpoints);
+ qh num_outside= 0;
+ pointp= SETaddr_(pointset, pointT);
+ for (i=numpoints, point= points; i--; point += qh hull_dim)
+ *(pointp++)= point;
+ qh_settruncate(pointset, numpoints);
+ FOREACHvertex_(vertices) {
+ if ((id= qh_pointid(vertex->point)) >= 0)
+ SETelem_(pointset, id)= NULL;
+ }
+ id= qh_pointid(qh GOODpointp);
+ if (id >=0 && qh STOPcone-1 != id && -qh STOPpoint-1 != id)
+ SETelem_(pointset, id)= NULL;
+ if (qh GOODvertexp && qh ONLYgood && !qh MERGING) { /* matches qhull()*/
+ if ((id= qh_pointid(qh GOODvertexp)) >= 0)
+ SETelem_(pointset, id)= NULL;
+ }
+ if (!qh BESToutside) { /* matches conditional for qh_partitionpoint below */
+ distoutside= qh_DISToutside; /* multiple of qh.MINoutside & qh.max_outside, see user.h */
+ zval_(Ztotpartition)= qh num_points - qh hull_dim - 1; /*misses GOOD... */
+ remaining= qh num_facets;
+ point_end= numpoints;
+ FORALLfacets {
+ size= point_end/(remaining--) + 100;
+ facet->outsideset= qh_setnew(size);
+ bestpoint= NULL;
+ point_end= 0;
+ FOREACHpoint_i_(pointset) {
+ if (point) {
+ zzinc_(Zpartitionall);
+ qh_distplane(point, facet, &dist);
+ if (dist < distoutside)
+ SETelem_(pointset, point_end++)= point;
+ else {
+ qh num_outside++;
+ if (!bestpoint) {
+ bestpoint= point;
+ bestdist= dist;
+ }else if (dist > bestdist) {
+ qh_setappend(&facet->outsideset, bestpoint);
+ bestpoint= point;
+ bestdist= dist;
+ }else
+ qh_setappend(&facet->outsideset, point);
+ }
+ }
+ }
+ if (bestpoint) {
+ qh_setappend(&facet->outsideset, bestpoint);
+#if !qh_COMPUTEfurthest
+ facet->furthestdist= bestdist;
+#endif
+ }else
+ qh_setfree(&facet->outsideset);
+ qh_settruncate(pointset, point_end);
+ }
+ }
+ /* if !qh BESToutside, pointset contains points not assigned to outsideset */
+ if (qh BESToutside || qh MERGING || qh KEEPcoplanar || qh KEEPinside) {
+ qh findbestnew= True;
+ FOREACHpoint_i_(pointset) {
+ if (point)
+ qh_partitionpoint(point, qh facet_list);
+ }
+ qh findbestnew= False;
+ }
+ zzadd_(Zpartitionall, zzval_(Zpartition));
+ zzval_(Zpartition)= 0;
+ qh_settempfree(&pointset);
+ if (qh IStracing >= 4)
+ qh_printfacetlist(qh facet_list, NULL, True);
+} /* partitionall */
+
+
+/*-<a href="qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="partitioncoplanar">-</a>
+
+ qh_partitioncoplanar( point, facet, dist )
+ partition coplanar point to a facet
+ dist is distance from point to facet
+ if dist NULL,
+ searches for bestfacet and does nothing if inside
+ if qh.findbestnew set,
+ searches new facets instead of using qh_findbest()
+
+ returns:
+ qh.max_ouside updated
+ if qh.KEEPcoplanar or qh.KEEPinside
+ point assigned to best coplanarset
+
+ notes:
+ facet->maxoutside is updated at end by qh_check_maxout
+
+ design:
+ if dist undefined
+ find best facet for point
+ if point sufficiently below facet (depends on qh.NEARinside and qh.KEEPinside)
+ exit
+ if keeping coplanar/nearinside/inside points
+ if point is above furthest coplanar point
+ append point to coplanar set (it is the new furthest)
+ update qh.max_outside
+ else
+ append point one before end of coplanar set
+ else if point is clearly outside of qh.max_outside and bestfacet->coplanarset
+ and bestfacet is more than perpendicular to facet
+ repartition the point using qh_findbest() -- it may be put on an outsideset
+ else
+ update qh.max_outside
+*/
+void qh_partitioncoplanar(pointT *point, facetT *facet, realT *dist) {
+ facetT *bestfacet;
+ pointT *oldfurthest;
+ realT bestdist, dist2= 0, angle;
+ int numpart= 0, oldfindbest;
+ boolT isoutside;
+
+ qh WAScoplanar= True;
+ if (!dist) {
+ if (qh findbestnew)
+ bestfacet= qh_findbestnew(point, facet, &bestdist, qh_ALL, &isoutside, &numpart);
+ else
+ bestfacet= qh_findbest(point, facet, qh_ALL, !qh_ISnewfacets, qh DELAUNAY,
+ &bestdist, &isoutside, &numpart);
+ zinc_(Ztotpartcoplanar);
+ zzadd_(Zpartcoplanar, numpart);
+ if (!qh DELAUNAY && !qh KEEPinside) { /* for 'd', bestdist skips upperDelaunay facets */
+ if (qh KEEPnearinside) {
+ if (bestdist < -qh NEARinside) {
+ zinc_(Zcoplanarinside);
+ trace4((qh ferr, 4062, "qh_partitioncoplanar: point p%d is more than near-inside facet f%d dist %2.2g findbestnew %d\n",
+ qh_pointid(point), bestfacet->id, bestdist, qh findbestnew));
+ return;
+ }
+ }else if (bestdist < -qh MAXcoplanar) {
+ trace4((qh ferr, 4063, "qh_partitioncoplanar: point p%d is inside facet f%d dist %2.2g findbestnew %d\n",
+ qh_pointid(point), bestfacet->id, bestdist, qh findbestnew));
+ zinc_(Zcoplanarinside);
+ return;
+ }
+ }
+ }else {
+ bestfacet= facet;
+ bestdist= *dist;
+ }
+ if (bestdist > qh max_outside) {
+ if (!dist && facet != bestfacet) {
+ zinc_(Zpartangle);
+ angle= qh_getangle(facet->normal, bestfacet->normal);
+ if (angle < 0) {
+ /* typically due to deleted vertex and coplanar facets, e.g.,
+ RBOX 1000 s Z1 G1e-13 t1001185205 | QHULL Tv */
+ zinc_(Zpartflip);
+ trace2((qh ferr, 2058, "qh_partitioncoplanar: repartition point p%d from f%d. It is above flipped facet f%d dist %2.2g\n",
+ qh_pointid(point), facet->id, bestfacet->id, bestdist));
+ oldfindbest= qh findbestnew;
+ qh findbestnew= False;
+ qh_partitionpoint(point, bestfacet);
+ qh findbestnew= oldfindbest;
+ return;
+ }
+ }
+ qh max_outside= bestdist;
+ if (bestdist > qh TRACEdist) {
+ qh_fprintf(qh ferr, 8122, "qh_partitioncoplanar: ====== p%d from f%d increases max_outside to %2.2g of f%d last p%d\n",
+ qh_pointid(point), facet->id, bestdist, bestfacet->id, qh furthest_id);
+ qh_errprint("DISTANT", facet, bestfacet, NULL, NULL);
+ }
+ }
+ if (qh KEEPcoplanar + qh KEEPinside + qh KEEPnearinside) {
+ oldfurthest= (pointT*)qh_setlast(bestfacet->coplanarset);
+ if (oldfurthest) {
+ zinc_(Zcomputefurthest);
+ qh_distplane(oldfurthest, bestfacet, &dist2);
+ }
+ if (!oldfurthest || dist2 < bestdist)
+ qh_setappend(&bestfacet->coplanarset, point);
+ else
+ qh_setappend2ndlast(&bestfacet->coplanarset, point);
+ }
+ trace4((qh ferr, 4064, "qh_partitioncoplanar: point p%d is coplanar with facet f%d(or inside) dist %2.2g\n",
+ qh_pointid(point), bestfacet->id, bestdist));
+} /* partitioncoplanar */
+
+/*-<a href="qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="partitionpoint">-</a>
+
+ qh_partitionpoint( point, facet )
+ assigns point to an outside set, coplanar set, or inside set (i.e., dropt)
+ if qh.findbestnew
+ uses qh_findbestnew() to search all new facets
+ else
+ uses qh_findbest()
+
+ notes:
+ after qh_distplane(), this and qh_findbest() are most expensive in 3-d
+
+ design:
+ find best facet for point
+ (either exhaustive search of new facets or directed search from facet)
+ if qh.NARROWhull
+ retain coplanar and nearinside points as outside points
+ if point is outside bestfacet
+ if point above furthest point for bestfacet
+ append point to outside set (it becomes the new furthest)
+ if outside set was empty
+ move bestfacet to end of qh.facet_list (i.e., after qh.facet_next)
+ update bestfacet->furthestdist
+ else
+ append point one before end of outside set
+ else if point is coplanar to bestfacet
+ if keeping coplanar points or need to update qh.max_outside
+ partition coplanar point into bestfacet
+ else if near-inside point
+ partition as coplanar point into bestfacet
+ else is an inside point
+ if keeping inside points
+ partition as coplanar point into bestfacet
+*/
+void qh_partitionpoint(pointT *point, facetT *facet) {
+ realT bestdist;
+ boolT isoutside;
+ facetT *bestfacet;
+ int numpart;
+#if qh_COMPUTEfurthest
+ realT dist;
+#endif
+
+ if (qh findbestnew)
+ bestfacet= qh_findbestnew(point, facet, &bestdist, qh BESToutside, &isoutside, &numpart);
+ else
+ bestfacet= qh_findbest(point, facet, qh BESToutside, qh_ISnewfacets, !qh_NOupper,
+ &bestdist, &isoutside, &numpart);
+ zinc_(Ztotpartition);
+ zzadd_(Zpartition, numpart);
+ if (qh NARROWhull) {
+ if (qh DELAUNAY && !isoutside && bestdist >= -qh MAXcoplanar)
+ qh_precision("nearly incident point(narrow hull)");
+ if (qh KEEPnearinside) {
+ if (bestdist >= -qh NEARinside)
+ isoutside= True;
+ }else if (bestdist >= -qh MAXcoplanar)
+ isoutside= True;
+ }
+
+ if (isoutside) {
+ if (!bestfacet->outsideset
+ || !qh_setlast(bestfacet->outsideset)) {
+ qh_setappend(&(bestfacet->outsideset), point);
+ if (!bestfacet->newfacet) {
+ qh_removefacet(bestfacet); /* make sure it's after qh facet_next */
+ qh_appendfacet(bestfacet);
+ }
+#if !qh_COMPUTEfurthest
+ bestfacet->furthestdist= bestdist;
+#endif
+ }else {
+#if qh_COMPUTEfurthest
+ zinc_(Zcomputefurthest);
+ qh_distplane(oldfurthest, bestfacet, &dist);
+ if (dist < bestdist)
+ qh_setappend(&(bestfacet->outsideset), point);
+ else
+ qh_setappend2ndlast(&(bestfacet->outsideset), point);
+#else
+ if (bestfacet->furthestdist < bestdist) {
+ qh_setappend(&(bestfacet->outsideset), point);
+ bestfacet->furthestdist= bestdist;
+ }else
+ qh_setappend2ndlast(&(bestfacet->outsideset), point);
+#endif
+ }
+ qh num_outside++;
+ trace4((qh ferr, 4065, "qh_partitionpoint: point p%d is outside facet f%d new? %d (or narrowhull)\n",
+ qh_pointid(point), bestfacet->id, bestfacet->newfacet));
+ }else if (qh DELAUNAY || bestdist >= -qh MAXcoplanar) { /* for 'd', bestdist skips upperDelaunay facets */
+ zzinc_(Zcoplanarpart);
+ if (qh DELAUNAY)
+ qh_precision("nearly incident point");
+ if ((qh KEEPcoplanar + qh KEEPnearinside) || bestdist > qh max_outside)
+ qh_partitioncoplanar(point, bestfacet, &bestdist);
+ else {
+ trace4((qh ferr, 4066, "qh_partitionpoint: point p%d is coplanar to facet f%d (dropped)\n",
+ qh_pointid(point), bestfacet->id));
+ }
+ }else if (qh KEEPnearinside && bestdist > -qh NEARinside) {
+ zinc_(Zpartnear);
+ qh_partitioncoplanar(point, bestfacet, &bestdist);
+ }else {
+ zinc_(Zpartinside);
+ trace4((qh ferr, 4067, "qh_partitionpoint: point p%d is inside all facets, closest to f%d dist %2.2g\n",
+ qh_pointid(point), bestfacet->id, bestdist));
+ if (qh KEEPinside)
+ qh_partitioncoplanar(point, bestfacet, &bestdist);
+ }
+} /* partitionpoint */
+
+/*-<a href="qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="partitionvisible">-</a>
+
+ qh_partitionvisible( allpoints, numoutside )
+ partitions points in visible facets to qh.newfacet_list
+ qh.visible_list= visible facets
+ for visible facets
+ 1st neighbor (if any) points to a horizon facet or a new facet
+ if allpoints(!used),
+ repartitions coplanar points
+
+ returns:
+ updates outside sets and coplanar sets of qh.newfacet_list
+ updates qh.num_outside (count of outside points)
+
+ notes:
+ qh.findbest_notsharp should be clear (extra work if set)
+
+ design:
+ for all visible facets with outside set or coplanar set
+ select a newfacet for visible facet
+ if outside set
+ partition outside set into new facets
+ if coplanar set and keeping coplanar/near-inside/inside points
+ if allpoints
+ partition coplanar set into new facets, may be assigned outside
+ else
+ partition coplanar set into coplanar sets of new facets
+ for each deleted vertex
+ if allpoints
+ partition vertex into new facets, may be assigned outside
+ else
+ partition vertex into coplanar sets of new facets
+*/
+void qh_partitionvisible(/*qh.visible_list*/ boolT allpoints, int *numoutside) {
+ facetT *visible, *newfacet;
+ pointT *point, **pointp;
+ int coplanar=0, size;
+ unsigned count;
+ vertexT *vertex, **vertexp;
+
+ if (qh ONLYmax)
+ maximize_(qh MINoutside, qh max_vertex);
+ *numoutside= 0;
+ FORALLvisible_facets {
+ if (!visible->outsideset && !visible->coplanarset)
+ continue;
+ newfacet= visible->f.replace;
+ count= 0;
+ while (newfacet && newfacet->visible) {
+ newfacet= newfacet->f.replace;
+ if (count++ > qh facet_id)
+ qh_infiniteloop(visible);
+ }
+ if (!newfacet)
+ newfacet= qh newfacet_list;
+ if (newfacet == qh facet_tail) {
+ qh_fprintf(qh ferr, 6170, "qhull precision error (qh_partitionvisible): all new facets deleted as\n degenerate facets. Can not continue.\n");
+ qh_errexit(qh_ERRprec, NULL, NULL);
+ }
+ if (visible->outsideset) {
+ size= qh_setsize(visible->outsideset);
+ *numoutside += size;
+ qh num_outside -= size;
+ FOREACHpoint_(visible->outsideset)
+ qh_partitionpoint(point, newfacet);
+ }
+ if (visible->coplanarset && (qh KEEPcoplanar + qh KEEPinside + qh KEEPnearinside)) {
+ size= qh_setsize(visible->coplanarset);
+ coplanar += size;
+ FOREACHpoint_(visible->coplanarset) {
+ if (allpoints) /* not used */
+ qh_partitionpoint(point, newfacet);
+ else
+ qh_partitioncoplanar(point, newfacet, NULL);
+ }
+ }
+ }
+ FOREACHvertex_(qh del_vertices) {
+ if (vertex->point) {
+ if (allpoints) /* not used */
+ qh_partitionpoint(vertex->point, qh newfacet_list);
+ else
+ qh_partitioncoplanar(vertex->point, qh newfacet_list, NULL);
+ }
+ }
+ trace1((qh ferr, 1043,"qh_partitionvisible: partitioned %d points from outsidesets and %d points from coplanarsets\n", *numoutside, coplanar));
+} /* partitionvisible */
+
+
+
+/*-<a href="qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="precision">-</a>
+
+ qh_precision( reason )
+ restart on precision errors if not merging and if 'QJn'
+*/
+void qh_precision(const char *reason) {
+
+ if (qh ALLOWrestart && !qh PREmerge && !qh MERGEexact) {
+ if (qh JOGGLEmax < REALmax/2) {
+ trace0((qh ferr, 26, "qh_precision: qhull restart because of %s\n", reason));
+ /* May be called repeatedly if qh->ALLOWrestart */
+ longjmp(qh restartexit, qh_ERRprec);
+ }
+ }
+} /* qh_precision */
+
+/*-<a href="qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="printsummary">-</a>
+
+ qh_printsummary( fp )
+ prints summary to fp
+
+ notes:
+ not in io.c so that user_eg.c can prevent io.c from loading
+ qh_printsummary and qh_countfacets must match counts
+
+ design:
+ determine number of points, vertices, and coplanar points
+ print summary
+*/
+void qh_printsummary(FILE *fp) {
+ realT ratio, outerplane, innerplane;
+ float cpu;
+ int size, id, nummerged, numvertices, numcoplanars= 0, nonsimplicial=0;
+ int goodused;
+ facetT *facet;
+ const char *s;
+ int numdel= zzval_(Zdelvertextot);
+ int numtricoplanars= 0;
+
+ size= qh num_points + qh_setsize(qh other_points);
+ numvertices= qh num_vertices - qh_setsize(qh del_vertices);
+ id= qh_pointid(qh GOODpointp);
+ FORALLfacets {
+ if (facet->coplanarset)
+ numcoplanars += qh_setsize( facet->coplanarset);
+ if (facet->good) {
+ if (facet->simplicial) {
+ if (facet->keepcentrum && facet->tricoplanar)
+ numtricoplanars++;
+ }else if (qh_setsize(facet->vertices) != qh hull_dim)
+ nonsimplicial++;
+ }
+ }
+ if (id >=0 && qh STOPcone-1 != id && -qh STOPpoint-1 != id)
+ size--;
+ if (qh STOPcone || qh STOPpoint)
+ qh_fprintf(fp, 9288, "\nAt a premature exit due to 'TVn', 'TCn', 'TRn', or precision error with 'QJn'.");
+ if (qh UPPERdelaunay)
+ goodused= qh GOODvertex + qh GOODpoint + qh SPLITthresholds;
+ else if (qh DELAUNAY)
+ goodused= qh GOODvertex + qh GOODpoint + qh GOODthreshold;
+ else
+ goodused= qh num_good;
+ nummerged= zzval_(Ztotmerge) - zzval_(Zcyclehorizon) + zzval_(Zcyclefacettot);
+ if (qh VORONOI) {
+ if (qh UPPERdelaunay)
+ qh_fprintf(fp, 9289, "\n\
+Furthest-site Voronoi vertices by the convex hull of %d points in %d-d:\n\n", size, qh hull_dim);
+ else
+ qh_fprintf(fp, 9290, "\n\
+Voronoi diagram by the convex hull of %d points in %d-d:\n\n", size, qh hull_dim);
+ qh_fprintf(fp, 9291, " Number of Voronoi regions%s: %d\n",
+ qh ATinfinity ? " and at-infinity" : "", numvertices);
+ if (numdel)
+ qh_fprintf(fp, 9292, " Total number of deleted points due to merging: %d\n", numdel);
+ if (numcoplanars - numdel > 0)
+ qh_fprintf(fp, 9293, " Number of nearly incident points: %d\n", numcoplanars - numdel);
+ else if (size - numvertices - numdel > 0)
+ qh_fprintf(fp, 9294, " Total number of nearly incident points: %d\n", size - numvertices - numdel);
+ qh_fprintf(fp, 9295, " Number of%s Voronoi vertices: %d\n",
+ goodused ? " 'good'" : "", qh num_good);
+ if (nonsimplicial)
+ qh_fprintf(fp, 9296, " Number of%s non-simplicial Voronoi vertices: %d\n",
+ goodused ? " 'good'" : "", nonsimplicial);
+ }else if (qh DELAUNAY) {
+ if (qh UPPERdelaunay)
+ qh_fprintf(fp, 9297, "\n\
+Furthest-site Delaunay triangulation by the convex hull of %d points in %d-d:\n\n", size, qh hull_dim);
+ else
+ qh_fprintf(fp, 9298, "\n\
+Delaunay triangulation by the convex hull of %d points in %d-d:\n\n", size, qh hull_dim);
+ qh_fprintf(fp, 9299, " Number of input sites%s: %d\n",
+ qh ATinfinity ? " and at-infinity" : "", numvertices);
+ if (numdel)
+ qh_fprintf(fp, 9300, " Total number of deleted points due to merging: %d\n", numdel);
+ if (numcoplanars - numdel > 0)
+ qh_fprintf(fp, 9301, " Number of nearly incident points: %d\n", numcoplanars - numdel);
+ else if (size - numvertices - numdel > 0)
+ qh_fprintf(fp, 9302, " Total number of nearly incident points: %d\n", size - numvertices - numdel);
+ qh_fprintf(fp, 9303, " Number of%s Delaunay regions: %d\n",
+ goodused ? " 'good'" : "", qh num_good);
+ if (nonsimplicial)
+ qh_fprintf(fp, 9304, " Number of%s non-simplicial Delaunay regions: %d\n",
+ goodused ? " 'good'" : "", nonsimplicial);
+ }else if (qh HALFspace) {
+ qh_fprintf(fp, 9305, "\n\
+Halfspace intersection by the convex hull of %d points in %d-d:\n\n", size, qh hull_dim);
+ qh_fprintf(fp, 9306, " Number of halfspaces: %d\n", size);
+ qh_fprintf(fp, 9307, " Number of non-redundant halfspaces: %d\n", numvertices);
+ if (numcoplanars) {
+ if (qh KEEPinside && qh KEEPcoplanar)
+ s= "similar and redundant";
+ else if (qh KEEPinside)
+ s= "redundant";
+ else
+ s= "similar";
+ qh_fprintf(fp, 9308, " Number of %s halfspaces: %d\n", s, numcoplanars);
+ }
+ qh_fprintf(fp, 9309, " Number of intersection points: %d\n", qh num_facets - qh num_visible);
+ if (goodused)
+ qh_fprintf(fp, 9310, " Number of 'good' intersection points: %d\n", qh num_good);
+ if (nonsimplicial)
+ qh_fprintf(fp, 9311, " Number of%s non-simplicial intersection points: %d\n",
+ goodused ? " 'good'" : "", nonsimplicial);
+ }else {
+ qh_fprintf(fp, 9312, "\n\
+Convex hull of %d points in %d-d:\n\n", size, qh hull_dim);
+ qh_fprintf(fp, 9313, " Number of vertices: %d\n", numvertices);
+ if (numcoplanars) {
+ if (qh KEEPinside && qh KEEPcoplanar)
+ s= "coplanar and interior";
+ else if (qh KEEPinside)
+ s= "interior";
+ else
+ s= "coplanar";
+ qh_fprintf(fp, 9314, " Number of %s points: %d\n", s, numcoplanars);
+ }
+ qh_fprintf(fp, 9315, " Number of facets: %d\n", qh num_facets - qh num_visible);
+ if (goodused)
+ qh_fprintf(fp, 9316, " Number of 'good' facets: %d\n", qh num_good);
+ if (nonsimplicial)
+ qh_fprintf(fp, 9317, " Number of%s non-simplicial facets: %d\n",
+ goodused ? " 'good'" : "", nonsimplicial);
+ }
+ if (numtricoplanars)
+ qh_fprintf(fp, 9318, " Number of triangulated facets: %d\n", numtricoplanars);
+ qh_fprintf(fp, 9319, "\nStatistics for: %s | %s",
+ qh rbox_command, qh qhull_command);
+ if (qh ROTATErandom != INT_MIN)
+ qh_fprintf(fp, 9320, " QR%d\n\n", qh ROTATErandom);
+ else
+ qh_fprintf(fp, 9321, "\n\n");
+ qh_fprintf(fp, 9322, " Number of points processed: %d\n", zzval_(Zprocessed));
+ qh_fprintf(fp, 9323, " Number of hyperplanes created: %d\n", zzval_(Zsetplane));
+ if (qh DELAUNAY)
+ qh_fprintf(fp, 9324, " Number of facets in hull: %d\n", qh num_facets - qh num_visible);
+ qh_fprintf(fp, 9325, " Number of distance tests for qhull: %d\n", zzval_(Zpartition)+
+ zzval_(Zpartitionall)+zzval_(Znumvisibility)+zzval_(Zpartcoplanar));
+#if 0 /* NOTE: must print before printstatistics() */
+ {realT stddev, ave;
+ qh_fprintf(fp, 9326, " average new facet balance: %2.2g\n",
+ wval_(Wnewbalance)/zval_(Zprocessed));
+ stddev= qh_stddev(zval_(Zprocessed), wval_(Wnewbalance),
+ wval_(Wnewbalance2), &ave);
+ qh_fprintf(fp, 9327, " new facet standard deviation: %2.2g\n", stddev);
+ qh_fprintf(fp, 9328, " average partition balance: %2.2g\n",
+ wval_(Wpbalance)/zval_(Zpbalance));
+ stddev= qh_stddev(zval_(Zpbalance), wval_(Wpbalance),
+ wval_(Wpbalance2), &ave);
+ qh_fprintf(fp, 9329, " partition standard deviation: %2.2g\n", stddev);
+ }
+#endif
+ if (nummerged) {
+ qh_fprintf(fp, 9330," Number of distance tests for merging: %d\n",zzval_(Zbestdist)+
+ zzval_(Zcentrumtests)+zzval_(Zdistconvex)+zzval_(Zdistcheck)+
+ zzval_(Zdistzero));
+ qh_fprintf(fp, 9331," Number of distance tests for checking: %d\n",zzval_(Zcheckpart));
+ qh_fprintf(fp, 9332," Number of merged facets: %d\n", nummerged);
+ }
+ if (!qh RANDOMoutside && qh QHULLfinished) {
+ cpu= (float)qh hulltime;
+ cpu /= (float)qh_SECticks;
+ wval_(Wcpu)= cpu;
+ qh_fprintf(fp, 9333, " CPU seconds to compute hull (after input): %2.4g\n", cpu);
+ }
+ if (qh RERUN) {
+ if (!qh PREmerge && !qh MERGEexact)
+ qh_fprintf(fp, 9334, " Percentage of runs with precision errors: %4.1f\n",
+ zzval_(Zretry)*100.0/qh build_cnt); /* careful of order */
+ }else if (qh JOGGLEmax < REALmax/2) {
+ if (zzval_(Zretry))
+ qh_fprintf(fp, 9335, " After %d retries, input joggled by: %2.2g\n",
+ zzval_(Zretry), qh JOGGLEmax);
+ else
+ qh_fprintf(fp, 9336, " Input joggled by: %2.2g\n", qh JOGGLEmax);
+ }
+ if (qh totarea != 0.0)
+ qh_fprintf(fp, 9337, " %s facet area: %2.8g\n",
+ zzval_(Ztotmerge) ? "Approximate" : "Total", qh totarea);
+ if (qh totvol != 0.0)
+ qh_fprintf(fp, 9338, " %s volume: %2.8g\n",
+ zzval_(Ztotmerge) ? "Approximate" : "Total", qh totvol);
+ if (qh MERGING) {
+ qh_outerinner(NULL, &outerplane, &innerplane);
+ if (outerplane > 2 * qh DISTround) {
+ qh_fprintf(fp, 9339, " Maximum distance of %spoint above facet: %2.2g",
+ (qh QHULLfinished ? "" : "merged "), outerplane);
+ ratio= outerplane/(qh ONEmerge + qh DISTround);
+ /* don't report ratio if MINoutside is large */
+ if (ratio > 0.05 && 2* qh ONEmerge > qh MINoutside && qh JOGGLEmax > REALmax/2)
+ qh_fprintf(fp, 9340, " (%.1fx)\n", ratio);
+ else
+ qh_fprintf(fp, 9341, "\n");
+ }
+ if (innerplane < -2 * qh DISTround) {
+ qh_fprintf(fp, 9342, " Maximum distance of %svertex below facet: %2.2g",
+ (qh QHULLfinished ? "" : "merged "), innerplane);
+ ratio= -innerplane/(qh ONEmerge+qh DISTround);
+ if (ratio > 0.05 && qh JOGGLEmax > REALmax/2)
+ qh_fprintf(fp, 9343, " (%.1fx)\n", ratio);
+ else
+ qh_fprintf(fp, 9344, "\n");
+ }
+ }
+ qh_fprintf(fp, 9345, "\n");
+} /* printsummary */
+
+
diff --git a/xs/src/qhull/src/libqhull/libqhull.h b/xs/src/qhull/src/libqhull/libqhull.h
new file mode 100644
index 000000000..677085808
--- /dev/null
+++ b/xs/src/qhull/src/libqhull/libqhull.h
@@ -0,0 +1,1140 @@
+/*<html><pre> -<a href="qh-qhull.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ libqhull.h
+ user-level header file for using qhull.a library
+
+ see qh-qhull.htm, qhull_a.h
+
+ Copyright (c) 1993-2015 The Geometry Center.
+ $Id: //main/2015/qhull/src/libqhull/libqhull.h#7 $$Change: 2066 $
+ $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
+
+ NOTE: access to qh_qh is via the 'qh' macro. This allows
+ qh_qh to be either a pointer or a structure. An example
+ of using qh is "qh.DROPdim" which accesses the DROPdim
+ field of qh_qh. Similarly, access to qh_qhstat is via
+ the 'qhstat' macro.
+
+ includes function prototypes for libqhull.c, geom.c, global.c, io.c, user.c
+
+ use mem.h for mem.c
+ use qset.h for qset.c
+
+ see unix.c for an example of using libqhull.h
+
+ recompile qhull if you change this file
+*/
+
+#ifndef qhDEFlibqhull
+#define qhDEFlibqhull 1
+
+/*=========================== -included files ==============*/
+
+/* user_r.h first for QHULL_CRTDBG */
+#include "user.h" /* user definable constants (e.g., qh_QHpointer) */
+
+#include "mem.h" /* Needed qhT in libqhull_r.h. Here for compatibility */
+#include "qset.h" /* Needed for QHULL_LIB_CHECK */
+/* include stat_r.h after defining boolT. Needed for qhT in libqhull_r.h. Here for compatibility and statT */
+
+#include <setjmp.h>
+#include <float.h>
+#include <time.h>
+#include <stdio.h>
+
+#if __MWERKS__ && __POWERPC__
+#include <SIOUX.h>
+#include <Files.h>
+#include <Desk.h>
+#endif
+
+#ifndef __STDC__
+#ifndef __cplusplus
+#if !_MSC_VER
+#error Neither __STDC__ nor __cplusplus is defined. Please use strict ANSI C or C++ to compile
+#error Qhull. You may need to turn off compiler extensions in your project configuration. If
+#error your compiler is a standard C compiler, you can delete this warning from libqhull.h
+#endif
+#endif
+#endif
+
+/*============ constants and basic types ====================*/
+
+extern const char qh_version[]; /* defined in global.c */
+extern const char qh_version2[]; /* defined in global.c */
+
+/*-<a href="qh-geom.htm#TOC"
+ >--------------------------------</a><a name="coordT">-</a>
+
+ coordT
+ coordinates and coefficients are stored as realT (i.e., double)
+
+ notes:
+ Qhull works well if realT is 'float'. If so joggle (QJ) is not effective.
+
+ Could use 'float' for data and 'double' for calculations (realT vs. coordT)
+ This requires many type casts, and adjusted error bounds.
+ Also C compilers may do expressions in double anyway.
+*/
+#define coordT realT
+
+/*-<a href="qh-geom.htm#TOC"
+ >--------------------------------</a><a name="pointT">-</a>
+
+ pointT
+ a point is an array of coordinates, usually qh.hull_dim
+ qh_pointid returns
+ qh_IDnone if point==0 or qh is undefined
+ qh_IDinterior for qh.interior_point
+ qh_IDunknown if point is neither in qh.first_point... nor qh.other_points
+
+ notes:
+ qh.STOPcone and qh.STOPpoint assume that qh_IDunknown==-1 (other negative numbers indicate points)
+ qh_IDunknown is also returned by getid_() for unknown facet, ridge, or vertex
+*/
+#define pointT coordT
+typedef enum
+{
+ qh_IDnone = -3, qh_IDinterior = -2, qh_IDunknown = -1
+}
+qh_pointT;
+
+/*-<a href="qh-qhull.htm#TOC"
+ >--------------------------------</a><a name="flagT">-</a>
+
+ flagT
+ Boolean flag as a bit
+*/
+#define flagT unsigned int
+
+/*-<a href="qh-qhull.htm#TOC"
+ >--------------------------------</a><a name="boolT">-</a>
+
+ boolT
+ boolean value, either True or False
+
+ notes:
+ needed for portability
+ Use qh_False/qh_True as synonyms
+*/
+#define boolT unsigned int
+#ifdef False
+#undef False
+#endif
+#ifdef True
+#undef True
+#endif
+#define False 0
+#define True 1
+#define qh_False 0
+#define qh_True 1
+
+#include "stat.h" /* after define of boolT */
+
+/*-<a href="qh-qhull.htm#TOC"
+ >--------------------------------</a><a name="CENTERtype">-</a>
+
+ qh_CENTER
+ to distinguish facet->center
+*/
+typedef enum
+{
+ qh_ASnone = 0, /* If not MERGING and not VORONOI */
+ qh_ASvoronoi, /* Set by qh_clearcenters on qh_prepare_output, or if not MERGING and VORONOI */
+ qh_AScentrum /* If MERGING (assumed during merging) */
+}
+qh_CENTER;
+
+/*-<a href="qh-qhull.htm#TOC"
+ >--------------------------------</a><a name="qh_PRINT">-</a>
+
+ qh_PRINT
+ output formats for printing (qh.PRINTout).
+ 'Fa' 'FV' 'Fc' 'FC'
+
+
+ notes:
+ some of these names are similar to qhT names. The similar names are only
+ used in switch statements in qh_printbegin() etc.
+*/
+typedef enum {qh_PRINTnone= 0,
+ qh_PRINTarea, qh_PRINTaverage, /* 'Fa' 'FV' 'Fc' 'FC' */
+ qh_PRINTcoplanars, qh_PRINTcentrums,
+ qh_PRINTfacets, qh_PRINTfacets_xridge, /* 'f' 'FF' 'G' 'FI' 'Fi' 'Fn' */
+ qh_PRINTgeom, qh_PRINTids, qh_PRINTinner, qh_PRINTneighbors,
+ qh_PRINTnormals, qh_PRINTouter, qh_PRINTmaple, /* 'n' 'Fo' 'i' 'm' 'Fm' 'FM', 'o' */
+ qh_PRINTincidences, qh_PRINTmathematica, qh_PRINTmerges, qh_PRINToff,
+ qh_PRINToptions, qh_PRINTpointintersect, /* 'FO' 'Fp' 'FP' 'p' 'FQ' 'FS' */
+ qh_PRINTpointnearest, qh_PRINTpoints, qh_PRINTqhull, qh_PRINTsize,
+ qh_PRINTsummary, qh_PRINTtriangles, /* 'Fs' 'Ft' 'Fv' 'FN' 'Fx' */
+ qh_PRINTvertices, qh_PRINTvneighbors, qh_PRINTextremes,
+ qh_PRINTEND} qh_PRINT;
+
+/*-<a href="qh-qhull.htm#TOC"
+ >--------------------------------</a><a name="qh_ALL">-</a>
+
+ qh_ALL
+ argument flag for selecting everything
+*/
+#define qh_ALL True
+#define qh_NOupper True /* argument for qh_findbest */
+#define qh_IScheckmax True /* argument for qh_findbesthorizon */
+#define qh_ISnewfacets True /* argument for qh_findbest */
+#define qh_RESETvisible True /* argument for qh_resetlists */
+
+/*-<a href="qh-qhull.htm#TOC"
+ >--------------------------------</a><a name="qh_ERR">-</a>
+
+ qh_ERR
+ Qhull exit codes, for indicating errors
+ See: MSG_ERROR and MSG_WARNING [user.h]
+*/
+#define qh_ERRnone 0 /* no error occurred during qhull */
+#define qh_ERRinput 1 /* input inconsistency */
+#define qh_ERRsingular 2 /* singular input data */
+#define qh_ERRprec 3 /* precision error */
+#define qh_ERRmem 4 /* insufficient memory, matches mem.h */
+#define qh_ERRqhull 5 /* internal error detected, matches mem.h */
+
+/*-<a href="qh-qhull.htm#TOC"
+>--------------------------------</a><a name="qh_FILEstderr">-</a>
+
+qh_FILEstderr
+Fake stderr to distinguish error output from normal output
+For C++ interface. Must redefine qh_fprintf_qhull
+*/
+#define qh_FILEstderr ((FILE*)1)
+
+/* ============ -structures- ====================
+ each of the following structures is defined by a typedef
+ all realT and coordT fields occur at the beginning of a structure
+ (otherwise space may be wasted due to alignment)
+ define all flags together and pack into 32-bit number
+ DEFsetT is likewise defined in
+ mem.h and qset.h
+*/
+
+typedef struct vertexT vertexT;
+typedef struct ridgeT ridgeT;
+typedef struct facetT facetT;
+#ifndef DEFsetT
+#define DEFsetT 1
+typedef struct setT setT; /* defined in qset.h */
+#endif
+
+/*-<a href="qh-poly.htm#TOC"
+ >--------------------------------</a><a name="facetT">-</a>
+
+ facetT
+ defines a facet
+
+ notes:
+ qhull() generates the hull as a list of facets.
+
+ topological information:
+ f.previous,next doubly-linked list of facets
+ f.vertices set of vertices
+ f.ridges set of ridges
+ f.neighbors set of neighbors
+ f.toporient True if facet has top-orientation (else bottom)
+
+ geometric information:
+ f.offset,normal hyperplane equation
+ f.maxoutside offset to outer plane -- all points inside
+ f.center centrum for testing convexity
+ f.simplicial True if facet is simplicial
+ f.flipped True if facet does not include qh.interior_point
+
+ for constructing hull:
+ f.visible True if facet on list of visible facets (will be deleted)
+ f.newfacet True if facet on list of newly created facets
+ f.coplanarset set of points coplanar with this facet
+ (includes near-inside points for later testing)
+ f.outsideset set of points outside of this facet
+ f.furthestdist distance to furthest point of outside set
+ f.visitid marks visited facets during a loop
+ f.replace replacement facet for to-be-deleted, visible facets
+ f.samecycle,newcycle cycle of facets for merging into horizon facet
+
+ see below for other flags and fields
+*/
+struct facetT {
+#if !qh_COMPUTEfurthest
+ coordT furthestdist;/* distance to furthest point of outsideset */
+#endif
+#if qh_MAXoutside
+ coordT maxoutside; /* max computed distance of point to facet
+ Before QHULLfinished this is an approximation
+ since maxdist not always set for mergefacet
+ Actual outer plane is +DISTround and
+ computed outer plane is +2*DISTround */
+#endif
+ coordT offset; /* exact offset of hyperplane from origin */
+ coordT *normal; /* normal of hyperplane, hull_dim coefficients */
+ /* if tricoplanar, shared with a neighbor */
+ union { /* in order of testing */
+ realT area; /* area of facet, only in io.c if ->isarea */
+ facetT *replace; /* replacement facet if ->visible and NEWfacets
+ is NULL only if qh_mergedegen_redundant or interior */
+ facetT *samecycle; /* cycle of facets from the same visible/horizon intersection,
+ if ->newfacet */
+ facetT *newcycle; /* in horizon facet, current samecycle of new facets */
+ facetT *trivisible; /* visible facet for ->tricoplanar facets during qh_triangulate() */
+ facetT *triowner; /* owner facet for ->tricoplanar, !isarea facets w/ ->keepcentrum */
+ }f;
+ coordT *center; /* set according to qh.CENTERtype */
+ /* qh_ASnone: no center (not MERGING) */
+ /* qh_AScentrum: centrum for testing convexity (qh_getcentrum) */
+ /* assumed qh_AScentrum while merging */
+ /* qh_ASvoronoi: Voronoi center (qh_facetcenter) */
+ /* after constructing the hull, it may be changed (qh_clearcenter) */
+ /* if tricoplanar and !keepcentrum, shared with a neighbor */
+ facetT *previous; /* previous facet in the facet_list */
+ facetT *next; /* next facet in the facet_list */
+ setT *vertices; /* vertices for this facet, inverse sorted by ID
+ if simplicial, 1st vertex was apex/furthest */
+ setT *ridges; /* explicit ridges for nonsimplicial facets.
+ for simplicial facets, neighbors define the ridges */
+ setT *neighbors; /* neighbors of the facet. If simplicial, the kth
+ neighbor is opposite the kth vertex, and the first
+ neighbor is the horizon facet for the first vertex*/
+ setT *outsideset; /* set of points outside this facet
+ if non-empty, last point is furthest
+ if NARROWhull, includes coplanars for partitioning*/
+ setT *coplanarset; /* set of points coplanar with this facet
+ > qh.min_vertex and <= facet->max_outside
+ a point is assigned to the furthest facet
+ if non-empty, last point is furthest away */
+ unsigned visitid; /* visit_id, for visiting all neighbors,
+ all uses are independent */
+ unsigned id; /* unique identifier from qh.facet_id */
+ unsigned nummerge:9; /* number of merges */
+#define qh_MAXnummerge 511 /* 2^9-1, 32 flags total, see "flags:" in io.c */
+ flagT tricoplanar:1; /* True if TRIangulate and simplicial and coplanar with a neighbor */
+ /* all tricoplanars share the same apex */
+ /* all tricoplanars share the same ->center, ->normal, ->offset, ->maxoutside */
+ /* ->keepcentrum is true for the owner. It has the ->coplanareset */
+ /* if ->degenerate, does not span facet (one logical ridge) */
+ /* during qh_triangulate, f.trivisible points to original facet */
+ flagT newfacet:1; /* True if facet on qh.newfacet_list (new or merged) */
+ flagT visible:1; /* True if visible facet (will be deleted) */
+ flagT toporient:1; /* True if created with top orientation
+ after merging, use ridge orientation */
+ flagT simplicial:1;/* True if simplicial facet, ->ridges may be implicit */
+ flagT seen:1; /* used to perform operations only once, like visitid */
+ flagT seen2:1; /* used to perform operations only once, like visitid */
+ flagT flipped:1; /* True if facet is flipped */
+ flagT upperdelaunay:1; /* True if facet is upper envelope of Delaunay triangulation */
+ flagT notfurthest:1; /* True if last point of outsideset is not furthest*/
+
+/*-------- flags primarily for output ---------*/
+ flagT good:1; /* True if a facet marked good for output */
+ flagT isarea:1; /* True if facet->f.area is defined */
+
+/*-------- flags for merging ------------------*/
+ flagT dupridge:1; /* True if duplicate ridge in facet */
+ flagT mergeridge:1; /* True if facet or neighbor contains a qh_MERGEridge
+ ->normal defined (also defined for mergeridge2) */
+ flagT mergeridge2:1; /* True if neighbor contains a qh_MERGEridge (mark_dupridges */
+ flagT coplanar:1; /* True if horizon facet is coplanar at last use */
+ flagT mergehorizon:1; /* True if will merge into horizon (->coplanar) */
+ flagT cycledone:1;/* True if mergecycle_all already done */
+ flagT tested:1; /* True if facet convexity has been tested (false after merge */
+ flagT keepcentrum:1; /* True if keep old centrum after a merge, or marks owner for ->tricoplanar */
+ flagT newmerge:1; /* True if facet is newly merged for reducevertices */
+ flagT degenerate:1; /* True if facet is degenerate (degen_mergeset or ->tricoplanar) */
+ flagT redundant:1; /* True if facet is redundant (degen_mergeset) */
+};
+
+
+/*-<a href="qh-poly.htm#TOC"
+ >--------------------------------</a><a name="ridgeT">-</a>
+
+ ridgeT
+ defines a ridge
+
+ notes:
+ a ridge is hull_dim-1 simplex between two neighboring facets. If the
+ facets are non-simplicial, there may be more than one ridge between
+ two facets. E.G. a 4-d hypercube has two triangles between each pair
+ of neighboring facets.
+
+ topological information:
+ vertices a set of vertices
+ top,bottom neighboring facets with orientation
+
+ geometric information:
+ tested True if ridge is clearly convex
+ nonconvex True if ridge is non-convex
+*/
+struct ridgeT {
+ setT *vertices; /* vertices belonging to this ridge, inverse sorted by ID
+ NULL if a degen ridge (matchsame) */
+ facetT *top; /* top facet this ridge is part of */
+ facetT *bottom; /* bottom facet this ridge is part of */
+ unsigned id; /* unique identifier. Same size as vertex_id and ridge_id */
+ flagT seen:1; /* used to perform operations only once */
+ flagT tested:1; /* True when ridge is tested for convexity */
+ flagT nonconvex:1; /* True if getmergeset detected a non-convex neighbor
+ only one ridge between neighbors may have nonconvex */
+};
+
+/*-<a href="qh-poly.htm#TOC"
+ >--------------------------------</a><a name="vertexT">-</a>
+
+ vertexT
+ defines a vertex
+
+ topological information:
+ next,previous doubly-linked list of all vertices
+ neighbors set of adjacent facets (only if qh.VERTEXneighbors)
+
+ geometric information:
+ point array of DIM3 coordinates
+*/
+struct vertexT {
+ vertexT *next; /* next vertex in vertex_list */
+ vertexT *previous; /* previous vertex in vertex_list */
+ pointT *point; /* hull_dim coordinates (coordT) */
+ setT *neighbors; /* neighboring facets of vertex, qh_vertexneighbors()
+ inits in io.c or after first merge */
+ unsigned id; /* unique identifier. Same size as qh.vertex_id and qh.ridge_id */
+ unsigned visitid; /* for use with qh.vertex_visit, size must match */
+ flagT seen:1; /* used to perform operations only once */
+ flagT seen2:1; /* another seen flag */
+ flagT delridge:1; /* vertex was part of a deleted ridge */
+ flagT deleted:1; /* true if vertex on qh.del_vertices */
+ flagT newlist:1; /* true if vertex on qh.newvertex_list */
+};
+
+/*======= -global variables -qh ============================*/
+
+/*-<a href="qh-globa.htm#TOC"
+ >--------------------------------</a><a name="qh">-</a>
+
+ qh
+ all global variables for qhull are in qh, qhmem, and qhstat
+
+ notes:
+ qhmem is defined in mem.h, qhstat is defined in stat.h, qhrbox is defined in rboxpoints.h
+ Access to qh_qh is via the "qh" macro. See qh_QHpointer in user.h
+
+ All global variables for qhull are in qh, qhmem, and qhstat
+ qh must be unique for each instance of qhull
+ qhstat may be shared between qhull instances.
+ qhmem may be shared across multiple instances of Qhull.
+ Rbox uses global variables rbox_inuse and rbox, but does not persist data across calls.
+
+ Qhull is not multi-threaded. Global state could be stored in thread-local storage.
+
+ QHULL_LIB_CHECK checks that a program and the corresponding
+ qhull library were built with the same type of header files.
+*/
+
+typedef struct qhT qhT;
+
+#define QHULL_NON_REENTRANT 0
+#define QHULL_QH_POINTER 1
+#define QHULL_REENTRANT 2
+
+#if qh_QHpointer_dllimport
+#define qh qh_qh->
+__declspec(dllimport) extern qhT *qh_qh; /* allocated in global.c */
+#define QHULL_LIB_TYPE QHULL_QH_POINTER
+
+#elif qh_QHpointer
+#define qh qh_qh->
+extern qhT *qh_qh; /* allocated in global.c */
+#define QHULL_LIB_TYPE QHULL_QH_POINTER
+
+#elif qh_dllimport
+#define qh qh_qh.
+__declspec(dllimport) extern qhT qh_qh; /* allocated in global.c */
+#define QHULL_LIB_TYPE QHULL_NON_REENTRANT
+
+#else
+#define qh qh_qh.
+extern qhT qh_qh;
+#define QHULL_LIB_TYPE QHULL_NON_REENTRANT
+#endif
+
+#define QHULL_LIB_CHECK qh_lib_check(QHULL_LIB_TYPE, sizeof(qhT), sizeof(vertexT), sizeof(ridgeT), sizeof(facetT), sizeof(setT), sizeof(qhmemT));
+#define QHULL_LIB_CHECK_RBOX qh_lib_check(QHULL_LIB_TYPE, sizeof(qhT), sizeof(vertexT), sizeof(ridgeT), sizeof(facetT), 0, 0);
+
+struct qhT {
+
+/*-<a href="qh-globa.htm#TOC"
+ >--------------------------------</a><a name="qh-const">-</a>
+
+ qh constants
+ configuration flags and constants for Qhull
+
+ notes:
+ The user configures Qhull by defining flags. They are
+ copied into qh by qh_setflags(). qh-quick.htm#options defines the flags.
+*/
+ boolT ALLpoints; /* true 'Qs' if search all points for initial simplex */
+ boolT ANGLEmerge; /* true 'Qa' if sort potential merges by angle */
+ boolT APPROXhull; /* true 'Wn' if MINoutside set */
+ realT MINoutside; /* 'Wn' min. distance for an outside point */
+ boolT ANNOTATEoutput; /* true 'Ta' if annotate output with message codes */
+ boolT ATinfinity; /* true 'Qz' if point num_points-1 is "at-infinity"
+ for improving precision in Delaunay triangulations */
+ boolT AVOIDold; /* true 'Q4' if avoid old->new merges */
+ boolT BESToutside; /* true 'Qf' if partition points into best outsideset */
+ boolT CDDinput; /* true 'Pc' if input uses CDD format (1.0/offset first) */
+ boolT CDDoutput; /* true 'PC' if print normals in CDD format (offset first) */
+ boolT CHECKfrequently; /* true 'Tc' if checking frequently */
+ realT premerge_cos; /* 'A-n' cos_max when pre merging */
+ realT postmerge_cos; /* 'An' cos_max when post merging */
+ boolT DELAUNAY; /* true 'd' if computing DELAUNAY triangulation */
+ boolT DOintersections; /* true 'Gh' if print hyperplane intersections */
+ int DROPdim; /* drops dim 'GDn' for 4-d -> 3-d output */
+ boolT FORCEoutput; /* true 'Po' if forcing output despite degeneracies */
+ int GOODpoint; /* 1+n for 'QGn', good facet if visible/not(-) from point n*/
+ pointT *GOODpointp; /* the actual point */
+ boolT GOODthreshold; /* true if qh.lower_threshold/upper_threshold defined
+ false if qh.SPLITthreshold */
+ int GOODvertex; /* 1+n, good facet if vertex for point n */
+ pointT *GOODvertexp; /* the actual point */
+ boolT HALFspace; /* true 'Hn,n,n' if halfspace intersection */
+ boolT ISqhullQh; /* Set by Qhull.cpp on initialization */
+ int IStracing; /* trace execution, 0=none, 1=least, 4=most, -1=events */
+ int KEEParea; /* 'PAn' number of largest facets to keep */
+ boolT KEEPcoplanar; /* true 'Qc' if keeping nearest facet for coplanar points */
+ boolT KEEPinside; /* true 'Qi' if keeping nearest facet for inside points
+ set automatically if 'd Qc' */
+ int KEEPmerge; /* 'PMn' number of facets to keep with most merges */
+ realT KEEPminArea; /* 'PFn' minimum facet area to keep */
+ realT MAXcoplanar; /* 'Un' max distance below a facet to be coplanar*/
+ boolT MERGEexact; /* true 'Qx' if exact merges (coplanar, degen, dupridge, flipped) */
+ boolT MERGEindependent; /* true 'Q2' if merging independent sets */
+ boolT MERGING; /* true if exact-, pre- or post-merging, with angle and centrum tests */
+ realT premerge_centrum; /* 'C-n' centrum_radius when pre merging. Default is round-off */
+ realT postmerge_centrum; /* 'Cn' centrum_radius when post merging. Default is round-off */
+ boolT MERGEvertices; /* true 'Q3' if merging redundant vertices */
+ realT MINvisible; /* 'Vn' min. distance for a facet to be visible */
+ boolT NOnarrow; /* true 'Q10' if no special processing for narrow distributions */
+ boolT NOnearinside; /* true 'Q8' if ignore near-inside points when partitioning */
+ boolT NOpremerge; /* true 'Q0' if no defaults for C-0 or Qx */
+ boolT NOwide; /* true 'Q12' if no error on wide merge due to duplicate ridge */
+ boolT ONLYgood; /* true 'Qg' if process points with good visible or horizon facets */
+ boolT ONLYmax; /* true 'Qm' if only process points that increase max_outside */
+ boolT PICKfurthest; /* true 'Q9' if process furthest of furthest points*/
+ boolT POSTmerge; /* true if merging after buildhull (Cn or An) */
+ boolT PREmerge; /* true if merging during buildhull (C-n or A-n) */
+ /* NOTE: some of these names are similar to qh_PRINT names */
+ boolT PRINTcentrums; /* true 'Gc' if printing centrums */
+ boolT PRINTcoplanar; /* true 'Gp' if printing coplanar points */
+ int PRINTdim; /* print dimension for Geomview output */
+ boolT PRINTdots; /* true 'Ga' if printing all points as dots */
+ boolT PRINTgood; /* true 'Pg' if printing good facets */
+ boolT PRINTinner; /* true 'Gi' if printing inner planes */
+ boolT PRINTneighbors; /* true 'PG' if printing neighbors of good facets */
+ boolT PRINTnoplanes; /* true 'Gn' if printing no planes */
+ boolT PRINToptions1st; /* true 'FO' if printing options to stderr */
+ boolT PRINTouter; /* true 'Go' if printing outer planes */
+ boolT PRINTprecision; /* false 'Pp' if not reporting precision problems */
+ qh_PRINT PRINTout[qh_PRINTEND]; /* list of output formats to print */
+ boolT PRINTridges; /* true 'Gr' if print ridges */
+ boolT PRINTspheres; /* true 'Gv' if print vertices as spheres */
+ boolT PRINTstatistics; /* true 'Ts' if printing statistics to stderr */
+ boolT PRINTsummary; /* true 's' if printing summary to stderr */
+ boolT PRINTtransparent; /* true 'Gt' if print transparent outer ridges */
+ boolT PROJECTdelaunay; /* true if DELAUNAY, no readpoints() and
+ need projectinput() for Delaunay in qh_init_B */
+ int PROJECTinput; /* number of projected dimensions 'bn:0Bn:0' */
+ boolT QUICKhelp; /* true if quick help message for degen input */
+ boolT RANDOMdist; /* true if randomly change distplane and setfacetplane */
+ realT RANDOMfactor; /* maximum random perturbation */
+ realT RANDOMa; /* qh_randomfactor is randr * RANDOMa + RANDOMb */
+ realT RANDOMb;
+ boolT RANDOMoutside; /* true if select a random outside point */
+ int REPORTfreq; /* buildtracing reports every n facets */
+ int REPORTfreq2; /* tracemerging reports every REPORTfreq/2 facets */
+ int RERUN; /* 'TRn' rerun qhull n times (qh.build_cnt) */
+ int ROTATErandom; /* 'QRn' seed, 0 time, >= rotate input */
+ boolT SCALEinput; /* true 'Qbk' if scaling input */
+ boolT SCALElast; /* true 'Qbb' if scale last coord to max prev coord */
+ boolT SETroundoff; /* true 'E' if qh.DISTround is predefined */
+ boolT SKIPcheckmax; /* true 'Q5' if skip qh_check_maxout */
+ boolT SKIPconvex; /* true 'Q6' if skip convexity testing during pre-merge */
+ boolT SPLITthresholds; /* true if upper_/lower_threshold defines a region
+ used only for printing (!for qh.ONLYgood) */
+ int STOPcone; /* 'TCn' 1+n for stopping after cone for point n */
+ /* also used by qh_build_withresart for err exit*/
+ int STOPpoint; /* 'TVn' 'TV-n' 1+n for stopping after/before(-)
+ adding point n */
+ int TESTpoints; /* 'QTn' num of test points after qh.num_points. Test points always coplanar. */
+ boolT TESTvneighbors; /* true 'Qv' if test vertex neighbors at end */
+ int TRACElevel; /* 'Tn' conditional IStracing level */
+ int TRACElastrun; /* qh.TRACElevel applies to last qh.RERUN */
+ int TRACEpoint; /* 'TPn' start tracing when point n is a vertex */
+ realT TRACEdist; /* 'TWn' start tracing when merge distance too big */
+ int TRACEmerge; /* 'TMn' start tracing before this merge */
+ boolT TRIangulate; /* true 'Qt' if triangulate non-simplicial facets */
+ boolT TRInormals; /* true 'Q11' if triangulate duplicates ->normal and ->center (sets Qt) */
+ boolT UPPERdelaunay; /* true 'Qu' if computing furthest-site Delaunay */
+ boolT USEstdout; /* true 'Tz' if using stdout instead of stderr */
+ boolT VERIFYoutput; /* true 'Tv' if verify output at end of qhull */
+ boolT VIRTUALmemory; /* true 'Q7' if depth-first processing in buildhull */
+ boolT VORONOI; /* true 'v' if computing Voronoi diagram */
+
+ /*--------input constants ---------*/
+ realT AREAfactor; /* 1/(hull_dim-1)! for converting det's to area */
+ boolT DOcheckmax; /* true if calling qh_check_maxout (qh_initqhull_globals) */
+ char *feasible_string; /* feasible point 'Hn,n,n' for halfspace intersection */
+ coordT *feasible_point; /* as coordinates, both malloc'd */
+ boolT GETarea; /* true 'Fa', 'FA', 'FS', 'PAn', 'PFn' if compute facet area/Voronoi volume in io.c */
+ boolT KEEPnearinside; /* true if near-inside points in coplanarset */
+ int hull_dim; /* dimension of hull, set by initbuffers */
+ int input_dim; /* dimension of input, set by initbuffers */
+ int num_points; /* number of input points */
+ pointT *first_point; /* array of input points, see POINTSmalloc */
+ boolT POINTSmalloc; /* true if qh.first_point/num_points allocated */
+ pointT *input_points; /* copy of original qh.first_point for input points for qh_joggleinput */
+ boolT input_malloc; /* true if qh.input_points malloc'd */
+ char qhull_command[256];/* command line that invoked this program */
+ int qhull_commandsiz2; /* size of qhull_command at qh_clear_outputflags */
+ char rbox_command[256]; /* command line that produced the input points */
+ char qhull_options[512];/* descriptive list of options */
+ int qhull_optionlen; /* length of last line */
+ int qhull_optionsiz; /* size of qhull_options at qh_build_withrestart */
+ int qhull_optionsiz2; /* size of qhull_options at qh_clear_outputflags */
+ int run_id; /* non-zero, random identifier for this instance of qhull */
+ boolT VERTEXneighbors; /* true if maintaining vertex neighbors */
+ boolT ZEROcentrum; /* true if 'C-0' or 'C-0 Qx'. sets ZEROall_ok */
+ realT *upper_threshold; /* don't print if facet->normal[k]>=upper_threshold[k]
+ must set either GOODthreshold or SPLITthreshold
+ if Delaunay, default is 0.0 for upper envelope */
+ realT *lower_threshold; /* don't print if facet->normal[k] <=lower_threshold[k] */
+ realT *upper_bound; /* scale point[k] to new upper bound */
+ realT *lower_bound; /* scale point[k] to new lower bound
+ project if both upper_ and lower_bound == 0 */
+
+/*-<a href="qh-globa.htm#TOC"
+ >--------------------------------</a><a name="qh-prec">-</a>
+
+ qh precision constants
+ precision constants for Qhull
+
+ notes:
+ qh_detroundoff() computes the maximum roundoff error for distance
+ and other computations. It also sets default values for the
+ qh constants above.
+*/
+ realT ANGLEround; /* max round off error for angles */
+ realT centrum_radius; /* max centrum radius for convexity (roundoff added) */
+ realT cos_max; /* max cosine for convexity (roundoff added) */
+ realT DISTround; /* max round off error for distances, 'E' overrides qh_distround() */
+ realT MAXabs_coord; /* max absolute coordinate */
+ realT MAXlastcoord; /* max last coordinate for qh_scalelast */
+ realT MAXsumcoord; /* max sum of coordinates */
+ realT MAXwidth; /* max rectilinear width of point coordinates */
+ realT MINdenom_1; /* min. abs. value for 1/x */
+ realT MINdenom; /* use divzero if denominator < MINdenom */
+ realT MINdenom_1_2; /* min. abs. val for 1/x that allows normalization */
+ realT MINdenom_2; /* use divzero if denominator < MINdenom_2 */
+ realT MINlastcoord; /* min. last coordinate for qh_scalelast */
+ boolT NARROWhull; /* set in qh_initialhull if angle < qh_MAXnarrow */
+ realT *NEARzero; /* hull_dim array for near zero in gausselim */
+ realT NEARinside; /* keep points for qh_check_maxout if close to facet */
+ realT ONEmerge; /* max distance for merging simplicial facets */
+ realT outside_err; /* application's epsilon for coplanar points
+ qh_check_bestdist() qh_check_points() reports error if point outside */
+ realT WIDEfacet; /* size of wide facet for skipping ridge in
+ area computation and locking centrum */
+
+/*-<a href="qh-globa.htm#TOC"
+ >--------------------------------</a><a name="qh-codetern">-</a>
+
+ qh internal constants
+ internal constants for Qhull
+*/
+ char qhull[sizeof("qhull")]; /* "qhull" for checking ownership while debugging */
+ jmp_buf errexit; /* exit label for qh_errexit, defined by setjmp() and NOerrexit */
+ char jmpXtra[40]; /* extra bytes in case jmp_buf is defined wrong by compiler */
+ jmp_buf restartexit; /* restart label for qh_errexit, defined by setjmp() and ALLOWrestart */
+ char jmpXtra2[40]; /* extra bytes in case jmp_buf is defined wrong by compiler*/
+ FILE *fin; /* pointer to input file, init by qh_initqhull_start2 */
+ FILE *fout; /* pointer to output file */
+ FILE *ferr; /* pointer to error file */
+ pointT *interior_point; /* center point of the initial simplex*/
+ int normal_size; /* size in bytes for facet normals and point coords*/
+ int center_size; /* size in bytes for Voronoi centers */
+ int TEMPsize; /* size for small, temporary sets (in quick mem) */
+
+/*-<a href="qh-globa.htm#TOC"
+ >--------------------------------</a><a name="qh-lists">-</a>
+
+ qh facet and vertex lists
+ defines lists of facets, new facets, visible facets, vertices, and
+ new vertices. Includes counts, next ids, and trace ids.
+ see:
+ qh_resetlists()
+*/
+ facetT *facet_list; /* first facet */
+ facetT *facet_tail; /* end of facet_list (dummy facet) */
+ facetT *facet_next; /* next facet for buildhull()
+ previous facets do not have outside sets
+ NARROWhull: previous facets may have coplanar outside sets for qh_outcoplanar */
+ facetT *newfacet_list; /* list of new facets to end of facet_list */
+ facetT *visible_list; /* list of visible facets preceding newfacet_list,
+ facet->visible set */
+ int num_visible; /* current number of visible facets */
+ unsigned tracefacet_id; /* set at init, then can print whenever */
+ facetT *tracefacet; /* set in newfacet/mergefacet, undone in delfacet*/
+ unsigned tracevertex_id; /* set at buildtracing, can print whenever */
+ vertexT *tracevertex; /* set in newvertex, undone in delvertex*/
+ vertexT *vertex_list; /* list of all vertices, to vertex_tail */
+ vertexT *vertex_tail; /* end of vertex_list (dummy vertex) */
+ vertexT *newvertex_list; /* list of vertices in newfacet_list, to vertex_tail
+ all vertices have 'newlist' set */
+ int num_facets; /* number of facets in facet_list
+ includes visible faces (num_visible) */
+ int num_vertices; /* number of vertices in facet_list */
+ int num_outside; /* number of points in outsidesets (for tracing and RANDOMoutside)
+ includes coplanar outsideset points for NARROWhull/qh_outcoplanar() */
+ int num_good; /* number of good facets (after findgood_all) */
+ unsigned facet_id; /* ID of next, new facet from newfacet() */
+ unsigned ridge_id; /* ID of next, new ridge from newridge() */
+ unsigned vertex_id; /* ID of next, new vertex from newvertex() */
+
+/*-<a href="qh-globa.htm#TOC"
+ >--------------------------------</a><a name="qh-var">-</a>
+
+ qh global variables
+ defines minimum and maximum distances, next visit ids, several flags,
+ and other global variables.
+ initialize in qh_initbuild or qh_maxmin if used in qh_buildhull
+*/
+ unsigned long hulltime; /* ignore time to set up input and randomize */
+ /* use unsigned to avoid wrap-around errors */
+ boolT ALLOWrestart; /* true if qh_precision can use qh.restartexit */
+ int build_cnt; /* number of calls to qh_initbuild */
+ qh_CENTER CENTERtype; /* current type of facet->center, qh_CENTER */
+ int furthest_id; /* pointid of furthest point, for tracing */
+ facetT *GOODclosest; /* closest facet to GOODthreshold in qh_findgood */
+ boolT hasAreaVolume; /* true if totarea, totvol was defined by qh_getarea */
+ boolT hasTriangulation; /* true if triangulation created by qh_triangulate */
+ realT JOGGLEmax; /* set 'QJn' if randomly joggle input */
+ boolT maxoutdone; /* set qh_check_maxout(), cleared by qh_addpoint() */
+ realT max_outside; /* maximum distance from a point to a facet,
+ before roundoff, not simplicial vertices
+ actual outer plane is +DISTround and
+ computed outer plane is +2*DISTround */
+ realT max_vertex; /* maximum distance (>0) from vertex to a facet,
+ before roundoff, due to a merge */
+ realT min_vertex; /* minimum distance (<0) from vertex to a facet,
+ before roundoff, due to a merge
+ if qh.JOGGLEmax, qh_makenewplanes sets it
+ recomputed if qh.DOcheckmax, default -qh.DISTround */
+ boolT NEWfacets; /* true while visible facets invalid due to new or merge
+ from makecone/attachnewfacets to deletevisible */
+ boolT findbestnew; /* true if partitioning calls qh_findbestnew */
+ boolT findbest_notsharp; /* true if new facets are at least 90 degrees */
+ boolT NOerrexit; /* true if qh.errexit is not available, cleared after setjmp */
+ realT PRINTcradius; /* radius for printing centrums */
+ realT PRINTradius; /* radius for printing vertex spheres and points */
+ boolT POSTmerging; /* true when post merging */
+ int printoutvar; /* temporary variable for qh_printbegin, etc. */
+ int printoutnum; /* number of facets printed */
+ boolT QHULLfinished; /* True after qhull() is finished */
+ realT totarea; /* 'FA': total facet area computed by qh_getarea, hasAreaVolume */
+ realT totvol; /* 'FA': total volume computed by qh_getarea, hasAreaVolume */
+ unsigned int visit_id; /* unique ID for searching neighborhoods, */
+ unsigned int vertex_visit; /* unique ID for searching vertices, reset with qh_buildtracing */
+ boolT ZEROall_ok; /* True if qh_checkzero always succeeds */
+ boolT WAScoplanar; /* True if qh_partitioncoplanar (qh_check_maxout) */
+
+/*-<a href="qh-globa.htm#TOC"
+ >--------------------------------</a><a name="qh-set">-</a>
+
+ qh global sets
+ defines sets for merging, initial simplex, hashing, extra input points,
+ and deleted vertices
+*/
+ setT *facet_mergeset; /* temporary set of merges to be done */
+ setT *degen_mergeset; /* temporary set of degenerate and redundant merges */
+ setT *hash_table; /* hash table for matching ridges in qh_matchfacets
+ size is setsize() */
+ setT *other_points; /* additional points */
+ setT *del_vertices; /* vertices to partition and delete with visible
+ facets. Have deleted set for checkfacet */
+
+/*-<a href="qh-globa.htm#TOC"
+ >--------------------------------</a><a name="qh-buf">-</a>
+
+ qh global buffers
+ defines buffers for maxtrix operations, input, and error messages
+*/
+ coordT *gm_matrix; /* (dim+1)Xdim matrix for geom.c */
+ coordT **gm_row; /* array of gm_matrix rows */
+ char* line; /* malloc'd input line of maxline+1 chars */
+ int maxline;
+ coordT *half_space; /* malloc'd input array for halfspace (qh normal_size+coordT) */
+ coordT *temp_malloc; /* malloc'd input array for points */
+
+/*-<a href="qh-globa.htm#TOC"
+ >--------------------------------</a><a name="qh-static">-</a>
+
+ qh static variables
+ defines static variables for individual functions
+
+ notes:
+ do not use 'static' within a function. Multiple instances of qhull
+ may exist.
+
+ do not assume zero initialization, 'QPn' may cause a restart
+*/
+ boolT ERREXITcalled; /* true during qh_errexit (prevents duplicate calls */
+ boolT firstcentrum; /* for qh_printcentrum */
+ boolT old_randomdist; /* save RANDOMdist flag during io, tracing, or statistics */
+ setT *coplanarfacetset; /* set of coplanar facets for searching qh_findbesthorizon() */
+ realT last_low; /* qh_scalelast parameters for qh_setdelaunay */
+ realT last_high;
+ realT last_newhigh;
+ unsigned lastreport; /* for qh_buildtracing */
+ int mergereport; /* for qh_tracemerging */
+ qhstatT *old_qhstat; /* for saving qh_qhstat in save_qhull() and UsingLibQhull. Free with qh_free() */
+ setT *old_tempstack; /* for saving qhmem.tempstack in save_qhull */
+ int ridgeoutnum; /* number of ridges for 4OFF output (qh_printbegin,etc) */
+};
+
+/*=========== -macros- =========================*/
+
+/*-<a href="qh-poly.htm#TOC"
+ >--------------------------------</a><a name="otherfacet_">-</a>
+
+ otherfacet_(ridge, facet)
+ return neighboring facet for a ridge in facet
+*/
+#define otherfacet_(ridge, facet) \
+ (((ridge)->top == (facet)) ? (ridge)->bottom : (ridge)->top)
+
+/*-<a href="qh-poly.htm#TOC"
+ >--------------------------------</a><a name="getid_">-</a>
+
+ getid_(p)
+ return int ID for facet, ridge, or vertex
+ return qh_IDunknown(-1) if NULL
+*/
+#define getid_(p) ((p) ? (int)((p)->id) : qh_IDunknown)
+
+/*============== FORALL macros ===================*/
+
+/*-<a href="qh-poly.htm#TOC"
+ >--------------------------------</a><a name="FORALLfacets">-</a>
+
+ FORALLfacets { ... }
+ assign 'facet' to each facet in qh.facet_list
+
+ notes:
+ uses 'facetT *facet;'
+ assumes last facet is a sentinel
+
+ see:
+ FORALLfacet_( facetlist )
+*/
+#define FORALLfacets for (facet=qh facet_list;facet && facet->next;facet=facet->next)
+
+/*-<a href="qh-poly.htm#TOC"
+ >--------------------------------</a><a name="FORALLpoints">-</a>
+
+ FORALLpoints { ... }
+ assign 'point' to each point in qh.first_point, qh.num_points
+
+ declare:
+ coordT *point, *pointtemp;
+*/
+#define FORALLpoints FORALLpoint_(qh first_point, qh num_points)
+
+/*-<a href="qh-poly.htm#TOC"
+ >--------------------------------</a><a name="FORALLpoint_">-</a>
+
+ FORALLpoint_( points, num) { ... }
+ assign 'point' to each point in points array of num points
+
+ declare:
+ coordT *point, *pointtemp;
+*/
+#define FORALLpoint_(points, num) for (point= (points), \
+ pointtemp= (points)+qh hull_dim*(num); point < pointtemp; point += qh hull_dim)
+
+/*-<a href="qh-poly.htm#TOC"
+ >--------------------------------</a><a name="FORALLvertices">-</a>
+
+ FORALLvertices { ... }
+ assign 'vertex' to each vertex in qh.vertex_list
+
+ declare:
+ vertexT *vertex;
+
+ notes:
+ assumes qh.vertex_list terminated with a sentinel
+*/
+#define FORALLvertices for (vertex=qh vertex_list;vertex && vertex->next;vertex= vertex->next)
+
+/*-<a href="qh-poly.htm#TOC"
+ >--------------------------------</a><a name="FOREACHfacet_">-</a>
+
+ FOREACHfacet_( facets ) { ... }
+ assign 'facet' to each facet in facets
+
+ declare:
+ facetT *facet, **facetp;
+
+ see:
+ <a href="qset.h#FOREACHsetelement_">FOREACHsetelement_</a>
+*/
+#define FOREACHfacet_(facets) FOREACHsetelement_(facetT, facets, facet)
+
+/*-<a href="qh-poly.htm#TOC"
+ >--------------------------------</a><a name="FOREACHneighbor_">-</a>
+
+ FOREACHneighbor_( facet ) { ... }
+ assign 'neighbor' to each neighbor in facet->neighbors
+
+ FOREACHneighbor_( vertex ) { ... }
+ assign 'neighbor' to each neighbor in vertex->neighbors
+
+ declare:
+ facetT *neighbor, **neighborp;
+
+ see:
+ <a href="qset.h#FOREACHsetelement_">FOREACHsetelement_</a>
+*/
+#define FOREACHneighbor_(facet) FOREACHsetelement_(facetT, facet->neighbors, neighbor)
+
+/*-<a href="qh-poly.htm#TOC"
+ >--------------------------------</a><a name="FOREACHpoint_">-</a>
+
+ FOREACHpoint_( points ) { ... }
+ assign 'point' to each point in points set
+
+ declare:
+ pointT *point, **pointp;
+
+ see:
+ <a href="qset.h#FOREACHsetelement_">FOREACHsetelement_</a>
+*/
+#define FOREACHpoint_(points) FOREACHsetelement_(pointT, points, point)
+
+/*-<a href="qh-poly.htm#TOC"
+ >--------------------------------</a><a name="FOREACHridge_">-</a>
+
+ FOREACHridge_( ridges ) { ... }
+ assign 'ridge' to each ridge in ridges set
+
+ declare:
+ ridgeT *ridge, **ridgep;
+
+ see:
+ <a href="qset.h#FOREACHsetelement_">FOREACHsetelement_</a>
+*/
+#define FOREACHridge_(ridges) FOREACHsetelement_(ridgeT, ridges, ridge)
+
+/*-<a href="qh-poly.htm#TOC"
+ >--------------------------------</a><a name="FOREACHvertex_">-</a>
+
+ FOREACHvertex_( vertices ) { ... }
+ assign 'vertex' to each vertex in vertices set
+
+ declare:
+ vertexT *vertex, **vertexp;
+
+ see:
+ <a href="qset.h#FOREACHsetelement_">FOREACHsetelement_</a>
+*/
+#define FOREACHvertex_(vertices) FOREACHsetelement_(vertexT, vertices,vertex)
+
+/*-<a href="qh-poly.htm#TOC"
+ >--------------------------------</a><a name="FOREACHfacet_i_">-</a>
+
+ FOREACHfacet_i_( facets ) { ... }
+ assign 'facet' and 'facet_i' for each facet in facets set
+
+ declare:
+ facetT *facet;
+ int facet_n, facet_i;
+
+ see:
+ <a href="qset.h#FOREACHsetelement_i_">FOREACHsetelement_i_</a>
+*/
+#define FOREACHfacet_i_(facets) FOREACHsetelement_i_(facetT, facets, facet)
+
+/*-<a href="qh-poly.htm#TOC"
+ >--------------------------------</a><a name="FOREACHneighbor_i_">-</a>
+
+ FOREACHneighbor_i_( facet ) { ... }
+ assign 'neighbor' and 'neighbor_i' for each neighbor in facet->neighbors
+
+ FOREACHneighbor_i_( vertex ) { ... }
+ assign 'neighbor' and 'neighbor_i' for each neighbor in vertex->neighbors
+
+ declare:
+ facetT *neighbor;
+ int neighbor_n, neighbor_i;
+
+ see:
+ <a href="qset.h#FOREACHsetelement_i_">FOREACHsetelement_i_</a>
+*/
+#define FOREACHneighbor_i_(facet) FOREACHsetelement_i_(facetT, facet->neighbors, neighbor)
+
+/*-<a href="qh-poly.htm#TOC"
+ >--------------------------------</a><a name="FOREACHpoint_i_">-</a>
+
+ FOREACHpoint_i_( points ) { ... }
+ assign 'point' and 'point_i' for each point in points set
+
+ declare:
+ pointT *point;
+ int point_n, point_i;
+
+ see:
+ <a href="qset.h#FOREACHsetelement_i_">FOREACHsetelement_i_</a>
+*/
+#define FOREACHpoint_i_(points) FOREACHsetelement_i_(pointT, points, point)
+
+/*-<a href="qh-poly.htm#TOC"
+ >--------------------------------</a><a name="FOREACHridge_i_">-</a>
+
+ FOREACHridge_i_( ridges ) { ... }
+ assign 'ridge' and 'ridge_i' for each ridge in ridges set
+
+ declare:
+ ridgeT *ridge;
+ int ridge_n, ridge_i;
+
+ see:
+ <a href="qset.h#FOREACHsetelement_i_">FOREACHsetelement_i_</a>
+*/
+#define FOREACHridge_i_(ridges) FOREACHsetelement_i_(ridgeT, ridges, ridge)
+
+/*-<a href="qh-poly.htm#TOC"
+ >--------------------------------</a><a name="FOREACHvertex_i_">-</a>
+
+ FOREACHvertex_i_( vertices ) { ... }
+ assign 'vertex' and 'vertex_i' for each vertex in vertices set
+
+ declare:
+ vertexT *vertex;
+ int vertex_n, vertex_i;
+
+ see:
+ <a href="qset.h#FOREACHsetelement_i_">FOREACHsetelement_i_</a>
+*/
+#define FOREACHvertex_i_(vertices) FOREACHsetelement_i_(vertexT, vertices,vertex)
+
+/********* -libqhull.c prototypes (duplicated from qhull_a.h) **********************/
+
+void qh_qhull(void);
+boolT qh_addpoint(pointT *furthest, facetT *facet, boolT checkdist);
+void qh_printsummary(FILE *fp);
+
+/********* -user.c prototypes (alphabetical) **********************/
+
+void qh_errexit(int exitcode, facetT *facet, ridgeT *ridge);
+void qh_errprint(const char* string, facetT *atfacet, facetT *otherfacet, ridgeT *atridge, vertexT *atvertex);
+int qh_new_qhull(int dim, int numpoints, coordT *points, boolT ismalloc,
+ char *qhull_cmd, FILE *outfile, FILE *errfile);
+void qh_printfacetlist(facetT *facetlist, setT *facets, boolT printall);
+void qh_printhelp_degenerate(FILE *fp);
+void qh_printhelp_narrowhull(FILE *fp, realT minangle);
+void qh_printhelp_singular(FILE *fp);
+void qh_user_memsizes(void);
+
+/********* -usermem.c prototypes (alphabetical) **********************/
+void qh_exit(int exitcode);
+void qh_fprintf_stderr(int msgcode, const char *fmt, ... );
+void qh_free(void *mem);
+void *qh_malloc(size_t size);
+
+/********* -userprintf.c and userprintf_rbox.c prototypes **********************/
+void qh_fprintf(FILE *fp, int msgcode, const char *fmt, ... );
+void qh_fprintf_rbox(FILE *fp, int msgcode, const char *fmt, ... );
+
+/***** -geom.c/geom2.c/random.c prototypes (duplicated from geom.h, random.h) ****************/
+
+facetT *qh_findbest(pointT *point, facetT *startfacet,
+ boolT bestoutside, boolT newfacets, boolT noupper,
+ realT *dist, boolT *isoutside, int *numpart);
+facetT *qh_findbestnew(pointT *point, facetT *startfacet,
+ realT *dist, boolT bestoutside, boolT *isoutside, int *numpart);
+boolT qh_gram_schmidt(int dim, realT **rows);
+void qh_outerinner(facetT *facet, realT *outerplane, realT *innerplane);
+void qh_printsummary(FILE *fp);
+void qh_projectinput(void);
+void qh_randommatrix(realT *buffer, int dim, realT **row);
+void qh_rotateinput(realT **rows);
+void qh_scaleinput(void);
+void qh_setdelaunay(int dim, int count, pointT *points);
+coordT *qh_sethalfspace_all(int dim, int count, coordT *halfspaces, pointT *feasible);
+
+/***** -global.c prototypes (alphabetical) ***********************/
+
+unsigned long qh_clock(void);
+void qh_checkflags(char *command, char *hiddenflags);
+void qh_clear_outputflags(void);
+void qh_freebuffers(void);
+void qh_freeqhull(boolT allmem);
+void qh_freeqhull2(boolT allmem);
+void qh_init_A(FILE *infile, FILE *outfile, FILE *errfile, int argc, char *argv[]);
+void qh_init_B(coordT *points, int numpoints, int dim, boolT ismalloc);
+void qh_init_qhull_command(int argc, char *argv[]);
+void qh_initbuffers(coordT *points, int numpoints, int dim, boolT ismalloc);
+void qh_initflags(char *command);
+void qh_initqhull_buffers(void);
+void qh_initqhull_globals(coordT *points, int numpoints, int dim, boolT ismalloc);
+void qh_initqhull_mem(void);
+void qh_initqhull_outputflags(void);
+void qh_initqhull_start(FILE *infile, FILE *outfile, FILE *errfile);
+void qh_initqhull_start2(FILE *infile, FILE *outfile, FILE *errfile);
+void qh_initthresholds(char *command);
+void qh_lib_check(int qhullLibraryType, int qhTsize, int vertexTsize, int ridgeTsize, int facetTsize, int setTsize, int qhmemTsize);
+void qh_option(const char *option, int *i, realT *r);
+#if qh_QHpointer
+void qh_restore_qhull(qhT **oldqh);
+qhT *qh_save_qhull(void);
+#endif
+
+/***** -io.c prototypes (duplicated from io.h) ***********************/
+
+void qh_dfacet(unsigned id);
+void qh_dvertex(unsigned id);
+void qh_printneighborhood(FILE *fp, qh_PRINT format, facetT *facetA, facetT *facetB, boolT printall);
+void qh_produce_output(void);
+coordT *qh_readpoints(int *numpoints, int *dimension, boolT *ismalloc);
+
+
+/********* -mem.c prototypes (duplicated from mem.h) **********************/
+
+void qh_meminit(FILE *ferr);
+void qh_memfreeshort(int *curlong, int *totlong);
+
+/********* -poly.c/poly2.c prototypes (duplicated from poly.h) **********************/
+
+void qh_check_output(void);
+void qh_check_points(void);
+setT *qh_facetvertices(facetT *facetlist, setT *facets, boolT allfacets);
+facetT *qh_findbestfacet(pointT *point, boolT bestoutside,
+ realT *bestdist, boolT *isoutside);
+vertexT *qh_nearvertex(facetT *facet, pointT *point, realT *bestdistp);
+pointT *qh_point(int id);
+setT *qh_pointfacet(void /*qh.facet_list*/);
+int qh_pointid(pointT *point);
+setT *qh_pointvertex(void /*qh.facet_list*/);
+void qh_setvoronoi_all(void);
+void qh_triangulate(void /*qh.facet_list*/);
+
+/********* -rboxlib.c prototypes **********************/
+int qh_rboxpoints(FILE* fout, FILE* ferr, char* rbox_command);
+void qh_errexit_rbox(int exitcode);
+
+/********* -stat.c prototypes (duplicated from stat.h) **********************/
+
+void qh_collectstatistics(void);
+void qh_printallstatistics(FILE *fp, const char *string);
+
+#endif /* qhDEFlibqhull */
diff --git a/xs/src/qhull/src/libqhull/libqhull.pro b/xs/src/qhull/src/libqhull/libqhull.pro
new file mode 100644
index 000000000..18005da59
--- /dev/null
+++ b/xs/src/qhull/src/libqhull/libqhull.pro
@@ -0,0 +1,67 @@
+# -------------------------------------------------
+# libqhull.pro -- Qt project for Qhull shared library
+# -------------------------------------------------
+
+include(../qhull-warn.pri)
+
+DESTDIR = ../../lib
+DLLDESTDIR = ../../bin
+TEMPLATE = lib
+CONFIG += shared warn_on
+CONFIG -= qt
+
+build_pass:CONFIG(debug, debug|release):{
+ TARGET = qhull_d
+ OBJECTS_DIR = Debug
+}else:build_pass:CONFIG(release, debug|release):{
+ TARGET = qhull
+ OBJECTS_DIR = Release
+}
+win32-msvc* : QMAKE_LFLAGS += /INCREMENTAL:NO
+
+win32-msvc* : DEF_FILE += ../../src/libqhull/qhull-exports.def
+
+# Order object files by frequency of execution. Small files at end.
+
+# libqhull/libqhull.pro and ../qhull-libqhull-src.pri have the same SOURCES and HEADERS
+SOURCES += ../libqhull/global.c
+SOURCES += ../libqhull/stat.c
+SOURCES += ../libqhull/geom2.c
+SOURCES += ../libqhull/poly2.c
+SOURCES += ../libqhull/merge.c
+SOURCES += ../libqhull/libqhull.c
+SOURCES += ../libqhull/geom.c
+SOURCES += ../libqhull/poly.c
+SOURCES += ../libqhull/qset.c
+SOURCES += ../libqhull/mem.c
+SOURCES += ../libqhull/random.c
+SOURCES += ../libqhull/usermem.c
+SOURCES += ../libqhull/userprintf.c
+SOURCES += ../libqhull/io.c
+SOURCES += ../libqhull/user.c
+SOURCES += ../libqhull/rboxlib.c
+SOURCES += ../libqhull/userprintf_rbox.c
+
+HEADERS += ../libqhull/geom.h
+HEADERS += ../libqhull/io.h
+HEADERS += ../libqhull/libqhull.h
+HEADERS += ../libqhull/mem.h
+HEADERS += ../libqhull/merge.h
+HEADERS += ../libqhull/poly.h
+HEADERS += ../libqhull/random.h
+HEADERS += ../libqhull/qhull_a.h
+HEADERS += ../libqhull/qset.h
+HEADERS += ../libqhull/stat.h
+HEADERS += ../libqhull/user.h
+
+OTHER_FILES += Mborland
+OTHER_FILES += qh-geom.htm
+OTHER_FILES += qh-globa.htm
+OTHER_FILES += qh-io.htm
+OTHER_FILES += qh-mem.htm
+OTHER_FILES += qh-merge.htm
+OTHER_FILES += qh-poly.htm
+OTHER_FILES += qh-qhull.htm
+OTHER_FILES += qh-set.htm
+OTHER_FILES += qh-stat.htm
+OTHER_FILES += qh-user.htm
diff --git a/xs/src/qhull/src/libqhull/mem.c b/xs/src/qhull/src/libqhull/mem.c
new file mode 100644
index 000000000..db72bb4e1
--- /dev/null
+++ b/xs/src/qhull/src/libqhull/mem.c
@@ -0,0 +1,576 @@
+/*<html><pre> -<a href="qh-mem.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ mem.c
+ memory management routines for qhull
+
+ This is a standalone program.
+
+ To initialize memory:
+
+ qh_meminit(stderr);
+ qh_meminitbuffers(qh IStracing, qh_MEMalign, 7, qh_MEMbufsize,qh_MEMinitbuf);
+ qh_memsize((int)sizeof(facetT));
+ qh_memsize((int)sizeof(facetT));
+ ...
+ qh_memsetup();
+
+ To free up all memory buffers:
+ qh_memfreeshort(&curlong, &totlong);
+
+ if qh_NOmem,
+ malloc/free is used instead of mem.c
+
+ notes:
+ uses Quickfit algorithm (freelists for commonly allocated sizes)
+ assumes small sizes for freelists (it discards the tail of memory buffers)
+
+ see:
+ qh-mem.htm and mem.h
+ global.c (qh_initbuffers) for an example of using mem.c
+
+ Copyright (c) 1993-2015 The Geometry Center.
+ $Id: //main/2015/qhull/src/libqhull/mem.c#7 $$Change: 2065 $
+ $DateTime: 2016/01/18 13:51:04 $$Author: bbarber $
+*/
+
+#include "user.h" /* for QHULL_CRTDBG */
+#include "mem.h"
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifndef qhDEFlibqhull
+typedef struct ridgeT ridgeT;
+typedef struct facetT facetT;
+#ifdef _MSC_VER /* Microsoft Visual C++ -- warning level 4 */
+#pragma warning( disable : 4127) /* conditional expression is constant */
+#pragma warning( disable : 4706) /* assignment within conditional function */
+#endif
+void qh_errexit(int exitcode, facetT *, ridgeT *);
+void qh_exit(int exitcode);
+void qh_fprintf(FILE *fp, int msgcode, const char *fmt, ... );
+void qh_fprintf_stderr(int msgcode, const char *fmt, ... );
+void qh_free(void *mem);
+void *qh_malloc(size_t size);
+#endif
+
+/*============ -global data structure ==============
+ see mem.h for definition
+*/
+
+qhmemT qhmem= {0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0}; /* remove "= {0}" if this causes a compiler error */
+
+#ifndef qh_NOmem
+
+/*============= internal functions ==============*/
+
+static int qh_intcompare(const void *i, const void *j);
+
+/*========== functions in alphabetical order ======== */
+
+/*-<a href="qh-mem.htm#TOC"
+ >-------------------------------</a><a name="intcompare">-</a>
+
+ qh_intcompare( i, j )
+ used by qsort and bsearch to compare two integers
+*/
+static int qh_intcompare(const void *i, const void *j) {
+ return(*((const int *)i) - *((const int *)j));
+} /* intcompare */
+
+
+/*-<a href="qh-mem.htm#TOC"
+ >--------------------------------</a><a name="memalloc">-</a>
+
+ qh_memalloc( insize )
+ returns object of insize bytes
+ qhmem is the global memory structure
+
+ returns:
+ pointer to allocated memory
+ errors if insufficient memory
+
+ notes:
+ use explicit type conversion to avoid type warnings on some compilers
+ actual object may be larger than insize
+ use qh_memalloc_() for inline code for quick allocations
+ logs allocations if 'T5'
+ caller is responsible for freeing the memory.
+ short memory is freed on shutdown by qh_memfreeshort unless qh_NOmem
+
+ design:
+ if size < qhmem.LASTsize
+ if qhmem.freelists[size] non-empty
+ return first object on freelist
+ else
+ round up request to size of qhmem.freelists[size]
+ allocate new allocation buffer if necessary
+ allocate object from allocation buffer
+ else
+ allocate object with qh_malloc() in user.c
+*/
+void *qh_memalloc(int insize) {
+ void **freelistp, *newbuffer;
+ int idx, size, n;
+ int outsize, bufsize;
+ void *object;
+
+ if (insize<0) {
+ qh_fprintf(qhmem.ferr, 6235, "qhull error (qh_memalloc): negative request size (%d). Did int overflow due to high-D?\n", insize); /* WARN64 */
+ qh_errexit(qhmem_ERRmem, NULL, NULL);
+ }
+ if (insize>=0 && insize <= qhmem.LASTsize) {
+ idx= qhmem.indextable[insize];
+ outsize= qhmem.sizetable[idx];
+ qhmem.totshort += outsize;
+ freelistp= qhmem.freelists+idx;
+ if ((object= *freelistp)) {
+ qhmem.cntquick++;
+ qhmem.totfree -= outsize;
+ *freelistp= *((void **)*freelistp); /* replace freelist with next object */
+#ifdef qh_TRACEshort
+ n= qhmem.cntshort+qhmem.cntquick+qhmem.freeshort;
+ if (qhmem.IStracing >= 5)
+ qh_fprintf(qhmem.ferr, 8141, "qh_mem %p n %8d alloc quick: %d bytes (tot %d cnt %d)\n", object, n, outsize, qhmem.totshort, qhmem.cntshort+qhmem.cntquick-qhmem.freeshort);
+#endif
+ return(object);
+ }else {
+ qhmem.cntshort++;
+ if (outsize > qhmem.freesize) {
+ qhmem.totdropped += qhmem.freesize;
+ if (!qhmem.curbuffer)
+ bufsize= qhmem.BUFinit;
+ else
+ bufsize= qhmem.BUFsize;
+ if (!(newbuffer= qh_malloc((size_t)bufsize))) {
+ qh_fprintf(qhmem.ferr, 6080, "qhull error (qh_memalloc): insufficient memory to allocate short memory buffer (%d bytes)\n", bufsize);
+ qh_errexit(qhmem_ERRmem, NULL, NULL);
+ }
+ *((void **)newbuffer)= qhmem.curbuffer; /* prepend newbuffer to curbuffer
+ list. newbuffer!=0 by QH6080 */
+ qhmem.curbuffer= newbuffer;
+ size= (sizeof(void **) + qhmem.ALIGNmask) & ~qhmem.ALIGNmask;
+ qhmem.freemem= (void *)((char *)newbuffer+size);
+ qhmem.freesize= bufsize - size;
+ qhmem.totbuffer += bufsize - size; /* easier to check */
+ /* Periodically test totbuffer. It matches at beginning and exit of every call */
+ n = qhmem.totshort + qhmem.totfree + qhmem.totdropped + qhmem.freesize - outsize;
+ if (qhmem.totbuffer != n) {
+ qh_fprintf(qhmem.ferr, 6212, "qh_memalloc internal error: short totbuffer %d != totshort+totfree... %d\n", qhmem.totbuffer, n);
+ qh_errexit(qhmem_ERRmem, NULL, NULL);
+ }
+ }
+ object= qhmem.freemem;
+ qhmem.freemem= (void *)((char *)qhmem.freemem + outsize);
+ qhmem.freesize -= outsize;
+ qhmem.totunused += outsize - insize;
+#ifdef qh_TRACEshort
+ n= qhmem.cntshort+qhmem.cntquick+qhmem.freeshort;
+ if (qhmem.IStracing >= 5)
+ qh_fprintf(qhmem.ferr, 8140, "qh_mem %p n %8d alloc short: %d bytes (tot %d cnt %d)\n", object, n, outsize, qhmem.totshort, qhmem.cntshort+qhmem.cntquick-qhmem.freeshort);
+#endif
+ return object;
+ }
+ }else { /* long allocation */
+ if (!qhmem.indextable) {
+ qh_fprintf(qhmem.ferr, 6081, "qhull internal error (qh_memalloc): qhmem has not been initialized.\n");
+ qh_errexit(qhmem_ERRqhull, NULL, NULL);
+ }
+ outsize= insize;
+ qhmem.cntlong++;
+ qhmem.totlong += outsize;
+ if (qhmem.maxlong < qhmem.totlong)
+ qhmem.maxlong= qhmem.totlong;
+ if (!(object= qh_malloc((size_t)outsize))) {
+ qh_fprintf(qhmem.ferr, 6082, "qhull error (qh_memalloc): insufficient memory to allocate %d bytes\n", outsize);
+ qh_errexit(qhmem_ERRmem, NULL, NULL);
+ }
+ if (qhmem.IStracing >= 5)
+ qh_fprintf(qhmem.ferr, 8057, "qh_mem %p n %8d alloc long: %d bytes (tot %d cnt %d)\n", object, qhmem.cntlong+qhmem.freelong, outsize, qhmem.totlong, qhmem.cntlong-qhmem.freelong);
+ }
+ return(object);
+} /* memalloc */
+
+
+/*-<a href="qh-mem.htm#TOC"
+ >--------------------------------</a><a name="memcheck">-</a>
+
+ qh_memcheck( )
+*/
+void qh_memcheck(void) {
+ int i, count, totfree= 0;
+ void *object;
+
+ if (qhmem.ferr == 0 || qhmem.IStracing < 0 || qhmem.IStracing > 10 || (((qhmem.ALIGNmask+1) & qhmem.ALIGNmask) != 0)) {
+ qh_fprintf_stderr(6244, "qh_memcheck error: either qhmem is overwritten or qhmem is not initialized. Call qh_meminit() or qh_new_qhull() before calling qh_mem routines. ferr 0x%x IsTracing %d ALIGNmask 0x%x", qhmem.ferr, qhmem.IStracing, qhmem.ALIGNmask);
+ qh_exit(qhmem_ERRqhull); /* can not use qh_errexit() */
+ }
+ if (qhmem.IStracing != 0)
+ qh_fprintf(qhmem.ferr, 8143, "qh_memcheck: check size of freelists on qhmem\nqh_memcheck: A segmentation fault indicates an overwrite of qhmem\n");
+ for (i=0; i < qhmem.TABLEsize; i++) {
+ count=0;
+ for (object= qhmem.freelists[i]; object; object= *((void **)object))
+ count++;
+ totfree += qhmem.sizetable[i] * count;
+ }
+ if (totfree != qhmem.totfree) {
+ qh_fprintf(qhmem.ferr, 6211, "Qhull internal error (qh_memcheck): totfree %d not equal to freelist total %d\n", qhmem.totfree, totfree);
+ qh_errexit(qhmem_ERRqhull, NULL, NULL);
+ }
+ if (qhmem.IStracing != 0)
+ qh_fprintf(qhmem.ferr, 8144, "qh_memcheck: total size of freelists totfree is the same as qhmem.totfree\n", totfree);
+} /* memcheck */
+
+/*-<a href="qh-mem.htm#TOC"
+ >--------------------------------</a><a name="memfree">-</a>
+
+ qh_memfree( object, insize )
+ free up an object of size bytes
+ size is insize from qh_memalloc
+
+ notes:
+ object may be NULL
+ type checking warns if using (void **)object
+ use qh_memfree_() for quick free's of small objects
+
+ design:
+ if size <= qhmem.LASTsize
+ append object to corresponding freelist
+ else
+ call qh_free(object)
+*/
+void qh_memfree(void *object, int insize) {
+ void **freelistp;
+ int idx, outsize;
+
+ if (!object)
+ return;
+ if (insize <= qhmem.LASTsize) {
+ qhmem.freeshort++;
+ idx= qhmem.indextable[insize];
+ outsize= qhmem.sizetable[idx];
+ qhmem.totfree += outsize;
+ qhmem.totshort -= outsize;
+ freelistp= qhmem.freelists + idx;
+ *((void **)object)= *freelistp;
+ *freelistp= object;
+#ifdef qh_TRACEshort
+ idx= qhmem.cntshort+qhmem.cntquick+qhmem.freeshort;
+ if (qhmem.IStracing >= 5)
+ qh_fprintf(qhmem.ferr, 8142, "qh_mem %p n %8d free short: %d bytes (tot %d cnt %d)\n", object, idx, outsize, qhmem.totshort, qhmem.cntshort+qhmem.cntquick-qhmem.freeshort);
+#endif
+ }else {
+ qhmem.freelong++;
+ qhmem.totlong -= insize;
+ if (qhmem.IStracing >= 5)
+ qh_fprintf(qhmem.ferr, 8058, "qh_mem %p n %8d free long: %d bytes (tot %d cnt %d)\n", object, qhmem.cntlong+qhmem.freelong, insize, qhmem.totlong, qhmem.cntlong-qhmem.freelong);
+ qh_free(object);
+ }
+} /* memfree */
+
+
+/*-<a href="qh-mem.htm#TOC"
+ >-------------------------------</a><a name="memfreeshort">-</a>
+
+ qh_memfreeshort( curlong, totlong )
+ frees up all short and qhmem memory allocations
+
+ returns:
+ number and size of current long allocations
+
+ see:
+ qh_freeqhull(allMem)
+ qh_memtotal(curlong, totlong, curshort, totshort, maxlong, totbuffer);
+*/
+void qh_memfreeshort(int *curlong, int *totlong) {
+ void *buffer, *nextbuffer;
+ FILE *ferr;
+
+ *curlong= qhmem.cntlong - qhmem.freelong;
+ *totlong= qhmem.totlong;
+ for (buffer= qhmem.curbuffer; buffer; buffer= nextbuffer) {
+ nextbuffer= *((void **) buffer);
+ qh_free(buffer);
+ }
+ qhmem.curbuffer= NULL;
+ if (qhmem.LASTsize) {
+ qh_free(qhmem.indextable);
+ qh_free(qhmem.freelists);
+ qh_free(qhmem.sizetable);
+ }
+ ferr= qhmem.ferr;
+ memset((char *)&qhmem, 0, sizeof(qhmem)); /* every field is 0, FALSE, NULL */
+ qhmem.ferr= ferr;
+} /* memfreeshort */
+
+
+/*-<a href="qh-mem.htm#TOC"
+ >--------------------------------</a><a name="meminit">-</a>
+
+ qh_meminit( ferr )
+ initialize qhmem and test sizeof( void*)
+ Does not throw errors. qh_exit on failure
+*/
+void qh_meminit(FILE *ferr) {
+
+ memset((char *)&qhmem, 0, sizeof(qhmem)); /* every field is 0, FALSE, NULL */
+ if (ferr)
+ qhmem.ferr= ferr;
+ else
+ qhmem.ferr= stderr;
+ if (sizeof(void*) < sizeof(int)) {
+ qh_fprintf(qhmem.ferr, 6083, "qhull internal error (qh_meminit): sizeof(void*) %d < sizeof(int) %d. qset.c will not work\n", (int)sizeof(void*), (int)sizeof(int));
+ qh_exit(qhmem_ERRqhull); /* can not use qh_errexit() */
+ }
+ if (sizeof(void*) > sizeof(ptr_intT)) {
+ qh_fprintf(qhmem.ferr, 6084, "qhull internal error (qh_meminit): sizeof(void*) %d > sizeof(ptr_intT) %d. Change ptr_intT in mem.h to 'long long'\n", (int)sizeof(void*), (int)sizeof(ptr_intT));
+ qh_exit(qhmem_ERRqhull); /* can not use qh_errexit() */
+ }
+ qh_memcheck();
+} /* meminit */
+
+/*-<a href="qh-mem.htm#TOC"
+ >-------------------------------</a><a name="meminitbuffers">-</a>
+
+ qh_meminitbuffers( tracelevel, alignment, numsizes, bufsize, bufinit )
+ initialize qhmem
+ if tracelevel >= 5, trace memory allocations
+ alignment= desired address alignment for memory allocations
+ numsizes= number of freelists
+ bufsize= size of additional memory buffers for short allocations
+ bufinit= size of initial memory buffer for short allocations
+*/
+void qh_meminitbuffers(int tracelevel, int alignment, int numsizes, int bufsize, int bufinit) {
+
+ qhmem.IStracing= tracelevel;
+ qhmem.NUMsizes= numsizes;
+ qhmem.BUFsize= bufsize;
+ qhmem.BUFinit= bufinit;
+ qhmem.ALIGNmask= alignment-1;
+ if (qhmem.ALIGNmask & ~qhmem.ALIGNmask) {
+ qh_fprintf(qhmem.ferr, 6085, "qhull internal error (qh_meminit): memory alignment %d is not a power of 2\n", alignment);
+ qh_errexit(qhmem_ERRqhull, NULL, NULL);
+ }
+ qhmem.sizetable= (int *) calloc((size_t)numsizes, sizeof(int));
+ qhmem.freelists= (void **) calloc((size_t)numsizes, sizeof(void *));
+ if (!qhmem.sizetable || !qhmem.freelists) {
+ qh_fprintf(qhmem.ferr, 6086, "qhull error (qh_meminit): insufficient memory\n");
+ qh_errexit(qhmem_ERRmem, NULL, NULL);
+ }
+ if (qhmem.IStracing >= 1)
+ qh_fprintf(qhmem.ferr, 8059, "qh_meminitbuffers: memory initialized with alignment %d\n", alignment);
+} /* meminitbuffers */
+
+/*-<a href="qh-mem.htm#TOC"
+ >-------------------------------</a><a name="memsetup">-</a>
+
+ qh_memsetup()
+ set up memory after running memsize()
+*/
+void qh_memsetup(void) {
+ int k,i;
+
+ qsort(qhmem.sizetable, (size_t)qhmem.TABLEsize, sizeof(int), qh_intcompare);
+ qhmem.LASTsize= qhmem.sizetable[qhmem.TABLEsize-1];
+ if (qhmem.LASTsize >= qhmem.BUFsize || qhmem.LASTsize >= qhmem.BUFinit) {
+ qh_fprintf(qhmem.ferr, 6087, "qhull error (qh_memsetup): largest mem size %d is >= buffer size %d or initial buffer size %d\n",
+ qhmem.LASTsize, qhmem.BUFsize, qhmem.BUFinit);
+ qh_errexit(qhmem_ERRmem, NULL, NULL);
+ }
+ if (!(qhmem.indextable= (int *)qh_malloc((qhmem.LASTsize+1) * sizeof(int)))) {
+ qh_fprintf(qhmem.ferr, 6088, "qhull error (qh_memsetup): insufficient memory\n");
+ qh_errexit(qhmem_ERRmem, NULL, NULL);
+ }
+ for (k=qhmem.LASTsize+1; k--; )
+ qhmem.indextable[k]= k;
+ i= 0;
+ for (k=0; k <= qhmem.LASTsize; k++) {
+ if (qhmem.indextable[k] <= qhmem.sizetable[i])
+ qhmem.indextable[k]= i;
+ else
+ qhmem.indextable[k]= ++i;
+ }
+} /* memsetup */
+
+/*-<a href="qh-mem.htm#TOC"
+ >-------------------------------</a><a name="memsize">-</a>
+
+ qh_memsize( size )
+ define a free list for this size
+*/
+void qh_memsize(int size) {
+ int k;
+
+ if (qhmem.LASTsize) {
+ qh_fprintf(qhmem.ferr, 6089, "qhull error (qh_memsize): called after qhmem_setup\n");
+ qh_errexit(qhmem_ERRqhull, NULL, NULL);
+ }
+ size= (size + qhmem.ALIGNmask) & ~qhmem.ALIGNmask;
+ for (k=qhmem.TABLEsize; k--; ) {
+ if (qhmem.sizetable[k] == size)
+ return;
+ }
+ if (qhmem.TABLEsize < qhmem.NUMsizes)
+ qhmem.sizetable[qhmem.TABLEsize++]= size;
+ else
+ qh_fprintf(qhmem.ferr, 7060, "qhull warning (memsize): free list table has room for only %d sizes\n", qhmem.NUMsizes);
+} /* memsize */
+
+
+/*-<a href="qh-mem.htm#TOC"
+ >-------------------------------</a><a name="memstatistics">-</a>
+
+ qh_memstatistics( fp )
+ print out memory statistics
+
+ Verifies that qhmem.totfree == sum of freelists
+*/
+void qh_memstatistics(FILE *fp) {
+ int i;
+ int count;
+ void *object;
+
+ qh_memcheck();
+ qh_fprintf(fp, 9278, "\nmemory statistics:\n\
+%7d quick allocations\n\
+%7d short allocations\n\
+%7d long allocations\n\
+%7d short frees\n\
+%7d long frees\n\
+%7d bytes of short memory in use\n\
+%7d bytes of short memory in freelists\n\
+%7d bytes of dropped short memory\n\
+%7d bytes of unused short memory (estimated)\n\
+%7d bytes of long memory allocated (max, except for input)\n\
+%7d bytes of long memory in use (in %d pieces)\n\
+%7d bytes of short memory buffers (minus links)\n\
+%7d bytes per short memory buffer (initially %d bytes)\n",
+ qhmem.cntquick, qhmem.cntshort, qhmem.cntlong,
+ qhmem.freeshort, qhmem.freelong,
+ qhmem.totshort, qhmem.totfree,
+ qhmem.totdropped + qhmem.freesize, qhmem.totunused,
+ qhmem.maxlong, qhmem.totlong, qhmem.cntlong - qhmem.freelong,
+ qhmem.totbuffer, qhmem.BUFsize, qhmem.BUFinit);
+ if (qhmem.cntlarger) {
+ qh_fprintf(fp, 9279, "%7d calls to qh_setlarger\n%7.2g average copy size\n",
+ qhmem.cntlarger, ((float)qhmem.totlarger)/(float)qhmem.cntlarger);
+ qh_fprintf(fp, 9280, " freelists(bytes->count):");
+ }
+ for (i=0; i < qhmem.TABLEsize; i++) {
+ count=0;
+ for (object= qhmem.freelists[i]; object; object= *((void **)object))
+ count++;
+ qh_fprintf(fp, 9281, " %d->%d", qhmem.sizetable[i], count);
+ }
+ qh_fprintf(fp, 9282, "\n\n");
+} /* memstatistics */
+
+
+/*-<a href="qh-mem.htm#TOC"
+ >-------------------------------</a><a name="NOmem">-</a>
+
+ qh_NOmem
+ turn off quick-fit memory allocation
+
+ notes:
+ uses qh_malloc() and qh_free() instead
+*/
+#else /* qh_NOmem */
+
+void *qh_memalloc(int insize) {
+ void *object;
+
+ if (!(object= qh_malloc((size_t)insize))) {
+ qh_fprintf(qhmem.ferr, 6090, "qhull error (qh_memalloc): insufficient memory\n");
+ qh_errexit(qhmem_ERRmem, NULL, NULL);
+ }
+ qhmem.cntlong++;
+ qhmem.totlong += insize;
+ if (qhmem.maxlong < qhmem.totlong)
+ qhmem.maxlong= qhmem.totlong;
+ if (qhmem.IStracing >= 5)
+ qh_fprintf(qhmem.ferr, 8060, "qh_mem %p n %8d alloc long: %d bytes (tot %d cnt %d)\n", object, qhmem.cntlong+qhmem.freelong, insize, qhmem.totlong, qhmem.cntlong-qhmem.freelong);
+ return object;
+}
+
+void qh_memfree(void *object, int insize) {
+
+ if (!object)
+ return;
+ qh_free(object);
+ qhmem.freelong++;
+ qhmem.totlong -= insize;
+ if (qhmem.IStracing >= 5)
+ qh_fprintf(qhmem.ferr, 8061, "qh_mem %p n %8d free long: %d bytes (tot %d cnt %d)\n", object, qhmem.cntlong+qhmem.freelong, insize, qhmem.totlong, qhmem.cntlong-qhmem.freelong);
+}
+
+void qh_memfreeshort(int *curlong, int *totlong) {
+ *totlong= qhmem.totlong;
+ *curlong= qhmem.cntlong - qhmem.freelong;
+ memset((char *)&qhmem, 0, sizeof(qhmem)); /* every field is 0, FALSE, NULL */
+}
+
+void qh_meminit(FILE *ferr) {
+
+ memset((char *)&qhmem, 0, sizeof(qhmem)); /* every field is 0, FALSE, NULL */
+ if (ferr)
+ qhmem.ferr= ferr;
+ else
+ qhmem.ferr= stderr;
+ if (sizeof(void*) < sizeof(int)) {
+ qh_fprintf(qhmem.ferr, 6091, "qhull internal error (qh_meminit): sizeof(void*) %d < sizeof(int) %d. qset.c will not work\n", (int)sizeof(void*), (int)sizeof(int));
+ qh_errexit(qhmem_ERRqhull, NULL, NULL);
+ }
+}
+
+void qh_meminitbuffers(int tracelevel, int alignment, int numsizes, int bufsize, int bufinit) {
+
+ qhmem.IStracing= tracelevel;
+}
+
+void qh_memsetup(void) {
+
+}
+
+void qh_memsize(int size) {
+
+}
+
+void qh_memstatistics(FILE *fp) {
+
+ qh_fprintf(fp, 9409, "\nmemory statistics:\n\
+%7d long allocations\n\
+%7d long frees\n\
+%7d bytes of long memory allocated (max, except for input)\n\
+%7d bytes of long memory in use (in %d pieces)\n",
+ qhmem.cntlong,
+ qhmem.freelong,
+ qhmem.maxlong, qhmem.totlong, qhmem.cntlong - qhmem.freelong);
+}
+
+#endif /* qh_NOmem */
+
+/*-<a href="qh-mem.htm#TOC"
+>-------------------------------</a><a name="memtotlong">-</a>
+
+ qh_memtotal( totlong, curlong, totshort, curshort, maxlong, totbuffer )
+ Return the total, allocated long and short memory
+
+ returns:
+ Returns the total current bytes of long and short allocations
+ Returns the current count of long and short allocations
+ Returns the maximum long memory and total short buffer (minus one link per buffer)
+ Does not error (UsingLibQhull.cpp)
+*/
+void qh_memtotal(int *totlong, int *curlong, int *totshort, int *curshort, int *maxlong, int *totbuffer) {
+ *totlong= qhmem.totlong;
+ *curlong= qhmem.cntlong - qhmem.freelong;
+ *totshort= qhmem.totshort;
+ *curshort= qhmem.cntshort + qhmem.cntquick - qhmem.freeshort;
+ *maxlong= qhmem.maxlong;
+ *totbuffer= qhmem.totbuffer;
+} /* memtotlong */
+
diff --git a/xs/src/qhull/src/libqhull/mem.h b/xs/src/qhull/src/libqhull/mem.h
new file mode 100644
index 000000000..453f319df
--- /dev/null
+++ b/xs/src/qhull/src/libqhull/mem.h
@@ -0,0 +1,222 @@
+/*<html><pre> -<a href="qh-mem.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ mem.h
+ prototypes for memory management functions
+
+ see qh-mem.htm, mem.c and qset.h
+
+ for error handling, writes message and calls
+ qh_errexit(qhmem_ERRmem, NULL, NULL) if insufficient memory
+ and
+ qh_errexit(qhmem_ERRqhull, NULL, NULL) otherwise
+
+ Copyright (c) 1993-2015 The Geometry Center.
+ $Id: //main/2015/qhull/src/libqhull/mem.h#2 $$Change: 2062 $
+ $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $
+*/
+
+#ifndef qhDEFmem
+#define qhDEFmem 1
+
+#include <stdio.h>
+
+/*-<a href="qh-mem.htm#TOC"
+ >-------------------------------</a><a name="NOmem">-</a>
+
+ qh_NOmem
+ turn off quick-fit memory allocation
+
+ notes:
+ mem.c implements Quickfit memory allocation for about 20% time
+ savings. If it fails on your machine, try to locate the
+ problem, and send the answer to qhull@qhull.org. If this can
+ not be done, define qh_NOmem to use malloc/free instead.
+
+ #define qh_NOmem
+*/
+
+/*-<a href="qh-mem.htm#TOC"
+>-------------------------------</a><a name="TRACEshort">-</a>
+
+qh_TRACEshort
+Trace short and quick memory allocations at T5
+
+*/
+#define qh_TRACEshort
+
+/*-------------------------------------------
+ to avoid bus errors, memory allocation must consider alignment requirements.
+ malloc() automatically takes care of alignment. Since mem.c manages
+ its own memory, we need to explicitly specify alignment in
+ qh_meminitbuffers().
+
+ A safe choice is sizeof(double). sizeof(float) may be used if doubles
+ do not occur in data structures and pointers are the same size. Be careful
+ of machines (e.g., DEC Alpha) with large pointers. If gcc is available,
+ use __alignof__(double) or fmax_(__alignof__(float), __alignof__(void *)).
+
+ see <a href="user.h#MEMalign">qh_MEMalign</a> in user.h for qhull's alignment
+*/
+
+#define qhmem_ERRmem 4 /* matches qh_ERRmem in libqhull.h */
+#define qhmem_ERRqhull 5 /* matches qh_ERRqhull in libqhull.h */
+
+/*-<a href="qh-mem.htm#TOC"
+ >--------------------------------</a><a name="ptr_intT">-</a>
+
+ ptr_intT
+ for casting a void * to an integer-type that holds a pointer
+ Used for integer expressions (e.g., computing qh_gethash() in poly.c)
+
+ notes:
+ WARN64 -- these notes indicate 64-bit issues
+ On 64-bit machines, a pointer may be larger than an 'int'.
+ qh_meminit()/mem.c checks that 'ptr_intT' holds a 'void*'
+ ptr_intT is typically a signed value, but not necessarily so
+ size_t is typically unsigned, but should match the parameter type
+ Qhull uses int instead of size_t except for system calls such as malloc, qsort, qh_malloc, etc.
+ This matches Qt convention and is easier to work with.
+*/
+#if (defined(__MINGW64__)) && defined(_WIN64)
+typedef long long ptr_intT;
+#elif (_MSC_VER) && defined(_WIN64)
+typedef long long ptr_intT;
+#else
+typedef long ptr_intT;
+#endif
+
+/*-<a href="qh-mem.htm#TOC"
+ >--------------------------------</a><a name="qhmemT">-</a>
+
+ qhmemT
+ global memory structure for mem.c
+
+ notes:
+ users should ignore qhmem except for writing extensions
+ qhmem is allocated in mem.c
+
+ qhmem could be swapable like qh and qhstat, but then
+ multiple qh's and qhmem's would need to keep in synch.
+ A swapable qhmem would also waste memory buffers. As long
+ as memory operations are atomic, there is no problem with
+ multiple qh structures being active at the same time.
+ If you need separate address spaces, you can swap the
+ contents of qhmem.
+*/
+typedef struct qhmemT qhmemT;
+extern qhmemT qhmem;
+
+#ifndef DEFsetT
+#define DEFsetT 1
+typedef struct setT setT; /* defined in qset.h */
+#endif
+
+/* Update qhmem in mem.c if add or remove fields */
+struct qhmemT { /* global memory management variables */
+ int BUFsize; /* size of memory allocation buffer */
+ int BUFinit; /* initial size of memory allocation buffer */
+ int TABLEsize; /* actual number of sizes in free list table */
+ int NUMsizes; /* maximum number of sizes in free list table */
+ int LASTsize; /* last size in free list table */
+ int ALIGNmask; /* worst-case alignment, must be 2^n-1 */
+ void **freelists; /* free list table, linked by offset 0 */
+ int *sizetable; /* size of each freelist */
+ int *indextable; /* size->index table */
+ void *curbuffer; /* current buffer, linked by offset 0 */
+ void *freemem; /* free memory in curbuffer */
+ int freesize; /* size of freemem in bytes */
+ setT *tempstack; /* stack of temporary memory, managed by users */
+ FILE *ferr; /* file for reporting errors when 'qh' may be undefined */
+ int IStracing; /* =5 if tracing memory allocations */
+ int cntquick; /* count of quick allocations */
+ /* Note: removing statistics doesn't effect speed */
+ int cntshort; /* count of short allocations */
+ int cntlong; /* count of long allocations */
+ int freeshort; /* count of short memfrees */
+ int freelong; /* count of long memfrees */
+ int totbuffer; /* total short memory buffers minus buffer links */
+ int totdropped; /* total dropped memory at end of short memory buffers (e.g., freesize) */
+ int totfree; /* total size of free, short memory on freelists */
+ int totlong; /* total size of long memory in use */
+ int maxlong; /* maximum totlong */
+ int totshort; /* total size of short memory in use */
+ int totunused; /* total unused short memory (estimated, short size - request size of first allocations) */
+ int cntlarger; /* count of setlarger's */
+ int totlarger; /* total copied by setlarger */
+};
+
+
+/*==================== -macros ====================*/
+
+/*-<a href="qh-mem.htm#TOC"
+ >--------------------------------</a><a name="memalloc_">-</a>
+
+ qh_memalloc_(insize, freelistp, object, type)
+ returns object of size bytes
+ assumes size<=qhmem.LASTsize and void **freelistp is a temp
+*/
+
+#if defined qh_NOmem
+#define qh_memalloc_(insize, freelistp, object, type) {\
+ object= (type*)qh_memalloc(insize); }
+#elif defined qh_TRACEshort
+#define qh_memalloc_(insize, freelistp, object, type) {\
+ freelistp= NULL; /* Avoid warnings */ \
+ object= (type*)qh_memalloc(insize); }
+#else /* !qh_NOmem */
+
+#define qh_memalloc_(insize, freelistp, object, type) {\
+ freelistp= qhmem.freelists + qhmem.indextable[insize];\
+ if ((object= (type*)*freelistp)) {\
+ qhmem.totshort += qhmem.sizetable[qhmem.indextable[insize]]; \
+ qhmem.totfree -= qhmem.sizetable[qhmem.indextable[insize]]; \
+ qhmem.cntquick++; \
+ *freelistp= *((void **)*freelistp);\
+ }else object= (type*)qh_memalloc(insize);}
+#endif
+
+/*-<a href="qh-mem.htm#TOC"
+ >--------------------------------</a><a name="memfree_">-</a>
+
+ qh_memfree_(object, insize, freelistp)
+ free up an object
+
+ notes:
+ object may be NULL
+ assumes size<=qhmem.LASTsize and void **freelistp is a temp
+*/
+#if defined qh_NOmem
+#define qh_memfree_(object, insize, freelistp) {\
+ qh_memfree(object, insize); }
+#elif defined qh_TRACEshort
+#define qh_memfree_(object, insize, freelistp) {\
+ freelistp= NULL; /* Avoid warnings */ \
+ qh_memfree(object, insize); }
+#else /* !qh_NOmem */
+
+#define qh_memfree_(object, insize, freelistp) {\
+ if (object) { \
+ qhmem.freeshort++;\
+ freelistp= qhmem.freelists + qhmem.indextable[insize];\
+ qhmem.totshort -= qhmem.sizetable[qhmem.indextable[insize]]; \
+ qhmem.totfree += qhmem.sizetable[qhmem.indextable[insize]]; \
+ *((void **)object)= *freelistp;\
+ *freelistp= object;}}
+#endif
+
+/*=============== prototypes in alphabetical order ============*/
+
+void *qh_memalloc(int insize);
+void qh_memcheck(void);
+void qh_memfree(void *object, int insize);
+void qh_memfreeshort(int *curlong, int *totlong);
+void qh_meminit(FILE *ferr);
+void qh_meminitbuffers(int tracelevel, int alignment, int numsizes,
+ int bufsize, int bufinit);
+void qh_memsetup(void);
+void qh_memsize(int size);
+void qh_memstatistics(FILE *fp);
+void qh_memtotal(int *totlong, int *curlong, int *totshort, int *curshort, int *maxlong, int *totbuffer);
+
+#endif /* qhDEFmem */
diff --git a/xs/src/qhull/src/libqhull/merge.c b/xs/src/qhull/src/libqhull/merge.c
new file mode 100644
index 000000000..22104dc03
--- /dev/null
+++ b/xs/src/qhull/src/libqhull/merge.c
@@ -0,0 +1,3628 @@
+/*<html><pre> -<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ merge.c
+ merges non-convex facets
+
+ see qh-merge.htm and merge.h
+
+ other modules call qh_premerge() and qh_postmerge()
+
+ the user may call qh_postmerge() to perform additional merges.
+
+ To remove deleted facets and vertices (qhull() in libqhull.c):
+ qh_partitionvisible(!qh_ALL, &numoutside); // visible_list, newfacet_list
+ qh_deletevisible(); // qh.visible_list
+ qh_resetlists(False, qh_RESETvisible); // qh.visible_list newvertex_list newfacet_list
+
+ assumes qh.CENTERtype= centrum
+
+ merges occur in qh_mergefacet and in qh_mergecycle
+ vertex->neighbors not set until the first merge occurs
+
+ Copyright (c) 1993-2015 C.B. Barber.
+ $Id: //main/2015/qhull/src/libqhull/merge.c#4 $$Change: 2064 $
+ $DateTime: 2016/01/18 12:36:08 $$Author: bbarber $
+*/
+
+#include "qhull_a.h"
+
+#ifndef qh_NOmerge
+
+/*===== functions(alphabetical after premerge and postmerge) ======*/
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="premerge">-</a>
+
+ qh_premerge( apex, maxcentrum )
+ pre-merge nonconvex facets in qh.newfacet_list for apex
+ maxcentrum defines coplanar and concave (qh_test_appendmerge)
+
+ returns:
+ deleted facets added to qh.visible_list with facet->visible set
+
+ notes:
+ uses globals, qh.MERGEexact, qh.PREmerge
+
+ design:
+ mark duplicate ridges in qh.newfacet_list
+ merge facet cycles in qh.newfacet_list
+ merge duplicate ridges and concave facets in qh.newfacet_list
+ check merged facet cycles for degenerate and redundant facets
+ merge degenerate and redundant facets
+ collect coplanar and concave facets
+ merge concave, coplanar, degenerate, and redundant facets
+*/
+void qh_premerge(vertexT *apex, realT maxcentrum, realT maxangle) {
+ boolT othermerge= False;
+ facetT *newfacet;
+
+ if (qh ZEROcentrum && qh_checkzero(!qh_ALL))
+ return;
+ trace2((qh ferr, 2008, "qh_premerge: premerge centrum %2.2g angle %2.2g for apex v%d facetlist f%d\n",
+ maxcentrum, maxangle, apex->id, getid_(qh newfacet_list)));
+ if (qh IStracing >= 4 && qh num_facets < 50)
+ qh_printlists();
+ qh centrum_radius= maxcentrum;
+ qh cos_max= maxangle;
+ qh degen_mergeset= qh_settemp(qh TEMPsize);
+ qh facet_mergeset= qh_settemp(qh TEMPsize);
+ if (qh hull_dim >=3) {
+ qh_mark_dupridges(qh newfacet_list); /* facet_mergeset */
+ qh_mergecycle_all(qh newfacet_list, &othermerge);
+ qh_forcedmerges(&othermerge /* qh.facet_mergeset */);
+ FORALLnew_facets { /* test samecycle merges */
+ if (!newfacet->simplicial && !newfacet->mergeridge)
+ qh_degen_redundant_neighbors(newfacet, NULL);
+ }
+ if (qh_merge_degenredundant())
+ othermerge= True;
+ }else /* qh.hull_dim == 2 */
+ qh_mergecycle_all(qh newfacet_list, &othermerge);
+ qh_flippedmerges(qh newfacet_list, &othermerge);
+ if (!qh MERGEexact || zzval_(Ztotmerge)) {
+ zinc_(Zpremergetot);
+ qh POSTmerging= False;
+ qh_getmergeset_initial(qh newfacet_list);
+ qh_all_merges(othermerge, False);
+ }
+ qh_settempfree(&qh facet_mergeset);
+ qh_settempfree(&qh degen_mergeset);
+} /* premerge */
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="postmerge">-</a>
+
+ qh_postmerge( reason, maxcentrum, maxangle, vneighbors )
+ post-merge nonconvex facets as defined by maxcentrum and maxangle
+ 'reason' is for reporting progress
+ if vneighbors,
+ calls qh_test_vneighbors at end of qh_all_merge
+ if firstmerge,
+ calls qh_reducevertices before qh_getmergeset
+
+ returns:
+ if first call (qh.visible_list != qh.facet_list),
+ builds qh.facet_newlist, qh.newvertex_list
+ deleted facets added to qh.visible_list with facet->visible
+ qh.visible_list == qh.facet_list
+
+ notes:
+
+
+ design:
+ if first call
+ set qh.visible_list and qh.newfacet_list to qh.facet_list
+ add all facets to qh.newfacet_list
+ mark non-simplicial facets, facet->newmerge
+ set qh.newvertext_list to qh.vertex_list
+ add all vertices to qh.newvertex_list
+ if a pre-merge occured
+ set vertex->delridge {will retest the ridge}
+ if qh.MERGEexact
+ call qh_reducevertices()
+ if no pre-merging
+ merge flipped facets
+ determine non-convex facets
+ merge all non-convex facets
+*/
+void qh_postmerge(const char *reason, realT maxcentrum, realT maxangle,
+ boolT vneighbors) {
+ facetT *newfacet;
+ boolT othermerges= False;
+ vertexT *vertex;
+
+ if (qh REPORTfreq || qh IStracing) {
+ qh_buildtracing(NULL, NULL);
+ qh_printsummary(qh ferr);
+ if (qh PRINTstatistics)
+ qh_printallstatistics(qh ferr, "reason");
+ qh_fprintf(qh ferr, 8062, "\n%s with 'C%.2g' and 'A%.2g'\n",
+ reason, maxcentrum, maxangle);
+ }
+ trace2((qh ferr, 2009, "qh_postmerge: postmerge. test vneighbors? %d\n",
+ vneighbors));
+ qh centrum_radius= maxcentrum;
+ qh cos_max= maxangle;
+ qh POSTmerging= True;
+ qh degen_mergeset= qh_settemp(qh TEMPsize);
+ qh facet_mergeset= qh_settemp(qh TEMPsize);
+ if (qh visible_list != qh facet_list) { /* first call */
+ qh NEWfacets= True;
+ qh visible_list= qh newfacet_list= qh facet_list;
+ FORALLnew_facets {
+ newfacet->newfacet= True;
+ if (!newfacet->simplicial)
+ newfacet->newmerge= True;
+ zinc_(Zpostfacets);
+ }
+ qh newvertex_list= qh vertex_list;
+ FORALLvertices
+ vertex->newlist= True;
+ if (qh VERTEXneighbors) { /* a merge has occurred */
+ FORALLvertices
+ vertex->delridge= True; /* test for redundant, needed? */
+ if (qh MERGEexact) {
+ if (qh hull_dim <= qh_DIMreduceBuild)
+ qh_reducevertices(); /* was skipped during pre-merging */
+ }
+ }
+ if (!qh PREmerge && !qh MERGEexact)
+ qh_flippedmerges(qh newfacet_list, &othermerges);
+ }
+ qh_getmergeset_initial(qh newfacet_list);
+ qh_all_merges(False, vneighbors);
+ qh_settempfree(&qh facet_mergeset);
+ qh_settempfree(&qh degen_mergeset);
+} /* post_merge */
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="all_merges">-</a>
+
+ qh_all_merges( othermerge, vneighbors )
+ merge all non-convex facets
+
+ set othermerge if already merged facets (for qh_reducevertices)
+ if vneighbors
+ tests vertex neighbors for convexity at end
+ qh.facet_mergeset lists the non-convex ridges in qh_newfacet_list
+ qh.degen_mergeset is defined
+ if qh.MERGEexact && !qh.POSTmerging,
+ does not merge coplanar facets
+
+ returns:
+ deleted facets added to qh.visible_list with facet->visible
+ deleted vertices added qh.delvertex_list with vertex->delvertex
+
+ notes:
+ unless !qh.MERGEindependent,
+ merges facets in independent sets
+ uses qh.newfacet_list as argument since merges call qh_removefacet()
+
+ design:
+ while merges occur
+ for each merge in qh.facet_mergeset
+ unless one of the facets was already merged in this pass
+ merge the facets
+ test merged facets for additional merges
+ add merges to qh.facet_mergeset
+ if vertices record neighboring facets
+ rename redundant vertices
+ update qh.facet_mergeset
+ if vneighbors ??
+ tests vertex neighbors for convexity at end
+*/
+void qh_all_merges(boolT othermerge, boolT vneighbors) {
+ facetT *facet1, *facet2;
+ mergeT *merge;
+ boolT wasmerge= True, isreduce;
+ void **freelistp; /* used if !qh_NOmem by qh_memfree_() */
+ vertexT *vertex;
+ mergeType mergetype;
+ int numcoplanar=0, numconcave=0, numdegenredun= 0, numnewmerges= 0;
+
+ trace2((qh ferr, 2010, "qh_all_merges: starting to merge facets beginning from f%d\n",
+ getid_(qh newfacet_list)));
+ while (True) {
+ wasmerge= False;
+ while (qh_setsize(qh facet_mergeset)) {
+ while ((merge= (mergeT*)qh_setdellast(qh facet_mergeset))) {
+ facet1= merge->facet1;
+ facet2= merge->facet2;
+ mergetype= merge->type;
+ qh_memfree_(merge, (int)sizeof(mergeT), freelistp);
+ if (facet1->visible || facet2->visible) /*deleted facet*/
+ continue;
+ if ((facet1->newfacet && !facet1->tested)
+ || (facet2->newfacet && !facet2->tested)) {
+ if (qh MERGEindependent && mergetype <= MRGanglecoplanar)
+ continue; /* perform independent sets of merges */
+ }
+ qh_merge_nonconvex(facet1, facet2, mergetype);
+ numdegenredun += qh_merge_degenredundant();
+ numnewmerges++;
+ wasmerge= True;
+ if (mergetype == MRGconcave)
+ numconcave++;
+ else /* MRGcoplanar or MRGanglecoplanar */
+ numcoplanar++;
+ } /* while setdellast */
+ if (qh POSTmerging && qh hull_dim <= qh_DIMreduceBuild
+ && numnewmerges > qh_MAXnewmerges) {
+ numnewmerges= 0;
+ qh_reducevertices(); /* otherwise large post merges too slow */
+ }
+ qh_getmergeset(qh newfacet_list); /* facet_mergeset */
+ } /* while mergeset */
+ if (qh VERTEXneighbors) {
+ isreduce= False;
+ if (qh hull_dim >=4 && qh POSTmerging) {
+ FORALLvertices
+ vertex->delridge= True;
+ isreduce= True;
+ }
+ if ((wasmerge || othermerge) && (!qh MERGEexact || qh POSTmerging)
+ && qh hull_dim <= qh_DIMreduceBuild) {
+ othermerge= False;
+ isreduce= True;
+ }
+ if (isreduce) {
+ if (qh_reducevertices()) {
+ qh_getmergeset(qh newfacet_list); /* facet_mergeset */
+ continue;
+ }
+ }
+ }
+ if (vneighbors && qh_test_vneighbors(/* qh.newfacet_list */))
+ continue;
+ break;
+ } /* while (True) */
+ if (qh CHECKfrequently && !qh MERGEexact) {
+ qh old_randomdist= qh RANDOMdist;
+ qh RANDOMdist= False;
+ qh_checkconvex(qh newfacet_list, qh_ALGORITHMfault);
+ /* qh_checkconnect(); [this is slow and it changes the facet order] */
+ qh RANDOMdist= qh old_randomdist;
+ }
+ trace1((qh ferr, 1009, "qh_all_merges: merged %d coplanar facets %d concave facets and %d degen or redundant facets.\n",
+ numcoplanar, numconcave, numdegenredun));
+ if (qh IStracing >= 4 && qh num_facets < 50)
+ qh_printlists();
+} /* all_merges */
+
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="appendmergeset">-</a>
+
+ qh_appendmergeset( facet, neighbor, mergetype, angle )
+ appends an entry to qh.facet_mergeset or qh.degen_mergeset
+
+ angle ignored if NULL or !qh.ANGLEmerge
+
+ returns:
+ merge appended to facet_mergeset or degen_mergeset
+ sets ->degenerate or ->redundant if degen_mergeset
+
+ see:
+ qh_test_appendmerge()
+
+ design:
+ allocate merge entry
+ if regular merge
+ append to qh.facet_mergeset
+ else if degenerate merge and qh.facet_mergeset is all degenerate
+ append to qh.degen_mergeset
+ else if degenerate merge
+ prepend to qh.degen_mergeset
+ else if redundant merge
+ append to qh.degen_mergeset
+*/
+void qh_appendmergeset(facetT *facet, facetT *neighbor, mergeType mergetype, realT *angle) {
+ mergeT *merge, *lastmerge;
+ void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */
+
+ if (facet->redundant)
+ return;
+ if (facet->degenerate && mergetype == MRGdegen)
+ return;
+ qh_memalloc_((int)sizeof(mergeT), freelistp, merge, mergeT);
+ merge->facet1= facet;
+ merge->facet2= neighbor;
+ merge->type= mergetype;
+ if (angle && qh ANGLEmerge)
+ merge->angle= *angle;
+ if (mergetype < MRGdegen)
+ qh_setappend(&(qh facet_mergeset), merge);
+ else if (mergetype == MRGdegen) {
+ facet->degenerate= True;
+ if (!(lastmerge= (mergeT*)qh_setlast(qh degen_mergeset))
+ || lastmerge->type == MRGdegen)
+ qh_setappend(&(qh degen_mergeset), merge);
+ else
+ qh_setaddnth(&(qh degen_mergeset), 0, merge);
+ }else if (mergetype == MRGredundant) {
+ facet->redundant= True;
+ qh_setappend(&(qh degen_mergeset), merge);
+ }else /* mergetype == MRGmirror */ {
+ if (facet->redundant || neighbor->redundant) {
+ qh_fprintf(qh ferr, 6092, "qhull error (qh_appendmergeset): facet f%d or f%d is already a mirrored facet\n",
+ facet->id, neighbor->id);
+ qh_errexit2(qh_ERRqhull, facet, neighbor);
+ }
+ if (!qh_setequal(facet->vertices, neighbor->vertices)) {
+ qh_fprintf(qh ferr, 6093, "qhull error (qh_appendmergeset): mirrored facets f%d and f%d do not have the same vertices\n",
+ facet->id, neighbor->id);
+ qh_errexit2(qh_ERRqhull, facet, neighbor);
+ }
+ facet->redundant= True;
+ neighbor->redundant= True;
+ qh_setappend(&(qh degen_mergeset), merge);
+ }
+} /* appendmergeset */
+
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="basevertices">-</a>
+
+ qh_basevertices( samecycle )
+ return temporary set of base vertices for samecycle
+ samecycle is first facet in the cycle
+ assumes apex is SETfirst_( samecycle->vertices )
+
+ returns:
+ vertices(settemp)
+ all ->seen are cleared
+
+ notes:
+ uses qh_vertex_visit;
+
+ design:
+ for each facet in samecycle
+ for each unseen vertex in facet->vertices
+ append to result
+*/
+setT *qh_basevertices(facetT *samecycle) {
+ facetT *same;
+ vertexT *apex, *vertex, **vertexp;
+ setT *vertices= qh_settemp(qh TEMPsize);
+
+ apex= SETfirstt_(samecycle->vertices, vertexT);
+ apex->visitid= ++qh vertex_visit;
+ FORALLsame_cycle_(samecycle) {
+ if (same->mergeridge)
+ continue;
+ FOREACHvertex_(same->vertices) {
+ if (vertex->visitid != qh vertex_visit) {
+ qh_setappend(&vertices, vertex);
+ vertex->visitid= qh vertex_visit;
+ vertex->seen= False;
+ }
+ }
+ }
+ trace4((qh ferr, 4019, "qh_basevertices: found %d vertices\n",
+ qh_setsize(vertices)));
+ return vertices;
+} /* basevertices */
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="checkconnect">-</a>
+
+ qh_checkconnect()
+ check that new facets are connected
+ new facets are on qh.newfacet_list
+
+ notes:
+ this is slow and it changes the order of the facets
+ uses qh.visit_id
+
+ design:
+ move first new facet to end of qh.facet_list
+ for all newly appended facets
+ append unvisited neighbors to end of qh.facet_list
+ for all new facets
+ report error if unvisited
+*/
+void qh_checkconnect(void /* qh.newfacet_list */) {
+ facetT *facet, *newfacet, *errfacet= NULL, *neighbor, **neighborp;
+
+ facet= qh newfacet_list;
+ qh_removefacet(facet);
+ qh_appendfacet(facet);
+ facet->visitid= ++qh visit_id;
+ FORALLfacet_(facet) {
+ FOREACHneighbor_(facet) {
+ if (neighbor->visitid != qh visit_id) {
+ qh_removefacet(neighbor);
+ qh_appendfacet(neighbor);
+ neighbor->visitid= qh visit_id;
+ }
+ }
+ }
+ FORALLnew_facets {
+ if (newfacet->visitid == qh visit_id)
+ break;
+ qh_fprintf(qh ferr, 6094, "qhull error: f%d is not attached to the new facets\n",
+ newfacet->id);
+ errfacet= newfacet;
+ }
+ if (errfacet)
+ qh_errexit(qh_ERRqhull, errfacet, NULL);
+} /* checkconnect */
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="checkzero">-</a>
+
+ qh_checkzero( testall )
+ check that facets are clearly convex for qh.DISTround with qh.MERGEexact
+
+ if testall,
+ test all facets for qh.MERGEexact post-merging
+ else
+ test qh.newfacet_list
+
+ if qh.MERGEexact,
+ allows coplanar ridges
+ skips convexity test while qh.ZEROall_ok
+
+ returns:
+ True if all facets !flipped, !dupridge, normal
+ if all horizon facets are simplicial
+ if all vertices are clearly below neighbor
+ if all opposite vertices of horizon are below
+ clears qh.ZEROall_ok if any problems or coplanar facets
+
+ notes:
+ uses qh.vertex_visit
+ horizon facets may define multiple new facets
+
+ design:
+ for all facets in qh.newfacet_list or qh.facet_list
+ check for flagged faults (flipped, etc.)
+ for all facets in qh.newfacet_list or qh.facet_list
+ for each neighbor of facet
+ skip horizon facets for qh.newfacet_list
+ test the opposite vertex
+ if qh.newfacet_list
+ test the other vertices in the facet's horizon facet
+*/
+boolT qh_checkzero(boolT testall) {
+ facetT *facet, *neighbor, **neighborp;
+ facetT *horizon, *facetlist;
+ int neighbor_i;
+ vertexT *vertex, **vertexp;
+ realT dist;
+
+ if (testall)
+ facetlist= qh facet_list;
+ else {
+ facetlist= qh newfacet_list;
+ FORALLfacet_(facetlist) {
+ horizon= SETfirstt_(facet->neighbors, facetT);
+ if (!horizon->simplicial)
+ goto LABELproblem;
+ if (facet->flipped || facet->dupridge || !facet->normal)
+ goto LABELproblem;
+ }
+ if (qh MERGEexact && qh ZEROall_ok) {
+ trace2((qh ferr, 2011, "qh_checkzero: skip convexity check until first pre-merge\n"));
+ return True;
+ }
+ }
+ FORALLfacet_(facetlist) {
+ qh vertex_visit++;
+ neighbor_i= 0;
+ horizon= NULL;
+ FOREACHneighbor_(facet) {
+ if (!neighbor_i && !testall) {
+ horizon= neighbor;
+ neighbor_i++;
+ continue; /* horizon facet tested in qh_findhorizon */
+ }
+ vertex= SETelemt_(facet->vertices, neighbor_i++, vertexT);
+ vertex->visitid= qh vertex_visit;
+ zzinc_(Zdistzero);
+ qh_distplane(vertex->point, neighbor, &dist);
+ if (dist >= -qh DISTround) {
+ qh ZEROall_ok= False;
+ if (!qh MERGEexact || testall || dist > qh DISTround)
+ goto LABELnonconvex;
+ }
+ }
+ if (!testall && horizon) {
+ FOREACHvertex_(horizon->vertices) {
+ if (vertex->visitid != qh vertex_visit) {
+ zzinc_(Zdistzero);
+ qh_distplane(vertex->point, facet, &dist);
+ if (dist >= -qh DISTround) {
+ qh ZEROall_ok= False;
+ if (!qh MERGEexact || dist > qh DISTround)
+ goto LABELnonconvex;
+ }
+ break;
+ }
+ }
+ }
+ }
+ trace2((qh ferr, 2012, "qh_checkzero: testall %d, facets are %s\n", testall,
+ (qh MERGEexact && !testall) ?
+ "not concave, flipped, or duplicate ridged" : "clearly convex"));
+ return True;
+
+ LABELproblem:
+ qh ZEROall_ok= False;
+ trace2((qh ferr, 2013, "qh_checkzero: facet f%d needs pre-merging\n",
+ facet->id));
+ return False;
+
+ LABELnonconvex:
+ trace2((qh ferr, 2014, "qh_checkzero: facet f%d and f%d are not clearly convex. v%d dist %.2g\n",
+ facet->id, neighbor->id, vertex->id, dist));
+ return False;
+} /* checkzero */
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="compareangle">-</a>
+
+ qh_compareangle( angle1, angle2 )
+ used by qsort() to order merges by angle
+*/
+int qh_compareangle(const void *p1, const void *p2) {
+ const mergeT *a= *((mergeT *const*)p1), *b= *((mergeT *const*)p2);
+
+ return((a->angle > b->angle) ? 1 : -1);
+} /* compareangle */
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="comparemerge">-</a>
+
+ qh_comparemerge( merge1, merge2 )
+ used by qsort() to order merges
+*/
+int qh_comparemerge(const void *p1, const void *p2) {
+ const mergeT *a= *((mergeT *const*)p1), *b= *((mergeT *const*)p2);
+
+ return(a->type - b->type);
+} /* comparemerge */
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="comparevisit">-</a>
+
+ qh_comparevisit( vertex1, vertex2 )
+ used by qsort() to order vertices by their visitid
+*/
+int qh_comparevisit(const void *p1, const void *p2) {
+ const vertexT *a= *((vertexT *const*)p1), *b= *((vertexT *const*)p2);
+
+ return(a->visitid - b->visitid);
+} /* comparevisit */
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="copynonconvex">-</a>
+
+ qh_copynonconvex( atridge )
+ set non-convex flag on other ridges (if any) between same neighbors
+
+ notes:
+ may be faster if use smaller ridge set
+
+ design:
+ for each ridge of atridge's top facet
+ if ridge shares the same neighbor
+ set nonconvex flag
+*/
+void qh_copynonconvex(ridgeT *atridge) {
+ facetT *facet, *otherfacet;
+ ridgeT *ridge, **ridgep;
+
+ facet= atridge->top;
+ otherfacet= atridge->bottom;
+ FOREACHridge_(facet->ridges) {
+ if (otherfacet == otherfacet_(ridge, facet) && ridge != atridge) {
+ ridge->nonconvex= True;
+ trace4((qh ferr, 4020, "qh_copynonconvex: moved nonconvex flag from r%d to r%d\n",
+ atridge->id, ridge->id));
+ break;
+ }
+ }
+} /* copynonconvex */
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="degen_redundant_facet">-</a>
+
+ qh_degen_redundant_facet( facet )
+ check facet for degen. or redundancy
+
+ notes:
+ bumps vertex_visit
+ called if a facet was redundant but no longer is (qh_merge_degenredundant)
+ qh_appendmergeset() only appends first reference to facet (i.e., redundant)
+
+ see:
+ qh_degen_redundant_neighbors()
+
+ design:
+ test for redundant neighbor
+ test for degenerate facet
+*/
+void qh_degen_redundant_facet(facetT *facet) {
+ vertexT *vertex, **vertexp;
+ facetT *neighbor, **neighborp;
+
+ trace4((qh ferr, 4021, "qh_degen_redundant_facet: test facet f%d for degen/redundant\n",
+ facet->id));
+ FOREACHneighbor_(facet) {
+ qh vertex_visit++;
+ FOREACHvertex_(neighbor->vertices)
+ vertex->visitid= qh vertex_visit;
+ FOREACHvertex_(facet->vertices) {
+ if (vertex->visitid != qh vertex_visit)
+ break;
+ }
+ if (!vertex) {
+ qh_appendmergeset(facet, neighbor, MRGredundant, NULL);
+ trace2((qh ferr, 2015, "qh_degen_redundant_facet: f%d is contained in f%d. merge\n", facet->id, neighbor->id));
+ return;
+ }
+ }
+ if (qh_setsize(facet->neighbors) < qh hull_dim) {
+ qh_appendmergeset(facet, facet, MRGdegen, NULL);
+ trace2((qh ferr, 2016, "qh_degen_redundant_neighbors: f%d is degenerate.\n", facet->id));
+ }
+} /* degen_redundant_facet */
+
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="degen_redundant_neighbors">-</a>
+
+ qh_degen_redundant_neighbors( facet, delfacet, )
+ append degenerate and redundant neighbors to facet_mergeset
+ if delfacet,
+ only checks neighbors of both delfacet and facet
+ also checks current facet for degeneracy
+
+ notes:
+ bumps vertex_visit
+ called for each qh_mergefacet() and qh_mergecycle()
+ merge and statistics occur in merge_nonconvex
+ qh_appendmergeset() only appends first reference to facet (i.e., redundant)
+ it appends redundant facets after degenerate ones
+
+ a degenerate facet has fewer than hull_dim neighbors
+ a redundant facet's vertices is a subset of its neighbor's vertices
+ tests for redundant merges first (appendmergeset is nop for others)
+ in a merge, only needs to test neighbors of merged facet
+
+ see:
+ qh_merge_degenredundant() and qh_degen_redundant_facet()
+
+ design:
+ test for degenerate facet
+ test for redundant neighbor
+ test for degenerate neighbor
+*/
+void qh_degen_redundant_neighbors(facetT *facet, facetT *delfacet) {
+ vertexT *vertex, **vertexp;
+ facetT *neighbor, **neighborp;
+ int size;
+
+ trace4((qh ferr, 4022, "qh_degen_redundant_neighbors: test neighbors of f%d with delfacet f%d\n",
+ facet->id, getid_(delfacet)));
+ if ((size= qh_setsize(facet->neighbors)) < qh hull_dim) {
+ qh_appendmergeset(facet, facet, MRGdegen, NULL);
+ trace2((qh ferr, 2017, "qh_degen_redundant_neighbors: f%d is degenerate with %d neighbors.\n", facet->id, size));
+ }
+ if (!delfacet)
+ delfacet= facet;
+ qh vertex_visit++;
+ FOREACHvertex_(facet->vertices)
+ vertex->visitid= qh vertex_visit;
+ FOREACHneighbor_(delfacet) {
+ /* uses early out instead of checking vertex count */
+ if (neighbor == facet)
+ continue;
+ FOREACHvertex_(neighbor->vertices) {
+ if (vertex->visitid != qh vertex_visit)
+ break;
+ }
+ if (!vertex) {
+ qh_appendmergeset(neighbor, facet, MRGredundant, NULL);
+ trace2((qh ferr, 2018, "qh_degen_redundant_neighbors: f%d is contained in f%d. merge\n", neighbor->id, facet->id));
+ }
+ }
+ FOREACHneighbor_(delfacet) { /* redundant merges occur first */
+ if (neighbor == facet)
+ continue;
+ if ((size= qh_setsize(neighbor->neighbors)) < qh hull_dim) {
+ qh_appendmergeset(neighbor, neighbor, MRGdegen, NULL);
+ trace2((qh ferr, 2019, "qh_degen_redundant_neighbors: f%d is degenerate with %d neighbors. Neighbor of f%d.\n", neighbor->id, size, facet->id));
+ }
+ }
+} /* degen_redundant_neighbors */
+
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="find_newvertex">-</a>
+
+ qh_find_newvertex( oldvertex, vertices, ridges )
+ locate new vertex for renaming old vertex
+ vertices is a set of possible new vertices
+ vertices sorted by number of deleted ridges
+
+ returns:
+ newvertex or NULL
+ each ridge includes both vertex and oldvertex
+ vertices sorted by number of deleted ridges
+
+ notes:
+ modifies vertex->visitid
+ new vertex is in one of the ridges
+ renaming will not cause a duplicate ridge
+ renaming will minimize the number of deleted ridges
+ newvertex may not be adjacent in the dual (though unlikely)
+
+ design:
+ for each vertex in vertices
+ set vertex->visitid to number of references in ridges
+ remove unvisited vertices
+ set qh.vertex_visit above all possible values
+ sort vertices by number of references in ridges
+ add each ridge to qh.hash_table
+ for each vertex in vertices
+ look for a vertex that would not cause a duplicate ridge after a rename
+*/
+vertexT *qh_find_newvertex(vertexT *oldvertex, setT *vertices, setT *ridges) {
+ vertexT *vertex, **vertexp;
+ setT *newridges;
+ ridgeT *ridge, **ridgep;
+ int size, hashsize;
+ int hash;
+
+#ifndef qh_NOtrace
+ if (qh IStracing >= 4) {
+ qh_fprintf(qh ferr, 8063, "qh_find_newvertex: find new vertex for v%d from ",
+ oldvertex->id);
+ FOREACHvertex_(vertices)
+ qh_fprintf(qh ferr, 8064, "v%d ", vertex->id);
+ FOREACHridge_(ridges)
+ qh_fprintf(qh ferr, 8065, "r%d ", ridge->id);
+ qh_fprintf(qh ferr, 8066, "\n");
+ }
+#endif
+ FOREACHvertex_(vertices)
+ vertex->visitid= 0;
+ FOREACHridge_(ridges) {
+ FOREACHvertex_(ridge->vertices)
+ vertex->visitid++;
+ }
+ FOREACHvertex_(vertices) {
+ if (!vertex->visitid) {
+ qh_setdelnth(vertices, SETindex_(vertices,vertex));
+ vertexp--; /* repeat since deleted this vertex */
+ }
+ }
+ qh vertex_visit += (unsigned int)qh_setsize(ridges);
+ if (!qh_setsize(vertices)) {
+ trace4((qh ferr, 4023, "qh_find_newvertex: vertices not in ridges for v%d\n",
+ oldvertex->id));
+ return NULL;
+ }
+ qsort(SETaddr_(vertices, vertexT), (size_t)qh_setsize(vertices),
+ sizeof(vertexT *), qh_comparevisit);
+ /* can now use qh vertex_visit */
+ if (qh PRINTstatistics) {
+ size= qh_setsize(vertices);
+ zinc_(Zintersect);
+ zadd_(Zintersecttot, size);
+ zmax_(Zintersectmax, size);
+ }
+ hashsize= qh_newhashtable(qh_setsize(ridges));
+ FOREACHridge_(ridges)
+ qh_hashridge(qh hash_table, hashsize, ridge, oldvertex);
+ FOREACHvertex_(vertices) {
+ newridges= qh_vertexridges(vertex);
+ FOREACHridge_(newridges) {
+ if (qh_hashridge_find(qh hash_table, hashsize, ridge, vertex, oldvertex, &hash)) {
+ zinc_(Zdupridge);
+ break;
+ }
+ }
+ qh_settempfree(&newridges);
+ if (!ridge)
+ break; /* found a rename */
+ }
+ if (vertex) {
+ /* counted in qh_renamevertex */
+ trace2((qh ferr, 2020, "qh_find_newvertex: found v%d for old v%d from %d vertices and %d ridges.\n",
+ vertex->id, oldvertex->id, qh_setsize(vertices), qh_setsize(ridges)));
+ }else {
+ zinc_(Zfindfail);
+ trace0((qh ferr, 14, "qh_find_newvertex: no vertex for renaming v%d(all duplicated ridges) during p%d\n",
+ oldvertex->id, qh furthest_id));
+ }
+ qh_setfree(&qh hash_table);
+ return vertex;
+} /* find_newvertex */
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="findbest_test">-</a>
+
+ qh_findbest_test( testcentrum, facet, neighbor, bestfacet, dist, mindist, maxdist )
+ test neighbor of facet for qh_findbestneighbor()
+ if testcentrum,
+ tests centrum (assumes it is defined)
+ else
+ tests vertices
+
+ returns:
+ if a better facet (i.e., vertices/centrum of facet closer to neighbor)
+ updates bestfacet, dist, mindist, and maxdist
+*/
+void qh_findbest_test(boolT testcentrum, facetT *facet, facetT *neighbor,
+ facetT **bestfacet, realT *distp, realT *mindistp, realT *maxdistp) {
+ realT dist, mindist, maxdist;
+
+ if (testcentrum) {
+ zzinc_(Zbestdist);
+ qh_distplane(facet->center, neighbor, &dist);
+ dist *= qh hull_dim; /* estimate furthest vertex */
+ if (dist < 0) {
+ maxdist= 0;
+ mindist= dist;
+ dist= -dist;
+ }else {
+ mindist= 0;
+ maxdist= dist;
+ }
+ }else
+ dist= qh_getdistance(facet, neighbor, &mindist, &maxdist);
+ if (dist < *distp) {
+ *bestfacet= neighbor;
+ *mindistp= mindist;
+ *maxdistp= maxdist;
+ *distp= dist;
+ }
+} /* findbest_test */
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="findbestneighbor">-</a>
+
+ qh_findbestneighbor( facet, dist, mindist, maxdist )
+ finds best neighbor (least dist) of a facet for merging
+
+ returns:
+ returns min and max distances and their max absolute value
+
+ notes:
+ error if qh_ASvoronoi
+ avoids merging old into new
+ assumes ridge->nonconvex only set on one ridge between a pair of facets
+ could use an early out predicate but not worth it
+
+ design:
+ if a large facet
+ will test centrum
+ else
+ will test vertices
+ if a large facet
+ test nonconvex neighbors for best merge
+ else
+ test all neighbors for the best merge
+ if testing centrum
+ get distance information
+*/
+facetT *qh_findbestneighbor(facetT *facet, realT *distp, realT *mindistp, realT *maxdistp) {
+ facetT *neighbor, **neighborp, *bestfacet= NULL;
+ ridgeT *ridge, **ridgep;
+ boolT nonconvex= True, testcentrum= False;
+ int size= qh_setsize(facet->vertices);
+
+ if(qh CENTERtype==qh_ASvoronoi){
+ qh_fprintf(qh ferr, 6272, "qhull error: cannot call qh_findbestneighor for f%d while qh.CENTERtype is qh_ASvoronoi\n", facet->id);
+ qh_errexit(qh_ERRqhull, facet, NULL);
+ }
+ *distp= REALmax;
+ if (size > qh_BESTcentrum2 * qh hull_dim + qh_BESTcentrum) {
+ testcentrum= True;
+ zinc_(Zbestcentrum);
+ if (!facet->center)
+ facet->center= qh_getcentrum(facet);
+ }
+ if (size > qh hull_dim + qh_BESTnonconvex) {
+ FOREACHridge_(facet->ridges) {
+ if (ridge->nonconvex) {
+ neighbor= otherfacet_(ridge, facet);
+ qh_findbest_test(testcentrum, facet, neighbor,
+ &bestfacet, distp, mindistp, maxdistp);
+ }
+ }
+ }
+ if (!bestfacet) {
+ nonconvex= False;
+ FOREACHneighbor_(facet)
+ qh_findbest_test(testcentrum, facet, neighbor,
+ &bestfacet, distp, mindistp, maxdistp);
+ }
+ if (!bestfacet) {
+ qh_fprintf(qh ferr, 6095, "qhull internal error (qh_findbestneighbor): no neighbors for f%d\n", facet->id);
+
+ qh_errexit(qh_ERRqhull, facet, NULL);
+ }
+ if (testcentrum)
+ qh_getdistance(facet, bestfacet, mindistp, maxdistp);
+ trace3((qh ferr, 3002, "qh_findbestneighbor: f%d is best neighbor for f%d testcentrum? %d nonconvex? %d dist %2.2g min %2.2g max %2.2g\n",
+ bestfacet->id, facet->id, testcentrum, nonconvex, *distp, *mindistp, *maxdistp));
+ return(bestfacet);
+} /* findbestneighbor */
+
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="flippedmerges">-</a>
+
+ qh_flippedmerges( facetlist, wasmerge )
+ merge flipped facets into best neighbor
+ assumes qh.facet_mergeset at top of temporary stack
+
+ returns:
+ no flipped facets on facetlist
+ sets wasmerge if merge occurred
+ degen/redundant merges passed through
+
+ notes:
+ othermerges not needed since qh.facet_mergeset is empty before & after
+ keep it in case of change
+
+ design:
+ append flipped facets to qh.facetmergeset
+ for each flipped merge
+ find best neighbor
+ merge facet into neighbor
+ merge degenerate and redundant facets
+ remove flipped merges from qh.facet_mergeset
+*/
+void qh_flippedmerges(facetT *facetlist, boolT *wasmerge) {
+ facetT *facet, *neighbor, *facet1;
+ realT dist, mindist, maxdist;
+ mergeT *merge, **mergep;
+ setT *othermerges;
+ int nummerge=0;
+
+ trace4((qh ferr, 4024, "qh_flippedmerges: begin\n"));
+ FORALLfacet_(facetlist) {
+ if (facet->flipped && !facet->visible)
+ qh_appendmergeset(facet, facet, MRGflip, NULL);
+ }
+ othermerges= qh_settemppop(); /* was facet_mergeset */
+ qh facet_mergeset= qh_settemp(qh TEMPsize);
+ qh_settemppush(othermerges);
+ FOREACHmerge_(othermerges) {
+ facet1= merge->facet1;
+ if (merge->type != MRGflip || facet1->visible)
+ continue;
+ if (qh TRACEmerge-1 == zzval_(Ztotmerge))
+ qhmem.IStracing= qh IStracing= qh TRACElevel;
+ neighbor= qh_findbestneighbor(facet1, &dist, &mindist, &maxdist);
+ trace0((qh ferr, 15, "qh_flippedmerges: merge flipped f%d into f%d dist %2.2g during p%d\n",
+ facet1->id, neighbor->id, dist, qh furthest_id));
+ qh_mergefacet(facet1, neighbor, &mindist, &maxdist, !qh_MERGEapex);
+ nummerge++;
+ if (qh PRINTstatistics) {
+ zinc_(Zflipped);
+ wadd_(Wflippedtot, dist);
+ wmax_(Wflippedmax, dist);
+ }
+ qh_merge_degenredundant();
+ }
+ FOREACHmerge_(othermerges) {
+ if (merge->facet1->visible || merge->facet2->visible)
+ qh_memfree(merge, (int)sizeof(mergeT));
+ else
+ qh_setappend(&qh facet_mergeset, merge);
+ }
+ qh_settempfree(&othermerges);
+ if (nummerge)
+ *wasmerge= True;
+ trace1((qh ferr, 1010, "qh_flippedmerges: merged %d flipped facets into a good neighbor\n", nummerge));
+} /* flippedmerges */
+
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="forcedmerges">-</a>
+
+ qh_forcedmerges( wasmerge )
+ merge duplicated ridges
+
+ returns:
+ removes all duplicate ridges on facet_mergeset
+ wasmerge set if merge
+ qh.facet_mergeset may include non-forced merges(none for now)
+ qh.degen_mergeset includes degen/redun merges
+
+ notes:
+ duplicate ridges occur when the horizon is pinched,
+ i.e. a subridge occurs in more than two horizon ridges.
+ could rename vertices that pinch the horizon
+ assumes qh_merge_degenredundant() has not be called
+ othermerges isn't needed since facet_mergeset is empty afterwards
+ keep it in case of change
+
+ design:
+ for each duplicate ridge
+ find current facets by chasing f.replace links
+ check for wide merge due to duplicate ridge
+ determine best direction for facet
+ merge one facet into the other
+ remove duplicate ridges from qh.facet_mergeset
+*/
+void qh_forcedmerges(boolT *wasmerge) {
+ facetT *facet1, *facet2;
+ mergeT *merge, **mergep;
+ realT dist1, dist2, mindist1, mindist2, maxdist1, maxdist2;
+ setT *othermerges;
+ int nummerge=0, numflip=0;
+
+ if (qh TRACEmerge-1 == zzval_(Ztotmerge))
+ qhmem.IStracing= qh IStracing= qh TRACElevel;
+ trace4((qh ferr, 4025, "qh_forcedmerges: begin\n"));
+ othermerges= qh_settemppop(); /* was facet_mergeset */
+ qh facet_mergeset= qh_settemp(qh TEMPsize);
+ qh_settemppush(othermerges);
+ FOREACHmerge_(othermerges) {
+ if (merge->type != MRGridge)
+ continue;
+ if (qh TRACEmerge-1 == zzval_(Ztotmerge))
+ qhmem.IStracing= qh IStracing= qh TRACElevel;
+ facet1= merge->facet1;
+ facet2= merge->facet2;
+ while (facet1->visible) /* must exist, no qh_merge_degenredunant */
+ facet1= facet1->f.replace; /* previously merged facet */
+ while (facet2->visible)
+ facet2= facet2->f.replace; /* previously merged facet */
+ if (facet1 == facet2)
+ continue;
+ if (!qh_setin(facet2->neighbors, facet1)) {
+ qh_fprintf(qh ferr, 6096, "qhull internal error (qh_forcedmerges): f%d and f%d had a duplicate ridge but as f%d and f%d they are no longer neighbors\n",
+ merge->facet1->id, merge->facet2->id, facet1->id, facet2->id);
+ qh_errexit2(qh_ERRqhull, facet1, facet2);
+ }
+ dist1= qh_getdistance(facet1, facet2, &mindist1, &maxdist1);
+ dist2= qh_getdistance(facet2, facet1, &mindist2, &maxdist2);
+ qh_check_dupridge(facet1, dist1, facet2, dist2);
+ if (dist1 < dist2)
+ qh_mergefacet(facet1, facet2, &mindist1, &maxdist1, !qh_MERGEapex);
+ else {
+ qh_mergefacet(facet2, facet1, &mindist2, &maxdist2, !qh_MERGEapex);
+ dist1= dist2;
+ facet1= facet2;
+ }
+ if (facet1->flipped) {
+ zinc_(Zmergeflipdup);
+ numflip++;
+ }else
+ nummerge++;
+ if (qh PRINTstatistics) {
+ zinc_(Zduplicate);
+ wadd_(Wduplicatetot, dist1);
+ wmax_(Wduplicatemax, dist1);
+ }
+ }
+ FOREACHmerge_(othermerges) {
+ if (merge->type == MRGridge)
+ qh_memfree(merge, (int)sizeof(mergeT));
+ else
+ qh_setappend(&qh facet_mergeset, merge);
+ }
+ qh_settempfree(&othermerges);
+ if (nummerge)
+ *wasmerge= True;
+ trace1((qh ferr, 1011, "qh_forcedmerges: merged %d facets and %d flipped facets across duplicated ridges\n",
+ nummerge, numflip));
+} /* forcedmerges */
+
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="getmergeset">-</a>
+
+ qh_getmergeset( facetlist )
+ determines nonconvex facets on facetlist
+ tests !tested ridges and nonconvex ridges of !tested facets
+
+ returns:
+ returns sorted qh.facet_mergeset of facet-neighbor pairs to be merged
+ all ridges tested
+
+ notes:
+ assumes no nonconvex ridges with both facets tested
+ uses facet->tested/ridge->tested to prevent duplicate tests
+ can not limit tests to modified ridges since the centrum changed
+ uses qh.visit_id
+
+ see:
+ qh_getmergeset_initial()
+
+ design:
+ for each facet on facetlist
+ for each ridge of facet
+ if untested ridge
+ test ridge for convexity
+ if non-convex
+ append ridge to qh.facet_mergeset
+ sort qh.facet_mergeset by angle
+*/
+void qh_getmergeset(facetT *facetlist) {
+ facetT *facet, *neighbor, **neighborp;
+ ridgeT *ridge, **ridgep;
+ int nummerges;
+
+ nummerges= qh_setsize(qh facet_mergeset);
+ trace4((qh ferr, 4026, "qh_getmergeset: started.\n"));
+ qh visit_id++;
+ FORALLfacet_(facetlist) {
+ if (facet->tested)
+ continue;
+ facet->visitid= qh visit_id;
+ facet->tested= True; /* must be non-simplicial due to merge */
+ FOREACHneighbor_(facet)
+ neighbor->seen= False;
+ FOREACHridge_(facet->ridges) {
+ if (ridge->tested && !ridge->nonconvex)
+ continue;
+ /* if tested & nonconvex, need to append merge */
+ neighbor= otherfacet_(ridge, facet);
+ if (neighbor->seen) {
+ ridge->tested= True;
+ ridge->nonconvex= False;
+ }else if (neighbor->visitid != qh visit_id) {
+ ridge->tested= True;
+ ridge->nonconvex= False;
+ neighbor->seen= True; /* only one ridge is marked nonconvex */
+ if (qh_test_appendmerge(facet, neighbor))
+ ridge->nonconvex= True;
+ }
+ }
+ }
+ nummerges= qh_setsize(qh facet_mergeset);
+ if (qh ANGLEmerge)
+ qsort(SETaddr_(qh facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_compareangle);
+ else
+ qsort(SETaddr_(qh facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_comparemerge);
+ if (qh POSTmerging) {
+ zadd_(Zmergesettot2, nummerges);
+ }else {
+ zadd_(Zmergesettot, nummerges);
+ zmax_(Zmergesetmax, nummerges);
+ }
+ trace2((qh ferr, 2021, "qh_getmergeset: %d merges found\n", nummerges));
+} /* getmergeset */
+
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="getmergeset_initial">-</a>
+
+ qh_getmergeset_initial( facetlist )
+ determine initial qh.facet_mergeset for facets
+ tests all facet/neighbor pairs on facetlist
+
+ returns:
+ sorted qh.facet_mergeset with nonconvex ridges
+ sets facet->tested, ridge->tested, and ridge->nonconvex
+
+ notes:
+ uses visit_id, assumes ridge->nonconvex is False
+
+ see:
+ qh_getmergeset()
+
+ design:
+ for each facet on facetlist
+ for each untested neighbor of facet
+ test facet and neighbor for convexity
+ if non-convex
+ append merge to qh.facet_mergeset
+ mark one of the ridges as nonconvex
+ sort qh.facet_mergeset by angle
+*/
+void qh_getmergeset_initial(facetT *facetlist) {
+ facetT *facet, *neighbor, **neighborp;
+ ridgeT *ridge, **ridgep;
+ int nummerges;
+
+ qh visit_id++;
+ FORALLfacet_(facetlist) {
+ facet->visitid= qh visit_id;
+ facet->tested= True;
+ FOREACHneighbor_(facet) {
+ if (neighbor->visitid != qh visit_id) {
+ if (qh_test_appendmerge(facet, neighbor)) {
+ FOREACHridge_(neighbor->ridges) {
+ if (facet == otherfacet_(ridge, neighbor)) {
+ ridge->nonconvex= True;
+ break; /* only one ridge is marked nonconvex */
+ }
+ }
+ }
+ }
+ }
+ FOREACHridge_(facet->ridges)
+ ridge->tested= True;
+ }
+ nummerges= qh_setsize(qh facet_mergeset);
+ if (qh ANGLEmerge)
+ qsort(SETaddr_(qh facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_compareangle);
+ else
+ qsort(SETaddr_(qh facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_comparemerge);
+ if (qh POSTmerging) {
+ zadd_(Zmergeinittot2, nummerges);
+ }else {
+ zadd_(Zmergeinittot, nummerges);
+ zmax_(Zmergeinitmax, nummerges);
+ }
+ trace2((qh ferr, 2022, "qh_getmergeset_initial: %d merges found\n", nummerges));
+} /* getmergeset_initial */
+
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="hashridge">-</a>
+
+ qh_hashridge( hashtable, hashsize, ridge, oldvertex )
+ add ridge to hashtable without oldvertex
+
+ notes:
+ assumes hashtable is large enough
+
+ design:
+ determine hash value for ridge without oldvertex
+ find next empty slot for ridge
+*/
+void qh_hashridge(setT *hashtable, int hashsize, ridgeT *ridge, vertexT *oldvertex) {
+ int hash;
+ ridgeT *ridgeA;
+
+ hash= qh_gethash(hashsize, ridge->vertices, qh hull_dim-1, 0, oldvertex);
+ while (True) {
+ if (!(ridgeA= SETelemt_(hashtable, hash, ridgeT))) {
+ SETelem_(hashtable, hash)= ridge;
+ break;
+ }else if (ridgeA == ridge)
+ break;
+ if (++hash == hashsize)
+ hash= 0;
+ }
+} /* hashridge */
+
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="hashridge_find">-</a>
+
+ qh_hashridge_find( hashtable, hashsize, ridge, vertex, oldvertex, hashslot )
+ returns matching ridge without oldvertex in hashtable
+ for ridge without vertex
+ if oldvertex is NULL
+ matches with any one skip
+
+ returns:
+ matching ridge or NULL
+ if no match,
+ if ridge already in table
+ hashslot= -1
+ else
+ hashslot= next NULL index
+
+ notes:
+ assumes hashtable is large enough
+ can't match ridge to itself
+
+ design:
+ get hash value for ridge without vertex
+ for each hashslot
+ return match if ridge matches ridgeA without oldvertex
+*/
+ridgeT *qh_hashridge_find(setT *hashtable, int hashsize, ridgeT *ridge,
+ vertexT *vertex, vertexT *oldvertex, int *hashslot) {
+ int hash;
+ ridgeT *ridgeA;
+
+ *hashslot= 0;
+ zinc_(Zhashridge);
+ hash= qh_gethash(hashsize, ridge->vertices, qh hull_dim-1, 0, vertex);
+ while ((ridgeA= SETelemt_(hashtable, hash, ridgeT))) {
+ if (ridgeA == ridge)
+ *hashslot= -1;
+ else {
+ zinc_(Zhashridgetest);
+ if (qh_setequal_except(ridge->vertices, vertex, ridgeA->vertices, oldvertex))
+ return ridgeA;
+ }
+ if (++hash == hashsize)
+ hash= 0;
+ }
+ if (!*hashslot)
+ *hashslot= hash;
+ return NULL;
+} /* hashridge_find */
+
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="makeridges">-</a>
+
+ qh_makeridges( facet )
+ creates explicit ridges between simplicial facets
+
+ returns:
+ facet with ridges and without qh_MERGEridge
+ ->simplicial is False
+
+ notes:
+ allows qh_MERGEridge flag
+ uses existing ridges
+ duplicate neighbors ok if ridges already exist (qh_mergecycle_ridges)
+
+ see:
+ qh_mergecycle_ridges()
+
+ design:
+ look for qh_MERGEridge neighbors
+ mark neighbors that already have ridges
+ for each unprocessed neighbor of facet
+ create a ridge for neighbor and facet
+ if any qh_MERGEridge neighbors
+ delete qh_MERGEridge flags (already handled by qh_mark_dupridges)
+*/
+void qh_makeridges(facetT *facet) {
+ facetT *neighbor, **neighborp;
+ ridgeT *ridge, **ridgep;
+ int neighbor_i, neighbor_n;
+ boolT toporient, mergeridge= False;
+
+ if (!facet->simplicial)
+ return;
+ trace4((qh ferr, 4027, "qh_makeridges: make ridges for f%d\n", facet->id));
+ facet->simplicial= False;
+ FOREACHneighbor_(facet) {
+ if (neighbor == qh_MERGEridge)
+ mergeridge= True;
+ else
+ neighbor->seen= False;
+ }
+ FOREACHridge_(facet->ridges)
+ otherfacet_(ridge, facet)->seen= True;
+ FOREACHneighbor_i_(facet) {
+ if (neighbor == qh_MERGEridge)
+ continue; /* fixed by qh_mark_dupridges */
+ else if (!neighbor->seen) { /* no current ridges */
+ ridge= qh_newridge();
+ ridge->vertices= qh_setnew_delnthsorted(facet->vertices, qh hull_dim,
+ neighbor_i, 0);
+ toporient= facet->toporient ^ (neighbor_i & 0x1);
+ if (toporient) {
+ ridge->top= facet;
+ ridge->bottom= neighbor;
+ }else {
+ ridge->top= neighbor;
+ ridge->bottom= facet;
+ }
+#if 0 /* this also works */
+ flip= (facet->toporient ^ neighbor->toporient)^(skip1 & 0x1) ^ (skip2 & 0x1);
+ if (facet->toporient ^ (skip1 & 0x1) ^ flip) {
+ ridge->top= neighbor;
+ ridge->bottom= facet;
+ }else {
+ ridge->top= facet;
+ ridge->bottom= neighbor;
+ }
+#endif
+ qh_setappend(&(facet->ridges), ridge);
+ qh_setappend(&(neighbor->ridges), ridge);
+ }
+ }
+ if (mergeridge) {
+ while (qh_setdel(facet->neighbors, qh_MERGEridge))
+ ; /* delete each one */
+ }
+} /* makeridges */
+
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="mark_dupridges">-</a>
+
+ qh_mark_dupridges( facetlist )
+ add duplicated ridges to qh.facet_mergeset
+ facet->dupridge is true
+
+ returns:
+ duplicate ridges on qh.facet_mergeset
+ ->mergeridge/->mergeridge2 set
+ duplicate ridges marked by qh_MERGEridge and both sides facet->dupridge
+ no MERGEridges in neighbor sets
+
+ notes:
+ duplicate ridges occur when the horizon is pinched,
+ i.e. a subridge occurs in more than two horizon ridges.
+ could rename vertices that pinch the horizon (thus removing subridge)
+ uses qh.visit_id
+
+ design:
+ for all facets on facetlist
+ if facet contains a duplicate ridge
+ for each neighbor of facet
+ if neighbor marked qh_MERGEridge (one side of the merge)
+ set facet->mergeridge
+ else
+ if neighbor contains a duplicate ridge
+ and the back link is qh_MERGEridge
+ append duplicate ridge to qh.facet_mergeset
+ for each duplicate ridge
+ make ridge sets in preparation for merging
+ remove qh_MERGEridge from neighbor set
+ for each duplicate ridge
+ restore the missing neighbor from the neighbor set that was qh_MERGEridge
+ add the missing ridge for this neighbor
+*/
+void qh_mark_dupridges(facetT *facetlist) {
+ facetT *facet, *neighbor, **neighborp;
+ int nummerge=0;
+ mergeT *merge, **mergep;
+
+
+ trace4((qh ferr, 4028, "qh_mark_dupridges: identify duplicate ridges\n"));
+ FORALLfacet_(facetlist) {
+ if (facet->dupridge) {
+ FOREACHneighbor_(facet) {
+ if (neighbor == qh_MERGEridge) {
+ facet->mergeridge= True;
+ continue;
+ }
+ if (neighbor->dupridge
+ && !qh_setin(neighbor->neighbors, facet)) { /* qh_MERGEridge */
+ qh_appendmergeset(facet, neighbor, MRGridge, NULL);
+ facet->mergeridge2= True;
+ facet->mergeridge= True;
+ nummerge++;
+ }
+ }
+ }
+ }
+ if (!nummerge)
+ return;
+ FORALLfacet_(facetlist) { /* gets rid of qh_MERGEridge */
+ if (facet->mergeridge && !facet->mergeridge2)
+ qh_makeridges(facet);
+ }
+ FOREACHmerge_(qh facet_mergeset) { /* restore the missing neighbors */
+ if (merge->type == MRGridge) {
+ qh_setappend(&merge->facet2->neighbors, merge->facet1);
+ qh_makeridges(merge->facet1); /* and the missing ridges */
+ }
+ }
+ trace1((qh ferr, 1012, "qh_mark_dupridges: found %d duplicated ridges\n",
+ nummerge));
+} /* mark_dupridges */
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="maydropneighbor">-</a>
+
+ qh_maydropneighbor( facet )
+ drop neighbor relationship if no ridge between facet and neighbor
+
+ returns:
+ neighbor sets updated
+ appends degenerate facets to qh.facet_mergeset
+
+ notes:
+ won't cause redundant facets since vertex inclusion is the same
+ may drop vertex and neighbor if no ridge
+ uses qh.visit_id
+
+ design:
+ visit all neighbors with ridges
+ for each unvisited neighbor of facet
+ delete neighbor and facet from the neighbor sets
+ if neighbor becomes degenerate
+ append neighbor to qh.degen_mergeset
+ if facet is degenerate
+ append facet to qh.degen_mergeset
+*/
+void qh_maydropneighbor(facetT *facet) {
+ ridgeT *ridge, **ridgep;
+ realT angledegen= qh_ANGLEdegen;
+ facetT *neighbor, **neighborp;
+
+ qh visit_id++;
+ trace4((qh ferr, 4029, "qh_maydropneighbor: test f%d for no ridges to a neighbor\n",
+ facet->id));
+ FOREACHridge_(facet->ridges) {
+ ridge->top->visitid= qh visit_id;
+ ridge->bottom->visitid= qh visit_id;
+ }
+ FOREACHneighbor_(facet) {
+ if (neighbor->visitid != qh visit_id) {
+ trace0((qh ferr, 17, "qh_maydropneighbor: facets f%d and f%d are no longer neighbors during p%d\n",
+ facet->id, neighbor->id, qh furthest_id));
+ zinc_(Zdropneighbor);
+ qh_setdel(facet->neighbors, neighbor);
+ neighborp--; /* repeat, deleted a neighbor */
+ qh_setdel(neighbor->neighbors, facet);
+ if (qh_setsize(neighbor->neighbors) < qh hull_dim) {
+ zinc_(Zdropdegen);
+ qh_appendmergeset(neighbor, neighbor, MRGdegen, &angledegen);
+ trace2((qh ferr, 2023, "qh_maydropneighbors: f%d is degenerate.\n", neighbor->id));
+ }
+ }
+ }
+ if (qh_setsize(facet->neighbors) < qh hull_dim) {
+ zinc_(Zdropdegen);
+ qh_appendmergeset(facet, facet, MRGdegen, &angledegen);
+ trace2((qh ferr, 2024, "qh_maydropneighbors: f%d is degenerate.\n", facet->id));
+ }
+} /* maydropneighbor */
+
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="merge_degenredundant">-</a>
+
+ qh_merge_degenredundant()
+ merge all degenerate and redundant facets
+ qh.degen_mergeset contains merges from qh_degen_redundant_neighbors()
+
+ returns:
+ number of merges performed
+ resets facet->degenerate/redundant
+ if deleted (visible) facet has no neighbors
+ sets ->f.replace to NULL
+
+ notes:
+ redundant merges happen before degenerate ones
+ merging and renaming vertices can result in degen/redundant facets
+
+ design:
+ for each merge on qh.degen_mergeset
+ if redundant merge
+ if non-redundant facet merged into redundant facet
+ recheck facet for redundancy
+ else
+ merge redundant facet into other facet
+*/
+int qh_merge_degenredundant(void) {
+ int size;
+ mergeT *merge;
+ facetT *bestneighbor, *facet1, *facet2;
+ realT dist, mindist, maxdist;
+ vertexT *vertex, **vertexp;
+ int nummerges= 0;
+ mergeType mergetype;
+
+ while ((merge= (mergeT*)qh_setdellast(qh degen_mergeset))) {
+ facet1= merge->facet1;
+ facet2= merge->facet2;
+ mergetype= merge->type;
+ qh_memfree(merge, (int)sizeof(mergeT));
+ if (facet1->visible)
+ continue;
+ facet1->degenerate= False;
+ facet1->redundant= False;
+ if (qh TRACEmerge-1 == zzval_(Ztotmerge))
+ qhmem.IStracing= qh IStracing= qh TRACElevel;
+ if (mergetype == MRGredundant) {
+ zinc_(Zneighbor);
+ while (facet2->visible) {
+ if (!facet2->f.replace) {
+ qh_fprintf(qh ferr, 6097, "qhull internal error (qh_merge_degenredunant): f%d redundant but f%d has no replacement\n",
+ facet1->id, facet2->id);
+ qh_errexit2(qh_ERRqhull, facet1, facet2);
+ }
+ facet2= facet2->f.replace;
+ }
+ if (facet1 == facet2) {
+ qh_degen_redundant_facet(facet1); /* in case of others */
+ continue;
+ }
+ trace2((qh ferr, 2025, "qh_merge_degenredundant: facet f%d is contained in f%d, will merge\n",
+ facet1->id, facet2->id));
+ qh_mergefacet(facet1, facet2, NULL, NULL, !qh_MERGEapex);
+ /* merge distance is already accounted for */
+ nummerges++;
+ }else { /* mergetype == MRGdegen, other merges may have fixed */
+ if (!(size= qh_setsize(facet1->neighbors))) {
+ zinc_(Zdelfacetdup);
+ trace2((qh ferr, 2026, "qh_merge_degenredundant: facet f%d has no neighbors. Deleted\n", facet1->id));
+ qh_willdelete(facet1, NULL);
+ FOREACHvertex_(facet1->vertices) {
+ qh_setdel(vertex->neighbors, facet1);
+ if (!SETfirst_(vertex->neighbors)) {
+ zinc_(Zdegenvertex);
+ trace2((qh ferr, 2027, "qh_merge_degenredundant: deleted v%d because f%d has no neighbors\n",
+ vertex->id, facet1->id));
+ vertex->deleted= True;
+ qh_setappend(&qh del_vertices, vertex);
+ }
+ }
+ nummerges++;
+ }else if (size < qh hull_dim) {
+ bestneighbor= qh_findbestneighbor(facet1, &dist, &mindist, &maxdist);
+ trace2((qh ferr, 2028, "qh_merge_degenredundant: facet f%d has %d neighbors, merge into f%d dist %2.2g\n",
+ facet1->id, size, bestneighbor->id, dist));
+ qh_mergefacet(facet1, bestneighbor, &mindist, &maxdist, !qh_MERGEapex);
+ nummerges++;
+ if (qh PRINTstatistics) {
+ zinc_(Zdegen);
+ wadd_(Wdegentot, dist);
+ wmax_(Wdegenmax, dist);
+ }
+ } /* else, another merge fixed the degeneracy and redundancy tested */
+ }
+ }
+ return nummerges;
+} /* merge_degenredundant */
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="merge_nonconvex">-</a>
+
+ qh_merge_nonconvex( facet1, facet2, mergetype )
+ remove non-convex ridge between facet1 into facet2
+ mergetype gives why the facet's are non-convex
+
+ returns:
+ merges one of the facets into the best neighbor
+
+ design:
+ if one of the facets is a new facet
+ prefer merging new facet into old facet
+ find best neighbors for both facets
+ merge the nearest facet into its best neighbor
+ update the statistics
+*/
+void qh_merge_nonconvex(facetT *facet1, facetT *facet2, mergeType mergetype) {
+ facetT *bestfacet, *bestneighbor, *neighbor;
+ realT dist, dist2, mindist, mindist2, maxdist, maxdist2;
+
+ if (qh TRACEmerge-1 == zzval_(Ztotmerge))
+ qhmem.IStracing= qh IStracing= qh TRACElevel;
+ trace3((qh ferr, 3003, "qh_merge_nonconvex: merge #%d for f%d and f%d type %d\n",
+ zzval_(Ztotmerge) + 1, facet1->id, facet2->id, mergetype));
+ /* concave or coplanar */
+ if (!facet1->newfacet) {
+ bestfacet= facet2; /* avoid merging old facet if new is ok */
+ facet2= facet1;
+ facet1= bestfacet;
+ }else
+ bestfacet= facet1;
+ bestneighbor= qh_findbestneighbor(bestfacet, &dist, &mindist, &maxdist);
+ neighbor= qh_findbestneighbor(facet2, &dist2, &mindist2, &maxdist2);
+ if (dist < dist2) {
+ qh_mergefacet(bestfacet, bestneighbor, &mindist, &maxdist, !qh_MERGEapex);
+ }else if (qh AVOIDold && !facet2->newfacet
+ && ((mindist >= -qh MAXcoplanar && maxdist <= qh max_outside)
+ || dist * 1.5 < dist2)) {
+ zinc_(Zavoidold);
+ wadd_(Wavoidoldtot, dist);
+ wmax_(Wavoidoldmax, dist);
+ trace2((qh ferr, 2029, "qh_merge_nonconvex: avoid merging old facet f%d dist %2.2g. Use f%d dist %2.2g instead\n",
+ facet2->id, dist2, facet1->id, dist2));
+ qh_mergefacet(bestfacet, bestneighbor, &mindist, &maxdist, !qh_MERGEapex);
+ }else {
+ qh_mergefacet(facet2, neighbor, &mindist2, &maxdist2, !qh_MERGEapex);
+ dist= dist2;
+ }
+ if (qh PRINTstatistics) {
+ if (mergetype == MRGanglecoplanar) {
+ zinc_(Zacoplanar);
+ wadd_(Wacoplanartot, dist);
+ wmax_(Wacoplanarmax, dist);
+ }else if (mergetype == MRGconcave) {
+ zinc_(Zconcave);
+ wadd_(Wconcavetot, dist);
+ wmax_(Wconcavemax, dist);
+ }else { /* MRGcoplanar */
+ zinc_(Zcoplanar);
+ wadd_(Wcoplanartot, dist);
+ wmax_(Wcoplanarmax, dist);
+ }
+ }
+} /* merge_nonconvex */
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="mergecycle">-</a>
+
+ qh_mergecycle( samecycle, newfacet )
+ merge a cycle of facets starting at samecycle into a newfacet
+ newfacet is a horizon facet with ->normal
+ samecycle facets are simplicial from an apex
+
+ returns:
+ initializes vertex neighbors on first merge
+ samecycle deleted (placed on qh.visible_list)
+ newfacet at end of qh.facet_list
+ deleted vertices on qh.del_vertices
+
+ see:
+ qh_mergefacet()
+ called by qh_mergecycle_all() for multiple, same cycle facets
+
+ design:
+ make vertex neighbors if necessary
+ make ridges for newfacet
+ merge neighbor sets of samecycle into newfacet
+ merge ridges of samecycle into newfacet
+ merge vertex neighbors of samecycle into newfacet
+ make apex of samecycle the apex of newfacet
+ if newfacet wasn't a new facet
+ add its vertices to qh.newvertex_list
+ delete samecycle facets a make newfacet a newfacet
+*/
+void qh_mergecycle(facetT *samecycle, facetT *newfacet) {
+ int traceonce= False, tracerestore= 0;
+ vertexT *apex;
+#ifndef qh_NOtrace
+ facetT *same;
+#endif
+
+ if (newfacet->tricoplanar) {
+ if (!qh TRInormals) {
+ qh_fprintf(qh ferr, 6224, "Qhull internal error (qh_mergecycle): does not work for tricoplanar facets. Use option 'Q11'\n");
+ qh_errexit(qh_ERRqhull, newfacet, NULL);
+ }
+ newfacet->tricoplanar= False;
+ newfacet->keepcentrum= False;
+ }
+ if (!qh VERTEXneighbors)
+ qh_vertexneighbors();
+ zzinc_(Ztotmerge);
+ if (qh REPORTfreq2 && qh POSTmerging) {
+ if (zzval_(Ztotmerge) > qh mergereport + qh REPORTfreq2)
+ qh_tracemerging();
+ }
+#ifndef qh_NOtrace
+ if (qh TRACEmerge == zzval_(Ztotmerge))
+ qhmem.IStracing= qh IStracing= qh TRACElevel;
+ trace2((qh ferr, 2030, "qh_mergecycle: merge #%d for facets from cycle f%d into coplanar horizon f%d\n",
+ zzval_(Ztotmerge), samecycle->id, newfacet->id));
+ if (newfacet == qh tracefacet) {
+ tracerestore= qh IStracing;
+ qh IStracing= 4;
+ qh_fprintf(qh ferr, 8068, "qh_mergecycle: ========= trace merge %d of samecycle %d into trace f%d, furthest is p%d\n",
+ zzval_(Ztotmerge), samecycle->id, newfacet->id, qh furthest_id);
+ traceonce= True;
+ }
+ if (qh IStracing >=4) {
+ qh_fprintf(qh ferr, 8069, " same cycle:");
+ FORALLsame_cycle_(samecycle)
+ qh_fprintf(qh ferr, 8070, " f%d", same->id);
+ qh_fprintf(qh ferr, 8071, "\n");
+ }
+ if (qh IStracing >=4)
+ qh_errprint("MERGING CYCLE", samecycle, newfacet, NULL, NULL);
+#endif /* !qh_NOtrace */
+ apex= SETfirstt_(samecycle->vertices, vertexT);
+ qh_makeridges(newfacet);
+ qh_mergecycle_neighbors(samecycle, newfacet);
+ qh_mergecycle_ridges(samecycle, newfacet);
+ qh_mergecycle_vneighbors(samecycle, newfacet);
+ if (SETfirstt_(newfacet->vertices, vertexT) != apex)
+ qh_setaddnth(&newfacet->vertices, 0, apex); /* apex has last id */
+ if (!newfacet->newfacet)
+ qh_newvertices(newfacet->vertices);
+ qh_mergecycle_facets(samecycle, newfacet);
+ qh_tracemerge(samecycle, newfacet);
+ /* check for degen_redundant_neighbors after qh_forcedmerges() */
+ if (traceonce) {
+ qh_fprintf(qh ferr, 8072, "qh_mergecycle: end of trace facet\n");
+ qh IStracing= tracerestore;
+ }
+} /* mergecycle */
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="mergecycle_all">-</a>
+
+ qh_mergecycle_all( facetlist, wasmerge )
+ merge all samecycles of coplanar facets into horizon
+ don't merge facets with ->mergeridge (these already have ->normal)
+ all facets are simplicial from apex
+ all facet->cycledone == False
+
+ returns:
+ all newfacets merged into coplanar horizon facets
+ deleted vertices on qh.del_vertices
+ sets wasmerge if any merge
+
+ see:
+ calls qh_mergecycle for multiple, same cycle facets
+
+ design:
+ for each facet on facetlist
+ skip facets with duplicate ridges and normals
+ check that facet is in a samecycle (->mergehorizon)
+ if facet only member of samecycle
+ sets vertex->delridge for all vertices except apex
+ merge facet into horizon
+ else
+ mark all facets in samecycle
+ remove facets with duplicate ridges from samecycle
+ merge samecycle into horizon (deletes facets from facetlist)
+*/
+void qh_mergecycle_all(facetT *facetlist, boolT *wasmerge) {
+ facetT *facet, *same, *prev, *horizon;
+ facetT *samecycle= NULL, *nextfacet, *nextsame;
+ vertexT *apex, *vertex, **vertexp;
+ int cycles=0, total=0, facets, nummerge;
+
+ trace2((qh ferr, 2031, "qh_mergecycle_all: begin\n"));
+ for (facet= facetlist; facet && (nextfacet= facet->next); facet= nextfacet) {
+ if (facet->normal)
+ continue;
+ if (!facet->mergehorizon) {
+ qh_fprintf(qh ferr, 6225, "Qhull internal error (qh_mergecycle_all): f%d without normal\n", facet->id);
+ qh_errexit(qh_ERRqhull, facet, NULL);
+ }
+ horizon= SETfirstt_(facet->neighbors, facetT);
+ if (facet->f.samecycle == facet) {
+ zinc_(Zonehorizon);
+ /* merge distance done in qh_findhorizon */
+ apex= SETfirstt_(facet->vertices, vertexT);
+ FOREACHvertex_(facet->vertices) {
+ if (vertex != apex)
+ vertex->delridge= True;
+ }
+ horizon->f.newcycle= NULL;
+ qh_mergefacet(facet, horizon, NULL, NULL, qh_MERGEapex);
+ }else {
+ samecycle= facet;
+ facets= 0;
+ prev= facet;
+ for (same= facet->f.samecycle; same; /* FORALLsame_cycle_(facet) */
+ same= (same == facet ? NULL :nextsame)) { /* ends at facet */
+ nextsame= same->f.samecycle;
+ if (same->cycledone || same->visible)
+ qh_infiniteloop(same);
+ same->cycledone= True;
+ if (same->normal) {
+ prev->f.samecycle= same->f.samecycle; /* unlink ->mergeridge */
+ same->f.samecycle= NULL;
+ }else {
+ prev= same;
+ facets++;
+ }
+ }
+ while (nextfacet && nextfacet->cycledone) /* will delete samecycle */
+ nextfacet= nextfacet->next;
+ horizon->f.newcycle= NULL;
+ qh_mergecycle(samecycle, horizon);
+ nummerge= horizon->nummerge + facets;
+ if (nummerge > qh_MAXnummerge)
+ horizon->nummerge= qh_MAXnummerge;
+ else
+ horizon->nummerge= (short unsigned int)nummerge;
+ zzinc_(Zcyclehorizon);
+ total += facets;
+ zzadd_(Zcyclefacettot, facets);
+ zmax_(Zcyclefacetmax, facets);
+ }
+ cycles++;
+ }
+ if (cycles)
+ *wasmerge= True;
+ trace1((qh ferr, 1013, "qh_mergecycle_all: merged %d same cycles or facets into coplanar horizons\n", cycles));
+} /* mergecycle_all */
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="mergecycle_facets">-</a>
+
+ qh_mergecycle_facets( samecycle, newfacet )
+ finish merge of samecycle into newfacet
+
+ returns:
+ samecycle prepended to visible_list for later deletion and partitioning
+ each facet->f.replace == newfacet
+
+ newfacet moved to end of qh.facet_list
+ makes newfacet a newfacet (get's facet1->id if it was old)
+ sets newfacet->newmerge
+ clears newfacet->center (unless merging into a large facet)
+ clears newfacet->tested and ridge->tested for facet1
+
+ adds neighboring facets to facet_mergeset if redundant or degenerate
+
+ design:
+ make newfacet a new facet and set its flags
+ move samecycle facets to qh.visible_list for later deletion
+ unless newfacet is large
+ remove its centrum
+*/
+void qh_mergecycle_facets(facetT *samecycle, facetT *newfacet) {
+ facetT *same, *next;
+
+ trace4((qh ferr, 4030, "qh_mergecycle_facets: make newfacet new and samecycle deleted\n"));
+ qh_removefacet(newfacet); /* append as a newfacet to end of qh facet_list */
+ qh_appendfacet(newfacet);
+ newfacet->newfacet= True;
+ newfacet->simplicial= False;
+ newfacet->newmerge= True;
+
+ for (same= samecycle->f.samecycle; same; same= (same == samecycle ? NULL : next)) {
+ next= same->f.samecycle; /* reused by willdelete */
+ qh_willdelete(same, newfacet);
+ }
+ if (newfacet->center
+ && qh_setsize(newfacet->vertices) <= qh hull_dim + qh_MAXnewcentrum) {
+ qh_memfree(newfacet->center, qh normal_size);
+ newfacet->center= NULL;
+ }
+ trace3((qh ferr, 3004, "qh_mergecycle_facets: merged facets from cycle f%d into f%d\n",
+ samecycle->id, newfacet->id));
+} /* mergecycle_facets */
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="mergecycle_neighbors">-</a>
+
+ qh_mergecycle_neighbors( samecycle, newfacet )
+ add neighbors for samecycle facets to newfacet
+
+ returns:
+ newfacet with updated neighbors and vice-versa
+ newfacet has ridges
+ all neighbors of newfacet marked with qh.visit_id
+ samecycle facets marked with qh.visit_id-1
+ ridges updated for simplicial neighbors of samecycle with a ridge
+
+ notes:
+ assumes newfacet not in samecycle
+ usually, samecycle facets are new, simplicial facets without internal ridges
+ not so if horizon facet is coplanar to two different samecycles
+
+ see:
+ qh_mergeneighbors()
+
+ design:
+ check samecycle
+ delete neighbors from newfacet that are also in samecycle
+ for each neighbor of a facet in samecycle
+ if neighbor is simplicial
+ if first visit
+ move the neighbor relation to newfacet
+ update facet links for its ridges
+ else
+ make ridges for neighbor
+ remove samecycle reference
+ else
+ update neighbor sets
+*/
+void qh_mergecycle_neighbors(facetT *samecycle, facetT *newfacet) {
+ facetT *same, *neighbor, **neighborp;
+ int delneighbors= 0, newneighbors= 0;
+ unsigned int samevisitid;
+ ridgeT *ridge, **ridgep;
+
+ samevisitid= ++qh visit_id;
+ FORALLsame_cycle_(samecycle) {
+ if (same->visitid == samevisitid || same->visible)
+ qh_infiniteloop(samecycle);
+ same->visitid= samevisitid;
+ }
+ newfacet->visitid= ++qh visit_id;
+ trace4((qh ferr, 4031, "qh_mergecycle_neighbors: delete shared neighbors from newfacet\n"));
+ FOREACHneighbor_(newfacet) {
+ if (neighbor->visitid == samevisitid) {
+ SETref_(neighbor)= NULL; /* samecycle neighbors deleted */
+ delneighbors++;
+ }else
+ neighbor->visitid= qh visit_id;
+ }
+ qh_setcompact(newfacet->neighbors);
+
+ trace4((qh ferr, 4032, "qh_mergecycle_neighbors: update neighbors\n"));
+ FORALLsame_cycle_(samecycle) {
+ FOREACHneighbor_(same) {
+ if (neighbor->visitid == samevisitid)
+ continue;
+ if (neighbor->simplicial) {
+ if (neighbor->visitid != qh visit_id) {
+ qh_setappend(&newfacet->neighbors, neighbor);
+ qh_setreplace(neighbor->neighbors, same, newfacet);
+ newneighbors++;
+ neighbor->visitid= qh visit_id;
+ FOREACHridge_(neighbor->ridges) { /* update ridge in case of qh_makeridges */
+ if (ridge->top == same) {
+ ridge->top= newfacet;
+ break;
+ }else if (ridge->bottom == same) {
+ ridge->bottom= newfacet;
+ break;
+ }
+ }
+ }else {
+ qh_makeridges(neighbor);
+ qh_setdel(neighbor->neighbors, same);
+ /* same can't be horizon facet for neighbor */
+ }
+ }else { /* non-simplicial neighbor */
+ qh_setdel(neighbor->neighbors, same);
+ if (neighbor->visitid != qh visit_id) {
+ qh_setappend(&neighbor->neighbors, newfacet);
+ qh_setappend(&newfacet->neighbors, neighbor);
+ neighbor->visitid= qh visit_id;
+ newneighbors++;
+ }
+ }
+ }
+ }
+ trace2((qh ferr, 2032, "qh_mergecycle_neighbors: deleted %d neighbors and added %d\n",
+ delneighbors, newneighbors));
+} /* mergecycle_neighbors */
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="mergecycle_ridges">-</a>
+
+ qh_mergecycle_ridges( samecycle, newfacet )
+ add ridges/neighbors for facets in samecycle to newfacet
+ all new/old neighbors of newfacet marked with qh.visit_id
+ facets in samecycle marked with qh.visit_id-1
+ newfacet marked with qh.visit_id
+
+ returns:
+ newfacet has merged ridges
+
+ notes:
+ ridge already updated for simplicial neighbors of samecycle with a ridge
+
+ see:
+ qh_mergeridges()
+ qh_makeridges()
+
+ design:
+ remove ridges between newfacet and samecycle
+ for each facet in samecycle
+ for each ridge in facet
+ update facet pointers in ridge
+ skip ridges processed in qh_mergecycle_neighors
+ free ridges between newfacet and samecycle
+ free ridges between facets of samecycle (on 2nd visit)
+ append remaining ridges to newfacet
+ if simpilicial facet
+ for each neighbor of facet
+ if simplicial facet
+ and not samecycle facet or newfacet
+ make ridge between neighbor and newfacet
+*/
+void qh_mergecycle_ridges(facetT *samecycle, facetT *newfacet) {
+ facetT *same, *neighbor= NULL;
+ int numold=0, numnew=0;
+ int neighbor_i, neighbor_n;
+ unsigned int samevisitid;
+ ridgeT *ridge, **ridgep;
+ boolT toporient;
+ void **freelistp; /* used if !qh_NOmem by qh_memfree_() */
+
+ trace4((qh ferr, 4033, "qh_mergecycle_ridges: delete shared ridges from newfacet\n"));
+ samevisitid= qh visit_id -1;
+ FOREACHridge_(newfacet->ridges) {
+ neighbor= otherfacet_(ridge, newfacet);
+ if (neighbor->visitid == samevisitid)
+ SETref_(ridge)= NULL; /* ridge free'd below */
+ }
+ qh_setcompact(newfacet->ridges);
+
+ trace4((qh ferr, 4034, "qh_mergecycle_ridges: add ridges to newfacet\n"));
+ FORALLsame_cycle_(samecycle) {
+ FOREACHridge_(same->ridges) {
+ if (ridge->top == same) {
+ ridge->top= newfacet;
+ neighbor= ridge->bottom;
+ }else if (ridge->bottom == same) {
+ ridge->bottom= newfacet;
+ neighbor= ridge->top;
+ }else if (ridge->top == newfacet || ridge->bottom == newfacet) {
+ qh_setappend(&newfacet->ridges, ridge);
+ numold++; /* already set by qh_mergecycle_neighbors */
+ continue;
+ }else {
+ qh_fprintf(qh ferr, 6098, "qhull internal error (qh_mergecycle_ridges): bad ridge r%d\n", ridge->id);
+ qh_errexit(qh_ERRqhull, NULL, ridge);
+ }
+ if (neighbor == newfacet) {
+ qh_setfree(&(ridge->vertices));
+ qh_memfree_(ridge, (int)sizeof(ridgeT), freelistp);
+ numold++;
+ }else if (neighbor->visitid == samevisitid) {
+ qh_setdel(neighbor->ridges, ridge);
+ qh_setfree(&(ridge->vertices));
+ qh_memfree_(ridge, (int)sizeof(ridgeT), freelistp);
+ numold++;
+ }else {
+ qh_setappend(&newfacet->ridges, ridge);
+ numold++;
+ }
+ }
+ if (same->ridges)
+ qh_settruncate(same->ridges, 0);
+ if (!same->simplicial)
+ continue;
+ FOREACHneighbor_i_(same) { /* note: !newfact->simplicial */
+ if (neighbor->visitid != samevisitid && neighbor->simplicial) {
+ ridge= qh_newridge();
+ ridge->vertices= qh_setnew_delnthsorted(same->vertices, qh hull_dim,
+ neighbor_i, 0);
+ toporient= same->toporient ^ (neighbor_i & 0x1);
+ if (toporient) {
+ ridge->top= newfacet;
+ ridge->bottom= neighbor;
+ }else {
+ ridge->top= neighbor;
+ ridge->bottom= newfacet;
+ }
+ qh_setappend(&(newfacet->ridges), ridge);
+ qh_setappend(&(neighbor->ridges), ridge);
+ numnew++;
+ }
+ }
+ }
+
+ trace2((qh ferr, 2033, "qh_mergecycle_ridges: found %d old ridges and %d new ones\n",
+ numold, numnew));
+} /* mergecycle_ridges */
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="mergecycle_vneighbors">-</a>
+
+ qh_mergecycle_vneighbors( samecycle, newfacet )
+ create vertex neighbors for newfacet from vertices of facets in samecycle
+ samecycle marked with visitid == qh.visit_id - 1
+
+ returns:
+ newfacet vertices with updated neighbors
+ marks newfacet with qh.visit_id-1
+ deletes vertices that are merged away
+ sets delridge on all vertices (faster here than in mergecycle_ridges)
+
+ see:
+ qh_mergevertex_neighbors()
+
+ design:
+ for each vertex of samecycle facet
+ set vertex->delridge
+ delete samecycle facets from vertex neighbors
+ append newfacet to vertex neighbors
+ if vertex only in newfacet
+ delete it from newfacet
+ add it to qh.del_vertices for later deletion
+*/
+void qh_mergecycle_vneighbors(facetT *samecycle, facetT *newfacet) {
+ facetT *neighbor, **neighborp;
+ unsigned int mergeid;
+ vertexT *vertex, **vertexp, *apex;
+ setT *vertices;
+
+ trace4((qh ferr, 4035, "qh_mergecycle_vneighbors: update vertex neighbors for newfacet\n"));
+ mergeid= qh visit_id - 1;
+ newfacet->visitid= mergeid;
+ vertices= qh_basevertices(samecycle); /* temp */
+ apex= SETfirstt_(samecycle->vertices, vertexT);
+ qh_setappend(&vertices, apex);
+ FOREACHvertex_(vertices) {
+ vertex->delridge= True;
+ FOREACHneighbor_(vertex) {
+ if (neighbor->visitid == mergeid)
+ SETref_(neighbor)= NULL;
+ }
+ qh_setcompact(vertex->neighbors);
+ qh_setappend(&vertex->neighbors, newfacet);
+ if (!SETsecond_(vertex->neighbors)) {
+ zinc_(Zcyclevertex);
+ trace2((qh ferr, 2034, "qh_mergecycle_vneighbors: deleted v%d when merging cycle f%d into f%d\n",
+ vertex->id, samecycle->id, newfacet->id));
+ qh_setdelsorted(newfacet->vertices, vertex);
+ vertex->deleted= True;
+ qh_setappend(&qh del_vertices, vertex);
+ }
+ }
+ qh_settempfree(&vertices);
+ trace3((qh ferr, 3005, "qh_mergecycle_vneighbors: merged vertices from cycle f%d into f%d\n",
+ samecycle->id, newfacet->id));
+} /* mergecycle_vneighbors */
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="mergefacet">-</a>
+
+ qh_mergefacet( facet1, facet2, mindist, maxdist, mergeapex )
+ merges facet1 into facet2
+ mergeapex==qh_MERGEapex if merging new facet into coplanar horizon
+
+ returns:
+ qh.max_outside and qh.min_vertex updated
+ initializes vertex neighbors on first merge
+
+ returns:
+ facet2 contains facet1's vertices, neighbors, and ridges
+ facet2 moved to end of qh.facet_list
+ makes facet2 a newfacet
+ sets facet2->newmerge set
+ clears facet2->center (unless merging into a large facet)
+ clears facet2->tested and ridge->tested for facet1
+
+ facet1 prepended to visible_list for later deletion and partitioning
+ facet1->f.replace == facet2
+
+ adds neighboring facets to facet_mergeset if redundant or degenerate
+
+ notes:
+ mindist/maxdist may be NULL (only if both NULL)
+ traces merge if fmax_(maxdist,-mindist) > TRACEdist
+
+ see:
+ qh_mergecycle()
+
+ design:
+ trace merge and check for degenerate simplex
+ make ridges for both facets
+ update qh.max_outside, qh.max_vertex, qh.min_vertex
+ update facet2->maxoutside and keepcentrum
+ update facet2->nummerge
+ update tested flags for facet2
+ if facet1 is simplicial
+ merge facet1 into facet2
+ else
+ merge facet1's neighbors into facet2
+ merge facet1's ridges into facet2
+ merge facet1's vertices into facet2
+ merge facet1's vertex neighbors into facet2
+ add facet2's vertices to qh.new_vertexlist
+ unless qh_MERGEapex
+ test facet2 for degenerate or redundant neighbors
+ move facet1 to qh.visible_list for later deletion
+ move facet2 to end of qh.newfacet_list
+*/
+void qh_mergefacet(facetT *facet1, facetT *facet2, realT *mindist, realT *maxdist, boolT mergeapex) {
+ boolT traceonce= False;
+ vertexT *vertex, **vertexp;
+ int tracerestore=0, nummerge;
+
+ if (facet1->tricoplanar || facet2->tricoplanar) {
+ if (!qh TRInormals) {
+ qh_fprintf(qh ferr, 6226, "Qhull internal error (qh_mergefacet): does not work for tricoplanar facets. Use option 'Q11'\n");
+ qh_errexit2(qh_ERRqhull, facet1, facet2);
+ }
+ if (facet2->tricoplanar) {
+ facet2->tricoplanar= False;
+ facet2->keepcentrum= False;
+ }
+ }
+ zzinc_(Ztotmerge);
+ if (qh REPORTfreq2 && qh POSTmerging) {
+ if (zzval_(Ztotmerge) > qh mergereport + qh REPORTfreq2)
+ qh_tracemerging();
+ }
+#ifndef qh_NOtrace
+ if (qh build_cnt >= qh RERUN) {
+ if (mindist && (-*mindist > qh TRACEdist || *maxdist > qh TRACEdist)) {
+ tracerestore= 0;
+ qh IStracing= qh TRACElevel;
+ traceonce= True;
+ qh_fprintf(qh ferr, 8075, "qh_mergefacet: ========= trace wide merge #%d(%2.2g) for f%d into f%d, last point was p%d\n", zzval_(Ztotmerge),
+ fmax_(-*mindist, *maxdist), facet1->id, facet2->id, qh furthest_id);
+ }else if (facet1 == qh tracefacet || facet2 == qh tracefacet) {
+ tracerestore= qh IStracing;
+ qh IStracing= 4;
+ traceonce= True;
+ qh_fprintf(qh ferr, 8076, "qh_mergefacet: ========= trace merge #%d involving f%d, furthest is p%d\n",
+ zzval_(Ztotmerge), qh tracefacet_id, qh furthest_id);
+ }
+ }
+ if (qh IStracing >= 2) {
+ realT mergemin= -2;
+ realT mergemax= -2;
+
+ if (mindist) {
+ mergemin= *mindist;
+ mergemax= *maxdist;
+ }
+ qh_fprintf(qh ferr, 8077, "qh_mergefacet: #%d merge f%d into f%d, mindist= %2.2g, maxdist= %2.2g\n",
+ zzval_(Ztotmerge), facet1->id, facet2->id, mergemin, mergemax);
+ }
+#endif /* !qh_NOtrace */
+ if (facet1 == facet2 || facet1->visible || facet2->visible) {
+ qh_fprintf(qh ferr, 6099, "qhull internal error (qh_mergefacet): either f%d and f%d are the same or one is a visible facet\n",
+ facet1->id, facet2->id);
+ qh_errexit2(qh_ERRqhull, facet1, facet2);
+ }
+ if (qh num_facets - qh num_visible <= qh hull_dim + 1) {
+ qh_fprintf(qh ferr, 6227, "\n\
+qhull precision error: Only %d facets remain. Can not merge another\n\
+pair. The input is too degenerate or the convexity constraints are\n\
+too strong.\n", qh hull_dim+1);
+ if (qh hull_dim >= 5 && !qh MERGEexact)
+ qh_fprintf(qh ferr, 8079, "Option 'Qx' may avoid this problem.\n");
+ qh_errexit(qh_ERRprec, NULL, NULL);
+ }
+ if (!qh VERTEXneighbors)
+ qh_vertexneighbors();
+ qh_makeridges(facet1);
+ qh_makeridges(facet2);
+ if (qh IStracing >=4)
+ qh_errprint("MERGING", facet1, facet2, NULL, NULL);
+ if (mindist) {
+ maximize_(qh max_outside, *maxdist);
+ maximize_(qh max_vertex, *maxdist);
+#if qh_MAXoutside
+ maximize_(facet2->maxoutside, *maxdist);
+#endif
+ minimize_(qh min_vertex, *mindist);
+ if (!facet2->keepcentrum
+ && (*maxdist > qh WIDEfacet || *mindist < -qh WIDEfacet)) {
+ facet2->keepcentrum= True;
+ zinc_(Zwidefacet);
+ }
+ }
+ nummerge= facet1->nummerge + facet2->nummerge + 1;
+ if (nummerge >= qh_MAXnummerge)
+ facet2->nummerge= qh_MAXnummerge;
+ else
+ facet2->nummerge= (short unsigned int)nummerge;
+ facet2->newmerge= True;
+ facet2->dupridge= False;
+ qh_updatetested(facet1, facet2);
+ if (qh hull_dim > 2 && qh_setsize(facet1->vertices) == qh hull_dim)
+ qh_mergesimplex(facet1, facet2, mergeapex);
+ else {
+ qh vertex_visit++;
+ FOREACHvertex_(facet2->vertices)
+ vertex->visitid= qh vertex_visit;
+ if (qh hull_dim == 2)
+ qh_mergefacet2d(facet1, facet2);
+ else {
+ qh_mergeneighbors(facet1, facet2);
+ qh_mergevertices(facet1->vertices, &facet2->vertices);
+ }
+ qh_mergeridges(facet1, facet2);
+ qh_mergevertex_neighbors(facet1, facet2);
+ if (!facet2->newfacet)
+ qh_newvertices(facet2->vertices);
+ }
+ if (!mergeapex)
+ qh_degen_redundant_neighbors(facet2, facet1);
+ if (facet2->coplanar || !facet2->newfacet) {
+ zinc_(Zmergeintohorizon);
+ }else if (!facet1->newfacet && facet2->newfacet) {
+ zinc_(Zmergehorizon);
+ }else {
+ zinc_(Zmergenew);
+ }
+ qh_willdelete(facet1, facet2);
+ qh_removefacet(facet2); /* append as a newfacet to end of qh facet_list */
+ qh_appendfacet(facet2);
+ facet2->newfacet= True;
+ facet2->tested= False;
+ qh_tracemerge(facet1, facet2);
+ if (traceonce) {
+ qh_fprintf(qh ferr, 8080, "qh_mergefacet: end of wide tracing\n");
+ qh IStracing= tracerestore;
+ }
+} /* mergefacet */
+
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="mergefacet2d">-</a>
+
+ qh_mergefacet2d( facet1, facet2 )
+ in 2d, merges neighbors and vertices of facet1 into facet2
+
+ returns:
+ build ridges for neighbors if necessary
+ facet2 looks like a simplicial facet except for centrum, ridges
+ neighbors are opposite the corresponding vertex
+ maintains orientation of facet2
+
+ notes:
+ qh_mergefacet() retains non-simplicial structures
+ they are not needed in 2d, but later routines may use them
+ preserves qh.vertex_visit for qh_mergevertex_neighbors()
+
+ design:
+ get vertices and neighbors
+ determine new vertices and neighbors
+ set new vertices and neighbors and adjust orientation
+ make ridges for new neighbor if needed
+*/
+void qh_mergefacet2d(facetT *facet1, facetT *facet2) {
+ vertexT *vertex1A, *vertex1B, *vertex2A, *vertex2B, *vertexA, *vertexB;
+ facetT *neighbor1A, *neighbor1B, *neighbor2A, *neighbor2B, *neighborA, *neighborB;
+
+ vertex1A= SETfirstt_(facet1->vertices, vertexT);
+ vertex1B= SETsecondt_(facet1->vertices, vertexT);
+ vertex2A= SETfirstt_(facet2->vertices, vertexT);
+ vertex2B= SETsecondt_(facet2->vertices, vertexT);
+ neighbor1A= SETfirstt_(facet1->neighbors, facetT);
+ neighbor1B= SETsecondt_(facet1->neighbors, facetT);
+ neighbor2A= SETfirstt_(facet2->neighbors, facetT);
+ neighbor2B= SETsecondt_(facet2->neighbors, facetT);
+ if (vertex1A == vertex2A) {
+ vertexA= vertex1B;
+ vertexB= vertex2B;
+ neighborA= neighbor2A;
+ neighborB= neighbor1A;
+ }else if (vertex1A == vertex2B) {
+ vertexA= vertex1B;
+ vertexB= vertex2A;
+ neighborA= neighbor2B;
+ neighborB= neighbor1A;
+ }else if (vertex1B == vertex2A) {
+ vertexA= vertex1A;
+ vertexB= vertex2B;
+ neighborA= neighbor2A;
+ neighborB= neighbor1B;
+ }else { /* 1B == 2B */
+ vertexA= vertex1A;
+ vertexB= vertex2A;
+ neighborA= neighbor2B;
+ neighborB= neighbor1B;
+ }
+ /* vertexB always from facet2, neighborB always from facet1 */
+ if (vertexA->id > vertexB->id) {
+ SETfirst_(facet2->vertices)= vertexA;
+ SETsecond_(facet2->vertices)= vertexB;
+ if (vertexB == vertex2A)
+ facet2->toporient= !facet2->toporient;
+ SETfirst_(facet2->neighbors)= neighborA;
+ SETsecond_(facet2->neighbors)= neighborB;
+ }else {
+ SETfirst_(facet2->vertices)= vertexB;
+ SETsecond_(facet2->vertices)= vertexA;
+ if (vertexB == vertex2B)
+ facet2->toporient= !facet2->toporient;
+ SETfirst_(facet2->neighbors)= neighborB;
+ SETsecond_(facet2->neighbors)= neighborA;
+ }
+ qh_makeridges(neighborB);
+ qh_setreplace(neighborB->neighbors, facet1, facet2);
+ trace4((qh ferr, 4036, "qh_mergefacet2d: merged v%d and neighbor f%d of f%d into f%d\n",
+ vertexA->id, neighborB->id, facet1->id, facet2->id));
+} /* mergefacet2d */
+
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="mergeneighbors">-</a>
+
+ qh_mergeneighbors( facet1, facet2 )
+ merges the neighbors of facet1 into facet2
+
+ see:
+ qh_mergecycle_neighbors()
+
+ design:
+ for each neighbor of facet1
+ if neighbor is also a neighbor of facet2
+ if neighbor is simpilicial
+ make ridges for later deletion as a degenerate facet
+ update its neighbor set
+ else
+ move the neighbor relation to facet2
+ remove the neighbor relation for facet1 and facet2
+*/
+void qh_mergeneighbors(facetT *facet1, facetT *facet2) {
+ facetT *neighbor, **neighborp;
+
+ trace4((qh ferr, 4037, "qh_mergeneighbors: merge neighbors of f%d and f%d\n",
+ facet1->id, facet2->id));
+ qh visit_id++;
+ FOREACHneighbor_(facet2) {
+ neighbor->visitid= qh visit_id;
+ }
+ FOREACHneighbor_(facet1) {
+ if (neighbor->visitid == qh visit_id) {
+ if (neighbor->simplicial) /* is degen, needs ridges */
+ qh_makeridges(neighbor);
+ if (SETfirstt_(neighbor->neighbors, facetT) != facet1) /*keep newfacet->horizon*/
+ qh_setdel(neighbor->neighbors, facet1);
+ else {
+ qh_setdel(neighbor->neighbors, facet2);
+ qh_setreplace(neighbor->neighbors, facet1, facet2);
+ }
+ }else if (neighbor != facet2) {
+ qh_setappend(&(facet2->neighbors), neighbor);
+ qh_setreplace(neighbor->neighbors, facet1, facet2);
+ }
+ }
+ qh_setdel(facet1->neighbors, facet2); /* here for makeridges */
+ qh_setdel(facet2->neighbors, facet1);
+} /* mergeneighbors */
+
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="mergeridges">-</a>
+
+ qh_mergeridges( facet1, facet2 )
+ merges the ridge set of facet1 into facet2
+
+ returns:
+ may delete all ridges for a vertex
+ sets vertex->delridge on deleted ridges
+
+ see:
+ qh_mergecycle_ridges()
+
+ design:
+ delete ridges between facet1 and facet2
+ mark (delridge) vertices on these ridges for later testing
+ for each remaining ridge
+ rename facet1 to facet2
+*/
+void qh_mergeridges(facetT *facet1, facetT *facet2) {
+ ridgeT *ridge, **ridgep;
+ vertexT *vertex, **vertexp;
+
+ trace4((qh ferr, 4038, "qh_mergeridges: merge ridges of f%d and f%d\n",
+ facet1->id, facet2->id));
+ FOREACHridge_(facet2->ridges) {
+ if ((ridge->top == facet1) || (ridge->bottom == facet1)) {
+ FOREACHvertex_(ridge->vertices)
+ vertex->delridge= True;
+ qh_delridge(ridge); /* expensive in high-d, could rebuild */
+ ridgep--; /*repeat*/
+ }
+ }
+ FOREACHridge_(facet1->ridges) {
+ if (ridge->top == facet1)
+ ridge->top= facet2;
+ else
+ ridge->bottom= facet2;
+ qh_setappend(&(facet2->ridges), ridge);
+ }
+} /* mergeridges */
+
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="mergesimplex">-</a>
+
+ qh_mergesimplex( facet1, facet2, mergeapex )
+ merge simplicial facet1 into facet2
+ mergeapex==qh_MERGEapex if merging samecycle into horizon facet
+ vertex id is latest (most recently created)
+ facet1 may be contained in facet2
+ ridges exist for both facets
+
+ returns:
+ facet2 with updated vertices, ridges, neighbors
+ updated neighbors for facet1's vertices
+ facet1 not deleted
+ sets vertex->delridge on deleted ridges
+
+ notes:
+ special case code since this is the most common merge
+ called from qh_mergefacet()
+
+ design:
+ if qh_MERGEapex
+ add vertices of facet2 to qh.new_vertexlist if necessary
+ add apex to facet2
+ else
+ for each ridge between facet1 and facet2
+ set vertex->delridge
+ determine the apex for facet1 (i.e., vertex to be merged)
+ unless apex already in facet2
+ insert apex into vertices for facet2
+ add vertices of facet2 to qh.new_vertexlist if necessary
+ add apex to qh.new_vertexlist if necessary
+ for each vertex of facet1
+ if apex
+ rename facet1 to facet2 in its vertex neighbors
+ else
+ delete facet1 from vertex neighors
+ if only in facet2
+ add vertex to qh.del_vertices for later deletion
+ for each ridge of facet1
+ delete ridges between facet1 and facet2
+ append other ridges to facet2 after renaming facet to facet2
+*/
+void qh_mergesimplex(facetT *facet1, facetT *facet2, boolT mergeapex) {
+ vertexT *vertex, **vertexp, *apex;
+ ridgeT *ridge, **ridgep;
+ boolT issubset= False;
+ int vertex_i= -1, vertex_n;
+ facetT *neighbor, **neighborp, *otherfacet;
+
+ if (mergeapex) {
+ if (!facet2->newfacet)
+ qh_newvertices(facet2->vertices); /* apex is new */
+ apex= SETfirstt_(facet1->vertices, vertexT);
+ if (SETfirstt_(facet2->vertices, vertexT) != apex)
+ qh_setaddnth(&facet2->vertices, 0, apex); /* apex has last id */
+ else
+ issubset= True;
+ }else {
+ zinc_(Zmergesimplex);
+ FOREACHvertex_(facet1->vertices)
+ vertex->seen= False;
+ FOREACHridge_(facet1->ridges) {
+ if (otherfacet_(ridge, facet1) == facet2) {
+ FOREACHvertex_(ridge->vertices) {
+ vertex->seen= True;
+ vertex->delridge= True;
+ }
+ break;
+ }
+ }
+ FOREACHvertex_(facet1->vertices) {
+ if (!vertex->seen)
+ break; /* must occur */
+ }
+ apex= vertex;
+ trace4((qh ferr, 4039, "qh_mergesimplex: merge apex v%d of f%d into facet f%d\n",
+ apex->id, facet1->id, facet2->id));
+ FOREACHvertex_i_(facet2->vertices) {
+ if (vertex->id < apex->id) {
+ break;
+ }else if (vertex->id == apex->id) {
+ issubset= True;
+ break;
+ }
+ }
+ if (!issubset)
+ qh_setaddnth(&facet2->vertices, vertex_i, apex);
+ if (!facet2->newfacet)
+ qh_newvertices(facet2->vertices);
+ else if (!apex->newlist) {
+ qh_removevertex(apex);
+ qh_appendvertex(apex);
+ }
+ }
+ trace4((qh ferr, 4040, "qh_mergesimplex: update vertex neighbors of f%d\n",
+ facet1->id));
+ FOREACHvertex_(facet1->vertices) {
+ if (vertex == apex && !issubset)
+ qh_setreplace(vertex->neighbors, facet1, facet2);
+ else {
+ qh_setdel(vertex->neighbors, facet1);
+ if (!SETsecond_(vertex->neighbors))
+ qh_mergevertex_del(vertex, facet1, facet2);
+ }
+ }
+ trace4((qh ferr, 4041, "qh_mergesimplex: merge ridges and neighbors of f%d into f%d\n",
+ facet1->id, facet2->id));
+ qh visit_id++;
+ FOREACHneighbor_(facet2)
+ neighbor->visitid= qh visit_id;
+ FOREACHridge_(facet1->ridges) {
+ otherfacet= otherfacet_(ridge, facet1);
+ if (otherfacet == facet2) {
+ qh_setdel(facet2->ridges, ridge);
+ qh_setfree(&(ridge->vertices));
+ qh_memfree(ridge, (int)sizeof(ridgeT));
+ qh_setdel(facet2->neighbors, facet1);
+ }else {
+ qh_setappend(&facet2->ridges, ridge);
+ if (otherfacet->visitid != qh visit_id) {
+ qh_setappend(&facet2->neighbors, otherfacet);
+ qh_setreplace(otherfacet->neighbors, facet1, facet2);
+ otherfacet->visitid= qh visit_id;
+ }else {
+ if (otherfacet->simplicial) /* is degen, needs ridges */
+ qh_makeridges(otherfacet);
+ if (SETfirstt_(otherfacet->neighbors, facetT) != facet1)
+ qh_setdel(otherfacet->neighbors, facet1);
+ else { /*keep newfacet->neighbors->horizon*/
+ qh_setdel(otherfacet->neighbors, facet2);
+ qh_setreplace(otherfacet->neighbors, facet1, facet2);
+ }
+ }
+ if (ridge->top == facet1) /* wait until after qh_makeridges */
+ ridge->top= facet2;
+ else
+ ridge->bottom= facet2;
+ }
+ }
+ SETfirst_(facet1->ridges)= NULL; /* it will be deleted */
+ trace3((qh ferr, 3006, "qh_mergesimplex: merged simplex f%d apex v%d into facet f%d\n",
+ facet1->id, getid_(apex), facet2->id));
+} /* mergesimplex */
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="mergevertex_del">-</a>
+
+ qh_mergevertex_del( vertex, facet1, facet2 )
+ delete a vertex because of merging facet1 into facet2
+
+ returns:
+ deletes vertex from facet2
+ adds vertex to qh.del_vertices for later deletion
+*/
+void qh_mergevertex_del(vertexT *vertex, facetT *facet1, facetT *facet2) {
+
+ zinc_(Zmergevertex);
+ trace2((qh ferr, 2035, "qh_mergevertex_del: deleted v%d when merging f%d into f%d\n",
+ vertex->id, facet1->id, facet2->id));
+ qh_setdelsorted(facet2->vertices, vertex);
+ vertex->deleted= True;
+ qh_setappend(&qh del_vertices, vertex);
+} /* mergevertex_del */
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="mergevertex_neighbors">-</a>
+
+ qh_mergevertex_neighbors( facet1, facet2 )
+ merge the vertex neighbors of facet1 to facet2
+
+ returns:
+ if vertex is current qh.vertex_visit
+ deletes facet1 from vertex->neighbors
+ else
+ renames facet1 to facet2 in vertex->neighbors
+ deletes vertices if only one neighbor
+
+ notes:
+ assumes vertex neighbor sets are good
+*/
+void qh_mergevertex_neighbors(facetT *facet1, facetT *facet2) {
+ vertexT *vertex, **vertexp;
+
+ trace4((qh ferr, 4042, "qh_mergevertex_neighbors: merge vertex neighbors of f%d and f%d\n",
+ facet1->id, facet2->id));
+ if (qh tracevertex) {
+ qh_fprintf(qh ferr, 8081, "qh_mergevertex_neighbors: of f%d and f%d at furthest p%d f0= %p\n",
+ facet1->id, facet2->id, qh furthest_id, qh tracevertex->neighbors->e[0].p);
+ qh_errprint("TRACE", NULL, NULL, NULL, qh tracevertex);
+ }
+ FOREACHvertex_(facet1->vertices) {
+ if (vertex->visitid != qh vertex_visit)
+ qh_setreplace(vertex->neighbors, facet1, facet2);
+ else {
+ qh_setdel(vertex->neighbors, facet1);
+ if (!SETsecond_(vertex->neighbors))
+ qh_mergevertex_del(vertex, facet1, facet2);
+ }
+ }
+ if (qh tracevertex)
+ qh_errprint("TRACE", NULL, NULL, NULL, qh tracevertex);
+} /* mergevertex_neighbors */
+
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="mergevertices">-</a>
+
+ qh_mergevertices( vertices1, vertices2 )
+ merges the vertex set of facet1 into facet2
+
+ returns:
+ replaces vertices2 with merged set
+ preserves vertex_visit for qh_mergevertex_neighbors
+ updates qh.newvertex_list
+
+ design:
+ create a merged set of both vertices (in inverse id order)
+*/
+void qh_mergevertices(setT *vertices1, setT **vertices2) {
+ int newsize= qh_setsize(vertices1)+qh_setsize(*vertices2) - qh hull_dim + 1;
+ setT *mergedvertices;
+ vertexT *vertex, **vertexp, **vertex2= SETaddr_(*vertices2, vertexT);
+
+ mergedvertices= qh_settemp(newsize);
+ FOREACHvertex_(vertices1) {
+ if (!*vertex2 || vertex->id > (*vertex2)->id)
+ qh_setappend(&mergedvertices, vertex);
+ else {
+ while (*vertex2 && (*vertex2)->id > vertex->id)
+ qh_setappend(&mergedvertices, *vertex2++);
+ if (!*vertex2 || (*vertex2)->id < vertex->id)
+ qh_setappend(&mergedvertices, vertex);
+ else
+ qh_setappend(&mergedvertices, *vertex2++);
+ }
+ }
+ while (*vertex2)
+ qh_setappend(&mergedvertices, *vertex2++);
+ if (newsize < qh_setsize(mergedvertices)) {
+ qh_fprintf(qh ferr, 6100, "qhull internal error (qh_mergevertices): facets did not share a ridge\n");
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ qh_setfree(vertices2);
+ *vertices2= mergedvertices;
+ qh_settemppop();
+} /* mergevertices */
+
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="neighbor_intersections">-</a>
+
+ qh_neighbor_intersections( vertex )
+ return intersection of all vertices in vertex->neighbors except for vertex
+
+ returns:
+ returns temporary set of vertices
+ does not include vertex
+ NULL if a neighbor is simplicial
+ NULL if empty set
+
+ notes:
+ used for renaming vertices
+
+ design:
+ initialize the intersection set with vertices of the first two neighbors
+ delete vertex from the intersection
+ for each remaining neighbor
+ intersect its vertex set with the intersection set
+ return NULL if empty
+ return the intersection set
+*/
+setT *qh_neighbor_intersections(vertexT *vertex) {
+ facetT *neighbor, **neighborp, *neighborA, *neighborB;
+ setT *intersect;
+ int neighbor_i, neighbor_n;
+
+ FOREACHneighbor_(vertex) {
+ if (neighbor->simplicial)
+ return NULL;
+ }
+ neighborA= SETfirstt_(vertex->neighbors, facetT);
+ neighborB= SETsecondt_(vertex->neighbors, facetT);
+ zinc_(Zintersectnum);
+ if (!neighborA)
+ return NULL;
+ if (!neighborB)
+ intersect= qh_setcopy(neighborA->vertices, 0);
+ else
+ intersect= qh_vertexintersect_new(neighborA->vertices, neighborB->vertices);
+ qh_settemppush(intersect);
+ qh_setdelsorted(intersect, vertex);
+ FOREACHneighbor_i_(vertex) {
+ if (neighbor_i >= 2) {
+ zinc_(Zintersectnum);
+ qh_vertexintersect(&intersect, neighbor->vertices);
+ if (!SETfirst_(intersect)) {
+ zinc_(Zintersectfail);
+ qh_settempfree(&intersect);
+ return NULL;
+ }
+ }
+ }
+ trace3((qh ferr, 3007, "qh_neighbor_intersections: %d vertices in neighbor intersection of v%d\n",
+ qh_setsize(intersect), vertex->id));
+ return intersect;
+} /* neighbor_intersections */
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="newvertices">-</a>
+
+ qh_newvertices( vertices )
+ add vertices to end of qh.vertex_list (marks as new vertices)
+
+ returns:
+ vertices on qh.newvertex_list
+ vertex->newlist set
+*/
+void qh_newvertices(setT *vertices) {
+ vertexT *vertex, **vertexp;
+
+ FOREACHvertex_(vertices) {
+ if (!vertex->newlist) {
+ qh_removevertex(vertex);
+ qh_appendvertex(vertex);
+ }
+ }
+} /* newvertices */
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="reducevertices">-</a>
+
+ qh_reducevertices()
+ reduce extra vertices, shared vertices, and redundant vertices
+ facet->newmerge is set if merged since last call
+ if !qh.MERGEvertices, only removes extra vertices
+
+ returns:
+ True if also merged degen_redundant facets
+ vertices are renamed if possible
+ clears facet->newmerge and vertex->delridge
+
+ notes:
+ ignored if 2-d
+
+ design:
+ merge any degenerate or redundant facets
+ for each newly merged facet
+ remove extra vertices
+ if qh.MERGEvertices
+ for each newly merged facet
+ for each vertex
+ if vertex was on a deleted ridge
+ rename vertex if it is shared
+ remove delridge flag from new vertices
+*/
+boolT qh_reducevertices(void) {
+ int numshare=0, numrename= 0;
+ boolT degenredun= False;
+ facetT *newfacet;
+ vertexT *vertex, **vertexp;
+
+ if (qh hull_dim == 2)
+ return False;
+ if (qh_merge_degenredundant())
+ degenredun= True;
+ LABELrestart:
+ FORALLnew_facets {
+ if (newfacet->newmerge) {
+ if (!qh MERGEvertices)
+ newfacet->newmerge= False;
+ qh_remove_extravertices(newfacet);
+ }
+ }
+ if (!qh MERGEvertices)
+ return False;
+ FORALLnew_facets {
+ if (newfacet->newmerge) {
+ newfacet->newmerge= False;
+ FOREACHvertex_(newfacet->vertices) {
+ if (vertex->delridge) {
+ if (qh_rename_sharedvertex(vertex, newfacet)) {
+ numshare++;
+ vertexp--; /* repeat since deleted vertex */
+ }
+ }
+ }
+ }
+ }
+ FORALLvertex_(qh newvertex_list) {
+ if (vertex->delridge && !vertex->deleted) {
+ vertex->delridge= False;
+ if (qh hull_dim >= 4 && qh_redundant_vertex(vertex)) {
+ numrename++;
+ if (qh_merge_degenredundant()) {
+ degenredun= True;
+ goto LABELrestart;
+ }
+ }
+ }
+ }
+ trace1((qh ferr, 1014, "qh_reducevertices: renamed %d shared vertices and %d redundant vertices. Degen? %d\n",
+ numshare, numrename, degenredun));
+ return degenredun;
+} /* reducevertices */
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="redundant_vertex">-</a>
+
+ qh_redundant_vertex( vertex )
+ detect and rename a redundant vertex
+ vertices have full vertex->neighbors
+
+ returns:
+ returns true if find a redundant vertex
+ deletes vertex(vertex->deleted)
+
+ notes:
+ only needed if vertex->delridge and hull_dim >= 4
+ may add degenerate facets to qh.facet_mergeset
+ doesn't change vertex->neighbors or create redundant facets
+
+ design:
+ intersect vertices of all facet neighbors of vertex
+ determine ridges for these vertices
+ if find a new vertex for vertex amoung these ridges and vertices
+ rename vertex to the new vertex
+*/
+vertexT *qh_redundant_vertex(vertexT *vertex) {
+ vertexT *newvertex= NULL;
+ setT *vertices, *ridges;
+
+ trace3((qh ferr, 3008, "qh_redundant_vertex: check if v%d can be renamed\n", vertex->id));
+ if ((vertices= qh_neighbor_intersections(vertex))) {
+ ridges= qh_vertexridges(vertex);
+ if ((newvertex= qh_find_newvertex(vertex, vertices, ridges)))
+ qh_renamevertex(vertex, newvertex, ridges, NULL, NULL);
+ qh_settempfree(&ridges);
+ qh_settempfree(&vertices);
+ }
+ return newvertex;
+} /* redundant_vertex */
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="remove_extravertices">-</a>
+
+ qh_remove_extravertices( facet )
+ remove extra vertices from non-simplicial facets
+
+ returns:
+ returns True if it finds them
+
+ design:
+ for each vertex in facet
+ if vertex not in a ridge (i.e., no longer used)
+ delete vertex from facet
+ delete facet from vertice's neighbors
+ unless vertex in another facet
+ add vertex to qh.del_vertices for later deletion
+*/
+boolT qh_remove_extravertices(facetT *facet) {
+ ridgeT *ridge, **ridgep;
+ vertexT *vertex, **vertexp;
+ boolT foundrem= False;
+
+ trace4((qh ferr, 4043, "qh_remove_extravertices: test f%d for extra vertices\n",
+ facet->id));
+ FOREACHvertex_(facet->vertices)
+ vertex->seen= False;
+ FOREACHridge_(facet->ridges) {
+ FOREACHvertex_(ridge->vertices)
+ vertex->seen= True;
+ }
+ FOREACHvertex_(facet->vertices) {
+ if (!vertex->seen) {
+ foundrem= True;
+ zinc_(Zremvertex);
+ qh_setdelsorted(facet->vertices, vertex);
+ qh_setdel(vertex->neighbors, facet);
+ if (!qh_setsize(vertex->neighbors)) {
+ vertex->deleted= True;
+ qh_setappend(&qh del_vertices, vertex);
+ zinc_(Zremvertexdel);
+ trace2((qh ferr, 2036, "qh_remove_extravertices: v%d deleted because it's lost all ridges\n", vertex->id));
+ }else
+ trace3((qh ferr, 3009, "qh_remove_extravertices: v%d removed from f%d because it's lost all ridges\n", vertex->id, facet->id));
+ vertexp--; /*repeat*/
+ }
+ }
+ return foundrem;
+} /* remove_extravertices */
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="rename_sharedvertex">-</a>
+
+ qh_rename_sharedvertex( vertex, facet )
+ detect and rename if shared vertex in facet
+ vertices have full ->neighbors
+
+ returns:
+ newvertex or NULL
+ the vertex may still exist in other facets (i.e., a neighbor was pinched)
+ does not change facet->neighbors
+ updates vertex->neighbors
+
+ notes:
+ a shared vertex for a facet is only in ridges to one neighbor
+ this may undo a pinched facet
+
+ it does not catch pinches involving multiple facets. These appear
+ to be difficult to detect, since an exhaustive search is too expensive.
+
+ design:
+ if vertex only has two neighbors
+ determine the ridges that contain the vertex
+ determine the vertices shared by both neighbors
+ if can find a new vertex in this set
+ rename the vertex to the new vertex
+*/
+vertexT *qh_rename_sharedvertex(vertexT *vertex, facetT *facet) {
+ facetT *neighbor, **neighborp, *neighborA= NULL;
+ setT *vertices, *ridges;
+ vertexT *newvertex;
+
+ if (qh_setsize(vertex->neighbors) == 2) {
+ neighborA= SETfirstt_(vertex->neighbors, facetT);
+ if (neighborA == facet)
+ neighborA= SETsecondt_(vertex->neighbors, facetT);
+ }else if (qh hull_dim == 3)
+ return NULL;
+ else {
+ qh visit_id++;
+ FOREACHneighbor_(facet)
+ neighbor->visitid= qh visit_id;
+ FOREACHneighbor_(vertex) {
+ if (neighbor->visitid == qh visit_id) {
+ if (neighborA)
+ return NULL;
+ neighborA= neighbor;
+ }
+ }
+ if (!neighborA) {
+ qh_fprintf(qh ferr, 6101, "qhull internal error (qh_rename_sharedvertex): v%d's neighbors not in f%d\n",
+ vertex->id, facet->id);
+ qh_errprint("ERRONEOUS", facet, NULL, NULL, vertex);
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ }
+ /* the vertex is shared by facet and neighborA */
+ ridges= qh_settemp(qh TEMPsize);
+ neighborA->visitid= ++qh visit_id;
+ qh_vertexridges_facet(vertex, facet, &ridges);
+ trace2((qh ferr, 2037, "qh_rename_sharedvertex: p%d(v%d) is shared by f%d(%d ridges) and f%d\n",
+ qh_pointid(vertex->point), vertex->id, facet->id, qh_setsize(ridges), neighborA->id));
+ zinc_(Zintersectnum);
+ vertices= qh_vertexintersect_new(facet->vertices, neighborA->vertices);
+ qh_setdel(vertices, vertex);
+ qh_settemppush(vertices);
+ if ((newvertex= qh_find_newvertex(vertex, vertices, ridges)))
+ qh_renamevertex(vertex, newvertex, ridges, facet, neighborA);
+ qh_settempfree(&vertices);
+ qh_settempfree(&ridges);
+ return newvertex;
+} /* rename_sharedvertex */
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="renameridgevertex">-</a>
+
+ qh_renameridgevertex( ridge, oldvertex, newvertex )
+ renames oldvertex as newvertex in ridge
+
+ returns:
+
+ design:
+ delete oldvertex from ridge
+ if newvertex already in ridge
+ copy ridge->noconvex to another ridge if possible
+ delete the ridge
+ else
+ insert newvertex into the ridge
+ adjust the ridge's orientation
+*/
+void qh_renameridgevertex(ridgeT *ridge, vertexT *oldvertex, vertexT *newvertex) {
+ int nth= 0, oldnth;
+ facetT *temp;
+ vertexT *vertex, **vertexp;
+
+ oldnth= qh_setindex(ridge->vertices, oldvertex);
+ qh_setdelnthsorted(ridge->vertices, oldnth);
+ FOREACHvertex_(ridge->vertices) {
+ if (vertex == newvertex) {
+ zinc_(Zdelridge);
+ if (ridge->nonconvex) /* only one ridge has nonconvex set */
+ qh_copynonconvex(ridge);
+ trace2((qh ferr, 2038, "qh_renameridgevertex: ridge r%d deleted. It contained both v%d and v%d\n",
+ ridge->id, oldvertex->id, newvertex->id));
+ qh_delridge(ridge);
+ return;
+ }
+ if (vertex->id < newvertex->id)
+ break;
+ nth++;
+ }
+ qh_setaddnth(&ridge->vertices, nth, newvertex);
+ if (abs(oldnth - nth)%2) {
+ trace3((qh ferr, 3010, "qh_renameridgevertex: swapped the top and bottom of ridge r%d\n",
+ ridge->id));
+ temp= ridge->top;
+ ridge->top= ridge->bottom;
+ ridge->bottom= temp;
+ }
+} /* renameridgevertex */
+
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="renamevertex">-</a>
+
+ qh_renamevertex( oldvertex, newvertex, ridges, oldfacet, neighborA )
+ renames oldvertex as newvertex in ridges
+ gives oldfacet/neighborA if oldvertex is shared between two facets
+
+ returns:
+ oldvertex may still exist afterwards
+
+
+ notes:
+ can not change neighbors of newvertex (since it's a subset)
+
+ design:
+ for each ridge in ridges
+ rename oldvertex to newvertex and delete degenerate ridges
+ if oldfacet not defined
+ for each neighbor of oldvertex
+ delete oldvertex from neighbor's vertices
+ remove extra vertices from neighbor
+ add oldvertex to qh.del_vertices
+ else if oldvertex only between oldfacet and neighborA
+ delete oldvertex from oldfacet and neighborA
+ add oldvertex to qh.del_vertices
+ else oldvertex is in oldfacet and neighborA and other facets (i.e., pinched)
+ delete oldvertex from oldfacet
+ delete oldfacet from oldvertice's neighbors
+ remove extra vertices (e.g., oldvertex) from neighborA
+*/
+void qh_renamevertex(vertexT *oldvertex, vertexT *newvertex, setT *ridges, facetT *oldfacet, facetT *neighborA) {
+ facetT *neighbor, **neighborp;
+ ridgeT *ridge, **ridgep;
+ boolT istrace= False;
+
+ if (qh IStracing >= 2 || oldvertex->id == qh tracevertex_id ||
+ newvertex->id == qh tracevertex_id)
+ istrace= True;
+ FOREACHridge_(ridges)
+ qh_renameridgevertex(ridge, oldvertex, newvertex);
+ if (!oldfacet) {
+ zinc_(Zrenameall);
+ if (istrace)
+ qh_fprintf(qh ferr, 8082, "qh_renamevertex: renamed v%d to v%d in several facets\n",
+ oldvertex->id, newvertex->id);
+ FOREACHneighbor_(oldvertex) {
+ qh_maydropneighbor(neighbor);
+ qh_setdelsorted(neighbor->vertices, oldvertex);
+ if (qh_remove_extravertices(neighbor))
+ neighborp--; /* neighbor may be deleted */
+ }
+ if (!oldvertex->deleted) {
+ oldvertex->deleted= True;
+ qh_setappend(&qh del_vertices, oldvertex);
+ }
+ }else if (qh_setsize(oldvertex->neighbors) == 2) {
+ zinc_(Zrenameshare);
+ if (istrace)
+ qh_fprintf(qh ferr, 8083, "qh_renamevertex: renamed v%d to v%d in oldfacet f%d\n",
+ oldvertex->id, newvertex->id, oldfacet->id);
+ FOREACHneighbor_(oldvertex)
+ qh_setdelsorted(neighbor->vertices, oldvertex);
+ oldvertex->deleted= True;
+ qh_setappend(&qh del_vertices, oldvertex);
+ }else {
+ zinc_(Zrenamepinch);
+ if (istrace || qh IStracing)
+ qh_fprintf(qh ferr, 8084, "qh_renamevertex: renamed pinched v%d to v%d between f%d and f%d\n",
+ oldvertex->id, newvertex->id, oldfacet->id, neighborA->id);
+ qh_setdelsorted(oldfacet->vertices, oldvertex);
+ qh_setdel(oldvertex->neighbors, oldfacet);
+ qh_remove_extravertices(neighborA);
+ }
+} /* renamevertex */
+
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="test_appendmerge">-</a>
+
+ qh_test_appendmerge( facet, neighbor )
+ tests facet/neighbor for convexity
+ appends to mergeset if non-convex
+ if pre-merging,
+ nop if qh.SKIPconvex, or qh.MERGEexact and coplanar
+
+ returns:
+ true if appends facet/neighbor to mergeset
+ sets facet->center as needed
+ does not change facet->seen
+
+ design:
+ if qh.cos_max is defined
+ if the angle between facet normals is too shallow
+ append an angle-coplanar merge to qh.mergeset
+ return True
+ make facet's centrum if needed
+ if facet's centrum is above the neighbor
+ set isconcave
+ else
+ if facet's centrum is not below the neighbor
+ set iscoplanar
+ make neighbor's centrum if needed
+ if neighbor's centrum is above the facet
+ set isconcave
+ else if neighbor's centrum is not below the facet
+ set iscoplanar
+ if isconcave or iscoplanar
+ get angle if needed
+ append concave or coplanar merge to qh.mergeset
+*/
+boolT qh_test_appendmerge(facetT *facet, facetT *neighbor) {
+ realT dist, dist2= -REALmax, angle= -REALmax;
+ boolT isconcave= False, iscoplanar= False, okangle= False;
+
+ if (qh SKIPconvex && !qh POSTmerging)
+ return False;
+ if ((!qh MERGEexact || qh POSTmerging) && qh cos_max < REALmax/2) {
+ angle= qh_getangle(facet->normal, neighbor->normal);
+ zinc_(Zangletests);
+ if (angle > qh cos_max) {
+ zinc_(Zcoplanarangle);
+ qh_appendmergeset(facet, neighbor, MRGanglecoplanar, &angle);
+ trace2((qh ferr, 2039, "qh_test_appendmerge: coplanar angle %4.4g between f%d and f%d\n",
+ angle, facet->id, neighbor->id));
+ return True;
+ }else
+ okangle= True;
+ }
+ if (!facet->center)
+ facet->center= qh_getcentrum(facet);
+ zzinc_(Zcentrumtests);
+ qh_distplane(facet->center, neighbor, &dist);
+ if (dist > qh centrum_radius)
+ isconcave= True;
+ else {
+ if (dist > -qh centrum_radius)
+ iscoplanar= True;
+ if (!neighbor->center)
+ neighbor->center= qh_getcentrum(neighbor);
+ zzinc_(Zcentrumtests);
+ qh_distplane(neighbor->center, facet, &dist2);
+ if (dist2 > qh centrum_radius)
+ isconcave= True;
+ else if (!iscoplanar && dist2 > -qh centrum_radius)
+ iscoplanar= True;
+ }
+ if (!isconcave && (!iscoplanar || (qh MERGEexact && !qh POSTmerging)))
+ return False;
+ if (!okangle && qh ANGLEmerge) {
+ angle= qh_getangle(facet->normal, neighbor->normal);
+ zinc_(Zangletests);
+ }
+ if (isconcave) {
+ zinc_(Zconcaveridge);
+ if (qh ANGLEmerge)
+ angle += qh_ANGLEconcave + 0.5;
+ qh_appendmergeset(facet, neighbor, MRGconcave, &angle);
+ trace0((qh ferr, 18, "qh_test_appendmerge: concave f%d to f%d dist %4.4g and reverse dist %4.4g angle %4.4g during p%d\n",
+ facet->id, neighbor->id, dist, dist2, angle, qh furthest_id));
+ }else /* iscoplanar */ {
+ zinc_(Zcoplanarcentrum);
+ qh_appendmergeset(facet, neighbor, MRGcoplanar, &angle);
+ trace2((qh ferr, 2040, "qh_test_appendmerge: coplanar f%d to f%d dist %4.4g, reverse dist %4.4g angle %4.4g\n",
+ facet->id, neighbor->id, dist, dist2, angle));
+ }
+ return True;
+} /* test_appendmerge */
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="test_vneighbors">-</a>
+
+ qh_test_vneighbors()
+ test vertex neighbors for convexity
+ tests all facets on qh.newfacet_list
+
+ returns:
+ true if non-convex vneighbors appended to qh.facet_mergeset
+ initializes vertex neighbors if needed
+
+ notes:
+ assumes all facet neighbors have been tested
+ this can be expensive
+ this does not guarantee that a centrum is below all facets
+ but it is unlikely
+ uses qh.visit_id
+
+ design:
+ build vertex neighbors if necessary
+ for all new facets
+ for all vertices
+ for each unvisited facet neighbor of the vertex
+ test new facet and neighbor for convexity
+*/
+boolT qh_test_vneighbors(void /* qh.newfacet_list */) {
+ facetT *newfacet, *neighbor, **neighborp;
+ vertexT *vertex, **vertexp;
+ int nummerges= 0;
+
+ trace1((qh ferr, 1015, "qh_test_vneighbors: testing vertex neighbors for convexity\n"));
+ if (!qh VERTEXneighbors)
+ qh_vertexneighbors();
+ FORALLnew_facets
+ newfacet->seen= False;
+ FORALLnew_facets {
+ newfacet->seen= True;
+ newfacet->visitid= qh visit_id++;
+ FOREACHneighbor_(newfacet)
+ newfacet->visitid= qh visit_id;
+ FOREACHvertex_(newfacet->vertices) {
+ FOREACHneighbor_(vertex) {
+ if (neighbor->seen || neighbor->visitid == qh visit_id)
+ continue;
+ if (qh_test_appendmerge(newfacet, neighbor))
+ nummerges++;
+ }
+ }
+ }
+ zadd_(Ztestvneighbor, nummerges);
+ trace1((qh ferr, 1016, "qh_test_vneighbors: found %d non-convex, vertex neighbors\n",
+ nummerges));
+ return (nummerges > 0);
+} /* test_vneighbors */
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="tracemerge">-</a>
+
+ qh_tracemerge( facet1, facet2 )
+ print trace message after merge
+*/
+void qh_tracemerge(facetT *facet1, facetT *facet2) {
+ boolT waserror= False;
+
+#ifndef qh_NOtrace
+ if (qh IStracing >= 4)
+ qh_errprint("MERGED", facet2, NULL, NULL, NULL);
+ if (facet2 == qh tracefacet || (qh tracevertex && qh tracevertex->newlist)) {
+ qh_fprintf(qh ferr, 8085, "qh_tracemerge: trace facet and vertex after merge of f%d and f%d, furthest p%d\n", facet1->id, facet2->id, qh furthest_id);
+ if (facet2 != qh tracefacet)
+ qh_errprint("TRACE", qh tracefacet,
+ (qh tracevertex && qh tracevertex->neighbors) ?
+ SETfirstt_(qh tracevertex->neighbors, facetT) : NULL,
+ NULL, qh tracevertex);
+ }
+ if (qh tracevertex) {
+ if (qh tracevertex->deleted)
+ qh_fprintf(qh ferr, 8086, "qh_tracemerge: trace vertex deleted at furthest p%d\n",
+ qh furthest_id);
+ else
+ qh_checkvertex(qh tracevertex);
+ }
+ if (qh tracefacet) {
+ qh_checkfacet(qh tracefacet, True, &waserror);
+ if (waserror)
+ qh_errexit(qh_ERRqhull, qh tracefacet, NULL);
+ }
+#endif /* !qh_NOtrace */
+ if (qh CHECKfrequently || qh IStracing >= 4) { /* can't check polygon here */
+ qh_checkfacet(facet2, True, &waserror);
+ if (waserror)
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+} /* tracemerge */
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="tracemerging">-</a>
+
+ qh_tracemerging()
+ print trace message during POSTmerging
+
+ returns:
+ updates qh.mergereport
+
+ notes:
+ called from qh_mergecycle() and qh_mergefacet()
+
+ see:
+ qh_buildtracing()
+*/
+void qh_tracemerging(void) {
+ realT cpu;
+ int total;
+ time_t timedata;
+ struct tm *tp;
+
+ qh mergereport= zzval_(Ztotmerge);
+ time(&timedata);
+ tp= localtime(&timedata);
+ cpu= qh_CPUclock;
+ cpu /= qh_SECticks;
+ total= zzval_(Ztotmerge) - zzval_(Zcyclehorizon) + zzval_(Zcyclefacettot);
+ qh_fprintf(qh ferr, 8087, "\n\
+At %d:%d:%d & %2.5g CPU secs, qhull has merged %d facets. The hull\n\
+ contains %d facets and %d vertices.\n",
+ tp->tm_hour, tp->tm_min, tp->tm_sec, cpu,
+ total, qh num_facets - qh num_visible,
+ qh num_vertices-qh_setsize(qh del_vertices));
+} /* tracemerging */
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="updatetested">-</a>
+
+ qh_updatetested( facet1, facet2 )
+ clear facet2->tested and facet1->ridge->tested for merge
+
+ returns:
+ deletes facet2->center unless it's already large
+ if so, clears facet2->ridge->tested
+
+ design:
+ clear facet2->tested
+ clear ridge->tested for facet1's ridges
+ if facet2 has a centrum
+ if facet2 is large
+ set facet2->keepcentrum
+ else if facet2 has 3 vertices due to many merges, or not large and post merging
+ clear facet2->keepcentrum
+ unless facet2->keepcentrum
+ clear facet2->center to recompute centrum later
+ clear ridge->tested for facet2's ridges
+*/
+void qh_updatetested(facetT *facet1, facetT *facet2) {
+ ridgeT *ridge, **ridgep;
+ int size;
+
+ facet2->tested= False;
+ FOREACHridge_(facet1->ridges)
+ ridge->tested= False;
+ if (!facet2->center)
+ return;
+ size= qh_setsize(facet2->vertices);
+ if (!facet2->keepcentrum) {
+ if (size > qh hull_dim + qh_MAXnewcentrum) {
+ facet2->keepcentrum= True;
+ zinc_(Zwidevertices);
+ }
+ }else if (size <= qh hull_dim + qh_MAXnewcentrum) {
+ /* center and keepcentrum was set */
+ if (size == qh hull_dim || qh POSTmerging)
+ facet2->keepcentrum= False; /* if many merges need to recompute centrum */
+ }
+ if (!facet2->keepcentrum) {
+ qh_memfree(facet2->center, qh normal_size);
+ facet2->center= NULL;
+ FOREACHridge_(facet2->ridges)
+ ridge->tested= False;
+ }
+} /* updatetested */
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="vertexridges">-</a>
+
+ qh_vertexridges( vertex )
+ return temporary set of ridges adjacent to a vertex
+ vertex->neighbors defined
+
+ ntoes:
+ uses qh.visit_id
+ does not include implicit ridges for simplicial facets
+
+ design:
+ for each neighbor of vertex
+ add ridges that include the vertex to ridges
+*/
+setT *qh_vertexridges(vertexT *vertex) {
+ facetT *neighbor, **neighborp;
+ setT *ridges= qh_settemp(qh TEMPsize);
+ int size;
+
+ qh visit_id++;
+ FOREACHneighbor_(vertex)
+ neighbor->visitid= qh visit_id;
+ FOREACHneighbor_(vertex) {
+ if (*neighborp) /* no new ridges in last neighbor */
+ qh_vertexridges_facet(vertex, neighbor, &ridges);
+ }
+ if (qh PRINTstatistics || qh IStracing) {
+ size= qh_setsize(ridges);
+ zinc_(Zvertexridge);
+ zadd_(Zvertexridgetot, size);
+ zmax_(Zvertexridgemax, size);
+ trace3((qh ferr, 3011, "qh_vertexridges: found %d ridges for v%d\n",
+ size, vertex->id));
+ }
+ return ridges;
+} /* vertexridges */
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="vertexridges_facet">-</a>
+
+ qh_vertexridges_facet( vertex, facet, ridges )
+ add adjacent ridges for vertex in facet
+ neighbor->visitid==qh.visit_id if it hasn't been visited
+
+ returns:
+ ridges updated
+ sets facet->visitid to qh.visit_id-1
+
+ design:
+ for each ridge of facet
+ if ridge of visited neighbor (i.e., unprocessed)
+ if vertex in ridge
+ append ridge to vertex
+ mark facet processed
+*/
+void qh_vertexridges_facet(vertexT *vertex, facetT *facet, setT **ridges) {
+ ridgeT *ridge, **ridgep;
+ facetT *neighbor;
+
+ FOREACHridge_(facet->ridges) {
+ neighbor= otherfacet_(ridge, facet);
+ if (neighbor->visitid == qh visit_id
+ && qh_setin(ridge->vertices, vertex))
+ qh_setappend(ridges, ridge);
+ }
+ facet->visitid= qh visit_id-1;
+} /* vertexridges_facet */
+
+/*-<a href="qh-merge.htm#TOC"
+ >-------------------------------</a><a name="willdelete">-</a>
+
+ qh_willdelete( facet, replace )
+ moves facet to visible list
+ sets facet->f.replace to replace (may be NULL)
+
+ returns:
+ bumps qh.num_visible
+*/
+void qh_willdelete(facetT *facet, facetT *replace) {
+
+ qh_removefacet(facet);
+ qh_prependfacet(facet, &qh visible_list);
+ qh num_visible++;
+ facet->visible= True;
+ facet->f.replace= replace;
+} /* willdelete */
+
+#else /* qh_NOmerge */
+void qh_premerge(vertexT *apex, realT maxcentrum, realT maxangle) {
+}
+void qh_postmerge(const char *reason, realT maxcentrum, realT maxangle,
+ boolT vneighbors) {
+}
+boolT qh_checkzero(boolT testall) {
+ }
+#endif /* qh_NOmerge */
+
diff --git a/xs/src/qhull/src/libqhull/merge.h b/xs/src/qhull/src/libqhull/merge.h
new file mode 100644
index 000000000..7f5ec3fb6
--- /dev/null
+++ b/xs/src/qhull/src/libqhull/merge.h
@@ -0,0 +1,178 @@
+/*<html><pre> -<a href="qh-merge.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ merge.h
+ header file for merge.c
+
+ see qh-merge.htm and merge.c
+
+ Copyright (c) 1993-2015 C.B. Barber.
+ $Id: //main/2015/qhull/src/libqhull/merge.h#1 $$Change: 1981 $
+ $DateTime: 2015/09/28 20:26:32 $$Author: bbarber $
+*/
+
+#ifndef qhDEFmerge
+#define qhDEFmerge 1
+
+#include "libqhull.h"
+
+
+/*============ -constants- ==============*/
+
+/*-<a href="qh-merge.htm#TOC"
+ >--------------------------------</a><a name="qh_ANGLEredundant">-</a>
+
+ qh_ANGLEredundant
+ indicates redundant merge in mergeT->angle
+*/
+#define qh_ANGLEredundant 6.0
+
+/*-<a href="qh-merge.htm#TOC"
+ >--------------------------------</a><a name="qh_ANGLEdegen">-</a>
+
+ qh_ANGLEdegen
+ indicates degenerate facet in mergeT->angle
+*/
+#define qh_ANGLEdegen 5.0
+
+/*-<a href="qh-merge.htm#TOC"
+ >--------------------------------</a><a name="qh_ANGLEconcave">-</a>
+
+ qh_ANGLEconcave
+ offset to indicate concave facets in mergeT->angle
+
+ notes:
+ concave facets are assigned the range of [2,4] in mergeT->angle
+ roundoff error may make the angle less than 2
+*/
+#define qh_ANGLEconcave 1.5
+
+/*-<a href="qh-merge.htm#TOC"
+ >--------------------------------</a><a name="MRG">-</a>
+
+ MRG... (mergeType)
+ indicates the type of a merge (mergeT->type)
+*/
+typedef enum { /* in sort order for facet_mergeset */
+ MRGnone= 0,
+ MRGcoplanar, /* centrum coplanar */
+ MRGanglecoplanar, /* angle coplanar */
+ /* could detect half concave ridges */
+ MRGconcave, /* concave ridge */
+ MRGflip, /* flipped facet. facet1 == facet2 */
+ MRGridge, /* duplicate ridge (qh_MERGEridge) */
+ /* degen and redundant go onto degen_mergeset */
+ MRGdegen, /* degenerate facet (!enough neighbors) facet1 == facet2 */
+ MRGredundant, /* redundant facet (vertex subset) */
+ /* merge_degenredundant assumes degen < redundant */
+ MRGmirror, /* mirror facet from qh_triangulate */
+ ENDmrg
+} mergeType;
+
+/*-<a href="qh-merge.htm#TOC"
+ >--------------------------------</a><a name="qh_MERGEapex">-</a>
+
+ qh_MERGEapex
+ flag for qh_mergefacet() to indicate an apex merge
+*/
+#define qh_MERGEapex True
+
+/*============ -structures- ====================*/
+
+/*-<a href="qh-merge.htm#TOC"
+ >--------------------------------</a><a name="mergeT">-</a>
+
+ mergeT
+ structure used to merge facets
+*/
+
+typedef struct mergeT mergeT;
+struct mergeT { /* initialize in qh_appendmergeset */
+ realT angle; /* angle between normals of facet1 and facet2 */
+ facetT *facet1; /* will merge facet1 into facet2 */
+ facetT *facet2;
+ mergeType type;
+};
+
+
+/*=========== -macros- =========================*/
+
+/*-<a href="qh-merge.htm#TOC"
+ >--------------------------------</a><a name="FOREACHmerge_">-</a>
+
+ FOREACHmerge_( merges ) {...}
+ assign 'merge' to each merge in merges
+
+ notes:
+ uses 'mergeT *merge, **mergep;'
+ if qh_mergefacet(),
+ restart since qh.facet_mergeset may change
+ see <a href="qset.h#FOREACHsetelement_">FOREACHsetelement_</a>
+*/
+#define FOREACHmerge_( merges ) FOREACHsetelement_(mergeT, merges, merge)
+
+/*============ prototypes in alphabetical order after pre/postmerge =======*/
+
+void qh_premerge(vertexT *apex, realT maxcentrum, realT maxangle);
+void qh_postmerge(const char *reason, realT maxcentrum, realT maxangle,
+ boolT vneighbors);
+void qh_all_merges(boolT othermerge, boolT vneighbors);
+void qh_appendmergeset(facetT *facet, facetT *neighbor, mergeType mergetype, realT *angle);
+setT *qh_basevertices( facetT *samecycle);
+void qh_checkconnect(void /* qh.new_facets */);
+boolT qh_checkzero(boolT testall);
+int qh_compareangle(const void *p1, const void *p2);
+int qh_comparemerge(const void *p1, const void *p2);
+int qh_comparevisit(const void *p1, const void *p2);
+void qh_copynonconvex(ridgeT *atridge);
+void qh_degen_redundant_facet(facetT *facet);
+void qh_degen_redundant_neighbors(facetT *facet, facetT *delfacet);
+vertexT *qh_find_newvertex(vertexT *oldvertex, setT *vertices, setT *ridges);
+void qh_findbest_test(boolT testcentrum, facetT *facet, facetT *neighbor,
+ facetT **bestfacet, realT *distp, realT *mindistp, realT *maxdistp);
+facetT *qh_findbestneighbor(facetT *facet, realT *distp, realT *mindistp, realT *maxdistp);
+void qh_flippedmerges(facetT *facetlist, boolT *wasmerge);
+void qh_forcedmerges( boolT *wasmerge);
+void qh_getmergeset(facetT *facetlist);
+void qh_getmergeset_initial(facetT *facetlist);
+void qh_hashridge(setT *hashtable, int hashsize, ridgeT *ridge, vertexT *oldvertex);
+ridgeT *qh_hashridge_find(setT *hashtable, int hashsize, ridgeT *ridge,
+ vertexT *vertex, vertexT *oldvertex, int *hashslot);
+void qh_makeridges(facetT *facet);
+void qh_mark_dupridges(facetT *facetlist);
+void qh_maydropneighbor(facetT *facet);
+int qh_merge_degenredundant(void);
+void qh_merge_nonconvex( facetT *facet1, facetT *facet2, mergeType mergetype);
+void qh_mergecycle(facetT *samecycle, facetT *newfacet);
+void qh_mergecycle_all(facetT *facetlist, boolT *wasmerge);
+void qh_mergecycle_facets( facetT *samecycle, facetT *newfacet);
+void qh_mergecycle_neighbors(facetT *samecycle, facetT *newfacet);
+void qh_mergecycle_ridges(facetT *samecycle, facetT *newfacet);
+void qh_mergecycle_vneighbors( facetT *samecycle, facetT *newfacet);
+void qh_mergefacet(facetT *facet1, facetT *facet2, realT *mindist, realT *maxdist, boolT mergeapex);
+void qh_mergefacet2d(facetT *facet1, facetT *facet2);
+void qh_mergeneighbors(facetT *facet1, facetT *facet2);
+void qh_mergeridges(facetT *facet1, facetT *facet2);
+void qh_mergesimplex(facetT *facet1, facetT *facet2, boolT mergeapex);
+void qh_mergevertex_del(vertexT *vertex, facetT *facet1, facetT *facet2);
+void qh_mergevertex_neighbors(facetT *facet1, facetT *facet2);
+void qh_mergevertices(setT *vertices1, setT **vertices);
+setT *qh_neighbor_intersections(vertexT *vertex);
+void qh_newvertices(setT *vertices);
+boolT qh_reducevertices(void);
+vertexT *qh_redundant_vertex(vertexT *vertex);
+boolT qh_remove_extravertices(facetT *facet);
+vertexT *qh_rename_sharedvertex(vertexT *vertex, facetT *facet);
+void qh_renameridgevertex(ridgeT *ridge, vertexT *oldvertex, vertexT *newvertex);
+void qh_renamevertex(vertexT *oldvertex, vertexT *newvertex, setT *ridges,
+ facetT *oldfacet, facetT *neighborA);
+boolT qh_test_appendmerge(facetT *facet, facetT *neighbor);
+boolT qh_test_vneighbors(void /* qh.newfacet_list */);
+void qh_tracemerge(facetT *facet1, facetT *facet2);
+void qh_tracemerging(void);
+void qh_updatetested( facetT *facet1, facetT *facet2);
+setT *qh_vertexridges(vertexT *vertex);
+void qh_vertexridges_facet(vertexT *vertex, facetT *facet, setT **ridges);
+void qh_willdelete(facetT *facet, facetT *replace);
+
+#endif /* qhDEFmerge */
diff --git a/xs/src/qhull/src/libqhull/poly.c b/xs/src/qhull/src/libqhull/poly.c
new file mode 100644
index 000000000..b8db6a9ef
--- /dev/null
+++ b/xs/src/qhull/src/libqhull/poly.c
@@ -0,0 +1,1205 @@
+/*<html><pre> -<a href="qh-poly.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ poly.c
+ implements polygons and simplices
+
+ see qh-poly.htm, poly.h and libqhull.h
+
+ infrequent code is in poly2.c
+ (all but top 50 and their callers 12/3/95)
+
+ Copyright (c) 1993-2015 The Geometry Center.
+ $Id: //main/2015/qhull/src/libqhull/poly.c#3 $$Change: 2064 $
+ $DateTime: 2016/01/18 12:36:08 $$Author: bbarber $
+*/
+
+#include "qhull_a.h"
+
+/*======== functions in alphabetical order ==========*/
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="appendfacet">-</a>
+
+ qh_appendfacet( facet )
+ appends facet to end of qh.facet_list,
+
+ returns:
+ updates qh.newfacet_list, facet_next, facet_list
+ increments qh.numfacets
+
+ notes:
+ assumes qh.facet_list/facet_tail is defined (createsimplex)
+
+ see:
+ qh_removefacet()
+
+*/
+void qh_appendfacet(facetT *facet) {
+ facetT *tail= qh facet_tail;
+
+ if (tail == qh newfacet_list)
+ qh newfacet_list= facet;
+ if (tail == qh facet_next)
+ qh facet_next= facet;
+ facet->previous= tail->previous;
+ facet->next= tail;
+ if (tail->previous)
+ tail->previous->next= facet;
+ else
+ qh facet_list= facet;
+ tail->previous= facet;
+ qh num_facets++;
+ trace4((qh ferr, 4044, "qh_appendfacet: append f%d to facet_list\n", facet->id));
+} /* appendfacet */
+
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="appendvertex">-</a>
+
+ qh_appendvertex( vertex )
+ appends vertex to end of qh.vertex_list,
+
+ returns:
+ sets vertex->newlist
+ updates qh.vertex_list, newvertex_list
+ increments qh.num_vertices
+
+ notes:
+ assumes qh.vertex_list/vertex_tail is defined (createsimplex)
+
+*/
+void qh_appendvertex(vertexT *vertex) {
+ vertexT *tail= qh vertex_tail;
+
+ if (tail == qh newvertex_list)
+ qh newvertex_list= vertex;
+ vertex->newlist= True;
+ vertex->previous= tail->previous;
+ vertex->next= tail;
+ if (tail->previous)
+ tail->previous->next= vertex;
+ else
+ qh vertex_list= vertex;
+ tail->previous= vertex;
+ qh num_vertices++;
+ trace4((qh ferr, 4045, "qh_appendvertex: append v%d to vertex_list\n", vertex->id));
+} /* appendvertex */
+
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="attachnewfacets">-</a>
+
+ qh_attachnewfacets( )
+ attach horizon facets to new facets in qh.newfacet_list
+ newfacets have neighbor and ridge links to horizon but not vice versa
+ only needed for qh.ONLYgood
+
+ returns:
+ set qh.NEWfacets
+ horizon facets linked to new facets
+ ridges changed from visible facets to new facets
+ simplicial ridges deleted
+ qh.visible_list, no ridges valid
+ facet->f.replace is a newfacet (if any)
+
+ design:
+ delete interior ridges and neighbor sets by
+ for each visible, non-simplicial facet
+ for each ridge
+ if last visit or if neighbor is simplicial
+ if horizon neighbor
+ delete ridge for horizon's ridge set
+ delete ridge
+ erase neighbor set
+ attach horizon facets and new facets by
+ for all new facets
+ if corresponding horizon facet is simplicial
+ locate corresponding visible facet {may be more than one}
+ link visible facet to new facet
+ replace visible facet with new facet in horizon
+ else it's non-simplicial
+ for all visible neighbors of the horizon facet
+ link visible neighbor to new facet
+ delete visible neighbor from horizon facet
+ append new facet to horizon's neighbors
+ the first ridge of the new facet is the horizon ridge
+ link the new facet into the horizon ridge
+*/
+void qh_attachnewfacets(void /* qh.visible_list, newfacet_list */) {
+ facetT *newfacet= NULL, *neighbor, **neighborp, *horizon, *visible;
+ ridgeT *ridge, **ridgep;
+
+ qh NEWfacets= True;
+ trace3((qh ferr, 3012, "qh_attachnewfacets: delete interior ridges\n"));
+ qh visit_id++;
+ FORALLvisible_facets {
+ visible->visitid= qh visit_id;
+ if (visible->ridges) {
+ FOREACHridge_(visible->ridges) {
+ neighbor= otherfacet_(ridge, visible);
+ if (neighbor->visitid == qh visit_id
+ || (!neighbor->visible && neighbor->simplicial)) {
+ if (!neighbor->visible) /* delete ridge for simplicial horizon */
+ qh_setdel(neighbor->ridges, ridge);
+ qh_setfree(&(ridge->vertices)); /* delete on 2nd visit */
+ qh_memfree(ridge, (int)sizeof(ridgeT));
+ }
+ }
+ SETfirst_(visible->ridges)= NULL;
+ }
+ SETfirst_(visible->neighbors)= NULL;
+ }
+ trace1((qh ferr, 1017, "qh_attachnewfacets: attach horizon facets to new facets\n"));
+ FORALLnew_facets {
+ horizon= SETfirstt_(newfacet->neighbors, facetT);
+ if (horizon->simplicial) {
+ visible= NULL;
+ FOREACHneighbor_(horizon) { /* may have more than one horizon ridge */
+ if (neighbor->visible) {
+ if (visible) {
+ if (qh_setequal_skip(newfacet->vertices, 0, horizon->vertices,
+ SETindex_(horizon->neighbors, neighbor))) {
+ visible= neighbor;
+ break;
+ }
+ }else
+ visible= neighbor;
+ }
+ }
+ if (visible) {
+ visible->f.replace= newfacet;
+ qh_setreplace(horizon->neighbors, visible, newfacet);
+ }else {
+ qh_fprintf(qh ferr, 6102, "qhull internal error (qh_attachnewfacets): couldn't find visible facet for horizon f%d of newfacet f%d\n",
+ horizon->id, newfacet->id);
+ qh_errexit2(qh_ERRqhull, horizon, newfacet);
+ }
+ }else { /* non-simplicial, with a ridge for newfacet */
+ FOREACHneighbor_(horizon) { /* may hold for many new facets */
+ if (neighbor->visible) {
+ neighbor->f.replace= newfacet;
+ qh_setdelnth(horizon->neighbors,
+ SETindex_(horizon->neighbors, neighbor));
+ neighborp--; /* repeat */
+ }
+ }
+ qh_setappend(&horizon->neighbors, newfacet);
+ ridge= SETfirstt_(newfacet->ridges, ridgeT);
+ if (ridge->top == horizon)
+ ridge->bottom= newfacet;
+ else
+ ridge->top= newfacet;
+ }
+ } /* newfacets */
+ if (qh PRINTstatistics) {
+ FORALLvisible_facets {
+ if (!visible->f.replace)
+ zinc_(Zinsidevisible);
+ }
+ }
+} /* attachnewfacets */
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="checkflipped">-</a>
+
+ qh_checkflipped( facet, dist, allerror )
+ checks facet orientation to interior point
+
+ if allerror set,
+ tests against qh.DISTround
+ else
+ tests against 0 since tested against DISTround before
+
+ returns:
+ False if it flipped orientation (sets facet->flipped)
+ distance if non-NULL
+*/
+boolT qh_checkflipped(facetT *facet, realT *distp, boolT allerror) {
+ realT dist;
+
+ if (facet->flipped && !distp)
+ return False;
+ zzinc_(Zdistcheck);
+ qh_distplane(qh interior_point, facet, &dist);
+ if (distp)
+ *distp= dist;
+ if ((allerror && dist > -qh DISTround)|| (!allerror && dist >= 0.0)) {
+ facet->flipped= True;
+ zzinc_(Zflippedfacets);
+ trace0((qh ferr, 19, "qh_checkflipped: facet f%d is flipped, distance= %6.12g during p%d\n",
+ facet->id, dist, qh furthest_id));
+ qh_precision("flipped facet");
+ return False;
+ }
+ return True;
+} /* checkflipped */
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="delfacet">-</a>
+
+ qh_delfacet( facet )
+ removes facet from facet_list and frees up its memory
+
+ notes:
+ assumes vertices and ridges already freed
+*/
+void qh_delfacet(facetT *facet) {
+ void **freelistp; /* used if !qh_NOmem by qh_memfree_() */
+
+ trace4((qh ferr, 4046, "qh_delfacet: delete f%d\n", facet->id));
+ if (facet == qh tracefacet)
+ qh tracefacet= NULL;
+ if (facet == qh GOODclosest)
+ qh GOODclosest= NULL;
+ qh_removefacet(facet);
+ if (!facet->tricoplanar || facet->keepcentrum) {
+ qh_memfree_(facet->normal, qh normal_size, freelistp);
+ if (qh CENTERtype == qh_ASvoronoi) { /* braces for macro calls */
+ qh_memfree_(facet->center, qh center_size, freelistp);
+ }else /* AScentrum */ {
+ qh_memfree_(facet->center, qh normal_size, freelistp);
+ }
+ }
+ qh_setfree(&(facet->neighbors));
+ if (facet->ridges)
+ qh_setfree(&(facet->ridges));
+ qh_setfree(&(facet->vertices));
+ if (facet->outsideset)
+ qh_setfree(&(facet->outsideset));
+ if (facet->coplanarset)
+ qh_setfree(&(facet->coplanarset));
+ qh_memfree_(facet, (int)sizeof(facetT), freelistp);
+} /* delfacet */
+
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="deletevisible">-</a>
+
+ qh_deletevisible()
+ delete visible facets and vertices
+
+ returns:
+ deletes each facet and removes from facetlist
+ at exit, qh.visible_list empty (== qh.newfacet_list)
+
+ notes:
+ ridges already deleted
+ horizon facets do not reference facets on qh.visible_list
+ new facets in qh.newfacet_list
+ uses qh.visit_id;
+*/
+void qh_deletevisible(void /*qh.visible_list*/) {
+ facetT *visible, *nextfacet;
+ vertexT *vertex, **vertexp;
+ int numvisible= 0, numdel= qh_setsize(qh del_vertices);
+
+ trace1((qh ferr, 1018, "qh_deletevisible: delete %d visible facets and %d vertices\n",
+ qh num_visible, numdel));
+ for (visible= qh visible_list; visible && visible->visible;
+ visible= nextfacet) { /* deleting current */
+ nextfacet= visible->next;
+ numvisible++;
+ qh_delfacet(visible);
+ }
+ if (numvisible != qh num_visible) {
+ qh_fprintf(qh ferr, 6103, "qhull internal error (qh_deletevisible): qh num_visible %d is not number of visible facets %d\n",
+ qh num_visible, numvisible);
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ qh num_visible= 0;
+ zadd_(Zvisfacettot, numvisible);
+ zmax_(Zvisfacetmax, numvisible);
+ zzadd_(Zdelvertextot, numdel);
+ zmax_(Zdelvertexmax, numdel);
+ FOREACHvertex_(qh del_vertices)
+ qh_delvertex(vertex);
+ qh_settruncate(qh del_vertices, 0);
+} /* deletevisible */
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="facetintersect">-</a>
+
+ qh_facetintersect( facetA, facetB, skipa, skipB, prepend )
+ return vertices for intersection of two simplicial facets
+ may include 1 prepended entry (if more, need to settemppush)
+
+ returns:
+ returns set of qh.hull_dim-1 + prepend vertices
+ returns skipped index for each test and checks for exactly one
+
+ notes:
+ does not need settemp since set in quick memory
+
+ see also:
+ qh_vertexintersect and qh_vertexintersect_new
+ use qh_setnew_delnthsorted to get nth ridge (no skip information)
+
+ design:
+ locate skipped vertex by scanning facet A's neighbors
+ locate skipped vertex by scanning facet B's neighbors
+ intersect the vertex sets
+*/
+setT *qh_facetintersect(facetT *facetA, facetT *facetB,
+ int *skipA,int *skipB, int prepend) {
+ setT *intersect;
+ int dim= qh hull_dim, i, j;
+ facetT **neighborsA, **neighborsB;
+
+ neighborsA= SETaddr_(facetA->neighbors, facetT);
+ neighborsB= SETaddr_(facetB->neighbors, facetT);
+ i= j= 0;
+ if (facetB == *neighborsA++)
+ *skipA= 0;
+ else if (facetB == *neighborsA++)
+ *skipA= 1;
+ else if (facetB == *neighborsA++)
+ *skipA= 2;
+ else {
+ for (i=3; i < dim; i++) {
+ if (facetB == *neighborsA++) {
+ *skipA= i;
+ break;
+ }
+ }
+ }
+ if (facetA == *neighborsB++)
+ *skipB= 0;
+ else if (facetA == *neighborsB++)
+ *skipB= 1;
+ else if (facetA == *neighborsB++)
+ *skipB= 2;
+ else {
+ for (j=3; j < dim; j++) {
+ if (facetA == *neighborsB++) {
+ *skipB= j;
+ break;
+ }
+ }
+ }
+ if (i >= dim || j >= dim) {
+ qh_fprintf(qh ferr, 6104, "qhull internal error (qh_facetintersect): f%d or f%d not in others neighbors\n",
+ facetA->id, facetB->id);
+ qh_errexit2(qh_ERRqhull, facetA, facetB);
+ }
+ intersect= qh_setnew_delnthsorted(facetA->vertices, qh hull_dim, *skipA, prepend);
+ trace4((qh ferr, 4047, "qh_facetintersect: f%d skip %d matches f%d skip %d\n",
+ facetA->id, *skipA, facetB->id, *skipB));
+ return(intersect);
+} /* facetintersect */
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="gethash">-</a>
+
+ qh_gethash( hashsize, set, size, firstindex, skipelem )
+ return hashvalue for a set with firstindex and skipelem
+
+ notes:
+ returned hash is in [0,hashsize)
+ assumes at least firstindex+1 elements
+ assumes skipelem is NULL, in set, or part of hash
+
+ hashes memory addresses which may change over different runs of the same data
+ using sum for hash does badly in high d
+*/
+int qh_gethash(int hashsize, setT *set, int size, int firstindex, void *skipelem) {
+ void **elemp= SETelemaddr_(set, firstindex, void);
+ ptr_intT hash = 0, elem;
+ unsigned result;
+ int i;
+#ifdef _MSC_VER /* Microsoft Visual C++ -- warn about 64-bit issues */
+#pragma warning( push) /* WARN64 -- ptr_intT holds a 64-bit pointer */
+#pragma warning( disable : 4311) /* 'type cast': pointer truncation from 'void*' to 'ptr_intT' */
+#endif
+
+ switch (size-firstindex) {
+ case 1:
+ hash= (ptr_intT)(*elemp) - (ptr_intT) skipelem;
+ break;
+ case 2:
+ hash= (ptr_intT)(*elemp) + (ptr_intT)elemp[1] - (ptr_intT) skipelem;
+ break;
+ case 3:
+ hash= (ptr_intT)(*elemp) + (ptr_intT)elemp[1] + (ptr_intT)elemp[2]
+ - (ptr_intT) skipelem;
+ break;
+ case 4:
+ hash= (ptr_intT)(*elemp) + (ptr_intT)elemp[1] + (ptr_intT)elemp[2]
+ + (ptr_intT)elemp[3] - (ptr_intT) skipelem;
+ break;
+ case 5:
+ hash= (ptr_intT)(*elemp) + (ptr_intT)elemp[1] + (ptr_intT)elemp[2]
+ + (ptr_intT)elemp[3] + (ptr_intT)elemp[4] - (ptr_intT) skipelem;
+ break;
+ case 6:
+ hash= (ptr_intT)(*elemp) + (ptr_intT)elemp[1] + (ptr_intT)elemp[2]
+ + (ptr_intT)elemp[3] + (ptr_intT)elemp[4]+ (ptr_intT)elemp[5]
+ - (ptr_intT) skipelem;
+ break;
+ default:
+ hash= 0;
+ i= 3;
+ do { /* this is about 10% in 10-d */
+ if ((elem= (ptr_intT)*elemp++) != (ptr_intT)skipelem) {
+ hash ^= (elem << i) + (elem >> (32-i));
+ i += 3;
+ if (i >= 32)
+ i -= 32;
+ }
+ }while (*elemp);
+ break;
+ }
+ if (hashsize<0) {
+ qh_fprintf(qh ferr, 6202, "qhull internal error: negative hashsize %d passed to qh_gethash [poly.c]\n", hashsize);
+ qh_errexit2(qh_ERRqhull, NULL, NULL);
+ }
+ result= (unsigned)hash;
+ result %= (unsigned)hashsize;
+ /* result= 0; for debugging */
+ return result;
+#ifdef _MSC_VER
+#pragma warning( pop)
+#endif
+} /* gethash */
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="makenewfacet">-</a>
+
+ qh_makenewfacet( vertices, toporient, horizon )
+ creates a toporient? facet from vertices
+
+ returns:
+ returns newfacet
+ adds newfacet to qh.facet_list
+ newfacet->vertices= vertices
+ if horizon
+ newfacet->neighbor= horizon, but not vice versa
+ newvertex_list updated with vertices
+*/
+facetT *qh_makenewfacet(setT *vertices, boolT toporient,facetT *horizon) {
+ facetT *newfacet;
+ vertexT *vertex, **vertexp;
+
+ FOREACHvertex_(vertices) {
+ if (!vertex->newlist) {
+ qh_removevertex(vertex);
+ qh_appendvertex(vertex);
+ }
+ }
+ newfacet= qh_newfacet();
+ newfacet->vertices= vertices;
+ newfacet->toporient= (unsigned char)toporient;
+ if (horizon)
+ qh_setappend(&(newfacet->neighbors), horizon);
+ qh_appendfacet(newfacet);
+ return(newfacet);
+} /* makenewfacet */
+
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="makenewplanes">-</a>
+
+ qh_makenewplanes()
+ make new hyperplanes for facets on qh.newfacet_list
+
+ returns:
+ all facets have hyperplanes or are marked for merging
+ doesn't create hyperplane if horizon is coplanar (will merge)
+ updates qh.min_vertex if qh.JOGGLEmax
+
+ notes:
+ facet->f.samecycle is defined for facet->mergehorizon facets
+*/
+void qh_makenewplanes(void /* newfacet_list */) {
+ facetT *newfacet;
+
+ FORALLnew_facets {
+ if (!newfacet->mergehorizon)
+ qh_setfacetplane(newfacet);
+ }
+ if (qh JOGGLEmax < REALmax/2)
+ minimize_(qh min_vertex, -wwval_(Wnewvertexmax));
+} /* makenewplanes */
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="makenew_nonsimplicial">-</a>
+
+ qh_makenew_nonsimplicial( visible, apex, numnew )
+ make new facets for ridges of a visible facet
+
+ returns:
+ first newfacet, bumps numnew as needed
+ attaches new facets if !qh.ONLYgood
+ marks ridge neighbors for simplicial visible
+ if (qh.ONLYgood)
+ ridges on newfacet, horizon, and visible
+ else
+ ridge and neighbors between newfacet and horizon
+ visible facet's ridges are deleted
+
+ notes:
+ qh.visit_id if visible has already been processed
+ sets neighbor->seen for building f.samecycle
+ assumes all 'seen' flags initially false
+
+ design:
+ for each ridge of visible facet
+ get neighbor of visible facet
+ if neighbor was already processed
+ delete the ridge (will delete all visible facets later)
+ if neighbor is a horizon facet
+ create a new facet
+ if neighbor coplanar
+ adds newfacet to f.samecycle for later merging
+ else
+ updates neighbor's neighbor set
+ (checks for non-simplicial facet with multiple ridges to visible facet)
+ updates neighbor's ridge set
+ (checks for simplicial neighbor to non-simplicial visible facet)
+ (deletes ridge if neighbor is simplicial)
+
+*/
+#ifndef qh_NOmerge
+facetT *qh_makenew_nonsimplicial(facetT *visible, vertexT *apex, int *numnew) {
+ void **freelistp; /* used if !qh_NOmem by qh_memfree_() */
+ ridgeT *ridge, **ridgep;
+ facetT *neighbor, *newfacet= NULL, *samecycle;
+ setT *vertices;
+ boolT toporient;
+ int ridgeid;
+
+ FOREACHridge_(visible->ridges) {
+ ridgeid= ridge->id;
+ neighbor= otherfacet_(ridge, visible);
+ if (neighbor->visible) {
+ if (!qh ONLYgood) {
+ if (neighbor->visitid == qh visit_id) {
+ qh_setfree(&(ridge->vertices)); /* delete on 2nd visit */
+ qh_memfree_(ridge, (int)sizeof(ridgeT), freelistp);
+ }
+ }
+ }else { /* neighbor is an horizon facet */
+ toporient= (ridge->top == visible);
+ vertices= qh_setnew(qh hull_dim); /* makes sure this is quick */
+ qh_setappend(&vertices, apex);
+ qh_setappend_set(&vertices, ridge->vertices);
+ newfacet= qh_makenewfacet(vertices, toporient, neighbor);
+ (*numnew)++;
+ if (neighbor->coplanar) {
+ newfacet->mergehorizon= True;
+ if (!neighbor->seen) {
+ newfacet->f.samecycle= newfacet;
+ neighbor->f.newcycle= newfacet;
+ }else {
+ samecycle= neighbor->f.newcycle;
+ newfacet->f.samecycle= samecycle->f.samecycle;
+ samecycle->f.samecycle= newfacet;
+ }
+ }
+ if (qh ONLYgood) {
+ if (!neighbor->simplicial)
+ qh_setappend(&(newfacet->ridges), ridge);
+ }else { /* qh_attachnewfacets */
+ if (neighbor->seen) {
+ if (neighbor->simplicial) {
+ qh_fprintf(qh ferr, 6105, "qhull internal error (qh_makenew_nonsimplicial): simplicial f%d sharing two ridges with f%d\n",
+ neighbor->id, visible->id);
+ qh_errexit2(qh_ERRqhull, neighbor, visible);
+ }
+ qh_setappend(&(neighbor->neighbors), newfacet);
+ }else
+ qh_setreplace(neighbor->neighbors, visible, newfacet);
+ if (neighbor->simplicial) {
+ qh_setdel(neighbor->ridges, ridge);
+ qh_setfree(&(ridge->vertices));
+ qh_memfree(ridge, (int)sizeof(ridgeT));
+ }else {
+ qh_setappend(&(newfacet->ridges), ridge);
+ if (toporient)
+ ridge->top= newfacet;
+ else
+ ridge->bottom= newfacet;
+ }
+ trace4((qh ferr, 4048, "qh_makenew_nonsimplicial: created facet f%d from v%d and r%d of horizon f%d\n",
+ newfacet->id, apex->id, ridgeid, neighbor->id));
+ }
+ }
+ neighbor->seen= True;
+ } /* for each ridge */
+ if (!qh ONLYgood)
+ SETfirst_(visible->ridges)= NULL;
+ return newfacet;
+} /* makenew_nonsimplicial */
+#else /* qh_NOmerge */
+facetT *qh_makenew_nonsimplicial(facetT *visible, vertexT *apex, int *numnew) {
+ return NULL;
+}
+#endif /* qh_NOmerge */
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="makenew_simplicial">-</a>
+
+ qh_makenew_simplicial( visible, apex, numnew )
+ make new facets for simplicial visible facet and apex
+
+ returns:
+ attaches new facets if (!qh.ONLYgood)
+ neighbors between newfacet and horizon
+
+ notes:
+ nop if neighbor->seen or neighbor->visible(see qh_makenew_nonsimplicial)
+
+ design:
+ locate neighboring horizon facet for visible facet
+ determine vertices and orientation
+ create new facet
+ if coplanar,
+ add new facet to f.samecycle
+ update horizon facet's neighbor list
+*/
+facetT *qh_makenew_simplicial(facetT *visible, vertexT *apex, int *numnew) {
+ facetT *neighbor, **neighborp, *newfacet= NULL;
+ setT *vertices;
+ boolT flip, toporient;
+ int horizonskip= 0, visibleskip= 0;
+
+ FOREACHneighbor_(visible) {
+ if (!neighbor->seen && !neighbor->visible) {
+ vertices= qh_facetintersect(neighbor,visible, &horizonskip, &visibleskip, 1);
+ SETfirst_(vertices)= apex;
+ flip= ((horizonskip & 0x1) ^ (visibleskip & 0x1));
+ if (neighbor->toporient)
+ toporient= horizonskip & 0x1;
+ else
+ toporient= (horizonskip & 0x1) ^ 0x1;
+ newfacet= qh_makenewfacet(vertices, toporient, neighbor);
+ (*numnew)++;
+ if (neighbor->coplanar && (qh PREmerge || qh MERGEexact)) {
+#ifndef qh_NOmerge
+ newfacet->f.samecycle= newfacet;
+ newfacet->mergehorizon= True;
+#endif
+ }
+ if (!qh ONLYgood)
+ SETelem_(neighbor->neighbors, horizonskip)= newfacet;
+ trace4((qh ferr, 4049, "qh_makenew_simplicial: create facet f%d top %d from v%d and horizon f%d skip %d top %d and visible f%d skip %d, flip? %d\n",
+ newfacet->id, toporient, apex->id, neighbor->id, horizonskip,
+ neighbor->toporient, visible->id, visibleskip, flip));
+ }
+ }
+ return newfacet;
+} /* makenew_simplicial */
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="matchneighbor">-</a>
+
+ qh_matchneighbor( newfacet, newskip, hashsize, hashcount )
+ either match subridge of newfacet with neighbor or add to hash_table
+
+ returns:
+ duplicate ridges are unmatched and marked by qh_DUPLICATEridge
+
+ notes:
+ ridge is newfacet->vertices w/o newskip vertex
+ do not allocate memory (need to free hash_table cleanly)
+ uses linear hash chains
+
+ see also:
+ qh_matchduplicates
+
+ design:
+ for each possible matching facet in qh.hash_table
+ if vertices match
+ set ismatch, if facets have opposite orientation
+ if ismatch and matching facet doesn't have a match
+ match the facets by updating their neighbor sets
+ else
+ indicate a duplicate ridge
+ set facet hyperplane for later testing
+ add facet to hashtable
+ unless the other facet was already a duplicate ridge
+ mark both facets with a duplicate ridge
+ add other facet (if defined) to hash table
+*/
+void qh_matchneighbor(facetT *newfacet, int newskip, int hashsize, int *hashcount) {
+ boolT newfound= False; /* True, if new facet is already in hash chain */
+ boolT same, ismatch;
+ int hash, scan;
+ facetT *facet, *matchfacet;
+ int skip, matchskip;
+
+ hash= qh_gethash(hashsize, newfacet->vertices, qh hull_dim, 1,
+ SETelem_(newfacet->vertices, newskip));
+ trace4((qh ferr, 4050, "qh_matchneighbor: newfacet f%d skip %d hash %d hashcount %d\n",
+ newfacet->id, newskip, hash, *hashcount));
+ zinc_(Zhashlookup);
+ for (scan= hash; (facet= SETelemt_(qh hash_table, scan, facetT));
+ scan= (++scan >= hashsize ? 0 : scan)) {
+ if (facet == newfacet) {
+ newfound= True;
+ continue;
+ }
+ zinc_(Zhashtests);
+ if (qh_matchvertices(1, newfacet->vertices, newskip, facet->vertices, &skip, &same)) {
+ if (SETelem_(newfacet->vertices, newskip) ==
+ SETelem_(facet->vertices, skip)) {
+ qh_precision("two facets with the same vertices");
+ qh_fprintf(qh ferr, 6106, "qhull precision error: Vertex sets are the same for f%d and f%d. Can not force output.\n",
+ facet->id, newfacet->id);
+ qh_errexit2(qh_ERRprec, facet, newfacet);
+ }
+ ismatch= (same == (boolT)((newfacet->toporient ^ facet->toporient)));
+ matchfacet= SETelemt_(facet->neighbors, skip, facetT);
+ if (ismatch && !matchfacet) {
+ SETelem_(facet->neighbors, skip)= newfacet;
+ SETelem_(newfacet->neighbors, newskip)= facet;
+ (*hashcount)--;
+ trace4((qh ferr, 4051, "qh_matchneighbor: f%d skip %d matched with new f%d skip %d\n",
+ facet->id, skip, newfacet->id, newskip));
+ return;
+ }
+ if (!qh PREmerge && !qh MERGEexact) {
+ qh_precision("a ridge with more than two neighbors");
+ qh_fprintf(qh ferr, 6107, "qhull precision error: facets f%d, f%d and f%d meet at a ridge with more than 2 neighbors. Can not continue.\n",
+ facet->id, newfacet->id, getid_(matchfacet));
+ qh_errexit2(qh_ERRprec, facet, newfacet);
+ }
+ SETelem_(newfacet->neighbors, newskip)= qh_DUPLICATEridge;
+ newfacet->dupridge= True;
+ if (!newfacet->normal)
+ qh_setfacetplane(newfacet);
+ qh_addhash(newfacet, qh hash_table, hashsize, hash);
+ (*hashcount)++;
+ if (!facet->normal)
+ qh_setfacetplane(facet);
+ if (matchfacet != qh_DUPLICATEridge) {
+ SETelem_(facet->neighbors, skip)= qh_DUPLICATEridge;
+ facet->dupridge= True;
+ if (!facet->normal)
+ qh_setfacetplane(facet);
+ if (matchfacet) {
+ matchskip= qh_setindex(matchfacet->neighbors, facet);
+ if (matchskip<0) {
+ qh_fprintf(qh ferr, 6260, "qhull internal error (qh_matchneighbor): matchfacet f%d is in f%d neighbors but not vice versa. Can not continue.\n",
+ matchfacet->id, facet->id);
+ qh_errexit2(qh_ERRqhull, matchfacet, facet);
+ }
+ SETelem_(matchfacet->neighbors, matchskip)= qh_DUPLICATEridge; /* matchskip>=0 by QH6260 */
+ matchfacet->dupridge= True;
+ if (!matchfacet->normal)
+ qh_setfacetplane(matchfacet);
+ qh_addhash(matchfacet, qh hash_table, hashsize, hash);
+ *hashcount += 2;
+ }
+ }
+ trace4((qh ferr, 4052, "qh_matchneighbor: new f%d skip %d duplicates ridge for f%d skip %d matching f%d ismatch %d at hash %d\n",
+ newfacet->id, newskip, facet->id, skip,
+ (matchfacet == qh_DUPLICATEridge ? -2 : getid_(matchfacet)),
+ ismatch, hash));
+ return; /* end of duplicate ridge */
+ }
+ }
+ if (!newfound)
+ SETelem_(qh hash_table, scan)= newfacet; /* same as qh_addhash */
+ (*hashcount)++;
+ trace4((qh ferr, 4053, "qh_matchneighbor: no match for f%d skip %d at hash %d\n",
+ newfacet->id, newskip, hash));
+} /* matchneighbor */
+
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="matchnewfacets">-</a>
+
+ qh_matchnewfacets()
+ match newfacets in qh.newfacet_list to their newfacet neighbors
+
+ returns:
+ qh.newfacet_list with full neighbor sets
+ get vertices with nth neighbor by deleting nth vertex
+ if qh.PREmerge/MERGEexact or qh.FORCEoutput
+ sets facet->flippped if flipped normal (also prevents point partitioning)
+ if duplicate ridges and qh.PREmerge/MERGEexact
+ sets facet->dupridge
+ missing neighbor links identifies extra ridges to be merging (qh_MERGEridge)
+
+ notes:
+ newfacets already have neighbor[0] (horizon facet)
+ assumes qh.hash_table is NULL
+ vertex->neighbors has not been updated yet
+ do not allocate memory after qh.hash_table (need to free it cleanly)
+
+ design:
+ delete neighbor sets for all new facets
+ initialize a hash table
+ for all new facets
+ match facet with neighbors
+ if unmatched facets (due to duplicate ridges)
+ for each new facet with a duplicate ridge
+ match it with a facet
+ check for flipped facets
+*/
+void qh_matchnewfacets(void /* qh.newfacet_list */) {
+ int numnew=0, hashcount=0, newskip;
+ facetT *newfacet, *neighbor;
+ int dim= qh hull_dim, hashsize, neighbor_i, neighbor_n;
+ setT *neighbors;
+#ifndef qh_NOtrace
+ int facet_i, facet_n, numfree= 0;
+ facetT *facet;
+#endif
+
+ trace1((qh ferr, 1019, "qh_matchnewfacets: match neighbors for new facets.\n"));
+ FORALLnew_facets {
+ numnew++;
+ { /* inline qh_setzero(newfacet->neighbors, 1, qh hull_dim); */
+ neighbors= newfacet->neighbors;
+ neighbors->e[neighbors->maxsize].i= dim+1; /*may be overwritten*/
+ memset((char *)SETelemaddr_(neighbors, 1, void), 0, dim * SETelemsize);
+ }
+ }
+
+ qh_newhashtable(numnew*(qh hull_dim-1)); /* twice what is normally needed,
+ but every ridge could be DUPLICATEridge */
+ hashsize= qh_setsize(qh hash_table);
+ FORALLnew_facets {
+ for (newskip=1; newskip<qh hull_dim; newskip++) /* furthest/horizon already matched */
+ /* hashsize>0 because hull_dim>1 and numnew>0 */
+ qh_matchneighbor(newfacet, newskip, hashsize, &hashcount);
+#if 0 /* use the following to trap hashcount errors */
+ {
+ int count= 0, k;
+ facetT *facet, *neighbor;
+
+ count= 0;
+ FORALLfacet_(qh newfacet_list) { /* newfacet already in use */
+ for (k=1; k < qh hull_dim; k++) {
+ neighbor= SETelemt_(facet->neighbors, k, facetT);
+ if (!neighbor || neighbor == qh_DUPLICATEridge)
+ count++;
+ }
+ if (facet == newfacet)
+ break;
+ }
+ if (count != hashcount) {
+ qh_fprintf(qh ferr, 8088, "qh_matchnewfacets: after adding facet %d, hashcount %d != count %d\n",
+ newfacet->id, hashcount, count);
+ qh_errexit(qh_ERRqhull, newfacet, NULL);
+ }
+ }
+#endif /* end of trap code */
+ }
+ if (hashcount) {
+ FORALLnew_facets {
+ if (newfacet->dupridge) {
+ FOREACHneighbor_i_(newfacet) {
+ if (neighbor == qh_DUPLICATEridge) {
+ qh_matchduplicates(newfacet, neighbor_i, hashsize, &hashcount);
+ /* this may report MERGEfacet */
+ }
+ }
+ }
+ }
+ }
+ if (hashcount) {
+ qh_fprintf(qh ferr, 6108, "qhull internal error (qh_matchnewfacets): %d neighbors did not match up\n",
+ hashcount);
+ qh_printhashtable(qh ferr);
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+#ifndef qh_NOtrace
+ if (qh IStracing >= 2) {
+ FOREACHfacet_i_(qh hash_table) {
+ if (!facet)
+ numfree++;
+ }
+ qh_fprintf(qh ferr, 8089, "qh_matchnewfacets: %d new facets, %d unused hash entries . hashsize %d\n",
+ numnew, numfree, qh_setsize(qh hash_table));
+ }
+#endif /* !qh_NOtrace */
+ qh_setfree(&qh hash_table);
+ if (qh PREmerge || qh MERGEexact) {
+ if (qh IStracing >= 4)
+ qh_printfacetlist(qh newfacet_list, NULL, qh_ALL);
+ FORALLnew_facets {
+ if (newfacet->normal)
+ qh_checkflipped(newfacet, NULL, qh_ALL);
+ }
+ }else if (qh FORCEoutput)
+ qh_checkflipped_all(qh newfacet_list); /* prints warnings for flipped */
+} /* matchnewfacets */
+
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="matchvertices">-</a>
+
+ qh_matchvertices( firstindex, verticesA, skipA, verticesB, skipB, same )
+ tests whether vertices match with a single skip
+ starts match at firstindex since all new facets have a common vertex
+
+ returns:
+ true if matched vertices
+ skip index for each set
+ sets same iff vertices have the same orientation
+
+ notes:
+ assumes skipA is in A and both sets are the same size
+
+ design:
+ set up pointers
+ scan both sets checking for a match
+ test orientation
+*/
+boolT qh_matchvertices(int firstindex, setT *verticesA, int skipA,
+ setT *verticesB, int *skipB, boolT *same) {
+ vertexT **elemAp, **elemBp, **skipBp=NULL, **skipAp;
+
+ elemAp= SETelemaddr_(verticesA, firstindex, vertexT);
+ elemBp= SETelemaddr_(verticesB, firstindex, vertexT);
+ skipAp= SETelemaddr_(verticesA, skipA, vertexT);
+ do if (elemAp != skipAp) {
+ while (*elemAp != *elemBp++) {
+ if (skipBp)
+ return False;
+ skipBp= elemBp; /* one extra like FOREACH */
+ }
+ }while (*(++elemAp));
+ if (!skipBp)
+ skipBp= ++elemBp;
+ *skipB= SETindex_(verticesB, skipB); /* i.e., skipBp - verticesB */
+ *same= !((skipA & 0x1) ^ (*skipB & 0x1)); /* result is 0 or 1 */
+ trace4((qh ferr, 4054, "qh_matchvertices: matched by skip %d(v%d) and skip %d(v%d) same? %d\n",
+ skipA, (*skipAp)->id, *skipB, (*(skipBp-1))->id, *same));
+ return(True);
+} /* matchvertices */
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="newfacet">-</a>
+
+ qh_newfacet()
+ return a new facet
+
+ returns:
+ all fields initialized or cleared (NULL)
+ preallocates neighbors set
+*/
+facetT *qh_newfacet(void) {
+ facetT *facet;
+ void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */
+
+ qh_memalloc_((int)sizeof(facetT), freelistp, facet, facetT);
+ memset((char *)facet, (size_t)0, sizeof(facetT));
+ if (qh facet_id == qh tracefacet_id)
+ qh tracefacet= facet;
+ facet->id= qh facet_id++;
+ facet->neighbors= qh_setnew(qh hull_dim);
+#if !qh_COMPUTEfurthest
+ facet->furthestdist= 0.0;
+#endif
+#if qh_MAXoutside
+ if (qh FORCEoutput && qh APPROXhull)
+ facet->maxoutside= qh MINoutside;
+ else
+ facet->maxoutside= qh DISTround;
+#endif
+ facet->simplicial= True;
+ facet->good= True;
+ facet->newfacet= True;
+ trace4((qh ferr, 4055, "qh_newfacet: created facet f%d\n", facet->id));
+ return(facet);
+} /* newfacet */
+
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="newridge">-</a>
+
+ qh_newridge()
+ return a new ridge
+*/
+ridgeT *qh_newridge(void) {
+ ridgeT *ridge;
+ void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */
+
+ qh_memalloc_((int)sizeof(ridgeT), freelistp, ridge, ridgeT);
+ memset((char *)ridge, (size_t)0, sizeof(ridgeT));
+ zinc_(Ztotridges);
+ if (qh ridge_id == UINT_MAX) {
+ qh_fprintf(qh ferr, 7074, "\
+qhull warning: more than 2^32 ridges. Qhull results are OK. Since the ridge ID wraps around to 0, two ridges may have the same identifier.\n");
+ }
+ ridge->id= qh ridge_id++;
+ trace4((qh ferr, 4056, "qh_newridge: created ridge r%d\n", ridge->id));
+ return(ridge);
+} /* newridge */
+
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="pointid">-</a>
+
+ qh_pointid( point )
+ return id for a point,
+ returns qh_IDnone(-3) if null, qh_IDinterior(-2) if interior, or qh_IDunknown(-1) if not known
+
+ alternative code if point is in qh.first_point...
+ unsigned long id;
+ id= ((unsigned long)point - (unsigned long)qh.first_point)/qh.normal_size;
+
+ notes:
+ Valid points are non-negative
+ WARN64 -- id truncated to 32-bits, at most 2G points
+ NOerrors returned (QhullPoint::id)
+ if point not in point array
+ the code does a comparison of unrelated pointers.
+*/
+int qh_pointid(pointT *point) {
+ ptr_intT offset, id;
+
+ if (!point)
+ return qh_IDnone;
+ else if (point == qh interior_point)
+ return qh_IDinterior;
+ else if (point >= qh first_point
+ && point < qh first_point + qh num_points * qh hull_dim) {
+ offset= (ptr_intT)(point - qh first_point);
+ id= offset / qh hull_dim;
+ }else if ((id= qh_setindex(qh other_points, point)) != -1)
+ id += qh num_points;
+ else
+ return qh_IDunknown;
+ return (int)id;
+} /* pointid */
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="removefacet">-</a>
+
+ qh_removefacet( facet )
+ unlinks facet from qh.facet_list,
+
+ returns:
+ updates qh.facet_list .newfacet_list .facet_next visible_list
+ decrements qh.num_facets
+
+ see:
+ qh_appendfacet
+*/
+void qh_removefacet(facetT *facet) {
+ facetT *next= facet->next, *previous= facet->previous;
+
+ if (facet == qh newfacet_list)
+ qh newfacet_list= next;
+ if (facet == qh facet_next)
+ qh facet_next= next;
+ if (facet == qh visible_list)
+ qh visible_list= next;
+ if (previous) {
+ previous->next= next;
+ next->previous= previous;
+ }else { /* 1st facet in qh facet_list */
+ qh facet_list= next;
+ qh facet_list->previous= NULL;
+ }
+ qh num_facets--;
+ trace4((qh ferr, 4057, "qh_removefacet: remove f%d from facet_list\n", facet->id));
+} /* removefacet */
+
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="removevertex">-</a>
+
+ qh_removevertex( vertex )
+ unlinks vertex from qh.vertex_list,
+
+ returns:
+ updates qh.vertex_list .newvertex_list
+ decrements qh.num_vertices
+*/
+void qh_removevertex(vertexT *vertex) {
+ vertexT *next= vertex->next, *previous= vertex->previous;
+
+ if (vertex == qh newvertex_list)
+ qh newvertex_list= next;
+ if (previous) {
+ previous->next= next;
+ next->previous= previous;
+ }else { /* 1st vertex in qh vertex_list */
+ qh vertex_list= vertex->next;
+ qh vertex_list->previous= NULL;
+ }
+ qh num_vertices--;
+ trace4((qh ferr, 4058, "qh_removevertex: remove v%d from vertex_list\n", vertex->id));
+} /* removevertex */
+
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="updatevertices">-</a>
+
+ qh_updatevertices()
+ update vertex neighbors and delete interior vertices
+
+ returns:
+ if qh.VERTEXneighbors, updates neighbors for each vertex
+ if qh.newvertex_list,
+ removes visible neighbors from vertex neighbors
+ if qh.newfacet_list
+ adds new facets to vertex neighbors
+ if qh.visible_list
+ interior vertices added to qh.del_vertices for later partitioning
+
+ design:
+ if qh.VERTEXneighbors
+ deletes references to visible facets from vertex neighbors
+ appends new facets to the neighbor list for each vertex
+ checks all vertices of visible facets
+ removes visible facets from neighbor lists
+ marks unused vertices for deletion
+*/
+void qh_updatevertices(void /*qh.newvertex_list, newfacet_list, visible_list*/) {
+ facetT *newfacet= NULL, *neighbor, **neighborp, *visible;
+ vertexT *vertex, **vertexp;
+
+ trace3((qh ferr, 3013, "qh_updatevertices: delete interior vertices and update vertex->neighbors\n"));
+ if (qh VERTEXneighbors) {
+ FORALLvertex_(qh newvertex_list) {
+ FOREACHneighbor_(vertex) {
+ if (neighbor->visible)
+ SETref_(neighbor)= NULL;
+ }
+ qh_setcompact(vertex->neighbors);
+ }
+ FORALLnew_facets {
+ FOREACHvertex_(newfacet->vertices)
+ qh_setappend(&vertex->neighbors, newfacet);
+ }
+ FORALLvisible_facets {
+ FOREACHvertex_(visible->vertices) {
+ if (!vertex->newlist && !vertex->deleted) {
+ FOREACHneighbor_(vertex) { /* this can happen under merging */
+ if (!neighbor->visible)
+ break;
+ }
+ if (neighbor)
+ qh_setdel(vertex->neighbors, visible);
+ else {
+ vertex->deleted= True;
+ qh_setappend(&qh del_vertices, vertex);
+ trace2((qh ferr, 2041, "qh_updatevertices: delete vertex p%d(v%d) in f%d\n",
+ qh_pointid(vertex->point), vertex->id, visible->id));
+ }
+ }
+ }
+ }
+ }else { /* !VERTEXneighbors */
+ FORALLvisible_facets {
+ FOREACHvertex_(visible->vertices) {
+ if (!vertex->newlist && !vertex->deleted) {
+ vertex->deleted= True;
+ qh_setappend(&qh del_vertices, vertex);
+ trace2((qh ferr, 2042, "qh_updatevertices: delete vertex p%d(v%d) in f%d\n",
+ qh_pointid(vertex->point), vertex->id, visible->id));
+ }
+ }
+ }
+ }
+} /* updatevertices */
+
+
+
diff --git a/xs/src/qhull/src/libqhull/poly.h b/xs/src/qhull/src/libqhull/poly.h
new file mode 100644
index 000000000..af8b42077
--- /dev/null
+++ b/xs/src/qhull/src/libqhull/poly.h
@@ -0,0 +1,296 @@
+/*<html><pre> -<a href="qh-poly.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ poly.h
+ header file for poly.c and poly2.c
+
+ see qh-poly.htm, libqhull.h and poly.c
+
+ Copyright (c) 1993-2015 The Geometry Center.
+ $Id: //main/2015/qhull/src/libqhull/poly.h#3 $$Change: 2047 $
+ $DateTime: 2016/01/04 22:03:18 $$Author: bbarber $
+*/
+
+#ifndef qhDEFpoly
+#define qhDEFpoly 1
+
+#include "libqhull.h"
+
+/*=============== constants ========================== */
+
+/*-<a href="qh-geom.htm#TOC"
+ >--------------------------------</a><a name="ALGORITHMfault">-</a>
+
+ ALGORITHMfault
+ use as argument to checkconvex() to report errors during buildhull
+*/
+#define qh_ALGORITHMfault 0
+
+/*-<a href="qh-poly.htm#TOC"
+ >--------------------------------</a><a name="DATAfault">-</a>
+
+ DATAfault
+ use as argument to checkconvex() to report errors during initialhull
+*/
+#define qh_DATAfault 1
+
+/*-<a href="qh-poly.htm#TOC"
+ >--------------------------------</a><a name="DUPLICATEridge">-</a>
+
+ DUPLICATEridge
+ special value for facet->neighbor to indicate a duplicate ridge
+
+ notes:
+ set by matchneighbor, used by matchmatch and mark_dupridge
+*/
+#define qh_DUPLICATEridge (facetT *)1L
+
+/*-<a href="qh-poly.htm#TOC"
+ >--------------------------------</a><a name="MERGEridge">-</a>
+
+ MERGEridge flag in facet
+ special value for facet->neighbor to indicate a merged ridge
+
+ notes:
+ set by matchneighbor, used by matchmatch and mark_dupridge
+*/
+#define qh_MERGEridge (facetT *)2L
+
+
+/*============ -structures- ====================*/
+
+/*=========== -macros- =========================*/
+
+/*-<a href="qh-poly.htm#TOC"
+ >--------------------------------</a><a name="FORALLfacet_">-</a>
+
+ FORALLfacet_( facetlist ) { ... }
+ assign 'facet' to each facet in facetlist
+
+ notes:
+ uses 'facetT *facet;'
+ assumes last facet is a sentinel
+
+ see:
+ FORALLfacets
+*/
+#define FORALLfacet_( facetlist ) if (facetlist ) for ( facet=( facetlist ); facet && facet->next; facet= facet->next )
+
+/*-<a href="qh-poly.htm#TOC"
+ >--------------------------------</a><a name="FORALLnew_facets">-</a>
+
+ FORALLnew_facets { ... }
+ assign 'newfacet' to each facet in qh.newfacet_list
+
+ notes:
+ uses 'facetT *newfacet;'
+ at exit, newfacet==NULL
+*/
+#define FORALLnew_facets for ( newfacet=qh newfacet_list;newfacet && newfacet->next;newfacet=newfacet->next )
+
+/*-<a href="qh-poly.htm#TOC"
+ >--------------------------------</a><a name="FORALLvertex_">-</a>
+
+ FORALLvertex_( vertexlist ) { ... }
+ assign 'vertex' to each vertex in vertexlist
+
+ notes:
+ uses 'vertexT *vertex;'
+ at exit, vertex==NULL
+*/
+#define FORALLvertex_( vertexlist ) for (vertex=( vertexlist );vertex && vertex->next;vertex= vertex->next )
+
+/*-<a href="qh-poly.htm#TOC"
+ >--------------------------------</a><a name="FORALLvisible_facets">-</a>
+
+ FORALLvisible_facets { ... }
+ assign 'visible' to each visible facet in qh.visible_list
+
+ notes:
+ uses 'vacetT *visible;'
+ at exit, visible==NULL
+*/
+#define FORALLvisible_facets for (visible=qh visible_list; visible && visible->visible; visible= visible->next)
+
+/*-<a href="qh-poly.htm#TOC"
+ >--------------------------------</a><a name="FORALLsame_">-</a>
+
+ FORALLsame_( newfacet ) { ... }
+ assign 'same' to each facet in newfacet->f.samecycle
+
+ notes:
+ uses 'facetT *same;'
+ stops when it returns to newfacet
+*/
+#define FORALLsame_(newfacet) for (same= newfacet->f.samecycle; same != newfacet; same= same->f.samecycle)
+
+/*-<a href="qh-poly.htm#TOC"
+ >--------------------------------</a><a name="FORALLsame_cycle_">-</a>
+
+ FORALLsame_cycle_( newfacet ) { ... }
+ assign 'same' to each facet in newfacet->f.samecycle
+
+ notes:
+ uses 'facetT *same;'
+ at exit, same == NULL
+*/
+#define FORALLsame_cycle_(newfacet) \
+ for (same= newfacet->f.samecycle; \
+ same; same= (same == newfacet ? NULL : same->f.samecycle))
+
+/*-<a href="qh-poly.htm#TOC"
+ >--------------------------------</a><a name="FOREACHneighborA_">-</a>
+
+ FOREACHneighborA_( facet ) { ... }
+ assign 'neighborA' to each neighbor in facet->neighbors
+
+ FOREACHneighborA_( vertex ) { ... }
+ assign 'neighborA' to each neighbor in vertex->neighbors
+
+ declare:
+ facetT *neighborA, **neighborAp;
+
+ see:
+ <a href="qset.h#FOREACHsetelement_">FOREACHsetelement_</a>
+*/
+#define FOREACHneighborA_(facet) FOREACHsetelement_(facetT, facet->neighbors, neighborA)
+
+/*-<a href="qh-poly.htm#TOC"
+ >--------------------------------</a><a name="FOREACHvisible_">-</a>
+
+ FOREACHvisible_( facets ) { ... }
+ assign 'visible' to each facet in facets
+
+ notes:
+ uses 'facetT *facet, *facetp;'
+ see <a href="qset.h#FOREACHsetelement_">FOREACHsetelement_</a>
+*/
+#define FOREACHvisible_(facets) FOREACHsetelement_(facetT, facets, visible)
+
+/*-<a href="qh-poly.htm#TOC"
+ >--------------------------------</a><a name="FOREACHnewfacet_">-</a>
+
+ FOREACHnewfacet_( facets ) { ... }
+ assign 'newfacet' to each facet in facets
+
+ notes:
+ uses 'facetT *newfacet, *newfacetp;'
+ see <a href="qset.h#FOREACHsetelement_">FOREACHsetelement_</a>
+*/
+#define FOREACHnewfacet_(facets) FOREACHsetelement_(facetT, facets, newfacet)
+
+/*-<a href="qh-poly.htm#TOC"
+ >--------------------------------</a><a name="FOREACHvertexA_">-</a>
+
+ FOREACHvertexA_( vertices ) { ... }
+ assign 'vertexA' to each vertex in vertices
+
+ notes:
+ uses 'vertexT *vertexA, *vertexAp;'
+ see <a href="qset.h#FOREACHsetelement_">FOREACHsetelement_</a>
+*/
+#define FOREACHvertexA_(vertices) FOREACHsetelement_(vertexT, vertices, vertexA)
+
+/*-<a href="qh-poly.htm#TOC"
+ >--------------------------------</a><a name="FOREACHvertexreverse12_">-</a>
+
+ FOREACHvertexreverse12_( vertices ) { ... }
+ assign 'vertex' to each vertex in vertices
+ reverse order of first two vertices
+
+ notes:
+ uses 'vertexT *vertex, *vertexp;'
+ see <a href="qset.h#FOREACHsetelement_">FOREACHsetelement_</a>
+*/
+#define FOREACHvertexreverse12_(vertices) FOREACHsetelementreverse12_(vertexT, vertices, vertex)
+
+
+/*=============== prototypes poly.c in alphabetical order ================*/
+
+void qh_appendfacet(facetT *facet);
+void qh_appendvertex(vertexT *vertex);
+void qh_attachnewfacets(void /* qh.visible_list, qh.newfacet_list */);
+boolT qh_checkflipped(facetT *facet, realT *dist, boolT allerror);
+void qh_delfacet(facetT *facet);
+void qh_deletevisible(void /*qh.visible_list, qh.horizon_list*/);
+setT *qh_facetintersect(facetT *facetA, facetT *facetB, int *skipAp,int *skipBp, int extra);
+int qh_gethash(int hashsize, setT *set, int size, int firstindex, void *skipelem);
+facetT *qh_makenewfacet(setT *vertices, boolT toporient, facetT *facet);
+void qh_makenewplanes(void /* newfacet_list */);
+facetT *qh_makenew_nonsimplicial(facetT *visible, vertexT *apex, int *numnew);
+facetT *qh_makenew_simplicial(facetT *visible, vertexT *apex, int *numnew);
+void qh_matchneighbor(facetT *newfacet, int newskip, int hashsize,
+ int *hashcount);
+void qh_matchnewfacets(void);
+boolT qh_matchvertices(int firstindex, setT *verticesA, int skipA,
+ setT *verticesB, int *skipB, boolT *same);
+facetT *qh_newfacet(void);
+ridgeT *qh_newridge(void);
+int qh_pointid(pointT *point);
+void qh_removefacet(facetT *facet);
+void qh_removevertex(vertexT *vertex);
+void qh_updatevertices(void);
+
+
+/*========== -prototypes poly2.c in alphabetical order ===========*/
+
+void qh_addhash(void* newelem, setT *hashtable, int hashsize, int hash);
+void qh_check_bestdist(void);
+void qh_check_dupridge(facetT *facet1, realT dist1, facetT *facet2, realT dist2);
+void qh_check_maxout(void);
+void qh_check_output(void);
+void qh_check_point(pointT *point, facetT *facet, realT *maxoutside, realT *maxdist, facetT **errfacet1, facetT **errfacet2);
+void qh_check_points(void);
+void qh_checkconvex(facetT *facetlist, int fault);
+void qh_checkfacet(facetT *facet, boolT newmerge, boolT *waserrorp);
+void qh_checkflipped_all(facetT *facetlist);
+void qh_checkpolygon(facetT *facetlist);
+void qh_checkvertex(vertexT *vertex);
+void qh_clearcenters(qh_CENTER type);
+void qh_createsimplex(setT *vertices);
+void qh_delridge(ridgeT *ridge);
+void qh_delvertex(vertexT *vertex);
+setT *qh_facet3vertex(facetT *facet);
+facetT *qh_findbestfacet(pointT *point, boolT bestoutside,
+ realT *bestdist, boolT *isoutside);
+facetT *qh_findbestlower(facetT *upperfacet, pointT *point, realT *bestdistp, int *numpart);
+facetT *qh_findfacet_all(pointT *point, realT *bestdist, boolT *isoutside,
+ int *numpart);
+int qh_findgood(facetT *facetlist, int goodhorizon);
+void qh_findgood_all(facetT *facetlist);
+void qh_furthestnext(void /* qh.facet_list */);
+void qh_furthestout(facetT *facet);
+void qh_infiniteloop(facetT *facet);
+void qh_initbuild(void);
+void qh_initialhull(setT *vertices);
+setT *qh_initialvertices(int dim, setT *maxpoints, pointT *points, int numpoints);
+vertexT *qh_isvertex(pointT *point, setT *vertices);
+vertexT *qh_makenewfacets(pointT *point /*horizon_list, visible_list*/);
+void qh_matchduplicates(facetT *atfacet, int atskip, int hashsize, int *hashcount);
+void qh_nearcoplanar(void /* qh.facet_list */);
+vertexT *qh_nearvertex(facetT *facet, pointT *point, realT *bestdistp);
+int qh_newhashtable(int newsize);
+vertexT *qh_newvertex(pointT *point);
+ridgeT *qh_nextridge3d(ridgeT *atridge, facetT *facet, vertexT **vertexp);
+void qh_outcoplanar(void /* facet_list */);
+pointT *qh_point(int id);
+void qh_point_add(setT *set, pointT *point, void *elem);
+setT *qh_pointfacet(void /*qh.facet_list*/);
+setT *qh_pointvertex(void /*qh.facet_list*/);
+void qh_prependfacet(facetT *facet, facetT **facetlist);
+void qh_printhashtable(FILE *fp);
+void qh_printlists(void);
+void qh_resetlists(boolT stats, boolT resetVisible /*qh.newvertex_list qh.newfacet_list qh.visible_list*/);
+void qh_setvoronoi_all(void);
+void qh_triangulate(void /*qh.facet_list*/);
+void qh_triangulate_facet(facetT *facetA, vertexT **first_vertex);
+void qh_triangulate_link(facetT *oldfacetA, facetT *facetA, facetT *oldfacetB, facetT *facetB);
+void qh_triangulate_mirror(facetT *facetA, facetT *facetB);
+void qh_triangulate_null(facetT *facetA);
+void qh_vertexintersect(setT **vertexsetA,setT *vertexsetB);
+setT *qh_vertexintersect_new(setT *vertexsetA,setT *vertexsetB);
+void qh_vertexneighbors(void /*qh.facet_list*/);
+boolT qh_vertexsubset(setT *vertexsetA, setT *vertexsetB);
+
+
+#endif /* qhDEFpoly */
diff --git a/xs/src/qhull/src/libqhull/poly2.c b/xs/src/qhull/src/libqhull/poly2.c
new file mode 100644
index 000000000..de3e6ad0b
--- /dev/null
+++ b/xs/src/qhull/src/libqhull/poly2.c
@@ -0,0 +1,3222 @@
+/*<html><pre> -<a href="qh-poly.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ poly2.c
+ implements polygons and simplices
+
+ see qh-poly.htm, poly.h and libqhull.h
+
+ frequently used code is in poly.c
+
+ Copyright (c) 1993-2015 The Geometry Center.
+ $Id: //main/2015/qhull/src/libqhull/poly2.c#11 $$Change: 2069 $
+ $DateTime: 2016/01/18 22:05:03 $$Author: bbarber $
+*/
+
+#include "qhull_a.h"
+
+/*======== functions in alphabetical order ==========*/
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="addhash">-</a>
+
+ qh_addhash( newelem, hashtable, hashsize, hash )
+ add newelem to linear hash table at hash if not already there
+*/
+void qh_addhash(void* newelem, setT *hashtable, int hashsize, int hash) {
+ int scan;
+ void *elem;
+
+ for (scan= (int)hash; (elem= SETelem_(hashtable, scan));
+ scan= (++scan >= hashsize ? 0 : scan)) {
+ if (elem == newelem)
+ break;
+ }
+ /* loop terminates because qh_HASHfactor >= 1.1 by qh_initbuffers */
+ if (!elem)
+ SETelem_(hashtable, scan)= newelem;
+} /* addhash */
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="check_bestdist">-</a>
+
+ qh_check_bestdist()
+ check that all points are within max_outside of the nearest facet
+ if qh.ONLYgood,
+ ignores !good facets
+
+ see:
+ qh_check_maxout(), qh_outerinner()
+
+ notes:
+ only called from qh_check_points()
+ seldom used since qh.MERGING is almost always set
+ if notverified>0 at end of routine
+ some points were well inside the hull. If the hull contains
+ a lens-shaped component, these points were not verified. Use
+ options 'Qi Tv' to verify all points. (Exhaustive check also verifies)
+
+ design:
+ determine facet for each point (if any)
+ for each point
+ start with the assigned facet or with the first facet
+ find the best facet for the point and check all coplanar facets
+ error if point is outside of facet
+*/
+void qh_check_bestdist(void) {
+ boolT waserror= False, unassigned;
+ facetT *facet, *bestfacet, *errfacet1= NULL, *errfacet2= NULL;
+ facetT *facetlist;
+ realT dist, maxoutside, maxdist= -REALmax;
+ pointT *point;
+ int numpart= 0, facet_i, facet_n, notgood= 0, notverified= 0;
+ setT *facets;
+
+ trace1((qh ferr, 1020, "qh_check_bestdist: check points below nearest facet. Facet_list f%d\n",
+ qh facet_list->id));
+ maxoutside= qh_maxouter();
+ maxoutside += qh DISTround;
+ /* one more qh.DISTround for check computation */
+ trace1((qh ferr, 1021, "qh_check_bestdist: check that all points are within %2.2g of best facet\n", maxoutside));
+ facets= qh_pointfacet(/*qh.facet_list*/);
+ if (!qh_QUICKhelp && qh PRINTprecision)
+ qh_fprintf(qh ferr, 8091, "\n\
+qhull output completed. Verifying that %d points are\n\
+below %2.2g of the nearest %sfacet.\n",
+ qh_setsize(facets), maxoutside, (qh ONLYgood ? "good " : ""));
+ FOREACHfacet_i_(facets) { /* for each point with facet assignment */
+ if (facet)
+ unassigned= False;
+ else {
+ unassigned= True;
+ facet= qh facet_list;
+ }
+ point= qh_point(facet_i);
+ if (point == qh GOODpointp)
+ continue;
+ qh_distplane(point, facet, &dist);
+ numpart++;
+ bestfacet= qh_findbesthorizon(!qh_IScheckmax, point, facet, qh_NOupper, &dist, &numpart);
+ /* occurs after statistics reported */
+ maximize_(maxdist, dist);
+ if (dist > maxoutside) {
+ if (qh ONLYgood && !bestfacet->good
+ && !((bestfacet= qh_findgooddist(point, bestfacet, &dist, &facetlist))
+ && dist > maxoutside))
+ notgood++;
+ else {
+ waserror= True;
+ qh_fprintf(qh ferr, 6109, "qhull precision error: point p%d is outside facet f%d, distance= %6.8g maxoutside= %6.8g\n",
+ facet_i, bestfacet->id, dist, maxoutside);
+ if (errfacet1 != bestfacet) {
+ errfacet2= errfacet1;
+ errfacet1= bestfacet;
+ }
+ }
+ }else if (unassigned && dist < -qh MAXcoplanar)
+ notverified++;
+ }
+ qh_settempfree(&facets);
+ if (notverified && !qh DELAUNAY && !qh_QUICKhelp && qh PRINTprecision)
+ qh_fprintf(qh ferr, 8092, "\n%d points were well inside the hull. If the hull contains\n\
+a lens-shaped component, these points were not verified. Use\n\
+options 'Qci Tv' to verify all points.\n", notverified);
+ if (maxdist > qh outside_err) {
+ qh_fprintf(qh ferr, 6110, "qhull precision error (qh_check_bestdist): a coplanar point is %6.2g from convex hull. The maximum value(qh.outside_err) is %6.2g\n",
+ maxdist, qh outside_err);
+ qh_errexit2(qh_ERRprec, errfacet1, errfacet2);
+ }else if (waserror && qh outside_err > REALmax/2)
+ qh_errexit2(qh_ERRprec, errfacet1, errfacet2);
+ /* else if waserror, the error was logged to qh.ferr but does not effect the output */
+ trace0((qh ferr, 20, "qh_check_bestdist: max distance outside %2.2g\n", maxdist));
+} /* check_bestdist */
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="check_dupridge">-</a>
+
+ qh_check_dupridge(facet1, dist1, facet2, dist2)
+ Check duplicate ridge between facet1 and facet2 for wide merge
+ dist1 is the maximum distance of facet1's vertices to facet2
+ dist2 is the maximum distance of facet2's vertices to facet1
+
+ Returns
+ Level 1 log of the duplicate ridge with the minimum distance between vertices
+ Throws error if the merge will increase the maximum facet width by qh_WIDEduplicate (100x)
+
+ called from:
+ qh_forcedmerges()
+*/
+#ifndef qh_NOmerge
+void qh_check_dupridge(facetT *facet1, realT dist1, facetT *facet2, realT dist2) {
+ vertexT *vertex, **vertexp, *vertexA, **vertexAp;
+ realT dist, innerplane, mergedist, outerplane, prevdist, ratio;
+ realT minvertex= REALmax;
+
+ mergedist= fmin_(dist1, dist2);
+ qh_outerinner(NULL, &outerplane, &innerplane); /* ratio from qh_printsummary */
+ prevdist= fmax_(outerplane, innerplane);
+ maximize_(prevdist, qh ONEmerge + qh DISTround);
+ maximize_(prevdist, qh MINoutside + qh DISTround);
+ ratio= mergedist/prevdist;
+ FOREACHvertex_(facet1->vertices) { /* The duplicate ridge is between facet1 and facet2, so either facet can be tested */
+ FOREACHvertexA_(facet1->vertices) {
+ if (vertex > vertexA){ /* Test each pair once */
+ dist= qh_pointdist(vertex->point, vertexA->point, qh hull_dim);
+ minimize_(minvertex, dist);
+ }
+ }
+ }
+ trace0((qh ferr, 16, "qh_check_dupridge: duplicate ridge between f%d and f%d due to nearly-coincident vertices (%2.2g), dist %2.2g, reverse dist %2.2g, ratio %2.2g while processing p%d\n",
+ facet1->id, facet2->id, minvertex, dist1, dist2, ratio, qh furthest_id));
+ if (ratio > qh_WIDEduplicate) {
+ qh_fprintf(qh ferr, 6271, "qhull precision error (qh_check_dupridge): wide merge (%.0f times wider) due to duplicate ridge with nearly coincident points (%2.2g) between f%d and f%d, merge dist %2.2g, while processing p%d\n- Ignore error with option 'Q12'\n- To be fixed in a later version of Qhull\n",
+ ratio, minvertex, facet1->id, facet2->id, mergedist, qh furthest_id);
+ if (qh DELAUNAY)
+ qh_fprintf(qh ferr, 8145, "- A bounding box for the input sites may alleviate this error.\n");
+ if(minvertex > qh_WIDEduplicate*prevdist)
+ qh_fprintf(qh ferr, 8146, "- Vertex distance %2.2g is greater than %d times maximum distance %2.2g\n Please report to bradb@shore.net with steps to reproduce and all output\n",
+ minvertex, qh_WIDEduplicate, prevdist);
+ if (!qh NOwide)
+ qh_errexit2(qh_ERRqhull, facet1, facet2);
+ }
+} /* check_dupridge */
+#endif
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="check_maxout">-</a>
+
+ qh_check_maxout()
+ updates qh.max_outside by checking all points against bestfacet
+ if qh.ONLYgood, ignores !good facets
+
+ returns:
+ updates facet->maxoutside via qh_findbesthorizon()
+ sets qh.maxoutdone
+ if printing qh.min_vertex (qh_outerinner),
+ it is updated to the current vertices
+ removes inside/coplanar points from coplanarset as needed
+
+ notes:
+ defines coplanar as min_vertex instead of MAXcoplanar
+ may not need to check near-inside points because of qh.MAXcoplanar
+ and qh.KEEPnearinside (before it was -DISTround)
+
+ see also:
+ qh_check_bestdist()
+
+ design:
+ if qh.min_vertex is needed
+ for all neighbors of all vertices
+ test distance from vertex to neighbor
+ determine facet for each point (if any)
+ for each point with an assigned facet
+ find the best facet for the point and check all coplanar facets
+ (updates outer planes)
+ remove near-inside points from coplanar sets
+*/
+#ifndef qh_NOmerge
+void qh_check_maxout(void) {
+ facetT *facet, *bestfacet, *neighbor, **neighborp, *facetlist;
+ realT dist, maxoutside, minvertex, old_maxoutside;
+ pointT *point;
+ int numpart= 0, facet_i, facet_n, notgood= 0;
+ setT *facets, *vertices;
+ vertexT *vertex;
+
+ trace1((qh ferr, 1022, "qh_check_maxout: check and update maxoutside for each facet.\n"));
+ maxoutside= minvertex= 0;
+ if (qh VERTEXneighbors
+ && (qh PRINTsummary || qh KEEPinside || qh KEEPcoplanar
+ || qh TRACElevel || qh PRINTstatistics
+ || qh PRINTout[0] == qh_PRINTsummary || qh PRINTout[0] == qh_PRINTnone)) {
+ trace1((qh ferr, 1023, "qh_check_maxout: determine actual maxoutside and minvertex\n"));
+ vertices= qh_pointvertex(/*qh.facet_list*/);
+ FORALLvertices {
+ FOREACHneighbor_(vertex) {
+ zinc_(Zdistvertex); /* distance also computed by main loop below */
+ qh_distplane(vertex->point, neighbor, &dist);
+ minimize_(minvertex, dist);
+ if (-dist > qh TRACEdist || dist > qh TRACEdist
+ || neighbor == qh tracefacet || vertex == qh tracevertex)
+ qh_fprintf(qh ferr, 8093, "qh_check_maxout: p%d(v%d) is %.2g from f%d\n",
+ qh_pointid(vertex->point), vertex->id, dist, neighbor->id);
+ }
+ }
+ if (qh MERGING) {
+ wmin_(Wminvertex, qh min_vertex);
+ }
+ qh min_vertex= minvertex;
+ qh_settempfree(&vertices);
+ }
+ facets= qh_pointfacet(/*qh.facet_list*/);
+ do {
+ old_maxoutside= fmax_(qh max_outside, maxoutside);
+ FOREACHfacet_i_(facets) { /* for each point with facet assignment */
+ if (facet) {
+ point= qh_point(facet_i);
+ if (point == qh GOODpointp)
+ continue;
+ zzinc_(Ztotcheck);
+ qh_distplane(point, facet, &dist);
+ numpart++;
+ bestfacet= qh_findbesthorizon(qh_IScheckmax, point, facet, !qh_NOupper, &dist, &numpart);
+ if (bestfacet && dist > maxoutside) {
+ if (qh ONLYgood && !bestfacet->good
+ && !((bestfacet= qh_findgooddist(point, bestfacet, &dist, &facetlist))
+ && dist > maxoutside))
+ notgood++;
+ else
+ maxoutside= dist;
+ }
+ if (dist > qh TRACEdist || (bestfacet && bestfacet == qh tracefacet))
+ qh_fprintf(qh ferr, 8094, "qh_check_maxout: p%d is %.2g above f%d\n",
+ qh_pointid(point), dist, (bestfacet ? bestfacet->id : UINT_MAX));
+ }
+ }
+ }while
+ (maxoutside > 2*old_maxoutside);
+ /* if qh.maxoutside increases substantially, qh_SEARCHdist is not valid
+ e.g., RBOX 5000 s Z1 G1e-13 t1001200614 | qhull */
+ zzadd_(Zcheckpart, numpart);
+ qh_settempfree(&facets);
+ wval_(Wmaxout)= maxoutside - qh max_outside;
+ wmax_(Wmaxoutside, qh max_outside);
+ qh max_outside= maxoutside;
+ qh_nearcoplanar(/*qh.facet_list*/);
+ qh maxoutdone= True;
+ trace1((qh ferr, 1024, "qh_check_maxout: maxoutside %2.2g, min_vertex %2.2g, outside of not good %d\n",
+ maxoutside, qh min_vertex, notgood));
+} /* check_maxout */
+#else /* qh_NOmerge */
+void qh_check_maxout(void) {
+}
+#endif
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="check_output">-</a>
+
+ qh_check_output()
+ performs the checks at the end of qhull algorithm
+ Maybe called after voronoi output. Will recompute otherwise centrums are Voronoi centers instead
+*/
+void qh_check_output(void) {
+ int i;
+
+ if (qh STOPcone)
+ return;
+ if (qh VERIFYoutput | qh IStracing | qh CHECKfrequently) {
+ qh_checkpolygon(qh facet_list);
+ qh_checkflipped_all(qh facet_list);
+ qh_checkconvex(qh facet_list, qh_ALGORITHMfault);
+ }else if (!qh MERGING && qh_newstats(qhstat precision, &i)) {
+ qh_checkflipped_all(qh facet_list);
+ qh_checkconvex(qh facet_list, qh_ALGORITHMfault);
+ }
+} /* check_output */
+
+
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="check_point">-</a>
+
+ qh_check_point( point, facet, maxoutside, maxdist, errfacet1, errfacet2 )
+ check that point is less than maxoutside from facet
+*/
+void qh_check_point(pointT *point, facetT *facet, realT *maxoutside, realT *maxdist, facetT **errfacet1, facetT **errfacet2) {
+ realT dist;
+
+ /* occurs after statistics reported */
+ qh_distplane(point, facet, &dist);
+ if (dist > *maxoutside) {
+ if (*errfacet1 != facet) {
+ *errfacet2= *errfacet1;
+ *errfacet1= facet;
+ }
+ qh_fprintf(qh ferr, 6111, "qhull precision error: point p%d is outside facet f%d, distance= %6.8g maxoutside= %6.8g\n",
+ qh_pointid(point), facet->id, dist, *maxoutside);
+ }
+ maximize_(*maxdist, dist);
+} /* qh_check_point */
+
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="check_points">-</a>
+
+ qh_check_points()
+ checks that all points are inside all facets
+
+ notes:
+ if many points and qh_check_maxout not called (i.e., !qh.MERGING),
+ calls qh_findbesthorizon (seldom done).
+ ignores flipped facets
+ maxoutside includes 2 qh.DISTrounds
+ one qh.DISTround for the computed distances in qh_check_points
+ qh_printafacet and qh_printsummary needs only one qh.DISTround
+ the computation for qh.VERIFYdirect does not account for qh.other_points
+
+ design:
+ if many points
+ use qh_check_bestdist()
+ else
+ for all facets
+ for all points
+ check that point is inside facet
+*/
+void qh_check_points(void) {
+ facetT *facet, *errfacet1= NULL, *errfacet2= NULL;
+ realT total, maxoutside, maxdist= -REALmax;
+ pointT *point, **pointp, *pointtemp;
+ boolT testouter;
+
+ maxoutside= qh_maxouter();
+ maxoutside += qh DISTround;
+ /* one more qh.DISTround for check computation */
+ trace1((qh ferr, 1025, "qh_check_points: check all points below %2.2g of all facet planes\n",
+ maxoutside));
+ if (qh num_good) /* miss counts other_points and !good facets */
+ total= (float)qh num_good * (float)qh num_points;
+ else
+ total= (float)qh num_facets * (float)qh num_points;
+ if (total >= qh_VERIFYdirect && !qh maxoutdone) {
+ if (!qh_QUICKhelp && qh SKIPcheckmax && qh MERGING)
+ qh_fprintf(qh ferr, 7075, "qhull input warning: merging without checking outer planes('Q5' or 'Po').\n\
+Verify may report that a point is outside of a facet.\n");
+ qh_check_bestdist();
+ }else {
+ if (qh_MAXoutside && qh maxoutdone)
+ testouter= True;
+ else
+ testouter= False;
+ if (!qh_QUICKhelp) {
+ if (qh MERGEexact)
+ qh_fprintf(qh ferr, 7076, "qhull input warning: exact merge ('Qx'). Verify may report that a point\n\
+is outside of a facet. See qh-optq.htm#Qx\n");
+ else if (qh SKIPcheckmax || qh NOnearinside)
+ qh_fprintf(qh ferr, 7077, "qhull input warning: no outer plane check ('Q5') or no processing of\n\
+near-inside points ('Q8'). Verify may report that a point is outside\n\
+of a facet.\n");
+ }
+ if (qh PRINTprecision) {
+ if (testouter)
+ qh_fprintf(qh ferr, 8098, "\n\
+Output completed. Verifying that all points are below outer planes of\n\
+all %sfacets. Will make %2.0f distance computations.\n",
+ (qh ONLYgood ? "good " : ""), total);
+ else
+ qh_fprintf(qh ferr, 8099, "\n\
+Output completed. Verifying that all points are below %2.2g of\n\
+all %sfacets. Will make %2.0f distance computations.\n",
+ maxoutside, (qh ONLYgood ? "good " : ""), total);
+ }
+ FORALLfacets {
+ if (!facet->good && qh ONLYgood)
+ continue;
+ if (facet->flipped)
+ continue;
+ if (!facet->normal) {
+ qh_fprintf(qh ferr, 7061, "qhull warning (qh_check_points): missing normal for facet f%d\n", facet->id);
+ continue;
+ }
+ if (testouter) {
+#if qh_MAXoutside
+ maxoutside= facet->maxoutside + 2* qh DISTround;
+ /* one DISTround to actual point and another to computed point */
+#endif
+ }
+ FORALLpoints {
+ if (point != qh GOODpointp)
+ qh_check_point(point, facet, &maxoutside, &maxdist, &errfacet1, &errfacet2);
+ }
+ FOREACHpoint_(qh other_points) {
+ if (point != qh GOODpointp)
+ qh_check_point(point, facet, &maxoutside, &maxdist, &errfacet1, &errfacet2);
+ }
+ }
+ if (maxdist > qh outside_err) {
+ qh_fprintf(qh ferr, 6112, "qhull precision error (qh_check_points): a coplanar point is %6.2g from convex hull. The maximum value(qh.outside_err) is %6.2g\n",
+ maxdist, qh outside_err );
+ qh_errexit2( qh_ERRprec, errfacet1, errfacet2 );
+ }else if (errfacet1 && qh outside_err > REALmax/2)
+ qh_errexit2( qh_ERRprec, errfacet1, errfacet2 );
+ /* else if errfacet1, the error was logged to qh.ferr but does not effect the output */
+ trace0((qh ferr, 21, "qh_check_points: max distance outside %2.2g\n", maxdist));
+ }
+} /* check_points */
+
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="checkconvex">-</a>
+
+ qh_checkconvex( facetlist, fault )
+ check that each ridge in facetlist is convex
+ fault = qh_DATAfault if reporting errors
+ = qh_ALGORITHMfault otherwise
+
+ returns:
+ counts Zconcaveridges and Zcoplanarridges
+ errors if concaveridge or if merging an coplanar ridge
+
+ note:
+ if not merging,
+ tests vertices for neighboring simplicial facets
+ else if ZEROcentrum,
+ tests vertices for neighboring simplicial facets
+ else
+ tests centrums of neighboring facets
+
+ design:
+ for all facets
+ report flipped facets
+ if ZEROcentrum and simplicial neighbors
+ test vertices for neighboring simplicial facets
+ else
+ test centrum against all neighbors
+*/
+void qh_checkconvex(facetT *facetlist, int fault) {
+ facetT *facet, *neighbor, **neighborp, *errfacet1=NULL, *errfacet2=NULL;
+ vertexT *vertex;
+ realT dist;
+ pointT *centrum;
+ boolT waserror= False, centrum_warning= False, tempcentrum= False, allsimplicial;
+ int neighbor_i;
+
+ trace1((qh ferr, 1026, "qh_checkconvex: check all ridges are convex\n"));
+ if (!qh RERUN) {
+ zzval_(Zconcaveridges)= 0;
+ zzval_(Zcoplanarridges)= 0;
+ }
+ FORALLfacet_(facetlist) {
+ if (facet->flipped) {
+ qh_precision("flipped facet");
+ qh_fprintf(qh ferr, 6113, "qhull precision error: f%d is flipped(interior point is outside)\n",
+ facet->id);
+ errfacet1= facet;
+ waserror= True;
+ continue;
+ }
+ if (qh MERGING && (!qh ZEROcentrum || !facet->simplicial || facet->tricoplanar))
+ allsimplicial= False;
+ else {
+ allsimplicial= True;
+ neighbor_i= 0;
+ FOREACHneighbor_(facet) {
+ vertex= SETelemt_(facet->vertices, neighbor_i++, vertexT);
+ if (!neighbor->simplicial || neighbor->tricoplanar) {
+ allsimplicial= False;
+ continue;
+ }
+ qh_distplane(vertex->point, neighbor, &dist);
+ if (dist > -qh DISTround) {
+ if (fault == qh_DATAfault) {
+ qh_precision("coplanar or concave ridge");
+ qh_fprintf(qh ferr, 6114, "qhull precision error: initial simplex is not convex. Distance=%.2g\n", dist);
+ qh_errexit(qh_ERRsingular, NULL, NULL);
+ }
+ if (dist > qh DISTround) {
+ zzinc_(Zconcaveridges);
+ qh_precision("concave ridge");
+ qh_fprintf(qh ferr, 6115, "qhull precision error: f%d is concave to f%d, since p%d(v%d) is %6.4g above\n",
+ facet->id, neighbor->id, qh_pointid(vertex->point), vertex->id, dist);
+ errfacet1= facet;
+ errfacet2= neighbor;
+ waserror= True;
+ }else if (qh ZEROcentrum) {
+ if (dist > 0) { /* qh_checkzero checks that dist < - qh DISTround */
+ zzinc_(Zcoplanarridges);
+ qh_precision("coplanar ridge");
+ qh_fprintf(qh ferr, 6116, "qhull precision error: f%d is clearly not convex to f%d, since p%d(v%d) is %6.4g above\n",
+ facet->id, neighbor->id, qh_pointid(vertex->point), vertex->id, dist);
+ errfacet1= facet;
+ errfacet2= neighbor;
+ waserror= True;
+ }
+ }else {
+ zzinc_(Zcoplanarridges);
+ qh_precision("coplanar ridge");
+ trace0((qh ferr, 22, "qhull precision error: f%d may be coplanar to f%d, since p%d(v%d) is within %6.4g during p%d\n",
+ facet->id, neighbor->id, qh_pointid(vertex->point), vertex->id, dist, qh furthest_id));
+ }
+ }
+ }
+ }
+ if (!allsimplicial) {
+ if (qh CENTERtype == qh_AScentrum) {
+ if (!facet->center)
+ facet->center= qh_getcentrum(facet);
+ centrum= facet->center;
+ }else {
+ if (!centrum_warning && (!facet->simplicial || facet->tricoplanar)) {
+ centrum_warning= True;
+ qh_fprintf(qh ferr, 7062, "qhull warning: recomputing centrums for convexity test. This may lead to false, precision errors.\n");
+ }
+ centrum= qh_getcentrum(facet);
+ tempcentrum= True;
+ }
+ FOREACHneighbor_(facet) {
+ if (qh ZEROcentrum && facet->simplicial && neighbor->simplicial)
+ continue;
+ if (facet->tricoplanar || neighbor->tricoplanar)
+ continue;
+ zzinc_(Zdistconvex);
+ qh_distplane(centrum, neighbor, &dist);
+ if (dist > qh DISTround) {
+ zzinc_(Zconcaveridges);
+ qh_precision("concave ridge");
+ qh_fprintf(qh ferr, 6117, "qhull precision error: f%d is concave to f%d. Centrum of f%d is %6.4g above f%d\n",
+ facet->id, neighbor->id, facet->id, dist, neighbor->id);
+ errfacet1= facet;
+ errfacet2= neighbor;
+ waserror= True;
+ }else if (dist >= 0.0) { /* if arithmetic always rounds the same,
+ can test against centrum radius instead */
+ zzinc_(Zcoplanarridges);
+ qh_precision("coplanar ridge");
+ qh_fprintf(qh ferr, 6118, "qhull precision error: f%d is coplanar or concave to f%d. Centrum of f%d is %6.4g above f%d\n",
+ facet->id, neighbor->id, facet->id, dist, neighbor->id);
+ errfacet1= facet;
+ errfacet2= neighbor;
+ waserror= True;
+ }
+ }
+ if (tempcentrum)
+ qh_memfree(centrum, qh normal_size);
+ }
+ }
+ if (waserror && !qh FORCEoutput)
+ qh_errexit2(qh_ERRprec, errfacet1, errfacet2);
+} /* checkconvex */
+
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="checkfacet">-</a>
+
+ qh_checkfacet( facet, newmerge, waserror )
+ checks for consistency errors in facet
+ newmerge set if from merge.c
+
+ returns:
+ sets waserror if any error occurs
+
+ checks:
+ vertex ids are inverse sorted
+ unless newmerge, at least hull_dim neighbors and vertices (exactly if simplicial)
+ if non-simplicial, at least as many ridges as neighbors
+ neighbors are not duplicated
+ ridges are not duplicated
+ in 3-d, ridges=verticies
+ (qh.hull_dim-1) ridge vertices
+ neighbors are reciprocated
+ ridge neighbors are facet neighbors and a ridge for every neighbor
+ simplicial neighbors match facetintersect
+ vertex intersection matches vertices of common ridges
+ vertex neighbors and facet vertices agree
+ all ridges have distinct vertex sets
+
+ notes:
+ uses neighbor->seen
+
+ design:
+ check sets
+ check vertices
+ check sizes of neighbors and vertices
+ check for qh_MERGEridge and qh_DUPLICATEridge flags
+ check neighbor set
+ check ridge set
+ check ridges, neighbors, and vertices
+*/
+void qh_checkfacet(facetT *facet, boolT newmerge, boolT *waserrorp) {
+ facetT *neighbor, **neighborp, *errother=NULL;
+ ridgeT *ridge, **ridgep, *errridge= NULL, *ridge2;
+ vertexT *vertex, **vertexp;
+ unsigned previousid= INT_MAX;
+ int numneighbors, numvertices, numridges=0, numRvertices=0;
+ boolT waserror= False;
+ int skipA, skipB, ridge_i, ridge_n, i;
+ setT *intersection;
+
+ if (facet->visible) {
+ qh_fprintf(qh ferr, 6119, "qhull internal error (qh_checkfacet): facet f%d is on the visible_list\n",
+ facet->id);
+ qh_errexit(qh_ERRqhull, facet, NULL);
+ }
+ if (!facet->normal) {
+ qh_fprintf(qh ferr, 6120, "qhull internal error (qh_checkfacet): facet f%d does not have a normal\n",
+ facet->id);
+ waserror= True;
+ }
+ qh_setcheck(facet->vertices, "vertices for f", facet->id);
+ qh_setcheck(facet->ridges, "ridges for f", facet->id);
+ qh_setcheck(facet->outsideset, "outsideset for f", facet->id);
+ qh_setcheck(facet->coplanarset, "coplanarset for f", facet->id);
+ qh_setcheck(facet->neighbors, "neighbors for f", facet->id);
+ FOREACHvertex_(facet->vertices) {
+ if (vertex->deleted) {
+ qh_fprintf(qh ferr, 6121, "qhull internal error (qh_checkfacet): deleted vertex v%d in f%d\n", vertex->id, facet->id);
+ qh_errprint("ERRONEOUS", NULL, NULL, NULL, vertex);
+ waserror= True;
+ }
+ if (vertex->id >= previousid) {
+ qh_fprintf(qh ferr, 6122, "qhull internal error (qh_checkfacet): vertices of f%d are not in descending id order at v%d\n", facet->id, vertex->id);
+ waserror= True;
+ break;
+ }
+ previousid= vertex->id;
+ }
+ numneighbors= qh_setsize(facet->neighbors);
+ numvertices= qh_setsize(facet->vertices);
+ numridges= qh_setsize(facet->ridges);
+ if (facet->simplicial) {
+ if (numvertices+numneighbors != 2*qh hull_dim
+ && !facet->degenerate && !facet->redundant) {
+ qh_fprintf(qh ferr, 6123, "qhull internal error (qh_checkfacet): for simplicial facet f%d, #vertices %d + #neighbors %d != 2*qh hull_dim\n",
+ facet->id, numvertices, numneighbors);
+ qh_setprint(qh ferr, "", facet->neighbors);
+ waserror= True;
+ }
+ }else { /* non-simplicial */
+ if (!newmerge
+ &&(numvertices < qh hull_dim || numneighbors < qh hull_dim)
+ && !facet->degenerate && !facet->redundant) {
+ qh_fprintf(qh ferr, 6124, "qhull internal error (qh_checkfacet): for facet f%d, #vertices %d or #neighbors %d < qh hull_dim\n",
+ facet->id, numvertices, numneighbors);
+ waserror= True;
+ }
+ /* in 3-d, can get a vertex twice in an edge list, e.g., RBOX 1000 s W1e-13 t995849315 D2 | QHULL d Tc Tv TP624 TW1e-13 T4 */
+ if (numridges < numneighbors
+ ||(qh hull_dim == 3 && numvertices > numridges && !qh NEWfacets)
+ ||(qh hull_dim == 2 && numridges + numvertices + numneighbors != 6)) {
+ if (!facet->degenerate && !facet->redundant) {
+ qh_fprintf(qh ferr, 6125, "qhull internal error (qh_checkfacet): for facet f%d, #ridges %d < #neighbors %d or(3-d) > #vertices %d or(2-d) not all 2\n",
+ facet->id, numridges, numneighbors, numvertices);
+ waserror= True;
+ }
+ }
+ }
+ FOREACHneighbor_(facet) {
+ if (neighbor == qh_MERGEridge || neighbor == qh_DUPLICATEridge) {
+ qh_fprintf(qh ferr, 6126, "qhull internal error (qh_checkfacet): facet f%d still has a MERGE or DUP neighbor\n", facet->id);
+ qh_errexit(qh_ERRqhull, facet, NULL);
+ }
+ neighbor->seen= True;
+ }
+ FOREACHneighbor_(facet) {
+ if (!qh_setin(neighbor->neighbors, facet)) {
+ qh_fprintf(qh ferr, 6127, "qhull internal error (qh_checkfacet): facet f%d has neighbor f%d, but f%d does not have neighbor f%d\n",
+ facet->id, neighbor->id, neighbor->id, facet->id);
+ errother= neighbor;
+ waserror= True;
+ }
+ if (!neighbor->seen) {
+ qh_fprintf(qh ferr, 6128, "qhull internal error (qh_checkfacet): facet f%d has a duplicate neighbor f%d\n",
+ facet->id, neighbor->id);
+ errother= neighbor;
+ waserror= True;
+ }
+ neighbor->seen= False;
+ }
+ FOREACHridge_(facet->ridges) {
+ qh_setcheck(ridge->vertices, "vertices for r", ridge->id);
+ ridge->seen= False;
+ }
+ FOREACHridge_(facet->ridges) {
+ if (ridge->seen) {
+ qh_fprintf(qh ferr, 6129, "qhull internal error (qh_checkfacet): facet f%d has a duplicate ridge r%d\n",
+ facet->id, ridge->id);
+ errridge= ridge;
+ waserror= True;
+ }
+ ridge->seen= True;
+ numRvertices= qh_setsize(ridge->vertices);
+ if (numRvertices != qh hull_dim - 1) {
+ qh_fprintf(qh ferr, 6130, "qhull internal error (qh_checkfacet): ridge between f%d and f%d has %d vertices\n",
+ ridge->top->id, ridge->bottom->id, numRvertices);
+ errridge= ridge;
+ waserror= True;
+ }
+ neighbor= otherfacet_(ridge, facet);
+ neighbor->seen= True;
+ if (!qh_setin(facet->neighbors, neighbor)) {
+ qh_fprintf(qh ferr, 6131, "qhull internal error (qh_checkfacet): for facet f%d, neighbor f%d of ridge r%d not in facet\n",
+ facet->id, neighbor->id, ridge->id);
+ errridge= ridge;
+ waserror= True;
+ }
+ }
+ if (!facet->simplicial) {
+ FOREACHneighbor_(facet) {
+ if (!neighbor->seen) {
+ qh_fprintf(qh ferr, 6132, "qhull internal error (qh_checkfacet): facet f%d does not have a ridge for neighbor f%d\n",
+ facet->id, neighbor->id);
+ errother= neighbor;
+ waserror= True;
+ }
+ intersection= qh_vertexintersect_new(facet->vertices, neighbor->vertices);
+ qh_settemppush(intersection);
+ FOREACHvertex_(facet->vertices) {
+ vertex->seen= False;
+ vertex->seen2= False;
+ }
+ FOREACHvertex_(intersection)
+ vertex->seen= True;
+ FOREACHridge_(facet->ridges) {
+ if (neighbor != otherfacet_(ridge, facet))
+ continue;
+ FOREACHvertex_(ridge->vertices) {
+ if (!vertex->seen) {
+ qh_fprintf(qh ferr, 6133, "qhull internal error (qh_checkfacet): vertex v%d in r%d not in f%d intersect f%d\n",
+ vertex->id, ridge->id, facet->id, neighbor->id);
+ qh_errexit(qh_ERRqhull, facet, ridge);
+ }
+ vertex->seen2= True;
+ }
+ }
+ if (!newmerge) {
+ FOREACHvertex_(intersection) {
+ if (!vertex->seen2) {
+ if (qh IStracing >=3 || !qh MERGING) {
+ qh_fprintf(qh ferr, 6134, "qhull precision error (qh_checkfacet): vertex v%d in f%d intersect f%d but\n\
+ not in a ridge. This is ok under merging. Last point was p%d\n",
+ vertex->id, facet->id, neighbor->id, qh furthest_id);
+ if (!qh FORCEoutput && !qh MERGING) {
+ qh_errprint("ERRONEOUS", facet, neighbor, NULL, vertex);
+ if (!qh MERGING)
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ }
+ }
+ }
+ }
+ qh_settempfree(&intersection);
+ }
+ }else { /* simplicial */
+ FOREACHneighbor_(facet) {
+ if (neighbor->simplicial) {
+ skipA= SETindex_(facet->neighbors, neighbor);
+ skipB= qh_setindex(neighbor->neighbors, facet);
+ if (skipA<0 || skipB<0 || !qh_setequal_skip(facet->vertices, skipA, neighbor->vertices, skipB)) {
+ qh_fprintf(qh ferr, 6135, "qhull internal error (qh_checkfacet): facet f%d skip %d and neighbor f%d skip %d do not match \n",
+ facet->id, skipA, neighbor->id, skipB);
+ errother= neighbor;
+ waserror= True;
+ }
+ }
+ }
+ }
+ if (qh hull_dim < 5 && (qh IStracing > 2 || qh CHECKfrequently)) {
+ FOREACHridge_i_(facet->ridges) { /* expensive */
+ for (i=ridge_i+1; i < ridge_n; i++) {
+ ridge2= SETelemt_(facet->ridges, i, ridgeT);
+ if (qh_setequal(ridge->vertices, ridge2->vertices)) {
+ qh_fprintf(qh ferr, 6227, "Qhull internal error (qh_checkfacet): ridges r%d and r%d have the same vertices\n",
+ ridge->id, ridge2->id);
+ errridge= ridge;
+ waserror= True;
+ }
+ }
+ }
+ }
+ if (waserror) {
+ qh_errprint("ERRONEOUS", facet, errother, errridge, NULL);
+ *waserrorp= True;
+ }
+} /* checkfacet */
+
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="checkflipped_all">-</a>
+
+ qh_checkflipped_all( facetlist )
+ checks orientation of facets in list against interior point
+*/
+void qh_checkflipped_all(facetT *facetlist) {
+ facetT *facet;
+ boolT waserror= False;
+ realT dist;
+
+ if (facetlist == qh facet_list)
+ zzval_(Zflippedfacets)= 0;
+ FORALLfacet_(facetlist) {
+ if (facet->normal && !qh_checkflipped(facet, &dist, !qh_ALL)) {
+ qh_fprintf(qh ferr, 6136, "qhull precision error: facet f%d is flipped, distance= %6.12g\n",
+ facet->id, dist);
+ if (!qh FORCEoutput) {
+ qh_errprint("ERRONEOUS", facet, NULL, NULL, NULL);
+ waserror= True;
+ }
+ }
+ }
+ if (waserror) {
+ qh_fprintf(qh ferr, 8101, "\n\
+A flipped facet occurs when its distance to the interior point is\n\
+greater than %2.2g, the maximum roundoff error.\n", -qh DISTround);
+ qh_errexit(qh_ERRprec, NULL, NULL);
+ }
+} /* checkflipped_all */
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="checkpolygon">-</a>
+
+ qh_checkpolygon( facetlist )
+ checks the correctness of the structure
+
+ notes:
+ call with either qh.facet_list or qh.newfacet_list
+ checks num_facets and num_vertices if qh.facet_list
+
+ design:
+ for each facet
+ checks facet and outside set
+ initializes vertexlist
+ for each facet
+ checks vertex set
+ if checking all facets(qh.facetlist)
+ check facet count
+ if qh.VERTEXneighbors
+ check vertex neighbors and count
+ check vertex count
+*/
+void qh_checkpolygon(facetT *facetlist) {
+ facetT *facet;
+ vertexT *vertex, **vertexp, *vertexlist;
+ int numfacets= 0, numvertices= 0, numridges= 0;
+ int totvneighbors= 0, totvertices= 0;
+ boolT waserror= False, nextseen= False, visibleseen= False;
+
+ trace1((qh ferr, 1027, "qh_checkpolygon: check all facets from f%d\n", facetlist->id));
+ if (facetlist != qh facet_list || qh ONLYgood)
+ nextseen= True;
+ FORALLfacet_(facetlist) {
+ if (facet == qh visible_list)
+ visibleseen= True;
+ if (!facet->visible) {
+ if (!nextseen) {
+ if (facet == qh facet_next)
+ nextseen= True;
+ else if (qh_setsize(facet->outsideset)) {
+ if (!qh NARROWhull
+#if !qh_COMPUTEfurthest
+ || facet->furthestdist >= qh MINoutside
+#endif
+ ) {
+ qh_fprintf(qh ferr, 6137, "qhull internal error (qh_checkpolygon): f%d has outside points before qh facet_next\n",
+ facet->id);
+ qh_errexit(qh_ERRqhull, facet, NULL);
+ }
+ }
+ }
+ numfacets++;
+ qh_checkfacet(facet, False, &waserror);
+ }
+ }
+ if (qh visible_list && !visibleseen && facetlist == qh facet_list) {
+ qh_fprintf(qh ferr, 6138, "qhull internal error (qh_checkpolygon): visible list f%d no longer on facet list\n", qh visible_list->id);
+ qh_printlists();
+ qh_errexit(qh_ERRqhull, qh visible_list, NULL);
+ }
+ if (facetlist == qh facet_list)
+ vertexlist= qh vertex_list;
+ else if (facetlist == qh newfacet_list)
+ vertexlist= qh newvertex_list;
+ else
+ vertexlist= NULL;
+ FORALLvertex_(vertexlist) {
+ vertex->seen= False;
+ vertex->visitid= 0;
+ }
+ FORALLfacet_(facetlist) {
+ if (facet->visible)
+ continue;
+ if (facet->simplicial)
+ numridges += qh hull_dim;
+ else
+ numridges += qh_setsize(facet->ridges);
+ FOREACHvertex_(facet->vertices) {
+ vertex->visitid++;
+ if (!vertex->seen) {
+ vertex->seen= True;
+ numvertices++;
+ if (qh_pointid(vertex->point) == qh_IDunknown) {
+ qh_fprintf(qh ferr, 6139, "qhull internal error (qh_checkpolygon): unknown point %p for vertex v%d first_point %p\n",
+ vertex->point, vertex->id, qh first_point);
+ waserror= True;
+ }
+ }
+ }
+ }
+ qh vertex_visit += (unsigned int)numfacets;
+ if (facetlist == qh facet_list) {
+ if (numfacets != qh num_facets - qh num_visible) {
+ qh_fprintf(qh ferr, 6140, "qhull internal error (qh_checkpolygon): actual number of facets is %d, cumulative facet count is %d - %d visible facets\n",
+ numfacets, qh num_facets, qh num_visible);
+ waserror= True;
+ }
+ qh vertex_visit++;
+ if (qh VERTEXneighbors) {
+ FORALLvertices {
+ qh_setcheck(vertex->neighbors, "neighbors for v", vertex->id);
+ if (vertex->deleted)
+ continue;
+ totvneighbors += qh_setsize(vertex->neighbors);
+ }
+ FORALLfacet_(facetlist)
+ totvertices += qh_setsize(facet->vertices);
+ if (totvneighbors != totvertices) {
+ qh_fprintf(qh ferr, 6141, "qhull internal error (qh_checkpolygon): vertex neighbors inconsistent. Totvneighbors %d, totvertices %d\n",
+ totvneighbors, totvertices);
+ waserror= True;
+ }
+ }
+ if (numvertices != qh num_vertices - qh_setsize(qh del_vertices)) {
+ qh_fprintf(qh ferr, 6142, "qhull internal error (qh_checkpolygon): actual number of vertices is %d, cumulative vertex count is %d\n",
+ numvertices, qh num_vertices - qh_setsize(qh del_vertices));
+ waserror= True;
+ }
+ if (qh hull_dim == 2 && numvertices != numfacets) {
+ qh_fprintf(qh ferr, 6143, "qhull internal error (qh_checkpolygon): #vertices %d != #facets %d\n",
+ numvertices, numfacets);
+ waserror= True;
+ }
+ if (qh hull_dim == 3 && numvertices + numfacets - numridges/2 != 2) {
+ qh_fprintf(qh ferr, 7063, "qhull warning: #vertices %d + #facets %d - #edges %d != 2\n\
+ A vertex appears twice in a edge list. May occur during merging.",
+ numvertices, numfacets, numridges/2);
+ /* occurs if lots of merging and a vertex ends up twice in an edge list. e.g., RBOX 1000 s W1e-13 t995849315 D2 | QHULL d Tc Tv */
+ }
+ }
+ if (waserror)
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+} /* checkpolygon */
+
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="checkvertex">-</a>
+
+ qh_checkvertex( vertex )
+ check vertex for consistency
+ checks vertex->neighbors
+
+ notes:
+ neighbors checked efficiently in checkpolygon
+*/
+void qh_checkvertex(vertexT *vertex) {
+ boolT waserror= False;
+ facetT *neighbor, **neighborp, *errfacet=NULL;
+
+ if (qh_pointid(vertex->point) == qh_IDunknown) {
+ qh_fprintf(qh ferr, 6144, "qhull internal error (qh_checkvertex): unknown point id %p\n", vertex->point);
+ waserror= True;
+ }
+ if (vertex->id >= qh vertex_id) {
+ qh_fprintf(qh ferr, 6145, "qhull internal error (qh_checkvertex): unknown vertex id %d\n", vertex->id);
+ waserror= True;
+ }
+ if (!waserror && !vertex->deleted) {
+ if (qh_setsize(vertex->neighbors)) {
+ FOREACHneighbor_(vertex) {
+ if (!qh_setin(neighbor->vertices, vertex)) {
+ qh_fprintf(qh ferr, 6146, "qhull internal error (qh_checkvertex): neighbor f%d does not contain v%d\n", neighbor->id, vertex->id);
+ errfacet= neighbor;
+ waserror= True;
+ }
+ }
+ }
+ }
+ if (waserror) {
+ qh_errprint("ERRONEOUS", NULL, NULL, NULL, vertex);
+ qh_errexit(qh_ERRqhull, errfacet, NULL);
+ }
+} /* checkvertex */
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="clearcenters">-</a>
+
+ qh_clearcenters( type )
+ clear old data from facet->center
+
+ notes:
+ sets new centertype
+ nop if CENTERtype is the same
+*/
+void qh_clearcenters(qh_CENTER type) {
+ facetT *facet;
+
+ if (qh CENTERtype != type) {
+ FORALLfacets {
+ if (facet->tricoplanar && !facet->keepcentrum)
+ facet->center= NULL; /* center is owned by the ->keepcentrum facet */
+ else if (qh CENTERtype == qh_ASvoronoi){
+ if (facet->center) {
+ qh_memfree(facet->center, qh center_size);
+ facet->center= NULL;
+ }
+ }else /* qh.CENTERtype == qh_AScentrum */ {
+ if (facet->center) {
+ qh_memfree(facet->center, qh normal_size);
+ facet->center= NULL;
+ }
+ }
+ }
+ qh CENTERtype= type;
+ }
+ trace2((qh ferr, 2043, "qh_clearcenters: switched to center type %d\n", type));
+} /* clearcenters */
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="createsimplex">-</a>
+
+ qh_createsimplex( vertices )
+ creates a simplex from a set of vertices
+
+ returns:
+ initializes qh.facet_list to the simplex
+ initializes qh.newfacet_list, .facet_tail
+ initializes qh.vertex_list, .newvertex_list, .vertex_tail
+
+ design:
+ initializes lists
+ for each vertex
+ create a new facet
+ for each new facet
+ create its neighbor set
+*/
+void qh_createsimplex(setT *vertices) {
+ facetT *facet= NULL, *newfacet;
+ boolT toporient= True;
+ int vertex_i, vertex_n, nth;
+ setT *newfacets= qh_settemp(qh hull_dim+1);
+ vertexT *vertex;
+
+ qh facet_list= qh newfacet_list= qh facet_tail= qh_newfacet();
+ qh num_facets= qh num_vertices= qh num_visible= 0;
+ qh vertex_list= qh newvertex_list= qh vertex_tail= qh_newvertex(NULL);
+ FOREACHvertex_i_(vertices) {
+ newfacet= qh_newfacet();
+ newfacet->vertices= qh_setnew_delnthsorted(vertices, vertex_n,
+ vertex_i, 0);
+ newfacet->toporient= (unsigned char)toporient;
+ qh_appendfacet(newfacet);
+ newfacet->newfacet= True;
+ qh_appendvertex(vertex);
+ qh_setappend(&newfacets, newfacet);
+ toporient ^= True;
+ }
+ FORALLnew_facets {
+ nth= 0;
+ FORALLfacet_(qh newfacet_list) {
+ if (facet != newfacet)
+ SETelem_(newfacet->neighbors, nth++)= facet;
+ }
+ qh_settruncate(newfacet->neighbors, qh hull_dim);
+ }
+ qh_settempfree(&newfacets);
+ trace1((qh ferr, 1028, "qh_createsimplex: created simplex\n"));
+} /* createsimplex */
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="delridge">-</a>
+
+ qh_delridge( ridge )
+ deletes ridge from data structures it belongs to
+ frees up its memory
+
+ notes:
+ in merge.c, caller sets vertex->delridge for each vertex
+ ridges also freed in qh_freeqhull
+*/
+void qh_delridge(ridgeT *ridge) {
+ void **freelistp; /* used if !qh_NOmem by qh_memfree_() */
+
+ qh_setdel(ridge->top->ridges, ridge);
+ qh_setdel(ridge->bottom->ridges, ridge);
+ qh_setfree(&(ridge->vertices));
+ qh_memfree_(ridge, (int)sizeof(ridgeT), freelistp);
+} /* delridge */
+
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="delvertex">-</a>
+
+ qh_delvertex( vertex )
+ deletes a vertex and frees its memory
+
+ notes:
+ assumes vertex->adjacencies have been updated if needed
+ unlinks from vertex_list
+*/
+void qh_delvertex(vertexT *vertex) {
+
+ if (vertex == qh tracevertex)
+ qh tracevertex= NULL;
+ qh_removevertex(vertex);
+ qh_setfree(&vertex->neighbors);
+ qh_memfree(vertex, (int)sizeof(vertexT));
+} /* delvertex */
+
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="facet3vertex">-</a>
+
+ qh_facet3vertex( )
+ return temporary set of 3-d vertices in qh_ORIENTclock order
+
+ design:
+ if simplicial facet
+ build set from facet->vertices with facet->toporient
+ else
+ for each ridge in order
+ build set from ridge's vertices
+*/
+setT *qh_facet3vertex(facetT *facet) {
+ ridgeT *ridge, *firstridge;
+ vertexT *vertex;
+ int cntvertices, cntprojected=0;
+ setT *vertices;
+
+ cntvertices= qh_setsize(facet->vertices);
+ vertices= qh_settemp(cntvertices);
+ if (facet->simplicial) {
+ if (cntvertices != 3) {
+ qh_fprintf(qh ferr, 6147, "qhull internal error (qh_facet3vertex): only %d vertices for simplicial facet f%d\n",
+ cntvertices, facet->id);
+ qh_errexit(qh_ERRqhull, facet, NULL);
+ }
+ qh_setappend(&vertices, SETfirst_(facet->vertices));
+ if (facet->toporient ^ qh_ORIENTclock)
+ qh_setappend(&vertices, SETsecond_(facet->vertices));
+ else
+ qh_setaddnth(&vertices, 0, SETsecond_(facet->vertices));
+ qh_setappend(&vertices, SETelem_(facet->vertices, 2));
+ }else {
+ ridge= firstridge= SETfirstt_(facet->ridges, ridgeT); /* no infinite */
+ while ((ridge= qh_nextridge3d(ridge, facet, &vertex))) {
+ qh_setappend(&vertices, vertex);
+ if (++cntprojected > cntvertices || ridge == firstridge)
+ break;
+ }
+ if (!ridge || cntprojected != cntvertices) {
+ qh_fprintf(qh ferr, 6148, "qhull internal error (qh_facet3vertex): ridges for facet %d don't match up. got at least %d\n",
+ facet->id, cntprojected);
+ qh_errexit(qh_ERRqhull, facet, ridge);
+ }
+ }
+ return vertices;
+} /* facet3vertex */
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="findbestfacet">-</a>
+
+ qh_findbestfacet( point, bestoutside, bestdist, isoutside )
+ find facet that is furthest below a point
+
+ for Delaunay triangulations,
+ Use qh_setdelaunay() to lift point to paraboloid and scale by 'Qbb' if needed
+ Do not use options 'Qbk', 'QBk', or 'QbB' since they scale the coordinates.
+
+ returns:
+ if bestoutside is set (e.g., qh_ALL)
+ returns best facet that is not upperdelaunay
+ if Delaunay and inside, point is outside circumsphere of bestfacet
+ else
+ returns first facet below point
+ if point is inside, returns nearest, !upperdelaunay facet
+ distance to facet
+ isoutside set if outside of facet
+
+ notes:
+ For tricoplanar facets, this finds one of the tricoplanar facets closest
+ to the point. For Delaunay triangulations, the point may be inside a
+ different tricoplanar facet. See <a href="../html/qh-code.htm#findfacet">locate a facet with qh_findbestfacet()</a>
+
+ If inside, qh_findbestfacet performs an exhaustive search
+ this may be too conservative. Sometimes it is clearly required.
+
+ qh_findbestfacet is not used by qhull.
+ uses qh.visit_id and qh.coplanarset
+
+ see:
+ <a href="geom.c#findbest">qh_findbest</a>
+*/
+facetT *qh_findbestfacet(pointT *point, boolT bestoutside,
+ realT *bestdist, boolT *isoutside) {
+ facetT *bestfacet= NULL;
+ int numpart, totpart= 0;
+
+ bestfacet= qh_findbest(point, qh facet_list,
+ bestoutside, !qh_ISnewfacets, bestoutside /* qh_NOupper */,
+ bestdist, isoutside, &totpart);
+ if (*bestdist < -qh DISTround) {
+ bestfacet= qh_findfacet_all(point, bestdist, isoutside, &numpart);
+ totpart += numpart;
+ if ((isoutside && *isoutside && bestoutside)
+ || (isoutside && !*isoutside && bestfacet->upperdelaunay)) {
+ bestfacet= qh_findbest(point, bestfacet,
+ bestoutside, False, bestoutside,
+ bestdist, isoutside, &totpart);
+ totpart += numpart;
+ }
+ }
+ trace3((qh ferr, 3014, "qh_findbestfacet: f%d dist %2.2g isoutside %d totpart %d\n",
+ bestfacet->id, *bestdist, (isoutside ? *isoutside : UINT_MAX), totpart));
+ return bestfacet;
+} /* findbestfacet */
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="findbestlower">-</a>
+
+ qh_findbestlower( facet, point, bestdist, numpart )
+ returns best non-upper, non-flipped neighbor of facet for point
+ if needed, searches vertex neighbors
+
+ returns:
+ returns bestdist and updates numpart
+
+ notes:
+ if Delaunay and inside, point is outside of circumsphere of bestfacet
+ called by qh_findbest() for points above an upperdelaunay facet
+
+*/
+facetT *qh_findbestlower(facetT *upperfacet, pointT *point, realT *bestdistp, int *numpart) {
+ facetT *neighbor, **neighborp, *bestfacet= NULL;
+ realT bestdist= -REALmax/2 /* avoid underflow */;
+ realT dist;
+ vertexT *vertex;
+ boolT isoutside= False; /* not used */
+
+ zinc_(Zbestlower);
+ FOREACHneighbor_(upperfacet) {
+ if (neighbor->upperdelaunay || neighbor->flipped)
+ continue;
+ (*numpart)++;
+ qh_distplane(point, neighbor, &dist);
+ if (dist > bestdist) {
+ bestfacet= neighbor;
+ bestdist= dist;
+ }
+ }
+ if (!bestfacet) {
+ zinc_(Zbestlowerv);
+ /* rarely called, numpart does not count nearvertex computations */
+ vertex= qh_nearvertex(upperfacet, point, &dist);
+ qh_vertexneighbors();
+ FOREACHneighbor_(vertex) {
+ if (neighbor->upperdelaunay || neighbor->flipped)
+ continue;
+ (*numpart)++;
+ qh_distplane(point, neighbor, &dist);
+ if (dist > bestdist) {
+ bestfacet= neighbor;
+ bestdist= dist;
+ }
+ }
+ }
+ if (!bestfacet) {
+ zinc_(Zbestlowerall); /* invoked once per point in outsideset */
+ zmax_(Zbestloweralln, qh num_facets);
+ /* [dec'15] Previously reported as QH6228 */
+ trace3((qh ferr, 3025, "qh_findbestlower: all neighbors of facet %d are flipped or upper Delaunay. Search all facets\n",
+ upperfacet->id));
+ /* rarely called */
+ bestfacet= qh_findfacet_all(point, &bestdist, &isoutside, numpart);
+ }
+ *bestdistp= bestdist;
+ trace3((qh ferr, 3015, "qh_findbestlower: f%d dist %2.2g for f%d p%d\n",
+ bestfacet->id, bestdist, upperfacet->id, qh_pointid(point)));
+ return bestfacet;
+} /* findbestlower */
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="findfacet_all">-</a>
+
+ qh_findfacet_all( point, bestdist, isoutside, numpart )
+ exhaustive search for facet below a point
+
+ for Delaunay triangulations,
+ Use qh_setdelaunay() to lift point to paraboloid and scale by 'Qbb' if needed
+ Do not use options 'Qbk', 'QBk', or 'QbB' since they scale the coordinates.
+
+ returns:
+ returns first facet below point
+ if point is inside,
+ returns nearest facet
+ distance to facet
+ isoutside if point is outside of the hull
+ number of distance tests
+
+ notes:
+ primarily for library users, rarely used by Qhull
+*/
+facetT *qh_findfacet_all(pointT *point, realT *bestdist, boolT *isoutside,
+ int *numpart) {
+ facetT *bestfacet= NULL, *facet;
+ realT dist;
+ int totpart= 0;
+
+ *bestdist= -REALmax;
+ *isoutside= False;
+ FORALLfacets {
+ if (facet->flipped || !facet->normal)
+ continue;
+ totpart++;
+ qh_distplane(point, facet, &dist);
+ if (dist > *bestdist) {
+ *bestdist= dist;
+ bestfacet= facet;
+ if (dist > qh MINoutside) {
+ *isoutside= True;
+ break;
+ }
+ }
+ }
+ *numpart= totpart;
+ trace3((qh ferr, 3016, "qh_findfacet_all: f%d dist %2.2g isoutside %d totpart %d\n",
+ getid_(bestfacet), *bestdist, *isoutside, totpart));
+ return bestfacet;
+} /* findfacet_all */
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="findgood">-</a>
+
+ qh_findgood( facetlist, goodhorizon )
+ identify good facets for qh.PRINTgood
+ if qh.GOODvertex>0
+ facet includes point as vertex
+ if !match, returns goodhorizon
+ inactive if qh.MERGING
+ if qh.GOODpoint
+ facet is visible or coplanar (>0) or not visible (<0)
+ if qh.GOODthreshold
+ facet->normal matches threshold
+ if !goodhorizon and !match,
+ selects facet with closest angle
+ sets GOODclosest
+
+ returns:
+ number of new, good facets found
+ determines facet->good
+ may update qh.GOODclosest
+
+ notes:
+ qh_findgood_all further reduces the good region
+
+ design:
+ count good facets
+ mark good facets for qh.GOODpoint
+ mark good facets for qh.GOODthreshold
+ if necessary
+ update qh.GOODclosest
+*/
+int qh_findgood(facetT *facetlist, int goodhorizon) {
+ facetT *facet, *bestfacet= NULL;
+ realT angle, bestangle= REALmax, dist;
+ int numgood=0;
+
+ FORALLfacet_(facetlist) {
+ if (facet->good)
+ numgood++;
+ }
+ if (qh GOODvertex>0 && !qh MERGING) {
+ FORALLfacet_(facetlist) {
+ if (!qh_isvertex(qh GOODvertexp, facet->vertices)) {
+ facet->good= False;
+ numgood--;
+ }
+ }
+ }
+ if (qh GOODpoint && numgood) {
+ FORALLfacet_(facetlist) {
+ if (facet->good && facet->normal) {
+ zinc_(Zdistgood);
+ qh_distplane(qh GOODpointp, facet, &dist);
+ if ((qh GOODpoint > 0) ^ (dist > 0.0)) {
+ facet->good= False;
+ numgood--;
+ }
+ }
+ }
+ }
+ if (qh GOODthreshold && (numgood || goodhorizon || qh GOODclosest)) {
+ FORALLfacet_(facetlist) {
+ if (facet->good && facet->normal) {
+ if (!qh_inthresholds(facet->normal, &angle)) {
+ facet->good= False;
+ numgood--;
+ if (angle < bestangle) {
+ bestangle= angle;
+ bestfacet= facet;
+ }
+ }
+ }
+ }
+ if (!numgood && (!goodhorizon || qh GOODclosest)) {
+ if (qh GOODclosest) {
+ if (qh GOODclosest->visible)
+ qh GOODclosest= NULL;
+ else {
+ qh_inthresholds(qh GOODclosest->normal, &angle);
+ if (angle < bestangle)
+ bestfacet= qh GOODclosest;
+ }
+ }
+ if (bestfacet && bestfacet != qh GOODclosest) {
+ if (qh GOODclosest)
+ qh GOODclosest->good= False;
+ qh GOODclosest= bestfacet;
+ bestfacet->good= True;
+ numgood++;
+ trace2((qh ferr, 2044, "qh_findgood: f%d is closest(%2.2g) to thresholds\n",
+ bestfacet->id, bestangle));
+ return numgood;
+ }
+ }else if (qh GOODclosest) { /* numgood > 0 */
+ qh GOODclosest->good= False;
+ qh GOODclosest= NULL;
+ }
+ }
+ zadd_(Zgoodfacet, numgood);
+ trace2((qh ferr, 2045, "qh_findgood: found %d good facets with %d good horizon\n",
+ numgood, goodhorizon));
+ if (!numgood && qh GOODvertex>0 && !qh MERGING)
+ return goodhorizon;
+ return numgood;
+} /* findgood */
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="findgood_all">-</a>
+
+ qh_findgood_all( facetlist )
+ apply other constraints for good facets (used by qh.PRINTgood)
+ if qh.GOODvertex
+ facet includes (>0) or doesn't include (<0) point as vertex
+ if last good facet and ONLYgood, prints warning and continues
+ if qh.SPLITthresholds
+ facet->normal matches threshold, or if none, the closest one
+ calls qh_findgood
+ nop if good not used
+
+ returns:
+ clears facet->good if not good
+ sets qh.num_good
+
+ notes:
+ this is like qh_findgood but more restrictive
+
+ design:
+ uses qh_findgood to mark good facets
+ marks facets for qh.GOODvertex
+ marks facets for qh.SPLITthreholds
+*/
+void qh_findgood_all(facetT *facetlist) {
+ facetT *facet, *bestfacet=NULL;
+ realT angle, bestangle= REALmax;
+ int numgood=0, startgood;
+
+ if (!qh GOODvertex && !qh GOODthreshold && !qh GOODpoint
+ && !qh SPLITthresholds)
+ return;
+ if (!qh ONLYgood)
+ qh_findgood(qh facet_list, 0);
+ FORALLfacet_(facetlist) {
+ if (facet->good)
+ numgood++;
+ }
+ if (qh GOODvertex <0 || (qh GOODvertex > 0 && qh MERGING)) {
+ FORALLfacet_(facetlist) {
+ if (facet->good && ((qh GOODvertex > 0) ^ !!qh_isvertex(qh GOODvertexp, facet->vertices))) {
+ if (!--numgood) {
+ if (qh ONLYgood) {
+ qh_fprintf(qh ferr, 7064, "qhull warning: good vertex p%d does not match last good facet f%d. Ignored.\n",
+ qh_pointid(qh GOODvertexp), facet->id);
+ return;
+ }else if (qh GOODvertex > 0)
+ qh_fprintf(qh ferr, 7065, "qhull warning: point p%d is not a vertex('QV%d').\n",
+ qh GOODvertex-1, qh GOODvertex-1);
+ else
+ qh_fprintf(qh ferr, 7066, "qhull warning: point p%d is a vertex for every facet('QV-%d').\n",
+ -qh GOODvertex - 1, -qh GOODvertex - 1);
+ }
+ facet->good= False;
+ }
+ }
+ }
+ startgood= numgood;
+ if (qh SPLITthresholds) {
+ FORALLfacet_(facetlist) {
+ if (facet->good) {
+ if (!qh_inthresholds(facet->normal, &angle)) {
+ facet->good= False;
+ numgood--;
+ if (angle < bestangle) {
+ bestangle= angle;
+ bestfacet= facet;
+ }
+ }
+ }
+ }
+ if (!numgood && bestfacet) {
+ bestfacet->good= True;
+ numgood++;
+ trace0((qh ferr, 23, "qh_findgood_all: f%d is closest(%2.2g) to thresholds\n",
+ bestfacet->id, bestangle));
+ return;
+ }
+ }
+ qh num_good= numgood;
+ trace0((qh ferr, 24, "qh_findgood_all: %d good facets remain out of %d facets\n",
+ numgood, startgood));
+} /* findgood_all */
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="furthestnext">-</a>
+
+ qh_furthestnext()
+ set qh.facet_next to facet with furthest of all furthest points
+ searches all facets on qh.facet_list
+
+ notes:
+ this may help avoid precision problems
+*/
+void qh_furthestnext(void /* qh.facet_list */) {
+ facetT *facet, *bestfacet= NULL;
+ realT dist, bestdist= -REALmax;
+
+ FORALLfacets {
+ if (facet->outsideset) {
+#if qh_COMPUTEfurthest
+ pointT *furthest;
+ furthest= (pointT*)qh_setlast(facet->outsideset);
+ zinc_(Zcomputefurthest);
+ qh_distplane(furthest, facet, &dist);
+#else
+ dist= facet->furthestdist;
+#endif
+ if (dist > bestdist) {
+ bestfacet= facet;
+ bestdist= dist;
+ }
+ }
+ }
+ if (bestfacet) {
+ qh_removefacet(bestfacet);
+ qh_prependfacet(bestfacet, &qh facet_next);
+ trace1((qh ferr, 1029, "qh_furthestnext: made f%d next facet(dist %.2g)\n",
+ bestfacet->id, bestdist));
+ }
+} /* furthestnext */
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="furthestout">-</a>
+
+ qh_furthestout( facet )
+ make furthest outside point the last point of outsideset
+
+ returns:
+ updates facet->outsideset
+ clears facet->notfurthest
+ sets facet->furthestdist
+
+ design:
+ determine best point of outsideset
+ make it the last point of outsideset
+*/
+void qh_furthestout(facetT *facet) {
+ pointT *point, **pointp, *bestpoint= NULL;
+ realT dist, bestdist= -REALmax;
+
+ FOREACHpoint_(facet->outsideset) {
+ qh_distplane(point, facet, &dist);
+ zinc_(Zcomputefurthest);
+ if (dist > bestdist) {
+ bestpoint= point;
+ bestdist= dist;
+ }
+ }
+ if (bestpoint) {
+ qh_setdel(facet->outsideset, point);
+ qh_setappend(&facet->outsideset, point);
+#if !qh_COMPUTEfurthest
+ facet->furthestdist= bestdist;
+#endif
+ }
+ facet->notfurthest= False;
+ trace3((qh ferr, 3017, "qh_furthestout: p%d is furthest outside point of f%d\n",
+ qh_pointid(point), facet->id));
+} /* furthestout */
+
+
+/*-<a href="qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="infiniteloop">-</a>
+
+ qh_infiniteloop( facet )
+ report infinite loop error due to facet
+*/
+void qh_infiniteloop(facetT *facet) {
+
+ qh_fprintf(qh ferr, 6149, "qhull internal error (qh_infiniteloop): potential infinite loop detected\n");
+ qh_errexit(qh_ERRqhull, facet, NULL);
+} /* qh_infiniteloop */
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="initbuild">-</a>
+
+ qh_initbuild()
+ initialize hull and outside sets with point array
+ qh.FIRSTpoint/qh.NUMpoints is point array
+ if qh.GOODpoint
+ adds qh.GOODpoint to initial hull
+
+ returns:
+ qh_facetlist with initial hull
+ points partioned into outside sets, coplanar sets, or inside
+ initializes qh.GOODpointp, qh.GOODvertexp,
+
+ design:
+ initialize global variables used during qh_buildhull
+ determine precision constants and points with max/min coordinate values
+ if qh.SCALElast, scale last coordinate(for 'd')
+ build initial simplex
+ partition input points into facets of initial simplex
+ set up lists
+ if qh.ONLYgood
+ check consistency
+ add qh.GOODvertex if defined
+*/
+void qh_initbuild( void) {
+ setT *maxpoints, *vertices;
+ facetT *facet;
+ int i, numpart;
+ realT dist;
+ boolT isoutside;
+
+ qh furthest_id= qh_IDunknown;
+ qh lastreport= 0;
+ qh facet_id= qh vertex_id= qh ridge_id= 0;
+ qh visit_id= qh vertex_visit= 0;
+ qh maxoutdone= False;
+
+ if (qh GOODpoint > 0)
+ qh GOODpointp= qh_point(qh GOODpoint-1);
+ else if (qh GOODpoint < 0)
+ qh GOODpointp= qh_point(-qh GOODpoint-1);
+ if (qh GOODvertex > 0)
+ qh GOODvertexp= qh_point(qh GOODvertex-1);
+ else if (qh GOODvertex < 0)
+ qh GOODvertexp= qh_point(-qh GOODvertex-1);
+ if ((qh GOODpoint
+ && (qh GOODpointp < qh first_point /* also catches !GOODpointp */
+ || qh GOODpointp > qh_point(qh num_points-1)))
+ || (qh GOODvertex
+ && (qh GOODvertexp < qh first_point /* also catches !GOODvertexp */
+ || qh GOODvertexp > qh_point(qh num_points-1)))) {
+ qh_fprintf(qh ferr, 6150, "qhull input error: either QGn or QVn point is > p%d\n",
+ qh num_points-1);
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ maxpoints= qh_maxmin(qh first_point, qh num_points, qh hull_dim);
+ if (qh SCALElast)
+ qh_scalelast(qh first_point, qh num_points, qh hull_dim,
+ qh MINlastcoord, qh MAXlastcoord, qh MAXwidth);
+ qh_detroundoff();
+ if (qh DELAUNAY && qh upper_threshold[qh hull_dim-1] > REALmax/2
+ && qh lower_threshold[qh hull_dim-1] < -REALmax/2) {
+ for (i=qh_PRINTEND; i--; ) {
+ if (qh PRINTout[i] == qh_PRINTgeom && qh DROPdim < 0
+ && !qh GOODthreshold && !qh SPLITthresholds)
+ break; /* in this case, don't set upper_threshold */
+ }
+ if (i < 0) {
+ if (qh UPPERdelaunay) { /* matches qh.upperdelaunay in qh_setfacetplane */
+ qh lower_threshold[qh hull_dim-1]= qh ANGLEround * qh_ZEROdelaunay;
+ qh GOODthreshold= True;
+ }else {
+ qh upper_threshold[qh hull_dim-1]= -qh ANGLEround * qh_ZEROdelaunay;
+ if (!qh GOODthreshold)
+ qh SPLITthresholds= True; /* build upper-convex hull even if Qg */
+ /* qh_initqhull_globals errors if Qg without Pdk/etc. */
+ }
+ }
+ }
+ vertices= qh_initialvertices(qh hull_dim, maxpoints, qh first_point, qh num_points);
+ qh_initialhull(vertices); /* initial qh facet_list */
+ qh_partitionall(vertices, qh first_point, qh num_points);
+ if (qh PRINToptions1st || qh TRACElevel || qh IStracing) {
+ if (qh TRACElevel || qh IStracing)
+ qh_fprintf(qh ferr, 8103, "\nTrace level %d for %s | %s\n",
+ qh IStracing ? qh IStracing : qh TRACElevel, qh rbox_command, qh qhull_command);
+ qh_fprintf(qh ferr, 8104, "Options selected for Qhull %s:\n%s\n", qh_version, qh qhull_options);
+ }
+ qh_resetlists(False, qh_RESETvisible /*qh.visible_list newvertex_list newfacet_list */);
+ qh facet_next= qh facet_list;
+ qh_furthestnext(/* qh.facet_list */);
+ if (qh PREmerge) {
+ qh cos_max= qh premerge_cos;
+ qh centrum_radius= qh premerge_centrum;
+ }
+ if (qh ONLYgood) {
+ if (qh GOODvertex > 0 && qh MERGING) {
+ qh_fprintf(qh ferr, 6151, "qhull input error: 'Qg QVn' (only good vertex) does not work with merging.\nUse 'QJ' to joggle the input or 'Q0' to turn off merging.\n");
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ if (!(qh GOODthreshold || qh GOODpoint
+ || (!qh MERGEexact && !qh PREmerge && qh GOODvertexp))) {
+ qh_fprintf(qh ferr, 6152, "qhull input error: 'Qg' (ONLYgood) needs a good threshold('Pd0D0'), a\n\
+good point(QGn or QG-n), or a good vertex with 'QJ' or 'Q0' (QVn).\n");
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ if (qh GOODvertex > 0 && !qh MERGING /* matches qh_partitionall */
+ && !qh_isvertex(qh GOODvertexp, vertices)) {
+ facet= qh_findbestnew(qh GOODvertexp, qh facet_list,
+ &dist, !qh_ALL, &isoutside, &numpart);
+ zadd_(Zdistgood, numpart);
+ if (!isoutside) {
+ qh_fprintf(qh ferr, 6153, "qhull input error: point for QV%d is inside initial simplex. It can not be made a vertex.\n",
+ qh_pointid(qh GOODvertexp));
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ if (!qh_addpoint(qh GOODvertexp, facet, False)) {
+ qh_settempfree(&vertices);
+ qh_settempfree(&maxpoints);
+ return;
+ }
+ }
+ qh_findgood(qh facet_list, 0);
+ }
+ qh_settempfree(&vertices);
+ qh_settempfree(&maxpoints);
+ trace1((qh ferr, 1030, "qh_initbuild: initial hull created and points partitioned\n"));
+} /* initbuild */
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="initialhull">-</a>
+
+ qh_initialhull( vertices )
+ constructs the initial hull as a DIM3 simplex of vertices
+
+ design:
+ creates a simplex (initializes lists)
+ determines orientation of simplex
+ sets hyperplanes for facets
+ doubles checks orientation (in case of axis-parallel facets with Gaussian elimination)
+ checks for flipped facets and qh.NARROWhull
+ checks the result
+*/
+void qh_initialhull(setT *vertices) {
+ facetT *facet, *firstfacet, *neighbor, **neighborp;
+ realT dist, angle, minangle= REALmax;
+#ifndef qh_NOtrace
+ int k;
+#endif
+
+ qh_createsimplex(vertices); /* qh.facet_list */
+ qh_resetlists(False, qh_RESETvisible);
+ qh facet_next= qh facet_list; /* advance facet when processed */
+ qh interior_point= qh_getcenter(vertices);
+ firstfacet= qh facet_list;
+ qh_setfacetplane(firstfacet);
+ zinc_(Znumvisibility); /* needs to be in printsummary */
+ qh_distplane(qh interior_point, firstfacet, &dist);
+ if (dist > 0) {
+ FORALLfacets
+ facet->toporient ^= (unsigned char)True;
+ }
+ FORALLfacets
+ qh_setfacetplane(facet);
+ FORALLfacets {
+ if (!qh_checkflipped(facet, NULL, qh_ALL)) {/* due to axis-parallel facet */
+ trace1((qh ferr, 1031, "qh_initialhull: initial orientation incorrect. Correct all facets\n"));
+ facet->flipped= False;
+ FORALLfacets {
+ facet->toporient ^= (unsigned char)True;
+ qh_orientoutside(facet);
+ }
+ break;
+ }
+ }
+ FORALLfacets {
+ if (!qh_checkflipped(facet, NULL, !qh_ALL)) { /* can happen with 'R0.1' */
+ if (qh DELAUNAY && ! qh ATinfinity) {
+ if (qh UPPERdelaunay)
+ qh_fprintf(qh ferr, 6240, "Qhull precision error: Initial simplex is cocircular or cospherical. Option 'Qs' searches all points. Can not compute the upper Delaunay triangulation or upper Voronoi diagram of cocircular/cospherical points.\n");
+ else
+ qh_fprintf(qh ferr, 6239, "Qhull precision error: Initial simplex is cocircular or cospherical. Use option 'Qz' for the Delaunay triangulation or Voronoi diagram of cocircular/cospherical points. Option 'Qz' adds a point \"at infinity\". Use option 'Qs' to search all points for the initial simplex.\n");
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ qh_precision("initial simplex is flat");
+ qh_fprintf(qh ferr, 6154, "Qhull precision error: Initial simplex is flat (facet %d is coplanar with the interior point)\n",
+ facet->id);
+ qh_errexit(qh_ERRsingular, NULL, NULL); /* calls qh_printhelp_singular */
+ }
+ FOREACHneighbor_(facet) {
+ angle= qh_getangle(facet->normal, neighbor->normal);
+ minimize_( minangle, angle);
+ }
+ }
+ if (minangle < qh_MAXnarrow && !qh NOnarrow) {
+ realT diff= 1.0 + minangle;
+
+ qh NARROWhull= True;
+ qh_option("_narrow-hull", NULL, &diff);
+ if (minangle < qh_WARNnarrow && !qh RERUN && qh PRINTprecision)
+ qh_printhelp_narrowhull(qh ferr, minangle);
+ }
+ zzval_(Zprocessed)= qh hull_dim+1;
+ qh_checkpolygon(qh facet_list);
+ qh_checkconvex(qh facet_list, qh_DATAfault);
+#ifndef qh_NOtrace
+ if (qh IStracing >= 1) {
+ qh_fprintf(qh ferr, 8105, "qh_initialhull: simplex constructed, interior point:");
+ for (k=0; k < qh hull_dim; k++)
+ qh_fprintf(qh ferr, 8106, " %6.4g", qh interior_point[k]);
+ qh_fprintf(qh ferr, 8107, "\n");
+ }
+#endif
+} /* initialhull */
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="initialvertices">-</a>
+
+ qh_initialvertices( dim, maxpoints, points, numpoints )
+ determines a non-singular set of initial vertices
+ maxpoints may include duplicate points
+
+ returns:
+ temporary set of dim+1 vertices in descending order by vertex id
+ if qh.RANDOMoutside && !qh.ALLpoints
+ picks random points
+ if dim >= qh_INITIALmax,
+ uses min/max x and max points with non-zero determinants
+
+ notes:
+ unless qh.ALLpoints,
+ uses maxpoints as long as determinate is non-zero
+*/
+setT *qh_initialvertices(int dim, setT *maxpoints, pointT *points, int numpoints) {
+ pointT *point, **pointp;
+ setT *vertices, *simplex, *tested;
+ realT randr;
+ int idx, point_i, point_n, k;
+ boolT nearzero= False;
+
+ vertices= qh_settemp(dim + 1);
+ simplex= qh_settemp(dim+1);
+ if (qh ALLpoints)
+ qh_maxsimplex(dim, NULL, points, numpoints, &simplex);
+ else if (qh RANDOMoutside) {
+ while (qh_setsize(simplex) != dim+1) {
+ randr= qh_RANDOMint;
+ randr= randr/(qh_RANDOMmax+1);
+ idx= (int)floor(qh num_points * randr);
+ while (qh_setin(simplex, qh_point(idx))) {
+ idx++; /* in case qh_RANDOMint always returns the same value */
+ idx= idx < qh num_points ? idx : 0;
+ }
+ qh_setappend(&simplex, qh_point(idx));
+ }
+ }else if (qh hull_dim >= qh_INITIALmax) {
+ tested= qh_settemp(dim+1);
+ qh_setappend(&simplex, SETfirst_(maxpoints)); /* max and min X coord */
+ qh_setappend(&simplex, SETsecond_(maxpoints));
+ qh_maxsimplex(fmin_(qh_INITIALsearch, dim), maxpoints, points, numpoints, &simplex);
+ k= qh_setsize(simplex);
+ FOREACHpoint_i_(maxpoints) {
+ if (point_i & 0x1) { /* first pick up max. coord. points */
+ if (!qh_setin(simplex, point) && !qh_setin(tested, point)){
+ qh_detsimplex(point, simplex, k, &nearzero);
+ if (nearzero)
+ qh_setappend(&tested, point);
+ else {
+ qh_setappend(&simplex, point);
+ if (++k == dim) /* use search for last point */
+ break;
+ }
+ }
+ }
+ }
+ while (k != dim && (point= (pointT*)qh_setdellast(maxpoints))) {
+ if (!qh_setin(simplex, point) && !qh_setin(tested, point)){
+ qh_detsimplex(point, simplex, k, &nearzero);
+ if (nearzero)
+ qh_setappend(&tested, point);
+ else {
+ qh_setappend(&simplex, point);
+ k++;
+ }
+ }
+ }
+ idx= 0;
+ while (k != dim && (point= qh_point(idx++))) {
+ if (!qh_setin(simplex, point) && !qh_setin(tested, point)){
+ qh_detsimplex(point, simplex, k, &nearzero);
+ if (!nearzero){
+ qh_setappend(&simplex, point);
+ k++;
+ }
+ }
+ }
+ qh_settempfree(&tested);
+ qh_maxsimplex(dim, maxpoints, points, numpoints, &simplex);
+ }else
+ qh_maxsimplex(dim, maxpoints, points, numpoints, &simplex);
+ FOREACHpoint_(simplex)
+ qh_setaddnth(&vertices, 0, qh_newvertex(point)); /* descending order */
+ qh_settempfree(&simplex);
+ return vertices;
+} /* initialvertices */
+
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="isvertex">-</a>
+
+ qh_isvertex( point, vertices )
+ returns vertex if point is in vertex set, else returns NULL
+
+ notes:
+ for qh.GOODvertex
+*/
+vertexT *qh_isvertex(pointT *point, setT *vertices) {
+ vertexT *vertex, **vertexp;
+
+ FOREACHvertex_(vertices) {
+ if (vertex->point == point)
+ return vertex;
+ }
+ return NULL;
+} /* isvertex */
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="makenewfacets">-</a>
+
+ qh_makenewfacets( point )
+ make new facets from point and qh.visible_list
+
+ returns:
+ qh.newfacet_list= list of new facets with hyperplanes and ->newfacet
+ qh.newvertex_list= list of vertices in new facets with ->newlist set
+
+ if (qh.ONLYgood)
+ newfacets reference horizon facets, but not vice versa
+ ridges reference non-simplicial horizon ridges, but not vice versa
+ does not change existing facets
+ else
+ sets qh.NEWfacets
+ new facets attached to horizon facets and ridges
+ for visible facets,
+ visible->r.replace is corresponding new facet
+
+ see also:
+ qh_makenewplanes() -- make hyperplanes for facets
+ qh_attachnewfacets() -- attachnewfacets if not done here(qh ONLYgood)
+ qh_matchnewfacets() -- match up neighbors
+ qh_updatevertices() -- update vertex neighbors and delvertices
+ qh_deletevisible() -- delete visible facets
+ qh_checkpolygon() --check the result
+ qh_triangulate() -- triangulate a non-simplicial facet
+
+ design:
+ for each visible facet
+ make new facets to its horizon facets
+ update its f.replace
+ clear its neighbor set
+*/
+vertexT *qh_makenewfacets(pointT *point /*visible_list*/) {
+ facetT *visible, *newfacet= NULL, *newfacet2= NULL, *neighbor, **neighborp;
+ vertexT *apex;
+ int numnew=0;
+
+ qh newfacet_list= qh facet_tail;
+ qh newvertex_list= qh vertex_tail;
+ apex= qh_newvertex(point);
+ qh_appendvertex(apex);
+ qh visit_id++;
+ if (!qh ONLYgood)
+ qh NEWfacets= True;
+ FORALLvisible_facets {
+ FOREACHneighbor_(visible)
+ neighbor->seen= False;
+ if (visible->ridges) {
+ visible->visitid= qh visit_id;
+ newfacet2= qh_makenew_nonsimplicial(visible, apex, &numnew);
+ }
+ if (visible->simplicial)
+ newfacet= qh_makenew_simplicial(visible, apex, &numnew);
+ if (!qh ONLYgood) {
+ if (newfacet2) /* newfacet is null if all ridges defined */
+ newfacet= newfacet2;
+ if (newfacet)
+ visible->f.replace= newfacet;
+ else
+ zinc_(Zinsidevisible);
+ SETfirst_(visible->neighbors)= NULL;
+ }
+ }
+ trace1((qh ferr, 1032, "qh_makenewfacets: created %d new facets from point p%d to horizon\n",
+ numnew, qh_pointid(point)));
+ if (qh IStracing >= 4)
+ qh_printfacetlist(qh newfacet_list, NULL, qh_ALL);
+ return apex;
+} /* makenewfacets */
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="matchduplicates">-</a>
+
+ qh_matchduplicates( atfacet, atskip, hashsize, hashcount )
+ match duplicate ridges in qh.hash_table for atfacet/atskip
+ duplicates marked with ->dupridge and qh_DUPLICATEridge
+
+ returns:
+ picks match with worst merge (min distance apart)
+ updates hashcount
+
+ see also:
+ qh_matchneighbor
+
+ notes:
+
+ design:
+ compute hash value for atfacet and atskip
+ repeat twice -- once to make best matches, once to match the rest
+ for each possible facet in qh.hash_table
+ if it is a matching facet and pass 2
+ make match
+ unless tricoplanar, mark match for merging (qh_MERGEridge)
+ [e.g., tricoplanar RBOX s 1000 t993602376 | QHULL C-1e-3 d Qbb FA Qt]
+ if it is a matching facet and pass 1
+ test if this is a better match
+ if pass 1,
+ make best match (it will not be merged)
+*/
+#ifndef qh_NOmerge
+void qh_matchduplicates(facetT *atfacet, int atskip, int hashsize, int *hashcount) {
+ boolT same, ismatch;
+ int hash, scan;
+ facetT *facet, *newfacet, *maxmatch= NULL, *maxmatch2= NULL, *nextfacet;
+ int skip, newskip, nextskip= 0, maxskip= 0, maxskip2= 0, makematch;
+ realT maxdist= -REALmax, mindist, dist2, low, high;
+
+ hash= qh_gethash(hashsize, atfacet->vertices, qh hull_dim, 1,
+ SETelem_(atfacet->vertices, atskip));
+ trace2((qh ferr, 2046, "qh_matchduplicates: find duplicate matches for f%d skip %d hash %d hashcount %d\n",
+ atfacet->id, atskip, hash, *hashcount));
+ for (makematch= 0; makematch < 2; makematch++) {
+ qh visit_id++;
+ for (newfacet= atfacet, newskip= atskip; newfacet; newfacet= nextfacet, newskip= nextskip) {
+ zinc_(Zhashlookup);
+ nextfacet= NULL;
+ newfacet->visitid= qh visit_id;
+ for (scan= hash; (facet= SETelemt_(qh hash_table, scan, facetT));
+ scan= (++scan >= hashsize ? 0 : scan)) {
+ if (!facet->dupridge || facet->visitid == qh visit_id)
+ continue;
+ zinc_(Zhashtests);
+ if (qh_matchvertices(1, newfacet->vertices, newskip, facet->vertices, &skip, &same)) {
+ ismatch= (same == (boolT)(newfacet->toporient ^ facet->toporient));
+ if (SETelemt_(facet->neighbors, skip, facetT) != qh_DUPLICATEridge) {
+ if (!makematch) {
+ qh_fprintf(qh ferr, 6155, "qhull internal error (qh_matchduplicates): missing dupridge at f%d skip %d for new f%d skip %d hash %d\n",
+ facet->id, skip, newfacet->id, newskip, hash);
+ qh_errexit2(qh_ERRqhull, facet, newfacet);
+ }
+ }else if (ismatch && makematch) {
+ if (SETelemt_(newfacet->neighbors, newskip, facetT) == qh_DUPLICATEridge) {
+ SETelem_(facet->neighbors, skip)= newfacet;
+ if (newfacet->tricoplanar)
+ SETelem_(newfacet->neighbors, newskip)= facet;
+ else
+ SETelem_(newfacet->neighbors, newskip)= qh_MERGEridge;
+ *hashcount -= 2; /* removed two unmatched facets */
+ trace4((qh ferr, 4059, "qh_matchduplicates: duplicate f%d skip %d matched with new f%d skip %d merge\n",
+ facet->id, skip, newfacet->id, newskip));
+ }
+ }else if (ismatch) {
+ mindist= qh_getdistance(facet, newfacet, &low, &high);
+ dist2= qh_getdistance(newfacet, facet, &low, &high);
+ minimize_(mindist, dist2);
+ if (mindist > maxdist) {
+ maxdist= mindist;
+ maxmatch= facet;
+ maxskip= skip;
+ maxmatch2= newfacet;
+ maxskip2= newskip;
+ }
+ trace3((qh ferr, 3018, "qh_matchduplicates: duplicate f%d skip %d new f%d skip %d at dist %2.2g, max is now f%d f%d\n",
+ facet->id, skip, newfacet->id, newskip, mindist,
+ maxmatch->id, maxmatch2->id));
+ }else { /* !ismatch */
+ nextfacet= facet;
+ nextskip= skip;
+ }
+ }
+ if (makematch && !facet
+ && SETelemt_(facet->neighbors, skip, facetT) == qh_DUPLICATEridge) {
+ qh_fprintf(qh ferr, 6156, "qhull internal error (qh_matchduplicates): no MERGEridge match for duplicate f%d skip %d at hash %d\n",
+ newfacet->id, newskip, hash);
+ qh_errexit(qh_ERRqhull, newfacet, NULL);
+ }
+ }
+ } /* end of for each new facet at hash */
+ if (!makematch) {
+ if (!maxmatch) {
+ qh_fprintf(qh ferr, 6157, "qhull internal error (qh_matchduplicates): no maximum match at duplicate f%d skip %d at hash %d\n",
+ atfacet->id, atskip, hash);
+ qh_errexit(qh_ERRqhull, atfacet, NULL);
+ }
+ SETelem_(maxmatch->neighbors, maxskip)= maxmatch2; /* maxmatch!=0 by QH6157 */
+ SETelem_(maxmatch2->neighbors, maxskip2)= maxmatch;
+ *hashcount -= 2; /* removed two unmatched facets */
+ zzinc_(Zmultiridge);
+ trace0((qh ferr, 25, "qh_matchduplicates: duplicate f%d skip %d matched with new f%d skip %d keep\n",
+ maxmatch->id, maxskip, maxmatch2->id, maxskip2));
+ qh_precision("ridge with multiple neighbors");
+ if (qh IStracing >= 4)
+ qh_errprint("DUPLICATED/MATCH", maxmatch, maxmatch2, NULL, NULL);
+ }
+ }
+} /* matchduplicates */
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="nearcoplanar">-</a>
+
+ qh_nearcoplanar()
+ for all facets, remove near-inside points from facet->coplanarset</li>
+ coplanar points defined by innerplane from qh_outerinner()
+
+ returns:
+ if qh KEEPcoplanar && !qh KEEPinside
+ facet->coplanarset only contains coplanar points
+ if qh.JOGGLEmax
+ drops inner plane by another qh.JOGGLEmax diagonal since a
+ vertex could shift out while a coplanar point shifts in
+
+ notes:
+ used for qh.PREmerge and qh.JOGGLEmax
+ must agree with computation of qh.NEARcoplanar in qh_detroundoff()
+ design:
+ if not keeping coplanar or inside points
+ free all coplanar sets
+ else if not keeping both coplanar and inside points
+ remove !coplanar or !inside points from coplanar sets
+*/
+void qh_nearcoplanar(void /* qh.facet_list */) {
+ facetT *facet;
+ pointT *point, **pointp;
+ int numpart;
+ realT dist, innerplane;
+
+ if (!qh KEEPcoplanar && !qh KEEPinside) {
+ FORALLfacets {
+ if (facet->coplanarset)
+ qh_setfree( &facet->coplanarset);
+ }
+ }else if (!qh KEEPcoplanar || !qh KEEPinside) {
+ qh_outerinner(NULL, NULL, &innerplane);
+ if (qh JOGGLEmax < REALmax/2)
+ innerplane -= qh JOGGLEmax * sqrt((realT)qh hull_dim);
+ numpart= 0;
+ FORALLfacets {
+ if (facet->coplanarset) {
+ FOREACHpoint_(facet->coplanarset) {
+ numpart++;
+ qh_distplane(point, facet, &dist);
+ if (dist < innerplane) {
+ if (!qh KEEPinside)
+ SETref_(point)= NULL;
+ }else if (!qh KEEPcoplanar)
+ SETref_(point)= NULL;
+ }
+ qh_setcompact(facet->coplanarset);
+ }
+ }
+ zzadd_(Zcheckpart, numpart);
+ }
+} /* nearcoplanar */
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="nearvertex">-</a>
+
+ qh_nearvertex( facet, point, bestdist )
+ return nearest vertex in facet to point
+
+ returns:
+ vertex and its distance
+
+ notes:
+ if qh.DELAUNAY
+ distance is measured in the input set
+ searches neighboring tricoplanar facets (requires vertexneighbors)
+ Slow implementation. Recomputes vertex set for each point.
+ The vertex set could be stored in the qh.keepcentrum facet.
+*/
+vertexT *qh_nearvertex(facetT *facet, pointT *point, realT *bestdistp) {
+ realT bestdist= REALmax, dist;
+ vertexT *bestvertex= NULL, *vertex, **vertexp, *apex;
+ coordT *center;
+ facetT *neighbor, **neighborp;
+ setT *vertices;
+ int dim= qh hull_dim;
+
+ if (qh DELAUNAY)
+ dim--;
+ if (facet->tricoplanar) {
+ if (!qh VERTEXneighbors || !facet->center) {
+ qh_fprintf(qh ferr, 6158, "qhull internal error (qh_nearvertex): qh.VERTEXneighbors and facet->center required for tricoplanar facets\n");
+ qh_errexit(qh_ERRqhull, facet, NULL);
+ }
+ vertices= qh_settemp(qh TEMPsize);
+ apex= SETfirstt_(facet->vertices, vertexT);
+ center= facet->center;
+ FOREACHneighbor_(apex) {
+ if (neighbor->center == center) {
+ FOREACHvertex_(neighbor->vertices)
+ qh_setappend(&vertices, vertex);
+ }
+ }
+ }else
+ vertices= facet->vertices;
+ FOREACHvertex_(vertices) {
+ dist= qh_pointdist(vertex->point, point, -dim);
+ if (dist < bestdist) {
+ bestdist= dist;
+ bestvertex= vertex;
+ }
+ }
+ if (facet->tricoplanar)
+ qh_settempfree(&vertices);
+ *bestdistp= sqrt(bestdist);
+ if (!bestvertex) {
+ qh_fprintf(qh ferr, 6261, "qhull internal error (qh_nearvertex): did not find bestvertex for f%d p%d\n", facet->id, qh_pointid(point));
+ qh_errexit(qh_ERRqhull, facet, NULL);
+ }
+ trace3((qh ferr, 3019, "qh_nearvertex: v%d dist %2.2g for f%d p%d\n",
+ bestvertex->id, *bestdistp, facet->id, qh_pointid(point))); /* bestvertex!=0 by QH2161 */
+ return bestvertex;
+} /* nearvertex */
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="newhashtable">-</a>
+
+ qh_newhashtable( newsize )
+ returns size of qh.hash_table of at least newsize slots
+
+ notes:
+ assumes qh.hash_table is NULL
+ qh_HASHfactor determines the number of extra slots
+ size is not divisible by 2, 3, or 5
+*/
+int qh_newhashtable(int newsize) {
+ int size;
+
+ size= ((newsize+1)*qh_HASHfactor) | 0x1; /* odd number */
+ while (True) {
+ if (newsize<0 || size<0) {
+ qh_fprintf(qhmem.ferr, 6236, "qhull error (qh_newhashtable): negative request (%d) or size (%d). Did int overflow due to high-D?\n", newsize, size); /* WARN64 */
+ qh_errexit(qhmem_ERRmem, NULL, NULL);
+ }
+ if ((size%3) && (size%5))
+ break;
+ size += 2;
+ /* loop terminates because there is an infinite number of primes */
+ }
+ qh hash_table= qh_setnew(size);
+ qh_setzero(qh hash_table, 0, size);
+ return size;
+} /* newhashtable */
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="newvertex">-</a>
+
+ qh_newvertex( point )
+ returns a new vertex for point
+*/
+vertexT *qh_newvertex(pointT *point) {
+ vertexT *vertex;
+
+ zinc_(Ztotvertices);
+ vertex= (vertexT *)qh_memalloc((int)sizeof(vertexT));
+ memset((char *) vertex, (size_t)0, sizeof(vertexT));
+ if (qh vertex_id == UINT_MAX) {
+ qh_memfree(vertex, (int)sizeof(vertexT));
+ qh_fprintf(qh ferr, 6159, "qhull error: more than 2^32 vertices. vertexT.id field overflows. Vertices would not be sorted correctly.\n");
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }
+ if (qh vertex_id == qh tracevertex_id)
+ qh tracevertex= vertex;
+ vertex->id= qh vertex_id++;
+ vertex->point= point;
+ trace4((qh ferr, 4060, "qh_newvertex: vertex p%d(v%d) created\n", qh_pointid(vertex->point),
+ vertex->id));
+ return(vertex);
+} /* newvertex */
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="nextridge3d">-</a>
+
+ qh_nextridge3d( atridge, facet, vertex )
+ return next ridge and vertex for a 3d facet
+ returns NULL on error
+ [for QhullFacet::nextRidge3d] Does not call qh_errexit nor access qh_qh.
+
+ notes:
+ in qh_ORIENTclock order
+ this is a O(n^2) implementation to trace all ridges
+ be sure to stop on any 2nd visit
+ same as QhullRidge::nextRidge3d
+ does not use qh_qh or qh_errexit [QhullFacet.cpp]
+
+ design:
+ for each ridge
+ exit if it is the ridge after atridge
+*/
+ridgeT *qh_nextridge3d(ridgeT *atridge, facetT *facet, vertexT **vertexp) {
+ vertexT *atvertex, *vertex, *othervertex;
+ ridgeT *ridge, **ridgep;
+
+ if ((atridge->top == facet) ^ qh_ORIENTclock)
+ atvertex= SETsecondt_(atridge->vertices, vertexT);
+ else
+ atvertex= SETfirstt_(atridge->vertices, vertexT);
+ FOREACHridge_(facet->ridges) {
+ if (ridge == atridge)
+ continue;
+ if ((ridge->top == facet) ^ qh_ORIENTclock) {
+ othervertex= SETsecondt_(ridge->vertices, vertexT);
+ vertex= SETfirstt_(ridge->vertices, vertexT);
+ }else {
+ vertex= SETsecondt_(ridge->vertices, vertexT);
+ othervertex= SETfirstt_(ridge->vertices, vertexT);
+ }
+ if (vertex == atvertex) {
+ if (vertexp)
+ *vertexp= othervertex;
+ return ridge;
+ }
+ }
+ return NULL;
+} /* nextridge3d */
+#else /* qh_NOmerge */
+void qh_matchduplicates(facetT *atfacet, int atskip, int hashsize, int *hashcount) {
+}
+ridgeT *qh_nextridge3d(ridgeT *atridge, facetT *facet, vertexT **vertexp) {
+
+ return NULL;
+}
+#endif /* qh_NOmerge */
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="outcoplanar">-</a>
+
+ qh_outcoplanar()
+ move points from all facets' outsidesets to their coplanarsets
+
+ notes:
+ for post-processing under qh.NARROWhull
+
+ design:
+ for each facet
+ for each outside point for facet
+ partition point into coplanar set
+*/
+void qh_outcoplanar(void /* facet_list */) {
+ pointT *point, **pointp;
+ facetT *facet;
+ realT dist;
+
+ trace1((qh ferr, 1033, "qh_outcoplanar: move outsideset to coplanarset for qh NARROWhull\n"));
+ FORALLfacets {
+ FOREACHpoint_(facet->outsideset) {
+ qh num_outside--;
+ if (qh KEEPcoplanar || qh KEEPnearinside) {
+ qh_distplane(point, facet, &dist);
+ zinc_(Zpartition);
+ qh_partitioncoplanar(point, facet, &dist);
+ }
+ }
+ qh_setfree(&facet->outsideset);
+ }
+} /* outcoplanar */
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="point">-</a>
+
+ qh_point( id )
+ return point for a point id, or NULL if unknown
+
+ alternative code:
+ return((pointT *)((unsigned long)qh.first_point
+ + (unsigned long)((id)*qh.normal_size)));
+*/
+pointT *qh_point(int id) {
+
+ if (id < 0)
+ return NULL;
+ if (id < qh num_points)
+ return qh first_point + id * qh hull_dim;
+ id -= qh num_points;
+ if (id < qh_setsize(qh other_points))
+ return SETelemt_(qh other_points, id, pointT);
+ return NULL;
+} /* point */
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="point_add">-</a>
+
+ qh_point_add( set, point, elem )
+ stores elem at set[point.id]
+
+ returns:
+ access function for qh_pointfacet and qh_pointvertex
+
+ notes:
+ checks point.id
+*/
+void qh_point_add(setT *set, pointT *point, void *elem) {
+ int id, size;
+
+ SETreturnsize_(set, size);
+ if ((id= qh_pointid(point)) < 0)
+ qh_fprintf(qh ferr, 7067, "qhull internal warning (point_add): unknown point %p id %d\n",
+ point, id);
+ else if (id >= size) {
+ qh_fprintf(qh ferr, 6160, "qhull internal errror(point_add): point p%d is out of bounds(%d)\n",
+ id, size);
+ qh_errexit(qh_ERRqhull, NULL, NULL);
+ }else
+ SETelem_(set, id)= elem;
+} /* point_add */
+
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="pointfacet">-</a>
+
+ qh_pointfacet()
+ return temporary set of facet for each point
+ the set is indexed by point id
+
+ notes:
+ vertices assigned to one of the facets
+ coplanarset assigned to the facet
+ outside set assigned to the facet
+ NULL if no facet for point (inside)
+ includes qh.GOODpointp
+
+ access:
+ FOREACHfacet_i_(facets) { ... }
+ SETelem_(facets, i)
+
+ design:
+ for each facet
+ add each vertex
+ add each coplanar point
+ add each outside point
+*/
+setT *qh_pointfacet(void /*qh.facet_list*/) {
+ int numpoints= qh num_points + qh_setsize(qh other_points);
+ setT *facets;
+ facetT *facet;
+ vertexT *vertex, **vertexp;
+ pointT *point, **pointp;
+
+ facets= qh_settemp(numpoints);
+ qh_setzero(facets, 0, numpoints);
+ qh vertex_visit++;
+ FORALLfacets {
+ FOREACHvertex_(facet->vertices) {
+ if (vertex->visitid != qh vertex_visit) {
+ vertex->visitid= qh vertex_visit;
+ qh_point_add(facets, vertex->point, facet);
+ }
+ }
+ FOREACHpoint_(facet->coplanarset)
+ qh_point_add(facets, point, facet);
+ FOREACHpoint_(facet->outsideset)
+ qh_point_add(facets, point, facet);
+ }
+ return facets;
+} /* pointfacet */
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="pointvertex">-</a>
+
+ qh_pointvertex( )
+ return temporary set of vertices indexed by point id
+ entry is NULL if no vertex for a point
+ this will include qh.GOODpointp
+
+ access:
+ FOREACHvertex_i_(vertices) { ... }
+ SETelem_(vertices, i)
+*/
+setT *qh_pointvertex(void /*qh.facet_list*/) {
+ int numpoints= qh num_points + qh_setsize(qh other_points);
+ setT *vertices;
+ vertexT *vertex;
+
+ vertices= qh_settemp(numpoints);
+ qh_setzero(vertices, 0, numpoints);
+ FORALLvertices
+ qh_point_add(vertices, vertex->point, vertex);
+ return vertices;
+} /* pointvertex */
+
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="prependfacet">-</a>
+
+ qh_prependfacet( facet, facetlist )
+ prepend facet to the start of a facetlist
+
+ returns:
+ increments qh.numfacets
+ updates facetlist, qh.facet_list, facet_next
+
+ notes:
+ be careful of prepending since it can lose a pointer.
+ e.g., can lose _next by deleting and then prepending before _next
+*/
+void qh_prependfacet(facetT *facet, facetT **facetlist) {
+ facetT *prevfacet, *list;
+
+
+ trace4((qh ferr, 4061, "qh_prependfacet: prepend f%d before f%d\n",
+ facet->id, getid_(*facetlist)));
+ if (!*facetlist)
+ (*facetlist)= qh facet_tail;
+ list= *facetlist;
+ prevfacet= list->previous;
+ facet->previous= prevfacet;
+ if (prevfacet)
+ prevfacet->next= facet;
+ list->previous= facet;
+ facet->next= *facetlist;
+ if (qh facet_list == list) /* this may change *facetlist */
+ qh facet_list= facet;
+ if (qh facet_next == list)
+ qh facet_next= facet;
+ *facetlist= facet;
+ qh num_facets++;
+} /* prependfacet */
+
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="printhashtable">-</a>
+
+ qh_printhashtable( fp )
+ print hash table to fp
+
+ notes:
+ not in I/O to avoid bringing io.c in
+
+ design:
+ for each hash entry
+ if defined
+ if unmatched or will merge (NULL, qh_MERGEridge, qh_DUPLICATEridge)
+ print entry and neighbors
+*/
+void qh_printhashtable(FILE *fp) {
+ facetT *facet, *neighbor;
+ int id, facet_i, facet_n, neighbor_i= 0, neighbor_n= 0;
+ vertexT *vertex, **vertexp;
+
+ FOREACHfacet_i_(qh hash_table) {
+ if (facet) {
+ FOREACHneighbor_i_(facet) {
+ if (!neighbor || neighbor == qh_MERGEridge || neighbor == qh_DUPLICATEridge)
+ break;
+ }
+ if (neighbor_i == neighbor_n)
+ continue;
+ qh_fprintf(fp, 9283, "hash %d f%d ", facet_i, facet->id);
+ FOREACHvertex_(facet->vertices)
+ qh_fprintf(fp, 9284, "v%d ", vertex->id);
+ qh_fprintf(fp, 9285, "\n neighbors:");
+ FOREACHneighbor_i_(facet) {
+ if (neighbor == qh_MERGEridge)
+ id= -3;
+ else if (neighbor == qh_DUPLICATEridge)
+ id= -2;
+ else
+ id= getid_(neighbor);
+ qh_fprintf(fp, 9286, " %d", id);
+ }
+ qh_fprintf(fp, 9287, "\n");
+ }
+ }
+} /* printhashtable */
+
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="printlists">-</a>
+
+ qh_printlists( fp )
+ print out facet and vertex list for debugging (without 'f/v' tags)
+*/
+void qh_printlists(void) {
+ facetT *facet;
+ vertexT *vertex;
+ int count= 0;
+
+ qh_fprintf(qh ferr, 8108, "qh_printlists: facets:");
+ FORALLfacets {
+ if (++count % 100 == 0)
+ qh_fprintf(qh ferr, 8109, "\n ");
+ qh_fprintf(qh ferr, 8110, " %d", facet->id);
+ }
+ qh_fprintf(qh ferr, 8111, "\n new facets %d visible facets %d next facet for qh_addpoint %d\n vertices(new %d):",
+ getid_(qh newfacet_list), getid_(qh visible_list), getid_(qh facet_next),
+ getid_(qh newvertex_list));
+ count = 0;
+ FORALLvertices {
+ if (++count % 100 == 0)
+ qh_fprintf(qh ferr, 8112, "\n ");
+ qh_fprintf(qh ferr, 8113, " %d", vertex->id);
+ }
+ qh_fprintf(qh ferr, 8114, "\n");
+} /* printlists */
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="resetlists">-</a>
+
+ qh_resetlists( stats, qh_RESETvisible )
+ reset newvertex_list, newfacet_list, visible_list
+ if stats,
+ maintains statistics
+
+ returns:
+ visible_list is empty if qh_deletevisible was called
+*/
+void qh_resetlists(boolT stats, boolT resetVisible /*qh.newvertex_list newfacet_list visible_list*/) {
+ vertexT *vertex;
+ facetT *newfacet, *visible;
+ int totnew=0, totver=0;
+
+ if (stats) {
+ FORALLvertex_(qh newvertex_list)
+ totver++;
+ FORALLnew_facets
+ totnew++;
+ zadd_(Zvisvertextot, totver);
+ zmax_(Zvisvertexmax, totver);
+ zadd_(Znewfacettot, totnew);
+ zmax_(Znewfacetmax, totnew);
+ }
+ FORALLvertex_(qh newvertex_list)
+ vertex->newlist= False;
+ qh newvertex_list= NULL;
+ FORALLnew_facets
+ newfacet->newfacet= False;
+ qh newfacet_list= NULL;
+ if (resetVisible) {
+ FORALLvisible_facets {
+ visible->f.replace= NULL;
+ visible->visible= False;
+ }
+ qh num_visible= 0;
+ }
+ qh visible_list= NULL; /* may still have visible facets via qh_triangulate */
+ qh NEWfacets= False;
+} /* resetlists */
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="setvoronoi_all">-</a>
+
+ qh_setvoronoi_all()
+ compute Voronoi centers for all facets
+ includes upperDelaunay facets if qh.UPPERdelaunay ('Qu')
+
+ returns:
+ facet->center is the Voronoi center
+
+ notes:
+ this is unused/untested code
+ please email bradb@shore.net if this works ok for you
+
+ use:
+ FORALLvertices {...} to locate the vertex for a point.
+ FOREACHneighbor_(vertex) {...} to visit the Voronoi centers for a Voronoi cell.
+*/
+void qh_setvoronoi_all(void) {
+ facetT *facet;
+
+ qh_clearcenters(qh_ASvoronoi);
+ qh_vertexneighbors();
+
+ FORALLfacets {
+ if (!facet->normal || !facet->upperdelaunay || qh UPPERdelaunay) {
+ if (!facet->center)
+ facet->center= qh_facetcenter(facet->vertices);
+ }
+ }
+} /* setvoronoi_all */
+
+#ifndef qh_NOmerge
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="triangulate">-</a>
+
+ qh_triangulate()
+ triangulate non-simplicial facets on qh.facet_list,
+ if qh VORONOI, sets Voronoi centers of non-simplicial facets
+ nop if hasTriangulation
+
+ returns:
+ all facets simplicial
+ each tricoplanar facet has ->f.triowner == owner of ->center,normal,etc.
+
+ notes:
+ call after qh_check_output since may switch to Voronoi centers
+ Output may overwrite ->f.triowner with ->f.area
+*/
+void qh_triangulate(void /*qh.facet_list*/) {
+ facetT *facet, *nextfacet, *owner;
+ int onlygood= qh ONLYgood;
+ facetT *neighbor, *visible= NULL, *facet1, *facet2, *new_facet_list= NULL;
+ facetT *orig_neighbor= NULL, *otherfacet;
+ vertexT *new_vertex_list= NULL;
+ mergeT *merge;
+ mergeType mergetype;
+ int neighbor_i, neighbor_n;
+
+ if (qh hasTriangulation)
+ return;
+ trace1((qh ferr, 1034, "qh_triangulate: triangulate non-simplicial facets\n"));
+ if (qh hull_dim == 2)
+ return;
+ if (qh VORONOI) { /* otherwise lose Voronoi centers [could rebuild vertex set from tricoplanar] */
+ qh_clearcenters(qh_ASvoronoi);
+ qh_vertexneighbors();
+ }
+ qh ONLYgood= False; /* for makenew_nonsimplicial */
+ qh visit_id++;
+ qh NEWfacets= True;
+ qh degen_mergeset= qh_settemp(qh TEMPsize);
+ qh newvertex_list= qh vertex_tail;
+ for (facet= qh facet_list; facet && facet->next; facet= nextfacet) { /* non-simplicial facets moved to end */
+ nextfacet= facet->next;
+ if (facet->visible || facet->simplicial)
+ continue;
+ /* triangulate all non-simplicial facets, otherwise merging does not work, e.g., RBOX c P-0.1 P+0.1 P+0.1 D3 | QHULL d Qt Tv */
+ if (!new_facet_list)
+ new_facet_list= facet; /* will be moved to end */
+ qh_triangulate_facet(facet, &new_vertex_list);
+ }
+ trace2((qh ferr, 2047, "qh_triangulate: delete null facets from f%d -- apex same as second vertex\n", getid_(new_facet_list)));
+ for (facet= new_facet_list; facet && facet->next; facet= nextfacet) { /* null facets moved to end */
+ nextfacet= facet->next;
+ if (facet->visible)
+ continue;
+ if (facet->ridges) {
+ if (qh_setsize(facet->ridges) > 0) {
+ qh_fprintf(qh ferr, 6161, "qhull error (qh_triangulate): ridges still defined for f%d\n", facet->id);
+ qh_errexit(qh_ERRqhull, facet, NULL);
+ }
+ qh_setfree(&facet->ridges);
+ }
+ if (SETfirst_(facet->vertices) == SETsecond_(facet->vertices)) {
+ zinc_(Ztrinull);
+ qh_triangulate_null(facet);
+ }
+ }
+ trace2((qh ferr, 2048, "qh_triangulate: delete %d or more mirror facets -- same vertices and neighbors\n", qh_setsize(qh degen_mergeset)));
+ qh visible_list= qh facet_tail;
+ while ((merge= (mergeT*)qh_setdellast(qh degen_mergeset))) {
+ facet1= merge->facet1;
+ facet2= merge->facet2;
+ mergetype= merge->type;
+ qh_memfree(merge, (int)sizeof(mergeT));
+ if (mergetype == MRGmirror) {
+ zinc_(Ztrimirror);
+ qh_triangulate_mirror(facet1, facet2);
+ }
+ }
+ qh_settempfree(&qh degen_mergeset);
+ trace2((qh ferr, 2049, "qh_triangulate: update neighbor lists for vertices from v%d\n", getid_(new_vertex_list)));
+ qh newvertex_list= new_vertex_list; /* all vertices of new facets */
+ qh visible_list= NULL;
+ qh_updatevertices(/*qh.newvertex_list, empty newfacet_list and visible_list*/);
+ qh_resetlists(False, !qh_RESETvisible /*qh.newvertex_list, empty newfacet_list and visible_list*/);
+
+ trace2((qh ferr, 2050, "qh_triangulate: identify degenerate tricoplanar facets from f%d\n", getid_(new_facet_list)));
+ trace2((qh ferr, 2051, "qh_triangulate: and replace facet->f.triowner with tricoplanar facets that own center, normal, etc.\n"));
+ FORALLfacet_(new_facet_list) {
+ if (facet->tricoplanar && !facet->visible) {
+ FOREACHneighbor_i_(facet) {
+ if (neighbor_i == 0) { /* first iteration */
+ if (neighbor->tricoplanar)
+ orig_neighbor= neighbor->f.triowner;
+ else
+ orig_neighbor= neighbor;
+ }else {
+ if (neighbor->tricoplanar)
+ otherfacet= neighbor->f.triowner;
+ else
+ otherfacet= neighbor;
+ if (orig_neighbor == otherfacet) {
+ zinc_(Ztridegen);
+ facet->degenerate= True;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ trace2((qh ferr, 2052, "qh_triangulate: delete visible facets -- non-simplicial, null, and mirrored facets\n"));
+ owner= NULL;
+ visible= NULL;
+ for (facet= new_facet_list; facet && facet->next; facet= nextfacet) { /* may delete facet */
+ nextfacet= facet->next;
+ if (facet->visible) {
+ if (facet->tricoplanar) { /* a null or mirrored facet */
+ qh_delfacet(facet);
+ qh num_visible--;
+ }else { /* a non-simplicial facet followed by its tricoplanars */
+ if (visible && !owner) {
+ /* RBOX 200 s D5 t1001471447 | QHULL Qt C-0.01 Qx Qc Tv Qt -- f4483 had 6 vertices/neighbors and 8 ridges */
+ trace2((qh ferr, 2053, "qh_triangulate: all tricoplanar facets degenerate for non-simplicial facet f%d\n",
+ visible->id));
+ qh_delfacet(visible);
+ qh num_visible--;
+ }
+ visible= facet;
+ owner= NULL;
+ }
+ }else if (facet->tricoplanar) {
+ if (facet->f.triowner != visible || visible==NULL) {
+ qh_fprintf(qh ferr, 6162, "qhull error (qh_triangulate): tricoplanar facet f%d not owned by its visible, non-simplicial facet f%d\n", facet->id, getid_(visible));
+ qh_errexit2(qh_ERRqhull, facet, visible);
+ }
+ if (owner)
+ facet->f.triowner= owner;
+ else if (!facet->degenerate) {
+ owner= facet;
+ nextfacet= visible->next; /* rescan tricoplanar facets with owner, visible!=0 by QH6162 */
+ facet->keepcentrum= True; /* one facet owns ->normal, etc. */
+ facet->coplanarset= visible->coplanarset;
+ facet->outsideset= visible->outsideset;
+ visible->coplanarset= NULL;
+ visible->outsideset= NULL;
+ if (!qh TRInormals) { /* center and normal copied to tricoplanar facets */
+ visible->center= NULL;
+ visible->normal= NULL;
+ }
+ qh_delfacet(visible);
+ qh num_visible--;
+ }
+ }
+ }
+ if (visible && !owner) {
+ trace2((qh ferr, 2054, "qh_triangulate: all tricoplanar facets degenerate for last non-simplicial facet f%d\n",
+ visible->id));
+ qh_delfacet(visible);
+ qh num_visible--;
+ }
+ qh NEWfacets= False;
+ qh ONLYgood= onlygood; /* restore value */
+ if (qh CHECKfrequently)
+ qh_checkpolygon(qh facet_list);
+ qh hasTriangulation= True;
+} /* triangulate */
+
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="triangulate_facet">-</a>
+
+ qh_triangulate_facet(qh, facetA, &firstVertex )
+ triangulate a non-simplicial facet
+ if qh.CENTERtype=qh_ASvoronoi, sets its Voronoi center
+ returns:
+ qh.newfacet_list == simplicial facets
+ facet->tricoplanar set and ->keepcentrum false
+ facet->degenerate set if duplicated apex
+ facet->f.trivisible set to facetA
+ facet->center copied from facetA (created if qh_ASvoronoi)
+ qh_eachvoronoi, qh_detvridge, qh_detvridge3 assume centers copied
+ facet->normal,offset,maxoutside copied from facetA
+
+ notes:
+ only called by qh_triangulate
+ qh_makenew_nonsimplicial uses neighbor->seen for the same
+ if qh.TRInormals, newfacet->normal will need qh_free
+ if qh.TRInormals and qh_AScentrum, newfacet->center will need qh_free
+ keepcentrum is also set on Zwidefacet in qh_mergefacet
+ freed by qh_clearcenters
+
+ see also:
+ qh_addpoint() -- add a point
+ qh_makenewfacets() -- construct a cone of facets for a new vertex
+
+ design:
+ if qh_ASvoronoi,
+ compute Voronoi center (facet->center)
+ select first vertex (highest ID to preserve ID ordering of ->vertices)
+ triangulate from vertex to ridges
+ copy facet->center, normal, offset
+ update vertex neighbors
+*/
+void qh_triangulate_facet(facetT *facetA, vertexT **first_vertex) {
+ facetT *newfacet;
+ facetT *neighbor, **neighborp;
+ vertexT *apex;
+ int numnew=0;
+
+ trace3((qh ferr, 3020, "qh_triangulate_facet: triangulate facet f%d\n", facetA->id));
+
+ if (qh IStracing >= 4)
+ qh_printfacet(qh ferr, facetA);
+ FOREACHneighbor_(facetA) {
+ neighbor->seen= False;
+ neighbor->coplanar= False;
+ }
+ if (qh CENTERtype == qh_ASvoronoi && !facetA->center /* matches upperdelaunay in qh_setfacetplane() */
+ && fabs_(facetA->normal[qh hull_dim -1]) >= qh ANGLEround * qh_ZEROdelaunay) {
+ facetA->center= qh_facetcenter(facetA->vertices);
+ }
+ qh_willdelete(facetA, NULL);
+ qh newfacet_list= qh facet_tail;
+ facetA->visitid= qh visit_id;
+ apex= SETfirstt_(facetA->vertices, vertexT);
+ qh_makenew_nonsimplicial(facetA, apex, &numnew);
+ SETfirst_(facetA->neighbors)= NULL;
+ FORALLnew_facets {
+ newfacet->tricoplanar= True;
+ newfacet->f.trivisible= facetA;
+ newfacet->degenerate= False;
+ newfacet->upperdelaunay= facetA->upperdelaunay;
+ newfacet->good= facetA->good;
+ if (qh TRInormals) { /* 'Q11' triangulate duplicates ->normal and ->center */
+ newfacet->keepcentrum= True;
+ if(facetA->normal){
+ newfacet->normal= qh_memalloc(qh normal_size);
+ memcpy((char *)newfacet->normal, facetA->normal, qh normal_size);
+ }
+ if (qh CENTERtype == qh_AScentrum)
+ newfacet->center= qh_getcentrum(newfacet);
+ else if (qh CENTERtype == qh_ASvoronoi && facetA->center){
+ newfacet->center= qh_memalloc(qh center_size);
+ memcpy((char *)newfacet->center, facetA->center, qh center_size);
+ }
+ }else {
+ newfacet->keepcentrum= False;
+ /* one facet will have keepcentrum=True at end of qh_triangulate */
+ newfacet->normal= facetA->normal;
+ newfacet->center= facetA->center;
+ }
+ newfacet->offset= facetA->offset;
+#if qh_MAXoutside
+ newfacet->maxoutside= facetA->maxoutside;
+#endif
+ }
+ qh_matchnewfacets(/*qh.newfacet_list*/);
+ zinc_(Ztricoplanar);
+ zadd_(Ztricoplanartot, numnew);
+ zmax_(Ztricoplanarmax, numnew);
+ qh visible_list= NULL;
+ if (!(*first_vertex))
+ (*first_vertex)= qh newvertex_list;
+ qh newvertex_list= NULL;
+ qh_updatevertices(/*qh.newfacet_list, qh.empty visible_list and qh.newvertex_list*/);
+ qh_resetlists(False, !qh_RESETvisible /*qh.newfacet_list, qh.empty visible_list and qh.newvertex_list*/);
+} /* triangulate_facet */
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="triangulate_link">-</a>
+
+ qh_triangulate_link(oldfacetA, facetA, oldfacetB, facetB)
+ relink facetA to facetB via oldfacets
+ returns:
+ adds mirror facets to qh degen_mergeset (4-d and up only)
+ design:
+ if they are already neighbors, the opposing neighbors become MRGmirror facets
+*/
+void qh_triangulate_link(facetT *oldfacetA, facetT *facetA, facetT *oldfacetB, facetT *facetB) {
+ int errmirror= False;
+
+ trace3((qh ferr, 3021, "qh_triangulate_link: relink old facets f%d and f%d between neighbors f%d and f%d\n",
+ oldfacetA->id, oldfacetB->id, facetA->id, facetB->id));
+ if (qh_setin(facetA->neighbors, facetB)) {
+ if (!qh_setin(facetB->neighbors, facetA))
+ errmirror= True;
+ else
+ qh_appendmergeset(facetA, facetB, MRGmirror, NULL);
+ }else if (qh_setin(facetB->neighbors, facetA))
+ errmirror= True;
+ if (errmirror) {
+ qh_fprintf(qh ferr, 6163, "qhull error (qh_triangulate_link): mirror facets f%d and f%d do not match for old facets f%d and f%d\n",
+ facetA->id, facetB->id, oldfacetA->id, oldfacetB->id);
+ qh_errexit2(qh_ERRqhull, facetA, facetB);
+ }
+ qh_setreplace(facetB->neighbors, oldfacetB, facetA);
+ qh_setreplace(facetA->neighbors, oldfacetA, facetB);
+} /* triangulate_link */
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="triangulate_mirror">-</a>
+
+ qh_triangulate_mirror(facetA, facetB)
+ delete mirrored facets from qh_triangulate_null() and qh_triangulate_mirror
+ a mirrored facet shares the same vertices of a logical ridge
+ design:
+ since a null facet duplicates the first two vertices, the opposing neighbors absorb the null facet
+ if they are already neighbors, the opposing neighbors become MRGmirror facets
+*/
+void qh_triangulate_mirror(facetT *facetA, facetT *facetB) {
+ facetT *neighbor, *neighborB;
+ int neighbor_i, neighbor_n;
+
+ trace3((qh ferr, 3022, "qh_triangulate_mirror: delete mirrored facets f%d and f%d\n",
+ facetA->id, facetB->id));
+ FOREACHneighbor_i_(facetA) {
+ neighborB= SETelemt_(facetB->neighbors, neighbor_i, facetT);
+ if (neighbor == neighborB)
+ continue; /* occurs twice */
+ qh_triangulate_link(facetA, neighbor, facetB, neighborB);
+ }
+ qh_willdelete(facetA, NULL);
+ qh_willdelete(facetB, NULL);
+} /* triangulate_mirror */
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="triangulate_null">-</a>
+
+ qh_triangulate_null(facetA)
+ remove null facetA from qh_triangulate_facet()
+ a null facet has vertex #1 (apex) == vertex #2
+ returns:
+ adds facetA to ->visible for deletion after qh_updatevertices
+ qh degen_mergeset contains mirror facets (4-d and up only)
+ design:
+ since a null facet duplicates the first two vertices, the opposing neighbors absorb the null facet
+ if they are already neighbors, the opposing neighbors become MRGmirror facets
+*/
+void qh_triangulate_null(facetT *facetA) {
+ facetT *neighbor, *otherfacet;
+
+ trace3((qh ferr, 3023, "qh_triangulate_null: delete null facet f%d\n", facetA->id));
+ neighbor= SETfirstt_(facetA->neighbors, facetT);
+ otherfacet= SETsecondt_(facetA->neighbors, facetT);
+ qh_triangulate_link(facetA, neighbor, facetA, otherfacet);
+ qh_willdelete(facetA, NULL);
+} /* triangulate_null */
+
+#else /* qh_NOmerge */
+void qh_triangulate(void) {
+}
+#endif /* qh_NOmerge */
+
+ /*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="vertexintersect">-</a>
+
+ qh_vertexintersect( vertexsetA, vertexsetB )
+ intersects two vertex sets (inverse id ordered)
+ vertexsetA is a temporary set at the top of qhmem.tempstack
+
+ returns:
+ replaces vertexsetA with the intersection
+
+ notes:
+ could overwrite vertexsetA if currently too slow
+*/
+void qh_vertexintersect(setT **vertexsetA,setT *vertexsetB) {
+ setT *intersection;
+
+ intersection= qh_vertexintersect_new(*vertexsetA, vertexsetB);
+ qh_settempfree(vertexsetA);
+ *vertexsetA= intersection;
+ qh_settemppush(intersection);
+} /* vertexintersect */
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="vertexintersect_new">-</a>
+
+ qh_vertexintersect_new( )
+ intersects two vertex sets (inverse id ordered)
+
+ returns:
+ a new set
+*/
+setT *qh_vertexintersect_new(setT *vertexsetA,setT *vertexsetB) {
+ setT *intersection= qh_setnew(qh hull_dim - 1);
+ vertexT **vertexA= SETaddr_(vertexsetA, vertexT);
+ vertexT **vertexB= SETaddr_(vertexsetB, vertexT);
+
+ while (*vertexA && *vertexB) {
+ if (*vertexA == *vertexB) {
+ qh_setappend(&intersection, *vertexA);
+ vertexA++; vertexB++;
+ }else {
+ if ((*vertexA)->id > (*vertexB)->id)
+ vertexA++;
+ else
+ vertexB++;
+ }
+ }
+ return intersection;
+} /* vertexintersect_new */
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="vertexneighbors">-</a>
+
+ qh_vertexneighbors()
+ for each vertex in qh.facet_list,
+ determine its neighboring facets
+
+ returns:
+ sets qh.VERTEXneighbors
+ nop if qh.VERTEXneighbors already set
+ qh_addpoint() will maintain them
+
+ notes:
+ assumes all vertex->neighbors are NULL
+
+ design:
+ for each facet
+ for each vertex
+ append facet to vertex->neighbors
+*/
+void qh_vertexneighbors(void /*qh.facet_list*/) {
+ facetT *facet;
+ vertexT *vertex, **vertexp;
+
+ if (qh VERTEXneighbors)
+ return;
+ trace1((qh ferr, 1035, "qh_vertexneighbors: determining neighboring facets for each vertex\n"));
+ qh vertex_visit++;
+ FORALLfacets {
+ if (facet->visible)
+ continue;
+ FOREACHvertex_(facet->vertices) {
+ if (vertex->visitid != qh vertex_visit) {
+ vertex->visitid= qh vertex_visit;
+ vertex->neighbors= qh_setnew(qh hull_dim);
+ }
+ qh_setappend(&vertex->neighbors, facet);
+ }
+ }
+ qh VERTEXneighbors= True;
+} /* vertexneighbors */
+
+/*-<a href="qh-poly.htm#TOC"
+ >-------------------------------</a><a name="vertexsubset">-</a>
+
+ qh_vertexsubset( vertexsetA, vertexsetB )
+ returns True if vertexsetA is a subset of vertexsetB
+ assumes vertexsets are sorted
+
+ note:
+ empty set is a subset of any other set
+*/
+boolT qh_vertexsubset(setT *vertexsetA, setT *vertexsetB) {
+ vertexT **vertexA= (vertexT **) SETaddr_(vertexsetA, vertexT);
+ vertexT **vertexB= (vertexT **) SETaddr_(vertexsetB, vertexT);
+
+ while (True) {
+ if (!*vertexA)
+ return True;
+ if (!*vertexB)
+ return False;
+ if ((*vertexA)->id > (*vertexB)->id)
+ return False;
+ if (*vertexA == *vertexB)
+ vertexA++;
+ vertexB++;
+ }
+ return False; /* avoid warnings */
+} /* vertexsubset */
diff --git a/xs/src/qhull/src/libqhull/qh-geom.htm b/xs/src/qhull/src/libqhull/qh-geom.htm
new file mode 100644
index 000000000..6dc7465eb
--- /dev/null
+++ b/xs/src/qhull/src/libqhull/qh-geom.htm
@@ -0,0 +1,295 @@
+<!-- Do not edit with Front Page, it adds too many spaces -->
+<html>
+<head>
+<meta http-equiv="Content-Type"
+content="text/html; charset=iso-8859-1">
+<title>geom.c, geom2.c -- geometric and floating point routines</title>
+</head>
+
+<body>
+<!-- Navigation links -->
+<p><a name="TOP"><b>Up:</b></a> <a
+href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual</a>: Table of Contents <br>
+<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a>
+&#149; <a href="../../html/qh-quick.htm#options">Options</a>
+&#149; <a href="../../html/qh-opto.htm#output">Output</a>
+&#149; <a href="../../html/qh-optf.htm#format">Formats</a>
+&#149; <a href="../../html/qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="../../html/qh-optp.htm#print">Print</a>
+&#149; <a href="../../html/qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="../../html/qh-optc.htm#prec">Precision</a>
+&#149; <a href="../../html/qh-optt.htm#trace">Trace</a>
+&#149; <a href="index.htm">Functions</a><br>
+<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a><br>
+<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br>
+<b>To:</b> <a href="qh-geom.htm#TOC">Geom</a> &#149; <a href="qh-globa.htm">Global</a>
+&#149; <a href="qh-io.htm">Io</a> &#149; <a href="qh-mem.htm">Mem</a>
+&#149; <a href="qh-merge.htm">Merge</a> &#149; <a href="qh-poly.htm">Poly</a>
+&#149; <a href="qh-qhull.htm">Qhull</a> &#149; <a href="qh-set.htm">Set</a>
+&#149; <a href="qh-stat.htm">Stat</a> &#149; <a href="qh-user.htm">User</a>
+</p>
+
+<hr>
+<!-- Main text of document. -->
+
+<h2>geom.c, geom2.c, random.c -- geometric and floating point routines</h2>
+<blockquote>
+<p>Geometrically, a vertex is a point with <em>d</em> coordinates
+and a facet is a halfspace. A <em>halfspace</em> is defined by an
+oriented hyperplane through the facet's vertices. A <em>hyperplane</em>
+is defined by <em>d</em> normalized coefficients and an offset. A
+point is <em>above</em> a facet if its distance to the facet is
+positive.</p>
+
+<p>Qhull uses floating point coordinates for input points,
+vertices, halfspace equations, centrums, and an interior point.</p>
+
+<p>Qhull may be configured for single precision or double
+precision floating point arithmetic (see <a href="user.h#realT">realT</a>
+). </p>
+
+<p>Each floating point operation may incur round-off error (see
+<a href="qh-merge.htm#TOC">Merge</a>). The maximum error for distance
+computations is determined at initialization. The roundoff error
+in halfspace computation is accounted for by computing the
+distance from vertices to the halfspace. </p>
+</blockquote>
+<p><b>Copyright &copy; 1995-2015 C.B. Barber</b></p>
+<hr>
+<p><a href="#TOP">&#187;</a> <b>Geom</b>
+<a name="TOC">&#149;</a> <a href="qh-globa.htm#TOC">Global</a> &#149;
+<a href="qh-io.htm#TOC">Io</a> &#149; <a href="qh-mem.htm#TOC">Mem</a> &#149;
+<a href="qh-merge.htm#TOC">Merge</a> &#149; <a href="qh-poly.htm#TOC">Poly</a> &#149;
+<a href="qh-qhull.htm#TOC">Qhull</a> &#149; <a href="qh-set.htm#TOC">Set</a> &#149;
+<a href="qh-stat.htm#TOC">Stat</a> &#149; <a href="qh-user.htm#TOC">User</a> </p>
+
+<h3>Index to <a href="geom.c">geom.c</a>,
+<a href="geom2.c">geom2.c</a>, <a href="geom.h">geom.h</a>,
+<a href="random.c">random.c</a>, <a href="random.h">random.h</a>
+</h3>
+
+<ul>
+<li><a href="#gtype">geometric data types and constants</a> </li>
+<li><a href="#gmacro">mathematical macros</a>
+</li>
+<li><a href="#gmath">mathematical functions</a> </li>
+<li><a href="#gcomp">computational geometry functions</a> </li>
+<li><a href="#gpoint">point array functions</a> </li>
+<li><a href="#gfacet">geometric facet functions</a> </li>
+<li><a href="#ground">geometric roundoff functions</a></li>
+</ul>
+
+<h3><a href="qh-geom.htm#TOC">&#187;</a><a name="gtype">geometric data types
+and constants</a></h3>
+
+<ul>
+<li><a href="libqhull.h#coordT">coordT</a> coordinates and
+coefficients are stored as realT</li>
+<li><a href="libqhull.h#pointT">pointT</a> a point is an array
+of <tt>DIM3</tt> coordinates </li>
+</ul>
+
+<h3><a href="qh-geom.htm#TOC">&#187;</a><a name="gmacro">mathematical macros</a></h3>
+
+<ul>
+<li><a href="geom.h#fabs_">fabs_</a> returns the absolute
+value of a </li>
+<li><a href="geom.h#fmax_">fmax_</a> returns the maximum
+value of a and b </li>
+<li><a href="geom.h#fmin_">fmin_</a> returns the minimum
+value of a and b </li>
+<li><a href="geom.h#maximize_">maximize_</a> maximize a value
+</li>
+<li><a href="geom.h#minimize_">minimize_</a> minimize a value
+</li>
+<li><a href="geom.h#det2_">det2_</a> compute a 2-d
+determinate </li>
+<li><a href="geom.h#det3_">det3_</a> compute a 3-d
+determinate </li>
+<li><a href="geom.h#dX">dX, dY, dZ</a> compute the difference
+between two coordinates </li>
+</ul>
+
+<h3><a href="qh-geom.htm#TOC">&#187;</a><a name="gmath">mathematical functions</a></h3>
+
+<ul>
+<li><a href="geom.c#backnormal">qh_backnormal</a> solve for
+normal using back substitution </li>
+<li><a href="geom2.c#crossproduct">qh_crossproduct</a>
+compute the cross product of two 3-d vectors </li>
+<li><a href="geom2.c#determinant">qh_determinant</a> compute
+the determinant of a square matrix </li>
+<li><a href="geom.c#gausselim">qh_gausselim</a> Gaussian
+elimination with partial pivoting </li>
+<li><a href="geom2.c#gram_schmidt">qh_gram_schmidt</a>
+implements Gram-Schmidt orthogonalization by rows </li>
+<li><a href="geom2.c#maxabsval">qh_maxabsval</a> return max
+absolute value of a vector </li>
+<li><a href="geom2.c#minabsval">qh_minabsval</a> return min
+absolute value of a dim vector </li>
+<li><a href="geom2.c#mindiff">qh_mindiff</a> return index of
+min absolute difference of two vectors </li>
+<li><a href="geom.c#normalize">qh_normalize</a> normalize a
+vector </li>
+<li><a href="geom.c#normalize2">qh_normalize2</a> normalize a
+vector and report if too small </li>
+<li><a href="geom2.c#printmatrix">qh_printmatrix</a> print
+matrix given by row vectors </li>
+<li><a href="random.c#rand">qh_rand/srand</a> generate random
+numbers </li>
+<li><a href="random.c#randomfactor">qh_randomfactor</a> return
+a random factor near 1.0 </li>
+<li><a href="random.c#randommatrix">qh_randommatrix</a>
+generate a random dimXdim matrix in range (-1,1) </li>
+</ul>
+
+<h3><a href="qh-geom.htm#TOC">&#187;</a><a name="gcomp">computational geometry functions</a></h3>
+
+<ul>
+<li><a href="geom2.c#detsimplex">qh_detsimplex</a> compute
+determinate of a simplex of points </li>
+<li><a href="io.c#detvnorm">qh_detvnorm</a> determine normal for Voronoi ridge </li>
+<li><a href="geom2.c#distnorm">qh_distnorm</a> compute
+distance from point to hyperplane as defined by normal and offset</li>
+<li><a href="geom2.c#facetarea_simplex">qh_facetarea_simplex</a>
+return area of a simplex</li>
+<li><a href="geom.c#getangle">qh_getangle</a> return cosine
+of angle (i.e., dot product) </li>
+<li><a href="geom.c#getcenter">qh_getcenter</a> return
+arithmetic center for a set of vertices </li>
+<li><a href="geom2.c#pointdist">qh_pointdist</a> return
+distance between two points </li>
+<li><a href="geom2.c#rotatepoints">qh_rotatepoints</a> rotate
+numpoints points by a row matrix </li>
+<li><a href="geom2.c#sethalfspace">qh_sethalfspace</a> set
+coords to dual of halfspace relative to an interior point </li>
+<li><a href="geom.c#sethyperplane_det">qh_sethyperplane_det</a>
+return hyperplane for oriented simplex using determinates
+</li>
+<li><a href="geom.c#sethyperplane_gauss">qh_sethyperplane_gauss</a>
+return hyperplane for oriented simplex using Gaussian
+elimination </li>
+<li><a href="geom2.c#voronoi_center">qh_voronoi_center</a>
+return Voronoi center for a set of points </li>
+</ul>
+
+<h3><a href="qh-geom.htm#TOC">&#187;</a><a name="gpoint">point array functions</a></h3>
+<ul>
+<li><a href="geom2.c#copypoints">qh_copypoints</a> return
+malloc'd copy of points</li>
+<li><a href="geom2.c#joggleinput">qh_joggleinput</a> joggle
+input points by qh.JOGGLEmax </li>
+<li><a href="geom2.c#maxmin">qh_maxmin</a> return max/min
+points for each dimension</li>
+<li><a href="geom2.c#maxsimplex">qh_maxsimplex</a> determines
+maximum simplex for a set of points </li>
+<li><a href="geom2.c#printpoints">qh_printpoints</a> print ids for a
+set of points </li>
+<li><a href="geom2.c#projectinput">qh_projectinput</a> project
+input using qh DELAUNAY and qh low_bound/high_bound </li>
+<li><a href="geom2.c#projectpoints">qh_projectpoints</a>
+project points along one or more dimensions </li>
+<li><a href="geom2.c#rotateinput">qh_rotateinput</a> rotate
+input points using row matrix </li>
+<li><a href="geom2.c#scaleinput">qh_scaleinput</a> scale
+input points using qh low_bound/high_bound </li>
+<li><a href="geom2.c#scalelast">qh_scalelast</a> scale last
+coordinate to [0,m] for Delaunay triangulations </li>
+<li><a href="geom2.c#scalepoints">qh_scalepoints</a> scale
+points to new lowbound and highbound </li>
+<li><a href="geom2.c#setdelaunay">qh_setdelaunay</a> project
+points to paraboloid for Delaunay triangulation </li>
+<li><a href="geom2.c#sethalfspace_all">qh_sethalfspace_all</a>
+generate dual for halfspace intersection with interior
+point </li>
+</ul>
+
+<h3><a href="qh-geom.htm#TOC">&#187;</a><a name="gfacet">geometric facet functions</a></h3>
+<ul>
+<li><a href="geom.c#distplane">qh_distplane</a> return
+distance from point to facet </li>
+<li><a href="geom2.c#facetarea">qh_facetarea</a> return area
+of a facet </li>
+<li><a href="geom2.c#facetcenter">qh_facetcenter</a> return
+Voronoi center for a facet's vertices </li>
+<li><a href="geom.c#findbest">qh_findbest</a> find visible
+facet or best facet for a point </li>
+<li><a href="geom.c#findbesthorizon">qh_findbesthorizon</a>
+update best new facet with horizon facets</li>
+<li><a href="geom.c#findbestnew">qh_findbestnew</a> find best
+new facet for point </li>
+<li><a href="geom2.c#getarea">qh_getarea</a> get area of all
+facets in facetlist, collect statistics </li>
+<li><a href="geom.c#getcentrum">qh_getcentrum</a> return
+centrum for a facet </li>
+<li><a href="geom.c#getdistance">qh_getdistance</a> returns
+the max and min distance of a facet's vertices to a
+neighboring facet</li>
+<li><a href="geom2.c#findgooddist">qh_findgooddist</a> find
+best good facet visible for point from facet </li>
+<li><a href="geom2.c#inthresholds">qh_inthresholds</a> return
+True if facet normal within 'Pdn' and 'PDn'</li>
+<li><a href="geom2.c#orientoutside">qh_orientoutside</a>
+orient facet so that <tt>qh.interior_point</tt> is inside</li>
+<li><a href="geom.c#projectpoint">qh_projectpoint</a> project
+point onto a facet </li>
+<li><a href="geom.c#setfacetplane">qh_setfacetplane</a> sets
+the hyperplane for a facet </li>
+<li><a href="geom2.c#sharpnewfacets">qh_sharpnewfacets</a> true
+if new facets contains a sharp corner</li>
+</ul>
+
+<h3><a href="qh-geom.htm#TOC">&#187;</a><a name="ground">geometric roundoff functions</a></h3>
+<ul>
+<li><a href="geom2.c#detjoggle">qh_detjoggle</a> determine
+default joggle for points and distance roundoff error</li>
+<li><a href="geom2.c#detroundoff">qh_detroundoff</a>
+determine maximum roundoff error and other precision constants</li>
+<li><a href="geom2.c#distround">qh_distround</a> compute
+maximum roundoff error due to a distance computation to a
+normalized hyperplane</li>
+<li><a href="geom2.c#divzero">qh_divzero</a> divide by a
+number that is nearly zero </li>
+<li><a href="geom2.c#maxouter">qh_maxouter</a> return maximum outer
+plane</li>
+<li><a href="geom2.c#outerinner">qh_outerinner</a> return actual
+outer and inner planes
+</ul>
+
+<p><!-- Navigation links --> </p>
+<hr>
+<p><b>Up:</b>
+<a href="http://www.qhull.org">Home page for
+Qhull</a> <br>
+<b>Up:</b> <a href="index.htm#TOC">Qhull manual: Table of Contents</a> <br>
+<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a>
+&#149; <a href="../../html/qh-quick.htm#options">Options</a>
+&#149; <a href="../../html/qh-opto.htm#output">Output</a>
+&#149; <a href="../../html/qh-optf.htm#format">Formats</a>
+&#149; <a href="../../html/qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="../../html/qh-optp.htm#print">Print</a>
+&#149; <a href="../../html/qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="../../html/qh-optc.htm#prec">Precision</a>
+&#149; <a href="../../html/qh-optt.htm#trace">Trace</a>
+&#149; <a href="index.htm">Functions</a><br>
+<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a> <br>
+<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br>
+<b>To:</b> <a href="qh-geom.htm">Geom</a> &#149;
+<a href="qh-globa.htm">Global</a> &#149; <a href="qh-io.htm">Io</a>
+&#149; <a href="qh-mem.htm">Mem</a> &#149; <a href="qh-merge.htm">Merge</a>
+&#149; <a href="qh-poly.htm">Poly</a> &#149; <a href="qh-qhull.htm#TOC">Qhull</a>
+&#149; <a href="qh-set.htm">Set</a> &#149; <a href="qh-stat.htm">Stat</a>
+&#149; <a href="qh-user.htm">User</a><br>
+
+
+<p><!-- GC common information --> </p>
+<hr>
+<p><a href="http://www.geom.uiuc.edu/"><img
+src="../../html/qh--geom.gif" align="middle" width="40" height="40"></a><i>The
+Geometry Center Home Page </i></p>
+<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a>
+</a><br>
+Created: May 2, 1997 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p>
+</body>
+</html>
diff --git a/xs/src/qhull/src/libqhull/qh-globa.htm b/xs/src/qhull/src/libqhull/qh-globa.htm
new file mode 100644
index 000000000..c87508b66
--- /dev/null
+++ b/xs/src/qhull/src/libqhull/qh-globa.htm
@@ -0,0 +1,165 @@
+<!-- Do not edit with Front Page, it adds too many spaces -->
+<html>
+<head>
+<meta http-equiv="Content-Type"
+content="text/html; charset=iso-8859-1">
+<title>global.c -- global variables and their functions</title>
+</head>
+
+<body>
+<!-- Navigation links -->
+<p><a name="TOP"><b>Up:</b></a> <a
+href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual</a>: Table of Contents <br>
+<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a>
+&#149; <a href="../../html/qh-quick.htm#options">Options</a>
+&#149; <a href="../../html/qh-opto.htm#output">Output</a>
+&#149; <a href="../../html/qh-optf.htm#format">Formats</a>
+&#149; <a href="../../html/qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="../../html/qh-optp.htm#print">Print</a>
+&#149; <a href="../../html/qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="../../html/qh-optc.htm#prec">Precision</a>
+&#149; <a href="../../html/qh-optt.htm#trace">Trace</a>
+&#149; <a href="index.htm">Functions</a><br>
+<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a><br>
+<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br>
+<b>To:</b> <a href="qh-geom.htm">Geom</a> &#149; <a href="qh-globa.htm#TOC">Global</a>
+&#149; <a href="qh-io.htm">Io</a> &#149; <a href="qh-mem.htm">Mem</a>
+&#149; <a href="qh-merge.htm">Merge</a> &#149; <a href="qh-poly.htm">Poly</a>
+&#149; <a href="qh-qhull.htm">Qhull</a> &#149; <a href="qh-set.htm">Set</a>
+&#149; <a href="qh-stat.htm">Stat</a> &#149; <a href="qh-user.htm">User</a>
+</p>
+
+<hr>
+<!-- Main text of document. -->
+
+<h2>global.c -- global variables and their functions</h2>
+<blockquote>
+<p>Qhull uses a global data structure, <tt>qh</tt>, to store
+globally defined constants, lists, sets, and variables. This
+allows multiple instances of Qhull to execute at the same time.
+The structure may be statically allocated or
+dynamically allocated with malloc(). See
+<a href="user.h#QHpointer">QHpointer</a>.
+</p>
+</blockquote>
+<p><b>Copyright &copy; 1995-2015 C.B. Barber</b></p>
+<hr>
+<p><a href="#TOP">&#187;</a> <a href="qh-geom.htm#TOC">Geom</a>
+<a name="TOC">&#149;</a> <b>Global</b> &#149;
+<a href="qh-io.htm#TOC">Io</a> &#149; <a href="qh-mem.htm#TOC">Mem</a> &#149;
+<a href="qh-merge.htm#TOC">Merge</a> &#149; <a href="qh-poly.htm#TOC">Poly</a> &#149;
+<a href="qh-qhull.htm#TOC">Qhull</a> &#149; <a href="qh-set.htm#TOC">Set</a> &#149;
+<a href="qh-stat.htm#TOC">Stat</a> &#149; <a href="qh-user.htm#TOC">User</a> </p>
+
+<h3>Index to <a href="global.c">global.c</a> and
+<a href="libqhull.h">libqhull.h</a></h3>
+
+<ul>
+<li><a href="#ovar">Qhull's global variables</a> </li>
+<li><a href="#ofunc">Global variable and initialization
+routines</a> </li>
+</ul>
+
+<h3><a href="qh-globa.htm#TOC">&#187;</a><a name="ovar">Qhull's global
+variables</a></h3>
+
+<ul>
+<li><a href=global.c#qh_version>qh_version</a> version string
+<li><a href="libqhull.h#qh">qh</a> all global variables for
+qhull are in <tt>qh,qhmem</tt>, and <tt>qhstat</tt></li>
+<li><a href="libqhull_r.h#qh">QHULL_LIB_CHECK</a> Check for compatible library</li>
+<li><a href="libqhull.h#qh-const">qh constants</a> configuration
+flags and constants for Qhull </li>
+<li><a href="libqhull.h#qh-prec">qh precision constants</a>
+precision constants for Qhull </li>
+<li><a href="libqhull.h#qh-codetern">qh internal constants</a>
+internal constants for Qhull </li>
+<li><a href="libqhull.h#qh-lists">qh facet and vertex lists</a>
+lists of facets and vertices </li>
+<li><a href="libqhull.h#qh-var">qh global variables</a> minimum
+and maximum distances, next visit ids, several flags, and
+other global variables. </li>
+<li><a href="libqhull.h#qh-set">qh global sets</a> global sets
+for merging, hashing, input, etc. </li>
+<li><a href="libqhull.h#qh-buf">qh global buffers</a> buffers
+for matrix operations and input </li>
+<li><a href="libqhull.h#qh-static">qh static variables</a>
+static variables for individual functions </li>
+</ul>
+
+<h3><a href="qh-globa.htm#TOC">&#187;</a><a name="ofunc">Global variable and
+initialization routines</a></h3>
+
+<ul>
+<li><a href="global.c#appendprint">qh_appendprint</a> append
+output format to <tt>qh.PRINTout</tt> </li>
+<li><a href="global.c#freebuffers">qh_freebuffers</a> free
+global memory buffers </li>
+<li><a href="global.c#freeqhull">qh_freeqhull</a> free memory
+used by qhull </li>
+<li><a href="global.c#init_A">qh_init_A</a> called before
+error handling initialized </li>
+<li><a href="global.c#init_B">qh_init_B</a> called after
+points are defined </li>
+<li><a href="global.c#init_qhull_command">qh_init_qhull_command</a>
+build <tt>qh.qhull_command</tt> from <tt>argc/argv</tt></li>
+<li><a href="global.c#initflags">qh_initflags</a> set flags
+and constants from command line </li>
+<li><a href="global.c#initqhull_buffers">qh_initqhull_buffers</a>
+initialize global memory buffers </li>
+<li><a href="global.c#initqhull_globals">qh_initqhull_globals</a>
+initialize global variables </li>
+<li><a href="global.c#initqhull_mem">qh_initqhull_mem</a>
+initialize Qhull memory management </li>
+<li><a href="global.c#initqhull_start">qh_initqhull_start</a>
+allocate qh_qh and call qh_initqhull_start2()
+<li><a href="global.c#initqhull_start2">qh_initqhull_start2</a>
+initialize default values at Qhull startup </li>
+<li><a href="global.c#initthresholds">qh_initthresholds</a>
+initialize 'Pdn' and 'PDn' thresholds </li>
+<li><a href="global.c#lib_check">qh_lib_check</a> check for compatible Qhull library. Invoked by QHULL_LIB_CHECK at start of each program.</li>
+<li><a href="global.c#option">qh_option</a> append option
+description to <tt>qh.global_options</tt> </li>
+<li><a href="global.c#restore_qhull">qh_restore_qhull</a>
+restores a previously saved qhull </li>
+<li><a href="global.c#save_qhull">qh_save_qhull</a> saves
+qhull for a later qh_restore_qhull() </li>
+<li><a href="global.c#strtol">qh_strtol</a> duplicates
+strtod() and strtol() </li>
+</ul>
+
+<p><!-- Navigation links --> </p>
+<hr>
+<p><b>Up:</b>
+<a href="http://www.qhull.org">Home page for
+Qhull</a> <br>
+<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual: Table of Contents</a> <br>
+<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a>
+&#149; <a href="../../html/qh-quick.htm#options">Options</a>
+&#149; <a href="../../html/qh-opto.htm#output">Output</a>
+&#149; <a href="../../html/qh-optf.htm#format">Formats</a>
+&#149; <a href="../../html/qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="../../html/qh-optp.htm#print">Print</a>
+&#149; <a href="../../html/qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="../../html/qh-optc.htm#prec">Precision</a>
+&#149; <a href="../../html/qh-optt.htm#trace">Trace</a>
+&#149; <a href="index.htm">Functions</a><br>
+<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a> <br>
+<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br>
+<b>To:</b> <a href="qh-geom.htm">Geom</a> &#149;
+<a href="qh-globa.htm">Global</a> &#149; <a href="qh-io.htm">Io</a>
+&#149; <a href="qh-mem.htm">Mem</a> &#149; <a href="qh-merge.htm">Merge</a>
+&#149; <a href="qh-poly.htm">Poly</a> &#149; <a href="qh-qhull.htm#TOC">Qhull</a>
+&#149; <a href="qh-set.htm">Set</a> &#149; <a href="qh-stat.htm">Stat</a>
+&#149; <a href="qh-user.htm">User</a><br>
+<p><!-- GC common information --> </p>
+<hr>
+<p><a href="http://www.geom.uiuc.edu/"><img
+src="../../html/qh--geom.gif" align="middle" width="40" height="40"></a><i>The
+Geometry Center Home Page </i></p>
+<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a>
+</a><br>
+Created: May 2, 1997 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p>
+</body>
+</html>
diff --git a/xs/src/qhull/src/libqhull/qh-io.htm b/xs/src/qhull/src/libqhull/qh-io.htm
new file mode 100644
index 000000000..5cb591d87
--- /dev/null
+++ b/xs/src/qhull/src/libqhull/qh-io.htm
@@ -0,0 +1,305 @@
+<!-- Do not edit with Front Page, it adds too many spaces -->
+<html>
+<head>
+<meta http-equiv="Content-Type"
+content="text/html; charset=iso-8859-1">
+<title>io.c -- input and output operations</title>
+</head>
+
+<body>
+<!-- Navigation links -->
+<p><a name="TOP"><b>Up:</b></a> <a
+href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual</a>: Table of Contents <br>
+<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a>
+&#149; <a href="../../html/qh-quick.htm#options">Options</a>
+&#149; <a href="../../html/qh-opto.htm#output">Output</a>
+&#149; <a href="../../html/qh-optf.htm#format">Formats</a>
+&#149; <a href="../../html/qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="../../html/qh-optp.htm#print">Print</a>
+&#149; <a href="../../html/qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="../../html/qh-optc.htm#prec">Precision</a>
+&#149; <a href="../../html/qh-optt.htm#trace">Trace</a>
+&#149; <a href="index.htm">Functions</a><br>
+<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a><br>
+<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br>
+<b>To:</b> <a href="qh-geom.htm">Geom</a> &#149; <a href="qh-globa.htm">Global</a>
+&#149; <a href="qh-io.htm#TOC">Io</a> &#149; <a href="qh-mem.htm">Mem</a>
+&#149; <a href="qh-merge.htm">Merge</a> &#149; <a href="qh-poly.htm">Poly</a>
+&#149; <a href="qh-qhull.htm">Qhull</a> &#149; <a href="qh-set.htm">Set</a>
+&#149; <a href="qh-stat.htm">Stat</a> &#149; <a href="qh-user.htm">User</a>
+</p>
+<hr>
+
+<h2>io.c -- input and output operations</h2>
+<blockquote>
+
+<p>Qhull provides a wide range of input
+and output options. To organize the code, most output formats use
+the same driver: </p>
+
+<pre>
+ qh_printbegin( fp, format, facetlist, facets, printall );
+
+ FORALLfacet_( facetlist )
+ qh_printafacet( fp, format, facet, printall );
+
+ FOREACHfacet_( facets )
+ qh_printafacet( fp, format, facet, printall );
+
+ qh_printend( fp, format );
+</pre>
+
+<p>Note the 'printall' flag. It selects whether or not
+qh_skipfacet() is tested. </p>
+
+</blockquote>
+<p><b>Copyright &copy; 1995-2015 C.B. Barber</b></p>
+<hr>
+<p><a href="#TOP">&#187;</a> <a href="qh-geom.htm#TOC">Geom</a> <a name="TOC">&#149;</a>
+<a href="qh-globa.htm#TOC">Global</a> &#149; <b>Io</b> &#149;
+<a href="qh-mem.htm#TOC">Mem</a> &#149; <a href="qh-merge.htm#TOC">Merge</a> &#149;
+<a href="qh-poly.htm#TOC">Poly</a> &#149; <a href="qh-qhull.htm#TOC">Qhull</a> &#149;
+<a href="qh-set.htm#TOC">Set</a> &#149; <a href="qh-stat.htm#TOC">Stat</a> &#149;
+<a href="qh-user.htm#TOC">User</a> </p>
+
+<h3>Index to <a href="io.c">io.c</a> and <a href="io.h">io.h</a></h3>
+
+<ul>
+<li><a href="#iconst">io.h constants and types</a> </li>
+<li><a href="#ilevel">User level functions</a> </li>
+<li><a href="#iprint">Print functions for all output formats</a></li>
+<li><a href="#itext">Text output functions</a> </li>
+<li><a href="#iutil">Text utility functions</a></li>
+<li><a href="#igeom">Geomview output functions</a> </li>
+<li><a href="#iview">Geomview utility functions</a></li>
+</ul>
+
+<h3><a href="qh-io.htm#TOC">&#187;</a><a name="iconst">io.h constants and types</a></h3>
+
+<ul>
+<li><a href="io.h#qh_MAXfirst">qh_MAXfirst</a> maximum length
+of first two lines of stdin </li>
+<li><a href="io.h#qh_WHITESPACE">qh_WHITESPACE</a> possible
+values of white space </li>
+<li><a href="io.h#printvridgeT">printvridgeT</a> function to
+print results of qh_printvdiagram or qh_eachvoronoi</li>
+</ul>
+
+<h3><a href="qh-io.htm#TOC">&#187;</a><a name="ilevel">User level functions</a></h3>
+
+<ul>
+<li><a href="io.c#copyfilename">qh_copyfilename</a>
+copy filename identified by qh_skipfilename
+<li><a href="io.c#eachvoronoi_all">qh_eachvoronoi_all</a>
+visit each Voronoi ridge of the Voronoi diagram
+<li><a href="io.c#prepare_output">qh_prepare_output</a>
+prepare Qhull for output (called by qh_produce_output())
+<li><a href="io.c#printhelp_degenerate">qh_printhelp_degenerate</a>
+prints descriptive message for precision error </li>
+<li><a href="io.c#printhelp_singular">qh_printhelp_singular</a>
+print help message for singular data </li>
+<li><a href="libqhull.c#printsummary">qh_printsummary</a> print
+summary ('s')</li>
+<li><a href="io.c#produce_output">qh_produce_output</a>
+prints out the result of qhull()</li>
+<li><a href="io.c#produce_output">qh_produce_output2</a>
+prints out the result of qhull() without calling qh_prepare_output()</li>
+<li><a href="io.c#readfeasible">qh_readfeasible</a> read
+interior point from remainder and qh fin ('H')</li>
+<li><a href="io.c#readpoints">qh_readpoints</a> read input
+points </li>
+<li><a href="io.c#setfeasible">qh_setfeasible</a> set
+interior point from qh feasible_string ('Hn,n,n')</li>
+<li><a href="io.c#skipfilename">qh_skipfilename</a>
+skip filename in string
+</ul>
+
+<h3><a href="qh-io.htm#TOC">&#187;</a><a name="iprint">Print functions for all
+output formats</a></h3>
+
+<ul>
+<li><a href="io.c#countfacets">qh_countfacets</a> count good
+facets for printing and set visitid </li>
+<li><a href="io.c#markkeep">qh_markkeep</a> mark good facets
+that meet qh.KEEParea ('PAn'), qh.KEEPmerge ('PMn'), and qh.KEEPminArea ('PFn')</li>
+<li><a href="io.c#order_vertexneighbors">qh_order_vertexneighbors</a>
+order neighbors for a 3-d vertex by adjacency ('i', 'o')</li>
+<li><a href="io.c#printafacet">qh_printafacet</a> print facet
+in an output format </li>
+<li><a href="io.c#printbegin">qh_printbegin</a> print header
+for an output format </li>
+<li><a href="io.c#printend">qh_printend</a> print trailer for
+an output format </li>
+<li><a href="user.c#printfacetlist">qh_printfacetlist</a>
+print facets in a facetlist</li>
+<li><a href="io.c#printfacets">qh_printfacets</a> print
+facetlist and/or facet set in an output format </li>
+<li><a href="io.c#printneighborhood">qh_printneighborhood</a>
+print neighborhood of one or two facets ('Po')</li>
+<li><a href="io.c#produce_output">qh_produce_output</a>
+print the results of qh_qhull() </li>
+<li><a href="io.c#skipfacet">qh_skipfacet</a> True if not
+printing this facet ('Pdk:n', 'QVn', 'QGn')</li>
+<li><a href="io.c#facetvertices">qh_facetvertices</a> return
+vertices in a set of facets ('p')</li>
+</ul>
+
+<h3><a href="qh-io.htm#TOC">&#187;</a><a name="itext">Text output functions</a></h3>
+<ul>
+<li><a href="io.c#eachvoronoi">qh_eachvoronoi</a>
+print or visit each Voronoi ridge for an input site of the Voronoi diagram
+<li><a href="io.c#printextremes">qh_printextremes</a> print
+extreme points by point ID (vertices of convex hull) ('Fx')</li>
+<li><a href="io.c#printextremes_2d">qh_printextremes_2d</a> print
+2-d extreme points by point ID ('Fx')</li>
+<li><a href="io.c#printextremes_d">qh_printextremes_d</a> print
+extreme points of input sites for Delaunay triangulations ('Fx')</li>
+<li><a href="io.c#printfacet">qh_printfacet</a> print all
+fields of a facet ('f')</li>
+<li><a href="io.c#printfacet2math">qh_printfacet2math</a> print
+2-d Maple or Mathematica output for a facet ('FM' or 'm')</li>
+<li><a href="io.c#printfacet3math">qh_printfacet3math</a>
+print 3-d Maple or Mathematica facet ('FM' or 'm')</li>
+<li><a href="io.c#printfacet3vertex">qh_printfacet3vertex</a>
+print vertices for a 3-d facet ('i', 'o')</li>
+<li><a href="io.c#printfacetheader">qh_printfacetheader</a>
+prints header fields of a facet ('f')</li>
+<li><a href="io.c#printfacetNvertex_nonsimplicial">qh_printfacetNvertex_nonsimplicial</a>
+print vertices for an N-d non-simplicial facet ('i', 'Ft')</li>
+<li><a href="io.c#printfacetNvertex_simplicial">qh_printfacetNvertex_simplicial</a>
+print vertices for an N-d simplicial facet ('i', 'o', 'Ft')</li>
+<li><a href="io.c#printfacetridges">qh_printfacetridges</a>
+prints ridges of a facet ('f')</li>
+<li><a href="io.c#printpoints_out">qh_printpoints_out</a> prints
+vertices for facets by their point coordinates ('p')</li>
+<li><a href="io.c#printridge">qh_printridge</a> print all
+fields for a ridge ('f')</li>
+<li><a href="io.c#printvdiagram">qh_printvdiagram</a> print
+voronoi diagram as Voronoi vertices for each input pair</li>
+<li><a href="io.c#printvertex">qh_printvertex</a> print all
+fields for a vertex ('f')</li>
+<li><a href="io.c#printvertexlist">qh_printvertexlist</a>
+print vertices used by a list or set of facets ('f')</li>
+<li><a href="io.c#printvertices">qh_printvertices</a> print a
+set of vertices ('f')</li>
+<li><a href="io.c#printvneighbors">qh_printvneighbors</a>
+print vertex neighbors of vertices ('FN')</li>
+<li><a href="io.c#printvoronoi">qh_printvoronoi</a> print
+voronoi diagram in 'o' or 'G' format</li>
+</ul>
+
+<h3><a href="qh-io.htm#TOC">&#187;</a><a name="iutil">Text utility functions</a></h3>
+<ul>
+<li><a href="io.c#dfacet">dfacet</a> print facet by ID </li>
+<li><a href="io.c#dvertex">dvertex</a> print vertex by ID </li>
+<li><a href="io.c#compare_facetarea">qh_compare_facetarea</a>
+used by qsort() to order facets by area </li>
+<li><a href="io.c#compare_facetmerge">qh_compare_facetmerge</a>
+used by qsort() to order facets by number of merges </li>
+<li><a href="io.c#compare_facetvisit">qh_compare_facetvisit</a>
+used by qsort() to order facets by visit ID or ID </li>
+<li><a href="io.c#compare_vertexpoint">qh_compare_vertexpoint</a>
+used by qsort() to order vertices by point ID </li>
+<li><a href="io.c#detvnorm">qh_detvnorm</a> determine normal for Voronoi ridge </li>
+<li><a href="io.c#detvridge">qh_detvridge</a> determine Voronoi
+ridge for an input site
+<li><a href="io.c#detvridge3">qh_detvridge3</a> determine 3-d Voronoi
+ridge for an input site
+<li><a href="io.c#facet2point">qh_facet2point</a> return two
+projected temporary vertices for a 2-d facet ('m', 'G')</li>
+<li><a href="io.c#markvoronoi">qh_markvoronoi</a> mark Voronoi
+vertices for printing
+<li><a href="io.c#printcenter">qh_printcenter</a> print
+facet-&gt;center as centrum or Voronoi center ('Ft', 'v p', 'FC', 'f') </li>
+<li><a href="io.c#printpoint">qh_printpoint</a>, qh_printpointid, print
+coordinates of a point ('p', 'o', 'Fp', 'G', 'f')</li>
+<li><a href="io.c#printpoint3">qh_printpoint3</a> prints 2-d,
+3-d, or 4-d point as 3-d coordinates ('G')</li>
+<li><a href="io.c#printvdiagram2">qh_printvdiagram2</a> print
+voronoi diagram for each ridge of each vertex from qh_markvoronoi</li>
+<li><a href="io.c#printvnorm">qh_printvnorm</a> print
+separating plane of the Voronoi diagram for a pair of input sites</li>
+<li><a href="io.c#printvridge">qh_printvridge</a> print
+ridge of the Voronoi diagram for a pair of input sites</li>
+<li><a href="io.c#projectdim3">qh_projectdim3</a> project 2-d
+3-d or 4-d point to a 3-d point ('G')</li>
+</ul>
+
+<h3><a href="qh-io.htm#TOC">&#187;</a><a name="igeom">Geomview output functions</a></h3>
+<ul>
+<li><a href="io.c#printfacet2geom">qh_printfacet2geom</a>
+print facet as a 2-d VECT object </li>
+<li><a href="io.c#printfacet2geom_points">qh_printfacet2geom_points</a>
+print points as a 2-d VECT object with offset </li>
+<li><a href="io.c#printfacet3geom_nonsimplicial">qh_printfacet3geom_nonsimplicial</a>
+print Geomview OFF for a 3-d nonsimplicial facet. </li>
+<li><a href="io.c#printfacet3geom_points">qh_printfacet3geom_points</a>
+prints a 3-d facet as OFF Geomview object. </li>
+<li><a href="io.c#printfacet3geom_simplicial">qh_printfacet3geom_simplicial</a>
+print Geomview OFF for a 3-d simplicial facet. </li>
+<li><a href="io.c#printfacet4geom_nonsimplicial">qh_printfacet4geom_nonsimplicial</a>
+print Geomview 4OFF file for a 4d nonsimplicial facet </li>
+<li><a href="io.c#printfacet4geom_simplicial">qh_printfacet4geom_simplicial</a>
+print Geomview 4OFF file for a 4d simplicial facet </li>
+<li><a href="io.c#printhyperplaneintersection">qh_printhyperplaneintersection</a>
+print hyperplane intersection as OFF or 4OFF </li>
+<li><a href="io.c#printvoronoi">qh_printvoronoi</a> print
+voronoi diagram in 'o' or 'G' format</li>
+</ul>
+<h3><a href="qh-io.htm#TOC">&#187;</a><a name="iview">Geomview utility functions</a></h3>
+<ul>
+<li><a href="io.c#geomplanes">qh_geomplanes</a>
+ return outer and inner planes for Geomview</li>
+<li><a href="io.c#printcentrum">qh_printcentrum</a> print
+centrum for a facet in OOGL format </li>
+<li><a href="io.c#printend4geom">qh_printend4geom</a> helper
+function for qh_printbegin/printend </li>
+<li><a href="io.c#printhyperplaneintersection">qh_printhyperplaneintersection</a>
+print Geomview OFF or 4OFF for the intersection of two
+hyperplanes in 3-d or 4-d </li>
+<li><a href="io.c#printline3geom">qh_printline3geom</a> prints a
+line as a VECT </li>
+<li><a href="io.c#printpointvect">qh_printpointvect</a>
+prints a 2-d or 3-d point as 3-d VECT's </li>
+<li><a href="io.c#printpointvect2">qh_printpointvect2</a>
+prints a 2-d or 3-d point as 2 3-d VECT's </li>
+<li><a href="io.c#printspheres">qh_printspheres</a> prints 3-d
+vertices as OFF spheres </li>
+</ul>
+<p>
+<p><!-- Navigation links --> </p>
+<hr>
+<p><b>Up:</b>
+<a href="http://www.qhull.org">Home page for
+Qhull</a> <br>
+<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual: Table of Contents</a> <br>
+<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a>
+&#149; <a href="../../html/qh-quick.htm#options">Options</a>
+&#149; <a href="../../html/qh-opto.htm#output">Output</a>
+&#149; <a href="../../html/qh-optf.htm#format">Formats</a>
+&#149; <a href="../../html/qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="../../html/qh-optp.htm#print">Print</a>
+&#149; <a href="../../html/qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="../../html/qh-optc.htm#prec">Precision</a>
+&#149; <a href="../../html/qh-optt.htm#trace">Trace</a>
+&#149; <a href="index.htm">Functions</a><br>
+<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a> <br>
+<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br>
+<b>To:</b> <a href="qh-geom.htm">Geom</a> &#149;
+<a href="qh-globa.htm">Global</a> &#149; <a href="qh-io.htm">Io</a>
+&#149; <a href="qh-mem.htm">Mem</a> &#149; <a href="qh-merge.htm">Merge</a>
+&#149; <a href="qh-poly.htm">Poly</a> &#149; <a href="qh-qhull.htm#TOC">Qhull</a>
+&#149; <a href="qh-set.htm">Set</a> &#149; <a href="qh-stat.htm">Stat</a>
+&#149; <a href="qh-user.htm">User</a><br>
+</p>
+<p><!-- GC common information --> </p>
+<hr>
+<p><a href="http://www.geom.uiuc.edu/"><img
+src="../../html/qh--geom.gif" align="middle" width="40" height="40"></a><i>The
+Geometry Center Home Page </i></p>
+<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a>
+</a><br>
+Created: May 2, 1997 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p>
+</body>
+</html>
diff --git a/xs/src/qhull/src/libqhull/qh-mem.htm b/xs/src/qhull/src/libqhull/qh-mem.htm
new file mode 100644
index 000000000..b993b2229
--- /dev/null
+++ b/xs/src/qhull/src/libqhull/qh-mem.htm
@@ -0,0 +1,145 @@
+<!-- Do not edit with Front Page, it adds too many spaces -->
+<html>
+<head>
+<meta http-equiv="Content-Type"
+content="text/html; charset=iso-8859-1">
+<title>mem.c -- memory operations</title>
+</head>
+
+<body>
+<!-- Navigation links -->
+<p><a name="TOP"><b>Up:</b></a> <a
+href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual</a>: Table of Contents <br>
+<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a>
+&#149; <a href="../../html/qh-quick.htm#options">Options</a>
+&#149; <a href="../../html/qh-opto.htm#output">Output</a>
+&#149; <a href="../../html/qh-optf.htm#format">Formats</a>
+&#149; <a href="../../html/qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="../../html/qh-optp.htm#print">Print</a>
+&#149; <a href="../../html/qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="../../html/qh-optc.htm#prec">Precision</a>
+&#149; <a href="../../html/qh-optt.htm#trace">Trace</a>
+&#149; <a href="index.htm">Functions</a><br>
+<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a><br>
+<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br>
+<b>To:</b> <a href="qh-geom.htm">Geom</a> &#149; <a href="qh-globa.htm">Global</a>
+&#149; <a href="qh-io.htm">Io</a> &#149; <a href="qh-mem.htm#TOC">Mem</a>
+&#149; <a href="qh-merge.htm">Merge</a> &#149; <a href="qh-poly.htm">Poly</a>
+&#149; <a href="qh-qhull.htm">Qhull</a> &#149; <a href="qh-set.htm">Set</a>
+&#149; <a href="qh-stat.htm">Stat</a> &#149; <a href="qh-user.htm">User</a>
+</p>
+<hr>
+
+<h2>mem.c -- memory operations</h2>
+<blockquote>
+<p>Qhull uses quick-fit memory allocation. It maintains a
+set of free lists for a variety of small allocations. A
+small request returns a block from the best fitting free
+list. If the free list is empty, Qhull allocates a block
+from a reserved buffer. </p>
+<p>Use 'T5' to trace memory allocations.</p>
+
+</blockquote>
+<p><b>Copyright &copy; 1995-2015 C.B. Barber</b></p>
+<hr>
+<p><a href="#TOP">&#187;</a> <a href="qh-geom.htm#TOC">Geom</a>
+<a name="TOC">&#149;</a> <a href="qh-globa.htm#TOC">Global</a> &#149;
+<a href="qh-io.htm#TOC">Io</a> &#149; <b>Mem</b>
+&#149; <a href="qh-merge.htm#TOC">Merge</a> &#149; <a href="qh-poly.htm#TOC">Poly</a>
+&#149; <a href="qh-qhull.htm#TOC">Qhull</a> &#149; <a href="qh-set.htm#TOC">Set</a>
+&#149; <a href="qh-stat.htm#TOC">Stat</a> &#149; <a href="qh-user.htm#TOC">User</a>
+</p>
+<h3>Index to <a href="mem.c">mem.c</a> and
+<a href="mem.h">mem.h</a></h3>
+<ul>
+<li><a href="#etype">mem.h data types</a> </li>
+<li><a href="#emacro">mem.h macros</a> </li>
+<li><a href="#efunc">User level functions</a> </li>
+</ul>
+<h3><a href="qh-mem.htm#TOC">&#187;</a><a name="etype">mem.h data types and constants</a></h3>
+<ul>
+<li><a href="mem.h#ptr_intT">ptr_intT</a> for casting
+a void* to an integer-type </li>
+<li><a href="mem.h#qhmemT">qhmemT</a> global memory
+structure for mem.c </li>
+<li><a href="mem.h#NOmem">qh_NOmem</a> disable memory allocation</li>
+</ul>
+<h3><a href="qh-mem.htm#TOC">&#187;</a><a name="emacro">mem.h macros</a></h3>
+<ul>
+<li><a href="mem.h#memalloc_">qh_memalloc_</a>
+allocate memory</li>
+<li><a href="mem.h#memfree_">qh_memfree_</a> free
+memory</li>
+</ul>
+<h3><a href="qh-mem.htm#TOC">&#187;</a><a name="efunc">User level
+functions</a></h3>
+<ul>
+<li><a href="mem.c#memalloc">qh_memalloc</a> allocate
+memory </li>
+<li><a href="mem.c#memcheck">qh_memcheck</a>
+quick check of memory for internal consistency</li>
+<li><a href="mem.c#memfree">qh_memfree</a> free
+memory </li>
+<li><a href="mem.c#meminit">qh_meminit</a> initialize
+memory </li>
+<li><a href="mem.c#memstatistics">qh_memstatistics</a>
+print memory statistics </li>
+<li><a href="mem.c#meminit">qh_memtotlong</a> return total, allocated long memory</li>
+<li><a href="mem.c#NOmem">qh_NOmem</a> allocation routines with malloc() and free()
+</ul>
+
+<h3><a href="qh-mem.htm#TOC">&#187;</a><a name="m">Initialization and
+termination functions</a></h3>
+<ul>
+<li><a href="mem.c#intcompare">qh_intcompare</a> used by
+qsort and bsearch to compare two integers </li>
+<li><a href="mem.c#memfreeshort">qh_memfreeshort</a>
+frees up all short and qhmem memory allocations </li>
+<li><a href="mem.c#meminit">qh_meminit</a> initialize
+memory </li>
+<li><a href="mem.c#meminitbuffers">qh_meminitbuffers</a>
+initialize qhmem </li>
+<li><a href="mem.c#memsetup">qh_memsetup</a> set up
+memory after running memsize() </li>
+<li><a href="mem.c#memsize">qh_memsize</a> define a free
+list for this size </li>
+<li><a href="mem.c#memstatistics">qh_memstatistics</a>
+print out memory statistics </li>
+</ul>
+
+<p><!-- Navigation links --> </p>
+<hr>
+<p><b>Up:</b>
+<a href="http://www.qhull.org">Home page for
+Qhull</a> <br>
+<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual: Table of Contents</a> <br>
+<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a>
+&#149; <a href="../../html/qh-quick.htm#options">Options</a>
+&#149; <a href="../../html/qh-opto.htm#output">Output</a>
+&#149; <a href="../../html/qh-optf.htm#format">Formats</a>
+&#149; <a href="../../html/qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="../../html/qh-optp.htm#print">Print</a>
+&#149; <a href="../../html/qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="../../html/qh-optc.htm#prec">Precision</a>
+&#149; <a href="../../html/qh-optt.htm#trace">Trace</a>
+&#149; <a href="index.htm">Functions</a><br>
+<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a> <br>
+<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br>
+<b>To:</b> <a href="qh-geom.htm">Geom</a> &#149;
+<a href="qh-globa.htm">Global</a> &#149; <a href="qh-io.htm">Io</a>
+&#149; <a href="qh-mem.htm">Mem</a> &#149; <a href="qh-merge.htm">Merge</a>
+&#149; <a href="qh-poly.htm">Poly</a> &#149; <a href="qh-qhull.htm#TOC">Qhull</a>
+&#149; <a href="qh-set.htm">Set</a> &#149; <a href="qh-stat.htm">Stat</a>
+&#149; <a href="qh-user.htm">User</a><br>
+</p>
+<p><!-- GC common information --> </p>
+<hr>
+<p><a href="http://www.geom.uiuc.edu/"><img
+src="../../html/qh--geom.gif" align="middle" width="40" height="40"></a><i>The
+Geometry Center Home Page </i></p>
+<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a>
+</a><br>
+Created: May 2, 1997 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p>
+</body>
+</html>
diff --git a/xs/src/qhull/src/libqhull/qh-merge.htm b/xs/src/qhull/src/libqhull/qh-merge.htm
new file mode 100644
index 000000000..54b97c88e
--- /dev/null
+++ b/xs/src/qhull/src/libqhull/qh-merge.htm
@@ -0,0 +1,366 @@
+<!-- Do not edit with Front Page, it adds too many spaces -->
+<html>
+<head>
+<meta http-equiv="Content-Type"
+content="text/html; charset=iso-8859-1">
+<title>merge.c -- facet merge operations</title>
+</head>
+
+<body>
+<!-- Navigation links -->
+<p><a name="TOP"><b>Up:</b></a> <a
+href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual</a>: Table of Contents <br>
+<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a>
+&#149; <a href="../../html/qh-quick.htm#options">Options</a>
+&#149; <a href="../../html/qh-opto.htm#output">Output</a>
+&#149; <a href="../../html/qh-optf.htm#format">Formats</a>
+&#149; <a href="../../html/qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="../../html/qh-optp.htm#print">Print</a>
+&#149; <a href="../../html/qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="../../html/qh-optc.htm#prec">Precision</a>
+&#149; <a href="../../html/qh-optt.htm#trace">Trace</a>
+&#149; <a href="index.htm">Functions</a><br>
+<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a><br>
+<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br>
+<b>To:</b> <a href="qh-geom.htm">Geom</a> &#149; <a href="qh-globa.htm">Global</a>
+&#149; <a href="qh-io.htm">Io</a> &#149; <a href="qh-mem.htm">Mem</a>
+&#149; <a href="qh-merge.htm#TOC">Merge</a> &#149; <a href="qh-poly.htm">Poly</a>
+&#149; <a href="qh-qhull.htm">Qhull</a> &#149; <a href="qh-set.htm">Set</a>
+&#149; <a href="qh-stat.htm">Stat</a> &#149; <a href="qh-user.htm">User</a>
+</p>
+<hr>
+
+<h2>merge.c -- facet merge operations</h2>
+<blockquote>
+<p>Qhull handles precision problems by merged facets or joggled input.
+Except for redundant vertices, it corrects a problem by
+merging two facets. When done, all facets are clearly
+convex. See <a href="../../html/qh-impre.htm">Imprecision in Qhull</a>
+for further information. </p>
+<p>Users may joggle the input ('<a href="../../html/qh-optq.htm#QJn">QJn</a>')
+instead of merging facets. </p>
+<p>Qhull detects and corrects the following problems: </p>
+<ul>
+<li><b>More than two facets meeting at a ridge. </b>When
+Qhull creates facets, it creates an even number
+of facets for each ridge. A convex hull always
+has two facets for each ridge. More than two
+facets may be created if non-adjacent facets
+share a vertex. This is called a <em>duplicate
+ridge</em>. In 2-d, a duplicate ridge would
+create a loop of facets. </li>
+</ul>
+<ul>
+<li><b>A facet contained in another facet. </b>Facet
+merging may leave all vertices of one facet as a
+subset of the vertices of another facet. This is
+called a <em>redundant facet</em>. </li>
+</ul>
+<ul>
+<li><b>A facet with fewer than three neighbors. </b>Facet
+merging may leave a facet with one or two
+neighbors. This is called a <em>degenerate facet</em>.
+</li>
+</ul>
+<ul>
+<li><b>A facet with flipped orientation. </b>A
+facet's hyperplane may define a halfspace that
+does not include the interior point.This is
+called a <em>flipped facet</em>. </li>
+</ul>
+<ul>
+<li><strong>A coplanar horizon facet.</strong> A
+newly processed point may be coplanar with an
+horizon facet. Qhull creates a new facet without
+a hyperplane. It links new facets for the same
+horizon facet together. This is called a <em>samecycle</em>.
+The new facet or samecycle is merged into the
+horizon facet. </li>
+</ul>
+<ul>
+<li><b>Concave facets. </b>A facet's centrum may be
+above a neighboring facet. If so, the facets meet
+at a concave angle. </li>
+</ul>
+<ul>
+<li><b>Coplanar facets. </b>A facet's centrum may be
+coplanar with a neighboring facet (i.e., it is
+neither clearly below nor clearly above the
+facet's hyperplane). Qhull removes coplanar
+facets in independent sets sorted by angle.</li>
+</ul>
+<ul>
+<li><b>Redundant vertex. </b>A vertex may have fewer
+than three neighboring facets. If so, it is
+redundant and may be renamed to an adjacent
+vertex without changing the topological
+structure.This is called a <em>redundant vertex</em>.
+</li>
+</ul>
+</blockquote>
+<p><b>Copyright &copy; 1995-2015 C.B. Barber</b></p>
+<hr>
+<p><a href="#TOP">&#187;</a> <a href="qh-geom.htm#TOC">Geom</a>
+<a name="TOC">&#149;</a> <a href="qh-globa.htm#TOC">Global</a>
+&#149; <a href="qh-io.htm#TOC">Io</a> &#149; <a href="qh-mem.htm#TOC">Mem</a>
+&#149; <b>Merge</b> &#149; <a href="qh-poly.htm#TOC">Poly</a>
+&#149; <a href="qh-qhull.htm#TOC">Qhull</a> &#149; <a href="qh-set.htm#TOC">Set</a>
+&#149; <a href="qh-stat.htm#TOC">Stat</a> &#149; <a href="qh-user.htm#TOC">User</a>
+</p>
+<h3>Index to <a href="merge.c">merge.c</a> and
+<a href="merge.h">merge.h</a></h3>
+<ul>
+<li><a href="#mtype">merge.h data types, macros, and
+global sets</a> </li>
+<li><a href="#mconst">merge.h constants</a> </li>
+</ul>
+<ul>
+<li><a href="#mtop">top-level merge functions</a> </li>
+<li><a href="#mset">functions for identifying merges</a></li>
+<li><a href="#mbest">functions for determining the
+best merge</a> </li>
+<li><a href="#mmerge">functions for merging facets</a>
+</li>
+<li><a href="#mcycle">functions for merging a cycle
+of facets</a> </li>
+<li><a href="#mrename">functions for renaming a
+vertex</a> </li>
+<li><a href="#mvertex">functions for identifying
+vertices for renaming</a> </li>
+<li><a href="#mcheck">functions for check and trace</a> </li>
+</ul>
+<h3><a href="qh-merge.htm#TOC">&#187;</a><a name="mtype">merge.h data
+types, macros, and global sets</a></h3>
+<ul>
+<li><a href="merge.h#mergeT">mergeT</a> structure to
+identify a merge of two facets</li>
+<li><a href="merge.h#FOREACHmerge_">FOREACHmerge_</a>
+assign 'merge' to each merge in merges </li>
+<li><a href="libqhull.h#qh-set">qh global sets</a>
+qh.facet_mergeset contains non-convex merges
+while qh.degen_mergeset contains degenerate and
+redundant facets</li>
+</ul>
+<h3><a href="qh-merge.htm#TOC">&#187;</a><a name="mconst">merge.h
+constants</a></h3>
+<ul>
+<li><a href="libqhull.h#qh-prec">qh precision constants</a>
+precision constants for Qhull </li>
+<li><a href="merge.h#MRG">MRG...</a> indicates the
+type of a merge (mergeT-&gt;type)</li>
+<li><a href="merge.h#qh_ANGLEredundant">qh_ANGLEredundant</a>
+indicates redundant merge in mergeT-&gt;angle </li>
+<li><a href="merge.h#qh_ANGLEdegen">qh_ANGLEdegen</a>
+indicates degenerate facet in mergeT-&gt;angle </li>
+<li><a href="merge.h#qh_ANGLEconcave">qh_ANGLEconcave</a>
+offset to indicate concave facets in
+mergeT-&gt;angle </li>
+<li><a href="merge.h#qh_MERGEapex">qh_MERGEapex</a>
+flag for qh_mergefacet() to indicate an apex
+merge </li>
+</ul>
+<h3><a href="qh-merge.htm#TOC">&#187;</a><a name="mtop">top-level merge
+functions</a></h3>
+<ul>
+<li><a href="merge.c#all_merges">qh_all_merges</a>
+merge all non-convex facets </li>
+<li><a href="merge.c#checkzero">qh_checkzero</a>
+check that facets are clearly convex </li>
+<li><a href="merge.c#flippedmerges">qh_flippedmerges</a>
+merge flipped facets into best neighbor </li>
+<li><a href="merge.c#forcedmerges">qh_forcedmerges</a>
+merge all duplicate ridges </li>
+<li><a href="merge.c#merge_degenredundant">qh_merge_degenredundant</a>
+merge degenerate and redundant facets </li>
+<li><a href="merge.c#merge_nonconvex">qh_merge_nonconvex</a>
+merge a non-convex ridge </li>
+<li><a href="merge.c#premerge">qh_premerge</a>
+pre-merge non-convex facets </li>
+<li><a href="merge.c#postmerge">qh_postmerge</a>
+post-merge nonconvex facets as defined by
+maxcentrum/maxangle </li>
+</ul>
+
+<h3><a href="qh-merge.htm#TOC">&#187;</a><a name="mset">functions for
+identifying merges</a></h3>
+<ul>
+<li><a href="merge.c#appendmergeset">qh_appendmergeset</a>
+appends an entry to qh.facet_mergeset</li>
+<li><a href="merge.c#compareangle">qh_compareangle</a>
+used by qsort() to order merges </li>
+<li><a href="merge.c#comparemerge">qh_comparemerge</a>
+used by qsort() to order merges </li>
+<li><a href="merge.c#degen_redundant_facet">qh_degen_redundant_facet</a>
+check for a degenerate and redundant facet</li>
+<li><a href="merge.c#degen_redundant_neighbors">qh_degen_redundant_neighbors</a>
+append degenerate and redundant neighbors to
+qh.degen_mergeset </li>
+<li><a href="merge.c#getmergeset_initial">qh_getmergeset_initial</a>
+build initial qh.facet_mergeset </li>
+<li><a href="merge.c#getmergeset">qh_getmergeset</a>
+update qh.facet_mergeset </li>
+<li><a href="merge.c#mark_dupridges">qh_mark_dupridges</a>
+add duplicated ridges to qh.facet_mergeset</li>
+<li><a href="merge.c#maydropneighbor">qh_maydropneighbor</a>
+drop neighbor relationship if no ridge between
+facet and neighbor </li>
+<li><a href="merge.c#test_appendmerge">qh_test_appendmerge</a>
+test a pair of facets for convexity and append to
+qh.facet_mergeset if non-convex </li>
+<li><a href="merge.c#test_vneighbors">qh_test_vneighbors</a>
+test vertex neighbors for convexity </li>
+</ul>
+
+<h3><a href="qh-merge.htm#TOC">&#187;</a><a name="mbest">functions for
+determining the best merge</a></h3>
+<ul>
+<li><a href="merge.c#findbest_test">qh_findbest_test</a>
+test neighbor for best merge </li>
+<li><a href="merge.c#findbestneighbor">qh_findbestneighbor</a>
+finds best neighbor of a facet for merging (i.e.,
+closest hyperplane) </li>
+</ul>
+
+<h3><a href="qh-merge.htm#TOC">&#187;</a><a name="mmerge">functions for
+merging facets</a></h3>
+<ul>
+<li><a href="merge.c#copynonconvex">qh_copynonconvex</a>
+copy non-convex flag to another ridge for the
+same neighbor </li>
+<li><a href="merge.c#makeridges">qh_makeridges</a>
+creates explicit ridges between simplicial facets
+</li>
+<li><a href="merge.c#mergefacet">qh_mergefacet</a>
+merges one facet into another facet</li>
+<li><a href="merge.c#mergeneighbors">qh_mergeneighbors</a>
+merges the neighbors of two facets </li>
+<li><a href="merge.c#mergeridges">qh_mergeridges</a>
+merges the ridge sets of two facets </li>
+<li><a href="merge.c#mergesimplex">qh_mergesimplex</a>
+merge a simplicial facet into another simplicial
+facet </li>
+<li><a href="merge.c#mergevertex_del">qh_mergevertex_del</a>
+delete a vertex due to merging one facet into
+another facet </li>
+<li><a href="merge.c#mergevertex_neighbors">qh_mergevertex_neighbors</a>
+merge the vertex neighbors of two facets </li>
+<li><a href="merge.c#mergevertices">qh_mergevertices</a>
+merge the vertex sets of two facets </li>
+<li><a href="merge.c#newvertices">qh_newvertices</a>
+register all vertices as new vertices </li>
+<li><a href="merge.c#updatetested">qh_updatetested</a>
+clear tested flags and centrums involved in a
+merge </li>
+<li><a href="merge.c#willdelete">qh_willdelete</a>
+moves facet to qh.visible_list; sets replacement
+or NULL </li>
+</ul>
+
+<h3><a href="qh-merge.htm#TOC">&#187;</a><a name="mcycle">functions for
+merging a cycle of facets</a></h3>
+<p>If a point is coplanar with an horizon facet, the
+corresponding new facets are linked together (a <em>samecycle</em>)
+for merging.</p>
+<ul>
+<li><a href="merge.c#basevertices">qh_basevertices</a>
+return temporary set of base vertices for a
+samecycle </li>
+<li><a href="merge.c#mergecycle">qh_mergecycle</a>
+merge a samecycle into a horizon facet </li>
+<li><a href="merge.c#mergecycle_all">qh_mergecycle_all</a>
+merge all samecycles into horizon facets</li>
+<li><a href="merge.c#mergecycle_facets">qh_mergecycle_facets</a>
+finish merge of samecycle </li>
+<li><a href="merge.c#mergecycle_neighbors">qh_mergecycle_neighbors</a>
+merge neighbor sets for samecycle </li>
+<li><a href="merge.c#mergecycle_ridges">qh_mergecycle_ridges</a>
+merge ridge sets for samecycle </li>
+<li><a href="merge.c#mergecycle_vneighbors">qh_mergecycle_vneighbors</a>
+merge vertex neighbor sets for samecycle </li>
+</ul>
+<h3><a href="qh-merge.htm#TOC">&#187;</a><a name="mrename">functions
+for renaming a vertex</a></h3>
+<ul>
+<li><a href="merge.c#comparevisit">qh_comparevisit</a>
+used by qsort() to order vertices by visitid</li>
+<li><a href="merge.c#reducevertices">qh_reducevertices</a>
+reduce vertex sets </li>
+<li><a href="merge.c#redundant_vertex">qh_redundant_vertex</a>
+returns true if detect and rename redundant
+vertex </li>
+<li><a href="merge.c#rename_sharedvertex">qh_rename_sharedvertex</a>
+detect and rename a shared vertex </li>
+<li><a href="merge.c#renameridgevertex">qh_renameridgevertex</a>
+rename oldvertex to newvertex in a ridge </li>
+<li><a href="merge.c#renamevertex">qh_renamevertex</a>
+rename oldvertex to newvertex in ridges </li>
+<li><a href="merge.c#remove_extravertices">qh_remove_extravertices</a>
+remove extra vertices in non-simplicial facets </li>
+</ul>
+
+<h3><a href="qh-merge.htm#TOC">&#187;</a><a name="mvertex">functions
+for identifying vertices for renaming</a></h3>
+<ul>
+<li><a href="merge.c#find_newvertex">qh_find_newvertex</a>
+locate new vertex for renaming old vertex </li>
+<li><a href="merge.c#hashridge">qh_hashridge</a> add
+ridge to hashtable </li>
+<li><a href="merge.c#hashridge_find">qh_hashridge_find</a>
+returns matching ridge in hashtable</li>
+<li><a href="merge.c#neighbor_intersections">qh_neighbor_intersections</a>
+return intersection of vertex sets for
+neighboring facets </li>
+<li><a href="merge.c#vertexridges">qh_vertexridges</a>
+return temporary set of ridges adjacent to a
+vertex </li>
+<li><a href="merge.c#vertexridges_facet">qh_vertexridges_facet</a>
+add adjacent ridges for a vertex in facet </li>
+</ul>
+
+<h3><a href="qh-merge.htm#TOC">&#187;</a><a name="mcheck">functions for check and
+trace</a></h3>
+<ul>
+<li><a href="merge.c#checkconnect">qh_checkconnect</a>
+check that new facets are connected </li>
+<li><a href="merge.c#tracemerge">qh_tracemerge</a>
+print trace message after merge </li>
+<li><a href="merge.c#tracemerging">qh_tracemerging</a>
+print trace message during post-merging </li>
+</ul>
+
+<p><!-- Navigation links --> </p>
+<hr>
+<p><b>Up:</b>
+<a href="http://www.qhull.org">Home page for
+Qhull</a> <br>
+<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual: Table of Contents</a> <br>
+<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a>
+&#149; <a href="../../html/qh-quick.htm#options">Options</a>
+&#149; <a href="../../html/qh-opto.htm#output">Output</a>
+&#149; <a href="../../html/qh-optf.htm#format">Formats</a>
+&#149; <a href="../../html/qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="../../html/qh-optp.htm#print">Print</a>
+&#149; <a href="../../html/qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="../../html/qh-optc.htm#prec">Precision</a>
+&#149; <a href="../../html/qh-optt.htm#trace">Trace</a>
+&#149; <a href="index.htm">Functions</a><br>
+<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a> <br>
+<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br>
+<b>To:</b> <a href="qh-geom.htm">Geom</a> &#149;
+<a href="qh-globa.htm">Global</a> &#149; <a href="qh-io.htm">Io</a>
+&#149; <a href="qh-mem.htm">Mem</a> &#149; <a href="qh-merge.htm">Merge</a>
+&#149; <a href="qh-poly.htm">Poly</a> &#149; <a href="qh-qhull.htm#TOC">Qhull</a>
+&#149; <a href="qh-set.htm">Set</a> &#149; <a href="qh-stat.htm">Stat</a>
+&#149; <a href="qh-user.htm">User</a><br>
+</p>
+<p><!-- GC common information --> </p>
+<hr>
+<p><a href="http://www.geom.uiuc.edu/"><img
+src="../../html/qh--geom.gif" align="middle" width="40" height="40"></a><i>The
+Geometry Center Home Page </i></p>
+<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a>
+</a><br>
+Created: May 2, 1997 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p>
+</body>
+</html>
diff --git a/xs/src/qhull/src/libqhull/qh-poly.htm b/xs/src/qhull/src/libqhull/qh-poly.htm
new file mode 100644
index 000000000..c8f6b38b0
--- /dev/null
+++ b/xs/src/qhull/src/libqhull/qh-poly.htm
@@ -0,0 +1,485 @@
+<!-- Do not edit with Front Page, it adds too many spaces -->
+<html>
+<head>
+<meta http-equiv="Content-Type"
+content="text/html; charset=iso-8859-1">
+<title>poly.c, poly2.c -- polyhedron operations</title>
+</head>
+
+<body>
+<!-- Navigation links -->
+<p><a name="TOP"><b>Up:</b></a> <a
+href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual</a>: Table of Contents <br>
+<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a>
+&#149; <a href="../../html/qh-quick.htm#options">Options</a>
+&#149; <a href="../../html/qh-opto.htm#output">Output</a>
+&#149; <a href="../../html/qh-optf.htm#format">Formats</a>
+&#149; <a href="../../html/qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="../../html/qh-optp.htm#print">Print</a>
+&#149; <a href="../../html/qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="../../html/qh-optc.htm#prec">Precision</a>
+&#149; <a href="../../html/qh-optt.htm#trace">Trace</a>
+&#149; <a href="index.htm">Functions</a><br>
+<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a><br>
+<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br>
+<b>To:</b> <a href="qh-geom.htm">Geom</a> &#149; <a href="qh-globa.htm">Global</a>
+&#149; <a href="qh-io.htm">Io</a> &#149; <a href="qh-mem.htm">Mem</a>
+&#149; <a href="qh-merge.htm">Merge</a> &#149; <a href="qh-poly.htm#TOC">Poly</a>
+&#149; <a href="qh-qhull.htm">Qhull</a> &#149; <a href="qh-set.htm">Set</a>
+&#149; <a href="qh-stat.htm">Stat</a> &#149; <a href="qh-user.htm">User</a>
+</p>
+<hr>
+
+<h2>poly.c, poly2.c -- polyhedron operations</h2>
+<blockquote>
+
+<p>Qhull uses dimension-free terminology. Qhull builds a
+polyhedron in dimension <em>d. </em>A <em>polyhedron</em> is a
+simplicial complex of faces with geometric information for the
+top and bottom-level faces. A (<em>d-1</em>)-face is a <em>facet</em>,
+a (<em>d-2</em>)-face is a <em>ridge</em>, and a <em>0</em>-face
+is a <em>vertex</em>. For example in 3-d, a facet is a polygon
+and a ridge is an edge. A facet is built from a ridge (the <em>base</em>)
+and a vertex (the <em>apex</em>). See
+<a href="../../html/index.htm#structure">Qhull's data structures</a>.</p>
+
+<p>Qhull's primary data structure is a polyhedron. A
+polyhedron is a list of facets. Each facet has a set of
+neighboring facets and a set of vertices. Each facet has a
+hyperplane. For example, a tetrahedron has four facets.
+If its vertices are <em>a, b, c, d</em>, and its facets
+are <em>1, 2, 3, 4,</em> the tetrahedron is </p>
+<blockquote>
+<ul>
+<li>facet 1 <ul>
+ <li>vertices: b c d </li>
+ <li>neighbors: 2 3 4 </li>
+</ul>
+</li>
+<li>facet 2 <ul>
+ <li>vertices: a c d </li>
+ <li>neighbors: 1 3 4 </li>
+</ul>
+</li>
+<li>facet 3 <ul>
+ <li>vertices: a b d </li>
+ <li>neighbors: 1 2 4 </li>
+</ul>
+</li>
+<li>facet 4 <ul>
+ <li>vertices: a b c </li>
+ <li>neighbors: 1 2 3 </li>
+</ul>
+</li>
+</ul>
+</blockquote>
+<p>A facet may be simplicial or non-simplicial. In 3-d, a
+<i>simplicial facet</i> has three vertices and three
+neighbors. A <i>nonsimplicial facet</i> has more than
+three vertices and more than three neighbors. A
+nonsimplicial facet has a set of ridges and a centrum. </p>
+<p>
+A simplicial facet has an orientation. An <i>orientation</i>
+is either <i>top</i> or <i>bottom</i>.
+The flag, <tt>facet-&gt;toporient,</tt>
+defines the orientation of the facet's vertices. For example in 3-d,
+'top' is left-handed orientation (i.e., the vertex order follows the direction
+of the left-hand fingers when the thumb is pointing away from the center).
+Except for axis-parallel facets in 5-d and higher, topological orientation
+determines the geometric orientation of the facet's hyperplane.
+
+<p>A nonsimplicial facet is due to merging two or more
+facets. The facet's ridge set determine a simplicial
+decomposition of the facet. Each ridge is a 1-face (i.e.,
+it has two vertices and two neighboring facets). The
+orientation of a ridge is determined by the order of the
+neighboring facets. The flag, <tt>facet-&gt;toporient,</tt>is
+ignored. </p>
+<p>A nonsimplicial facet has a centrum for testing
+convexity. A <i>centrum</i> is a point on the facet's
+hyperplane that is near the center of the facet. Except
+for large facets, it is the arithmetic average of the
+facet's vertices. </p>
+<p>A nonsimplicial facet is an approximation that is
+defined by offsets from the facet's hyperplane. When
+Qhull finishes, the <i>outer plane</i> is above all
+points while the <i>inner plane</i> is below the facet's
+vertices. This guarantees that any exact convex hull
+passes between the inner and outer planes. The outer
+plane is defined by <tt>facet-&gt;maxoutside</tt> while
+the inner plane is computed from the facet's vertices.</p>
+
+<p>Qhull 3.1 includes triangulation of non-simplicial facets
+('<a href="../../html/qh-optq.htm#Qt">Qt</a>').
+These facets,
+called <i>tricoplanar</i>, share the same normal. centrum, and Voronoi center.
+One facet (keepcentrum) owns these data structures.
+While tricoplanar facets are more accurate than the simplicial facets from
+joggled input, they
+may have zero area or flipped orientation.
+
+</blockquote>
+<p><b>Copyright &copy; 1995-2015 C.B. Barber</b></p>
+<hr>
+<p><a href="#TOP">&#187;</a> <a href="qh-geom.htm#TOC">Geom</a>
+<a name="TOC">&#149;</a> <a href="qh-globa.htm#TOC">Global</a>
+&#149; <a href="qh-io.htm#TOC">Io</a> &#149; <a href="qh-mem.htm#TOC">Mem</a>
+&#149; <a href="qh-merge.htm#TOC">Merge</a> &#149; <b>Poly</b>
+&#149; <a href="qh-qhull.htm#TOC">Qhull</a> &#149; <a href="qh-set.htm#TOC">Set</a>
+&#149; <a href="qh-stat.htm#TOC">Stat</a> &#149; <a href="qh-user.htm#TOC">User</a>
+</p>
+<h3>Index to <a href="poly.c">poly.c</a>,
+<a href="poly2.c">poly2.c</a>, <a href="poly.h">poly.h</a>,
+and <a href="libqhull.h">libqhull.h</a></h3>
+<ul>
+<li><a href="#ptype">Data types and global
+lists for polyhedrons</a> </li>
+<li><a href="#pconst">poly.h constants</a> </li>
+<li><a href="#pgall">Global FORALL macros</a> </li>
+<li><a href="#pall">FORALL macros</a> </li>
+<li><a href="#peach">FOREACH macros</a> </li>
+<li><a href="#pieach">Indexed FOREACH macros</a> </li>
+<li><a href="#pmacro">Other macros for polyhedrons</a><p>&nbsp;</li>
+<li><a href="#plist">Facetlist functions</a> </li>
+<li><a href="#pfacet">Facet functions</a> </li>
+<li><a href="#pvertex">Vertex, ridge, and point
+functions</a> </li>
+<li><a href="#phash">Hashtable functions</a> </li>
+<li><a href="#pnew">Allocation and deallocation
+functions</a> </li>
+<li><a href="#pcheck">Check functions</a> </li>
+</ul>
+<h3><a href="qh-poly.htm#TOC">&#187;</a><a name="ptype">Data
+types and global lists for polyhedrons</a></h3>
+<ul>
+<li><a href="libqhull.h#facetT">facetT</a> defines a
+facet </li>
+<li><a href="libqhull.h#ridgeT">ridgeT</a> defines a
+ridge </li>
+<li><a href="libqhull.h#vertexT">vertexT</a> defines a
+vertex </li>
+<li><a href="libqhull.h#qh-lists">qh facet and vertex
+lists</a> lists of facets and vertices </li>
+<li><a href="libqhull.h#qh-set">qh global sets</a>
+global sets for merging, hashing, input, etc. </li>
+</ul>
+<h3><a href="qh-poly.htm#TOC">&#187;</a><a name="pconst">poly.h constants</a></h3>
+<ul>
+<li><a href="poly.h#ALGORITHMfault">ALGORITHMfault</a>
+flag to not report errors in qh_checkconvex() </li>
+<li><a href="poly.h#DATAfault">DATAfault</a> flag to
+report errors in qh_checkconvex() </li>
+<li><a href="poly.h#DUPLICATEridge">DUPLICATEridge</a>
+special value for facet-&gt;neighbor to indicate
+a duplicate ridge </li>
+<li><a href="poly.h#MERGEridge">MERGEridge</a>
+special value for facet-&gt;neighbor to indicate
+a merged ridge </li>
+</ul>
+<h3><a href="qh-poly.htm#TOC">&#187;</a><a name="pgall">Global FORALL
+macros</a></h3>
+<ul>
+<li><a href="libqhull.h#FORALLfacets">FORALLfacets</a>
+assign 'facet' to each facet in qh.facet_list </li>
+<li><a href="poly.h#FORALLnew_facets">FORALLnew_facets</a>
+assign 'facet' to each facet in qh.newfacet_list </li>
+<li><a href="poly.h#FORALLvisible_facets">FORALLvisible_facets</a>
+assign 'visible' to each visible facet in
+qh.visible_list </li>
+<li><a href="libqhull.h#FORALLpoints">FORALLpoints</a>
+assign 'point' to each point in qh.first_point,
+qh.num_points </li>
+<li><a href="libqhull.h#FORALLvertices">FORALLvertices</a>
+assign 'vertex' to each vertex in qh.vertex_list </li>
+</ul>
+<h3><a href="qh-poly.htm#TOC">&#187;</a><a name="pall">FORALL macros</a></h3>
+<ul>
+<li><a href="poly.h#FORALLfacet_">FORALLfacet_</a>
+assign 'facet' to each facet in facetlist </li>
+<li><a href="libqhull.h#FORALLpoint_">FORALLpoint_</a>
+assign 'point' to each point in points array</li>
+<li><a href="poly.h#FORALLsame_">FORALLsame_</a>
+assign 'same' to each facet in samecycle</li>
+<li><a href="poly.h#FORALLsame_cycle_">FORALLsame_cycle_</a>
+assign 'same' to each facet in samecycle</li>
+<li><a href="poly.h#FORALLvertex_">FORALLvertex_</a>
+assign 'vertex' to each vertex in vertexlist </li>
+</ul>
+<h3><a href="qh-poly.htm#TOC">&#187;</a><a name="peach">FOREACH macros</a></h3>
+<ul>
+<li><a href="libqhull.h#FOREACHfacet_">FOREACHfacet_</a>
+assign 'facet' to each facet in facets </li>
+<li><a href="libqhull.h#FOREACHneighbor_">FOREACHneighbor_</a>
+assign 'neighbor' to each facet in
+facet-&gt;neighbors or vertex-&gt;neighbors</li>
+<li><a href="poly.h#FOREACHnewfacet_">FOREACHnewfacet_</a>
+assign 'newfacet' to each facet in facet set </li>
+<li><a href="libqhull.h#FOREACHpoint_">FOREACHpoint_</a>
+assign 'point' to each point in points set </li>
+<li><a href="libqhull.h#FOREACHridge_">FOREACHridge_</a>
+assign 'ridge' to each ridge in ridge set </li>
+<li><a href="libqhull.h#FOREACHvertex_">FOREACHvertex_</a>
+assign 'vertex' to each vertex in vertex set </li>
+<li><a href="poly.h#FOREACHvertexA_">FOREACHvertexA_</a>
+assign 'vertexA' to each vertex in vertex set</li>
+<li><a href="poly.h#FOREACHvisible_">FOREACHvisible_</a>
+assign 'visible' to each facet in facet set </li>
+</ul>
+<h3><a href="qh-poly.htm#TOC">&#187;</a><a name="pieach">Indexed
+FOREACH macros</a></h3>
+<ul>
+<li><a href="libqhull.h#FOREACHfacet_i_">FOREACHfacet_i_</a>
+assign 'facet' and 'facet_i' to each facet in
+facet set </li>
+<li><a href="libqhull.h#FOREACHneighbor_i_">FOREACHneighbor_i_</a>
+assign 'neighbor' and 'neighbor_i' to each facet
+in facet-&gt;neighbors or vertex-&gt;neighbors</li>
+<li><a href="libqhull.h#FOREACHpoint_i_">FOREACHpoint_i_</a>
+assign 'point' and 'point_i' to each point in
+points set </li>
+<li><a href="libqhull.h#FOREACHridge_i_">FOREACHridge_i_</a>
+assign 'ridge' and 'ridge_i' to each ridge in
+ridges set </li>
+<li><a href="libqhull.h#FOREACHvertex_i_">FOREACHvertex_i_</a>
+assign 'vertex' and 'vertex_i' to each vertex in
+vertices set </li>
+<li><a href="poly.h#FOREACHvertexreverse12_">FOREACHvertexreverse12_</a>
+assign 'vertex' to each vertex in vertex set;
+reverse the order of first two vertices </li>
+</ul>
+<h3><a href="qh-poly.htm#TOC">&#187;</a><a name="pmacro">Other macros for polyhedrons</a></h3>
+<ul>
+<li><a href="libqhull.h#getid_">getid_</a> return ID for
+a facet, ridge, or vertex </li>
+<li><a href="libqhull.h#otherfacet_">otherfacet_</a>
+return neighboring facet for a ridge in a facet </li>
+</ul>
+<h3><a href="qh-poly.htm#TOC">&#187;</a><a name="plist">Facetlist
+functions</a></h3>
+<ul>
+<li><a href="poly.c#appendfacet">qh_appendfacet</a>
+appends facet to end of qh.facet_list</li>
+<li><a href="poly.c#attachnewfacets">qh_attachnewfacets</a>
+attach new facets in qh.newfacet_list to the
+horizon </li>
+<li><a href="poly2.c#findgood">qh_findgood</a>
+identify good facets for qh.PRINTgood </li>
+<li><a href="poly2.c#findgood_all">qh_findgood_all</a>
+identify more good facets for qh.PRINTgood </li>
+<li><a href="poly2.c#furthestnext">qh_furthestnext</a>
+move facet with furthest of furthest points to
+facet_next </li>
+<li><a href="poly2.c#initialhull">qh_initialhull</a>
+construct the initial hull as a simplex of
+vertices </li>
+<li><a href="poly2.c#nearcoplanar">qh_nearcoplanar</a>
+ remove near-inside points from coplanar sets</li>
+<li><a href="poly2.c#prependfacet">qh_prependfacet</a>
+prepends facet to start of facetlist </li>
+<li><a href="user.c#printfacetlist">qh_printfacetlist</a>
+print facets in a facetlist</li>
+<li><a href="poly2.c#printlists">qh_printlists</a>
+print out facet list for debugging </li>
+<li><a href="poly.c#removefacet">qh_removefacet</a>
+unlinks facet from qh.facet_list</li>
+<li><a href="poly2.c#resetlists">qh_resetlists</a>
+reset qh.newvertex_list, qh.newfacet_list, and
+qh.visible_list </li>
+</ul>
+<h3><a href="qh-poly.htm#TOC">&#187;</a><a name="pfacet">Facet
+functions</a></h3>
+<ul>
+<li><a href="poly2.c#createsimplex">qh_createsimplex</a>
+create a simplex of facets from a set of vertices
+</li>
+<li><a href="poly2.c#findbestlower">qh_findbestlower</a> find best
+non-upper, non-flipped facet for point at upperfacet</li>
+<li><a href="poly2.c#furthestout">qh_furthestout</a>
+make furthest outside point the last point of a
+facet's outside set </li>
+<li><a href="poly.c#makenew_nonsimplicial">qh_makenew_nonsimplicial</a>
+make new facets from ridges of visible facets </li>
+<li><a href="poly.c#makenew_simplicial">qh_makenew_simplicial</a>
+make new facets for horizon neighbors </li>
+<li><a href="poly.c#makenewfacet">qh_makenewfacet</a>
+create a facet from vertices and apex </li>
+<li><a href="poly2.c#makenewfacets">qh_makenewfacets</a>
+make new facets from vertex, horizon facets, and
+visible facets </li>
+<li><a href="poly.c#makenewplanes">qh_makenewplanes</a>
+make new hyperplanes for facets </li>
+<li><a href="poly2.c#outcoplanar">qh_outcoplanar</a>
+move points from outside set to coplanar set </li>
+<li><a href="poly2.c#setvoronoi_all">qh_setvoronoi_all</a>
+compute Voronoi centers for all facets </li>
+<li><a href="poly2.c#triangulate">qh_triangulate</a>
+triangulate non-simplicial facets</li>
+<li><a href="poly2.c#triangulate_facet">qh_triangulate_facet</a>
+triangulate a non-simplicial facet</li>
+<li><a href="poly2.c#triangulate_link">qh_triangulate_link</a>
+link facets together from qh_triangulate</li>
+<li><a href="poly2.c#triangulate_mirror">qh_triangulate_mirror</a>
+delete mirrored facets from qh_triangulate</li>
+<li><a href="poly2.c#triangulate_null">qh_triangulate_null</a>
+delete null facet from qh_triangulate</li>
+</ul>
+<h3><a href="qh-poly.htm#TOC">&#187;</a><a name="pvertex">Vertex,
+ridge, and point functions</a></h3>
+<ul>
+<li><a href="poly.c#appendvertex">qh_appendvertex</a>
+append vertex to end of qh.vertex_list, </li>
+<li><a href="io.c#detvridge">qh_detvridge</a> determine Voronoi
+ridge for an input site
+<li><a href="io.c#detvridge3">qh_detvridge3</a> determine 3-d Voronoi
+ridge for an input site
+<li><a href="poly2.c#facet3vertex">qh_facet3vertex</a>
+return an oriented vertex set for a 3-d facet </li>
+<li><a href="poly.c#facetintersect">qh_facetintersect</a>
+return intersection of simplicial facets </li>
+<li><a href="poly2.c#initialvertices">qh_initialvertices</a>
+return non-singular set of initial vertices </li>
+<li><a href="poly2.c#isvertex">qh_isvertex</a> true
+if point is in a vertex set </li>
+<li><a href="poly2.c#nearvertex">qh_nearvertex</a>
+return nearest vertex to point </li>
+<li><a href="poly2.c#nextridge3d">qh_nextridge3d</a>
+iterate over each ridge and vertex for a 3-d
+facet </li>
+<li><a href="poly2.c#point">qh_point</a> return point
+for a point ID </li>
+<li><a href="poly2.c#pointfacet">qh_pointfacet</a>
+return temporary set of facets indexed by point
+ID </li>
+<li><a href="poly.c#pointid">qh_pointid</a> return ID
+for a point</li>
+<li><a href="poly2.c#pointvertex">qh_pointvertex</a>
+return temporary set of vertices indexed by point
+ID </li>
+<li><a href="poly.c#removevertex">qh_removevertex</a>
+unlink vertex from qh.vertex_list, </li>
+<li><a href="poly.c#updatevertices">qh_updatevertices</a>
+update vertex neighbors and delete interior
+vertices </li>
+<li><a href="poly2.c#vertexintersect">qh_vertexintersect</a>
+intersect two vertex sets </li>
+<li><a href="poly2.c#vertexintersect_new">qh_vertexintersect_new</a>
+return intersection of two vertex sets </li>
+<li><a href="poly2.c#vertexneighbors">qh_vertexneighbors</a>
+for each vertex in hull, determine facet
+neighbors </li>
+<li><a href="poly2.c#vertexsubset">qh_vertexsubset</a>
+returns True if vertexsetA is a subset of
+vertexsetB </li>
+</ul>
+<h3><a href="qh-poly.htm#TOC">&#187;</a><a name="phash">Hashtable functions</a></h3>
+<ul>
+<li><a href="poly2.c#addhash">qh_addhash</a> add hash
+element to linear hash table</li>
+<li><a href="poly.c#gethash">qh_gethash</a> return
+hash value for a set</li>
+<li><a href="poly2.c#matchduplicates">qh_matchduplicates</a>
+match duplicate ridges in hash table </li>
+<li><a href="poly.c#matchneighbor">qh_matchneighbor</a>
+try to match subridge of new facet with a
+neighbor </li>
+<li><a href="poly.c#matchnewfacets">qh_matchnewfacets</a>
+match new facets with their new facet neighbors </li>
+<li><a href="poly.c#matchvertices">qh_matchvertices</a>
+tests whether a facet and hash entry match at a
+ridge </li>
+<li><a href="poly2.c#newhashtable">qh_newhashtable</a>
+allocate a new qh.hash_table </li>
+<li><a href="poly2.c#printhashtable">qh_printhashtable</a>
+print hash table </li>
+</ul>
+<h3><a href="qh-poly.htm#TOC">&#187;</a><a name="pnew">Allocation and
+deallocation functions</a></h3>
+<ul>
+<li><a href="poly2.c#clearcenters">qh_clearcenters</a>
+clear old data from facet-&gt;center </li>
+<li><a href="poly.c#deletevisible">qh_deletevisible</a>
+delete visible facets and vertices </li>
+<li><a href="poly.c#delfacet">qh_delfacet</a> free up
+the memory occupied by a facet </li>
+<li><a href="poly2.c#delridge">qh_delridge</a> delete
+ridge</li>
+<li><a href="poly2.c#delvertex">qh_delvertex</a>
+delete vertex </li>
+<li><a href="poly.c#newfacet">qh_newfacet</a> create
+and allocate space for a facet </li>
+<li><a href="poly.c#newridge">qh_newridge</a> create
+and allocate space for a ridge </li>
+<li><a href="poly2.c#newvertex">qh_newvertex</a>
+create and allocate space for a vertex </li>
+</ul>
+<h3><a href="qh-poly.htm#TOC">&#187;</a><a name="pcheck">Check
+functions</a></h3>
+<ul>
+<li><a href="poly2.c#check_bestdist">qh_check_bestdist</a>
+check that points are not outside of facets </li>
+<li><a href="poly2.c#check_dupridge">qh_check_dupridge</a>
+check duplicate ridge between facet1 and facet2 for wide merge </li>
+<li><a href="poly2.c#check_maxout">qh_check_maxout</a>
+updates qh.max_outside and checks all points
+against bestfacet </li>
+<li><a href="poly2.c#check_output">qh_check_output</a>
+check topological and geometric output</li>
+<li><a href="poly2.c#check_point">qh_check_point</a>
+check that point is not outside of facet </li>
+<li><a href="poly2.c#check_points">qh_check_points</a>
+check that all points are inside all facets </li>
+<li><a href="poly2.c#checkconvex">qh_checkconvex</a>
+check that each ridge in facetlist is convex </li>
+<li><a href="poly2.c#checkfacet">qh_checkfacet</a>
+check for consistency errors in facet </li>
+<li><a href="poly.c#checkflipped">qh_checkflipped</a>
+check facet orientation to the interior point </li>
+<li><a href="poly2.c#checkflipped_all">qh_checkflipped_all</a>
+check facet orientation for a facet list </li>
+<li><a href="poly2.c#checkpolygon">qh_checkpolygon</a>
+check topological structure </li>
+<li><a href="poly2.c#checkvertex">qh_checkvertex</a>
+check vertex for consistency </li>
+<li><a href="poly2.c#infiniteloop">qh_infiniteloop</a>
+report error for a loop of facets </li>
+<li><a href="poly2.c#printlists">qh_printlists</a>
+print out facet list for debugging </li>
+</ul>
+
+
+<p><!-- Navigation links --> </p>
+<hr>
+<p><b>Up:</b>
+<a href="http://www.qhull.org">Home page for
+Qhull</a> <br>
+<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual: Table of Contents</a> <br>
+<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a>
+&#149; <a href="../../html/qh-quick.htm#options">Options</a>
+&#149; <a href="../../html/qh-opto.htm#output">Output</a>
+&#149; <a href="../../html/qh-optf.htm#format">Formats</a>
+&#149; <a href="../../html/qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="../../html/qh-optp.htm#print">Print</a>
+&#149; <a href="../../html/qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="../../html/qh-optc.htm#prec">Precision</a>
+&#149; <a href="../../html/qh-optt.htm#trace">Trace</a>
+&#149; <a href="index.htm">Functions</a><br>
+<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a> <br>
+<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br>
+<b>To:</b> <a href="qh-geom.htm">Geom</a> &#149;
+<a href="qh-globa.htm">Global</a> &#149; <a href="qh-io.htm">Io</a>
+&#149; <a href="qh-mem.htm">Mem</a> &#149; <a href="qh-merge.htm">Merge</a>
+&#149; <a href="qh-poly.htm">Poly</a> &#149; <a href="qh-qhull.htm#TOC">Qhull</a>
+&#149; <a href="qh-set.htm">Set</a> &#149; <a href="qh-stat.htm">Stat</a>
+&#149; <a href="qh-user.htm">User</a><br>
+</p>
+<p><!-- GC common information --> </p>
+<hr>
+<p><a href="http://www.geom.uiuc.edu/"><img
+src="../../html/qh--geom.gif" align="middle" width="40" height="40"></a><i>The
+Geometry Center Home Page </i></p>
+<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a>
+</a><br>
+Created: May 2, 1997 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p>
+</body>
+</html>
diff --git a/xs/src/qhull/src/libqhull/qh-qhull.htm b/xs/src/qhull/src/libqhull/qh-qhull.htm
new file mode 100644
index 000000000..5212c6422
--- /dev/null
+++ b/xs/src/qhull/src/libqhull/qh-qhull.htm
@@ -0,0 +1,279 @@
+<!-- Do not edit with Front Page, it adds too many spaces -->
+<html>
+<head>
+<meta http-equiv="Content-Type"
+content="text/html; charset=iso-8859-1">
+<title>libqhull.c -- top-level functions and basic data types</title>
+</head>
+
+<body>
+<!-- Navigation links -->
+<p><a name="TOP"><b>Up:</b></a> <a
+href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual</a>: Table of Contents <br>
+<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a>
+&#149; <a href="../../html/qh-quick.htm#options">Options</a>
+&#149; <a href="../../html/qh-opto.htm#output">Output</a>
+&#149; <a href="../../html/qh-optf.htm#format">Formats</a>
+&#149; <a href="../../html/qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="../../html/qh-optp.htm#print">Print</a>
+&#149; <a href="../../html/qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="../../html/qh-optc.htm#prec">Precision</a>
+&#149; <a href="../../html/qh-optt.htm#trace">Trace</a>
+&#149; <a href="index.htm">Functions</a><br>
+<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a><br>
+<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br>
+<b>To:</b> <a href="qh-geom.htm">Geom</a> &#149; <a href="qh-globa.htm">Global</a>
+&#149; <a href="qh-io.htm">Io</a> &#149; <a href="qh-mem.htm">Mem</a>
+&#149; <a href="qh-merge.htm">Merge</a> &#149; <a href="qh-poly.htm">Poly</a>
+&#149; <a href="qh-qhull.htm#TOC">Qhull</a> &#149; <a href="qh-set.htm">Set</a>
+&#149; <a href="qh-stat.htm">Stat</a> &#149; <a href="qh-user.htm">User</a>
+</p>
+<hr>
+
+<h2>libqhull.c -- top-level functions and basic data types</h2>
+<blockquote>
+<p>Qhull implements the Quickhull algorithm for computing
+the convex hull. The Quickhull algorithm combines two
+well-known algorithms: the 2-d quickhull algorithm and
+the n-d beneath-beyond algorithm. See
+<a href="../../html/index.htm#description">Description of Qhull</a>. </p>
+<p>This section provides an index to the top-level
+functions and base data types. The top-level header file, <tt>libqhull.h</tt>,
+contains prototypes for these functions.</p>
+</blockquote>
+<p><b>Copyright &copy; 1995-2015 C.B. Barber</b></p>
+<hr>
+<p><a href="#TOP">&#187;</a> <a href="qh-geom.htm#TOC">Geom</a>
+<a name="TOC">&#149;</a> <a href="qh-globa.htm#TOC">Global</a>
+&#149; <a href="qh-io.htm#TOC">Io</a> &#149; <a href="qh-mem.htm#TOC">Mem</a>
+&#149; <a href="qh-merge.htm#TOC">Merge</a> &#149; <a href="qh-poly.htm#TOC">Poly</a>
+&#149; <b>Qhull</b> &#149; <a href="qh-set.htm#TOC">Set</a>
+&#149; <a href="qh-stat.htm#TOC">Stat</a> &#149; <a href="qh-user.htm#TOC">User</a>
+</p>
+<h3>Index to <a href="libqhull.c">libqhull.c</a>,
+<a href="libqhull.h">libqhull.h</a>, and
+<a href="../qhull/unix.c">unix.c</a></h3>
+<ul>
+<li><a href="#qtype">libqhull.h and unix.c data types and
+constants</a> </li>
+<li><a href="#qmacro">libqhull.h other macros</a> </li>
+<li><a href="#qfunc">Quickhull routines in call order</a> </li>
+<li><a href="#qinit">Top-level routines for initializing and terminating Qhull</a></li>
+<li><a href="#qin">Top-level routines for reading and modifying the input</a></li>
+<li><a href="#qcall">Top-level routines for calling Qhull</a></li>
+<li><a href="#qout">Top-level routines for returning results</a></li>
+<li><a href="#qtest">Top-level routines for testing and debugging</a></li>
+</ul>
+
+<h3><a href="qh-qhull.htm#TOC">&#187;</a><a name="qtype">libqhull.h and unix.c
+data types and constants</a></h3>
+<ul>
+<li><a href="libqhull.h#flagT">flagT</a> Boolean flag as
+a bit </li>
+<li><a href="libqhull.h#boolT">boolT</a> boolean value,
+either True or False </li>
+<li><a href="libqhull.h#CENTERtype">CENTERtype</a> to
+distinguish facet-&gt;center </li>
+<li><a href="libqhull.h#qh_PRINT">qh_PRINT</a> output
+formats for printing (qh.PRINTout) </li>
+<li><a href="libqhull.h#qh_ALL">qh_ALL</a> argument flag
+for selecting everything </li>
+<li><a href="libqhull.h#qh_ERR">qh_ERR</a> Qhull exit
+codes for indicating errors </li>
+<li><a href="libqhull.h#qh_FILEstderr">qh_FILEstderr</a> Fake stderr
+to distinguish error output from normal output [C++ only]</li>
+<li><a href="../qhull/unix.c#prompt">qh_prompt</a> version and long prompt for Qhull</li>
+<li><a href="../qhull/unix.c#prompt2">qh_prompt2</a> synopsis for Qhull</li>
+<li><a href="../qhull/unix.c#prompt3">qh_prompt3</a> concise prompt for Qhull</li>
+<li><a href="global.c#qh_version">qh_version</a> version stamp</li>
+</ul>
+
+<h3><a href="qh-qhull.htm#TOC">&#187;</a><a name="qmacro">libqhull.h other
+macros</a></h3>
+<ul>
+<li><a href="qhull_a.h#traceN">traceN</a> print trace
+message if <em>qh.IStracing &gt;= N</em>. </li>
+<li><a href="qhull_a.h#QHULL_UNUSED">QHULL_UNUSED</a> declare an
+ unused variable to avoid warnings. </li>
+</ul>
+
+<h3><a href="qh-qhull.htm#TOC">&#187;</a><a name="qfunc">Quickhull
+routines in call order</a></h3>
+<ul>
+<li><a href="../qhull/unix.c#main">main</a> processes the
+command line, calls qhull() to do the work, and
+exits </li>
+<li><a href="libqhull.c#qhull">qh_qhull</a> construct
+the convex hull of a set of points </li>
+<li><a href="libqhull.c#build_withrestart">qh_build_withrestart</a>
+allow restarts while calling qh_buildhull</li>
+<li><a href="poly2.c#initbuild">qh_initbuild</a>
+initialize hull and outside sets with point array</li>
+<li><a href="libqhull.c#partitionall">qh_partitionall</a>
+partition all points into outside sets </li>
+<li><a href="libqhull.c#buildhull">qh_buildhull</a>
+construct a convex hull by adding points one at a
+time </li>
+<li><a href="libqhull.c#nextfurthest">qh_nextfurthest</a>
+return next furthest point for processing </li>
+<li><a href="libqhull.c#buildtracing">qh_buildtracing</a>
+trace an iteration of buildhull </li>
+<li><a href="libqhull.c#addpoint">qh_addpoint</a> add a
+point to the convex hull </li>
+<li><a href="libqhull.c#findhorizon">qh_findhorizon</a>
+find the horizon and visible facets for a point </li>
+<li><a href="libqhull.c#partitionvisible">qh_partitionvisible</a>
+partition points from facets in qh.visible_list
+to facets in qh.newfacet_list </li>
+<li><a href="libqhull.c#partitionpoint">qh_partitionpoint</a>
+partition a point as inside, coplanar with, or
+outside a facet </li>
+<li><a href="libqhull.c#partitioncoplanar">qh_partitioncoplanar</a>
+partition coplanar point into a facet </li>
+<li><a href="libqhull.c#precision">qh_precision</a> restart on precision errors if not merging and if 'QJn'</li>
+</ul>
+
+<h3><a href="qh-qhull.htm#TOC">&#187;</a><a name="qinit">Top-level routines for initializing and terminating Qhull (in other modules)</a></h3>
+<ul>
+<li><a href="global.c#freebuild">qh_freebuild</a>
+free memory used by qh_initbuild and qh_buildhull
+</li>
+<li><a href="global.c#checkflags">qh_checkflags</a>
+check flags for multiple frontends to qhull
+<li><a href="global.c#freeqhull">qh_freeqhull</a>
+free memory used by qhull </li>
+<li><a href="global.c#init_A">qh_init_A</a> called
+before error handling initialized </li>
+<li><a href="global.c#init_B">qh_init_B</a> called
+after points are defined </li>
+<li><a href="global.c#initflags">qh_initflags</a> set
+flags and constants from command line </li>
+<li><a href="rboxlib.c#rboxpoints">qh_rboxpoints</a>
+generate points for qhull </li>
+<li><a href="global.c#restore_qhull">qh_restore_qhull</a>
+restores a saved qhull </li>
+<li><a href="global.c#save_qhull">qh_save_qhull</a>
+saves qhull for later restoring </li>
+<li><a href="user.c#user_memsizes">qh_user_memsizes</a>
+define additional quick allocation sizes
+</li>
+</ul>
+
+<h3><a href="qh-qhull.htm#TOC">&#187;</a><a name="qin">Top-level routines for reading and modifying the input (in other modules)</a></h3>
+<ul>
+<li><a href="geom2.c#gram_schmidt">qh_gram_schmidt</a>
+implements Gram-Schmidt orthogonalization by rows </li>
+<li><a href="geom2.c#projectinput">qh_projectinput</a>
+project input along one or more dimensions +
+Delaunay projection </li>
+<li><a href="geom2.c#randommatrix">qh_randommatrix</a>
+generate a random dimXdim matrix in range (-1,1) </li>
+<li><a href="io.c#readpoints">qh_readpoints</a> read
+points from input </li>
+<li><a href="geom2.c#rotateinput">qh_rotateinput</a> rotate
+input points using row matrix </li>
+<li><a href="geom2.c#scaleinput">qh_scaleinput</a> scale
+input points using qh low_bound/high_bound </li>
+<li><a href="geom2.c#setdelaunay">qh_setdelaunay</a> project
+points to paraboloid for Delaunay triangulation </li>
+<li><a href="geom2.c#sethalfspace_all">qh_sethalfspace_all</a>
+generate dual for halfspace intersection with interior
+point </li>
+</ul>
+
+<h3><a href="qh-qhull.htm#TOC">&#187;</a><a name="qcall">Top-level routines for calling Qhull (in other modules)</a></h3>
+<ul>
+<li><a href="libqhull.c#addpoint">qh_addpoint</a> add
+point to convex hull </li>
+<li><a href="poly2.c#findbestfacet">qh_findbestfacet</a>
+find facet that is furthest below a point </li>
+<li><a href="poly2.c#findfacet_all">qh_findfacet_all</a>
+exhaustive search for facet below a point </li>
+<li><a href="libqhull.c#qhull">qh_qhull</a> construct
+the convex hull of a set of points </li>
+</ul>
+
+<h3><a href="qh-qhull.htm#TOC">&#187;</a><a name="qout">Top-level routines for returning results (in other modules)</a></h3>
+<ul>
+<li><a href="stat.c#collectstatistics">qh_collectstatistics</a>
+collect statistics for qh.facet_list </li>
+<li><a href="poly2.c#nearvertex">qh_nearvertex</a>
+return nearest vertex to point </li>
+<li><a href="poly2.c#point">qh_point</a> return point
+for a point ID </li>
+<li><a href="poly2.c#pointfacet">qh_pointfacet</a>
+return temporary set of facets indexed by point
+ID </li>
+<li><a href="poly.c#pointid">qh_pointid</a> return ID
+for a point</li>
+<li><a href="poly2.c#pointvertex">qh_pointvertex</a>
+return vertices (if any) for all points</li>
+<li><a href="stat.c#printallstatistics">qh_printallstatistics</a>
+print all statistics </li>
+<li><a href="io.c#printneighborhood">qh_printneighborhood</a>
+print neighborhood of one or two facets </li>
+<li><a href="libqhull.c#printsummary">qh_printsummary</a>
+print summary </li>
+<li><a href="io.c#produce_output">qh_produce_output</a>
+print the results of qh_qhull() </li>
+<li><a href="poly2.c#setvoronoi_all">qh_setvoronoi_all</a>
+compute Voronoi centers for all facets </li>
+</ul>
+
+<h3><a href="qh-qhull.htm#TOC">&#187;</a><a name="qtest">Top-level routines for testing and debugging (in other modules)</a></h3>
+<ul>
+<li><a href="io.c#dfacet">dfacet</a> print facet by
+ID from debugger </li>
+<li><a href="io.c#dvertex">dvertex</a> print vertex
+by ID from debugger </li>
+<li><a href="poly2.c#check_output">qh_check_output</a>
+check output </li>
+<li><a href="poly2.c#check_points">qh_check_points</a>
+verify that all points are inside the convex hull
+</li>
+<li><a href="user.c#errexit">qh_errexit</a> report
+error with a facet and a ridge</li>
+<li><a href="libqhull.c#errexit2">qh_errexit2</a> report
+error with two facets </li>
+<li><a href="user.c#errprint">qh_errprint</a> print
+erroneous facets, ridge, and vertex </li>
+<li><a href="user.c#printfacetlist">qh_printfacetlist</a>
+print all fields for a list of facets </li>
+</ul>
+
+<p><!-- Navigation links --> </p>
+<hr>
+<p><b>Up:</b>
+<a href="http://www.qhull.org">Home page for
+Qhull</a> <br>
+<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual: Table of Contents</a> <br>
+<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a>
+&#149; <a href="../../html/qh-quick.htm#options">Options</a>
+&#149; <a href="../../html/qh-opto.htm#output">Output</a>
+&#149; <a href="../../html/qh-optf.htm#format">Formats</a>
+&#149; <a href="../../html/qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="../../html/qh-optp.htm#print">Print</a>
+&#149; <a href="../../html/qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="../../html/qh-optc.htm#prec">Precision</a>
+&#149; <a href="../../html/qh-optt.htm#trace">Trace</a>
+&#149; <a href="index.htm">Functions</a><br>
+<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a> <br>
+<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br>
+<b>To:</b> <a href="qh-geom.htm">Geom</a> &#149;
+<a href="qh-globa.htm">Global</a> &#149; <a href="qh-io.htm">Io</a>
+&#149; <a href="qh-mem.htm">Mem</a> &#149; <a href="qh-merge.htm">Merge</a>
+&#149; <a href="qh-poly.htm">Poly</a> &#149; <a href="qh-qhull.htm#TOC">Qhull</a>
+&#149; <a href="qh-set.htm">Set</a> &#149; <a href="qh-stat.htm">Stat</a>
+&#149; <a href="qh-user.htm">User</a><br>
+</p>
+<p><!-- GC common information --> </p>
+<hr>
+<p><a href="http://www.geom.uiuc.edu/"><img
+src="../../html/qh--geom.gif" align="middle" width="40" height="40"></a><i>The
+Geometry Center Home Page </i></p>
+<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a>
+</a><br>
+Created: May 2, 1997 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p>
+</body>
+</html>
diff --git a/xs/src/qhull/src/libqhull/qh-set.htm b/xs/src/qhull/src/libqhull/qh-set.htm
new file mode 100644
index 000000000..06e71bbc9
--- /dev/null
+++ b/xs/src/qhull/src/libqhull/qh-set.htm
@@ -0,0 +1,308 @@
+<!-- Do not edit with Front Page, it adds too many spaces -->
+<html>
+<head>
+<meta http-equiv="Content-Type"
+content="text/html; charset=iso-8859-1">
+<title>qset.c -- set data type and operations</title>
+</head>
+
+<body>
+<!-- Navigation links -->
+<p><a name="TOP"><b>Up:</b></a> <a
+href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual</a>: Table of Contents <br>
+<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a>
+&#149; <a href="../../html/qh-quick.htm#options">Options</a>
+&#149; <a href="../../html/qh-opto.htm#output">Output</a>
+&#149; <a href="../../html/qh-optf.htm#format">Formats</a>
+&#149; <a href="../../html/qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="../../html/qh-optp.htm#print">Print</a>
+&#149; <a href="../../html/qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="../../html/qh-optc.htm#prec">Precision</a>
+&#149; <a href="../../html/qh-optt.htm#trace">Trace</a>
+&#149; <a href="index.htm">Functions</a><br>
+<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a><br>
+<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br>
+<b>To:</b> <a href="qh-geom.htm">Geom</a> &#149; <a href="qh-globa.htm">Global</a>
+&#149; <a href="qh-io.htm">Io</a> &#149; <a href="qh-mem.htm">Mem</a>
+&#149; <a href="qh-merge.htm">Merge</a> &#149; <a href="qh-poly.htm">Poly</a>
+&#149; <a href="qh-qhull.htm">Qhull</a> &#149; <a href="qh-set.htm#TOC">Set</a>
+&#149; <a href="qh-stat.htm">Stat</a> &#149; <a href="qh-user.htm">User</a>
+</p>
+<hr>
+
+<h2>qset.c -- set data type and operations</h2>
+<blockquote>
+<p>Qhull's data structures are constructed from sets. The
+functions and macros in qset.c construct, iterate, and
+modify these sets. They are the most frequently called
+functions in Qhull. For this reason, efficiency is the
+primary concern. </p>
+<p>In Qhull, a <i>set</i> is represented by an unordered
+array of pointers with a maximum size and a NULL
+terminator (<a href="qset.h#setT">setT</a>).
+Most sets correspond to mathematical sets
+(i.e., the pointers are unique). Some sets are sorted to
+enforce uniqueness. Some sets are ordered. For example,
+the order of vertices in a ridge determine the ridge's
+orientation. If you reverse the order of adjacent
+vertices, the orientation reverses. Some sets are not
+mathematical sets. They may be indexed as an array and
+they may include NULL pointers. </p>
+<p>The most common operation on a set is to iterate its
+members. This is done with a 'FOREACH...' macro. Each set
+has a custom macro. For example, 'FOREACHvertex_'
+iterates over a set of vertices. Each vertex is assigned
+to the variable 'vertex' from the pointer 'vertexp'. </p>
+<p>Most sets are constructed by appending elements to the
+set. The last element of a set is either NULL or the
+index of the terminating NULL for a partially full set.
+If a set is full, appending an element copies the set to
+a larger array. </p>
+
+</blockquote>
+<p><b>Copyright &copy; 1995-2015 C.B. Barber</b></p>
+<hr>
+<p><a href="#TOP">&#187;</a> <a href="qh-geom.htm#TOC">Geom</a>
+<a name="TOC">&#149;</a> <a href="qh-globa.htm#TOC">Global</a> &#149;
+<a href="qh-io.htm#TOC">Io</a> &#149; <a href="qh-mem.htm#TOC">Mem</a> &#149;
+<a href="qh-merge.htm#TOC">Merge</a> &#149; <a href="qh-poly.htm#TOC">Poly</a>
+&#149; <a href="qh-qhull.htm#TOC">Qhull</a> &#149; <b>Set</b>
+&#149; <a href="qh-stat.htm#TOC">Stat</a> &#149; <a href="qh-user.htm#TOC">User</a>
+</p>
+<h3>Index to <a href="qset.c">qset.c</a> and
+<a href="qset.h">qset.h</a></h3>
+<ul>
+<li><a href="#stype">Data types and constants</a> </li>
+<li><a href="#seach">FOREACH macros</a> </li>
+<li><a href="#saccess">access and size macros</a> </li>
+<li><a href="#sint">internal macros</a> </li>
+<li><a href="#saddr">address macros</a><p>&nbsp;</li>
+
+<li><a href="#snew">Allocation and deallocation functions</a> </li>
+<li><a href="#spred">Access and predicate functions</a>
+</li>
+<li><a href="#sadd">Add functions</a> </li>
+<li><a href="#scheck">Check and print functions</a></li>
+<li><a href="#scopy">Copy, compact, and zero functions</a></li>
+<li><a href="#sdel">Delete functions</a> </li>
+<li><a href="#stemp">Temporary set functions</a> </li>
+</ul>
+<h3><a href="qh-set.htm#TOC">&#187;</a><a name="stype">Data types and
+constants</a></h3>
+<ul>
+<li><a href="qset.h#SETelemsize">SETelemsize</a> size
+of a set element in bytes </li>
+<li><a href="qset.h#setT">setT</a> a set with a
+maximum size and a current size</li>
+<li><a href="libqhull.h#qh-set">qh global sets</a>
+global sets for temporary sets, etc. </li>
+</ul>
+<h3><a href="qh-set.htm#TOC">&#187;</a><a name="seach">FOREACH macros</a></h3>
+<ul>
+<li><a href="qset.h#FOREACHelem_">FOREACHelem_</a>
+assign 'elem' to each element in a set </li>
+<li><a href="qset.h#FOREACHset_">FOREACHset_</a>
+assign 'set' to each set in a set of sets </li>
+<li><a href="qset.h#FOREACHsetelement_">FOREACHsetelement_</a>
+define a FOREACH iterator </li>
+<li><a href="qset.h#FOREACHsetelement_i_">FOREACHsetelement_i_</a>
+define an indexed FOREACH iterator </li>
+<li><a href="qset.h#FOREACHsetelementreverse_">FOREACHsetelementreverse_</a>
+define a reversed FOREACH iterator </li>
+<li><a href="qset.h#FOREACHsetelementreverse12_">FOREACHsetelementreverse12_</a>
+define a FOREACH iterator with e[1] and e[0]
+reversed </li>
+</ul>
+<h3><a href="qh-set.htm#TOC">&#187;</a><a name="saccess">Access and
+size macros</a></h3>
+<ul>
+<li><a href="qset.h#SETelem_">SETelem_</a> return the
+n'th element of set </li>
+<li><a href="qset.h#SETelemt_">SETelemt_</a> return
+the n'th element of set as a type</li>
+<li><a href="qset.h#SETempty_">SETempty_</a> return
+true (1) if set is empty </li>
+<li><a href="qset.h#SETfirst_">SETfirst_</a> return
+first element of set </li>
+<li><a href="qset.h#SETfirstt_">SETfirstt_</a> return
+first element of set as a type</li>
+<li><a href="qset.h#SETindex_">SETindex_</a> return
+index of elem in set </li>
+<li><a href="qset.h#SETreturnsize_">SETreturnsize_</a>
+return size of a set (normally use <a href="qset.c#setsize">qh_setsize</a>) </li>
+<li><a href="qset.h#SETsecond_">SETsecond_</a> return
+second element of set </li>
+<li><a href="qset.h#SETsecondt_">SETsecondt_</a>
+return second element of set as a type</li>
+<li><a href="qset.h#SETtruncate_">SETtruncate_</a>
+truncate set to size, i.e., qh_settruncate()</li>
+</ul>
+<h3><a href="qh-set.htm#TOC">&#187;</a><a name="sint">Internal macros</a></h3>
+<ul>
+<li><a href="qset.c#SETsizeaddr_">SETsizeaddr_</a>
+return pointer to end element of a set (indicates
+current size) </li>
+</ul>
+
+<h3><a href="qh-set.htm#TOC">&#187;</a><a name="saddr">address macros</a></h3>
+<ul>
+<li><a href="qset.h#SETaddr_">SETaddr_</a> return
+address of a set's elements </li>
+<li><a href="qset.h#SETelemaddr_">SETelemaddr_</a>
+return address of the n'th element of a set </li>
+<li><a href="qset.h#SETref_">SETref_</a> l.h.s. for
+modifying the current element in a FOREACH
+iteration </li>
+</ul>
+
+<h3><a href="qh-set.htm#TOC">&#187;</a><a name="snew">Allocation and
+deallocation functions</a></h3>
+<ul>
+<li><a href="qset.c#setfree">qh_setfree</a> free the
+space occupied by a set </li>
+<li><a href="qset.c#setfree2">qh_setfree2</a> free a
+set and its elements </li>
+<li><a href="qset.c#setfreelong">qh_setfreelong</a>
+free a set only if it is in long memory </li>
+<li><a href="qset.c#setnew">qh_setnew</a> create a new
+set </li>
+</ul>
+
+<h3><a href="qh-set.htm#TOC">&#187;</a><a name="spred">Access and
+predicate functions </a></h3>
+<ul>
+<li><a href="qset.c#setendpointer">qh_setendpointer</a> return
+pointer to NULL terminator of a set</li>
+<li><a href="qset.c#setequal">qh_setequal</a> return 1
+if two sorted sets are equal </li>
+<li><a href="qset.c#setequal_except">qh_setequal_except</a>
+return 1 if two sorted sets are equal except for
+an element </li>
+<li><a href="qset.c#setequal_skip">qh_setequal_skip</a>
+return 1 if two sorted sets are equal except for
+a pair of skipped elements </li>
+<li><a href="qset.c#setequal_skip">qh_setequal_skip</a>
+return 1 if two sorted sets are equal except for
+a pair of skipped elements </li>
+<li><a href="qset.c#setin">qh_setin</a> return 1 if an
+element is in a set </li>
+<li><a href="qset.c#setindex">qh_setindex</a> return
+the index of an element in a set </li>
+<li><a href="qset.c#setlast">qh_setlast</a> return
+last element of a set</li>
+<li><a href="qset.c#setsize">qh_setsize</a> returns
+the size of a set </li>
+</ul>
+
+<h3><a href="qh-set.htm#TOC">&#187;</a><a name="sadd">Add functions</a></h3>
+<ul>
+<li><a href="qset.c#setaddnth">qh_setaddnth</a> add a
+element as n'th element of sorted or unsorted set
+</li>
+<li><a href="qset.c#setaddsorted">qh_setaddsorted</a>
+add an element to a sorted set </li>
+<li><a href="qset.c#setappend">qh_setappend</a> append
+an element to a set </li>
+<li><a href="qset.c#setappend_set">qh_setappend_set</a>
+append a set of elements to a set </li>
+<li><a href="qset.c#setappend2ndlast">qh_setappend2ndlast</a>
+add an element as the next to the last element in
+a set </li>
+<li><a href="qset.c#setlarger">qh_setlarger</a> return
+a larger set with the same elements</li>
+<li><a href="qset.c#setreplace">qh_setreplace</a>
+replace one element with another in a set</li>
+<li><a href="qset.c#setunique">qh_setunique</a> add an
+element if it is not already in a set </li>
+</ul>
+
+<h3><a href="qh-set.htm#TOC">&#187;</a><a name="scheck">Check and print functions</a></h3>
+<ul>
+<li><a href="qset.c#setcheck">qh_setcheck</a> check a
+set for validity </li>
+<li><a href="qset.c#setprint">qh_setprint</a> print a
+set's elements to fp </li>
+</ul>
+
+<h3><a href="qh-set.htm#TOC">&#187;</a><a name="scopy">Copy, compact, and zero functions</a></h3>
+<ul>
+<li><a href="qset.c#setcompact">qh_setcompact</a>
+compact NULLs from an unsorted set </li>
+<li><a href="qset.c#setcopy">qh_setcopy</a> make a
+copy of a sorted or unsorted set </li>
+<li><a href="qset.c#setduplicate">qh_setduplicate</a>
+duplicate a set and its elements </li>
+<li><a href="qset.c#settruncate">qh_settruncate</a>
+truncate a set to size elements </li>
+<li><a href="qset.c#setzero">qh_setzero</a> zero the
+remainder of a set </li>
+</ul>
+
+<h3><a href="qh-set.htm#TOC">&#187;</a><a name="sdel">Delete functions</a></h3>
+<ul>
+<li><a href="qset.c#setdel">qh_setdel</a> delete an
+element from an unsorted set. </li>
+<li><a href="qset.c#setdellast">qh_setdellast</a>
+delete and return last element from a set</li>
+<li><a href="qset.c#setdelnth">qh_setdelnth</a> delete
+and return nth element from an unsorted set </li>
+<li><a href="qset.c#setdelnthsorted">qh_setdelnthsorted</a>
+delete and return nth element from a sorted set </li>
+<li><a href="qset.c#setdelsorted">qh_setdelsorted</a>
+delete an element from a sorted set </li>
+<li><a href="qset.c#setnew_delnthsorted">qh_setnew_delnthsorted</a>
+create a sorted set not containing the nth
+element </li>
+</ul>
+
+<h3><a href="qh-set.htm#TOC">&#187;</a><a name="stemp">Temporary set functions</a></h3>
+<ul>
+<li><a href="qset.c#settemp">qh_settemp</a> return a
+temporary set and append it qhmem.tempstack</li>
+<li><a href="qset.c#settempfree">qh_settempfree</a>
+free and pop a set from qhmem.tempstack</li>
+<li><a href="qset.c#settempfree_all">qh_settempfree_all</a>
+free all sets in qhmem.tempstack </li>
+<li><a href="qset.c#settemppop">qh_settemppop</a> pop
+a set from qhmem.tempstack (makes it permanent) </li>
+<li><a href="qset.c#settemppush">qh_settemppush</a>
+push a set unto qhmem.tempstack (makes it
+temporary) </li>
+</ul>
+
+<p><!-- Navigation links --> </p>
+<hr>
+<p><b>Up:</b>
+<a href="http://www.qhull.org">Home page for
+Qhull</a> <br>
+<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual: Table of Contents</a> <br>
+<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a>
+&#149; <a href="../../html/qh-quick.htm#options">Options</a>
+&#149; <a href="../../html/qh-opto.htm#output">Output</a>
+&#149; <a href="../../html/qh-optf.htm#format">Formats</a>
+&#149; <a href="../../html/qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="../../html/qh-optp.htm#print">Print</a>
+&#149; <a href="../../html/qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="../../html/qh-optc.htm#prec">Precision</a>
+&#149; <a href="../../html/qh-optt.htm#trace">Trace</a>
+&#149; <a href="index.htm">Functions</a><br>
+<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a> <br>
+<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br>
+<b>To:</b> <a href="qh-geom.htm">Geom</a> &#149;
+<a href="qh-globa.htm">Global</a> &#149; <a href="qh-io.htm">Io</a>
+&#149; <a href="qh-mem.htm">Mem</a> &#149; <a href="qh-merge.htm">Merge</a>
+&#149; <a href="qh-poly.htm">Poly</a> &#149; <a href="qh-qhull.htm#TOC">Qhull</a>
+&#149; <a href="qh-set.htm">Set</a> &#149; <a href="qh-stat.htm">Stat</a>
+&#149; <a href="qh-user.htm">User</a><br>
+</p>
+<p><!-- GC common information --> </p>
+<hr>
+<p><a href="http://www.geom.uiuc.edu/"><img
+src="../../html/qh--geom.gif" align="middle" width="40" height="40"></a><i>The
+Geometry Center Home Page </i></p>
+<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a>
+</a><br>
+Created: May 2, 1997 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p>
+</body>
+</html>
diff --git a/xs/src/qhull/src/libqhull/qh-stat.htm b/xs/src/qhull/src/libqhull/qh-stat.htm
new file mode 100644
index 000000000..b96854031
--- /dev/null
+++ b/xs/src/qhull/src/libqhull/qh-stat.htm
@@ -0,0 +1,163 @@
+<!-- Do not edit with Front Page, it adds too many spaces -->
+<html>
+<head>
+<meta http-equiv="Content-Type"
+content="text/html; charset=iso-8859-1">
+<title>stat.c -- statistical operations</title>
+</head>
+
+<body>
+<!-- Navigation links -->
+<p><a name="TOP"><b>Up:</b></a> <a
+href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual</a>: Table of Contents <br>
+<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a>
+&#149; <a href="../../html/qh-quick.htm#options">Options</a>
+&#149; <a href="../../html/qh-opto.htm#output">Output</a>
+&#149; <a href="../../html/qh-optf.htm#format">Formats</a>
+&#149; <a href="../../html/qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="../../html/qh-optp.htm#print">Print</a>
+&#149; <a href="../../html/qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="../../html/qh-optc.htm#prec">Precision</a>
+&#149; <a href="../../html/qh-optt.htm#trace">Trace</a>
+&#149; <a href="index.htm">Functions</a><br>
+<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a><br>
+<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br>
+<b>To:</b> <a href="qh-geom.htm">Geom</a> &#149; <a href="qh-globa.htm">Global</a>
+&#149; <a href="qh-io.htm">Io</a> &#149; <a href="qh-mem.htm">Mem</a>
+&#149; <a href="qh-merge.htm">Merge</a> &#149; <a href="qh-poly.htm">Poly</a>
+&#149; <a href="qh-qhull.htm">Qhull</a> &#149; <a href="qh-set.htm">Set</a>
+&#149; <a href="qh-stat.htm#TOC">Stat</a> &#149; <a href="qh-user.htm">User</a>
+</p>
+<hr>
+
+<h2>stat.c -- statistical operations</h2>
+<blockquote>
+<p>Qhull records many statistics. These functions and
+macros make it inexpensive to add a statistic.
+<p>As with Qhull's global variables, the statistics data structure is
+accessed by a macro, 'qhstat'. If qh_QHpointer is defined, the macro
+is 'qh_qhstat->', otherwise the macro is 'qh_qhstat.'.
+Statistics
+may be turned off in user.h. If so, all but the 'zz'
+statistics are ignored.</p>
+</blockquote>
+<p><b>Copyright &copy; 1995-2015 C.B. Barber</b></p>
+<hr>
+<p><a href="#TOP">&#187;</a> <a href="qh-geom.htm#TOC">Geom</a>
+<a name="TOC">&#149;</a> <a href="qh-globa.htm#TOC">Global</a>
+&#149; <a href="qh-io.htm#TOC">Io</a> &#149; <a href="qh-mem.htm#TOC">Mem</a>
+&#149; <a href="qh-merge.htm#TOC">Merge</a> &#149; <a href="qh-poly.htm#TOC">Poly</a>
+&#149; <a href="qh-qhull.htm#TOC">Qhull</a> &#149; <a href="qh-set.htm#TOC">Set</a>
+&#149; <b>Stat</b> &#149; <a href="qh-user.htm#TOC">User</a>
+</p>
+<h3>Index to <a href="stat.c">stat.c</a> and
+<a href="stat.h">stat.h</a></h3>
+<ul>
+<li><a href="#ttype">stat.h types</a> </li>
+<li><a href="#tconst">stat.h constants</a> </li>
+<li><a href="#tmacro">stat.h macros</a> </li>
+<li><a href="#tfunc">stat.c functions</a> </li>
+</ul>
+
+<h3><a href="qh-stat.htm#TOC">&#187;</a><a name="ttype">stat.h types</a></h3>
+<ul>
+<li><a href="stat.h#intrealT">intrealT</a> union of
+integer and real</li>
+<li><a href="stat.h#qhstat">qhstat</a> global data
+structure for statistics </li>
+</ul>
+<h3><a href="qh-stat.htm#TOC">&#187;</a><a name="tconst">stat.h
+constants</a></h3>
+<ul>
+<li><a href="stat.h#KEEPstatistics">qh_KEEPstatistics</a> 0 turns off most statistics</li>
+<li><a href="stat.h#statistics">Z..., W...</a> integer (Z) and real (W) statistics
+</li>
+<li><a href="stat.h#ZZstat">ZZstat</a> Z.../W... statistics that
+remain defined if qh_KEEPstatistics=0
+</li>
+<li><a href="stat.h#ztype">ztype</a> zdoc, zinc, etc.
+for definining statistics </li>
+</ul>
+<h3><a href="qh-stat.htm#TOC">&#187;</a><a name="tmacro">stat.h macros</a></h3>
+<ul>
+<li><a href="stat.h#MAYdebugx">MAYdebugx</a> called
+frequently for error trapping </li>
+<li><a href="stat.h#zadd_">zadd_/wadd_</a> add value
+to an integer or real statistic </li>
+<li><a href="stat.h#zdef_">zdef_</a> define a
+statistic </li>
+<li><a href="stat.h#zinc_">zinc_</a> increment an
+integer statistic </li>
+<li><a href="stat.h#zmax_">zmax_/wmax_</a> update
+integer or real maximum statistic </li>
+<li><a href="stat.h#zmin_">zmin_/wmin_</a> update
+integer or real minimum statistic </li>
+<li><a href="stat.h#zval_">zval_/wval_</a> set or
+return value of a statistic </li>
+</ul>
+
+<h3><a href="qh-stat.htm#TOC">&#187;</a><a name="tfunc">stat.c
+functions</a></h3>
+<ul>
+<li><a href="stat.c#allstatA">qh_allstatA</a> define
+statistics in groups of 20 </li>
+<li><a href="stat.c#allstatistics">qh_allstatistics</a>
+reset printed flag for all statistics </li>
+<li><a href="stat.c#collectstatistics">qh_collectstatistics</a>
+collect statistics for qh.facet_list </li>
+<li><a href="stat.c#freestatistics">qh_freestatistics</a>
+free memory used for statistics </li>
+<li><a href="stat.c#initstatistics">qh_initstatistics</a>
+allocate and initialize statistics </li>
+<li><a href="stat.c#newstats">qh_newstats</a> returns
+True if statistics for zdoc </li>
+<li><a href="stat.c#nostatistic">qh_nostatistic</a>
+true if no statistic to print </li>
+<li><a href="stat.c#printallstatistics">qh_printallstatistics</a>
+print all statistics </li>
+<li><a href="stat.c#printstatistics">qh_printstatistics</a>
+print statistics to a file </li>
+<li><a href="stat.c#printstatlevel">qh_printstatlevel</a>
+print level information for a statistic </li>
+<li><a href="stat.c#printstats">qh_printstats</a>
+print statistics for a zdoc group </li>
+<li><a href="stat.c#stddev">qh_stddev</a> compute the
+standard deviation and average from statistics </li>
+</ul>
+
+<p><!-- Navigation links --> </p>
+<hr>
+<p><b>Up:</b>
+<a href="http://www.qhull.org">Home page for
+Qhull</a> <br>
+<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual: Table of Contents</a> <br>
+<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a>
+&#149; <a href="../../html/qh-quick.htm#options">Options</a>
+&#149; <a href="../../html/qh-opto.htm#output">Output</a>
+&#149; <a href="../../html/qh-optf.htm#format">Formats</a>
+&#149; <a href="../../html/qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="../../html/qh-optp.htm#print">Print</a>
+&#149; <a href="../../html/qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="../../html/qh-optc.htm#prec">Precision</a>
+&#149; <a href="../../html/qh-optt.htm#trace">Trace</a>
+&#149; <a href="index.htm">Functions</a><br>
+<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a> <br>
+<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br>
+<b>To:</b> <a href="qh-geom.htm">Geom</a> &#149;
+<a href="qh-globa.htm">Global</a> &#149; <a href="qh-io.htm">Io</a>
+&#149; <a href="qh-mem.htm">Mem</a> &#149; <a href="qh-merge.htm">Merge</a>
+&#149; <a href="qh-poly.htm">Poly</a> &#149; <a href="qh-qhull.htm#TOC">Qhull</a>
+&#149; <a href="qh-set.htm">Set</a> &#149; <a href="qh-stat.htm">Stat</a>
+&#149; <a href="qh-user.htm">User</a><br>
+</p>
+<p><!-- GC common information --> </p>
+<hr>
+<p><a href="http://www.geom.uiuc.edu/"><img
+src="../../html/qh--geom.gif" align="middle" width="40" height="40"></a><i>The
+Geometry Center Home Page </i></p>
+<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a>
+</a><br>
+Created: May 2, 1997 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p>
+</body>
+</html>
diff --git a/xs/src/qhull/src/libqhull/qh-user.htm b/xs/src/qhull/src/libqhull/qh-user.htm
new file mode 100644
index 000000000..6682f4b2f
--- /dev/null
+++ b/xs/src/qhull/src/libqhull/qh-user.htm
@@ -0,0 +1,271 @@
+<!-- Do not edit with Front Page, it adds too many spaces -->
+<html>
+<head>
+<meta http-equiv="Content-Type"
+content="text/html; charset=iso-8859-1">
+<title>user.c -- user-definable operations</title>
+</head>
+
+<body>
+<!-- Navigation links -->
+<p><a name="TOP"><b>Up:</b></a> <a
+href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual</a>: Table of Contents <br>
+<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a>
+&#149; <a href="../../html/qh-quick.htm#options">Options</a>
+&#149; <a href="../../html/qh-opto.htm#output">Output</a>
+&#149; <a href="../../html/qh-optf.htm#format">Formats</a>
+&#149; <a href="../../html/qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="../../html/qh-optp.htm#print">Print</a>
+&#149; <a href="../../html/qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="../../html/qh-optc.htm#prec">Precision</a>
+&#149; <a href="../../html/qh-optt.htm#trace">Trace</a>
+&#149; <a href="index.htm">Functions</a><br>
+<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a><br>
+<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br>
+<b>To:</b> <a href="qh-geom.htm">Geom</a> &#149; <a href="qh-globa.htm">Global</a>
+&#149; <a href="qh-io.htm">Io</a> &#149; <a href="qh-mem.htm">Mem</a>
+&#149; <a href="qh-merge.htm">Merge</a> &#149; <a href="qh-poly.htm">Poly</a>
+&#149; <a href="qh-qhull.htm">Qhull</a> &#149; <a href="qh-set.htm">Set</a>
+&#149; <a href="qh-stat.htm">Stat</a> &#149; <a href="qh-user.htm#TOC">User</a>
+</p>
+<hr>
+<h2>user.c -- user-definable operations</h2>
+<blockquote>
+<p>This section contains functions and constants that the
+user may want to change. </p>
+
+</blockquote>
+<p><b>Copyright &copy; 1995-2015 C.B. Barber</b></p>
+<hr>
+<p><a href="#TOP">&#187;</a> <a href="qh-geom.htm#TOC">Geom</a>
+<a name="TOC">&#149;</a> <a href="qh-globa.htm#TOC">Global</a>
+&#149; <a href="qh-io.htm#TOC">Io</a> &#149; <a href="qh-mem.htm#TOC">Mem</a>
+&#149; <a href="qh-merge.htm#TOC">Merge</a> &#149; <a href="qh-poly.htm#TOC">Poly</a>
+&#149; <a href="qh-qhull.htm#TOC">Qhull</a> &#149; <a href="qh-set.htm#TOC">Set</a>
+&#149; <a href="qh-stat.htm#TOC">Stat</a> &#149; <b>User</b>
+</p>
+<h3>Index to <a href="user.c">user.c</a>, <a href="usermem.c">usermem.c</a>, <a href="userprintf.c">userprintf.c</a>, <a href="userprintf_rbox.c">userprintf_rbox.c</a> and
+<a href="user.h">user.h</a></h3>
+<ul>
+<li><a href="#qulllib">qhull library constants</a></li>
+<li><a href="#utype">user.h data types and
+configuration macros</a> </li>
+<li><a href="#ujoggle">joggle constants</a></li>
+<li><a href="#uperform">performance related constants</a></li>
+<li><a href="#umemory">memory constants</a></li>
+<li><a href="#ucond">conditional compilation</a></li>
+<li><a href="#umerge">merge constants</a> </li>
+<li><a href="#ufunc">user.c functions</a> </li>
+<li><a href="#u2func">usermem.c functions</a> </li>
+<li><a href="#u3func">userprintf.c functions</a> </li>
+</ul>
+
+<h3><a href="qh-user.htm#TOC">&#187;</a><a name="qulllib">Qhull library constants</a></h3>
+<ul>
+<li><a href="user.h#filenamelen">FILENAMElen</a> -- max length of TI or TO filename </li>
+<li><a href="user.h#msgcode">msgcode</a> -- unique message codes for qh_fprintf </li>
+<li><a href="user.h#qh_OPTIONline">qh_OPTIONline</a> -- max length of option line ('FO')</li>
+</ul>
+
+
+<h3><a href="qh-user.htm#TOC">&#187;</a><a name="utype">user.h data
+types and configuration macros</a></h3>
+<ul>
+<li><a href="user.h#realT">realT, qh_REAL...</a> size
+of floating point numbers </li>
+<li><a href="user.h#countT">countT, COUNTmax</a> size
+of counts and identifiers, typically 'int' or 'long long' </li>
+<li><a href="user.h#CPUclock">qh_CPUclock</a> clock()
+function for reporting the total time spent by
+Qhull </li>
+<li><a href="user.h#RANDOM">qh_RANDOM...</a> random
+number generator </li>
+</ul>
+
+<h3><a href="qh-user.htm#TOC">&#187;</a><a name="udef">definition constants</a></h3>
+<ul>
+<li><a href="user.h#DEFAULTbox">qh_DEFAULTbox</a>
+define default box size for rbox, 'Qbb', and 'QbB' (Geomview expects 0.5) </li>
+<li><a href="user.h#INFINITE">qh_INFINITE</a> on
+output, indicates Voronoi center at infinity </li>
+<li><a href="user.h#ORIENTclock">qh_ORIENTclock</a>
+define convention for orienting facets</li>
+<li><a href="user.h#ZEROdelaunay">qh_ZEROdelaunay</a>
+define facets that are ignored in Delaunay triangulations</li>
+</ul>
+
+<h3><a href="qh-user.htm#TOC">&#187;</a><a name="ujoggle">joggle constants</a></h3>
+<ul>
+<li><a href="user.h#JOGGLEagain">qh_JOGGLEagain</a>
+how often to retry before using qh_JOGGLEmaxincrease
+again </li>
+<li><a href="user.h#JOGGLEdefault">qh_JOGGLEdefault</a>
+default value for qh.JOGGLEmax for 'QP' </li>
+<li><a href="user.h#JOGGLEincrease">qh_JOGGLEincrease</a>
+factor to increase qh.JOGGLEmax on retrys for
+'QPn' </li>
+<li><a href="user.h#JOGGLEmaxincrease">qh_JOGGLEmaxincrease</a> max
+for increasing qh.JOGGLEmax relative to
+qh.MAXwidth </li>
+<li><a href="user.h#JOGGLEretry">qh_JOGGLEmaxretry</a>
+report error if this many retries </li>
+<li><a href="user.h#JOGGLEretry">qh_JOGGLEretry</a>
+how often to retry before using qh_JOGGLEmax </li>
+</ul>
+
+<h3><a href="qh-user.htm#TOC">&#187;</a><a name="uperform">performance
+related constants</a></h3>
+<ul>
+<li><a href="user.h#HASHfactor">qh_HASHfactor</a>
+total/used hash slots </li>
+<li><a href="user.h#INITIALmax">qh_INITIALmax</a> if
+dim &gt;= qh_INITIALmax, use min/max coordinate
+points for initial simplex </li>
+<li><a href="user.h#INITIALsearch">qh_INITIALsearch</a>
+if qh.INITIALmax, search points up to this
+dimension </li>
+<li><a href="user.h#NOtrace">qh_NOtrace</a> disallow
+tracing </li>
+<li><a href="user.h#VERIFYdirect">qh_VERIFYdirect</a>
+'Tv' verifies all <em>points X facets</em> if op
+count is smaller </li>
+</ul>
+
+<h3><a href="qh-user.htm#TOC">&#187;</a><a name="umemory">memory constants</a></h3>
+<ul>
+<li><a href="user.h#MEMalign">qh_MEMalign</a> memory
+alignment for qh_meminitbuffers() in global.c </li>
+<li><a href="user.h#MEMbufsize">qh_MEMbufsize</a>
+size of additional memory buffers </li>
+<li><a href="user.h#MEMinitbuf">qh_MEMinitbuf</a>
+size of initial memory buffer </li>
+</ul>
+
+<h3><a href="qh-user.htm#TOC">&#187;</a><a name="ucond">conditional compilation</a></h3>
+<ul>
+<li><a href="user.h#compiler">compiler</a> defined symbols,
+e.g., _STDC_ and _cplusplus
+
+<li><a href="user.h#COMPUTEfurthest">qh_COMPUTEfurthest</a>
+ compute furthest distance to an outside point instead of storing it with the facet
+<li><a href="user.h#KEEPstatistics">qh_KEEPstatistics</a>
+ enable statistic gathering and reporting with option 'Ts'
+<li><a href="user.h#MAXoutside">qh_MAXoutside</a>
+record outer plane for each facet
+<li><a href="user.h#NOmerge">qh_NOmerge</a>
+disable facet merging
+<li><a href="user.h#NOtrace">qh_NOtrace</a>
+disable tracing with option 'T4'
+<li><a href="user.h#QHpointer">qh_QHpointer</a>
+access global data with pointer or static structure
+<li><a href="user.h#QUICKhelp">qh_QUICKhelp</a>
+use abbreviated help messages, e.g., for degenerate inputs
+</ul>
+
+<h3><a href="qh-user.htm#TOC">&#187;</a><a name="umerge">merge
+constants</a></h3>
+<ul>
+<li><a href="user.h#BESTcentrum">qh_BESTcentrum</a>
+when does qh_findbestneighbor() test centrums? </li>
+<li><a href="user.h#BESTnonconvex">qh_BESTnonconvex</a>
+when does qh_findbestneighbor() test nonconvex
+ridges only? </li>
+<li><a href="user.h#COPLANARratio">qh_COPLANARratio</a>
+what is qh.MINvisible? </li>
+<li><a href="user.h#DIMreduceBuild">qh_DIMreduceBuild</a>
+max dimension for vertex reduction </li>
+<li><a href="user.h#DIMmergeVertex">qh_DIMmergeVertex</a>
+max dimension for vertex merging </li>
+<li><a href="user.h#DISToutside">qh_DISToutside</a>
+when is a point clearly outside of a facet for qh_findbestnew and qh_partitionall</li>
+<li><a href="user.h#MAXnarrow">qh_MAXnarrow</a> max.
+cosine for qh.NARROWhull </li>
+<li><a href="user.h#MAXnewcentrum">qh_MAXnewcentrum</a>
+when does qh_reducevertices_centrum() reset the
+centrum? </li>
+<li><a href="user.h#MAXnewmerges">qh_MAXnewmerges</a>
+when does qh_merge_nonconvex() call
+qh_reducevertices_centrums? </li>
+<li><a href="user.h#RATIOnearinside">qh_RATIOnearinside</a>
+ratio for retaining inside points for
+qh_check_maxout() </li>
+<li><a href="user.h#SEARCHdist">qh_SEARCHdist</a>
+when is facet coplanar with the best facet for qh_findbesthorizon</li>
+<li><a href="user.h#USEfindbestnew">qh_USEfindbestnew</a>
+when to use qh_findbestnew for qh_partitionpoint()</li>
+<li><a href="user.h#WARNnarrow">qh_WARNnarrow</a>
+max. cosine to warn about qh.NARROWhull </li>
+<li><a href="user.h#WIDEcoplanar">qh_WIDEcoplanar</a>
+what is a wide facet? </li>
+<li><a href="user.h#WIDEduplicate">qh_WIDEduplicate</a>
+what is a wide ratio on merging duplicate ridges? </li>
+</ul>
+
+<h3><a href="qh-user.htm#TOC">&#187;</a><a name="ufunc">user.c
+functions</a></h3>
+<ul>
+<li><a href="user.c#qhull_template">Qhull template</a> for calling qh_new_qhull from your program</li>
+<li><a href="user.c#errexit">qh_errexit</a> report
+error and exit qhull()</li>
+<li><a href="user.c#errprint">qh_errprint</a> print
+information about facets and ridges </li>
+<li><a href="user.c#new_qhull">qh_new_qhull</a> call qhull on an array
+of points</li>
+<li><a href="user.c#printfacetlist">qh_printfacetlist</a>
+print all fields of all facets </li>
+</ul>
+
+<h3><a href="qh-user.htm#TOC">&#187;</a><a name="u2func">usermem.c
+functions</a></h3>
+<ul>
+<li><a href="usermem.c#qh_exit">qh_exit</a> exit program, same as exit(). May be redefined as throw "QH10003.." by libqhullcpp/usermem_r-cpp.cpp</li>
+<li><a href="usermem.c#qh_fprintf_stderr">qh_fprintf_stderr</a> print to stderr when qh.ferr is not defined.</li>
+<li><a href="usermem.c#qh_free">qh_free</a> free memory, same as free().</li>
+<li><a href="usermem.c#qh_malloc">qh_malloc</a> allocate memory, same as malloc()</li>
+</ul>
+
+<h3><a href="qh-user.htm#TOC">&#187;</a><a name="u3func">userprintf.c
+ and userprintf_rbox,c functions</a></h3>
+<ul>
+<li><a href="userprintf.c#qh_fprintf">qh_fprintf</a> print
+information from Qhull, sames as fprintf(). </li>
+<li><a href="userprintf_rbox.c#qh_fprintf_rbox">qh_fprintf_rbox</a> print
+information from Rbox, sames as fprintf(). </li>
+</ul>
+
+<p><!-- Navigation links --> </p>
+<hr>
+<p><b>Up:</b>
+<a href="http://www.qhull.org">Home page for
+Qhull</a> <br>
+<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual: Table of Contents</a> <br>
+<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a>
+&#149; <a href="../../html/qh-quick.htm#options">Options</a>
+&#149; <a href="../../html/qh-opto.htm#output">Output</a>
+&#149; <a href="../../html/qh-optf.htm#format">Formats</a>
+&#149; <a href="../../html/qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="../../html/qh-optp.htm#print">Print</a>
+&#149; <a href="../../html/qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="../../html/qh-optc.htm#prec">Precision</a>
+&#149; <a href="../../html/qh-optt.htm#trace">Trace</a>
+&#149; <a href="index.htm">Functions</a><br>
+<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a> <br>
+<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br>
+<b>To:</b> <a href="qh-geom.htm">Geom</a> &#149;
+<a href="qh-globa.htm">Global</a> &#149; <a href="qh-io.htm">Io</a>
+&#149; <a href="qh-mem.htm">Mem</a> &#149; <a href="qh-merge.htm">Merge</a>
+&#149; <a href="qh-poly.htm">Poly</a> &#149; <a href="qh-qhull.htm#TOC">Qhull</a>
+&#149; <a href="qh-set.htm">Set</a> &#149; <a href="qh-stat.htm">Stat</a>
+&#149; <a href="qh-user.htm">User</a><br>
+</p>
+<p><!-- GC common information --> </p>
+<hr>
+<p><a href="http://www.geom.uiuc.edu/"><img
+src="../../html/qh--geom.gif" align="middle" width="40" height="40"></a><i>The
+Geometry Center Home Page </i></p>
+<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a>
+</a><br>
+Created: May 2, 1997 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p>
+</body>
+</html>
diff --git a/xs/src/qhull/src/libqhull/qhull-exports.def b/xs/src/qhull/src/libqhull/qhull-exports.def
new file mode 100644
index 000000000..11a42b57e
--- /dev/null
+++ b/xs/src/qhull/src/libqhull/qhull-exports.def
@@ -0,0 +1,417 @@
+; qhull-exports.def -- msvc module-definition file
+;
+; Generated from depends.exe by cut-and-paste of exported symbols by mingw gcc
+; [mar'11] 399 symbols
+; Annotate as DATA qh_last_random qh_qh qh_qhstat qhmem rbox rbox_inuse
+; Annotate as __declspec for outside access in win32 -- qh_qh qh_qhstat
+; Same as ../libqhullp/qhull_p-exports.def without qh_save_qhull and qh_restore_qhull
+;
+; $Id: //main/2015/qhull/src/libqhull/qhull-exports.def#3 $$Change: 2047 $
+; $DateTime: 2016/01/04 22:03:18 $$Author: bbarber $
+;
+; Define qhull_VERSION in CMakeLists.txt, Makefile, qhull-exports.def, qhull_p-exports.def, qhull_r-exports.def, and qhull-warn.pri
+VERSION 7.0
+EXPORTS
+qh_addhash
+qh_addpoint
+qh_all_merges
+qh_allstatA
+qh_allstatB
+qh_allstatC
+qh_allstatD
+qh_allstatE
+qh_allstatE2
+qh_allstatF
+qh_allstatG
+qh_allstatH
+qh_allstatI
+qh_allstatistics
+qh_appendfacet
+qh_appendmergeset
+qh_appendprint
+qh_appendvertex
+qh_argv_to_command
+qh_argv_to_command_size
+qh_attachnewfacets
+qh_backnormal
+qh_basevertices
+qh_build_withrestart
+qh_buildhull
+qh_buildtracing
+qh_check_bestdist
+qh_check_dupridge
+qh_check_maxout
+qh_check_output
+qh_check_point
+qh_check_points
+qh_checkconnect
+qh_checkconvex
+qh_checkfacet
+qh_checkflags
+qh_checkflipped
+qh_checkflipped_all
+qh_checkpolygon
+qh_checkvertex
+qh_checkzero
+qh_clear_outputflags
+qh_clearcenters
+qh_clock
+qh_collectstatistics
+qh_compare_facetarea
+qh_compare_facetmerge
+qh_compare_facetvisit
+qh_compare_vertexpoint
+qh_compareangle
+qh_comparemerge
+qh_comparevisit
+qh_copyfilename
+qh_copynonconvex
+qh_copypoints
+qh_countfacets
+qh_createsimplex
+qh_crossproduct
+qh_degen_redundant_facet
+qh_degen_redundant_neighbors
+qh_deletevisible
+qh_delfacet
+qh_delridge
+qh_delvertex
+qh_determinant
+qh_detjoggle
+qh_detroundoff
+qh_detsimplex
+qh_detvnorm
+qh_detvridge
+qh_detvridge3
+qh_dfacet
+qh_distnorm
+qh_distplane
+qh_distround
+qh_divzero
+qh_dvertex
+qh_eachvoronoi
+qh_eachvoronoi_all
+qh_errexit
+qh_errexit2
+qh_errexit_rbox
+qh_errprint
+qh_exit
+qh_facet2point
+qh_facet3vertex
+qh_facetarea
+qh_facetarea_simplex
+qh_facetcenter
+qh_facetintersect
+qh_facetvertices
+qh_find_newvertex
+qh_findbest
+qh_findbest_test
+qh_findbestfacet
+qh_findbesthorizon
+qh_findbestlower
+qh_findbestneighbor
+qh_findbestnew
+qh_findfacet_all
+qh_findgood
+qh_findgood_all
+qh_findgooddist
+qh_findhorizon
+qh_flippedmerges
+qh_forcedmerges
+qh_fprintf
+qh_fprintf_rbox
+qh_fprintf_stderr
+qh_free
+qh_freebuffers
+qh_freebuild
+qh_freeqhull
+qh_freeqhull2
+qh_freestatistics
+qh_furthestnext
+qh_furthestout
+qh_gausselim
+qh_geomplanes
+qh_getangle
+qh_getarea
+qh_getcenter
+qh_getcentrum
+qh_getdistance
+qh_gethash
+qh_getmergeset
+qh_getmergeset_initial
+qh_gram_schmidt
+qh_hashridge
+qh_hashridge_find
+qh_infiniteloop
+qh_init_A
+qh_init_B
+qh_init_qhull_command
+qh_initbuild
+qh_initflags
+qh_initialhull
+qh_initialvertices
+qh_initqhull_buffers
+qh_initqhull_globals
+qh_initqhull_mem
+qh_initqhull_outputflags
+qh_initqhull_start
+qh_initqhull_start2
+qh_initstatistics
+qh_initthresholds
+qh_inthresholds
+qh_isvertex
+qh_joggleinput
+; Mark as DATA, otherwise links a separate qh_last_random. No __declspec.
+qh_last_random DATA
+qh_lib_check
+qh_makenew_nonsimplicial
+qh_makenew_simplicial
+qh_makenewfacet
+qh_makenewfacets
+qh_makenewplanes
+qh_makeridges
+qh_malloc
+qh_mark_dupridges
+qh_markkeep
+qh_markvoronoi
+qh_matchduplicates
+qh_matchneighbor
+qh_matchnewfacets
+qh_matchvertices
+qh_maxabsval
+qh_maxmin
+qh_maxouter
+qh_maxsimplex
+qh_maydropneighbor
+qh_memalloc
+qh_memfree
+qh_memfreeshort
+qh_meminit
+qh_meminitbuffers
+qh_memsetup
+qh_memsize
+qh_memstatistics
+qh_memtotal
+qh_merge_degenredundant
+qh_merge_nonconvex
+qh_mergecycle
+qh_mergecycle_all
+qh_mergecycle_facets
+qh_mergecycle_neighbors
+qh_mergecycle_ridges
+qh_mergecycle_vneighbors
+qh_mergefacet
+qh_mergefacet2d
+qh_mergeneighbors
+qh_mergeridges
+qh_mergesimplex
+qh_mergevertex_del
+qh_mergevertex_neighbors
+qh_mergevertices
+qh_minabsval
+qh_mindiff
+qh_nearcoplanar
+qh_nearvertex
+qh_neighbor_intersections
+qh_new_qhull
+qh_newfacet
+qh_newhashtable
+qh_newridge
+qh_newstats
+qh_newvertex
+qh_newvertices
+qh_nextfurthest
+qh_nextridge3d
+qh_normalize
+qh_normalize2
+qh_nostatistic
+qh_option
+qh_order_vertexneighbors
+qh_orientoutside
+qh_out1
+qh_out2n
+qh_out3n
+qh_outcoplanar
+qh_outerinner
+qh_partitionall
+qh_partitioncoplanar
+qh_partitionpoint
+qh_partitionvisible
+qh_point
+qh_point_add
+qh_pointdist
+qh_pointfacet
+qh_pointid
+qh_pointvertex
+qh_postmerge
+qh_precision
+qh_premerge
+qh_prepare_output
+qh_prependfacet
+qh_printafacet
+qh_printallstatistics
+qh_printbegin
+qh_printcenter
+qh_printcentrum
+qh_printend
+qh_printend4geom
+qh_printextremes
+qh_printextremes_2d
+qh_printextremes_d
+qh_printfacet
+qh_printfacet2geom
+qh_printfacet2geom_points
+qh_printfacet2math
+qh_printfacet3geom_nonsimplicial
+qh_printfacet3geom_points
+qh_printfacet3geom_simplicial
+qh_printfacet3math
+qh_printfacet3vertex
+qh_printfacet4geom_nonsimplicial
+qh_printfacet4geom_simplicial
+qh_printfacetNvertex_nonsimplicial
+qh_printfacetNvertex_simplicial
+qh_printfacetheader
+qh_printfacetlist
+qh_printfacetridges
+qh_printfacets
+qh_printhashtable
+qh_printhelp_degenerate
+qh_printhelp_narrowhull
+qh_printhelp_singular
+qh_printhyperplaneintersection
+qh_printline3geom
+qh_printlists
+qh_printmatrix
+qh_printneighborhood
+qh_printpoint
+qh_printpoint3
+qh_printpointid
+qh_printpoints
+qh_printpoints_out
+qh_printpointvect
+qh_printpointvect2
+qh_printridge
+qh_printspheres
+qh_printstatistics
+qh_printstatlevel
+qh_printstats
+qh_printsummary
+qh_printvdiagram
+qh_printvdiagram2
+qh_printvertex
+qh_printvertexlist
+qh_printvertices
+qh_printvneighbors
+qh_printvnorm
+qh_printvoronoi
+qh_printvridge
+qh_produce_output
+qh_produce_output2
+qh_projectdim3
+qh_projectinput
+qh_projectpoint
+qh_projectpoints
+; Mark as DATA, otherwise links a separate qh_qh. qh_qh and qh_qhstat requires __declspec
+qh_qh DATA
+qh_qhstat DATA
+qh_qhull
+qh_rand
+qh_randomfactor
+qh_randommatrix
+qh_rboxpoints
+qh_readfeasible
+qh_readpoints
+qh_reducevertices
+qh_redundant_vertex
+qh_remove_extravertices
+qh_removefacet
+qh_removevertex
+qh_rename_sharedvertex
+qh_renameridgevertex
+qh_renamevertex
+qh_resetlists
+qh_rotateinput
+qh_rotatepoints
+qh_roundi
+qh_scaleinput
+qh_scalelast
+qh_scalepoints
+qh_setaddnth
+qh_setaddsorted
+qh_setappend
+qh_setappend2ndlast
+qh_setappend_set
+qh_setcheck
+qh_setcompact
+qh_setcopy
+qh_setdel
+qh_setdelaunay
+qh_setdellast
+qh_setdelnth
+qh_setdelnthsorted
+qh_setdelsorted
+qh_setduplicate
+qh_setequal
+qh_setequal_except
+qh_setequal_skip
+qh_setfacetplane
+qh_setfeasible
+qh_setfree
+qh_setfree2
+qh_setfreelong
+qh_sethalfspace
+qh_sethalfspace_all
+qh_sethyperplane_det
+qh_sethyperplane_gauss
+qh_setin
+qh_setindex
+qh_setlarger
+qh_setlast
+qh_setnew
+qh_setnew_delnthsorted
+qh_setprint
+qh_setreplace
+qh_setsize
+qh_settemp
+qh_settempfree
+qh_settempfree_all
+qh_settemppop
+qh_settemppush
+qh_settruncate
+qh_setunique
+qh_setvoronoi_all
+qh_setzero
+qh_sharpnewfacets
+qh_skipfacet
+qh_skipfilename
+qh_srand
+qh_stddev
+qh_strtod
+qh_strtol
+qh_test_appendmerge
+qh_test_vneighbors
+qh_tracemerge
+qh_tracemerging
+qh_triangulate
+qh_triangulate_facet
+qh_triangulate_link
+qh_triangulate_mirror
+qh_triangulate_null
+qh_updatetested
+qh_updatevertices
+qh_user_memsizes
+qh_version
+qh_version2
+qh_vertexintersect
+qh_vertexintersect_new
+qh_vertexneighbors
+qh_vertexridges
+qh_vertexridges_facet
+qh_vertexsubset
+qh_voronoi_center
+qh_willdelete
+; Mark as DATA, otherwise links a separate qhmem. No __declspec
+qhmem DATA
+rbox DATA
+rbox_inuse DATA
diff --git a/xs/src/qhull/src/libqhull/qhull_a.h b/xs/src/qhull/src/libqhull/qhull_a.h
new file mode 100644
index 000000000..729b72327
--- /dev/null
+++ b/xs/src/qhull/src/libqhull/qhull_a.h
@@ -0,0 +1,150 @@
+/*<html><pre> -<a href="qh-qhull.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ qhull_a.h
+ all header files for compiling qhull with non-reentrant code
+ included before C++ headers for user_r.h:QHULL_CRTDBG
+
+ see qh-qhull.htm
+
+ see libqhull.h for user-level definitions
+
+ see user.h for user-definable constants
+
+ defines internal functions for libqhull.c global.c
+
+ Copyright (c) 1993-2015 The Geometry Center.
+ $Id: //main/2015/qhull/src/libqhull/qhull_a.h#4 $$Change: 2064 $
+ $DateTime: 2016/01/18 12:36:08 $$Author: bbarber $
+
+ Notes: grep for ((" and (" to catch fprintf("lkasdjf");
+ full parens around (x?y:z)
+ use '#include "libqhull/qhull_a.h"' to avoid name clashes
+*/
+
+#ifndef qhDEFqhulla
+#define qhDEFqhulla 1
+
+#include "libqhull.h" /* Includes user_r.h and data types */
+
+#include "stat.h"
+#include "random.h"
+#include "mem.h"
+#include "qset.h"
+#include "geom.h"
+#include "merge.h"
+#include "poly.h"
+#include "io.h"
+
+#include <setjmp.h>
+#include <string.h>
+#include <math.h>
+#include <float.h> /* some compilers will not need float.h */
+#include <limits.h>
+#include <time.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+/*** uncomment here and qset.c
+ if string.h does not define memcpy()
+#include <memory.h>
+*/
+
+#if qh_CLOCKtype == 2 /* defined in user.h from libqhull.h */
+#include <sys/types.h>
+#include <sys/times.h>
+#include <unistd.h>
+#endif
+
+#ifdef _MSC_VER /* Microsoft Visual C++ -- warning level 4 */
+#pragma warning( disable : 4100) /* unreferenced formal parameter */
+#pragma warning( disable : 4127) /* conditional expression is constant */
+#pragma warning( disable : 4706) /* assignment within conditional function */
+#pragma warning( disable : 4996) /* function was declared deprecated(strcpy, localtime, etc.) */
+#endif
+
+/* ======= -macros- =========== */
+
+/*-<a href="qh-qhull.htm#TOC"
+ >--------------------------------</a><a name="traceN">-</a>
+
+ traceN((qh ferr, 0Nnnn, "format\n", vars));
+ calls qh_fprintf if qh.IStracing >= N
+
+ Add debugging traps to the end of qh_fprintf
+
+ notes:
+ removing tracing reduces code size but doesn't change execution speed
+*/
+#ifndef qh_NOtrace
+#define trace0(args) {if (qh IStracing) qh_fprintf args;}
+#define trace1(args) {if (qh IStracing >= 1) qh_fprintf args;}
+#define trace2(args) {if (qh IStracing >= 2) qh_fprintf args;}
+#define trace3(args) {if (qh IStracing >= 3) qh_fprintf args;}
+#define trace4(args) {if (qh IStracing >= 4) qh_fprintf args;}
+#define trace5(args) {if (qh IStracing >= 5) qh_fprintf args;}
+#else /* qh_NOtrace */
+#define trace0(args) {}
+#define trace1(args) {}
+#define trace2(args) {}
+#define trace3(args) {}
+#define trace4(args) {}
+#define trace5(args) {}
+#endif /* qh_NOtrace */
+
+/*-<a href="qh-qhull.htm#TOC"
+ >--------------------------------</a><a name="QHULL_UNUSED">-</a>
+
+ Define an unused variable to avoid compiler warnings
+
+ Derived from Qt's corelib/global/qglobal.h
+
+*/
+
+#if defined(__cplusplus) && defined(__INTEL_COMPILER) && !defined(QHULL_OS_WIN)
+template <typename T>
+inline void qhullUnused(T &x) { (void)x; }
+# define QHULL_UNUSED(x) qhullUnused(x);
+#else
+# define QHULL_UNUSED(x) (void)x;
+#endif
+
+/***** -libqhull.c prototypes (alphabetical after qhull) ********************/
+
+void qh_qhull(void);
+boolT qh_addpoint(pointT *furthest, facetT *facet, boolT checkdist);
+void qh_buildhull(void);
+void qh_buildtracing(pointT *furthest, facetT *facet);
+void qh_build_withrestart(void);
+void qh_errexit2(int exitcode, facetT *facet, facetT *otherfacet);
+void qh_findhorizon(pointT *point, facetT *facet, int *goodvisible,int *goodhorizon);
+pointT *qh_nextfurthest(facetT **visible);
+void qh_partitionall(setT *vertices, pointT *points,int npoints);
+void qh_partitioncoplanar(pointT *point, facetT *facet, realT *dist);
+void qh_partitionpoint(pointT *point, facetT *facet);
+void qh_partitionvisible(boolT allpoints, int *numpoints);
+void qh_precision(const char *reason);
+void qh_printsummary(FILE *fp);
+
+/***** -global.c internal prototypes (alphabetical) ***********************/
+
+void qh_appendprint(qh_PRINT format);
+void qh_freebuild(boolT allmem);
+void qh_freebuffers(void);
+void qh_initbuffers(coordT *points, int numpoints, int dim, boolT ismalloc);
+
+/***** -stat.c internal prototypes (alphabetical) ***********************/
+
+void qh_allstatA(void);
+void qh_allstatB(void);
+void qh_allstatC(void);
+void qh_allstatD(void);
+void qh_allstatE(void);
+void qh_allstatE2(void);
+void qh_allstatF(void);
+void qh_allstatG(void);
+void qh_allstatH(void);
+void qh_freebuffers(void);
+void qh_initbuffers(coordT *points, int numpoints, int dim, boolT ismalloc);
+
+#endif /* qhDEFqhulla */
diff --git a/xs/src/qhull/src/libqhull/qhull_p-exports.def b/xs/src/qhull/src/libqhull/qhull_p-exports.def
new file mode 100644
index 000000000..cadf8a4fa
--- /dev/null
+++ b/xs/src/qhull/src/libqhull/qhull_p-exports.def
@@ -0,0 +1,418 @@
+; qhull_p-exports.def -- msvc module-definition file
+;
+; Generated from depends.exe by cut-and-paste of exported symbols by mingw gcc
+; [mar'11] 399 symbols [jan'15] added 3 symbols
+; Annotate as DATA qh_last_random qh_qh qh_qhstat qhmem rbox rbox_inuse
+; Annotate as __declspec for outside access in win32 -- qh_qh qh_qhstat
+;
+; $Id: //main/2011/qhull/src/libqhull/qhull-exports.def#2 $$Change: 1368 $
+; $DateTime: 2011/04/16 08:12:32 $$Author: bbarber $
+;
+; Define qhull_VERSION in CMakeLists.txt, Makefile, qhull-exports.def, qhull_p-exports.def, qhull_r-exports.def, and qhull-warn.pri
+VERSION 7.0
+EXPORTS
+qh_addhash
+qh_addpoint
+qh_all_merges
+qh_allstatA
+qh_allstatB
+qh_allstatC
+qh_allstatD
+qh_allstatE
+qh_allstatE2
+qh_allstatF
+qh_allstatG
+qh_allstatH
+qh_allstatI
+qh_allstatistics
+qh_appendfacet
+qh_appendmergeset
+qh_appendprint
+qh_appendvertex
+qh_argv_to_command
+qh_argv_to_command_size
+qh_attachnewfacets
+qh_backnormal
+qh_basevertices
+qh_build_withrestart
+qh_buildhull
+qh_buildtracing
+qh_check_bestdist
+qh_check_dupridge
+qh_check_maxout
+qh_check_output
+qh_check_point
+qh_check_points
+qh_checkconnect
+qh_checkconvex
+qh_checkfacet
+qh_checkflags
+qh_checkflipped
+qh_checkflipped_all
+qh_checkpolygon
+qh_checkvertex
+qh_checkzero
+qh_clear_outputflags
+qh_clearcenters
+qh_clock
+qh_collectstatistics
+qh_compare_facetarea
+qh_compare_facetmerge
+qh_compare_facetvisit
+qh_compare_vertexpoint
+qh_compareangle
+qh_comparemerge
+qh_comparevisit
+qh_copyfilename
+qh_copynonconvex
+qh_copypoints
+qh_countfacets
+qh_createsimplex
+qh_crossproduct
+qh_degen_redundant_facet
+qh_degen_redundant_neighbors
+qh_deletevisible
+qh_delfacet
+qh_delridge
+qh_delvertex
+qh_determinant
+qh_detjoggle
+qh_detroundoff
+qh_detsimplex
+qh_detvnorm
+qh_detvridge
+qh_detvridge3
+qh_dfacet
+qh_distnorm
+qh_distplane
+qh_distround
+qh_divzero
+qh_dvertex
+qh_eachvoronoi
+qh_eachvoronoi_all
+qh_errexit
+qh_errexit2
+qh_errexit_rbox
+qh_errprint
+qh_exit
+qh_facet2point
+qh_facet3vertex
+qh_facetarea
+qh_facetarea_simplex
+qh_facetcenter
+qh_facetintersect
+qh_facetvertices
+qh_find_newvertex
+qh_findbest
+qh_findbest_test
+qh_findbestfacet
+qh_findbesthorizon
+qh_findbestlower
+qh_findbestneighbor
+qh_findbestnew
+qh_findfacet_all
+qh_findgood
+qh_findgood_all
+qh_findgooddist
+qh_findhorizon
+qh_flippedmerges
+qh_forcedmerges
+qh_fprintf
+qh_fprintf_rbox
+qh_fprintf_stderr
+qh_free
+qh_freebuffers
+qh_freebuild
+qh_freeqhull
+qh_freeqhull2
+qh_freestatistics
+qh_furthestnext
+qh_furthestout
+qh_gausselim
+qh_geomplanes
+qh_getangle
+qh_getarea
+qh_getcenter
+qh_getcentrum
+qh_getdistance
+qh_gethash
+qh_getmergeset
+qh_getmergeset_initial
+qh_gram_schmidt
+qh_hashridge
+qh_hashridge_find
+qh_infiniteloop
+qh_init_A
+qh_init_B
+qh_init_qhull_command
+qh_initbuild
+qh_initflags
+qh_initialhull
+qh_initialvertices
+qh_initqhull_buffers
+qh_initqhull_globals
+qh_initqhull_mem
+qh_initqhull_outputflags
+qh_initqhull_start
+qh_initqhull_start2
+qh_initstatistics
+qh_initthresholds
+qh_inthresholds
+qh_isvertex
+qh_joggleinput
+; Mark as DATA, otherwise links a separate qh_last_random. No __declspec.
+qh_last_random DATA
+qh_lib_check
+qh_makenew_nonsimplicial
+qh_makenew_simplicial
+qh_makenewfacet
+qh_makenewfacets
+qh_makenewplanes
+qh_makeridges
+qh_malloc
+qh_mark_dupridges
+qh_markkeep
+qh_markvoronoi
+qh_matchduplicates
+qh_matchneighbor
+qh_matchnewfacets
+qh_matchvertices
+qh_maxabsval
+qh_maxmin
+qh_maxouter
+qh_maxsimplex
+qh_maydropneighbor
+qh_memalloc
+qh_memfree
+qh_memfreeshort
+qh_meminit
+qh_meminitbuffers
+qh_memsetup
+qh_memsize
+qh_memstatistics
+qh_memtotal
+qh_merge_degenredundant
+qh_merge_nonconvex
+qh_mergecycle
+qh_mergecycle_all
+qh_mergecycle_facets
+qh_mergecycle_neighbors
+qh_mergecycle_ridges
+qh_mergecycle_vneighbors
+qh_mergefacet
+qh_mergefacet2d
+qh_mergeneighbors
+qh_mergeridges
+qh_mergesimplex
+qh_mergevertex_del
+qh_mergevertex_neighbors
+qh_mergevertices
+qh_minabsval
+qh_mindiff
+qh_nearcoplanar
+qh_nearvertex
+qh_neighbor_intersections
+qh_new_qhull
+qh_newfacet
+qh_newhashtable
+qh_newridge
+qh_newstats
+qh_newvertex
+qh_newvertices
+qh_nextfurthest
+qh_nextridge3d
+qh_normalize
+qh_normalize2
+qh_nostatistic
+qh_option
+qh_order_vertexneighbors
+qh_orientoutside
+qh_out1
+qh_out2n
+qh_out3n
+qh_outcoplanar
+qh_outerinner
+qh_partitionall
+qh_partitioncoplanar
+qh_partitionpoint
+qh_partitionvisible
+qh_point
+qh_point_add
+qh_pointdist
+qh_pointfacet
+qh_pointid
+qh_pointvertex
+qh_postmerge
+qh_precision
+qh_premerge
+qh_prepare_output
+qh_prependfacet
+qh_printafacet
+qh_printallstatistics
+qh_printbegin
+qh_printcenter
+qh_printcentrum
+qh_printend
+qh_printend4geom
+qh_printextremes
+qh_printextremes_2d
+qh_printextremes_d
+qh_printfacet
+qh_printfacet2geom
+qh_printfacet2geom_points
+qh_printfacet2math
+qh_printfacet3geom_nonsimplicial
+qh_printfacet3geom_points
+qh_printfacet3geom_simplicial
+qh_printfacet3math
+qh_printfacet3vertex
+qh_printfacet4geom_nonsimplicial
+qh_printfacet4geom_simplicial
+qh_printfacetNvertex_nonsimplicial
+qh_printfacetNvertex_simplicial
+qh_printfacetheader
+qh_printfacetlist
+qh_printfacetridges
+qh_printfacets
+qh_printhashtable
+qh_printhelp_degenerate
+qh_printhelp_narrowhull
+qh_printhelp_singular
+qh_printhyperplaneintersection
+qh_printline3geom
+qh_printlists
+qh_printmatrix
+qh_printneighborhood
+qh_printpoint
+qh_printpoint3
+qh_printpointid
+qh_printpoints
+qh_printpoints_out
+qh_printpointvect
+qh_printpointvect2
+qh_printridge
+qh_printspheres
+qh_printstatistics
+qh_printstatlevel
+qh_printstats
+qh_printsummary
+qh_printvdiagram
+qh_printvdiagram2
+qh_printvertex
+qh_printvertexlist
+qh_printvertices
+qh_printvneighbors
+qh_printvnorm
+qh_printvoronoi
+qh_printvridge
+qh_produce_output
+qh_produce_output2
+qh_projectdim3
+qh_projectinput
+qh_projectpoint
+qh_projectpoints
+; Mark as DATA, otherwise links a separate qh_qh. qh_qh and qh_qhstat requires __declspec
+qh_qh DATA
+qh_qhstat DATA
+qh_qhull
+qh_rand
+qh_randomfactor
+qh_randommatrix
+qh_rboxpoints
+qh_readfeasible
+qh_readpoints
+qh_reducevertices
+qh_redundant_vertex
+qh_remove_extravertices
+qh_removefacet
+qh_removevertex
+qh_rename_sharedvertex
+qh_renameridgevertex
+qh_renamevertex
+qh_resetlists
+qh_restore_qhull
+qh_rotateinput
+qh_rotatepoints
+qh_roundi
+qh_save_qhull
+qh_scaleinput
+qh_scalelast
+qh_scalepoints
+qh_setaddnth
+qh_setaddsorted
+qh_setappend
+qh_setappend2ndlast
+qh_setappend_set
+qh_setcheck
+qh_setcompact
+qh_setcopy
+qh_setdel
+qh_setdelaunay
+qh_setdellast
+qh_setdelnth
+qh_setdelnthsorted
+qh_setdelsorted
+qh_setduplicate
+qh_setequal
+qh_setequal_except
+qh_setequal_skip
+qh_setfacetplane
+qh_setfeasible
+qh_setfree
+qh_setfree2
+qh_setfreelong
+qh_sethalfspace
+qh_sethalfspace_all
+qh_sethyperplane_det
+qh_sethyperplane_gauss
+qh_setin
+qh_setindex
+qh_setlarger
+qh_setlast
+qh_setnew
+qh_setnew_delnthsorted
+qh_setprint
+qh_setreplace
+qh_setsize
+qh_settemp
+qh_settempfree
+qh_settempfree_all
+qh_settemppop
+qh_settemppush
+qh_settruncate
+qh_setunique
+qh_setvoronoi_all
+qh_setzero
+qh_sharpnewfacets
+qh_skipfacet
+qh_skipfilename
+qh_srand
+qh_stddev
+qh_strtod
+qh_strtol
+qh_test_appendmerge
+qh_test_vneighbors
+qh_tracemerge
+qh_tracemerging
+qh_triangulate
+qh_triangulate_facet
+qh_triangulate_link
+qh_triangulate_mirror
+qh_triangulate_null
+qh_updatetested
+qh_updatevertices
+qh_user_memsizes
+qh_version
+qh_version2
+qh_vertexintersect
+qh_vertexintersect_new
+qh_vertexneighbors
+qh_vertexridges
+qh_vertexridges_facet
+qh_vertexsubset
+qh_voronoi_center
+qh_willdelete
+; Mark as DATA, otherwise links a separate qhmem. No __declspec
+qhmem DATA
+rbox DATA
+rbox_inuse DATA
diff --git a/xs/src/qhull/src/libqhull/qset.c b/xs/src/qhull/src/libqhull/qset.c
new file mode 100644
index 000000000..a969252a7
--- /dev/null
+++ b/xs/src/qhull/src/libqhull/qset.c
@@ -0,0 +1,1340 @@
+/*<html><pre> -<a href="qh-set.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ qset.c
+ implements set manipulations needed for quickhull
+
+ see qh-set.htm and qset.h
+
+ Be careful of strict aliasing (two pointers of different types
+ that reference the same location). The last slot of a set is
+ either the actual size of the set plus 1, or the NULL terminator
+ of the set (i.e., setelemT).
+
+ Copyright (c) 1993-2015 The Geometry Center.
+ $Id: //main/2015/qhull/src/libqhull/qset.c#3 $$Change: 2062 $
+ $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $
+*/
+
+#include "user.h" /* for QHULL_CRTDBG */
+#include "qset.h"
+#include "mem.h"
+#include <stdio.h>
+#include <string.h>
+/*** uncomment here and qhull_a.h
+ if string.h does not define memcpy()
+#include <memory.h>
+*/
+
+#ifndef qhDEFlibqhull
+typedef struct ridgeT ridgeT;
+typedef struct facetT facetT;
+void qh_errexit(int exitcode, facetT *, ridgeT *);
+void qh_fprintf(FILE *fp, int msgcode, const char *fmt, ... );
+# ifdef _MSC_VER /* Microsoft Visual C++ -- warning level 4 */
+# pragma warning( disable : 4127) /* conditional expression is constant */
+# pragma warning( disable : 4706) /* assignment within conditional function */
+# endif
+#endif
+
+/*=============== internal macros ===========================*/
+
+/*============ functions in alphabetical order ===================*/
+
+/*-<a href="qh-set.htm#TOC"
+ >--------------------------------<a name="setaddnth">-</a>
+
+ qh_setaddnth( setp, nth, newelem)
+ adds newelem as n'th element of sorted or unsorted *setp
+
+ notes:
+ *setp and newelem must be defined
+ *setp may be a temp set
+ nth=0 is first element
+ errors if nth is out of bounds
+
+ design:
+ expand *setp if empty or full
+ move tail of *setp up one
+ insert newelem
+*/
+void qh_setaddnth(setT **setp, int nth, void *newelem) {
+ int oldsize, i;
+ setelemT *sizep; /* avoid strict aliasing */
+ setelemT *oldp, *newp;
+
+ if (!*setp || (sizep= SETsizeaddr_(*setp))->i==0) {
+ qh_setlarger(setp);
+ sizep= SETsizeaddr_(*setp);
+ }
+ oldsize= sizep->i - 1;
+ if (nth < 0 || nth > oldsize) {
+ qh_fprintf(qhmem.ferr, 6171, "qhull internal error (qh_setaddnth): nth %d is out-of-bounds for set:\n", nth);
+ qh_setprint(qhmem.ferr, "", *setp);
+ qh_errexit(qhmem_ERRqhull, NULL, NULL);
+ }
+ sizep->i++;
+ oldp= (setelemT *)SETelemaddr_(*setp, oldsize, void); /* NULL */
+ newp= oldp+1;
+ for (i=oldsize-nth+1; i--; ) /* move at least NULL */
+ (newp--)->p= (oldp--)->p; /* may overwrite *sizep */
+ newp->p= newelem;
+} /* setaddnth */
+
+
+/*-<a href="qh-set.htm#TOC"
+ >--------------------------------<a name="setaddsorted">-</a>
+
+ setaddsorted( setp, newelem )
+ adds an newelem into sorted *setp
+
+ notes:
+ *setp and newelem must be defined
+ *setp may be a temp set
+ nop if newelem already in set
+
+ design:
+ find newelem's position in *setp
+ insert newelem
+*/
+void qh_setaddsorted(setT **setp, void *newelem) {
+ int newindex=0;
+ void *elem, **elemp;
+
+ FOREACHelem_(*setp) { /* could use binary search instead */
+ if (elem < newelem)
+ newindex++;
+ else if (elem == newelem)
+ return;
+ else
+ break;
+ }
+ qh_setaddnth(setp, newindex, newelem);
+} /* setaddsorted */
+
+
+/*-<a href="qh-set.htm#TOC"
+ >-------------------------------<a name="setappend">-</a>
+
+ qh_setappend( setp, newelem)
+ append newelem to *setp
+
+ notes:
+ *setp may be a temp set
+ *setp and newelem may be NULL
+
+ design:
+ expand *setp if empty or full
+ append newelem to *setp
+
+*/
+void qh_setappend(setT **setp, void *newelem) {
+ setelemT *sizep; /* Avoid strict aliasing. Writing to *endp may overwrite *sizep */
+ setelemT *endp;
+ int count;
+
+ if (!newelem)
+ return;
+ if (!*setp || (sizep= SETsizeaddr_(*setp))->i==0) {
+ qh_setlarger(setp);
+ sizep= SETsizeaddr_(*setp);
+ }
+ count= (sizep->i)++ - 1;
+ endp= (setelemT *)SETelemaddr_(*setp, count, void);
+ (endp++)->p= newelem;
+ endp->p= NULL;
+} /* setappend */
+
+/*-<a href="qh-set.htm#TOC"
+ >-------------------------------<a name="setappend_set">-</a>
+
+ qh_setappend_set( setp, setA)
+ appends setA to *setp
+
+ notes:
+ *setp can not be a temp set
+ *setp and setA may be NULL
+
+ design:
+ setup for copy
+ expand *setp if it is too small
+ append all elements of setA to *setp
+*/
+void qh_setappend_set(setT **setp, setT *setA) {
+ int sizeA, size;
+ setT *oldset;
+ setelemT *sizep;
+
+ if (!setA)
+ return;
+ SETreturnsize_(setA, sizeA);
+ if (!*setp)
+ *setp= qh_setnew(sizeA);
+ sizep= SETsizeaddr_(*setp);
+ if (!(size= sizep->i))
+ size= (*setp)->maxsize;
+ else
+ size--;
+ if (size + sizeA > (*setp)->maxsize) {
+ oldset= *setp;
+ *setp= qh_setcopy(oldset, sizeA);
+ qh_setfree(&oldset);
+ sizep= SETsizeaddr_(*setp);
+ }
+ if (sizeA > 0) {
+ sizep->i= size+sizeA+1; /* memcpy may overwrite */
+ memcpy((char *)&((*setp)->e[size].p), (char *)&(setA->e[0].p), (size_t)(sizeA+1) * SETelemsize);
+ }
+} /* setappend_set */
+
+
+/*-<a href="qh-set.htm#TOC"
+ >-------------------------------<a name="setappend2ndlast">-</a>
+
+ qh_setappend2ndlast( setp, newelem )
+ makes newelem the next to the last element in *setp
+
+ notes:
+ *setp must have at least one element
+ newelem must be defined
+ *setp may be a temp set
+
+ design:
+ expand *setp if empty or full
+ move last element of *setp up one
+ insert newelem
+*/
+void qh_setappend2ndlast(setT **setp, void *newelem) {
+ setelemT *sizep; /* Avoid strict aliasing. Writing to *endp may overwrite *sizep */
+ setelemT *endp, *lastp;
+ int count;
+
+ if (!*setp || (sizep= SETsizeaddr_(*setp))->i==0) {
+ qh_setlarger(setp);
+ sizep= SETsizeaddr_(*setp);
+ }
+ count= (sizep->i)++ - 1;
+ endp= (setelemT *)SETelemaddr_(*setp, count, void); /* NULL */
+ lastp= endp-1;
+ *(endp++)= *lastp;
+ endp->p= NULL; /* may overwrite *sizep */
+ lastp->p= newelem;
+} /* setappend2ndlast */
+
+/*-<a href="qh-set.htm#TOC"
+ >-------------------------------<a name="setcheck">-</a>
+
+ qh_setcheck( set, typename, id )
+ check set for validity
+ report errors with typename and id
+
+ design:
+ checks that maxsize, actual size, and NULL terminator agree
+*/
+void qh_setcheck(setT *set, const char *tname, unsigned id) {
+ int maxsize, size;
+ int waserr= 0;
+
+ if (!set)
+ return;
+ SETreturnsize_(set, size);
+ maxsize= set->maxsize;
+ if (size > maxsize || !maxsize) {
+ qh_fprintf(qhmem.ferr, 6172, "qhull internal error (qh_setcheck): actual size %d of %s%d is greater than max size %d\n",
+ size, tname, id, maxsize);
+ waserr= 1;
+ }else if (set->e[size].p) {
+ qh_fprintf(qhmem.ferr, 6173, "qhull internal error (qh_setcheck): %s%d(size %d max %d) is not null terminated.\n",
+ tname, id, size-1, maxsize);
+ waserr= 1;
+ }
+ if (waserr) {
+ qh_setprint(qhmem.ferr, "ERRONEOUS", set);
+ qh_errexit(qhmem_ERRqhull, NULL, NULL);
+ }
+} /* setcheck */
+
+
+/*-<a href="qh-set.htm#TOC"
+ >-------------------------------<a name="setcompact">-</a>
+
+ qh_setcompact( set )
+ remove internal NULLs from an unsorted set
+
+ returns:
+ updated set
+
+ notes:
+ set may be NULL
+ it would be faster to swap tail of set into holes, like qh_setdel
+
+ design:
+ setup pointers into set
+ skip NULLs while copying elements to start of set
+ update the actual size
+*/
+void qh_setcompact(setT *set) {
+ int size;
+ void **destp, **elemp, **endp, **firstp;
+
+ if (!set)
+ return;
+ SETreturnsize_(set, size);
+ destp= elemp= firstp= SETaddr_(set, void);
+ endp= destp + size;
+ while (1) {
+ if (!(*destp++ = *elemp++)) {
+ destp--;
+ if (elemp > endp)
+ break;
+ }
+ }
+ qh_settruncate(set, (int)(destp-firstp)); /* WARN64 */
+} /* setcompact */
+
+
+/*-<a href="qh-set.htm#TOC"
+ >-------------------------------<a name="setcopy">-</a>
+
+ qh_setcopy( set, extra )
+ make a copy of a sorted or unsorted set with extra slots
+
+ returns:
+ new set
+
+ design:
+ create a newset with extra slots
+ copy the elements to the newset
+
+*/
+setT *qh_setcopy(setT *set, int extra) {
+ setT *newset;
+ int size;
+
+ if (extra < 0)
+ extra= 0;
+ SETreturnsize_(set, size);
+ newset= qh_setnew(size+extra);
+ SETsizeaddr_(newset)->i= size+1; /* memcpy may overwrite */
+ memcpy((char *)&(newset->e[0].p), (char *)&(set->e[0].p), (size_t)(size+1) * SETelemsize);
+ return(newset);
+} /* setcopy */
+
+
+/*-<a href="qh-set.htm#TOC"
+ >-------------------------------<a name="setdel">-</a>
+
+ qh_setdel( set, oldelem )
+ delete oldelem from an unsorted set
+
+ returns:
+ returns oldelem if found
+ returns NULL otherwise
+
+ notes:
+ set may be NULL
+ oldelem must not be NULL;
+ only deletes one copy of oldelem in set
+
+ design:
+ locate oldelem
+ update actual size if it was full
+ move the last element to the oldelem's location
+*/
+void *qh_setdel(setT *set, void *oldelem) {
+ setelemT *sizep;
+ setelemT *elemp;
+ setelemT *lastp;
+
+ if (!set)
+ return NULL;
+ elemp= (setelemT *)SETaddr_(set, void);
+ while (elemp->p != oldelem && elemp->p)
+ elemp++;
+ if (elemp->p) {
+ sizep= SETsizeaddr_(set);
+ if (!(sizep->i)--) /* if was a full set */
+ sizep->i= set->maxsize; /* *sizep= (maxsize-1)+ 1 */
+ lastp= (setelemT *)SETelemaddr_(set, sizep->i-1, void);
+ elemp->p= lastp->p; /* may overwrite itself */
+ lastp->p= NULL;
+ return oldelem;
+ }
+ return NULL;
+} /* setdel */
+
+
+/*-<a href="qh-set.htm#TOC"
+ >-------------------------------<a name="setdellast">-</a>
+
+ qh_setdellast( set)
+ return last element of set or NULL
+
+ notes:
+ deletes element from set
+ set may be NULL
+
+ design:
+ return NULL if empty
+ if full set
+ delete last element and set actual size
+ else
+ delete last element and update actual size
+*/
+void *qh_setdellast(setT *set) {
+ int setsize; /* actually, actual_size + 1 */
+ int maxsize;
+ setelemT *sizep;
+ void *returnvalue;
+
+ if (!set || !(set->e[0].p))
+ return NULL;
+ sizep= SETsizeaddr_(set);
+ if ((setsize= sizep->i)) {
+ returnvalue= set->e[setsize - 2].p;
+ set->e[setsize - 2].p= NULL;
+ sizep->i--;
+ }else {
+ maxsize= set->maxsize;
+ returnvalue= set->e[maxsize - 1].p;
+ set->e[maxsize - 1].p= NULL;
+ sizep->i= maxsize;
+ }
+ return returnvalue;
+} /* setdellast */
+
+
+/*-<a href="qh-set.htm#TOC"
+ >-------------------------------<a name="setdelnth">-</a>
+
+ qh_setdelnth( set, nth )
+ deletes nth element from unsorted set
+ 0 is first element
+
+ returns:
+ returns the element (needs type conversion)
+
+ notes:
+ errors if nth invalid
+
+ design:
+ setup points and check nth
+ delete nth element and overwrite with last element
+*/
+void *qh_setdelnth(setT *set, int nth) {
+ void *elem;
+ setelemT *sizep;
+ setelemT *elemp, *lastp;
+
+ sizep= SETsizeaddr_(set);
+ if ((sizep->i--)==0) /* if was a full set */
+ sizep->i= set->maxsize; /* *sizep= (maxsize-1)+ 1 */
+ if (nth < 0 || nth >= sizep->i) {
+ qh_fprintf(qhmem.ferr, 6174, "qhull internal error (qh_setdelnth): nth %d is out-of-bounds for set:\n", nth);
+ qh_setprint(qhmem.ferr, "", set);
+ qh_errexit(qhmem_ERRqhull, NULL, NULL);
+ }
+ elemp= (setelemT *)SETelemaddr_(set, nth, void); /* nth valid by QH6174 */
+ lastp= (setelemT *)SETelemaddr_(set, sizep->i-1, void);
+ elem= elemp->p;
+ elemp->p= lastp->p; /* may overwrite itself */
+ lastp->p= NULL;
+ return elem;
+} /* setdelnth */
+
+/*-<a href="qh-set.htm#TOC"
+ >-------------------------------<a name="setdelnthsorted">-</a>
+
+ qh_setdelnthsorted( set, nth )
+ deletes nth element from sorted set
+
+ returns:
+ returns the element (use type conversion)
+
+ notes:
+ errors if nth invalid
+
+ see also:
+ setnew_delnthsorted
+
+ design:
+ setup points and check nth
+ copy remaining elements down one
+ update actual size
+*/
+void *qh_setdelnthsorted(setT *set, int nth) {
+ void *elem;
+ setelemT *sizep;
+ setelemT *newp, *oldp;
+
+ sizep= SETsizeaddr_(set);
+ if (nth < 0 || (sizep->i && nth >= sizep->i-1) || nth >= set->maxsize) {
+ qh_fprintf(qhmem.ferr, 6175, "qhull internal error (qh_setdelnthsorted): nth %d is out-of-bounds for set:\n", nth);
+ qh_setprint(qhmem.ferr, "", set);
+ qh_errexit(qhmem_ERRqhull, NULL, NULL);
+ }
+ newp= (setelemT *)SETelemaddr_(set, nth, void);
+ elem= newp->p;
+ oldp= newp+1;
+ while (((newp++)->p= (oldp++)->p))
+ ; /* copy remaining elements and NULL */
+ if ((sizep->i--)==0) /* if was a full set */
+ sizep->i= set->maxsize; /* *sizep= (max size-1)+ 1 */
+ return elem;
+} /* setdelnthsorted */
+
+
+/*-<a href="qh-set.htm#TOC"
+ >-------------------------------<a name="setdelsorted">-</a>
+
+ qh_setdelsorted( set, oldelem )
+ deletes oldelem from sorted set
+
+ returns:
+ returns oldelem if it was deleted
+
+ notes:
+ set may be NULL
+
+ design:
+ locate oldelem in set
+ copy remaining elements down one
+ update actual size
+*/
+void *qh_setdelsorted(setT *set, void *oldelem) {
+ setelemT *sizep;
+ setelemT *newp, *oldp;
+
+ if (!set)
+ return NULL;
+ newp= (setelemT *)SETaddr_(set, void);
+ while(newp->p != oldelem && newp->p)
+ newp++;
+ if (newp->p) {
+ oldp= newp+1;
+ while (((newp++)->p= (oldp++)->p))
+ ; /* copy remaining elements */
+ sizep= SETsizeaddr_(set);
+ if ((sizep->i--)==0) /* if was a full set */
+ sizep->i= set->maxsize; /* *sizep= (max size-1)+ 1 */
+ return oldelem;
+ }
+ return NULL;
+} /* setdelsorted */
+
+
+/*-<a href="qh-set.htm#TOC"
+ >-------------------------------<a name="setduplicate">-</a>
+
+ qh_setduplicate( set, elemsize )
+ duplicate a set of elemsize elements
+
+ notes:
+ use setcopy if retaining old elements
+
+ design:
+ create a new set
+ for each elem of the old set
+ create a newelem
+ append newelem to newset
+*/
+setT *qh_setduplicate(setT *set, int elemsize) {
+ void *elem, **elemp, *newElem;
+ setT *newSet;
+ int size;
+
+ if (!(size= qh_setsize(set)))
+ return NULL;
+ newSet= qh_setnew(size);
+ FOREACHelem_(set) {
+ newElem= qh_memalloc(elemsize);
+ memcpy(newElem, elem, (size_t)elemsize);
+ qh_setappend(&newSet, newElem);
+ }
+ return newSet;
+} /* setduplicate */
+
+
+/*-<a href="qh-set.htm#TOC"
+ >-------------------------------<a name="setendpointer">-</a>
+
+ qh_setendpointer( set )
+ Returns pointer to NULL terminator of a set's elements
+ set can not be NULL
+
+*/
+void **qh_setendpointer(setT *set) {
+
+ setelemT *sizep= SETsizeaddr_(set);
+ int n= sizep->i;
+ return (n ? &set->e[n-1].p : &sizep->p);
+} /* qh_setendpointer */
+
+/*-<a href="qh-set.htm#TOC"
+ >-------------------------------<a name="setequal">-</a>
+
+ qh_setequal( setA, setB )
+ returns 1 if two sorted sets are equal, otherwise returns 0
+
+ notes:
+ either set may be NULL
+
+ design:
+ check size of each set
+ setup pointers
+ compare elements of each set
+*/
+int qh_setequal(setT *setA, setT *setB) {
+ void **elemAp, **elemBp;
+ int sizeA= 0, sizeB= 0;
+
+ if (setA) {
+ SETreturnsize_(setA, sizeA);
+ }
+ if (setB) {
+ SETreturnsize_(setB, sizeB);
+ }
+ if (sizeA != sizeB)
+ return 0;
+ if (!sizeA)
+ return 1;
+ elemAp= SETaddr_(setA, void);
+ elemBp= SETaddr_(setB, void);
+ if (!memcmp((char *)elemAp, (char *)elemBp, sizeA*SETelemsize))
+ return 1;
+ return 0;
+} /* setequal */
+
+
+/*-<a href="qh-set.htm#TOC"
+ >-------------------------------<a name="setequal_except">-</a>
+
+ qh_setequal_except( setA, skipelemA, setB, skipelemB )
+ returns 1 if sorted setA and setB are equal except for skipelemA & B
+
+ returns:
+ false if either skipelemA or skipelemB are missing
+
+ notes:
+ neither set may be NULL
+
+ if skipelemB is NULL,
+ can skip any one element of setB
+
+ design:
+ setup pointers
+ search for skipelemA, skipelemB, and mismatches
+ check results
+*/
+int qh_setequal_except(setT *setA, void *skipelemA, setT *setB, void *skipelemB) {
+ void **elemA, **elemB;
+ int skip=0;
+
+ elemA= SETaddr_(setA, void);
+ elemB= SETaddr_(setB, void);
+ while (1) {
+ if (*elemA == skipelemA) {
+ skip++;
+ elemA++;
+ }
+ if (skipelemB) {
+ if (*elemB == skipelemB) {
+ skip++;
+ elemB++;
+ }
+ }else if (*elemA != *elemB) {
+ skip++;
+ if (!(skipelemB= *elemB++))
+ return 0;
+ }
+ if (!*elemA)
+ break;
+ if (*elemA++ != *elemB++)
+ return 0;
+ }
+ if (skip != 2 || *elemB)
+ return 0;
+ return 1;
+} /* setequal_except */
+
+
+/*-<a href="qh-set.htm#TOC"
+ >-------------------------------<a name="setequal_skip">-</a>
+
+ qh_setequal_skip( setA, skipA, setB, skipB )
+ returns 1 if sorted setA and setB are equal except for elements skipA & B
+
+ returns:
+ false if different size
+
+ notes:
+ neither set may be NULL
+
+ design:
+ setup pointers
+ search for mismatches while skipping skipA and skipB
+*/
+int qh_setequal_skip(setT *setA, int skipA, setT *setB, int skipB) {
+ void **elemA, **elemB, **skipAp, **skipBp;
+
+ elemA= SETaddr_(setA, void);
+ elemB= SETaddr_(setB, void);
+ skipAp= SETelemaddr_(setA, skipA, void);
+ skipBp= SETelemaddr_(setB, skipB, void);
+ while (1) {
+ if (elemA == skipAp)
+ elemA++;
+ if (elemB == skipBp)
+ elemB++;
+ if (!*elemA)
+ break;
+ if (*elemA++ != *elemB++)
+ return 0;
+ }
+ if (*elemB)
+ return 0;
+ return 1;
+} /* setequal_skip */
+
+
+/*-<a href="qh-set.htm#TOC"
+ >-------------------------------<a name="setfree">-</a>
+
+ qh_setfree( setp )
+ frees the space occupied by a sorted or unsorted set
+
+ returns:
+ sets setp to NULL
+
+ notes:
+ set may be NULL
+
+ design:
+ free array
+ free set
+*/
+void qh_setfree(setT **setp) {
+ int size;
+ void **freelistp; /* used if !qh_NOmem by qh_memfree_() */
+
+ if (*setp) {
+ size= sizeof(setT) + ((*setp)->maxsize)*SETelemsize;
+ if (size <= qhmem.LASTsize) {
+ qh_memfree_(*setp, size, freelistp);
+ }else
+ qh_memfree(*setp, size);
+ *setp= NULL;
+ }
+} /* setfree */
+
+
+/*-<a href="qh-set.htm#TOC"
+ >-------------------------------<a name="setfree2">-</a>
+
+ qh_setfree2( setp, elemsize )
+ frees the space occupied by a set and its elements
+
+ notes:
+ set may be NULL
+
+ design:
+ free each element
+ free set
+*/
+void qh_setfree2(setT **setp, int elemsize) {
+ void *elem, **elemp;
+
+ FOREACHelem_(*setp)
+ qh_memfree(elem, elemsize);
+ qh_setfree(setp);
+} /* setfree2 */
+
+
+
+/*-<a href="qh-set.htm#TOC"
+ >-------------------------------<a name="setfreelong">-</a>
+
+ qh_setfreelong( setp )
+ frees a set only if it's in long memory
+
+ returns:
+ sets setp to NULL if it is freed
+
+ notes:
+ set may be NULL
+
+ design:
+ if set is large
+ free it
+*/
+void qh_setfreelong(setT **setp) {
+ int size;
+
+ if (*setp) {
+ size= sizeof(setT) + ((*setp)->maxsize)*SETelemsize;
+ if (size > qhmem.LASTsize) {
+ qh_memfree(*setp, size);
+ *setp= NULL;
+ }
+ }
+} /* setfreelong */
+
+
+/*-<a href="qh-set.htm#TOC"
+ >-------------------------------<a name="setin">-</a>
+
+ qh_setin( set, setelem )
+ returns 1 if setelem is in a set, 0 otherwise
+
+ notes:
+ set may be NULL or unsorted
+
+ design:
+ scans set for setelem
+*/
+int qh_setin(setT *set, void *setelem) {
+ void *elem, **elemp;
+
+ FOREACHelem_(set) {
+ if (elem == setelem)
+ return 1;
+ }
+ return 0;
+} /* setin */
+
+
+/*-<a href="qh-set.htm#TOC"
+ >-------------------------------<a name="setindex">-</a>
+
+ qh_setindex( set, atelem )
+ returns the index of atelem in set.
+ returns -1, if not in set or maxsize wrong
+
+ notes:
+ set may be NULL and may contain nulls.
+ NOerrors returned (qh_pointid, QhullPoint::id)
+
+ design:
+ checks maxsize
+ scans set for atelem
+*/
+int qh_setindex(setT *set, void *atelem) {
+ void **elem;
+ int size, i;
+
+ if (!set)
+ return -1;
+ SETreturnsize_(set, size);
+ if (size > set->maxsize)
+ return -1;
+ elem= SETaddr_(set, void);
+ for (i=0; i < size; i++) {
+ if (*elem++ == atelem)
+ return i;
+ }
+ return -1;
+} /* setindex */
+
+
+/*-<a href="qh-set.htm#TOC"
+ >-------------------------------<a name="setlarger">-</a>
+
+ qh_setlarger( oldsetp )
+ returns a larger set that contains all elements of *oldsetp
+
+ notes:
+ the set is at least twice as large
+ if temp set, updates qhmem.tempstack
+
+ design:
+ creates a new set
+ copies the old set to the new set
+ updates pointers in tempstack
+ deletes the old set
+*/
+void qh_setlarger(setT **oldsetp) {
+ int size= 1;
+ setT *newset, *set, **setp, *oldset;
+ setelemT *sizep;
+ setelemT *newp, *oldp;
+
+ if (*oldsetp) {
+ oldset= *oldsetp;
+ SETreturnsize_(oldset, size);
+ qhmem.cntlarger++;
+ qhmem.totlarger += size+1;
+ newset= qh_setnew(2 * size);
+ oldp= (setelemT *)SETaddr_(oldset, void);
+ newp= (setelemT *)SETaddr_(newset, void);
+ memcpy((char *)newp, (char *)oldp, (size_t)(size+1) * SETelemsize);
+ sizep= SETsizeaddr_(newset);
+ sizep->i= size+1;
+ FOREACHset_((setT *)qhmem.tempstack) {
+ if (set == oldset)
+ *(setp-1)= newset;
+ }
+ qh_setfree(oldsetp);
+ }else
+ newset= qh_setnew(3);
+ *oldsetp= newset;
+} /* setlarger */
+
+
+/*-<a href="qh-set.htm#TOC"
+ >-------------------------------<a name="setlast">-</a>
+
+ qh_setlast( set )
+ return last element of set or NULL (use type conversion)
+
+ notes:
+ set may be NULL
+
+ design:
+ return last element
+*/
+void *qh_setlast(setT *set) {
+ int size;
+
+ if (set) {
+ size= SETsizeaddr_(set)->i;
+ if (!size)
+ return SETelem_(set, set->maxsize - 1);
+ else if (size > 1)
+ return SETelem_(set, size - 2);
+ }
+ return NULL;
+} /* setlast */
+
+
+/*-<a href="qh-set.htm#TOC"
+ >-------------------------------<a name="setnew">-</a>
+
+ qh_setnew( setsize )
+ creates and allocates space for a set
+
+ notes:
+ setsize means the number of elements (!including the NULL terminator)
+ use qh_settemp/qh_setfreetemp if set is temporary
+
+ design:
+ allocate memory for set
+ roundup memory if small set
+ initialize as empty set
+*/
+setT *qh_setnew(int setsize) {
+ setT *set;
+ int sizereceived; /* used if !qh_NOmem */
+ int size;
+ void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */
+
+ if (!setsize)
+ setsize++;
+ size= sizeof(setT) + setsize * SETelemsize;
+ if (size>0 && size <= qhmem.LASTsize) {
+ qh_memalloc_(size, freelistp, set, setT);
+#ifndef qh_NOmem
+ sizereceived= qhmem.sizetable[ qhmem.indextable[size]];
+ if (sizereceived > size)
+ setsize += (sizereceived - size)/SETelemsize;
+#endif
+ }else
+ set= (setT*)qh_memalloc(size);
+ set->maxsize= setsize;
+ set->e[setsize].i= 1;
+ set->e[0].p= NULL;
+ return(set);
+} /* setnew */
+
+
+/*-<a href="qh-set.htm#TOC"
+ >-------------------------------<a name="setnew_delnthsorted">-</a>
+
+ qh_setnew_delnthsorted( set, size, nth, prepend )
+ creates a sorted set not containing nth element
+ if prepend, the first prepend elements are undefined
+
+ notes:
+ set must be defined
+ checks nth
+ see also: setdelnthsorted
+
+ design:
+ create new set
+ setup pointers and allocate room for prepend'ed entries
+ append head of old set to new set
+ append tail of old set to new set
+*/
+setT *qh_setnew_delnthsorted(setT *set, int size, int nth, int prepend) {
+ setT *newset;
+ void **oldp, **newp;
+ int tailsize= size - nth -1, newsize;
+
+ if (tailsize < 0) {
+ qh_fprintf(qhmem.ferr, 6176, "qhull internal error (qh_setnew_delnthsorted): nth %d is out-of-bounds for set:\n", nth);
+ qh_setprint(qhmem.ferr, "", set);
+ qh_errexit(qhmem_ERRqhull, NULL, NULL);
+ }
+ newsize= size-1 + prepend;
+ newset= qh_setnew(newsize);
+ newset->e[newset->maxsize].i= newsize+1; /* may be overwritten */
+ oldp= SETaddr_(set, void);
+ newp= SETaddr_(newset, void) + prepend;
+ switch (nth) {
+ case 0:
+ break;
+ case 1:
+ *(newp++)= *oldp++;
+ break;
+ case 2:
+ *(newp++)= *oldp++;
+ *(newp++)= *oldp++;
+ break;
+ case 3:
+ *(newp++)= *oldp++;
+ *(newp++)= *oldp++;
+ *(newp++)= *oldp++;
+ break;
+ case 4:
+ *(newp++)= *oldp++;
+ *(newp++)= *oldp++;
+ *(newp++)= *oldp++;
+ *(newp++)= *oldp++;
+ break;
+ default:
+ memcpy((char *)newp, (char *)oldp, (size_t)nth * SETelemsize);
+ newp += nth;
+ oldp += nth;
+ break;
+ }
+ oldp++;
+ switch (tailsize) {
+ case 0:
+ break;
+ case 1:
+ *(newp++)= *oldp++;
+ break;
+ case 2:
+ *(newp++)= *oldp++;
+ *(newp++)= *oldp++;
+ break;
+ case 3:
+ *(newp++)= *oldp++;
+ *(newp++)= *oldp++;
+ *(newp++)= *oldp++;
+ break;
+ case 4:
+ *(newp++)= *oldp++;
+ *(newp++)= *oldp++;
+ *(newp++)= *oldp++;
+ *(newp++)= *oldp++;
+ break;
+ default:
+ memcpy((char *)newp, (char *)oldp, (size_t)tailsize * SETelemsize);
+ newp += tailsize;
+ }
+ *newp= NULL;
+ return(newset);
+} /* setnew_delnthsorted */
+
+
+/*-<a href="qh-set.htm#TOC"
+ >-------------------------------<a name="setprint">-</a>
+
+ qh_setprint( fp, string, set )
+ print set elements to fp with identifying string
+
+ notes:
+ never errors
+*/
+void qh_setprint(FILE *fp, const char* string, setT *set) {
+ int size, k;
+
+ if (!set)
+ qh_fprintf(fp, 9346, "%s set is null\n", string);
+ else {
+ SETreturnsize_(set, size);
+ qh_fprintf(fp, 9347, "%s set=%p maxsize=%d size=%d elems=",
+ string, set, set->maxsize, size);
+ if (size > set->maxsize)
+ size= set->maxsize+1;
+ for (k=0; k < size; k++)
+ qh_fprintf(fp, 9348, " %p", set->e[k].p);
+ qh_fprintf(fp, 9349, "\n");
+ }
+} /* setprint */
+
+/*-<a href="qh-set.htm#TOC"
+ >-------------------------------<a name="setreplace">-</a>
+
+ qh_setreplace( set, oldelem, newelem )
+ replaces oldelem in set with newelem
+
+ notes:
+ errors if oldelem not in the set
+ newelem may be NULL, but it turns the set into an indexed set (no FOREACH)
+
+ design:
+ find oldelem
+ replace with newelem
+*/
+void qh_setreplace(setT *set, void *oldelem, void *newelem) {
+ void **elemp;
+
+ elemp= SETaddr_(set, void);
+ while (*elemp != oldelem && *elemp)
+ elemp++;
+ if (*elemp)
+ *elemp= newelem;
+ else {
+ qh_fprintf(qhmem.ferr, 6177, "qhull internal error (qh_setreplace): elem %p not found in set\n",
+ oldelem);
+ qh_setprint(qhmem.ferr, "", set);
+ qh_errexit(qhmem_ERRqhull, NULL, NULL);
+ }
+} /* setreplace */
+
+
+/*-<a href="qh-set.htm#TOC"
+ >-------------------------------<a name="setsize">-</a>
+
+ qh_setsize( set )
+ returns the size of a set
+
+ notes:
+ errors if set's maxsize is incorrect
+ same as SETreturnsize_(set)
+ same code for qh_setsize [qset.c] and QhullSetBase::count
+
+ design:
+ determine actual size of set from maxsize
+*/
+int qh_setsize(setT *set) {
+ int size;
+ setelemT *sizep;
+
+ if (!set)
+ return(0);
+ sizep= SETsizeaddr_(set);
+ if ((size= sizep->i)) {
+ size--;
+ if (size > set->maxsize) {
+ qh_fprintf(qhmem.ferr, 6178, "qhull internal error (qh_setsize): current set size %d is greater than maximum size %d\n",
+ size, set->maxsize);
+ qh_setprint(qhmem.ferr, "set: ", set);
+ qh_errexit(qhmem_ERRqhull, NULL, NULL);
+ }
+ }else
+ size= set->maxsize;
+ return size;
+} /* setsize */
+
+/*-<a href="qh-set.htm#TOC"
+ >-------------------------------<a name="settemp">-</a>
+
+ qh_settemp( setsize )
+ return a stacked, temporary set of upto setsize elements
+
+ notes:
+ use settempfree or settempfree_all to release from qhmem.tempstack
+ see also qh_setnew
+
+ design:
+ allocate set
+ append to qhmem.tempstack
+
+*/
+setT *qh_settemp(int setsize) {
+ setT *newset;
+
+ newset= qh_setnew(setsize);
+ qh_setappend(&qhmem.tempstack, newset);
+ if (qhmem.IStracing >= 5)
+ qh_fprintf(qhmem.ferr, 8123, "qh_settemp: temp set %p of %d elements, depth %d\n",
+ newset, newset->maxsize, qh_setsize(qhmem.tempstack));
+ return newset;
+} /* settemp */
+
+/*-<a href="qh-set.htm#TOC"
+ >-------------------------------<a name="settempfree">-</a>
+
+ qh_settempfree( set )
+ free temporary set at top of qhmem.tempstack
+
+ notes:
+ nop if set is NULL
+ errors if set not from previous qh_settemp
+
+ to locate errors:
+ use 'T2' to find source and then find mis-matching qh_settemp
+
+ design:
+ check top of qhmem.tempstack
+ free it
+*/
+void qh_settempfree(setT **set) {
+ setT *stackedset;
+
+ if (!*set)
+ return;
+ stackedset= qh_settemppop();
+ if (stackedset != *set) {
+ qh_settemppush(stackedset);
+ qh_fprintf(qhmem.ferr, 6179, "qhull internal error (qh_settempfree): set %p(size %d) was not last temporary allocated(depth %d, set %p, size %d)\n",
+ *set, qh_setsize(*set), qh_setsize(qhmem.tempstack)+1,
+ stackedset, qh_setsize(stackedset));
+ qh_errexit(qhmem_ERRqhull, NULL, NULL);
+ }
+ qh_setfree(set);
+} /* settempfree */
+
+/*-<a href="qh-set.htm#TOC"
+ >-------------------------------<a name="settempfree_all">-</a>
+
+ qh_settempfree_all( )
+ free all temporary sets in qhmem.tempstack
+
+ design:
+ for each set in tempstack
+ free set
+ free qhmem.tempstack
+*/
+void qh_settempfree_all(void) {
+ setT *set, **setp;
+
+ FOREACHset_(qhmem.tempstack)
+ qh_setfree(&set);
+ qh_setfree(&qhmem.tempstack);
+} /* settempfree_all */
+
+/*-<a href="qh-set.htm#TOC"
+ >-------------------------------<a name="settemppop">-</a>
+
+ qh_settemppop( )
+ pop and return temporary set from qhmem.tempstack
+
+ notes:
+ the returned set is permanent
+
+ design:
+ pop and check top of qhmem.tempstack
+*/
+setT *qh_settemppop(void) {
+ setT *stackedset;
+
+ stackedset= (setT*)qh_setdellast(qhmem.tempstack);
+ if (!stackedset) {
+ qh_fprintf(qhmem.ferr, 6180, "qhull internal error (qh_settemppop): pop from empty temporary stack\n");
+ qh_errexit(qhmem_ERRqhull, NULL, NULL);
+ }
+ if (qhmem.IStracing >= 5)
+ qh_fprintf(qhmem.ferr, 8124, "qh_settemppop: depth %d temp set %p of %d elements\n",
+ qh_setsize(qhmem.tempstack)+1, stackedset, qh_setsize(stackedset));
+ return stackedset;
+} /* settemppop */
+
+/*-<a href="qh-set.htm#TOC"
+ >-------------------------------<a name="settemppush">-</a>
+
+ qh_settemppush( set )
+ push temporary set unto qhmem.tempstack (makes it temporary)
+
+ notes:
+ duplicates settemp() for tracing
+
+ design:
+ append set to tempstack
+*/
+void qh_settemppush(setT *set) {
+ if (!set) {
+ qh_fprintf(qhmem.ferr, 6267, "qhull error (qh_settemppush): can not push a NULL temp\n");
+ qh_errexit(qhmem_ERRqhull, NULL, NULL);
+ }
+ qh_setappend(&qhmem.tempstack, set);
+ if (qhmem.IStracing >= 5)
+ qh_fprintf(qhmem.ferr, 8125, "qh_settemppush: depth %d temp set %p of %d elements\n",
+ qh_setsize(qhmem.tempstack), set, qh_setsize(set));
+} /* settemppush */
+
+
+/*-<a href="qh-set.htm#TOC"
+ >-------------------------------<a name="settruncate">-</a>
+
+ qh_settruncate( set, size )
+ truncate set to size elements
+
+ notes:
+ set must be defined
+
+ see:
+ SETtruncate_
+
+ design:
+ check size
+ update actual size of set
+*/
+void qh_settruncate(setT *set, int size) {
+
+ if (size < 0 || size > set->maxsize) {
+ qh_fprintf(qhmem.ferr, 6181, "qhull internal error (qh_settruncate): size %d out of bounds for set:\n", size);
+ qh_setprint(qhmem.ferr, "", set);
+ qh_errexit(qhmem_ERRqhull, NULL, NULL);
+ }
+ set->e[set->maxsize].i= size+1; /* maybe overwritten */
+ set->e[size].p= NULL;
+} /* settruncate */
+
+/*-<a href="qh-set.htm#TOC"
+ >-------------------------------<a name="setunique">-</a>
+
+ qh_setunique( set, elem )
+ add elem to unsorted set unless it is already in set
+
+ notes:
+ returns 1 if it is appended
+
+ design:
+ if elem not in set
+ append elem to set
+*/
+int qh_setunique(setT **set, void *elem) {
+
+ if (!qh_setin(*set, elem)) {
+ qh_setappend(set, elem);
+ return 1;
+ }
+ return 0;
+} /* setunique */
+
+/*-<a href="qh-set.htm#TOC"
+ >-------------------------------<a name="setzero">-</a>
+
+ qh_setzero( set, index, size )
+ zero elements from index on
+ set actual size of set to size
+
+ notes:
+ set must be defined
+ the set becomes an indexed set (can not use FOREACH...)
+
+ see also:
+ qh_settruncate
+
+ design:
+ check index and size
+ update actual size
+ zero elements starting at e[index]
+*/
+void qh_setzero(setT *set, int idx, int size) {
+ int count;
+
+ if (idx < 0 || idx >= size || size > set->maxsize) {
+ qh_fprintf(qhmem.ferr, 6182, "qhull internal error (qh_setzero): index %d or size %d out of bounds for set:\n", idx, size);
+ qh_setprint(qhmem.ferr, "", set);
+ qh_errexit(qhmem_ERRqhull, NULL, NULL);
+ }
+ set->e[set->maxsize].i= size+1; /* may be overwritten */
+ count= size - idx + 1; /* +1 for NULL terminator */
+ memset((char *)SETelemaddr_(set, idx, void), 0, (size_t)count * SETelemsize);
+} /* setzero */
+
+
diff --git a/xs/src/qhull/src/libqhull/qset.h b/xs/src/qhull/src/libqhull/qset.h
new file mode 100644
index 000000000..7e4e7d14f
--- /dev/null
+++ b/xs/src/qhull/src/libqhull/qset.h
@@ -0,0 +1,490 @@
+/*<html><pre> -<a href="qh-set.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ qset.h
+ header file for qset.c that implements set
+
+ see qh-set.htm and qset.c
+
+ only uses mem.c, malloc/free
+
+ for error handling, writes message and calls
+ qh_errexit(qhmem_ERRqhull, NULL, NULL);
+
+ set operations satisfy the following properties:
+ - sets have a max size, the actual size (if different) is stored at the end
+ - every set is NULL terminated
+ - sets may be sorted or unsorted, the caller must distinguish this
+
+ Copyright (c) 1993-2015 The Geometry Center.
+ $Id: //main/2015/qhull/src/libqhull/qset.h#2 $$Change: 2062 $
+ $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $
+*/
+
+#ifndef qhDEFset
+#define qhDEFset 1
+
+#include <stdio.h>
+
+/*================= -structures- ===============*/
+
+#ifndef DEFsetT
+#define DEFsetT 1
+typedef struct setT setT; /* a set is a sorted or unsorted array of pointers */
+#endif
+
+/* [jan'15] Decided not to use countT. Most sets are small. The code uses signed tests */
+
+/*-<a href="qh-set.htm#TOC"
+>----------------------------------------</a><a name="setT">-</a>
+
+setT
+ a set or list of pointers with maximum size and actual size.
+
+variations:
+ unsorted, unique -- a list of unique pointers with NULL terminator
+ user guarantees uniqueness
+ sorted -- a sorted list of unique pointers with NULL terminator
+ qset.c guarantees uniqueness
+ unsorted -- a list of pointers terminated with NULL
+ indexed -- an array of pointers with NULL elements
+
+structure for set of n elements:
+
+ --------------
+ | maxsize
+ --------------
+ | e[0] - a pointer, may be NULL for indexed sets
+ --------------
+ | e[1]
+
+ --------------
+ | ...
+ --------------
+ | e[n-1]
+ --------------
+ | e[n] = NULL
+ --------------
+ | ...
+ --------------
+ | e[maxsize] - n+1 or NULL (determines actual size of set)
+ --------------
+
+*/
+
+/*-- setelemT -- internal type to allow both pointers and indices
+*/
+typedef union setelemT setelemT;
+union setelemT {
+ void *p;
+ int i; /* integer used for e[maxSize] */
+};
+
+struct setT {
+ int maxsize; /* maximum number of elements (except NULL) */
+ setelemT e[1]; /* array of pointers, tail is NULL */
+ /* last slot (unless NULL) is actual size+1
+ e[maxsize]==NULL or e[e[maxsize]-1]==NULL */
+ /* this may generate a warning since e[] contains
+ maxsize elements */
+};
+
+/*=========== -constants- =========================*/
+
+/*-<a href="qh-set.htm#TOC"
+ >-----------------------------------</a><a name="SETelemsize">-</a>
+
+ SETelemsize
+ size of a set element in bytes
+*/
+#define SETelemsize ((int)sizeof(setelemT))
+
+
+/*=========== -macros- =========================*/
+
+/*-<a href="qh-set.htm#TOC"
+ >-----------------------------------</a><a name="FOREACHsetelement_">-</a>
+
+ FOREACHsetelement_(type, set, variable)
+ define FOREACH iterator
+
+ declare:
+ assumes *variable and **variablep are declared
+ no space in "variable)" [DEC Alpha cc compiler]
+
+ each iteration:
+ variable is set element
+ variablep is one beyond variable.
+
+ to repeat an element:
+ variablep--; / *repeat* /
+
+ at exit:
+ variable is NULL at end of loop
+
+ example:
+ #define FOREACHfacet_( facets ) FOREACHsetelement_( facetT, facets, facet )
+
+ notes:
+ use FOREACHsetelement_i_() if need index or include NULLs
+
+ WARNING:
+ nested loops can't use the same variable (define another FOREACH)
+
+ needs braces if nested inside another FOREACH
+ this includes intervening blocks, e.g. FOREACH...{ if () FOREACH...} )
+*/
+#define FOREACHsetelement_(type, set, variable) \
+ if (((variable= NULL), set)) for (\
+ variable##p= (type **)&((set)->e[0].p); \
+ (variable= *variable##p++);)
+
+/*-<a href="qh-set.htm#TOC"
+ >----------------------------------------</a><a name="FOREACHsetelement_i_">-</a>
+
+ FOREACHsetelement_i_(type, set, variable)
+ define indexed FOREACH iterator
+
+ declare:
+ type *variable, variable_n, variable_i;
+
+ each iteration:
+ variable is set element, may be NULL
+ variable_i is index, variable_n is qh_setsize()
+
+ to repeat an element:
+ variable_i--; variable_n-- repeats for deleted element
+
+ at exit:
+ variable==NULL and variable_i==variable_n
+
+ example:
+ #define FOREACHfacet_i_( facets ) FOREACHsetelement_i_( facetT, facets, facet )
+
+ WARNING:
+ nested loops can't use the same variable (define another FOREACH)
+
+ needs braces if nested inside another FOREACH
+ this includes intervening blocks, e.g. FOREACH...{ if () FOREACH...} )
+*/
+#define FOREACHsetelement_i_(type, set, variable) \
+ if (((variable= NULL), set)) for (\
+ variable##_i= 0, variable= (type *)((set)->e[0].p), \
+ variable##_n= qh_setsize(set);\
+ variable##_i < variable##_n;\
+ variable= (type *)((set)->e[++variable##_i].p) )
+
+/*-<a href="qh-set.htm#TOC"
+ >--------------------------------------</a><a name="FOREACHsetelementreverse_">-</a>
+
+ FOREACHsetelementreverse_(type, set, variable)-
+ define FOREACH iterator in reverse order
+
+ declare:
+ assumes *variable and **variablep are declared
+ also declare 'int variabletemp'
+
+ each iteration:
+ variable is set element
+
+ to repeat an element:
+ variabletemp++; / *repeat* /
+
+ at exit:
+ variable is NULL
+
+ example:
+ #define FOREACHvertexreverse_( vertices ) FOREACHsetelementreverse_( vertexT, vertices, vertex )
+
+ notes:
+ use FOREACHsetelementreverse12_() to reverse first two elements
+ WARNING: needs braces if nested inside another FOREACH
+*/
+#define FOREACHsetelementreverse_(type, set, variable) \
+ if (((variable= NULL), set)) for (\
+ variable##temp= qh_setsize(set)-1, variable= qh_setlast(set);\
+ variable; variable= \
+ ((--variable##temp >= 0) ? SETelemt_(set, variable##temp, type) : NULL))
+
+/*-<a href="qh-set.htm#TOC"
+ >-----------------------------------</a><a name="FOREACHsetelementreverse12_">-</a>
+
+ FOREACHsetelementreverse12_(type, set, variable)-
+ define FOREACH iterator with e[1] and e[0] reversed
+
+ declare:
+ assumes *variable and **variablep are declared
+
+ each iteration:
+ variable is set element
+ variablep is one after variable.
+
+ to repeat an element:
+ variablep--; / *repeat* /
+
+ at exit:
+ variable is NULL at end of loop
+
+ example
+ #define FOREACHvertexreverse12_( vertices ) FOREACHsetelementreverse12_( vertexT, vertices, vertex )
+
+ notes:
+ WARNING: needs braces if nested inside another FOREACH
+*/
+#define FOREACHsetelementreverse12_(type, set, variable) \
+ if (((variable= NULL), set)) for (\
+ variable##p= (type **)&((set)->e[1].p); \
+ (variable= *variable##p); \
+ variable##p == ((type **)&((set)->e[0].p))?variable##p += 2: \
+ (variable##p == ((type **)&((set)->e[1].p))?variable##p--:variable##p++))
+
+/*-<a href="qh-set.htm#TOC"
+ >-----------------------------------</a><a name="FOREACHelem_">-</a>
+
+ FOREACHelem_( set )-
+ iterate elements in a set
+
+ declare:
+ void *elem, *elemp;
+
+ each iteration:
+ elem is set element
+ elemp is one beyond
+
+ to repeat an element:
+ elemp--; / *repeat* /
+
+ at exit:
+ elem == NULL at end of loop
+
+ example:
+ FOREACHelem_(set) {
+
+ notes:
+ WARNING: needs braces if nested inside another FOREACH
+*/
+#define FOREACHelem_(set) FOREACHsetelement_(void, set, elem)
+
+/*-<a href="qh-set.htm#TOC"
+ >-----------------------------------</a><a name="FOREACHset_">-</a>
+
+ FOREACHset_( set )-
+ iterate a set of sets
+
+ declare:
+ setT *set, **setp;
+
+ each iteration:
+ set is set element
+ setp is one beyond
+
+ to repeat an element:
+ setp--; / *repeat* /
+
+ at exit:
+ set == NULL at end of loop
+
+ example
+ FOREACHset_(sets) {
+
+ notes:
+ WARNING: needs braces if nested inside another FOREACH
+*/
+#define FOREACHset_(sets) FOREACHsetelement_(setT, sets, set)
+
+/*-<a href="qh-set.htm#TOC"
+ >-----------------------------------------</a><a name="SETindex_">-</a>
+
+ SETindex_( set, elem )
+ return index of elem in set
+
+ notes:
+ for use with FOREACH iteration
+ WARN64 -- Maximum set size is 2G
+
+ example:
+ i= SETindex_(ridges, ridge)
+*/
+#define SETindex_(set, elem) ((int)((void **)elem##p - (void **)&(set)->e[1].p))
+
+/*-<a href="qh-set.htm#TOC"
+ >---------------------------------------</a><a name="SETref_">-</a>
+
+ SETref_( elem )
+ l.h.s. for modifying the current element in a FOREACH iteration
+
+ example:
+ SETref_(ridge)= anotherridge;
+*/
+#define SETref_(elem) (elem##p[-1])
+
+/*-<a href="qh-set.htm#TOC"
+ >---------------------------------------</a><a name="SETelem_">-</a>
+
+ SETelem_(set, n)
+ return the n'th element of set
+
+ notes:
+ assumes that n is valid [0..size] and that set is defined
+ use SETelemt_() for type cast
+*/
+#define SETelem_(set, n) ((set)->e[n].p)
+
+/*-<a href="qh-set.htm#TOC"
+ >---------------------------------------</a><a name="SETelemt_">-</a>
+
+ SETelemt_(set, n, type)
+ return the n'th element of set as a type
+
+ notes:
+ assumes that n is valid [0..size] and that set is defined
+*/
+#define SETelemt_(set, n, type) ((type*)((set)->e[n].p))
+
+/*-<a href="qh-set.htm#TOC"
+ >---------------------------------------</a><a name="SETelemaddr_">-</a>
+
+ SETelemaddr_(set, n, type)
+ return address of the n'th element of a set
+
+ notes:
+ assumes that n is valid [0..size] and set is defined
+*/
+#define SETelemaddr_(set, n, type) ((type **)(&((set)->e[n].p)))
+
+/*-<a href="qh-set.htm#TOC"
+ >---------------------------------------</a><a name="SETfirst_">-</a>
+
+ SETfirst_(set)
+ return first element of set
+
+*/
+#define SETfirst_(set) ((set)->e[0].p)
+
+/*-<a href="qh-set.htm#TOC"
+ >---------------------------------------</a><a name="SETfirstt_">-</a>
+
+ SETfirstt_(set, type)
+ return first element of set as a type
+
+*/
+#define SETfirstt_(set, type) ((type*)((set)->e[0].p))
+
+/*-<a href="qh-set.htm#TOC"
+ >---------------------------------------</a><a name="SETsecond_">-</a>
+
+ SETsecond_(set)
+ return second element of set
+
+*/
+#define SETsecond_(set) ((set)->e[1].p)
+
+/*-<a href="qh-set.htm#TOC"
+ >---------------------------------------</a><a name="SETsecondt_">-</a>
+
+ SETsecondt_(set, type)
+ return second element of set as a type
+*/
+#define SETsecondt_(set, type) ((type*)((set)->e[1].p))
+
+/*-<a href="qh-set.htm#TOC"
+ >---------------------------------------</a><a name="SETaddr_">-</a>
+
+ SETaddr_(set, type)
+ return address of set's elements
+*/
+#define SETaddr_(set,type) ((type **)(&((set)->e[0].p)))
+
+/*-<a href="qh-set.htm#TOC"
+ >---------------------------------------</a><a name="SETreturnsize_">-</a>
+
+ SETreturnsize_(set, size)
+ return size of a set
+
+ notes:
+ set must be defined
+ use qh_setsize(set) unless speed is critical
+*/
+#define SETreturnsize_(set, size) (((size)= ((set)->e[(set)->maxsize].i))?(--(size)):((size)= (set)->maxsize))
+
+/*-<a href="qh-set.htm#TOC"
+ >---------------------------------------</a><a name="SETempty_">-</a>
+
+ SETempty_(set)
+ return true(1) if set is empty
+
+ notes:
+ set may be NULL
+*/
+#define SETempty_(set) (!set || (SETfirst_(set) ? 0 : 1))
+
+/*-<a href="qh-set.htm#TOC"
+ >-------------------------------<a name="SETsizeaddr_">-</a>
+
+ SETsizeaddr_(set)
+ return pointer to 'actual size+1' of set (set CANNOT be NULL!!)
+ Its type is setelemT* for strict aliasing
+ All SETelemaddr_ must be cast to setelemT
+
+
+ notes:
+ *SETsizeaddr==NULL or e[*SETsizeaddr-1].p==NULL
+*/
+#define SETsizeaddr_(set) (&((set)->e[(set)->maxsize]))
+
+/*-<a href="qh-set.htm#TOC"
+ >---------------------------------------</a><a name="SETtruncate_">-</a>
+
+ SETtruncate_(set, size)
+ truncate set to size
+
+ see:
+ qh_settruncate()
+
+*/
+#define SETtruncate_(set, size) {set->e[set->maxsize].i= size+1; /* maybe overwritten */ \
+ set->e[size].p= NULL;}
+
+/*======= prototypes in alphabetical order ============*/
+
+void qh_setaddsorted(setT **setp, void *elem);
+void qh_setaddnth(setT **setp, int nth, void *newelem);
+void qh_setappend(setT **setp, void *elem);
+void qh_setappend_set(setT **setp, setT *setA);
+void qh_setappend2ndlast(setT **setp, void *elem);
+void qh_setcheck(setT *set, const char *tname, unsigned id);
+void qh_setcompact(setT *set);
+setT *qh_setcopy(setT *set, int extra);
+void *qh_setdel(setT *set, void *elem);
+void *qh_setdellast(setT *set);
+void *qh_setdelnth(setT *set, int nth);
+void *qh_setdelnthsorted(setT *set, int nth);
+void *qh_setdelsorted(setT *set, void *newelem);
+setT *qh_setduplicate( setT *set, int elemsize);
+void **qh_setendpointer(setT *set);
+int qh_setequal(setT *setA, setT *setB);
+int qh_setequal_except(setT *setA, void *skipelemA, setT *setB, void *skipelemB);
+int qh_setequal_skip(setT *setA, int skipA, setT *setB, int skipB);
+void qh_setfree(setT **set);
+void qh_setfree2( setT **setp, int elemsize);
+void qh_setfreelong(setT **set);
+int qh_setin(setT *set, void *setelem);
+int qh_setindex(setT *set, void *setelem);
+void qh_setlarger(setT **setp);
+void *qh_setlast(setT *set);
+setT *qh_setnew(int size);
+setT *qh_setnew_delnthsorted(setT *set, int size, int nth, int prepend);
+void qh_setprint(FILE *fp, const char* string, setT *set);
+void qh_setreplace(setT *set, void *oldelem, void *newelem);
+int qh_setsize(setT *set);
+setT *qh_settemp(int setsize);
+void qh_settempfree(setT **set);
+void qh_settempfree_all(void);
+setT *qh_settemppop(void);
+void qh_settemppush(setT *set);
+void qh_settruncate(setT *set, int size);
+int qh_setunique(setT **set, void *elem);
+void qh_setzero(setT *set, int idx, int size);
+
+
+#endif /* qhDEFset */
diff --git a/xs/src/qhull/src/libqhull/random.c b/xs/src/qhull/src/libqhull/random.c
new file mode 100644
index 000000000..176d697ae
--- /dev/null
+++ b/xs/src/qhull/src/libqhull/random.c
@@ -0,0 +1,245 @@
+/*<html><pre> -<a href="index.htm#TOC"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ random.c and utilities
+ Park & Miller's minimimal standard random number generator
+ argc/argv conversion
+
+ Used by rbox. Do not use 'qh'
+*/
+
+#include "libqhull.h"
+#include "random.h"
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef _MSC_VER /* Microsoft Visual C++ -- warning level 4 */
+#pragma warning( disable : 4706) /* assignment within conditional function */
+#pragma warning( disable : 4996) /* function was declared deprecated(strcpy, localtime, etc.) */
+#endif
+
+/*-<a href="qh-globa.htm#TOC"
+ >-------------------------------</a><a name="argv_to_command">-</a>
+
+ qh_argv_to_command( argc, argv, command, max_size )
+
+ build command from argc/argv
+ max_size is at least
+
+ returns:
+ a space-delimited string of options (just as typed)
+ returns false if max_size is too short
+
+ notes:
+ silently removes
+ makes option string easy to input and output
+ matches qh_argv_to_command_size()
+
+ argc may be 0
+*/
+int qh_argv_to_command(int argc, char *argv[], char* command, int max_size) {
+ int i, remaining;
+ char *s;
+ *command= '\0'; /* max_size > 0 */
+
+ if (argc) {
+ if ((s= strrchr( argv[0], '\\')) /* get filename w/o .exe extension */
+ || (s= strrchr( argv[0], '/')))
+ s++;
+ else
+ s= argv[0];
+ if ((int)strlen(s) < max_size) /* WARN64 */
+ strcpy(command, s);
+ else
+ goto error_argv;
+ if ((s= strstr(command, ".EXE"))
+ || (s= strstr(command, ".exe")))
+ *s= '\0';
+ }
+ for (i=1; i < argc; i++) {
+ s= argv[i];
+ remaining= max_size - (int)strlen(command) - (int)strlen(s) - 2; /* WARN64 */
+ if (!*s || strchr(s, ' ')) {
+ char *t= command + strlen(command);
+ remaining -= 2;
+ if (remaining < 0) {
+ goto error_argv;
+ }
+ *t++= ' ';
+ *t++= '"';
+ while (*s) {
+ if (*s == '"') {
+ if (--remaining < 0)
+ goto error_argv;
+ *t++= '\\';
+ }
+ *t++= *s++;
+ }
+ *t++= '"';
+ *t= '\0';
+ }else if (remaining < 0) {
+ goto error_argv;
+ }else
+ strcat(command, " ");
+ strcat(command, s);
+ }
+ return 1;
+
+error_argv:
+ return 0;
+} /* argv_to_command */
+
+/*-<a href="qh-globa.htm#TOC"
+>-------------------------------</a><a name="argv_to_command_size">-</a>
+
+qh_argv_to_command_size( argc, argv )
+
+ return size to allocate for qh_argv_to_command()
+
+notes:
+ argc may be 0
+ actual size is usually shorter
+*/
+int qh_argv_to_command_size(int argc, char *argv[]) {
+ unsigned int count= 1; /* null-terminator if argc==0 */
+ int i;
+ char *s;
+
+ for (i=0; i<argc; i++){
+ count += (int)strlen(argv[i]) + 1; /* WARN64 */
+ if (i>0 && strchr(argv[i], ' ')) {
+ count += 2; /* quote delimiters */
+ for (s=argv[i]; *s; s++) {
+ if (*s == '"') {
+ count++;
+ }
+ }
+ }
+ }
+ return count;
+} /* argv_to_command_size */
+
+/*-<a href="qh-geom.htm#TOC"
+ >-------------------------------</a><a name="rand">-</a>
+
+ qh_rand()
+ qh_srand( seed )
+ generate pseudo-random number between 1 and 2^31 -2
+
+ notes:
+ For qhull and rbox, called from qh_RANDOMint(),etc. [user.h]
+
+ From Park & Miller's minimal standard random number generator
+ Communications of the ACM, 31:1192-1201, 1988.
+ Does not use 0 or 2^31 -1
+ this is silently enforced by qh_srand()
+ Can make 'Rn' much faster by moving qh_rand to qh_distplane
+*/
+
+/* Global variables and constants */
+
+int qh_last_random= 1; /* define as global variable instead of using qh */
+
+#define qh_rand_a 16807
+#define qh_rand_m 2147483647
+#define qh_rand_q 127773 /* m div a */
+#define qh_rand_r 2836 /* m mod a */
+
+int qh_rand( void) {
+ int lo, hi, test;
+ int seed = qh_last_random;
+
+ hi = seed / qh_rand_q; /* seed div q */
+ lo = seed % qh_rand_q; /* seed mod q */
+ test = qh_rand_a * lo - qh_rand_r * hi;
+ if (test > 0)
+ seed= test;
+ else
+ seed= test + qh_rand_m;
+ qh_last_random= seed;
+ /* seed = seed < qh_RANDOMmax/2 ? 0 : qh_RANDOMmax; for testing */
+ /* seed = qh_RANDOMmax; for testing */
+ return seed;
+} /* rand */
+
+void qh_srand( int seed) {
+ if (seed < 1)
+ qh_last_random= 1;
+ else if (seed >= qh_rand_m)
+ qh_last_random= qh_rand_m - 1;
+ else
+ qh_last_random= seed;
+} /* qh_srand */
+
+/*-<a href="qh-geom.htm#TOC"
+>-------------------------------</a><a name="randomfactor">-</a>
+
+qh_randomfactor( scale, offset )
+ return a random factor r * scale + offset
+
+notes:
+ qh.RANDOMa/b are defined in global.c
+*/
+realT qh_randomfactor(realT scale, realT offset) {
+ realT randr;
+
+ randr= qh_RANDOMint;
+ return randr * scale + offset;
+} /* randomfactor */
+
+/*-<a href="qh-geom.htm#TOC"
+>-------------------------------</a><a name="randommatrix">-</a>
+
+qh_randommatrix( buffer, dim, rows )
+ generate a random dim X dim matrix in range [-1,1]
+ assumes buffer is [dim+1, dim]
+
+returns:
+ sets buffer to random numbers
+ sets rows to rows of buffer
+ sets row[dim] as scratch row
+*/
+void qh_randommatrix(realT *buffer, int dim, realT **rows) {
+ int i, k;
+ realT **rowi, *coord, realr;
+
+ coord= buffer;
+ rowi= rows;
+ for (i=0; i < dim; i++) {
+ *(rowi++)= coord;
+ for (k=0; k < dim; k++) {
+ realr= qh_RANDOMint;
+ *(coord++)= 2.0 * realr/(qh_RANDOMmax+1) - 1.0;
+ }
+ }
+ *rowi= coord;
+} /* randommatrix */
+
+/*-<a href="qh-globa.htm#TOC"
+ >-------------------------------</a><a name="strtol">-</a>
+
+ qh_strtol( s, endp) qh_strtod( s, endp)
+ internal versions of strtol() and strtod()
+ does not skip trailing spaces
+ notes:
+ some implementations of strtol()/strtod() skip trailing spaces
+*/
+double qh_strtod(const char *s, char **endp) {
+ double result;
+
+ result= strtod(s, endp);
+ if (s < (*endp) && (*endp)[-1] == ' ')
+ (*endp)--;
+ return result;
+} /* strtod */
+
+int qh_strtol(const char *s, char **endp) {
+ int result;
+
+ result= (int) strtol(s, endp, 10); /* WARN64 */
+ if (s< (*endp) && (*endp)[-1] == ' ')
+ (*endp)--;
+ return result;
+} /* strtol */
diff --git a/xs/src/qhull/src/libqhull/random.h b/xs/src/qhull/src/libqhull/random.h
new file mode 100644
index 000000000..0c6896b76
--- /dev/null
+++ b/xs/src/qhull/src/libqhull/random.h
@@ -0,0 +1,34 @@
+/*<html><pre> -<a href="qh-geom.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ random.h
+ header file for random and utility routines
+
+ see qh-geom.htm and random.c
+
+ Copyright (c) 1993-2015 The Geometry Center.
+ $Id: //main/2015/qhull/src/libqhull/random.h#2 $$Change: 2026 $
+ $DateTime: 2015/11/07 22:44:39 $$Author: bbarber $
+*/
+
+#ifndef qhDEFrandom
+#define qhDEFrandom 1
+
+#include "libqhull.h"
+
+/*============= prototypes in alphabetical order ======= */
+
+
+int qh_argv_to_command(int argc, char *argv[], char* command, int max_size);
+int qh_argv_to_command_size(int argc, char *argv[]);
+int qh_rand( void);
+void qh_srand( int seed);
+realT qh_randomfactor(realT scale, realT offset);
+void qh_randommatrix(realT *buffer, int dim, realT **row);
+int qh_strtol(const char *s, char **endp);
+double qh_strtod(const char *s, char **endp);
+
+#endif /* qhDEFrandom */
+
+
+
diff --git a/xs/src/qhull/src/libqhull/rboxlib.c b/xs/src/qhull/src/libqhull/rboxlib.c
new file mode 100644
index 000000000..f945133fa
--- /dev/null
+++ b/xs/src/qhull/src/libqhull/rboxlib.c
@@ -0,0 +1,870 @@
+/*<html><pre> -<a href="index.htm#TOC"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ rboxlib.c
+ Generate input points
+
+ notes:
+ For documentation, see prompt[] of rbox.c
+ 50 points generated for 'rbox D4'
+
+ WARNING:
+ incorrect range if qh_RANDOMmax is defined wrong (user.h)
+*/
+
+#include "libqhull.h" /* First for user.h */
+#include "random.h"
+
+#include <ctype.h>
+#include <limits.h>
+#include <math.h>
+#include <setjmp.h>
+#include <string.h>
+#include <time.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef _MSC_VER /* Microsoft Visual C++ */
+#pragma warning( disable : 4706) /* assignment within conditional expression. */
+#pragma warning( disable : 4996) /* this function (strncat,sprintf,strcpy) or variable may be unsafe. */
+#endif
+
+#define MAXdim 200
+#define PI 3.1415926535897932384
+
+/* ------------------------------ prototypes ----------------*/
+int qh_roundi( double a);
+void qh_out1( double a);
+void qh_out2n( double a, double b);
+void qh_out3n( double a, double b, double c);
+void qh_outcoord(int iscdd, double *coord, int dim);
+void qh_outcoincident(int coincidentpoints, double radius, int iscdd, double *coord, int dim);
+
+void qh_fprintf_rbox(FILE *fp, int msgcode, const char *fmt, ... );
+void qh_free(void *mem);
+void *qh_malloc(size_t size);
+int qh_rand( void);
+void qh_srand( int seed);
+
+
+/* ------------------------------ globals -------------------*/
+
+/* No state is carried between rbox requests */
+typedef struct rboxT rboxT;
+struct rboxT {
+ FILE *fout;
+ FILE *ferr;
+ int isinteger;
+ double out_offset;
+ jmp_buf errexit; /* exit label for rboxpoints, defined by setjmp(), called by qh_errexit_rbox() */
+ char jmpXtra[40]; /* extra bytes in case jmp_buf is defined wrong by compiler */
+};
+
+
+int rbox_inuse= 0;
+rboxT rbox;
+
+/*-<a href="qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="rboxpoints">-</a>
+
+ qh_rboxpoints( fout, ferr, rbox_command )
+ Generate points to fout according to rbox options
+ Report errors on ferr
+
+ returns:
+ 0 (qh_ERRnone) on success
+ 1 (qh_ERRinput) on input error
+ 4 (qh_ERRmem) on memory error
+ 5 (qh_ERRqhull) on internal error
+
+ notes:
+ To avoid using stdio, redefine qh_malloc, qh_free, and qh_fprintf_rbox (user.c)
+
+ design:
+ Straight line code (consider defining a struct and functions):
+
+ Parse arguments into variables
+ Determine the number of points
+ Generate the points
+*/
+int qh_rboxpoints(FILE* fout, FILE* ferr, char* rbox_command) {
+ int i,j,k;
+ int gendim;
+ int coincidentcount=0, coincidenttotal=0, coincidentpoints=0;
+ int cubesize, diamondsize, seed=0, count, apex;
+ int dim=3, numpoints=0, totpoints, addpoints=0;
+ int issphere=0, isaxis=0, iscdd=0, islens=0, isregular=0, iswidth=0, addcube=0;
+ int isgap=0, isspiral=0, NOcommand=0, adddiamond=0;
+ int israndom=0, istime=0;
+ int isbox=0, issimplex=0, issimplex2=0, ismesh=0;
+ double width=0.0, gap=0.0, radius=0.0, coincidentradius=0.0;
+ double coord[MAXdim], offset, meshm=3.0, meshn=4.0, meshr=5.0;
+ double *coordp, *simplex= NULL, *simplexp;
+ int nthroot, mult[MAXdim];
+ double norm, factor, randr, rangap, lensangle=0, lensbase=1;
+ double anglediff, angle, x, y, cube=0.0, diamond=0.0;
+ double box= qh_DEFAULTbox; /* scale all numbers before output */
+ double randmax= qh_RANDOMmax;
+ char command[200], seedbuf[200];
+ char *s= command, *t, *first_point= NULL;
+ time_t timedata;
+ int exitcode;
+
+ if (rbox_inuse) {
+ qh_fprintf_rbox(rbox.ferr, 6188, "rbox error: rbox in use by another process. Please lock calls to rbox.\n");
+ return qh_ERRqhull;
+ }
+ rbox_inuse= True;
+ rbox.ferr= ferr;
+ rbox.fout= fout;
+
+ exitcode= setjmp(rbox.errexit);
+ if (exitcode) {
+ /* same code for error exit and normal return. qh.NOerrexit is set */
+ if (simplex)
+ qh_free(simplex);
+ rbox_inuse= False;
+ return exitcode;
+ }
+
+ *command= '\0';
+ strncat(command, rbox_command, sizeof(command)-strlen(command)-1);
+
+ while (*s && !isspace(*s)) /* skip program name */
+ s++;
+ while (*s) {
+ while (*s && isspace(*s))
+ s++;
+ if (*s == '-')
+ s++;
+ if (!*s)
+ break;
+ if (isdigit(*s)) {
+ numpoints= qh_strtol(s, &s);
+ continue;
+ }
+ /* ============= read flags =============== */
+ switch (*s++) {
+ case 'c':
+ addcube= 1;
+ t= s;
+ while (isspace(*t))
+ t++;
+ if (*t == 'G')
+ cube= qh_strtod(++t, &s);
+ break;
+ case 'd':
+ adddiamond= 1;
+ t= s;
+ while (isspace(*t))
+ t++;
+ if (*t == 'G')
+ diamond= qh_strtod(++t, &s);
+ break;
+ case 'h':
+ iscdd= 1;
+ break;
+ case 'l':
+ isspiral= 1;
+ break;
+ case 'n':
+ NOcommand= 1;
+ break;
+ case 'r':
+ isregular= 1;
+ break;
+ case 's':
+ issphere= 1;
+ break;
+ case 't':
+ istime= 1;
+ if (isdigit(*s)) {
+ seed= qh_strtol(s, &s);
+ israndom= 0;
+ }else
+ israndom= 1;
+ break;
+ case 'x':
+ issimplex= 1;
+ break;
+ case 'y':
+ issimplex2= 1;
+ break;
+ case 'z':
+ rbox.isinteger= 1;
+ break;
+ case 'B':
+ box= qh_strtod(s, &s);
+ isbox= 1;
+ break;
+ case 'C':
+ if (*s)
+ coincidentpoints= qh_strtol(s, &s);
+ if (*s == ',') {
+ ++s;
+ coincidentradius= qh_strtod(s, &s);
+ }
+ if (*s == ',') {
+ ++s;
+ coincidenttotal= qh_strtol(s, &s);
+ }
+ if (*s && !isspace(*s)) {
+ qh_fprintf_rbox(rbox.ferr, 7080, "rbox error: arguments for 'Cn,r,m' are not 'int', 'float', and 'int'. Remaining string is '%s'\n", s);
+ qh_errexit_rbox(qh_ERRinput);
+ }
+ if (coincidentpoints==0){
+ qh_fprintf_rbox(rbox.ferr, 6268, "rbox error: missing arguments for 'Cn,r,m' where n is the number of coincident points, r is the radius (default 0.0), and m is the number of points\n");
+ qh_errexit_rbox(qh_ERRinput);
+ }
+ if (coincidentpoints<0 || coincidenttotal<0 || coincidentradius<0.0){
+ qh_fprintf_rbox(rbox.ferr, 6269, "rbox error: negative arguments for 'Cn,m,r' where n (%d) is the number of coincident points, m (%d) is the number of points, and r (%.2g) is the radius (default 0.0)\n", coincidentpoints, coincidenttotal, coincidentradius);
+ qh_errexit_rbox(qh_ERRinput);
+ }
+ break;
+ case 'D':
+ dim= qh_strtol(s, &s);
+ if (dim < 1
+ || dim > MAXdim) {
+ qh_fprintf_rbox(rbox.ferr, 6189, "rbox error: dimension, D%d, out of bounds (>=%d or <=0)", dim, MAXdim);
+ qh_errexit_rbox(qh_ERRinput);
+ }
+ break;
+ case 'G':
+ if (isdigit(*s))
+ gap= qh_strtod(s, &s);
+ else
+ gap= 0.5;
+ isgap= 1;
+ break;
+ case 'L':
+ if (isdigit(*s))
+ radius= qh_strtod(s, &s);
+ else
+ radius= 10;
+ islens= 1;
+ break;
+ case 'M':
+ ismesh= 1;
+ if (*s)
+ meshn= qh_strtod(s, &s);
+ if (*s == ',') {
+ ++s;
+ meshm= qh_strtod(s, &s);
+ }else
+ meshm= 0.0;
+ if (*s == ',') {
+ ++s;
+ meshr= qh_strtod(s, &s);
+ }else
+ meshr= sqrt(meshn*meshn + meshm*meshm);
+ if (*s && !isspace(*s)) {
+ qh_fprintf_rbox(rbox.ferr, 7069, "rbox warning: assuming 'M3,4,5' since mesh args are not integers or reals\n");
+ meshn= 3.0, meshm=4.0, meshr=5.0;
+ }
+ break;
+ case 'O':
+ rbox.out_offset= qh_strtod(s, &s);
+ break;
+ case 'P':
+ if (!first_point)
+ first_point= s-1;
+ addpoints++;
+ while (*s && !isspace(*s)) /* read points later */
+ s++;
+ break;
+ case 'W':
+ width= qh_strtod(s, &s);
+ iswidth= 1;
+ break;
+ case 'Z':
+ if (isdigit(*s))
+ radius= qh_strtod(s, &s);
+ else
+ radius= 1.0;
+ isaxis= 1;
+ break;
+ default:
+ qh_fprintf_rbox(rbox.ferr, 7070, "rbox error: unknown flag at %s.\nExecute 'rbox' without arguments for documentation.\n", s);
+ qh_errexit_rbox(qh_ERRinput);
+ }
+ if (*s && !isspace(*s)) {
+ qh_fprintf_rbox(rbox.ferr, 7071, "rbox error: missing space between flags at %s.\n", s);
+ qh_errexit_rbox(qh_ERRinput);
+ }
+ }
+
+ /* ============= defaults, constants, and sizes =============== */
+ if (rbox.isinteger && !isbox)
+ box= qh_DEFAULTzbox;
+ if (addcube) {
+ cubesize= (int)floor(ldexp(1.0,dim)+0.5);
+ if (cube == 0.0)
+ cube= box;
+ }else
+ cubesize= 0;
+ if (adddiamond) {
+ diamondsize= 2*dim;
+ if (diamond == 0.0)
+ diamond= box;
+ }else
+ diamondsize= 0;
+ if (islens) {
+ if (isaxis) {
+ qh_fprintf_rbox(rbox.ferr, 6190, "rbox error: can not combine 'Ln' with 'Zn'\n");
+ qh_errexit_rbox(qh_ERRinput);
+ }
+ if (radius <= 1.0) {
+ qh_fprintf_rbox(rbox.ferr, 6191, "rbox error: lens radius %.2g should be greater than 1.0\n",
+ radius);
+ qh_errexit_rbox(qh_ERRinput);
+ }
+ lensangle= asin(1.0/radius);
+ lensbase= radius * cos(lensangle);
+ }
+
+ if (!numpoints) {
+ if (issimplex2)
+ ; /* ok */
+ else if (isregular + issimplex + islens + issphere + isaxis + isspiral + iswidth + ismesh) {
+ qh_fprintf_rbox(rbox.ferr, 6192, "rbox error: missing count\n");
+ qh_errexit_rbox(qh_ERRinput);
+ }else if (adddiamond + addcube + addpoints)
+ ; /* ok */
+ else {
+ numpoints= 50; /* ./rbox D4 is the test case */
+ issphere= 1;
+ }
+ }
+ if ((issimplex + islens + isspiral + ismesh > 1)
+ || (issimplex + issphere + isspiral + ismesh > 1)) {
+ qh_fprintf_rbox(rbox.ferr, 6193, "rbox error: can only specify one of 'l', 's', 'x', 'Ln', or 'Mn,m,r' ('Ln s' is ok).\n");
+ qh_errexit_rbox(qh_ERRinput);
+ }
+ if (coincidentpoints>0 && (numpoints == 0 || coincidenttotal > numpoints)) {
+ qh_fprintf_rbox(rbox.ferr, 6270, "rbox error: 'Cn,r,m' requested n coincident points for each of m points. Either there is no points or m (%d) is greater than the number of points (%d).\n", coincidenttotal, numpoints);
+ qh_errexit_rbox(qh_ERRinput);
+ }
+ if (coincidenttotal == 0)
+ coincidenttotal= numpoints;
+
+ /* ============= print header with total points =============== */
+ if (issimplex || ismesh)
+ totpoints= numpoints;
+ else if (issimplex2)
+ totpoints= numpoints+dim+1;
+ else if (isregular) {
+ totpoints= numpoints;
+ if (dim == 2) {
+ if (islens)
+ totpoints += numpoints - 2;
+ }else if (dim == 3) {
+ if (islens)
+ totpoints += 2 * numpoints;
+ else if (isgap)
+ totpoints += 1 + numpoints;
+ else
+ totpoints += 2;
+ }
+ }else
+ totpoints= numpoints + isaxis;
+ totpoints += cubesize + diamondsize + addpoints;
+ totpoints += coincidentpoints*coincidenttotal;
+
+ /* ============= seed randoms =============== */
+ if (istime == 0) {
+ for (s=command; *s; s++) {
+ if (issimplex2 && *s == 'y') /* make 'y' same seed as 'x' */
+ i= 'x';
+ else
+ i= *s;
+ seed= 11*seed + i;
+ }
+ }else if (israndom) {
+ seed= (int)time(&timedata);
+ sprintf(seedbuf, " t%d", seed); /* appends an extra t, not worth removing */
+ strncat(command, seedbuf, sizeof(command)-strlen(command)-1);
+ t= strstr(command, " t ");
+ if (t)
+ strcpy(t+1, t+3); /* remove " t " */
+ } /* else, seed explicitly set to n */
+ qh_RANDOMseed_(seed);
+
+ /* ============= print header =============== */
+
+ if (iscdd)
+ qh_fprintf_rbox(rbox.fout, 9391, "%s\nbegin\n %d %d %s\n",
+ NOcommand ? "" : command,
+ totpoints, dim+1,
+ rbox.isinteger ? "integer" : "real");
+ else if (NOcommand)
+ qh_fprintf_rbox(rbox.fout, 9392, "%d\n%d\n", dim, totpoints);
+ else
+ /* qh_fprintf_rbox special cases 9393 to append 'command' to the RboxPoints.comment() */
+ qh_fprintf_rbox(rbox.fout, 9393, "%d %s\n%d\n", dim, command, totpoints);
+
+ /* ============= explicit points =============== */
+ if ((s= first_point)) {
+ while (s && *s) { /* 'P' */
+ count= 0;
+ if (iscdd)
+ qh_out1( 1.0);
+ while (*++s) {
+ qh_out1( qh_strtod(s, &s));
+ count++;
+ if (isspace(*s) || !*s)
+ break;
+ if (*s != ',') {
+ qh_fprintf_rbox(rbox.ferr, 6194, "rbox error: missing comma after coordinate in %s\n\n", s);
+ qh_errexit_rbox(qh_ERRinput);
+ }
+ }
+ if (count < dim) {
+ for (k=dim-count; k--; )
+ qh_out1( 0.0);
+ }else if (count > dim) {
+ qh_fprintf_rbox(rbox.ferr, 6195, "rbox error: %d coordinates instead of %d coordinates in %s\n\n",
+ count, dim, s);
+ qh_errexit_rbox(qh_ERRinput);
+ }
+ qh_fprintf_rbox(rbox.fout, 9394, "\n");
+ while ((s= strchr(s, 'P'))) {
+ if (isspace(s[-1]))
+ break;
+ }
+ }
+ }
+
+ /* ============= simplex distribution =============== */
+ if (issimplex+issimplex2) {
+ if (!(simplex= (double*)qh_malloc( dim * (dim+1) * sizeof(double)))) {
+ qh_fprintf_rbox(rbox.ferr, 6196, "rbox error: insufficient memory for simplex\n");
+ qh_errexit_rbox(qh_ERRmem); /* qh_ERRmem */
+ }
+ simplexp= simplex;
+ if (isregular) {
+ for (i=0; i<dim; i++) {
+ for (k=0; k<dim; k++)
+ *(simplexp++)= i==k ? 1.0 : 0.0;
+ }
+ for (k=0; k<dim; k++)
+ *(simplexp++)= -1.0;
+ }else {
+ for (i=0; i<dim+1; i++) {
+ for (k=0; k<dim; k++) {
+ randr= qh_RANDOMint;
+ *(simplexp++)= 2.0 * randr/randmax - 1.0;
+ }
+ }
+ }
+ if (issimplex2) {
+ simplexp= simplex;
+ for (i=0; i<dim+1; i++) {
+ if (iscdd)
+ qh_out1( 1.0);
+ for (k=0; k<dim; k++)
+ qh_out1( *(simplexp++) * box);
+ qh_fprintf_rbox(rbox.fout, 9395, "\n");
+ }
+ }
+ for (j=0; j<numpoints; j++) {
+ if (iswidth)
+ apex= qh_RANDOMint % (dim+1);
+ else
+ apex= -1;
+ for (k=0; k<dim; k++)
+ coord[k]= 0.0;
+ norm= 0.0;
+ for (i=0; i<dim+1; i++) {
+ randr= qh_RANDOMint;
+ factor= randr/randmax;
+ if (i == apex)
+ factor *= width;
+ norm += factor;
+ for (k=0; k<dim; k++) {
+ simplexp= simplex + i*dim + k;
+ coord[k] += factor * (*simplexp);
+ }
+ }
+ for (k=0; k<dim; k++)
+ coord[k] *= box/norm;
+ qh_outcoord(iscdd, coord, dim);
+ if(coincidentcount++ < coincidenttotal)
+ qh_outcoincident(coincidentpoints, coincidentradius, iscdd, coord, dim);
+ }
+ isregular= 0; /* continue with isbox */
+ numpoints= 0;
+ }
+
+ /* ============= mesh distribution =============== */
+ if (ismesh) {
+ nthroot= (int)(pow((double)numpoints, 1.0/dim) + 0.99999);
+ for (k=dim; k--; )
+ mult[k]= 0;
+ for (i=0; i < numpoints; i++) {
+ coordp= coord;
+ for (k=0; k < dim; k++) {
+ if (k == 0)
+ *(coordp++)= mult[0] * meshn + mult[1] * (-meshm);
+ else if (k == 1)
+ *(coordp++)= mult[0] * meshm + mult[1] * meshn;
+ else
+ *(coordp++)= mult[k] * meshr;
+ }
+ qh_outcoord(iscdd, coord, dim);
+ if(coincidentcount++ < coincidenttotal)
+ qh_outcoincident(coincidentpoints, coincidentradius, iscdd, coord, dim);
+ for (k=0; k < dim; k++) {
+ if (++mult[k] < nthroot)
+ break;
+ mult[k]= 0;
+ }
+ }
+ }
+ /* ============= regular points for 's' =============== */
+ else if (isregular && !islens) {
+ if (dim != 2 && dim != 3) {
+ qh_free(simplex);
+ qh_fprintf_rbox(rbox.ferr, 6197, "rbox error: regular points can be used only in 2-d and 3-d\n\n");
+ qh_errexit_rbox(qh_ERRinput);
+ }
+ if (!isaxis || radius == 0.0) {
+ isaxis= 1;
+ radius= 1.0;
+ }
+ if (dim == 3) {
+ if (iscdd)
+ qh_out1( 1.0);
+ qh_out3n( 0.0, 0.0, -box);
+ if (!isgap) {
+ if (iscdd)
+ qh_out1( 1.0);
+ qh_out3n( 0.0, 0.0, box);
+ }
+ }
+ angle= 0.0;
+ anglediff= 2.0 * PI/numpoints;
+ for (i=0; i < numpoints; i++) {
+ angle += anglediff;
+ x= radius * cos(angle);
+ y= radius * sin(angle);
+ if (dim == 2) {
+ if (iscdd)
+ qh_out1( 1.0);
+ qh_out2n( x*box, y*box);
+ }else {
+ norm= sqrt(1.0 + x*x + y*y);
+ if (iscdd)
+ qh_out1( 1.0);
+ qh_out3n( box*x/norm, box*y/norm, box/norm);
+ if (isgap) {
+ x *= 1-gap;
+ y *= 1-gap;
+ norm= sqrt(1.0 + x*x + y*y);
+ if (iscdd)
+ qh_out1( 1.0);
+ qh_out3n( box*x/norm, box*y/norm, box/norm);
+ }
+ }
+ }
+ }
+ /* ============= regular points for 'r Ln D2' =============== */
+ else if (isregular && islens && dim == 2) {
+ double cos_0;
+
+ angle= lensangle;
+ anglediff= 2 * lensangle/(numpoints - 1);
+ cos_0= cos(lensangle);
+ for (i=0; i < numpoints; i++, angle -= anglediff) {
+ x= radius * sin(angle);
+ y= radius * (cos(angle) - cos_0);
+ if (iscdd)
+ qh_out1( 1.0);
+ qh_out2n( x*box, y*box);
+ if (i != 0 && i != numpoints - 1) {
+ if (iscdd)
+ qh_out1( 1.0);
+ qh_out2n( x*box, -y*box);
+ }
+ }
+ }
+ /* ============= regular points for 'r Ln D3' =============== */
+ else if (isregular && islens && dim != 2) {
+ if (dim != 3) {
+ qh_free(simplex);
+ qh_fprintf_rbox(rbox.ferr, 6198, "rbox error: regular points can be used only in 2-d and 3-d\n\n");
+ qh_errexit_rbox(qh_ERRinput);
+ }
+ angle= 0.0;
+ anglediff= 2* PI/numpoints;
+ if (!isgap) {
+ isgap= 1;
+ gap= 0.5;
+ }
+ offset= sqrt(radius * radius - (1-gap)*(1-gap)) - lensbase;
+ for (i=0; i < numpoints; i++, angle += anglediff) {
+ x= cos(angle);
+ y= sin(angle);
+ if (iscdd)
+ qh_out1( 1.0);
+ qh_out3n( box*x, box*y, 0.0);
+ x *= 1-gap;
+ y *= 1-gap;
+ if (iscdd)
+ qh_out1( 1.0);
+ qh_out3n( box*x, box*y, box * offset);
+ if (iscdd)
+ qh_out1( 1.0);
+ qh_out3n( box*x, box*y, -box * offset);
+ }
+ }
+ /* ============= apex of 'Zn' distribution + gendim =============== */
+ else {
+ if (isaxis) {
+ gendim= dim-1;
+ if (iscdd)
+ qh_out1( 1.0);
+ for (j=0; j < gendim; j++)
+ qh_out1( 0.0);
+ qh_out1( -box);
+ qh_fprintf_rbox(rbox.fout, 9398, "\n");
+ }else if (islens)
+ gendim= dim-1;
+ else
+ gendim= dim;
+ /* ============= generate random point in unit cube =============== */
+ for (i=0; i < numpoints; i++) {
+ norm= 0.0;
+ for (j=0; j < gendim; j++) {
+ randr= qh_RANDOMint;
+ coord[j]= 2.0 * randr/randmax - 1.0;
+ norm += coord[j] * coord[j];
+ }
+ norm= sqrt(norm);
+ /* ============= dim-1 point of 'Zn' distribution ========== */
+ if (isaxis) {
+ if (!isgap) {
+ isgap= 1;
+ gap= 1.0;
+ }
+ randr= qh_RANDOMint;
+ rangap= 1.0 - gap * randr/randmax;
+ factor= radius * rangap / norm;
+ for (j=0; j<gendim; j++)
+ coord[j]= factor * coord[j];
+ /* ============= dim-1 point of 'Ln s' distribution =========== */
+ }else if (islens && issphere) {
+ if (!isgap) {
+ isgap= 1;
+ gap= 1.0;
+ }
+ randr= qh_RANDOMint;
+ rangap= 1.0 - gap * randr/randmax;
+ factor= rangap / norm;
+ for (j=0; j<gendim; j++)
+ coord[j]= factor * coord[j];
+ /* ============= dim-1 point of 'Ln' distribution ========== */
+ }else if (islens && !issphere) {
+ if (!isgap) {
+ isgap= 1;
+ gap= 1.0;
+ }
+ j= qh_RANDOMint % gendim;
+ if (coord[j] < 0)
+ coord[j]= -1.0 - coord[j] * gap;
+ else
+ coord[j]= 1.0 - coord[j] * gap;
+ /* ============= point of 'l' distribution =============== */
+ }else if (isspiral) {
+ if (dim != 3) {
+ qh_free(simplex);
+ qh_fprintf_rbox(rbox.ferr, 6199, "rbox error: spiral distribution is available only in 3d\n\n");
+ qh_errexit_rbox(qh_ERRinput);
+ }
+ coord[0]= cos(2*PI*i/(numpoints - 1));
+ coord[1]= sin(2*PI*i/(numpoints - 1));
+ coord[2]= 2.0*(double)i/(double)(numpoints-1) - 1.0;
+ /* ============= point of 's' distribution =============== */
+ }else if (issphere) {
+ factor= 1.0/norm;
+ if (iswidth) {
+ randr= qh_RANDOMint;
+ factor *= 1.0 - width * randr/randmax;
+ }
+ for (j=0; j<dim; j++)
+ coord[j]= factor * coord[j];
+ }
+ /* ============= project 'Zn s' point in to sphere =============== */
+ if (isaxis && issphere) {
+ coord[dim-1]= 1.0;
+ norm= 1.0;
+ for (j=0; j<gendim; j++)
+ norm += coord[j] * coord[j];
+ norm= sqrt(norm);
+ for (j=0; j<dim; j++)
+ coord[j]= coord[j] / norm;
+ if (iswidth) {
+ randr= qh_RANDOMint;
+ coord[dim-1] *= 1 - width * randr/randmax;
+ }
+ /* ============= project 'Zn' point onto cube =============== */
+ }else if (isaxis && !issphere) { /* not very interesting */
+ randr= qh_RANDOMint;
+ coord[dim-1]= 2.0 * randr/randmax - 1.0;
+ /* ============= project 'Ln' point out to sphere =============== */
+ }else if (islens) {
+ coord[dim-1]= lensbase;
+ for (j=0, norm= 0; j<dim; j++)
+ norm += coord[j] * coord[j];
+ norm= sqrt(norm);
+ for (j=0; j<dim; j++)
+ coord[j]= coord[j] * radius/ norm;
+ coord[dim-1] -= lensbase;
+ if (iswidth) {
+ randr= qh_RANDOMint;
+ coord[dim-1] *= 1 - width * randr/randmax;
+ }
+ if (qh_RANDOMint > randmax/2)
+ coord[dim-1]= -coord[dim-1];
+ /* ============= project 'Wn' point toward boundary =============== */
+ }else if (iswidth && !issphere) {
+ j= qh_RANDOMint % gendim;
+ if (coord[j] < 0)
+ coord[j]= -1.0 - coord[j] * width;
+ else
+ coord[j]= 1.0 - coord[j] * width;
+ }
+ /* ============= scale point to box =============== */
+ for (k=0; k<dim; k++)
+ coord[k]= coord[k] * box;
+
+ /* ============= write output =============== */
+ qh_outcoord(iscdd, coord, dim);
+ if(coincidentcount++ < coincidenttotal)
+ qh_outcoincident(coincidentpoints, coincidentradius, iscdd, coord, dim);
+ }
+ }
+
+ /* ============= write cube vertices =============== */
+ if (addcube) {
+ for (j=0; j<cubesize; j++) {
+ if (iscdd)
+ qh_out1( 1.0);
+ for (k=dim-1; k>=0; k--) {
+ if (j & ( 1 << k))
+ qh_out1( cube);
+ else
+ qh_out1( -cube);
+ }
+ qh_fprintf_rbox(rbox.fout, 9400, "\n");
+ }
+ }
+
+ /* ============= write diamond vertices =============== */
+ if (adddiamond) {
+ for (j=0; j<diamondsize; j++) {
+ if (iscdd)
+ qh_out1( 1.0);
+ for (k=dim-1; k>=0; k--) {
+ if (j/2 != k)
+ qh_out1( 0.0);
+ else if (j & 0x1)
+ qh_out1( diamond);
+ else
+ qh_out1( -diamond);
+ }
+ qh_fprintf_rbox(rbox.fout, 9401, "\n");
+ }
+ }
+
+ if (iscdd)
+ qh_fprintf_rbox(rbox.fout, 9402, "end\nhull\n");
+
+ /* same code for error exit and normal return */
+ qh_free(simplex);
+ rbox_inuse= False;
+ return qh_ERRnone;
+} /* rboxpoints */
+
+/*------------------------------------------------
+outxxx - output functions for qh_rboxpoints
+*/
+int qh_roundi( double a) {
+ if (a < 0.0) {
+ if (a - 0.5 < INT_MIN) {
+ qh_fprintf_rbox(rbox.ferr, 6200, "rbox input error: negative coordinate %2.2g is too large. Reduce 'Bn'\n", a);
+ qh_errexit_rbox(qh_ERRinput);
+ }
+ return (int)(a - 0.5);
+ }else {
+ if (a + 0.5 > INT_MAX) {
+ qh_fprintf_rbox(rbox.ferr, 6201, "rbox input error: coordinate %2.2g is too large. Reduce 'Bn'\n", a);
+ qh_errexit_rbox(qh_ERRinput);
+ }
+ return (int)(a + 0.5);
+ }
+} /* qh_roundi */
+
+void qh_out1(double a) {
+
+ if (rbox.isinteger)
+ qh_fprintf_rbox(rbox.fout, 9403, "%d ", qh_roundi( a+rbox.out_offset));
+ else
+ qh_fprintf_rbox(rbox.fout, 9404, qh_REAL_1, a+rbox.out_offset);
+} /* qh_out1 */
+
+void qh_out2n( double a, double b) {
+
+ if (rbox.isinteger)
+ qh_fprintf_rbox(rbox.fout, 9405, "%d %d\n", qh_roundi(a+rbox.out_offset), qh_roundi(b+rbox.out_offset));
+ else
+ qh_fprintf_rbox(rbox.fout, 9406, qh_REAL_2n, a+rbox.out_offset, b+rbox.out_offset);
+} /* qh_out2n */
+
+void qh_out3n( double a, double b, double c) {
+
+ if (rbox.isinteger)
+ qh_fprintf_rbox(rbox.fout, 9407, "%d %d %d\n", qh_roundi(a+rbox.out_offset), qh_roundi(b+rbox.out_offset), qh_roundi(c+rbox.out_offset));
+ else
+ qh_fprintf_rbox(rbox.fout, 9408, qh_REAL_3n, a+rbox.out_offset, b+rbox.out_offset, c+rbox.out_offset);
+} /* qh_out3n */
+
+void qh_outcoord(int iscdd, double *coord, int dim) {
+ double *p= coord;
+ int k;
+
+ if (iscdd)
+ qh_out1( 1.0);
+ for (k=0; k < dim; k++)
+ qh_out1(*(p++));
+ qh_fprintf_rbox(rbox.fout, 9396, "\n");
+} /* qh_outcoord */
+
+void qh_outcoincident(int coincidentpoints, double radius, int iscdd, double *coord, int dim) {
+ double *p;
+ double randr, delta;
+ int i,k;
+ double randmax= qh_RANDOMmax;
+
+ for (i= 0; i<coincidentpoints; i++) {
+ p= coord;
+ if (iscdd)
+ qh_out1( 1.0);
+ for (k=0; k < dim; k++) {
+ randr= qh_RANDOMint;
+ delta= 2.0 * randr/randmax - 1.0; /* -1..+1 */
+ delta *= radius;
+ qh_out1(*(p++) + delta);
+ }
+ qh_fprintf_rbox(rbox.fout, 9410, "\n");
+ }
+} /* qh_outcoincident */
+
+/*------------------------------------------------
+ Only called from qh_rboxpoints or qh_fprintf_rbox
+ qh_fprintf_rbox is only called from qh_rboxpoints
+*/
+void qh_errexit_rbox(int exitcode)
+{
+ longjmp(rbox.errexit, exitcode);
+} /* qh_errexit_rbox */
+
diff --git a/xs/src/qhull/src/libqhull/stat.c b/xs/src/qhull/src/libqhull/stat.c
new file mode 100644
index 000000000..70196bbb0
--- /dev/null
+++ b/xs/src/qhull/src/libqhull/stat.c
@@ -0,0 +1,717 @@
+/*<html><pre> -<a href="qh-stat.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ stat.c
+ contains all statistics that are collected for qhull
+
+ see qh-stat.htm and stat.h
+
+ Copyright (c) 1993-2015 The Geometry Center.
+ $Id: //main/2015/qhull/src/libqhull/stat.c#5 $$Change: 2062 $
+ $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $
+*/
+
+#include "qhull_a.h"
+
+/*============ global data structure ==========*/
+
+#if qh_QHpointer
+qhstatT *qh_qhstat=NULL; /* global data structure */
+#else
+qhstatT qh_qhstat; /* add "={0}" if this causes a compiler error */
+#endif
+
+/*========== functions in alphabetic order ================*/
+
+/*-<a href="qh-stat.htm#TOC"
+ >-------------------------------</a><a name="allstatA">-</a>
+
+ qh_allstatA()
+ define statistics in groups of 20
+
+ notes:
+ (otherwise, 'gcc -O2' uses too much memory)
+ uses qhstat.next
+*/
+void qh_allstatA(void) {
+
+ /* zdef_(type,name,doc,average) */
+ zzdef_(zdoc, Zdoc2, "precision statistics", -1);
+ zdef_(zinc, Znewvertex, NULL, -1);
+ zdef_(wadd, Wnewvertex, "ave. distance of a new vertex to a facet(!0s)", Znewvertex);
+ zzdef_(wmax, Wnewvertexmax, "max. distance of a new vertex to a facet", -1);
+ zdef_(wmax, Wvertexmax, "max. distance of an output vertex to a facet", -1);
+ zdef_(wmin, Wvertexmin, "min. distance of an output vertex to a facet", -1);
+ zdef_(wmin, Wmindenom, "min. denominator in hyperplane computation", -1);
+
+ qhstat precision= qhstat next; /* call qh_precision for each of these */
+ zzdef_(zdoc, Zdoc3, "precision problems (corrected unless 'Q0' or an error)", -1);
+ zzdef_(zinc, Zcoplanarridges, "coplanar half ridges in output", -1);
+ zzdef_(zinc, Zconcaveridges, "concave half ridges in output", -1);
+ zzdef_(zinc, Zflippedfacets, "flipped facets", -1);
+ zzdef_(zinc, Zcoplanarhorizon, "coplanar horizon facets for new vertices", -1);
+ zzdef_(zinc, Zcoplanarpart, "coplanar points during partitioning", -1);
+ zzdef_(zinc, Zminnorm, "degenerate hyperplanes recomputed with gaussian elimination", -1);
+ zzdef_(zinc, Znearlysingular, "nearly singular or axis-parallel hyperplanes", -1);
+ zzdef_(zinc, Zback0, "zero divisors during back substitute", -1);
+ zzdef_(zinc, Zgauss0, "zero divisors during gaussian elimination", -1);
+ zzdef_(zinc, Zmultiridge, "ridges with multiple neighbors", -1);
+}
+void qh_allstatB(void) {
+ zzdef_(zdoc, Zdoc1, "summary information", -1);
+ zdef_(zinc, Zvertices, "number of vertices in output", -1);
+ zdef_(zinc, Znumfacets, "number of facets in output", -1);
+ zdef_(zinc, Znonsimplicial, "number of non-simplicial facets in output", -1);
+ zdef_(zinc, Znowsimplicial, "number of simplicial facets that were merged", -1);
+ zdef_(zinc, Znumridges, "number of ridges in output", -1);
+ zdef_(zadd, Znumridges, "average number of ridges per facet", Znumfacets);
+ zdef_(zmax, Zmaxridges, "maximum number of ridges", -1);
+ zdef_(zadd, Znumneighbors, "average number of neighbors per facet", Znumfacets);
+ zdef_(zmax, Zmaxneighbors, "maximum number of neighbors", -1);
+ zdef_(zadd, Znumvertices, "average number of vertices per facet", Znumfacets);
+ zdef_(zmax, Zmaxvertices, "maximum number of vertices", -1);
+ zdef_(zadd, Znumvneighbors, "average number of neighbors per vertex", Zvertices);
+ zdef_(zmax, Zmaxvneighbors, "maximum number of neighbors", -1);
+ zdef_(wadd, Wcpu, "cpu seconds for qhull after input", -1);
+ zdef_(zinc, Ztotvertices, "vertices created altogether", -1);
+ zzdef_(zinc, Zsetplane, "facets created altogether", -1);
+ zdef_(zinc, Ztotridges, "ridges created altogether", -1);
+ zdef_(zinc, Zpostfacets, "facets before post merge", -1);
+ zdef_(zadd, Znummergetot, "average merges per facet(at most 511)", Znumfacets);
+ zdef_(zmax, Znummergemax, " maximum merges for a facet(at most 511)", -1);
+ zdef_(zinc, Zangle, NULL, -1);
+ zdef_(wadd, Wangle, "average angle(cosine) of facet normals for all ridges", Zangle);
+ zdef_(wmax, Wanglemax, " maximum angle(cosine) of facet normals across a ridge", -1);
+ zdef_(wmin, Wanglemin, " minimum angle(cosine) of facet normals across a ridge", -1);
+ zdef_(wadd, Wareatot, "total area of facets", -1);
+ zdef_(wmax, Wareamax, " maximum facet area", -1);
+ zdef_(wmin, Wareamin, " minimum facet area", -1);
+}
+void qh_allstatC(void) {
+ zdef_(zdoc, Zdoc9, "build hull statistics", -1);
+ zzdef_(zinc, Zprocessed, "points processed", -1);
+ zzdef_(zinc, Zretry, "retries due to precision problems", -1);
+ zdef_(wmax, Wretrymax, " max. random joggle", -1);
+ zdef_(zmax, Zmaxvertex, "max. vertices at any one time", -1);
+ zdef_(zinc, Ztotvisible, "ave. visible facets per iteration", Zprocessed);
+ zdef_(zinc, Zinsidevisible, " ave. visible facets without an horizon neighbor", Zprocessed);
+ zdef_(zadd, Zvisfacettot, " ave. facets deleted per iteration", Zprocessed);
+ zdef_(zmax, Zvisfacetmax, " maximum", -1);
+ zdef_(zadd, Zvisvertextot, "ave. visible vertices per iteration", Zprocessed);
+ zdef_(zmax, Zvisvertexmax, " maximum", -1);
+ zdef_(zinc, Ztothorizon, "ave. horizon facets per iteration", Zprocessed);
+ zdef_(zadd, Znewfacettot, "ave. new or merged facets per iteration", Zprocessed);
+ zdef_(zmax, Znewfacetmax, " maximum(includes initial simplex)", -1);
+ zdef_(wadd, Wnewbalance, "average new facet balance", Zprocessed);
+ zdef_(wadd, Wnewbalance2, " standard deviation", -1);
+ zdef_(wadd, Wpbalance, "average partition balance", Zpbalance);
+ zdef_(wadd, Wpbalance2, " standard deviation", -1);
+ zdef_(zinc, Zpbalance, " number of trials", -1);
+ zdef_(zinc, Zsearchpoints, "searches of all points for initial simplex", -1);
+ zdef_(zinc, Zdetsimplex, "determinants computed(area & initial hull)", -1);
+ zdef_(zinc, Znoarea, "determinants not computed because vertex too low", -1);
+ zdef_(zinc, Znotmax, "points ignored(!above max_outside)", -1);
+ zdef_(zinc, Znotgood, "points ignored(!above a good facet)", -1);
+ zdef_(zinc, Znotgoodnew, "points ignored(didn't create a good new facet)", -1);
+ zdef_(zinc, Zgoodfacet, "good facets found", -1);
+ zzdef_(zinc, Znumvisibility, "distance tests for facet visibility", -1);
+ zdef_(zinc, Zdistvertex, "distance tests to report minimum vertex", -1);
+ zzdef_(zinc, Ztotcheck, "points checked for facets' outer planes", -1);
+ zzdef_(zinc, Zcheckpart, " ave. distance tests per check", Ztotcheck);
+}
+void qh_allstatD(void) {
+ zdef_(zinc, Zvisit, "resets of visit_id", -1);
+ zdef_(zinc, Zvvisit, " resets of vertex_visit", -1);
+ zdef_(zmax, Zvisit2max, " max visit_id/2", -1);
+ zdef_(zmax, Zvvisit2max, " max vertex_visit/2", -1);
+
+ zdef_(zdoc, Zdoc4, "partitioning statistics(see previous for outer planes)", -1);
+ zzdef_(zadd, Zdelvertextot, "total vertices deleted", -1);
+ zdef_(zmax, Zdelvertexmax, " maximum vertices deleted per iteration", -1);
+ zdef_(zinc, Zfindbest, "calls to findbest", -1);
+ zdef_(zadd, Zfindbesttot, " ave. facets tested", Zfindbest);
+ zdef_(zmax, Zfindbestmax, " max. facets tested", -1);
+ zdef_(zadd, Zfindcoplanar, " ave. coplanar search", Zfindbest);
+ zdef_(zinc, Zfindnew, "calls to findbestnew", -1);
+ zdef_(zadd, Zfindnewtot, " ave. facets tested", Zfindnew);
+ zdef_(zmax, Zfindnewmax, " max. facets tested", -1);
+ zdef_(zinc, Zfindnewjump, " ave. clearly better", Zfindnew);
+ zdef_(zinc, Zfindnewsharp, " calls due to qh_sharpnewfacets", -1);
+ zdef_(zinc, Zfindhorizon, "calls to findhorizon", -1);
+ zdef_(zadd, Zfindhorizontot, " ave. facets tested", Zfindhorizon);
+ zdef_(zmax, Zfindhorizonmax, " max. facets tested", -1);
+ zdef_(zinc, Zfindjump, " ave. clearly better", Zfindhorizon);
+ zdef_(zinc, Zparthorizon, " horizon facets better than bestfacet", -1);
+ zdef_(zinc, Zpartangle, "angle tests for repartitioned coplanar points", -1);
+ zdef_(zinc, Zpartflip, " repartitioned coplanar points for flipped orientation", -1);
+}
+void qh_allstatE(void) {
+ zdef_(zinc, Zpartinside, "inside points", -1);
+ zdef_(zinc, Zpartnear, " inside points kept with a facet", -1);
+ zdef_(zinc, Zcoplanarinside, " inside points that were coplanar with a facet", -1);
+ zdef_(zinc, Zbestlower, "calls to findbestlower", -1);
+ zdef_(zinc, Zbestlowerv, " with search of vertex neighbors", -1);
+ zdef_(zinc, Zbestlowerall, " with rare search of all facets", -1);
+ zdef_(zmax, Zbestloweralln, " facets per search of all facets", -1);
+ zdef_(wadd, Wmaxout, "difference in max_outside at final check", -1);
+ zzdef_(zinc, Zpartitionall, "distance tests for initial partition", -1);
+ zdef_(zinc, Ztotpartition, "partitions of a point", -1);
+ zzdef_(zinc, Zpartition, "distance tests for partitioning", -1);
+ zzdef_(zinc, Zdistcheck, "distance tests for checking flipped facets", -1);
+ zzdef_(zinc, Zdistconvex, "distance tests for checking convexity", -1);
+ zdef_(zinc, Zdistgood, "distance tests for checking good point", -1);
+ zdef_(zinc, Zdistio, "distance tests for output", -1);
+ zdef_(zinc, Zdiststat, "distance tests for statistics", -1);
+ zdef_(zinc, Zdistplane, "total number of distance tests", -1);
+ zdef_(zinc, Ztotpartcoplanar, "partitions of coplanar points or deleted vertices", -1);
+ zzdef_(zinc, Zpartcoplanar, " distance tests for these partitions", -1);
+ zdef_(zinc, Zcomputefurthest, "distance tests for computing furthest", -1);
+}
+void qh_allstatE2(void) {
+ zdef_(zdoc, Zdoc5, "statistics for matching ridges", -1);
+ zdef_(zinc, Zhashlookup, "total lookups for matching ridges of new facets", -1);
+ zdef_(zinc, Zhashtests, "average number of tests to match a ridge", Zhashlookup);
+ zdef_(zinc, Zhashridge, "total lookups of subridges(duplicates and boundary)", -1);
+ zdef_(zinc, Zhashridgetest, "average number of tests per subridge", Zhashridge);
+ zdef_(zinc, Zdupsame, "duplicated ridges in same merge cycle", -1);
+ zdef_(zinc, Zdupflip, "duplicated ridges with flipped facets", -1);
+
+ zdef_(zdoc, Zdoc6, "statistics for determining merges", -1);
+ zdef_(zinc, Zangletests, "angles computed for ridge convexity", -1);
+ zdef_(zinc, Zbestcentrum, "best merges used centrum instead of vertices",-1);
+ zzdef_(zinc, Zbestdist, "distance tests for best merge", -1);
+ zzdef_(zinc, Zcentrumtests, "distance tests for centrum convexity", -1);
+ zzdef_(zinc, Zdistzero, "distance tests for checking simplicial convexity", -1);
+ zdef_(zinc, Zcoplanarangle, "coplanar angles in getmergeset", -1);
+ zdef_(zinc, Zcoplanarcentrum, "coplanar centrums in getmergeset", -1);
+ zdef_(zinc, Zconcaveridge, "concave ridges in getmergeset", -1);
+}
+void qh_allstatF(void) {
+ zdef_(zdoc, Zdoc7, "statistics for merging", -1);
+ zdef_(zinc, Zpremergetot, "merge iterations", -1);
+ zdef_(zadd, Zmergeinittot, "ave. initial non-convex ridges per iteration", Zpremergetot);
+ zdef_(zadd, Zmergeinitmax, " maximum", -1);
+ zdef_(zadd, Zmergesettot, " ave. additional non-convex ridges per iteration", Zpremergetot);
+ zdef_(zadd, Zmergesetmax, " maximum additional in one pass", -1);
+ zdef_(zadd, Zmergeinittot2, "initial non-convex ridges for post merging", -1);
+ zdef_(zadd, Zmergesettot2, " additional non-convex ridges", -1);
+ zdef_(wmax, Wmaxoutside, "max distance of vertex or coplanar point above facet(w/roundoff)", -1);
+ zdef_(wmin, Wminvertex, "max distance of merged vertex below facet(or roundoff)", -1);
+ zdef_(zinc, Zwidefacet, "centrums frozen due to a wide merge", -1);
+ zdef_(zinc, Zwidevertices, "centrums frozen due to extra vertices", -1);
+ zzdef_(zinc, Ztotmerge, "total number of facets or cycles of facets merged", -1);
+ zdef_(zinc, Zmergesimplex, "merged a simplex", -1);
+ zdef_(zinc, Zonehorizon, "simplices merged into coplanar horizon", -1);
+ zzdef_(zinc, Zcyclehorizon, "cycles of facets merged into coplanar horizon", -1);
+ zzdef_(zadd, Zcyclefacettot, " ave. facets per cycle", Zcyclehorizon);
+ zdef_(zmax, Zcyclefacetmax, " max. facets", -1);
+ zdef_(zinc, Zmergeintohorizon, "new facets merged into horizon", -1);
+ zdef_(zinc, Zmergenew, "new facets merged", -1);
+ zdef_(zinc, Zmergehorizon, "horizon facets merged into new facets", -1);
+ zdef_(zinc, Zmergevertex, "vertices deleted by merging", -1);
+ zdef_(zinc, Zcyclevertex, "vertices deleted by merging into coplanar horizon", -1);
+ zdef_(zinc, Zdegenvertex, "vertices deleted by degenerate facet", -1);
+ zdef_(zinc, Zmergeflipdup, "merges due to flipped facets in duplicated ridge", -1);
+ zdef_(zinc, Zneighbor, "merges due to redundant neighbors", -1);
+ zdef_(zadd, Ztestvneighbor, "non-convex vertex neighbors", -1);
+}
+void qh_allstatG(void) {
+ zdef_(zinc, Zacoplanar, "merges due to angle coplanar facets", -1);
+ zdef_(wadd, Wacoplanartot, " average merge distance", Zacoplanar);
+ zdef_(wmax, Wacoplanarmax, " maximum merge distance", -1);
+ zdef_(zinc, Zcoplanar, "merges due to coplanar facets", -1);
+ zdef_(wadd, Wcoplanartot, " average merge distance", Zcoplanar);
+ zdef_(wmax, Wcoplanarmax, " maximum merge distance", -1);
+ zdef_(zinc, Zconcave, "merges due to concave facets", -1);
+ zdef_(wadd, Wconcavetot, " average merge distance", Zconcave);
+ zdef_(wmax, Wconcavemax, " maximum merge distance", -1);
+ zdef_(zinc, Zavoidold, "coplanar/concave merges due to avoiding old merge", -1);
+ zdef_(wadd, Wavoidoldtot, " average merge distance", Zavoidold);
+ zdef_(wmax, Wavoidoldmax, " maximum merge distance", -1);
+ zdef_(zinc, Zdegen, "merges due to degenerate facets", -1);
+ zdef_(wadd, Wdegentot, " average merge distance", Zdegen);
+ zdef_(wmax, Wdegenmax, " maximum merge distance", -1);
+ zdef_(zinc, Zflipped, "merges due to removing flipped facets", -1);
+ zdef_(wadd, Wflippedtot, " average merge distance", Zflipped);
+ zdef_(wmax, Wflippedmax, " maximum merge distance", -1);
+ zdef_(zinc, Zduplicate, "merges due to duplicated ridges", -1);
+ zdef_(wadd, Wduplicatetot, " average merge distance", Zduplicate);
+ zdef_(wmax, Wduplicatemax, " maximum merge distance", -1);
+}
+void qh_allstatH(void) {
+ zdef_(zdoc, Zdoc8, "renamed vertex statistics", -1);
+ zdef_(zinc, Zrenameshare, "renamed vertices shared by two facets", -1);
+ zdef_(zinc, Zrenamepinch, "renamed vertices in a pinched facet", -1);
+ zdef_(zinc, Zrenameall, "renamed vertices shared by multiple facets", -1);
+ zdef_(zinc, Zfindfail, "rename failures due to duplicated ridges", -1);
+ zdef_(zinc, Zdupridge, " duplicate ridges detected", -1);
+ zdef_(zinc, Zdelridge, "deleted ridges due to renamed vertices", -1);
+ zdef_(zinc, Zdropneighbor, "dropped neighbors due to renamed vertices", -1);
+ zdef_(zinc, Zdropdegen, "degenerate facets due to dropped neighbors", -1);
+ zdef_(zinc, Zdelfacetdup, " facets deleted because of no neighbors", -1);
+ zdef_(zinc, Zremvertex, "vertices removed from facets due to no ridges", -1);
+ zdef_(zinc, Zremvertexdel, " deleted", -1);
+ zdef_(zinc, Zintersectnum, "vertex intersections for locating redundant vertices", -1);
+ zdef_(zinc, Zintersectfail, "intersections failed to find a redundant vertex", -1);
+ zdef_(zinc, Zintersect, "intersections found redundant vertices", -1);
+ zdef_(zadd, Zintersecttot, " ave. number found per vertex", Zintersect);
+ zdef_(zmax, Zintersectmax, " max. found for a vertex", -1);
+ zdef_(zinc, Zvertexridge, NULL, -1);
+ zdef_(zadd, Zvertexridgetot, " ave. number of ridges per tested vertex", Zvertexridge);
+ zdef_(zmax, Zvertexridgemax, " max. number of ridges per tested vertex", -1);
+
+ zdef_(zdoc, Zdoc10, "memory usage statistics(in bytes)", -1);
+ zdef_(zadd, Zmemfacets, "for facets and their normals, neighbor and vertex sets", -1);
+ zdef_(zadd, Zmemvertices, "for vertices and their neighbor sets", -1);
+ zdef_(zadd, Zmempoints, "for input points and outside and coplanar sets",-1);
+ zdef_(zadd, Zmemridges, "for ridges and their vertex sets", -1);
+} /* allstat */
+
+void qh_allstatI(void) {
+ qhstat vridges= qhstat next;
+ zzdef_(zdoc, Zdoc11, "Voronoi ridge statistics", -1);
+ zzdef_(zinc, Zridge, "non-simplicial Voronoi vertices for all ridges", -1);
+ zzdef_(wadd, Wridge, " ave. distance to ridge", Zridge);
+ zzdef_(wmax, Wridgemax, " max. distance to ridge", -1);
+ zzdef_(zinc, Zridgemid, "bounded ridges", -1);
+ zzdef_(wadd, Wridgemid, " ave. distance of midpoint to ridge", Zridgemid);
+ zzdef_(wmax, Wridgemidmax, " max. distance of midpoint to ridge", -1);
+ zzdef_(zinc, Zridgeok, "bounded ridges with ok normal", -1);
+ zzdef_(wadd, Wridgeok, " ave. angle to ridge", Zridgeok);
+ zzdef_(wmax, Wridgeokmax, " max. angle to ridge", -1);
+ zzdef_(zinc, Zridge0, "bounded ridges with near-zero normal", -1);
+ zzdef_(wadd, Wridge0, " ave. angle to ridge", Zridge0);
+ zzdef_(wmax, Wridge0max, " max. angle to ridge", -1);
+
+ zdef_(zdoc, Zdoc12, "Triangulation statistics(Qt)", -1);
+ zdef_(zinc, Ztricoplanar, "non-simplicial facets triangulated", -1);
+ zdef_(zadd, Ztricoplanartot, " ave. new facets created(may be deleted)", Ztricoplanar);
+ zdef_(zmax, Ztricoplanarmax, " max. new facets created", -1);
+ zdef_(zinc, Ztrinull, "null new facets deleted(duplicated vertex)", -1);
+ zdef_(zinc, Ztrimirror, "mirrored pairs of new facets deleted(same vertices)", -1);
+ zdef_(zinc, Ztridegen, "degenerate new facets in output(same ridge)", -1);
+} /* allstat */
+
+/*-<a href="qh-stat.htm#TOC"
+ >-------------------------------</a><a name="allstatistics">-</a>
+
+ qh_allstatistics()
+ reset printed flag for all statistics
+*/
+void qh_allstatistics(void) {
+ int i;
+
+ for(i=ZEND; i--; )
+ qhstat printed[i]= False;
+} /* allstatistics */
+
+#if qh_KEEPstatistics
+/*-<a href="qh-stat.htm#TOC"
+ >-------------------------------</a><a name="collectstatistics">-</a>
+
+ qh_collectstatistics()
+ collect statistics for qh.facet_list
+
+*/
+void qh_collectstatistics(void) {
+ facetT *facet, *neighbor, **neighborp;
+ vertexT *vertex, **vertexp;
+ realT dotproduct, dist;
+ int sizneighbors, sizridges, sizvertices, i;
+
+ qh old_randomdist= qh RANDOMdist;
+ qh RANDOMdist= False;
+ zval_(Zmempoints)= qh num_points * qh normal_size +
+ sizeof(qhT) + sizeof(qhstatT);
+ zval_(Zmemfacets)= 0;
+ zval_(Zmemridges)= 0;
+ zval_(Zmemvertices)= 0;
+ zval_(Zangle)= 0;
+ wval_(Wangle)= 0.0;
+ zval_(Znumridges)= 0;
+ zval_(Znumfacets)= 0;
+ zval_(Znumneighbors)= 0;
+ zval_(Znumvertices)= 0;
+ zval_(Znumvneighbors)= 0;
+ zval_(Znummergetot)= 0;
+ zval_(Znummergemax)= 0;
+ zval_(Zvertices)= qh num_vertices - qh_setsize(qh del_vertices);
+ if (qh MERGING || qh APPROXhull || qh JOGGLEmax < REALmax/2)
+ wmax_(Wmaxoutside, qh max_outside);
+ if (qh MERGING)
+ wmin_(Wminvertex, qh min_vertex);
+ FORALLfacets
+ facet->seen= False;
+ if (qh DELAUNAY) {
+ FORALLfacets {
+ if (facet->upperdelaunay != qh UPPERdelaunay)
+ facet->seen= True; /* remove from angle statistics */
+ }
+ }
+ FORALLfacets {
+ if (facet->visible && qh NEWfacets)
+ continue;
+ sizvertices= qh_setsize(facet->vertices);
+ sizneighbors= qh_setsize(facet->neighbors);
+ sizridges= qh_setsize(facet->ridges);
+ zinc_(Znumfacets);
+ zadd_(Znumvertices, sizvertices);
+ zmax_(Zmaxvertices, sizvertices);
+ zadd_(Znumneighbors, sizneighbors);
+ zmax_(Zmaxneighbors, sizneighbors);
+ zadd_(Znummergetot, facet->nummerge);
+ i= facet->nummerge; /* avoid warnings */
+ zmax_(Znummergemax, i);
+ if (!facet->simplicial) {
+ if (sizvertices == qh hull_dim) {
+ zinc_(Znowsimplicial);
+ }else {
+ zinc_(Znonsimplicial);
+ }
+ }
+ if (sizridges) {
+ zadd_(Znumridges, sizridges);
+ zmax_(Zmaxridges, sizridges);
+ }
+ zadd_(Zmemfacets, sizeof(facetT) + qh normal_size + 2*sizeof(setT)
+ + SETelemsize * (sizneighbors + sizvertices));
+ if (facet->ridges) {
+ zadd_(Zmemridges,
+ sizeof(setT) + SETelemsize * sizridges + sizridges *
+ (sizeof(ridgeT) + sizeof(setT) + SETelemsize * (qh hull_dim-1))/2);
+ }
+ if (facet->outsideset)
+ zadd_(Zmempoints, sizeof(setT) + SETelemsize * qh_setsize(facet->outsideset));
+ if (facet->coplanarset)
+ zadd_(Zmempoints, sizeof(setT) + SETelemsize * qh_setsize(facet->coplanarset));
+ if (facet->seen) /* Delaunay upper envelope */
+ continue;
+ facet->seen= True;
+ FOREACHneighbor_(facet) {
+ if (neighbor == qh_DUPLICATEridge || neighbor == qh_MERGEridge
+ || neighbor->seen || !facet->normal || !neighbor->normal)
+ continue;
+ dotproduct= qh_getangle(facet->normal, neighbor->normal);
+ zinc_(Zangle);
+ wadd_(Wangle, dotproduct);
+ wmax_(Wanglemax, dotproduct)
+ wmin_(Wanglemin, dotproduct)
+ }
+ if (facet->normal) {
+ FOREACHvertex_(facet->vertices) {
+ zinc_(Zdiststat);
+ qh_distplane(vertex->point, facet, &dist);
+ wmax_(Wvertexmax, dist);
+ wmin_(Wvertexmin, dist);
+ }
+ }
+ }
+ FORALLvertices {
+ if (vertex->deleted)
+ continue;
+ zadd_(Zmemvertices, sizeof(vertexT));
+ if (vertex->neighbors) {
+ sizneighbors= qh_setsize(vertex->neighbors);
+ zadd_(Znumvneighbors, sizneighbors);
+ zmax_(Zmaxvneighbors, sizneighbors);
+ zadd_(Zmemvertices, sizeof(vertexT) + SETelemsize * sizneighbors);
+ }
+ }
+ qh RANDOMdist= qh old_randomdist;
+} /* collectstatistics */
+#endif /* qh_KEEPstatistics */
+
+/*-<a href="qh-stat.htm#TOC"
+ >-------------------------------</a><a name="freestatistics">-</a>
+
+ qh_freestatistics( )
+ free memory used for statistics
+*/
+void qh_freestatistics(void) {
+
+#if qh_QHpointer
+ qh_free(qh_qhstat);
+ qh_qhstat= NULL;
+#endif
+} /* freestatistics */
+
+/*-<a href="qh-stat.htm#TOC"
+ >-------------------------------</a><a name="initstatistics">-</a>
+
+ qh_initstatistics( )
+ allocate and initialize statistics
+
+ notes:
+ uses qh_malloc() instead of qh_memalloc() since mem.c not set up yet
+ NOerrors -- qh_initstatistics can not use qh_errexit(), qh_fprintf, or qh.ferr
+ On first call, only qhmem.ferr is defined. qh_memalloc is not setup.
+ Also invoked by QhullQh().
+*/
+void qh_initstatistics(void) {
+ int i;
+ realT realx;
+ int intx;
+
+#if qh_QHpointer
+ if(qh_qhstat){ /* qh_initstatistics may be called from Qhull::resetStatistics() */
+ qh_free(qh_qhstat);
+ qh_qhstat= 0;
+ }
+ if (!(qh_qhstat= (qhstatT *)qh_malloc(sizeof(qhstatT)))) {
+ qh_fprintf_stderr(6183, "qhull error (qh_initstatistics): insufficient memory\n");
+ qh_exit(qh_ERRmem); /* can not use qh_errexit() */
+ }
+#endif
+
+ qhstat next= 0;
+ qh_allstatA();
+ qh_allstatB();
+ qh_allstatC();
+ qh_allstatD();
+ qh_allstatE();
+ qh_allstatE2();
+ qh_allstatF();
+ qh_allstatG();
+ qh_allstatH();
+ qh_allstatI();
+ if (qhstat next > (int)sizeof(qhstat id)) {
+ qh_fprintf(qhmem.ferr, 6184, "qhull error (qh_initstatistics): increase size of qhstat.id[].\n\
+ qhstat.next %d should be <= sizeof(qhstat id) %d\n", qhstat next, (int)sizeof(qhstat id));
+#if 0 /* for locating error, Znumridges should be duplicated */
+ for(i=0; i < ZEND; i++) {
+ int j;
+ for(j=i+1; j < ZEND; j++) {
+ if (qhstat id[i] == qhstat id[j]) {
+ qh_fprintf(qhmem.ferr, 6185, "qhull error (qh_initstatistics): duplicated statistic %d at indices %d and %d\n",
+ qhstat id[i], i, j);
+ }
+ }
+ }
+#endif
+ qh_exit(qh_ERRqhull); /* can not use qh_errexit() */
+ }
+ qhstat init[zinc].i= 0;
+ qhstat init[zadd].i= 0;
+ qhstat init[zmin].i= INT_MAX;
+ qhstat init[zmax].i= INT_MIN;
+ qhstat init[wadd].r= 0;
+ qhstat init[wmin].r= REALmax;
+ qhstat init[wmax].r= -REALmax;
+ for(i=0; i < ZEND; i++) {
+ if (qhstat type[i] > ZTYPEreal) {
+ realx= qhstat init[(unsigned char)(qhstat type[i])].r;
+ qhstat stats[i].r= realx;
+ }else if (qhstat type[i] != zdoc) {
+ intx= qhstat init[(unsigned char)(qhstat type[i])].i;
+ qhstat stats[i].i= intx;
+ }
+ }
+} /* initstatistics */
+
+/*-<a href="qh-stat.htm#TOC"
+ >-------------------------------</a><a name="newstats">-</a>
+
+ qh_newstats( )
+ returns True if statistics for zdoc
+
+ returns:
+ next zdoc
+*/
+boolT qh_newstats(int idx, int *nextindex) {
+ boolT isnew= False;
+ int start, i;
+
+ if (qhstat type[qhstat id[idx]] == zdoc)
+ start= idx+1;
+ else
+ start= idx;
+ for(i= start; i < qhstat next && qhstat type[qhstat id[i]] != zdoc; i++) {
+ if (!qh_nostatistic(qhstat id[i]) && !qhstat printed[qhstat id[i]])
+ isnew= True;
+ }
+ *nextindex= i;
+ return isnew;
+} /* newstats */
+
+/*-<a href="qh-stat.htm#TOC"
+ >-------------------------------</a><a name="nostatistic">-</a>
+
+ qh_nostatistic( index )
+ true if no statistic to print
+*/
+boolT qh_nostatistic(int i) {
+
+ if ((qhstat type[i] > ZTYPEreal
+ &&qhstat stats[i].r == qhstat init[(unsigned char)(qhstat type[i])].r)
+ || (qhstat type[i] < ZTYPEreal
+ &&qhstat stats[i].i == qhstat init[(unsigned char)(qhstat type[i])].i))
+ return True;
+ return False;
+} /* nostatistic */
+
+#if qh_KEEPstatistics
+/*-<a href="qh-stat.htm#TOC"
+ >-------------------------------</a><a name="printallstatistics">-</a>
+
+ qh_printallstatistics( fp, string )
+ print all statistics with header 'string'
+*/
+void qh_printallstatistics(FILE *fp, const char *string) {
+
+ qh_allstatistics();
+ qh_collectstatistics();
+ qh_printstatistics(fp, string);
+ qh_memstatistics(fp);
+}
+
+
+/*-<a href="qh-stat.htm#TOC"
+ >-------------------------------</a><a name="printstatistics">-</a>
+
+ qh_printstatistics( fp, string )
+ print statistics to a file with header 'string'
+ skips statistics with qhstat.printed[] (reset with qh_allstatistics)
+
+ see:
+ qh_printallstatistics()
+*/
+void qh_printstatistics(FILE *fp, const char *string) {
+ int i, k;
+ realT ave;
+
+ if (qh num_points != qh num_vertices) {
+ wval_(Wpbalance)= 0;
+ wval_(Wpbalance2)= 0;
+ }else
+ wval_(Wpbalance2)= qh_stddev(zval_(Zpbalance), wval_(Wpbalance),
+ wval_(Wpbalance2), &ave);
+ wval_(Wnewbalance2)= qh_stddev(zval_(Zprocessed), wval_(Wnewbalance),
+ wval_(Wnewbalance2), &ave);
+ qh_fprintf(fp, 9350, "\n\
+%s\n\
+ qhull invoked by: %s | %s\n%s with options:\n%s\n", string, qh rbox_command,
+ qh qhull_command, qh_version, qh qhull_options);
+ qh_fprintf(fp, 9351, "\nprecision constants:\n\
+ %6.2g max. abs. coordinate in the (transformed) input('Qbd:n')\n\
+ %6.2g max. roundoff error for distance computation('En')\n\
+ %6.2g max. roundoff error for angle computations\n\
+ %6.2g min. distance for outside points ('Wn')\n\
+ %6.2g min. distance for visible facets ('Vn')\n\
+ %6.2g max. distance for coplanar facets ('Un')\n\
+ %6.2g max. facet width for recomputing centrum and area\n\
+",
+ qh MAXabs_coord, qh DISTround, qh ANGLEround, qh MINoutside,
+ qh MINvisible, qh MAXcoplanar, qh WIDEfacet);
+ if (qh KEEPnearinside)
+ qh_fprintf(fp, 9352, "\
+ %6.2g max. distance for near-inside points\n", qh NEARinside);
+ if (qh premerge_cos < REALmax/2) qh_fprintf(fp, 9353, "\
+ %6.2g max. cosine for pre-merge angle\n", qh premerge_cos);
+ if (qh PREmerge) qh_fprintf(fp, 9354, "\
+ %6.2g radius of pre-merge centrum\n", qh premerge_centrum);
+ if (qh postmerge_cos < REALmax/2) qh_fprintf(fp, 9355, "\
+ %6.2g max. cosine for post-merge angle\n", qh postmerge_cos);
+ if (qh POSTmerge) qh_fprintf(fp, 9356, "\
+ %6.2g radius of post-merge centrum\n", qh postmerge_centrum);
+ qh_fprintf(fp, 9357, "\
+ %6.2g max. distance for merging two simplicial facets\n\
+ %6.2g max. roundoff error for arithmetic operations\n\
+ %6.2g min. denominator for divisions\n\
+ zero diagonal for Gauss: ", qh ONEmerge, REALepsilon, qh MINdenom);
+ for(k=0; k < qh hull_dim; k++)
+ qh_fprintf(fp, 9358, "%6.2e ", qh NEARzero[k]);
+ qh_fprintf(fp, 9359, "\n\n");
+ for(i=0 ; i < qhstat next; )
+ qh_printstats(fp, i, &i);
+} /* printstatistics */
+#endif /* qh_KEEPstatistics */
+
+/*-<a href="qh-stat.htm#TOC"
+ >-------------------------------</a><a name="printstatlevel">-</a>
+
+ qh_printstatlevel( fp, id )
+ print level information for a statistic
+
+ notes:
+ nop if id >= ZEND, printed, or same as initial value
+*/
+void qh_printstatlevel(FILE *fp, int id) {
+#define NULLfield " "
+
+ if (id >= ZEND || qhstat printed[id])
+ return;
+ if (qhstat type[id] == zdoc) {
+ qh_fprintf(fp, 9360, "%s\n", qhstat doc[id]);
+ return;
+ }
+ if (qh_nostatistic(id) || !qhstat doc[id])
+ return;
+ qhstat printed[id]= True;
+ if (qhstat count[id] != -1
+ && qhstat stats[(unsigned char)(qhstat count[id])].i == 0)
+ qh_fprintf(fp, 9361, " *0 cnt*");
+ else if (qhstat type[id] >= ZTYPEreal && qhstat count[id] == -1)
+ qh_fprintf(fp, 9362, "%7.2g", qhstat stats[id].r);
+ else if (qhstat type[id] >= ZTYPEreal && qhstat count[id] != -1)
+ qh_fprintf(fp, 9363, "%7.2g", qhstat stats[id].r/ qhstat stats[(unsigned char)(qhstat count[id])].i);
+ else if (qhstat type[id] < ZTYPEreal && qhstat count[id] == -1)
+ qh_fprintf(fp, 9364, "%7d", qhstat stats[id].i);
+ else if (qhstat type[id] < ZTYPEreal && qhstat count[id] != -1)
+ qh_fprintf(fp, 9365, "%7.3g", (realT) qhstat stats[id].i / qhstat stats[(unsigned char)(qhstat count[id])].i);
+ qh_fprintf(fp, 9366, " %s\n", qhstat doc[id]);
+} /* printstatlevel */
+
+
+/*-<a href="qh-stat.htm#TOC"
+ >-------------------------------</a><a name="printstats">-</a>
+
+ qh_printstats( fp, index, nextindex )
+ print statistics for a zdoc group
+
+ returns:
+ next zdoc if non-null
+*/
+void qh_printstats(FILE *fp, int idx, int *nextindex) {
+ int j, nexti;
+
+ if (qh_newstats(idx, &nexti)) {
+ qh_fprintf(fp, 9367, "\n");
+ for (j=idx; j<nexti; j++)
+ qh_printstatlevel(fp, qhstat id[j]);
+ }
+ if (nextindex)
+ *nextindex= nexti;
+} /* printstats */
+
+#if qh_KEEPstatistics
+
+/*-<a href="qh-stat.htm#TOC"
+ >-------------------------------</a><a name="stddev">-</a>
+
+ qh_stddev( num, tot, tot2, ave )
+ compute the standard deviation and average from statistics
+
+ tot2 is the sum of the squares
+ notes:
+ computes r.m.s.:
+ (x-ave)^2
+ == x^2 - 2x tot/num + (tot/num)^2
+ == tot2 - 2 tot tot/num + tot tot/num
+ == tot2 - tot ave
+*/
+realT qh_stddev(int num, realT tot, realT tot2, realT *ave) {
+ realT stddev;
+
+ *ave= tot/num;
+ stddev= sqrt(tot2/num - *ave * *ave);
+ return stddev;
+} /* stddev */
+
+#endif /* qh_KEEPstatistics */
+
+#if !qh_KEEPstatistics
+void qh_collectstatistics(void) {}
+void qh_printallstatistics(FILE *fp, char *string) {};
+void qh_printstatistics(FILE *fp, char *string) {}
+#endif
+
diff --git a/xs/src/qhull/src/libqhull/stat.h b/xs/src/qhull/src/libqhull/stat.h
new file mode 100644
index 000000000..d86fc0a87
--- /dev/null
+++ b/xs/src/qhull/src/libqhull/stat.h
@@ -0,0 +1,543 @@
+/*<html><pre> -<a href="qh-stat.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ stat.h
+ contains all statistics that are collected for qhull
+
+ see qh-stat.htm and stat.c
+
+ Copyright (c) 1993-2015 The Geometry Center.
+ $Id: //main/2015/qhull/src/libqhull/stat.h#4 $$Change: 2062 $
+ $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $
+
+ recompile qhull if you change this file
+
+ Integer statistics are Z* while real statistics are W*.
+
+ define maydebugx to call a routine at every statistic event
+
+*/
+
+#ifndef qhDEFstat
+#define qhDEFstat 1
+
+#include "libqhull.h"
+
+/*-<a href="qh-stat.htm#TOC"
+ >-------------------------------</a><a name="KEEPstatistics">-</a>
+
+ qh_KEEPstatistics
+ 0 turns off statistic gathering (except zzdef/zzinc/zzadd/zzval/wwval)
+*/
+#ifndef qh_KEEPstatistics
+#define qh_KEEPstatistics 1
+#endif
+
+/*-<a href="qh-stat.htm#TOC"
+ >-------------------------------</a><a name="statistics">-</a>
+
+ Zxxx for integers, Wxxx for reals
+
+ notes:
+ be sure that all statistics are defined in stat.c
+ otherwise initialization may core dump
+ can pick up all statistics by:
+ grep '[zw].*_[(][ZW]' *.c >z.x
+ remove trailers with query">-</a>
+ remove leaders with query-replace-regexp [ ^I]+ (
+*/
+#if qh_KEEPstatistics
+enum qh_statistics { /* alphabetical after Z/W */
+ Zacoplanar,
+ Wacoplanarmax,
+ Wacoplanartot,
+ Zangle,
+ Wangle,
+ Wanglemax,
+ Wanglemin,
+ Zangletests,
+ Wareatot,
+ Wareamax,
+ Wareamin,
+ Zavoidold,
+ Wavoidoldmax,
+ Wavoidoldtot,
+ Zback0,
+ Zbestcentrum,
+ Zbestdist,
+ Zbestlower,
+ Zbestlowerall,
+ Zbestloweralln,
+ Zbestlowerv,
+ Zcentrumtests,
+ Zcheckpart,
+ Zcomputefurthest,
+ Zconcave,
+ Wconcavemax,
+ Wconcavetot,
+ Zconcaveridges,
+ Zconcaveridge,
+ Zcoplanar,
+ Wcoplanarmax,
+ Wcoplanartot,
+ Zcoplanarangle,
+ Zcoplanarcentrum,
+ Zcoplanarhorizon,
+ Zcoplanarinside,
+ Zcoplanarpart,
+ Zcoplanarridges,
+ Wcpu,
+ Zcyclefacetmax,
+ Zcyclefacettot,
+ Zcyclehorizon,
+ Zcyclevertex,
+ Zdegen,
+ Wdegenmax,
+ Wdegentot,
+ Zdegenvertex,
+ Zdelfacetdup,
+ Zdelridge,
+ Zdelvertextot,
+ Zdelvertexmax,
+ Zdetsimplex,
+ Zdistcheck,
+ Zdistconvex,
+ Zdistgood,
+ Zdistio,
+ Zdistplane,
+ Zdiststat,
+ Zdistvertex,
+ Zdistzero,
+ Zdoc1,
+ Zdoc2,
+ Zdoc3,
+ Zdoc4,
+ Zdoc5,
+ Zdoc6,
+ Zdoc7,
+ Zdoc8,
+ Zdoc9,
+ Zdoc10,
+ Zdoc11,
+ Zdoc12,
+ Zdropdegen,
+ Zdropneighbor,
+ Zdupflip,
+ Zduplicate,
+ Wduplicatemax,
+ Wduplicatetot,
+ Zdupridge,
+ Zdupsame,
+ Zflipped,
+ Wflippedmax,
+ Wflippedtot,
+ Zflippedfacets,
+ Zfindbest,
+ Zfindbestmax,
+ Zfindbesttot,
+ Zfindcoplanar,
+ Zfindfail,
+ Zfindhorizon,
+ Zfindhorizonmax,
+ Zfindhorizontot,
+ Zfindjump,
+ Zfindnew,
+ Zfindnewmax,
+ Zfindnewtot,
+ Zfindnewjump,
+ Zfindnewsharp,
+ Zgauss0,
+ Zgoodfacet,
+ Zhashlookup,
+ Zhashridge,
+ Zhashridgetest,
+ Zhashtests,
+ Zinsidevisible,
+ Zintersect,
+ Zintersectfail,
+ Zintersectmax,
+ Zintersectnum,
+ Zintersecttot,
+ Zmaxneighbors,
+ Wmaxout,
+ Wmaxoutside,
+ Zmaxridges,
+ Zmaxvertex,
+ Zmaxvertices,
+ Zmaxvneighbors,
+ Zmemfacets,
+ Zmempoints,
+ Zmemridges,
+ Zmemvertices,
+ Zmergeflipdup,
+ Zmergehorizon,
+ Zmergeinittot,
+ Zmergeinitmax,
+ Zmergeinittot2,
+ Zmergeintohorizon,
+ Zmergenew,
+ Zmergesettot,
+ Zmergesetmax,
+ Zmergesettot2,
+ Zmergesimplex,
+ Zmergevertex,
+ Wmindenom,
+ Wminvertex,
+ Zminnorm,
+ Zmultiridge,
+ Znearlysingular,
+ Zneighbor,
+ Wnewbalance,
+ Wnewbalance2,
+ Znewfacettot,
+ Znewfacetmax,
+ Znewvertex,
+ Wnewvertex,
+ Wnewvertexmax,
+ Znoarea,
+ Znonsimplicial,
+ Znowsimplicial,
+ Znotgood,
+ Znotgoodnew,
+ Znotmax,
+ Znumfacets,
+ Znummergemax,
+ Znummergetot,
+ Znumneighbors,
+ Znumridges,
+ Znumvertices,
+ Znumvisibility,
+ Znumvneighbors,
+ Zonehorizon,
+ Zpartangle,
+ Zpartcoplanar,
+ Zpartflip,
+ Zparthorizon,
+ Zpartinside,
+ Zpartition,
+ Zpartitionall,
+ Zpartnear,
+ Zpbalance,
+ Wpbalance,
+ Wpbalance2,
+ Zpostfacets,
+ Zpremergetot,
+ Zprocessed,
+ Zremvertex,
+ Zremvertexdel,
+ Zrenameall,
+ Zrenamepinch,
+ Zrenameshare,
+ Zretry,
+ Wretrymax,
+ Zridge,
+ Wridge,
+ Wridgemax,
+ Zridge0,
+ Wridge0,
+ Wridge0max,
+ Zridgemid,
+ Wridgemid,
+ Wridgemidmax,
+ Zridgeok,
+ Wridgeok,
+ Wridgeokmax,
+ Zsearchpoints,
+ Zsetplane,
+ Ztestvneighbor,
+ Ztotcheck,
+ Ztothorizon,
+ Ztotmerge,
+ Ztotpartcoplanar,
+ Ztotpartition,
+ Ztotridges,
+ Ztotvertices,
+ Ztotvisible,
+ Ztricoplanar,
+ Ztricoplanarmax,
+ Ztricoplanartot,
+ Ztridegen,
+ Ztrimirror,
+ Ztrinull,
+ Wvertexmax,
+ Wvertexmin,
+ Zvertexridge,
+ Zvertexridgetot,
+ Zvertexridgemax,
+ Zvertices,
+ Zvisfacettot,
+ Zvisfacetmax,
+ Zvisit,
+ Zvisit2max,
+ Zvisvertextot,
+ Zvisvertexmax,
+ Zvvisit,
+ Zvvisit2max,
+ Zwidefacet,
+ Zwidevertices,
+ ZEND};
+
+/*-<a href="qh-stat.htm#TOC"
+ >-------------------------------</a><a name="ZZstat">-</a>
+
+ Zxxx/Wxxx statistics that remain defined if qh_KEEPstatistics=0
+
+ notes:
+ be sure to use zzdef, zzinc, etc. with these statistics (no double checking!)
+*/
+#else
+enum qh_statistics { /* for zzdef etc. macros */
+ Zback0,
+ Zbestdist,
+ Zcentrumtests,
+ Zcheckpart,
+ Zconcaveridges,
+ Zcoplanarhorizon,
+ Zcoplanarpart,
+ Zcoplanarridges,
+ Zcyclefacettot,
+ Zcyclehorizon,
+ Zdelvertextot,
+ Zdistcheck,
+ Zdistconvex,
+ Zdistzero,
+ Zdoc1,
+ Zdoc2,
+ Zdoc3,
+ Zdoc11,
+ Zflippedfacets,
+ Zgauss0,
+ Zminnorm,
+ Zmultiridge,
+ Znearlysingular,
+ Wnewvertexmax,
+ Znumvisibility,
+ Zpartcoplanar,
+ Zpartition,
+ Zpartitionall,
+ Zprocessed,
+ Zretry,
+ Zridge,
+ Wridge,
+ Wridgemax,
+ Zridge0,
+ Wridge0,
+ Wridge0max,
+ Zridgemid,
+ Wridgemid,
+ Wridgemidmax,
+ Zridgeok,
+ Wridgeok,
+ Wridgeokmax,
+ Zsetplane,
+ Ztotcheck,
+ Ztotmerge,
+ ZEND};
+#endif
+
+/*-<a href="qh-stat.htm#TOC"
+ >-------------------------------</a><a name="ztype">-</a>
+
+ ztype
+ the type of a statistic sets its initial value.
+
+ notes:
+ The type should be the same as the macro for collecting the statistic
+*/
+enum ztypes {zdoc,zinc,zadd,zmax,zmin,ZTYPEreal,wadd,wmax,wmin,ZTYPEend};
+
+/*========== macros and constants =============*/
+
+/*-<a href="qh-stat.htm#TOC"
+ >--------------------------------</a><a name="MAYdebugx">-</a>
+
+ MAYdebugx
+ define as maydebug() to be called frequently for error trapping
+*/
+#define MAYdebugx
+
+/*-<a href="qh-stat.htm#TOC"
+ >--------------------------------</a><a name="zdef_">-</a>
+
+ zzdef_, zdef_( type, name, doc, -1)
+ define a statistic (assumes 'qhstat.next= 0;')
+
+ zdef_( type, name, doc, count)
+ define an averaged statistic
+ printed as name/count
+*/
+#define zzdef_(stype,name,string,cnt) qhstat id[qhstat next++]=name; \
+ qhstat doc[name]= string; qhstat count[name]= cnt; qhstat type[name]= stype
+#if qh_KEEPstatistics
+#define zdef_(stype,name,string,cnt) qhstat id[qhstat next++]=name; \
+ qhstat doc[name]= string; qhstat count[name]= cnt; qhstat type[name]= stype
+#else
+#define zdef_(type,name,doc,count)
+#endif
+
+/*-<a href="qh-stat.htm#TOC"
+ >--------------------------------</a><a name="zinc_">-</a>
+
+ zzinc_( name ), zinc_( name)
+ increment an integer statistic
+*/
+#define zzinc_(id) {MAYdebugx; qhstat stats[id].i++;}
+#if qh_KEEPstatistics
+#define zinc_(id) {MAYdebugx; qhstat stats[id].i++;}
+#else
+#define zinc_(id) {}
+#endif
+
+/*-<a href="qh-stat.htm#TOC"
+ >--------------------------------</a><a name="zadd_">-</a>
+
+ zzadd_( name, value ), zadd_( name, value ), wadd_( name, value )
+ add value to an integer or real statistic
+*/
+#define zzadd_(id, val) {MAYdebugx; qhstat stats[id].i += (val);}
+#define wwadd_(id, val) {MAYdebugx; qhstat stats[id].r += (val);}
+#if qh_KEEPstatistics
+#define zadd_(id, val) {MAYdebugx; qhstat stats[id].i += (val);}
+#define wadd_(id, val) {MAYdebugx; qhstat stats[id].r += (val);}
+#else
+#define zadd_(id, val) {}
+#define wadd_(id, val) {}
+#endif
+
+/*-<a href="qh-stat.htm#TOC"
+ >--------------------------------</a><a name="zval_">-</a>
+
+ zzval_( name ), zval_( name ), wwval_( name )
+ set or return value of a statistic
+*/
+#define zzval_(id) ((qhstat stats[id]).i)
+#define wwval_(id) ((qhstat stats[id]).r)
+#if qh_KEEPstatistics
+#define zval_(id) ((qhstat stats[id]).i)
+#define wval_(id) ((qhstat stats[id]).r)
+#else
+#define zval_(id) qhstat tempi
+#define wval_(id) qhstat tempr
+#endif
+
+/*-<a href="qh-stat.htm#TOC"
+ >--------------------------------</a><a name="zmax_">-</a>
+
+ zmax_( id, val ), wmax_( id, value )
+ maximize id with val
+*/
+#define wwmax_(id, val) {MAYdebugx; maximize_(qhstat stats[id].r,(val));}
+#if qh_KEEPstatistics
+#define zmax_(id, val) {MAYdebugx; maximize_(qhstat stats[id].i,(val));}
+#define wmax_(id, val) {MAYdebugx; maximize_(qhstat stats[id].r,(val));}
+#else
+#define zmax_(id, val) {}
+#define wmax_(id, val) {}
+#endif
+
+/*-<a href="qh-stat.htm#TOC"
+ >--------------------------------</a><a name="zmin_">-</a>
+
+ zmin_( id, val ), wmin_( id, value )
+ minimize id with val
+*/
+#if qh_KEEPstatistics
+#define zmin_(id, val) {MAYdebugx; minimize_(qhstat stats[id].i,(val));}
+#define wmin_(id, val) {MAYdebugx; minimize_(qhstat stats[id].r,(val));}
+#else
+#define zmin_(id, val) {}
+#define wmin_(id, val) {}
+#endif
+
+/*================== stat.h types ==============*/
+
+
+/*-<a href="qh-stat.htm#TOC"
+ >--------------------------------</a><a name="intrealT">-</a>
+
+ intrealT
+ union of integer and real, used for statistics
+*/
+typedef union intrealT intrealT; /* union of int and realT */
+union intrealT {
+ int i;
+ realT r;
+};
+
+/*-<a href="qh-stat.htm#TOC"
+ >--------------------------------</a><a name="qhstat">-</a>
+
+ qhstat
+ global data structure for statistics, similar to qh and qhrbox
+
+ notes:
+ access to qh_qhstat is via the "qhstat" macro. There are two choices
+ qh_QHpointer = 1 access globals via a pointer
+ enables qh_saveqhull() and qh_restoreqhull()
+ = 0 qh_qhstat is a static data structure
+ only one instance of qhull() can be active at a time
+ default value
+ qh_QHpointer is defined in libqhull.h
+ qh_QHpointer_dllimport and qh_dllimport define qh_qh as __declspec(dllimport) [libqhull.h]
+
+ allocated in stat.c using qh_malloc()
+*/
+#ifndef DEFqhstatT
+#define DEFqhstatT 1
+typedef struct qhstatT qhstatT;
+#endif
+
+#if qh_QHpointer_dllimport
+#define qhstat qh_qhstat->
+__declspec(dllimport) extern qhstatT *qh_qhstat;
+#elif qh_QHpointer
+#define qhstat qh_qhstat->
+extern qhstatT *qh_qhstat;
+#elif qh_dllimport
+#define qhstat qh_qhstat.
+__declspec(dllimport) extern qhstatT qh_qhstat;
+#else
+#define qhstat qh_qhstat.
+extern qhstatT qh_qhstat;
+#endif
+struct qhstatT {
+ intrealT stats[ZEND]; /* integer and real statistics */
+ unsigned char id[ZEND+10]; /* id's in print order */
+ const char *doc[ZEND]; /* array of documentation strings */
+ short int count[ZEND]; /* -1 if none, else index of count to use */
+ char type[ZEND]; /* type, see ztypes above */
+ char printed[ZEND]; /* true, if statistic has been printed */
+ intrealT init[ZTYPEend]; /* initial values by types, set initstatistics */
+
+ int next; /* next index for zdef_ */
+ int precision; /* index for precision problems */
+ int vridges; /* index for Voronoi ridges */
+ int tempi;
+ realT tempr;
+};
+
+/*========== function prototypes ===========*/
+
+void qh_allstatA(void);
+void qh_allstatB(void);
+void qh_allstatC(void);
+void qh_allstatD(void);
+void qh_allstatE(void);
+void qh_allstatE2(void);
+void qh_allstatF(void);
+void qh_allstatG(void);
+void qh_allstatH(void);
+void qh_allstatI(void);
+void qh_allstatistics(void);
+void qh_collectstatistics(void);
+void qh_freestatistics(void);
+void qh_initstatistics(void);
+boolT qh_newstats(int idx, int *nextindex);
+boolT qh_nostatistic(int i);
+void qh_printallstatistics(FILE *fp, const char *string);
+void qh_printstatistics(FILE *fp, const char *string);
+void qh_printstatlevel(FILE *fp, int id);
+void qh_printstats(FILE *fp, int idx, int *nextindex);
+realT qh_stddev(int num, realT tot, realT tot2, realT *ave);
+
+#endif /* qhDEFstat */
diff --git a/xs/src/qhull/src/libqhull/user.c b/xs/src/qhull/src/libqhull/user.c
new file mode 100644
index 000000000..d4726eaa3
--- /dev/null
+++ b/xs/src/qhull/src/libqhull/user.c
@@ -0,0 +1,538 @@
+/*<html><pre> -<a href="qh-user.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ user.c
+ user redefinable functions
+
+ see user2.c for qh_fprintf, qh_malloc, qh_free
+
+ see README.txt see COPYING.txt for copyright information.
+
+ see libqhull.h for data structures, macros, and user-callable functions.
+
+ see user_eg.c, user_eg2.c, and unix.c for examples.
+
+ see user.h for user-definable constants
+
+ use qh_NOmem in mem.h to turn off memory management
+ use qh_NOmerge in user.h to turn off facet merging
+ set qh_KEEPstatistics in user.h to 0 to turn off statistics
+
+ This is unsupported software. You're welcome to make changes,
+ but you're on your own if something goes wrong. Use 'Tc' to
+ check frequently. Usually qhull will report an error if
+ a data structure becomes inconsistent. If so, it also reports
+ the last point added to the hull, e.g., 102. You can then trace
+ the execution of qhull with "T4P102".
+
+ Please report any errors that you fix to qhull@qhull.org
+
+ Qhull-template is a template for calling qhull from within your application
+
+ if you recompile and load this module, then user.o will not be loaded
+ from qhull.a
+
+ you can add additional quick allocation sizes in qh_user_memsizes
+
+ if the other functions here are redefined to not use qh_print...,
+ then io.o will not be loaded from qhull.a. See user_eg.c for an
+ example. We recommend keeping io.o for the extra debugging
+ information it supplies.
+*/
+
+#include "qhull_a.h"
+
+#include <stdarg.h>
+
+/*-<a href="qh-user.htm#TOC"
+ >-------------------------------</a><a name="qhull_template">-</a>
+
+ Qhull-template
+ Template for calling qhull from inside your program
+
+ returns:
+ exit code(see qh_ERR... in libqhull.h)
+ all memory freed
+
+ notes:
+ This can be called any number of times.
+*/
+#if 0
+{
+ int dim; /* dimension of points */
+ int numpoints; /* number of points */
+ coordT *points; /* array of coordinates for each point */
+ boolT ismalloc; /* True if qhull should free points in qh_freeqhull() or reallocation */
+ char flags[]= "qhull Tv"; /* option flags for qhull, see qh_opt.htm */
+ FILE *outfile= stdout; /* output from qh_produce_output()
+ use NULL to skip qh_produce_output() */
+ FILE *errfile= stderr; /* error messages from qhull code */
+ int exitcode; /* 0 if no error from qhull */
+ facetT *facet; /* set by FORALLfacets */
+ int curlong, totlong; /* memory remaining after qh_memfreeshort */
+
+ QHULL_LIB_CHECK /* Check for compatible library */
+
+#if qh_QHpointer /* see user.h */
+ if (qh_qh){ /* should be NULL */
+ qh_printf_stderr(6238, "Qhull link error. The global variable qh_qh was not initialized\n\
+ to NULL by global.c. Please compile this program with -Dqh_QHpointer_dllimport\n\
+ as well as -Dqh_QHpointer, or use libqhullstatic, or use a different tool chain.\n\n");
+ exit(1);
+ }
+#endif
+
+ /* initialize dim, numpoints, points[], ismalloc here */
+ exitcode= qh_new_qhull(dim, numpoints, points, ismalloc,
+ flags, outfile, errfile);
+ if (!exitcode) { /* if no error */
+ /* 'qh facet_list' contains the convex hull */
+ FORALLfacets {
+ /* ... your code ... */
+ }
+ }
+ qh_freeqhull(!qh_ALL);
+ qh_memfreeshort(&curlong, &totlong);
+ if (curlong || totlong)
+ qh_fprintf(errfile, 7068, "qhull internal warning (main): did not free %d bytes of long memory(%d pieces)\n", totlong, curlong);
+}
+#endif
+
+/*-<a href="qh-user.htm#TOC"
+ >-------------------------------</a><a name="new_qhull">-</a>
+
+ qh_new_qhull( dim, numpoints, points, ismalloc, qhull_cmd, outfile, errfile )
+ build new qhull data structure and return exitcode (0 if no errors)
+ if numpoints=0 and points=NULL, initializes qhull
+
+ notes:
+ do not modify points until finished with results.
+ The qhull data structure contains pointers into the points array.
+ do not call qhull functions before qh_new_qhull().
+ The qhull data structure is not initialized until qh_new_qhull().
+
+ Default errfile is stderr, outfile may be null
+ qhull_cmd must start with "qhull "
+ projects points to a new point array for Delaunay triangulations ('d' and 'v')
+ transforms points into a new point array for halfspace intersection ('H')
+
+
+ To allow multiple, concurrent calls to qhull()
+ - set qh_QHpointer in user.h
+ - use qh_save_qhull and qh_restore_qhull to swap the global data structure between calls.
+ - use qh_freeqhull(qh_ALL) to free intermediate convex hulls
+
+ see:
+ Qhull-template at the beginning of this file.
+ An example of using qh_new_qhull is user_eg.c
+*/
+int qh_new_qhull(int dim, int numpoints, coordT *points, boolT ismalloc,
+ char *qhull_cmd, FILE *outfile, FILE *errfile) {
+ /* gcc may issue a "might be clobbered" warning for dim, points, and ismalloc [-Wclobbered].
+ These parameters are not referenced after a longjmp() and hence not clobbered.
+ See http://stackoverflow.com/questions/7721854/what-sense-do-these-clobbered-variable-warnings-make */
+ int exitcode, hulldim;
+ boolT new_ismalloc;
+ static boolT firstcall = True;
+ coordT *new_points;
+ if(!errfile){
+ errfile= stderr;
+ }
+ if (firstcall) {
+ qh_meminit(errfile);
+ firstcall= False;
+ } else {
+ qh_memcheck();
+ }
+ if (strncmp(qhull_cmd, "qhull ", (size_t)6)) {
+ qh_fprintf(errfile, 6186, "qhull error (qh_new_qhull): start qhull_cmd argument with \"qhull \"\n");
+ return qh_ERRinput;
+ }
+ qh_initqhull_start(NULL, outfile, errfile);
+ if(numpoints==0 && points==NULL){
+ trace1((qh ferr, 1047, "qh_new_qhull: initialize Qhull\n"));
+ return 0;
+ }
+ trace1((qh ferr, 1044, "qh_new_qhull: build new Qhull for %d %d-d points with %s\n", numpoints, dim, qhull_cmd));
+ exitcode = setjmp(qh errexit);
+ if (!exitcode)
+ {
+ qh NOerrexit = False;
+ qh_initflags(qhull_cmd);
+ if (qh DELAUNAY)
+ qh PROJECTdelaunay= True;
+ if (qh HALFspace) {
+ /* points is an array of halfspaces,
+ the last coordinate of each halfspace is its offset */
+ hulldim= dim-1;
+ qh_setfeasible(hulldim);
+ new_points= qh_sethalfspace_all(dim, numpoints, points, qh feasible_point);
+ new_ismalloc= True;
+ if (ismalloc)
+ qh_free(points);
+ }else {
+ hulldim= dim;
+ new_points= points;
+ new_ismalloc= ismalloc;
+ }
+ qh_init_B(new_points, numpoints, hulldim, new_ismalloc);
+ qh_qhull();
+ qh_check_output();
+ if (outfile) {
+ qh_produce_output();
+ }else {
+ qh_prepare_output();
+ }
+ if (qh VERIFYoutput && !qh STOPpoint && !qh STOPcone)
+ qh_check_points();
+ }
+ qh NOerrexit = True;
+ return exitcode;
+} /* new_qhull */
+
+/*-<a href="qh-user.htm#TOC"
+ >-------------------------------</a><a name="errexit">-</a>
+
+ qh_errexit( exitcode, facet, ridge )
+ report and exit from an error
+ report facet and ridge if non-NULL
+ reports useful information such as last point processed
+ set qh.FORCEoutput to print neighborhood of facet
+
+ see:
+ qh_errexit2() in libqhull.c for printing 2 facets
+
+ design:
+ check for error within error processing
+ compute qh.hulltime
+ print facet and ridge (if any)
+ report commandString, options, qh.furthest_id
+ print summary and statistics (including precision statistics)
+ if qh_ERRsingular
+ print help text for singular data set
+ exit program via long jump (if defined) or exit()
+*/
+void qh_errexit(int exitcode, facetT *facet, ridgeT *ridge) {
+
+ if (qh ERREXITcalled) {
+ qh_fprintf(qh ferr, 8126, "\nqhull error while processing previous error. Exit program\n");
+ qh_exit(qh_ERRqhull);
+ }
+ qh ERREXITcalled= True;
+ if (!qh QHULLfinished)
+ qh hulltime= qh_CPUclock - qh hulltime;
+ qh_errprint("ERRONEOUS", facet, NULL, ridge, NULL);
+ qh_fprintf(qh ferr, 8127, "\nWhile executing: %s | %s\n", qh rbox_command, qh qhull_command);
+ qh_fprintf(qh ferr, 8128, "Options selected for Qhull %s:\n%s\n", qh_version, qh qhull_options);
+ if (qh furthest_id >= 0) {
+ qh_fprintf(qh ferr, 8129, "Last point added to hull was p%d.", qh furthest_id);
+ if (zzval_(Ztotmerge))
+ qh_fprintf(qh ferr, 8130, " Last merge was #%d.", zzval_(Ztotmerge));
+ if (qh QHULLfinished)
+ qh_fprintf(qh ferr, 8131, "\nQhull has finished constructing the hull.");
+ else if (qh POSTmerging)
+ qh_fprintf(qh ferr, 8132, "\nQhull has started post-merging.");
+ qh_fprintf(qh ferr, 8133, "\n");
+ }
+ if (qh FORCEoutput && (qh QHULLfinished || (!facet && !ridge)))
+ qh_produce_output();
+ else if (exitcode != qh_ERRinput) {
+ if (exitcode != qh_ERRsingular && zzval_(Zsetplane) > qh hull_dim+1) {
+ qh_fprintf(qh ferr, 8134, "\nAt error exit:\n");
+ qh_printsummary(qh ferr);
+ if (qh PRINTstatistics) {
+ qh_collectstatistics();
+ qh_printstatistics(qh ferr, "at error exit");
+ qh_memstatistics(qh ferr);
+ }
+ }
+ if (qh PRINTprecision)
+ qh_printstats(qh ferr, qhstat precision, NULL);
+ }
+ if (!exitcode)
+ exitcode= qh_ERRqhull;
+ else if (exitcode == qh_ERRsingular)
+ qh_printhelp_singular(qh ferr);
+ else if (exitcode == qh_ERRprec && !qh PREmerge)
+ qh_printhelp_degenerate(qh ferr);
+ if (qh NOerrexit) {
+ qh_fprintf(qh ferr, 6187, "qhull error while ending program, or qh->NOerrexit not cleared after setjmp(). Exit program with error.\n");
+ qh_exit(qh_ERRqhull);
+ }
+ qh ERREXITcalled= False;
+ qh NOerrexit= True;
+ qh ALLOWrestart= False; /* longjmp will undo qh_build_withrestart */
+ longjmp(qh errexit, exitcode);
+} /* errexit */
+
+
+/*-<a href="qh-user.htm#TOC"
+ >-------------------------------</a><a name="errprint">-</a>
+
+ qh_errprint( fp, string, atfacet, otherfacet, atridge, atvertex )
+ prints out the information of facets and ridges to fp
+ also prints neighbors and geomview output
+
+ notes:
+ except for string, any parameter may be NULL
+*/
+void qh_errprint(const char *string, facetT *atfacet, facetT *otherfacet, ridgeT *atridge, vertexT *atvertex) {
+ int i;
+
+ if (atfacet) {
+ qh_fprintf(qh ferr, 8135, "%s FACET:\n", string);
+ qh_printfacet(qh ferr, atfacet);
+ }
+ if (otherfacet) {
+ qh_fprintf(qh ferr, 8136, "%s OTHER FACET:\n", string);
+ qh_printfacet(qh ferr, otherfacet);
+ }
+ if (atridge) {
+ qh_fprintf(qh ferr, 8137, "%s RIDGE:\n", string);
+ qh_printridge(qh ferr, atridge);
+ if (atridge->top && atridge->top != atfacet && atridge->top != otherfacet)
+ qh_printfacet(qh ferr, atridge->top);
+ if (atridge->bottom
+ && atridge->bottom != atfacet && atridge->bottom != otherfacet)
+ qh_printfacet(qh ferr, atridge->bottom);
+ if (!atfacet)
+ atfacet= atridge->top;
+ if (!otherfacet)
+ otherfacet= otherfacet_(atridge, atfacet);
+ }
+ if (atvertex) {
+ qh_fprintf(qh ferr, 8138, "%s VERTEX:\n", string);
+ qh_printvertex(qh ferr, atvertex);
+ }
+ if (qh fout && qh FORCEoutput && atfacet && !qh QHULLfinished && !qh IStracing) {
+ qh_fprintf(qh ferr, 8139, "ERRONEOUS and NEIGHBORING FACETS to output\n");
+ for (i=0; i < qh_PRINTEND; i++) /* use fout for geomview output */
+ qh_printneighborhood(qh fout, qh PRINTout[i], atfacet, otherfacet,
+ !qh_ALL);
+ }
+} /* errprint */
+
+
+/*-<a href="qh-user.htm#TOC"
+ >-------------------------------</a><a name="printfacetlist">-</a>
+
+ qh_printfacetlist( fp, facetlist, facets, printall )
+ print all fields for a facet list and/or set of facets to fp
+ if !printall,
+ only prints good facets
+
+ notes:
+ also prints all vertices
+*/
+void qh_printfacetlist(facetT *facetlist, setT *facets, boolT printall) {
+ facetT *facet, **facetp;
+
+ qh_printbegin(qh ferr, qh_PRINTfacets, facetlist, facets, printall);
+ FORALLfacet_(facetlist)
+ qh_printafacet(qh ferr, qh_PRINTfacets, facet, printall);
+ FOREACHfacet_(facets)
+ qh_printafacet(qh ferr, qh_PRINTfacets, facet, printall);
+ qh_printend(qh ferr, qh_PRINTfacets, facetlist, facets, printall);
+} /* printfacetlist */
+
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="printhelp_degenerate">-</a>
+
+ qh_printhelp_degenerate( fp )
+ prints descriptive message for precision error
+
+ notes:
+ no message if qh_QUICKhelp
+*/
+void qh_printhelp_degenerate(FILE *fp) {
+
+ if (qh MERGEexact || qh PREmerge || qh JOGGLEmax < REALmax/2)
+ qh_fprintf(fp, 9368, "\n\
+A Qhull error has occurred. Qhull should have corrected the above\n\
+precision error. Please send the input and all of the output to\n\
+qhull_bug@qhull.org\n");
+ else if (!qh_QUICKhelp) {
+ qh_fprintf(fp, 9369, "\n\
+Precision problems were detected during construction of the convex hull.\n\
+This occurs because convex hull algorithms assume that calculations are\n\
+exact, but floating-point arithmetic has roundoff errors.\n\
+\n\
+To correct for precision problems, do not use 'Q0'. By default, Qhull\n\
+selects 'C-0' or 'Qx' and merges non-convex facets. With option 'QJ',\n\
+Qhull joggles the input to prevent precision problems. See \"Imprecision\n\
+in Qhull\" (qh-impre.htm).\n\
+\n\
+If you use 'Q0', the output may include\n\
+coplanar ridges, concave ridges, and flipped facets. In 4-d and higher,\n\
+Qhull may produce a ridge with four neighbors or two facets with the same \n\
+vertices. Qhull reports these events when they occur. It stops when a\n\
+concave ridge, flipped facet, or duplicate facet occurs.\n");
+#if REALfloat
+ qh_fprintf(fp, 9370, "\
+\n\
+Qhull is currently using single precision arithmetic. The following\n\
+will probably remove the precision problems:\n\
+ - recompile qhull for realT precision(#define REALfloat 0 in user.h).\n");
+#endif
+ if (qh DELAUNAY && !qh SCALElast && qh MAXabs_coord > 1e4)
+ qh_fprintf(fp, 9371, "\
+\n\
+When computing the Delaunay triangulation of coordinates > 1.0,\n\
+ - use 'Qbb' to scale the last coordinate to [0,m] (max previous coordinate)\n");
+ if (qh DELAUNAY && !qh ATinfinity)
+ qh_fprintf(fp, 9372, "\
+When computing the Delaunay triangulation:\n\
+ - use 'Qz' to add a point at-infinity. This reduces precision problems.\n");
+
+ qh_fprintf(fp, 9373, "\
+\n\
+If you need triangular output:\n\
+ - use option 'Qt' to triangulate the output\n\
+ - use option 'QJ' to joggle the input points and remove precision errors\n\
+ - use option 'Ft'. It triangulates non-simplicial facets with added points.\n\
+\n\
+If you must use 'Q0',\n\
+try one or more of the following options. They can not guarantee an output.\n\
+ - use 'QbB' to scale the input to a cube.\n\
+ - use 'Po' to produce output and prevent partitioning for flipped facets\n\
+ - use 'V0' to set min. distance to visible facet as 0 instead of roundoff\n\
+ - use 'En' to specify a maximum roundoff error less than %2.2g.\n\
+ - options 'Qf', 'Qbb', and 'QR0' may also help\n",
+ qh DISTround);
+ qh_fprintf(fp, 9374, "\
+\n\
+To guarantee simplicial output:\n\
+ - use option 'Qt' to triangulate the output\n\
+ - use option 'QJ' to joggle the input points and remove precision errors\n\
+ - use option 'Ft' to triangulate the output by adding points\n\
+ - use exact arithmetic (see \"Imprecision in Qhull\", qh-impre.htm)\n\
+");
+ }
+} /* printhelp_degenerate */
+
+
+/*-<a href="qh-globa.htm#TOC"
+ >-------------------------------</a><a name="printhelp_narrowhull">-</a>
+
+ qh_printhelp_narrowhull( minangle )
+ Warn about a narrow hull
+
+ notes:
+ Alternatively, reduce qh_WARNnarrow in user.h
+
+*/
+void qh_printhelp_narrowhull(FILE *fp, realT minangle) {
+
+ qh_fprintf(fp, 9375, "qhull precision warning: \n\
+The initial hull is narrow (cosine of min. angle is %.16f).\n\
+Is the input lower dimensional (e.g., on a plane in 3-d)? Qhull may\n\
+produce a wide facet. Options 'QbB' (scale to unit box) or 'Qbb' (scale\n\
+last coordinate) may remove this warning. Use 'Pp' to skip this warning.\n\
+See 'Limitations' in qh-impre.htm.\n",
+ -minangle); /* convert from angle between normals to angle between facets */
+} /* printhelp_narrowhull */
+
+/*-<a href="qh-io.htm#TOC"
+ >-------------------------------</a><a name="printhelp_singular">-</a>
+
+ qh_printhelp_singular( fp )
+ prints descriptive message for singular input
+*/
+void qh_printhelp_singular(FILE *fp) {
+ facetT *facet;
+ vertexT *vertex, **vertexp;
+ realT min, max, *coord, dist;
+ int i,k;
+
+ qh_fprintf(fp, 9376, "\n\
+The input to qhull appears to be less than %d dimensional, or a\n\
+computation has overflowed.\n\n\
+Qhull could not construct a clearly convex simplex from points:\n",
+ qh hull_dim);
+ qh_printvertexlist(fp, "", qh facet_list, NULL, qh_ALL);
+ if (!qh_QUICKhelp)
+ qh_fprintf(fp, 9377, "\n\
+The center point is coplanar with a facet, or a vertex is coplanar\n\
+with a neighboring facet. The maximum round off error for\n\
+computing distances is %2.2g. The center point, facets and distances\n\
+to the center point are as follows:\n\n", qh DISTround);
+ qh_printpointid(fp, "center point", qh hull_dim, qh interior_point, qh_IDunknown);
+ qh_fprintf(fp, 9378, "\n");
+ FORALLfacets {
+ qh_fprintf(fp, 9379, "facet");
+ FOREACHvertex_(facet->vertices)
+ qh_fprintf(fp, 9380, " p%d", qh_pointid(vertex->point));
+ zinc_(Zdistio);
+ qh_distplane(qh interior_point, facet, &dist);
+ qh_fprintf(fp, 9381, " distance= %4.2g\n", dist);
+ }
+ if (!qh_QUICKhelp) {
+ if (qh HALFspace)
+ qh_fprintf(fp, 9382, "\n\
+These points are the dual of the given halfspaces. They indicate that\n\
+the intersection is degenerate.\n");
+ qh_fprintf(fp, 9383,"\n\
+These points either have a maximum or minimum x-coordinate, or\n\
+they maximize the determinant for k coordinates. Trial points\n\
+are first selected from points that maximize a coordinate.\n");
+ if (qh hull_dim >= qh_INITIALmax)
+ qh_fprintf(fp, 9384, "\n\
+Because of the high dimension, the min x-coordinate and max-coordinate\n\
+points are used if the determinant is non-zero. Option 'Qs' will\n\
+do a better, though much slower, job. Instead of 'Qs', you can change\n\
+the points by randomly rotating the input with 'QR0'.\n");
+ }
+ qh_fprintf(fp, 9385, "\nThe min and max coordinates for each dimension are:\n");
+ for (k=0; k < qh hull_dim; k++) {
+ min= REALmax;
+ max= -REALmin;
+ for (i=qh num_points, coord= qh first_point+k; i--; coord += qh hull_dim) {
+ maximize_(max, *coord);
+ minimize_(min, *coord);
+ }
+ qh_fprintf(fp, 9386, " %d: %8.4g %8.4g difference= %4.4g\n", k, min, max, max-min);
+ }
+ if (!qh_QUICKhelp) {
+ qh_fprintf(fp, 9387, "\n\
+If the input should be full dimensional, you have several options that\n\
+may determine an initial simplex:\n\
+ - use 'QJ' to joggle the input and make it full dimensional\n\
+ - use 'QbB' to scale the points to the unit cube\n\
+ - use 'QR0' to randomly rotate the input for different maximum points\n\
+ - use 'Qs' to search all points for the initial simplex\n\
+ - use 'En' to specify a maximum roundoff error less than %2.2g.\n\
+ - trace execution with 'T3' to see the determinant for each point.\n",
+ qh DISTround);
+#if REALfloat
+ qh_fprintf(fp, 9388, "\
+ - recompile qhull for realT precision(#define REALfloat 0 in libqhull.h).\n");
+#endif
+ qh_fprintf(fp, 9389, "\n\
+If the input is lower dimensional:\n\
+ - use 'QJ' to joggle the input and make it full dimensional\n\
+ - use 'Qbk:0Bk:0' to delete coordinate k from the input. You should\n\
+ pick the coordinate with the least range. The hull will have the\n\
+ correct topology.\n\
+ - determine the flat containing the points, rotate the points\n\
+ into a coordinate plane, and delete the other coordinates.\n\
+ - add one or more points to make the input full dimensional.\n\
+");
+ }
+} /* printhelp_singular */
+
+/*-<a href="qh-globa.htm#TOC"
+ >-------------------------------</a><a name="user_memsizes">-</a>
+
+ qh_user_memsizes()
+ allocate up to 10 additional, quick allocation sizes
+
+ notes:
+ increase maximum number of allocations in qh_initqhull_mem()
+*/
+void qh_user_memsizes(void) {
+
+ /* qh_memsize(size); */
+} /* user_memsizes */
+
+
diff --git a/xs/src/qhull/src/libqhull/user.h b/xs/src/qhull/src/libqhull/user.h
new file mode 100644
index 000000000..523aa7b4e
--- /dev/null
+++ b/xs/src/qhull/src/libqhull/user.h
@@ -0,0 +1,909 @@
+/*<html><pre> -<a href="qh-user.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ user.h
+ user redefinable constants
+
+ for each source file, user.h is included first
+ see qh-user.htm. see COPYING for copyright information.
+
+ See user.c for sample code.
+
+ before reading any code, review libqhull.h for data structure definitions and
+ the "qh" macro.
+
+Sections:
+ ============= qhull library constants ======================
+ ============= data types and configuration macros ==========
+ ============= performance related constants ================
+ ============= memory constants =============================
+ ============= joggle constants =============================
+ ============= conditional compilation ======================
+ ============= -merge constants- ============================
+
+Code flags --
+ NOerrors -- the code does not call qh_errexit()
+ WARN64 -- the code may be incompatible with 64-bit pointers
+
+*/
+
+#include <time.h>
+
+#ifndef qhDEFuser
+#define qhDEFuser 1
+
+/* Derived from Qt's corelib/global/qglobal.h */
+#if !defined(SAG_COM) && !defined(__CYGWIN__) && (defined(WIN64) || defined(_WIN64) || defined(__WIN64__) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__))
+# define QHULL_OS_WIN
+#elif defined(__MWERKS__) && defined(__INTEL__) /* Metrowerks discontinued before the release of Intel Macs */
+# define QHULL_OS_WIN
+#endif
+/*============================================================*/
+/*============= qhull library constants ======================*/
+/*============================================================*/
+
+/*-<a href="qh-user.htm#TOC"
+ >--------------------------------</a><a name="filenamelen">-</a>
+
+ FILENAMElen -- max length for TI and TO filenames
+
+*/
+
+#define qh_FILENAMElen 500
+
+/*-<a href="qh-user.htm#TOC"
+ >--------------------------------</a><a name="msgcode">-</a>
+
+ msgcode -- Unique message codes for qh_fprintf
+
+ If add new messages, assign these values and increment in user.h and user_r.h
+ See QhullError.h for 10000 errors.
+
+ def counters = [27, 1048, 2059, 3026, 4068, 5003,
+ 6273, 7081, 8147, 9411, 10000, 11029]
+
+ See: qh_ERR* [libqhull.h]
+*/
+
+#define MSG_TRACE0 0
+#define MSG_TRACE1 1000
+#define MSG_TRACE2 2000
+#define MSG_TRACE3 3000
+#define MSG_TRACE4 4000
+#define MSG_TRACE5 5000
+#define MSG_ERROR 6000 /* errors written to qh.ferr */
+#define MSG_WARNING 7000
+#define MSG_STDERR 8000 /* log messages Written to qh.ferr */
+#define MSG_OUTPUT 9000
+#define MSG_QHULL_ERROR 10000 /* errors thrown by QhullError.cpp (QHULLlastError is in QhullError.h) */
+#define MSG_FIXUP 11000 /* FIXUP QH11... */
+#define MSG_MAXLEN 3000 /* qh_printhelp_degenerate() in user.c */
+
+
+/*-<a href="qh-user.htm#TOC"
+ >--------------------------------</a><a name="qh_OPTIONline">-</a>
+
+ qh_OPTIONline -- max length of an option line 'FO'
+*/
+#define qh_OPTIONline 80
+
+/*============================================================*/
+/*============= data types and configuration macros ==========*/
+/*============================================================*/
+
+/*-<a href="qh-user.htm#TOC"
+ >--------------------------------</a><a name="realT">-</a>
+
+ realT
+ set the size of floating point numbers
+
+ qh_REALdigits
+ maximimum number of significant digits
+
+ qh_REAL_1, qh_REAL_2n, qh_REAL_3n
+ format strings for printf
+
+ qh_REALmax, qh_REALmin
+ maximum and minimum (near zero) values
+
+ qh_REALepsilon
+ machine roundoff. Maximum roundoff error for addition and multiplication.
+
+ notes:
+ Select whether to store floating point numbers in single precision (float)
+ or double precision (double).
+
+ Use 'float' to save about 8% in time and 25% in space. This is particularly
+ helpful if high-d where convex hulls are space limited. Using 'float' also
+ reduces the printed size of Qhull's output since numbers have 8 digits of
+ precision.
+
+ Use 'double' when greater arithmetic precision is needed. This is needed
+ for Delaunay triangulations and Voronoi diagrams when you are not merging
+ facets.
+
+ If 'double' gives insufficient precision, your data probably includes
+ degeneracies. If so you should use facet merging (done by default)
+ or exact arithmetic (see imprecision section of manual, qh-impre.htm).
+ You may also use option 'Po' to force output despite precision errors.
+
+ You may use 'long double', but many format statements need to be changed
+ and you may need a 'long double' square root routine. S. Grundmann
+ (sg@eeiwzb.et.tu-dresden.de) has done this. He reports that the code runs
+ much slower with little gain in precision.
+
+ WARNING: on some machines, int f(){realT a= REALmax;return (a == REALmax);}
+ returns False. Use (a > REALmax/2) instead of (a == REALmax).
+
+ REALfloat = 1 all numbers are 'float' type
+ = 0 all numbers are 'double' type
+*/
+#define REALfloat 0
+
+#if (REALfloat == 1)
+#define realT float
+#define REALmax FLT_MAX
+#define REALmin FLT_MIN
+#define REALepsilon FLT_EPSILON
+#define qh_REALdigits 8 /* maximum number of significant digits */
+#define qh_REAL_1 "%6.8g "
+#define qh_REAL_2n "%6.8g %6.8g\n"
+#define qh_REAL_3n "%6.8g %6.8g %6.8g\n"
+
+#elif (REALfloat == 0)
+#define realT double
+#define REALmax DBL_MAX
+#define REALmin DBL_MIN
+#define REALepsilon DBL_EPSILON
+#define qh_REALdigits 16 /* maximum number of significant digits */
+#define qh_REAL_1 "%6.16g "
+#define qh_REAL_2n "%6.16g %6.16g\n"
+#define qh_REAL_3n "%6.16g %6.16g %6.16g\n"
+
+#else
+#error unknown float option
+#endif
+
+/*-<a href="qh-user.htm#TOC"
+ >--------------------------------</a><a name="CPUclock">-</a>
+
+ qh_CPUclock
+ define the clock() function for reporting the total time spent by Qhull
+ returns CPU ticks as a 'long int'
+ qh_CPUclock is only used for reporting the total time spent by Qhull
+
+ qh_SECticks
+ the number of clock ticks per second
+
+ notes:
+ looks for CLOCKS_PER_SEC, CLOCKS_PER_SECOND, or assumes microseconds
+ to define a custom clock, set qh_CLOCKtype to 0
+
+ if your system does not use clock() to return CPU ticks, replace
+ qh_CPUclock with the corresponding function. It is converted
+ to 'unsigned long' to prevent wrap-around during long runs. By default,
+ <time.h> defines clock_t as 'long'
+
+ Set qh_CLOCKtype to
+
+ 1 for CLOCKS_PER_SEC, CLOCKS_PER_SECOND, or microsecond
+ Note: may fail if more than 1 hour elapsed time
+
+ 2 use qh_clock() with POSIX times() (see global.c)
+*/
+#define qh_CLOCKtype 1 /* change to the desired number */
+
+#if (qh_CLOCKtype == 1)
+
+#if defined(CLOCKS_PER_SECOND)
+#define qh_CPUclock ((unsigned long)clock()) /* return CPU clock */
+#define qh_SECticks CLOCKS_PER_SECOND
+
+#elif defined(CLOCKS_PER_SEC)
+#define qh_CPUclock ((unsigned long)clock()) /* return CPU clock */
+#define qh_SECticks CLOCKS_PER_SEC
+
+#elif defined(CLK_TCK)
+#define qh_CPUclock ((unsigned long)clock()) /* return CPU clock */
+#define qh_SECticks CLK_TCK
+
+#else
+#define qh_CPUclock ((unsigned long)clock()) /* return CPU clock */
+#define qh_SECticks 1E6
+#endif
+
+#elif (qh_CLOCKtype == 2)
+#define qh_CPUclock qh_clock() /* return CPU clock */
+#define qh_SECticks 100
+
+#else /* qh_CLOCKtype == ? */
+#error unknown clock option
+#endif
+
+/*-<a href="qh-user.htm#TOC"
+ >--------------------------------</a><a name="RANDOM">-</a>
+
+ qh_RANDOMtype, qh_RANDOMmax, qh_RANDOMseed
+ define random number generator
+
+ qh_RANDOMint generates a random integer between 0 and qh_RANDOMmax.
+ qh_RANDOMseed sets the random number seed for qh_RANDOMint
+
+ Set qh_RANDOMtype (default 5) to:
+ 1 for random() with 31 bits (UCB)
+ 2 for rand() with RAND_MAX or 15 bits (system 5)
+ 3 for rand() with 31 bits (Sun)
+ 4 for lrand48() with 31 bits (Solaris)
+ 5 for qh_rand() with 31 bits (included with Qhull)
+
+ notes:
+ Random numbers are used by rbox to generate point sets. Random
+ numbers are used by Qhull to rotate the input ('QRn' option),
+ simulate a randomized algorithm ('Qr' option), and to simulate
+ roundoff errors ('Rn' option).
+
+ Random number generators differ between systems. Most systems provide
+ rand() but the period varies. The period of rand() is not critical
+ since qhull does not normally use random numbers.
+
+ The default generator is Park & Miller's minimal standard random
+ number generator [CACM 31:1195 '88]. It is included with Qhull.
+
+ If qh_RANDOMmax is wrong, qhull will report a warning and Geomview
+ output will likely be invisible.
+*/
+#define qh_RANDOMtype 5 /* *** change to the desired number *** */
+
+#if (qh_RANDOMtype == 1)
+#define qh_RANDOMmax ((realT)0x7fffffffUL) /* 31 bits, random()/MAX */
+#define qh_RANDOMint random()
+#define qh_RANDOMseed_(seed) srandom(seed);
+
+#elif (qh_RANDOMtype == 2)
+#ifdef RAND_MAX
+#define qh_RANDOMmax ((realT)RAND_MAX)
+#else
+#define qh_RANDOMmax ((realT)32767) /* 15 bits (System 5) */
+#endif
+#define qh_RANDOMint rand()
+#define qh_RANDOMseed_(seed) srand((unsigned)seed);
+
+#elif (qh_RANDOMtype == 3)
+#define qh_RANDOMmax ((realT)0x7fffffffUL) /* 31 bits, Sun */
+#define qh_RANDOMint rand()
+#define qh_RANDOMseed_(seed) srand((unsigned)seed);
+
+#elif (qh_RANDOMtype == 4)
+#define qh_RANDOMmax ((realT)0x7fffffffUL) /* 31 bits, lrand38()/MAX */
+#define qh_RANDOMint lrand48()
+#define qh_RANDOMseed_(seed) srand48(seed);
+
+#elif (qh_RANDOMtype == 5)
+#define qh_RANDOMmax ((realT)2147483646UL) /* 31 bits, qh_rand/MAX */
+#define qh_RANDOMint qh_rand()
+#define qh_RANDOMseed_(seed) qh_srand(seed);
+/* unlike rand(), never returns 0 */
+
+#else
+#error: unknown random option
+#endif
+
+/*-<a href="qh-user.htm#TOC"
+ >--------------------------------</a><a name="ORIENTclock">-</a>
+
+ qh_ORIENTclock
+ 0 for inward pointing normals by Geomview convention
+*/
+#define qh_ORIENTclock 0
+
+
+/*============================================================*/
+/*============= joggle constants =============================*/
+/*============================================================*/
+
+/*-<a href="qh-user.htm#TOC"
+>--------------------------------</a><a name="JOGGLEdefault">-</a>
+
+qh_JOGGLEdefault
+default qh.JOGGLEmax is qh.DISTround * qh_JOGGLEdefault
+
+notes:
+rbox s r 100 | qhull QJ1e-15 QR0 generates 90% faults at distround 7e-16
+rbox s r 100 | qhull QJ1e-14 QR0 generates 70% faults
+rbox s r 100 | qhull QJ1e-13 QR0 generates 35% faults
+rbox s r 100 | qhull QJ1e-12 QR0 generates 8% faults
+rbox s r 100 | qhull QJ1e-11 QR0 generates 1% faults
+rbox s r 100 | qhull QJ1e-10 QR0 generates 0% faults
+rbox 1000 W0 | qhull QJ1e-12 QR0 generates 86% faults
+rbox 1000 W0 | qhull QJ1e-11 QR0 generates 20% faults
+rbox 1000 W0 | qhull QJ1e-10 QR0 generates 2% faults
+the later have about 20 points per facet, each of which may interfere
+
+pick a value large enough to avoid retries on most inputs
+*/
+#define qh_JOGGLEdefault 30000.0
+
+/*-<a href="qh-user.htm#TOC"
+>--------------------------------</a><a name="JOGGLEincrease">-</a>
+
+qh_JOGGLEincrease
+factor to increase qh.JOGGLEmax on qh_JOGGLEretry or qh_JOGGLEagain
+*/
+#define qh_JOGGLEincrease 10.0
+
+/*-<a href="qh-user.htm#TOC"
+>--------------------------------</a><a name="JOGGLEretry">-</a>
+
+qh_JOGGLEretry
+if ZZretry = qh_JOGGLEretry, increase qh.JOGGLEmax
+
+notes:
+try twice at the original value in case of bad luck the first time
+*/
+#define qh_JOGGLEretry 2
+
+/*-<a href="qh-user.htm#TOC"
+>--------------------------------</a><a name="JOGGLEagain">-</a>
+
+qh_JOGGLEagain
+every following qh_JOGGLEagain, increase qh.JOGGLEmax
+
+notes:
+1 is OK since it's already failed qh_JOGGLEretry times
+*/
+#define qh_JOGGLEagain 1
+
+/*-<a href="qh-user.htm#TOC"
+>--------------------------------</a><a name="JOGGLEmaxincrease">-</a>
+
+qh_JOGGLEmaxincrease
+maximum qh.JOGGLEmax due to qh_JOGGLEincrease
+relative to qh.MAXwidth
+
+notes:
+qh.joggleinput will retry at this value until qh_JOGGLEmaxretry
+*/
+#define qh_JOGGLEmaxincrease 1e-2
+
+/*-<a href="qh-user.htm#TOC"
+>--------------------------------</a><a name="JOGGLEmaxretry">-</a>
+
+qh_JOGGLEmaxretry
+stop after qh_JOGGLEmaxretry attempts
+*/
+#define qh_JOGGLEmaxretry 100
+
+/*============================================================*/
+/*============= performance related constants ================*/
+/*============================================================*/
+
+/*-<a href="qh-user.htm#TOC"
+ >--------------------------------</a><a name="HASHfactor">-</a>
+
+ qh_HASHfactor
+ total hash slots / used hash slots. Must be at least 1.1.
+
+ notes:
+ =2 for at worst 50% occupancy for qh.hash_table and normally 25% occupancy
+*/
+#define qh_HASHfactor 2
+
+/*-<a href="qh-user.htm#TOC"
+ >--------------------------------</a><a name="VERIFYdirect">-</a>
+
+ qh_VERIFYdirect
+ with 'Tv' verify all points against all facets if op count is smaller
+
+ notes:
+ if greater, calls qh_check_bestdist() instead
+*/
+#define qh_VERIFYdirect 1000000
+
+/*-<a href="qh-user.htm#TOC"
+ >--------------------------------</a><a name="INITIALsearch">-</a>
+
+ qh_INITIALsearch
+ if qh_INITIALmax, search points up to this dimension
+*/
+#define qh_INITIALsearch 6
+
+/*-<a href="qh-user.htm#TOC"
+ >--------------------------------</a><a name="INITIALmax">-</a>
+
+ qh_INITIALmax
+ if dim >= qh_INITIALmax, use min/max coordinate points for initial simplex
+
+ notes:
+ from points with non-zero determinants
+ use option 'Qs' to override (much slower)
+*/
+#define qh_INITIALmax 8
+
+/*============================================================*/
+/*============= memory constants =============================*/
+/*============================================================*/
+
+/*-<a href="qh-user.htm#TOC"
+ >--------------------------------</a><a name="MEMalign">-</a>
+
+ qh_MEMalign
+ memory alignment for qh_meminitbuffers() in global.c
+
+ notes:
+ to avoid bus errors, memory allocation must consider alignment requirements.
+ malloc() automatically takes care of alignment. Since mem.c manages
+ its own memory, we need to explicitly specify alignment in
+ qh_meminitbuffers().
+
+ A safe choice is sizeof(double). sizeof(float) may be used if doubles
+ do not occur in data structures and pointers are the same size. Be careful
+ of machines (e.g., DEC Alpha) with large pointers.
+
+ If using gcc, best alignment is [fmax_() is defined in geom_r.h]
+ #define qh_MEMalign fmax_(__alignof__(realT),__alignof__(void *))
+*/
+#define qh_MEMalign ((int)(fmax_(sizeof(realT), sizeof(void *))))
+
+/*-<a href="qh-user.htm#TOC"
+ >--------------------------------</a><a name="MEMbufsize">-</a>
+
+ qh_MEMbufsize
+ size of additional memory buffers
+
+ notes:
+ used for qh_meminitbuffers() in global.c
+*/
+#define qh_MEMbufsize 0x10000 /* allocate 64K memory buffers */
+
+/*-<a href="qh-user.htm#TOC"
+ >--------------------------------</a><a name="MEMinitbuf">-</a>
+
+ qh_MEMinitbuf
+ size of initial memory buffer
+
+ notes:
+ use for qh_meminitbuffers() in global.c
+*/
+#define qh_MEMinitbuf 0x20000 /* initially allocate 128K buffer */
+
+/*-<a href="qh-user.htm#TOC"
+ >--------------------------------</a><a name="INFINITE">-</a>
+
+ qh_INFINITE
+ on output, indicates Voronoi center at infinity
+*/
+#define qh_INFINITE -10.101
+
+/*-<a href="qh-user.htm#TOC"
+ >--------------------------------</a><a name="DEFAULTbox">-</a>
+
+ qh_DEFAULTbox
+ default box size (Geomview expects 0.5)
+
+ qh_DEFAULTbox
+ default box size for integer coorindate (rbox only)
+*/
+#define qh_DEFAULTbox 0.5
+#define qh_DEFAULTzbox 1e6
+
+/*============================================================*/
+/*============= conditional compilation ======================*/
+/*============================================================*/
+
+/*-<a href="qh-user.htm#TOC"
+ >--------------------------------</a><a name="compiler">-</a>
+
+ __cplusplus
+ defined by C++ compilers
+
+ __MSC_VER
+ defined by Microsoft Visual C++
+
+ __MWERKS__ && __INTEL__
+ defined by Metrowerks when compiling for Windows (not Intel-based Macintosh)
+
+ __MWERKS__ && __POWERPC__
+ defined by Metrowerks when compiling for PowerPC-based Macintosh
+ __STDC__
+ defined for strict ANSI C
+*/
+
+/*-<a href="qh-user.htm#TOC"
+ >--------------------------------</a><a name="COMPUTEfurthest">-</a>
+
+ qh_COMPUTEfurthest
+ compute furthest distance to an outside point instead of storing it with the facet
+ =1 to compute furthest
+
+ notes:
+ computing furthest saves memory but costs time
+ about 40% more distance tests for partitioning
+ removes facet->furthestdist
+*/
+#define qh_COMPUTEfurthest 0
+
+/*-<a href="qh-user.htm#TOC"
+ >--------------------------------</a><a name="KEEPstatistics">-</a>
+
+ qh_KEEPstatistics
+ =0 removes most of statistic gathering and reporting
+
+ notes:
+ if 0, code size is reduced by about 4%.
+*/
+#define qh_KEEPstatistics 1
+
+/*-<a href="qh-user.htm#TOC"
+ >--------------------------------</a><a name="MAXoutside">-</a>
+
+ qh_MAXoutside
+ record outer plane for each facet
+ =1 to record facet->maxoutside
+
+ notes:
+ this takes a realT per facet and slightly slows down qhull
+ it produces better outer planes for geomview output
+*/
+#define qh_MAXoutside 1
+
+/*-<a href="qh-user.htm#TOC"
+ >--------------------------------</a><a name="NOmerge">-</a>
+
+ qh_NOmerge
+ disables facet merging if defined
+
+ notes:
+ This saves about 10% space.
+
+ Unless 'Q0'
+ qh_NOmerge sets 'QJ' to avoid precision errors
+
+ #define qh_NOmerge
+
+ see:
+ <a href="mem.h#NOmem">qh_NOmem</a> in mem.c
+
+ see user.c/user_eg.c for removing io.o
+*/
+
+/*-<a href="qh-user.htm#TOC"
+ >--------------------------------</a><a name="NOtrace">-</a>
+
+ qh_NOtrace
+ no tracing if defined
+
+ notes:
+ This saves about 5% space.
+
+ #define qh_NOtrace
+*/
+
+/*-<a href="qh-user.htm#TOC"
+ >--------------------------------</a><a name="QHpointer">-</a>
+
+ qh_QHpointer
+ access global data with pointer or static structure
+
+ qh_QHpointer = 1 access globals via a pointer to allocated memory
+ enables qh_saveqhull() and qh_restoreqhull()
+ [2010, gcc] costs about 4% in time and 4% in space
+ [2003, msvc] costs about 8% in time and 2% in space
+
+ = 0 qh_qh and qh_qhstat are static data structures
+ only one instance of qhull() can be active at a time
+ default value
+
+ qh_QHpointer_dllimport and qh_dllimport define qh_qh as __declspec(dllimport) [libqhull.h]
+ It is required for msvc-2005. It is not needed for gcc.
+
+ notes:
+ [jan'16] qh_QHpointer is deprecated for Qhull. Use libqhull_r instead.
+ all global variables for qhull are in qh, qhmem, and qhstat
+ qh is defined in libqhull.h
+ qhmem is defined in mem.h
+ qhstat is defined in stat.h
+
+*/
+#ifdef qh_QHpointer
+#if qh_dllimport
+#error QH6207 Qhull error: Use qh_QHpointer_dllimport instead of qh_dllimport with qh_QHpointer
+#endif
+#else
+#define qh_QHpointer 0
+#if qh_QHpointer_dllimport
+#error QH6234 Qhull error: Use qh_dllimport instead of qh_QHpointer_dllimport when qh_QHpointer is not defined
+#endif
+#endif
+#if 0 /* sample code */
+ qhT *oldqhA, *oldqhB;
+
+ exitcode= qh_new_qhull(dim, numpoints, points, ismalloc,
+ flags, outfile, errfile);
+ /* use results from first call to qh_new_qhull */
+ oldqhA= qh_save_qhull();
+ exitcode= qh_new_qhull(dimB, numpointsB, pointsB, ismalloc,
+ flags, outfile, errfile);
+ /* use results from second call to qh_new_qhull */
+ oldqhB= qh_save_qhull();
+ qh_restore_qhull(&oldqhA);
+ /* use results from first call to qh_new_qhull */
+ qh_freeqhull(qh_ALL); /* frees all memory used by first call */
+ qh_restore_qhull(&oldqhB);
+ /* use results from second call to qh_new_qhull */
+ qh_freeqhull(!qh_ALL); /* frees long memory used by second call */
+ qh_memfreeshort(&curlong, &totlong); /* frees short memory and memory allocator */
+#endif
+
+/*-<a href="qh-user.htm#TOC"
+ >--------------------------------</a><a name="QUICKhelp">-</a>
+
+ qh_QUICKhelp
+ =1 to use abbreviated help messages, e.g., for degenerate inputs
+*/
+#define qh_QUICKhelp 0
+
+/*============================================================*/
+/*============= -merge constants- ============================*/
+/*============================================================*/
+/*
+ These constants effect facet merging. You probably will not need
+ to modify them. They effect the performance of facet merging.
+*/
+
+/*-<a href="qh-user.htm#TOC"
+ >--------------------------------</a><a name="DIMmergeVertex">-</a>
+
+ qh_DIMmergeVertex
+ max dimension for vertex merging (it is not effective in high-d)
+*/
+#define qh_DIMmergeVertex 6
+
+/*-<a href="qh-user.htm#TOC"
+ >--------------------------------</a><a name="DIMreduceBuild">-</a>
+
+ qh_DIMreduceBuild
+ max dimension for vertex reduction during build (slow in high-d)
+*/
+#define qh_DIMreduceBuild 5
+
+/*-<a href="qh-user.htm#TOC"
+ >--------------------------------</a><a name="BESTcentrum">-</a>
+
+ qh_BESTcentrum
+ if > 2*dim+n vertices, qh_findbestneighbor() tests centrums (faster)
+ else, qh_findbestneighbor() tests all vertices (much better merges)
+
+ qh_BESTcentrum2
+ if qh_BESTcentrum2 * DIM3 + BESTcentrum < #vertices tests centrums
+*/
+#define qh_BESTcentrum 20
+#define qh_BESTcentrum2 2
+
+/*-<a href="qh-user.htm#TOC"
+ >--------------------------------</a><a name="BESTnonconvex">-</a>
+
+ qh_BESTnonconvex
+ if > dim+n neighbors, qh_findbestneighbor() tests nonconvex ridges.
+
+ notes:
+ It is needed because qh_findbestneighbor is slow for large facets
+*/
+#define qh_BESTnonconvex 15
+
+/*-<a href="qh-user.htm#TOC"
+ >--------------------------------</a><a name="MAXnewmerges">-</a>
+
+ qh_MAXnewmerges
+ if >n newmerges, qh_merge_nonconvex() calls qh_reducevertices_centrums.
+
+ notes:
+ It is needed because postmerge can merge many facets at once
+*/
+#define qh_MAXnewmerges 2
+
+/*-<a href="qh-user.htm#TOC"
+ >--------------------------------</a><a name="MAXnewcentrum">-</a>
+
+ qh_MAXnewcentrum
+ if <= dim+n vertices (n approximates the number of merges),
+ reset the centrum in qh_updatetested() and qh_mergecycle_facets()
+
+ notes:
+ needed to reduce cost and because centrums may move too much if
+ many vertices in high-d
+*/
+#define qh_MAXnewcentrum 5
+
+/*-<a href="qh-user.htm#TOC"
+ >--------------------------------</a><a name="COPLANARratio">-</a>
+
+ qh_COPLANARratio
+ for 3-d+ merging, qh.MINvisible is n*premerge_centrum
+
+ notes:
+ for non-merging, it's DISTround
+*/
+#define qh_COPLANARratio 3
+
+/*-<a href="qh-user.htm#TOC"
+ >--------------------------------</a><a name="DISToutside">-</a>
+
+ qh_DISToutside
+ When is a point clearly outside of a facet?
+ Stops search in qh_findbestnew or qh_partitionall
+ qh_findbest uses qh.MINoutside since since it is only called if no merges.
+
+ notes:
+ 'Qf' always searches for best facet
+ if !qh.MERGING, same as qh.MINoutside.
+ if qh_USEfindbestnew, increase value since neighboring facets may be ill-behaved
+ [Note: Zdelvertextot occurs normally with interior points]
+ RBOX 1000 s Z1 G1e-13 t1001188774 | QHULL Tv
+ When there is a sharp edge, need to move points to a
+ clearly good facet; otherwise may be lost in another partitioning.
+ if too big then O(n^2) behavior for partitioning in cone
+ if very small then important points not processed
+ Needed in qh_partitionall for
+ RBOX 1000 s Z1 G1e-13 t1001032651 | QHULL Tv
+ Needed in qh_findbestnew for many instances of
+ RBOX 1000 s Z1 G1e-13 t | QHULL Tv
+
+ See:
+ qh_DISToutside -- when is a point clearly outside of a facet
+ qh_SEARCHdist -- when is facet coplanar with the best facet?
+ qh_USEfindbestnew -- when to use qh_findbestnew for qh_partitionpoint()
+*/
+#define qh_DISToutside ((qh_USEfindbestnew ? 2 : 1) * \
+ fmax_((qh MERGING ? 2 : 1)*qh MINoutside, qh max_outside))
+
+/*-<a href="qh-user.htm#TOC"
+ >--------------------------------</a><a name="RATIOnearinside">-</a>
+
+ qh_RATIOnearinside
+ ratio of qh.NEARinside to qh.ONEmerge for retaining inside points for
+ qh_check_maxout().
+
+ notes:
+ This is overkill since do not know the correct value.
+ It effects whether 'Qc' reports all coplanar points
+ Not used for 'd' since non-extreme points are coplanar
+*/
+#define qh_RATIOnearinside 5
+
+/*-<a href="qh-user.htm#TOC"
+ >--------------------------------</a><a name="SEARCHdist">-</a>
+
+ qh_SEARCHdist
+ When is a facet coplanar with the best facet?
+ qh_findbesthorizon: all coplanar facets of the best facet need to be searched.
+
+ See:
+ qh_DISToutside -- when is a point clearly outside of a facet
+ qh_SEARCHdist -- when is facet coplanar with the best facet?
+ qh_USEfindbestnew -- when to use qh_findbestnew for qh_partitionpoint()
+*/
+#define qh_SEARCHdist ((qh_USEfindbestnew ? 2 : 1) * \
+ (qh max_outside + 2 * qh DISTround + fmax_( qh MINvisible, qh MAXcoplanar)));
+
+/*-<a href="qh-user.htm#TOC"
+ >--------------------------------</a><a name="USEfindbestnew">-</a>
+
+ qh_USEfindbestnew
+ Always use qh_findbestnew for qh_partitionpoint, otherwise use
+ qh_findbestnew if merged new facet or sharpnewfacets.
+
+ See:
+ qh_DISToutside -- when is a point clearly outside of a facet
+ qh_SEARCHdist -- when is facet coplanar with the best facet?
+ qh_USEfindbestnew -- when to use qh_findbestnew for qh_partitionpoint()
+*/
+#define qh_USEfindbestnew (zzval_(Ztotmerge) > 50)
+
+/*-<a href="qh-user.htm#TOC"
+ >--------------------------------</a><a name="WIDEcoplanar">-</a>
+
+ qh_WIDEcoplanar
+ n*MAXcoplanar or n*MINvisible for a WIDEfacet
+
+ if vertex is further than qh.WIDEfacet from the hyperplane
+ then its ridges are not counted in computing the area, and
+ the facet's centrum is frozen.
+
+ notes:
+ qh.WIDEfacet= max(qh.MAXoutside,qh_WIDEcoplanar*qh.MAXcoplanar,
+ qh_WIDEcoplanar * qh.MINvisible);
+*/
+#define qh_WIDEcoplanar 6
+
+/*-<a href="qh-user.htm#TOC"
+ >--------------------------------</a><a name="WIDEduplicate">-</a>
+
+ qh_WIDEduplicate
+ Merge ratio for errexit from qh_forcedmerges due to duplicate ridge
+ Override with option Q12 no-wide-duplicate
+
+ Notes:
+ Merging a duplicate ridge can lead to very wide facets.
+ A future release of qhull will avoid duplicate ridges by removing duplicate sub-ridges from the horizon
+*/
+#define qh_WIDEduplicate 100
+
+/*-<a href="qh-user.htm#TOC"
+ >--------------------------------</a><a name="MAXnarrow">-</a>
+
+ qh_MAXnarrow
+ max. cosine in initial hull that sets qh.NARROWhull
+
+ notes:
+ If qh.NARROWhull, the initial partition does not make
+ coplanar points. If narrow, a coplanar point can be
+ coplanar to two facets of opposite orientations and
+ distant from the exact convex hull.
+
+ Conservative estimate. Don't actually see problems until it is -1.0
+*/
+#define qh_MAXnarrow -0.99999999
+
+/*-<a href="qh-user.htm#TOC"
+ >--------------------------------</a><a name="WARNnarrow">-</a>
+
+ qh_WARNnarrow
+ max. cosine in initial hull to warn about qh.NARROWhull
+
+ notes:
+ this is a conservative estimate.
+ Don't actually see problems until it is -1.0. See qh-impre.htm
+*/
+#define qh_WARNnarrow -0.999999999999999
+
+/*-<a href="qh-user.htm#TOC"
+ >--------------------------------</a><a name="ZEROdelaunay">-</a>
+
+ qh_ZEROdelaunay
+ a zero Delaunay facet occurs for input sites coplanar with their convex hull
+ the last normal coefficient of a zero Delaunay facet is within
+ qh_ZEROdelaunay * qh.ANGLEround of 0
+
+ notes:
+ qh_ZEROdelaunay does not allow for joggled input ('QJ').
+
+ You can avoid zero Delaunay facets by surrounding the input with a box.
+
+ Use option 'PDk:-n' to explicitly define zero Delaunay facets
+ k= dimension of input sites (e.g., 3 for 3-d Delaunay triangulation)
+ n= the cutoff for zero Delaunay facets (e.g., 'PD3:-1e-12')
+*/
+#define qh_ZEROdelaunay 2
+
+/*============================================================*/
+/*============= Microsoft DevStudio ==========================*/
+/*============================================================*/
+
+/*
+ Finding Memory Leaks Using the CRT Library
+ https://msdn.microsoft.com/en-us/library/x98tx3cf(v=vs.100).aspx
+
+ Reports enabled in qh_lib_check for Debug window and stderr
+
+ From 2005=>msvcr80d, 2010=>msvcr100d, 2012=>msvcr110d
+
+ Watch: {,,msvcr80d.dll}_crtBreakAlloc Value from {n} in the leak report
+ _CrtSetBreakAlloc(689); // qh_lib_check() [global_r.c]
+
+ Examples
+ http://free-cad.sourceforge.net/SrcDocu/d2/d7f/MemDebug_8cpp_source.html
+ https://github.com/illlust/Game/blob/master/library/MemoryLeak.cpp
+*/
+#if 0 /* off (0) by default for QHULL_CRTDBG */
+#define QHULL_CRTDBG
+#endif
+
+#if defined(_MSC_VER) && defined(_DEBUG) && defined(QHULL_CRTDBG)
+#define _CRTDBG_MAP_ALLOC
+#include <stdlib.h>
+#include <crtdbg.h>
+#endif
+#endif /* qh_DEFuser */
+
+
+
diff --git a/xs/src/qhull/src/libqhull/usermem.c b/xs/src/qhull/src/libqhull/usermem.c
new file mode 100644
index 000000000..0e99e8f66
--- /dev/null
+++ b/xs/src/qhull/src/libqhull/usermem.c
@@ -0,0 +1,94 @@
+/*<html><pre> -<a href="qh-user.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ usermem.c
+ qh_exit(), qh_free(), and qh_malloc()
+
+ See README.txt.
+
+ If you redefine one of these functions you must redefine all of them.
+ If you recompile and load this file, then usermem.o will not be loaded
+ from qhull.a or qhull.lib
+
+ See libqhull.h for data structures, macros, and user-callable functions.
+ See user.c for qhull-related, redefinable functions
+ see user.h for user-definable constants
+ See userprintf.c for qh_fprintf and userprintf_rbox.c for qh_fprintf_rbox
+
+ Please report any errors that you fix to qhull@qhull.org
+*/
+
+#include "libqhull.h"
+
+#include <stdarg.h>
+#include <stdlib.h>
+
+/*-<a href="qh-user.htm#TOC"
+ >-------------------------------</a><a name="qh_exit">-</a>
+
+ qh_exit( exitcode )
+ exit program
+
+ notes:
+ qh_exit() is called when qh_errexit() and longjmp() are not available.
+
+ This is the only use of exit() in Qhull
+ To replace qh_exit with 'throw', see libqhullcpp/usermem_r-cpp.cpp
+*/
+void qh_exit(int exitcode) {
+ exit(exitcode);
+} /* exit */
+
+/*-<a href="qh-user.htm#TOC"
+ >-------------------------------</a><a name="qh_fprintf_stderr">-</a>
+
+ qh_fprintf_stderr( msgcode, format, list of args )
+ fprintf to stderr with msgcode (non-zero)
+
+ notes:
+ qh_fprintf_stderr() is called when qh.ferr is not defined, usually due to an initialization error
+
+ It is typically followed by qh_errexit().
+
+ Redefine this function to avoid using stderr
+
+ Use qh_fprintf [userprintf.c] for normal printing
+*/
+void qh_fprintf_stderr(int msgcode, const char *fmt, ... ) {
+ va_list args;
+
+ va_start(args, fmt);
+ if(msgcode)
+ fprintf(stderr, "QH%.4d ", msgcode);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+} /* fprintf_stderr */
+
+/*-<a href="qh-user.htm#TOC"
+>-------------------------------</a><a name="qh_free">-</a>
+
+ qh_free( mem )
+ free memory
+
+ notes:
+ same as free()
+ No calls to qh_errexit()
+*/
+void qh_free(void *mem) {
+ free(mem);
+} /* free */
+
+/*-<a href="qh-user.htm#TOC"
+ >-------------------------------</a><a name="qh_malloc">-</a>
+
+ qh_malloc( mem )
+ allocate memory
+
+ notes:
+ same as malloc()
+*/
+void *qh_malloc(size_t size) {
+ return malloc(size);
+} /* malloc */
+
+
diff --git a/xs/src/qhull/src/libqhull/userprintf.c b/xs/src/qhull/src/libqhull/userprintf.c
new file mode 100644
index 000000000..190d7cd79
--- /dev/null
+++ b/xs/src/qhull/src/libqhull/userprintf.c
@@ -0,0 +1,66 @@
+/*<html><pre> -<a href="qh-user.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ userprintf.c
+ qh_fprintf()
+
+ see README.txt see COPYING.txt for copyright information.
+
+ If you recompile and load this file, then userprintf.o will not be loaded
+ from qhull.a or qhull.lib
+
+ See libqhull.h for data structures, macros, and user-callable functions.
+ See user.c for qhull-related, redefinable functions
+ see user.h for user-definable constants
+ See usermem.c for qh_exit(), qh_free(), and qh_malloc()
+ see Qhull.cpp and RboxPoints.cpp for examples.
+
+ Please report any errors that you fix to qhull@qhull.org
+*/
+
+#include "libqhull.h"
+#include "mem.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/*-<a href="qh-user.htm#TOC"
+ >-------------------------------</a><a name="qh_fprintf">-</a>
+
+ qh_fprintf(fp, msgcode, format, list of args )
+ print arguments to *fp according to format
+ Use qh_fprintf_rbox() for rboxlib.c
+
+ notes:
+ same as fprintf()
+ fgets() is not trapped like fprintf()
+ exit qh_fprintf via qh_errexit()
+ may be called for errors in qh_initstatistics and qh_meminit
+*/
+
+void qh_fprintf(FILE *fp, int msgcode, const char *fmt, ... ) {
+ va_list args;
+
+ if (!fp) {
+ /* could use qhmem.ferr, but probably better to be cautious */
+ qh_fprintf_stderr(6232, "Qhull internal error (userprintf.c): fp is 0. Wrong qh_fprintf called.\n");
+ qh_errexit(6232, NULL, NULL);
+ }
+ va_start(args, fmt);
+#if qh_QHpointer
+ if (qh_qh && qh ANNOTATEoutput) {
+#else
+ if (qh ANNOTATEoutput) {
+#endif
+ fprintf(fp, "[QH%.4d]", msgcode);
+ }else if (msgcode >= MSG_ERROR && msgcode < MSG_STDERR ) {
+ fprintf(fp, "QH%.4d ", msgcode);
+ }
+ vfprintf(fp, fmt, args);
+ va_end(args);
+
+ /* Place debugging traps here. Use with option 'Tn' */
+
+} /* qh_fprintf */
+
diff --git a/xs/src/qhull/src/libqhull/userprintf_rbox.c b/xs/src/qhull/src/libqhull/userprintf_rbox.c
new file mode 100644
index 000000000..8edd2001a
--- /dev/null
+++ b/xs/src/qhull/src/libqhull/userprintf_rbox.c
@@ -0,0 +1,53 @@
+/*<html><pre> -<a href="qh-user.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ userprintf_rbox.c
+ qh_fprintf_rbox()
+
+ see README.txt see COPYING.txt for copyright information.
+
+ If you recompile and load this file, then userprintf_rbox.o will not be loaded
+ from qhull.a or qhull.lib
+
+ See libqhull.h for data structures, macros, and user-callable functions.
+ See user.c for qhull-related, redefinable functions
+ see user.h for user-definable constants
+ See usermem.c for qh_exit(), qh_free(), and qh_malloc()
+ see Qhull.cpp and RboxPoints.cpp for examples.
+
+ Please report any errors that you fix to qhull@qhull.org
+*/
+
+#include "libqhull.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/*-<a href="qh-user.htm#TOC"
+ >-------------------------------</a><a name="qh_fprintf_rbox">-</a>
+
+ qh_fprintf_rbox(fp, msgcode, format, list of args )
+ print arguments to *fp according to format
+ Use qh_fprintf_rbox() for rboxlib.c
+
+ notes:
+ same as fprintf()
+ fgets() is not trapped like fprintf()
+ exit qh_fprintf_rbox via qh_errexit_rbox()
+*/
+
+void qh_fprintf_rbox(FILE *fp, int msgcode, const char *fmt, ... ) {
+ va_list args;
+
+ if (!fp) {
+ qh_fprintf_stderr(6231, "Qhull internal error (userprintf_rbox.c): fp is 0. Wrong qh_fprintf_rbox called.\n");
+ qh_errexit_rbox(6231);
+ }
+ if (msgcode >= MSG_ERROR && msgcode < MSG_STDERR)
+ fprintf(fp, "QH%.4d ", msgcode);
+ va_start(args, fmt);
+ vfprintf(fp, fmt, args);
+ va_end(args);
+} /* qh_fprintf_rbox */
+
diff --git a/xs/src/qhull/src/libqhull_r/Makefile b/xs/src/qhull/src/libqhull_r/Makefile
new file mode 100644
index 000000000..5c40969e0
--- /dev/null
+++ b/xs/src/qhull/src/libqhull_r/Makefile
@@ -0,0 +1,240 @@
+# Simple gcc Makefile for reentrant qhull and rbox (default gcc/g++)
+#
+# make help
+# See README.txt and ../../Makefile
+#
+# Variables
+# BINDIR directory where to copy executables
+# DESTDIR destination directory for 'make install'
+# DOCDIR directory where to copy html documentation
+# INCDIR directory where to copy headers
+# LIBDIR directory where to copy libraries
+# MANDIR directory where to copy manual pages
+# PRINTMAN command for printing manual pages
+# PRINTC command for printing C files
+# CC ANSI C or C++ compiler
+# CC_OPTS1 options used to compile .c files
+# CC_OPTS2 options used to link .o files
+# CC_OPTS3 options to build shared libraries
+#
+# LIBQHULL_OBJS .o files for linking
+# LIBQHULL_HDRS .h files for printing
+# CFILES .c files for printing
+# DOCFILES documentation files
+# FILES miscellaneous files for printing
+# TFILES .txt versions of html files
+# FILES all other files
+# LIBQHULL_OBJS specifies the object files of libqhullstatic_r.a
+#
+# Results
+# rbox Generates points sets for qhull, qconvex, etc.
+# qhull Computes convex hulls and related structures
+# qconvex, qdelaunay, qhalf, qvoronoi
+# Specializations of qhull for each geometric structure
+# libqhullstatic_r.a Static library for reentrant qhull
+# testqset_r Standalone test of reentrant qset_r.c with mem_r.c
+# user_eg An example of using qhull (reentrant)
+# user_eg2 An example of using qhull (reentrant)
+#
+# Make targets
+# make Build results using gcc or another compiler
+# make clean Remove object files
+# make cleanall Remove generated files
+# make doc Print documentation
+# make help
+# make install Copy qhull, rbox, qhull.1, rbox.1 to BINDIR, MANDIR
+# make new Rebuild qhull and rbox from source
+# make printall Print all files
+# make qtest Quick test of qset, rbox, and qhull
+# make test Quck test of qhull, qconvex, etc.
+#
+# Do not replace tabs with spaces. Needed for build rules
+# Unix line endings (\n)
+# $Id: //main/2015/qhull/src/libqhull_r/Makefile#6 $
+
+DESTDIR = /usr/local
+BINDIR = $(DESTDIR)/bin
+INCDIR = $(DESTDIR)/include
+LIBDIR = $(DESTDIR)/lib
+DOCDIR = $(DESTDIR)/share/doc/qhull
+MANDIR = $(DESTDIR)/share/man/man1
+
+# if you do not have enscript, try a2ps or just use lpr. The files are text.
+PRINTMAN = enscript -2rl
+PRINTC = enscript -2r
+# PRINTMAN = lpr
+# PRINTC = lpr
+
+#for Gnu's gcc compiler, -O3 for optimization, -g for debugging, -pg for profiling
+# -fpic needed for gcc x86_64-linux-gnu. Not needed for mingw
+CC = gcc
+CC_OPTS1 = -O3 -ansi -I../../src -fpic $(CC_WARNINGS)
+
+# for Sun's cc compiler, -fast or O2 for optimization, -g for debugging, -Xc for ANSI
+#CC = cc
+#CC_OPTS1 = -Xc -v -fast -I../../src
+
+# for Silicon Graphics cc compiler, -O2 for optimization, -g for debugging
+#CC = cc
+#CC_OPTS1 = -ansi -O2 -I../../src
+
+# for Next cc compiler with fat executable
+#CC = cc
+#CC_OPTS1 = -ansi -O2 -I../../src -arch m68k -arch i386 -arch hppa
+
+# For loader, ld,
+CC_OPTS2 = $(CC_OPTS1)
+
+# Default targets for make
+
+all: qhull_links qhull_all qtest
+
+help:
+ head -n 50 Makefile
+
+clean:
+ rm -f *.o
+ # Delete linked files from other directories [qhull_links]
+ rm -f qconvex_r.c unix_r.c qdelaun_r.c qhalf_r.c qvoronoi_r.c rbox_r.c
+ rm -f user_eg_r.c user_eg2_r.c testqset_r.c
+
+cleanall: clean
+ rm -f qconvex qdelaunay qhalf qvoronoi qhull *.exe
+ rm -f core user_eg_r user_eg2_r testqset_r libqhullstatic_r.a
+
+doc:
+ $(PRINTMAN) $(TXTFILES) $(DOCFILES)
+
+install:
+ mkdir -p $(BINDIR)
+ mkdir -p $(DOCDIR)
+ mkdir -p $(INCDIR)/libqhull
+ mkdir -p $(MANDIR)
+ cp -p qconvex qdelaunay qhalf qhull qvoronoi rbox $(BINDIR)
+ cp -p libqhullstatic_r.a $(LIBDIR)
+ cp -p ../../html/qhull.man $(MANDIR)/qhull.1
+ cp -p ../../html/rbox.man $(MANDIR)/rbox.1
+ cp -p ../../html/* $(DOCDIR)
+ cp *.h $(INCDIR)/libqhull_r
+
+new: cleanall all
+
+printall: doc printh printc printf
+
+printh:
+ $(PRINTC) $(LIBQHULL_HDRS)
+
+printc:
+ $(PRINTC) $(CFILES)
+
+# LIBQHULL_OBJS_1 ordered by frequency of execution with small files at end. Better locality.
+# Same definitions as ../../Makefile
+
+LIBQHULLS_OBJS_1= global_r.o stat_r.o geom2_r.o poly2_r.o merge_r.o \
+ libqhull_r.o geom_r.o poly_r.o qset_r.o mem_r.o random_r.o
+
+LIBQHULLS_OBJS_2= $(LIBQHULLS_OBJS_1) usermem_r.o userprintf_r.o io_r.o user_r.o
+
+LIBQHULLS_OBJS= $(LIBQHULLS_OBJS_2) rboxlib_r.o userprintf_rbox_r.o
+
+LIBQHULL_HDRS= user_r.h libqhull_r.h qhull_ra.h geom_r.h \
+ io_r.h mem_r.h merge_r.h poly_r.h random_r.h \
+ qset_r.h stat_r.h
+
+# CFILES ordered alphabetically after libqhull.c
+CFILES= ../qhull/unix_r.c libqhull_r.c geom_r.c geom2_r.c global_r.c io_r.c \
+ mem_r.c merge_r.c poly_r.c poly2_r.c random_r.c rboxlib_r.c \
+ qset_r.c stat_r.c user_r.c usermem_r.c userprintf_r.c \
+ ../qconvex/qconvex.c ../qdelaunay/qdelaun.c ../qhalf/qhalf.c ../qvoronoi/qvoronoi.c
+
+TXTFILES= ../../Announce.txt ../../REGISTER.txt ../../COPYING.txt ../../README.txt ../Changes.txt
+DOCFILES= ../../html/rbox.txt ../../html/qhull.txt
+
+.c.o:
+ $(CC) -c $(CC_OPTS1) -o $@ $<
+
+# Work around problems with ../ in Red Hat Linux
+qhull_links:
+ # On MINSYS, 'ln -s' may create a copy instead of a symbolic link
+ [ -f qconvex_r.c ] || ln -s ../qconvex/qconvex_r.c
+ [ -f qdelaun_r.c ] || ln -s ../qdelaunay/qdelaun_r.c
+ [ -f qhalf_r.c ] || ln -s ../qhalf/qhalf_r.c
+ [ -f qvoronoi_r.c ] || ln -s ../qvoronoi/qvoronoi_r.c
+ [ -f rbox_r.c ] || ln -s ../rbox/rbox_r.c
+ [ -f testqset_r.c ] || ln -s ../testqset_r/testqset_r.c
+ [ -f unix_r.c ] || ln -s ../qhull/unix_r.c
+ [ -f user_eg_r.c ] || ln -s ../user_eg/user_eg_r.c
+ [ -f user_eg2_r.c ] || ln -s ../user_eg2/user_eg2_r.c
+
+# compile qhull without using bin/libqhullstatic_r.a
+qhull_all: qconvex_r.o qdelaun_r.o qhalf_r.o qvoronoi_r.o unix_r.o user_eg_r.o user_eg2_r.o rbox_r.o testqset_r.o $(LIBQHULLS_OBJS)
+ $(CC) -o qconvex $(CC_OPTS2) -lm $(LIBQHULLS_OBJS_2) qconvex_r.o
+ $(CC) -o qdelaunay $(CC_OPTS2) -lm $(LIBQHULLS_OBJS_2) qdelaun_r.o
+ $(CC) -o qhalf $(CC_OPTS2) -lm $(LIBQHULLS_OBJS_2) qhalf_r.o
+ $(CC) -o qvoronoi $(CC_OPTS2) -lm $(LIBQHULLS_OBJS_2) qvoronoi_r.o
+ $(CC) -o qhull $(CC_OPTS2) -lm $(LIBQHULLS_OBJS_2) unix_r.o
+ $(CC) -o rbox $(CC_OPTS2) -lm $(LIBQHULLS_OBJS) rbox_r.o
+ $(CC) -o user_eg $(CC_OPTS2) -lm $(LIBQHULLS_OBJS_2) user_eg_r.o
+ $(CC) -o user_eg2 $(CC_OPTS2) -lm $(LIBQHULLS_OBJS_1) user_eg2_r.o usermem_r.o userprintf_r.o io_r.o
+ $(CC) -o testqset_r $(CC_OPTS2) -lm mem_r.o qset_r.o usermem_r.o testqset_r.o
+ -ar -rs libqhullstatic_r.a $(LIBQHULLS_OBJS)
+ #libqhullstatic_r.a is not needed for qhull
+ #If 'ar -rs' fails try using 'ar -s' with 'ranlib'
+ #ranlib libqhullstatic_r.a
+
+qtest:
+ @echo ============================================
+ @echo == make qtest ==============================
+ @echo ============================================
+ @echo -n "== "
+ @date
+ @echo
+ @echo Testing qset.c and mem.c with testqset
+ ./testqset_r 10000
+ @echo Run the qhull smoketest
+ ./rbox D4 | ./qhull
+ @echo ============================================
+ @echo == To smoketest qhull programs
+ @echo '== make test'
+ @echo ============================================
+ @echo
+ @echo ============================================
+ @echo == For all make targets
+ @echo '== make help'
+ @echo ============================================
+ @echo
+
+test: qtest
+ @echo ==============================
+ @echo ========= qconvex ============
+ @echo ==============================
+ -./rbox 10 | ./qconvex Tv
+ @echo
+ @echo ==============================
+ @echo ========= qdelaunay ==========
+ @echo ==============================
+ -./rbox 10 | ./qdelaunay Tv
+ @echo
+ @echo ==============================
+ @echo ========= qhalf ==============
+ @echo ==============================
+ -./rbox 10 | ./qconvex FQ FV n Tv | ./qhalf Tv
+ @echo
+ @echo ==============================
+ @echo ========= qvoronoi ===========
+ @echo ==============================
+ -./rbox 10 | ./qvoronoi Tv
+ @echo
+ @echo ==============================
+ @echo ========= user_eg ============
+ @echo == w/o shared library ========
+ @echo ==============================
+ -./user_eg
+ @echo
+ @echo ==============================
+ @echo ========= user_eg2 ===========
+ @echo ==============================
+ -./user_eg2
+ @echo
+
+# end of Makefile
diff --git a/xs/src/qhull/src/libqhull_r/geom2_r.c b/xs/src/qhull/src/libqhull_r/geom2_r.c
new file mode 100644
index 000000000..48addba1c
--- /dev/null
+++ b/xs/src/qhull/src/libqhull_r/geom2_r.c
@@ -0,0 +1,2096 @@
+/*<html><pre> -<a href="qh-geom_r.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+
+ geom2_r.c
+ infrequently used geometric routines of qhull
+
+ see qh-geom_r.htm and geom_r.h
+
+ Copyright (c) 1993-2015 The Geometry Center.
+ $Id: //main/2015/qhull/src/libqhull_r/geom2_r.c#6 $$Change: 2065 $
+ $DateTime: 2016/01/18 13:51:04 $$Author: bbarber $
+
+ frequently used code goes into geom_r.c
+*/
+
+#include "qhull_ra.h"
+
+/*================== functions in alphabetic order ============*/
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="copypoints">-</a>
+
+ qh_copypoints(qh, points, numpoints, dimension)
+ return qh_malloc'd copy of points
+
+ notes:
+ qh_free the returned points to avoid a memory leak
+*/
+coordT *qh_copypoints(qhT *qh, coordT *points, int numpoints, int dimension) {
+ int size;
+ coordT *newpoints;
+
+ size= numpoints * dimension * (int)sizeof(coordT);
+ if (!(newpoints= (coordT*)qh_malloc((size_t)size))) {
+ qh_fprintf(qh, qh->ferr, 6004, "qhull error: insufficient memory to copy %d points\n",
+ numpoints);
+ qh_errexit(qh, qh_ERRmem, NULL, NULL);
+ }
+ memcpy((char *)newpoints, (char *)points, (size_t)size); /* newpoints!=0 by QH6004 */
+ return newpoints;
+} /* copypoints */
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="crossproduct">-</a>
+
+ qh_crossproduct( dim, vecA, vecB, vecC )
+ crossproduct of 2 dim vectors
+ C= A x B
+
+ notes:
+ from Glasner, Graphics Gems I, p. 639
+ only defined for dim==3
+*/
+void qh_crossproduct(int dim, realT vecA[3], realT vecB[3], realT vecC[3]){
+
+ if (dim == 3) {
+ vecC[0]= det2_(vecA[1], vecA[2],
+ vecB[1], vecB[2]);
+ vecC[1]= - det2_(vecA[0], vecA[2],
+ vecB[0], vecB[2]);
+ vecC[2]= det2_(vecA[0], vecA[1],
+ vecB[0], vecB[1]);
+ }
+} /* vcross */
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="determinant">-</a>
+
+ qh_determinant(qh, rows, dim, nearzero )
+ compute signed determinant of a square matrix
+ uses qh.NEARzero to test for degenerate matrices
+
+ returns:
+ determinant
+ overwrites rows and the matrix
+ if dim == 2 or 3
+ nearzero iff determinant < qh->NEARzero[dim-1]
+ (!quite correct, not critical)
+ if dim >= 4
+ nearzero iff diagonal[k] < qh->NEARzero[k]
+*/
+realT qh_determinant(qhT *qh, realT **rows, int dim, boolT *nearzero) {
+ realT det=0;
+ int i;
+ boolT sign= False;
+
+ *nearzero= False;
+ if (dim < 2) {
+ qh_fprintf(qh, qh->ferr, 6005, "qhull internal error (qh_determinate): only implemented for dimension >= 2\n");
+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+ }else if (dim == 2) {
+ det= det2_(rows[0][0], rows[0][1],
+ rows[1][0], rows[1][1]);
+ if (fabs_(det) < 10*qh->NEARzero[1]) /* not really correct, what should this be? */
+ *nearzero= True;
+ }else if (dim == 3) {
+ det= det3_(rows[0][0], rows[0][1], rows[0][2],
+ rows[1][0], rows[1][1], rows[1][2],
+ rows[2][0], rows[2][1], rows[2][2]);
+ if (fabs_(det) < 10*qh->NEARzero[2]) /* what should this be? det 5.5e-12 was flat for qh_maxsimplex of qdelaunay 0,0 27,27 -36,36 -9,63 */
+ *nearzero= True;
+ }else {
+ qh_gausselim(qh, rows, dim, dim, &sign, nearzero); /* if nearzero, diagonal still ok*/
+ det= 1.0;
+ for (i=dim; i--; )
+ det *= (rows[i])[i];
+ if (sign)
+ det= -det;
+ }
+ return det;
+} /* determinant */
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="detjoggle">-</a>
+
+ qh_detjoggle(qh, points, numpoints, dimension )
+ determine default max joggle for point array
+ as qh_distround * qh_JOGGLEdefault
+
+ returns:
+ initial value for JOGGLEmax from points and REALepsilon
+
+ notes:
+ computes DISTround since qh_maxmin not called yet
+ if qh->SCALElast, last dimension will be scaled later to MAXwidth
+
+ loop duplicated from qh_maxmin
+*/
+realT qh_detjoggle(qhT *qh, pointT *points, int numpoints, int dimension) {
+ realT abscoord, distround, joggle, maxcoord, mincoord;
+ pointT *point, *pointtemp;
+ realT maxabs= -REALmax;
+ realT sumabs= 0;
+ realT maxwidth= 0;
+ int k;
+
+ for (k=0; k < dimension; k++) {
+ if (qh->SCALElast && k == dimension-1)
+ abscoord= maxwidth;
+ else if (qh->DELAUNAY && k == dimension-1) /* will qh_setdelaunay() */
+ abscoord= 2 * maxabs * maxabs; /* may be low by qh->hull_dim/2 */
+ else {
+ maxcoord= -REALmax;
+ mincoord= REALmax;
+ FORALLpoint_(qh, points, numpoints) {
+ maximize_(maxcoord, point[k]);
+ minimize_(mincoord, point[k]);
+ }
+ maximize_(maxwidth, maxcoord-mincoord);
+ abscoord= fmax_(maxcoord, -mincoord);
+ }
+ sumabs += abscoord;
+ maximize_(maxabs, abscoord);
+ } /* for k */
+ distround= qh_distround(qh, qh->hull_dim, maxabs, sumabs);
+ joggle= distround * qh_JOGGLEdefault;
+ maximize_(joggle, REALepsilon * qh_JOGGLEdefault);
+ trace2((qh, qh->ferr, 2001, "qh_detjoggle: joggle=%2.2g maxwidth=%2.2g\n", joggle, maxwidth));
+ return joggle;
+} /* detjoggle */
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="detroundoff">-</a>
+
+ qh_detroundoff(qh)
+ determine maximum roundoff errors from
+ REALepsilon, REALmax, REALmin, qh.hull_dim, qh.MAXabs_coord,
+ qh.MAXsumcoord, qh.MAXwidth, qh.MINdenom_1
+
+ accounts for qh.SETroundoff, qh.RANDOMdist, qh->MERGEexact
+ qh.premerge_cos, qh.postmerge_cos, qh.premerge_centrum,
+ qh.postmerge_centrum, qh.MINoutside,
+ qh_RATIOnearinside, qh_COPLANARratio, qh_WIDEcoplanar
+
+ returns:
+ sets qh.DISTround, etc. (see below)
+ appends precision constants to qh.qhull_options
+
+ see:
+ qh_maxmin() for qh.NEARzero
+
+ design:
+ determine qh.DISTround for distance computations
+ determine minimum denominators for qh_divzero
+ determine qh.ANGLEround for angle computations
+ adjust qh.premerge_cos,... for roundoff error
+ determine qh.ONEmerge for maximum error due to a single merge
+ determine qh.NEARinside, qh.MAXcoplanar, qh.MINvisible,
+ qh.MINoutside, qh.WIDEfacet
+ initialize qh.max_vertex and qh.minvertex
+*/
+void qh_detroundoff(qhT *qh) {
+
+ qh_option(qh, "_max-width", NULL, &qh->MAXwidth);
+ if (!qh->SETroundoff) {
+ qh->DISTround= qh_distround(qh, qh->hull_dim, qh->MAXabs_coord, qh->MAXsumcoord);
+ if (qh->RANDOMdist)
+ qh->DISTround += qh->RANDOMfactor * qh->MAXabs_coord;
+ qh_option(qh, "Error-roundoff", NULL, &qh->DISTround);
+ }
+ qh->MINdenom= qh->MINdenom_1 * qh->MAXabs_coord;
+ qh->MINdenom_1_2= sqrt(qh->MINdenom_1 * qh->hull_dim) ; /* if will be normalized */
+ qh->MINdenom_2= qh->MINdenom_1_2 * qh->MAXabs_coord;
+ /* for inner product */
+ qh->ANGLEround= 1.01 * qh->hull_dim * REALepsilon;
+ if (qh->RANDOMdist)
+ qh->ANGLEround += qh->RANDOMfactor;
+ if (qh->premerge_cos < REALmax/2) {
+ qh->premerge_cos -= qh->ANGLEround;
+ if (qh->RANDOMdist)
+ qh_option(qh, "Angle-premerge-with-random", NULL, &qh->premerge_cos);
+ }
+ if (qh->postmerge_cos < REALmax/2) {
+ qh->postmerge_cos -= qh->ANGLEround;
+ if (qh->RANDOMdist)
+ qh_option(qh, "Angle-postmerge-with-random", NULL, &qh->postmerge_cos);
+ }
+ qh->premerge_centrum += 2 * qh->DISTround; /*2 for centrum and distplane()*/
+ qh->postmerge_centrum += 2 * qh->DISTround;
+ if (qh->RANDOMdist && (qh->MERGEexact || qh->PREmerge))
+ qh_option(qh, "Centrum-premerge-with-random", NULL, &qh->premerge_centrum);
+ if (qh->RANDOMdist && qh->POSTmerge)
+ qh_option(qh, "Centrum-postmerge-with-random", NULL, &qh->postmerge_centrum);
+ { /* compute ONEmerge, max vertex offset for merging simplicial facets */
+ realT maxangle= 1.0, maxrho;
+
+ minimize_(maxangle, qh->premerge_cos);
+ minimize_(maxangle, qh->postmerge_cos);
+ /* max diameter * sin theta + DISTround for vertex to its hyperplane */
+ qh->ONEmerge= sqrt((realT)qh->hull_dim) * qh->MAXwidth *
+ sqrt(1.0 - maxangle * maxangle) + qh->DISTround;
+ maxrho= qh->hull_dim * qh->premerge_centrum + qh->DISTround;
+ maximize_(qh->ONEmerge, maxrho);
+ maxrho= qh->hull_dim * qh->postmerge_centrum + qh->DISTround;
+ maximize_(qh->ONEmerge, maxrho);
+ if (qh->MERGING)
+ qh_option(qh, "_one-merge", NULL, &qh->ONEmerge);
+ }
+ qh->NEARinside= qh->ONEmerge * qh_RATIOnearinside; /* only used if qh->KEEPnearinside */
+ if (qh->JOGGLEmax < REALmax/2 && (qh->KEEPcoplanar || qh->KEEPinside)) {
+ realT maxdist; /* adjust qh.NEARinside for joggle */
+ qh->KEEPnearinside= True;
+ maxdist= sqrt((realT)qh->hull_dim) * qh->JOGGLEmax + qh->DISTround;
+ maxdist= 2*maxdist; /* vertex and coplanar point can joggle in opposite directions */
+ maximize_(qh->NEARinside, maxdist); /* must agree with qh_nearcoplanar() */
+ }
+ if (qh->KEEPnearinside)
+ qh_option(qh, "_near-inside", NULL, &qh->NEARinside);
+ if (qh->JOGGLEmax < qh->DISTround) {
+ qh_fprintf(qh, qh->ferr, 6006, "qhull error: the joggle for 'QJn', %.2g, is below roundoff for distance computations, %.2g\n",
+ qh->JOGGLEmax, qh->DISTround);
+ qh_errexit(qh, qh_ERRinput, NULL, NULL);
+ }
+ if (qh->MINvisible > REALmax/2) {
+ if (!qh->MERGING)
+ qh->MINvisible= qh->DISTround;
+ else if (qh->hull_dim <= 3)
+ qh->MINvisible= qh->premerge_centrum;
+ else
+ qh->MINvisible= qh_COPLANARratio * qh->premerge_centrum;
+ if (qh->APPROXhull && qh->MINvisible > qh->MINoutside)
+ qh->MINvisible= qh->MINoutside;
+ qh_option(qh, "Visible-distance", NULL, &qh->MINvisible);
+ }
+ if (qh->MAXcoplanar > REALmax/2) {
+ qh->MAXcoplanar= qh->MINvisible;
+ qh_option(qh, "U-coplanar-distance", NULL, &qh->MAXcoplanar);
+ }
+ if (!qh->APPROXhull) { /* user may specify qh->MINoutside */
+ qh->MINoutside= 2 * qh->MINvisible;
+ if (qh->premerge_cos < REALmax/2)
+ maximize_(qh->MINoutside, (1- qh->premerge_cos) * qh->MAXabs_coord);
+ qh_option(qh, "Width-outside", NULL, &qh->MINoutside);
+ }
+ qh->WIDEfacet= qh->MINoutside;
+ maximize_(qh->WIDEfacet, qh_WIDEcoplanar * qh->MAXcoplanar);
+ maximize_(qh->WIDEfacet, qh_WIDEcoplanar * qh->MINvisible);
+ qh_option(qh, "_wide-facet", NULL, &qh->WIDEfacet);
+ if (qh->MINvisible > qh->MINoutside + 3 * REALepsilon
+ && !qh->BESToutside && !qh->FORCEoutput)
+ qh_fprintf(qh, qh->ferr, 7001, "qhull input warning: minimum visibility V%.2g is greater than \nminimum outside W%.2g. Flipped facets are likely.\n",
+ qh->MINvisible, qh->MINoutside);
+ qh->max_vertex= qh->DISTround;
+ qh->min_vertex= -qh->DISTround;
+ /* numeric constants reported in printsummary */
+} /* detroundoff */
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="detsimplex">-</a>
+
+ qh_detsimplex(qh, apex, points, dim, nearzero )
+ compute determinant of a simplex with point apex and base points
+
+ returns:
+ signed determinant and nearzero from qh_determinant
+
+ notes:
+ uses qh.gm_matrix/qh.gm_row (assumes they're big enough)
+
+ design:
+ construct qm_matrix by subtracting apex from points
+ compute determinate
+*/
+realT qh_detsimplex(qhT *qh, pointT *apex, setT *points, int dim, boolT *nearzero) {
+ pointT *coorda, *coordp, *gmcoord, *point, **pointp;
+ coordT **rows;
+ int k, i=0;
+ realT det;
+
+ zinc_(Zdetsimplex);
+ gmcoord= qh->gm_matrix;
+ rows= qh->gm_row;
+ FOREACHpoint_(points) {
+ if (i == dim)
+ break;
+ rows[i++]= gmcoord;
+ coordp= point;
+ coorda= apex;
+ for (k=dim; k--; )
+ *(gmcoord++)= *coordp++ - *coorda++;
+ }
+ if (i < dim) {
+ qh_fprintf(qh, qh->ferr, 6007, "qhull internal error (qh_detsimplex): #points %d < dimension %d\n",
+ i, dim);
+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+ }
+ det= qh_determinant(qh, rows, dim, nearzero);
+ trace2((qh, qh->ferr, 2002, "qh_detsimplex: det=%2.2g for point p%d, dim %d, nearzero? %d\n",
+ det, qh_pointid(qh, apex), dim, *nearzero));
+ return det;
+} /* detsimplex */
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="distnorm">-</a>
+
+ qh_distnorm( dim, point, normal, offset )
+ return distance from point to hyperplane at normal/offset
+
+ returns:
+ dist
+
+ notes:
+ dist > 0 if point is outside of hyperplane
+
+ see:
+ qh_distplane in geom_r.c
+*/
+realT qh_distnorm(int dim, pointT *point, pointT *normal, realT *offsetp) {
+ coordT *normalp= normal, *coordp= point;
+ realT dist;
+ int k;
+
+ dist= *offsetp;
+ for (k=dim; k--; )
+ dist += *(coordp++) * *(normalp++);
+ return dist;
+} /* distnorm */
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="distround">-</a>
+
+ qh_distround(qh, dimension, maxabs, maxsumabs )
+ compute maximum round-off error for a distance computation
+ to a normalized hyperplane
+ maxabs is the maximum absolute value of a coordinate
+ maxsumabs is the maximum possible sum of absolute coordinate values
+
+ returns:
+ max dist round for REALepsilon
+
+ notes:
+ calculate roundoff error according to Golub & van Loan, 1983, Lemma 3.2-1, "Rounding Errors"
+ use sqrt(dim) since one vector is normalized
+ or use maxsumabs since one vector is < 1
+*/
+realT qh_distround(qhT *qh, int dimension, realT maxabs, realT maxsumabs) {
+ realT maxdistsum, maxround;
+
+ maxdistsum= sqrt((realT)dimension) * maxabs;
+ minimize_( maxdistsum, maxsumabs);
+ maxround= REALepsilon * (dimension * maxdistsum * 1.01 + maxabs);
+ /* adds maxabs for offset */
+ trace4((qh, qh->ferr, 4008, "qh_distround: %2.2g maxabs %2.2g maxsumabs %2.2g maxdistsum %2.2g\n",
+ maxround, maxabs, maxsumabs, maxdistsum));
+ return maxround;
+} /* distround */
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="divzero">-</a>
+
+ qh_divzero( numer, denom, mindenom1, zerodiv )
+ divide by a number that's nearly zero
+ mindenom1= minimum denominator for dividing into 1.0
+
+ returns:
+ quotient
+ sets zerodiv and returns 0.0 if it would overflow
+
+ design:
+ if numer is nearly zero and abs(numer) < abs(denom)
+ return numer/denom
+ else if numer is nearly zero
+ return 0 and zerodiv
+ else if denom/numer non-zero
+ return numer/denom
+ else
+ return 0 and zerodiv
+*/
+realT qh_divzero(realT numer, realT denom, realT mindenom1, boolT *zerodiv) {
+ realT temp, numerx, denomx;
+
+
+ if (numer < mindenom1 && numer > -mindenom1) {
+ numerx= fabs_(numer);
+ denomx= fabs_(denom);
+ if (numerx < denomx) {
+ *zerodiv= False;
+ return numer/denom;
+ }else {
+ *zerodiv= True;
+ return 0.0;
+ }
+ }
+ temp= denom/numer;
+ if (temp > mindenom1 || temp < -mindenom1) {
+ *zerodiv= False;
+ return numer/denom;
+ }else {
+ *zerodiv= True;
+ return 0.0;
+ }
+} /* divzero */
+
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="facetarea">-</a>
+
+ qh_facetarea(qh, facet )
+ return area for a facet
+
+ notes:
+ if non-simplicial,
+ uses centrum to triangulate facet and sums the projected areas.
+ if (qh->DELAUNAY),
+ computes projected area instead for last coordinate
+ assumes facet->normal exists
+ projecting tricoplanar facets to the hyperplane does not appear to make a difference
+
+ design:
+ if simplicial
+ compute area
+ else
+ for each ridge
+ compute area from centrum to ridge
+ negate area if upper Delaunay facet
+*/
+realT qh_facetarea(qhT *qh, facetT *facet) {
+ vertexT *apex;
+ pointT *centrum;
+ realT area= 0.0;
+ ridgeT *ridge, **ridgep;
+
+ if (facet->simplicial) {
+ apex= SETfirstt_(facet->vertices, vertexT);
+ area= qh_facetarea_simplex(qh, qh->hull_dim, apex->point, facet->vertices,
+ apex, facet->toporient, facet->normal, &facet->offset);
+ }else {
+ if (qh->CENTERtype == qh_AScentrum)
+ centrum= facet->center;
+ else
+ centrum= qh_getcentrum(qh, facet);
+ FOREACHridge_(facet->ridges)
+ area += qh_facetarea_simplex(qh, qh->hull_dim, centrum, ridge->vertices,
+ NULL, (boolT)(ridge->top == facet), facet->normal, &facet->offset);
+ if (qh->CENTERtype != qh_AScentrum)
+ qh_memfree(qh, centrum, qh->normal_size);
+ }
+ if (facet->upperdelaunay && qh->DELAUNAY)
+ area= -area; /* the normal should be [0,...,1] */
+ trace4((qh, qh->ferr, 4009, "qh_facetarea: f%d area %2.2g\n", facet->id, area));
+ return area;
+} /* facetarea */
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="facetarea_simplex">-</a>
+
+ qh_facetarea_simplex(qh, dim, apex, vertices, notvertex, toporient, normal, offset )
+ return area for a simplex defined by
+ an apex, a base of vertices, an orientation, and a unit normal
+ if simplicial or tricoplanar facet,
+ notvertex is defined and it is skipped in vertices
+
+ returns:
+ computes area of simplex projected to plane [normal,offset]
+ returns 0 if vertex too far below plane (qh->WIDEfacet)
+ vertex can't be apex of tricoplanar facet
+
+ notes:
+ if (qh->DELAUNAY),
+ computes projected area instead for last coordinate
+ uses qh->gm_matrix/gm_row and qh->hull_dim
+ helper function for qh_facetarea
+
+ design:
+ if Notvertex
+ translate simplex to apex
+ else
+ project simplex to normal/offset
+ translate simplex to apex
+ if Delaunay
+ set last row/column to 0 with -1 on diagonal
+ else
+ set last row to Normal
+ compute determinate
+ scale and flip sign for area
+*/
+realT qh_facetarea_simplex(qhT *qh, int dim, coordT *apex, setT *vertices,
+ vertexT *notvertex, boolT toporient, coordT *normal, realT *offset) {
+ pointT *coorda, *coordp, *gmcoord;
+ coordT **rows, *normalp;
+ int k, i=0;
+ realT area, dist;
+ vertexT *vertex, **vertexp;
+ boolT nearzero;
+
+ gmcoord= qh->gm_matrix;
+ rows= qh->gm_row;
+ FOREACHvertex_(vertices) {
+ if (vertex == notvertex)
+ continue;
+ rows[i++]= gmcoord;
+ coorda= apex;
+ coordp= vertex->point;
+ normalp= normal;
+ if (notvertex) {
+ for (k=dim; k--; )
+ *(gmcoord++)= *coordp++ - *coorda++;
+ }else {
+ dist= *offset;
+ for (k=dim; k--; )
+ dist += *coordp++ * *normalp++;
+ if (dist < -qh->WIDEfacet) {
+ zinc_(Znoarea);
+ return 0.0;
+ }
+ coordp= vertex->point;
+ normalp= normal;
+ for (k=dim; k--; )
+ *(gmcoord++)= (*coordp++ - dist * *normalp++) - *coorda++;
+ }
+ }
+ if (i != dim-1) {
+ qh_fprintf(qh, qh->ferr, 6008, "qhull internal error (qh_facetarea_simplex): #points %d != dim %d -1\n",
+ i, dim);
+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+ }
+ rows[i]= gmcoord;
+ if (qh->DELAUNAY) {
+ for (i=0; i < dim-1; i++)
+ rows[i][dim-1]= 0.0;
+ for (k=dim; k--; )
+ *(gmcoord++)= 0.0;
+ rows[dim-1][dim-1]= -1.0;
+ }else {
+ normalp= normal;
+ for (k=dim; k--; )
+ *(gmcoord++)= *normalp++;
+ }
+ zinc_(Zdetsimplex);
+ area= qh_determinant(qh, rows, dim, &nearzero);
+ if (toporient)
+ area= -area;
+ area *= qh->AREAfactor;
+ trace4((qh, qh->ferr, 4010, "qh_facetarea_simplex: area=%2.2g for point p%d, toporient %d, nearzero? %d\n",
+ area, qh_pointid(qh, apex), toporient, nearzero));
+ return area;
+} /* facetarea_simplex */
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="facetcenter">-</a>
+
+ qh_facetcenter(qh, vertices )
+ return Voronoi center (Voronoi vertex) for a facet's vertices
+
+ returns:
+ return temporary point equal to the center
+
+ see:
+ qh_voronoi_center()
+*/
+pointT *qh_facetcenter(qhT *qh, setT *vertices) {
+ setT *points= qh_settemp(qh, qh_setsize(qh, vertices));
+ vertexT *vertex, **vertexp;
+ pointT *center;
+
+ FOREACHvertex_(vertices)
+ qh_setappend(qh, &points, vertex->point);
+ center= qh_voronoi_center(qh, qh->hull_dim-1, points);
+ qh_settempfree(qh, &points);
+ return center;
+} /* facetcenter */
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="findgooddist">-</a>
+
+ qh_findgooddist(qh, point, facetA, dist, facetlist )
+ find best good facet visible for point from facetA
+ assumes facetA is visible from point
+
+ returns:
+ best facet, i.e., good facet that is furthest from point
+ distance to best facet
+ NULL if none
+
+ moves good, visible facets (and some other visible facets)
+ to end of qh->facet_list
+
+ notes:
+ uses qh->visit_id
+
+ design:
+ initialize bestfacet if facetA is good
+ move facetA to end of facetlist
+ for each facet on facetlist
+ for each unvisited neighbor of facet
+ move visible neighbors to end of facetlist
+ update best good neighbor
+ if no good neighbors, update best facet
+*/
+facetT *qh_findgooddist(qhT *qh, pointT *point, facetT *facetA, realT *distp,
+ facetT **facetlist) {
+ realT bestdist= -REALmax, dist;
+ facetT *neighbor, **neighborp, *bestfacet=NULL, *facet;
+ boolT goodseen= False;
+
+ if (facetA->good) {
+ zzinc_(Zcheckpart); /* calls from check_bestdist occur after print stats */
+ qh_distplane(qh, point, facetA, &bestdist);
+ bestfacet= facetA;
+ goodseen= True;
+ }
+ qh_removefacet(qh, facetA);
+ qh_appendfacet(qh, facetA);
+ *facetlist= facetA;
+ facetA->visitid= ++qh->visit_id;
+ FORALLfacet_(*facetlist) {
+ FOREACHneighbor_(facet) {
+ if (neighbor->visitid == qh->visit_id)
+ continue;
+ neighbor->visitid= qh->visit_id;
+ if (goodseen && !neighbor->good)
+ continue;
+ zzinc_(Zcheckpart);
+ qh_distplane(qh, point, neighbor, &dist);
+ if (dist > 0) {
+ qh_removefacet(qh, neighbor);
+ qh_appendfacet(qh, neighbor);
+ if (neighbor->good) {
+ goodseen= True;
+ if (dist > bestdist) {
+ bestdist= dist;
+ bestfacet= neighbor;
+ }
+ }
+ }
+ }
+ }
+ if (bestfacet) {
+ *distp= bestdist;
+ trace2((qh, qh->ferr, 2003, "qh_findgooddist: p%d is %2.2g above good facet f%d\n",
+ qh_pointid(qh, point), bestdist, bestfacet->id));
+ return bestfacet;
+ }
+ trace4((qh, qh->ferr, 4011, "qh_findgooddist: no good facet for p%d above f%d\n",
+ qh_pointid(qh, point), facetA->id));
+ return NULL;
+} /* findgooddist */
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="getarea">-</a>
+
+ qh_getarea(qh, facetlist )
+ set area of all facets in facetlist
+ collect statistics
+ nop if hasAreaVolume
+
+ returns:
+ sets qh->totarea/totvol to total area and volume of convex hull
+ for Delaunay triangulation, computes projected area of the lower or upper hull
+ ignores upper hull if qh->ATinfinity
+
+ notes:
+ could compute outer volume by expanding facet area by rays from interior
+ the following attempt at perpendicular projection underestimated badly:
+ qh.totoutvol += (-dist + facet->maxoutside + qh->DISTround)
+ * area/ qh->hull_dim;
+ design:
+ for each facet on facetlist
+ compute facet->area
+ update qh.totarea and qh.totvol
+*/
+void qh_getarea(qhT *qh, facetT *facetlist) {
+ realT area;
+ realT dist;
+ facetT *facet;
+
+ if (qh->hasAreaVolume)
+ return;
+ if (qh->REPORTfreq)
+ qh_fprintf(qh, qh->ferr, 8020, "computing area of each facet and volume of the convex hull\n");
+ else
+ trace1((qh, qh->ferr, 1001, "qh_getarea: computing volume and area for each facet\n"));
+ qh->totarea= qh->totvol= 0.0;
+ FORALLfacet_(facetlist) {
+ if (!facet->normal)
+ continue;
+ if (facet->upperdelaunay && qh->ATinfinity)
+ continue;
+ if (!facet->isarea) {
+ facet->f.area= qh_facetarea(qh, facet);
+ facet->isarea= True;
+ }
+ area= facet->f.area;
+ if (qh->DELAUNAY) {
+ if (facet->upperdelaunay == qh->UPPERdelaunay)
+ qh->totarea += area;
+ }else {
+ qh->totarea += area;
+ qh_distplane(qh, qh->interior_point, facet, &dist);
+ qh->totvol += -dist * area/ qh->hull_dim;
+ }
+ if (qh->PRINTstatistics) {
+ wadd_(Wareatot, area);
+ wmax_(Wareamax, area);
+ wmin_(Wareamin, area);
+ }
+ }
+ qh->hasAreaVolume= True;
+} /* getarea */
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="gram_schmidt">-</a>
+
+ qh_gram_schmidt(qh, dim, row )
+ implements Gram-Schmidt orthogonalization by rows
+
+ returns:
+ false if zero norm
+ overwrites rows[dim][dim]
+
+ notes:
+ see Golub & van Loan, 1983, Algorithm 6.2-2, "Modified Gram-Schmidt"
+ overflow due to small divisors not handled
+
+ design:
+ for each row
+ compute norm for row
+ if non-zero, normalize row
+ for each remaining rowA
+ compute inner product of row and rowA
+ reduce rowA by row * inner product
+*/
+boolT qh_gram_schmidt(qhT *qh, int dim, realT **row) {
+ realT *rowi, *rowj, norm;
+ int i, j, k;
+
+ for (i=0; i < dim; i++) {
+ rowi= row[i];
+ for (norm= 0.0, k= dim; k--; rowi++)
+ norm += *rowi * *rowi;
+ norm= sqrt(norm);
+ wmin_(Wmindenom, norm);
+ if (norm == 0.0) /* either 0 or overflow due to sqrt */
+ return False;
+ for (k=dim; k--; )
+ *(--rowi) /= norm;
+ for (j=i+1; j < dim; j++) {
+ rowj= row[j];
+ for (norm= 0.0, k=dim; k--; )
+ norm += *rowi++ * *rowj++;
+ for (k=dim; k--; )
+ *(--rowj) -= *(--rowi) * norm;
+ }
+ }
+ return True;
+} /* gram_schmidt */
+
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="inthresholds">-</a>
+
+ qh_inthresholds(qh, normal, angle )
+ return True if normal within qh.lower_/upper_threshold
+
+ returns:
+ estimate of angle by summing of threshold diffs
+ angle may be NULL
+ smaller "angle" is better
+
+ notes:
+ invalid if qh.SPLITthresholds
+
+ see:
+ qh.lower_threshold in qh_initbuild()
+ qh_initthresholds()
+
+ design:
+ for each dimension
+ test threshold
+*/
+boolT qh_inthresholds(qhT *qh, coordT *normal, realT *angle) {
+ boolT within= True;
+ int k;
+ realT threshold;
+
+ if (angle)
+ *angle= 0.0;
+ for (k=0; k < qh->hull_dim; k++) {
+ threshold= qh->lower_threshold[k];
+ if (threshold > -REALmax/2) {
+ if (normal[k] < threshold)
+ within= False;
+ if (angle) {
+ threshold -= normal[k];
+ *angle += fabs_(threshold);
+ }
+ }
+ if (qh->upper_threshold[k] < REALmax/2) {
+ threshold= qh->upper_threshold[k];
+ if (normal[k] > threshold)
+ within= False;
+ if (angle) {
+ threshold -= normal[k];
+ *angle += fabs_(threshold);
+ }
+ }
+ }
+ return within;
+} /* inthresholds */
+
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="joggleinput">-</a>
+
+ qh_joggleinput(qh)
+ randomly joggle input to Qhull by qh.JOGGLEmax
+ initial input is qh.first_point/qh.num_points of qh.hull_dim
+ repeated calls use qh.input_points/qh.num_points
+
+ returns:
+ joggles points at qh.first_point/qh.num_points
+ copies data to qh.input_points/qh.input_malloc if first time
+ determines qh.JOGGLEmax if it was zero
+ if qh.DELAUNAY
+ computes the Delaunay projection of the joggled points
+
+ notes:
+ if qh.DELAUNAY, unnecessarily joggles the last coordinate
+ the initial 'QJn' may be set larger than qh_JOGGLEmaxincrease
+
+ design:
+ if qh.DELAUNAY
+ set qh.SCALElast for reduced precision errors
+ if first call
+ initialize qh.input_points to the original input points
+ if qh.JOGGLEmax == 0
+ determine default qh.JOGGLEmax
+ else
+ increase qh.JOGGLEmax according to qh.build_cnt
+ joggle the input by adding a random number in [-qh.JOGGLEmax,qh.JOGGLEmax]
+ if qh.DELAUNAY
+ sets the Delaunay projection
+*/
+void qh_joggleinput(qhT *qh) {
+ int i, seed, size;
+ coordT *coordp, *inputp;
+ realT randr, randa, randb;
+
+ if (!qh->input_points) { /* first call */
+ qh->input_points= qh->first_point;
+ qh->input_malloc= qh->POINTSmalloc;
+ size= qh->num_points * qh->hull_dim * sizeof(coordT);
+ if (!(qh->first_point=(coordT*)qh_malloc((size_t)size))) {
+ qh_fprintf(qh, qh->ferr, 6009, "qhull error: insufficient memory to joggle %d points\n",
+ qh->num_points);
+ qh_errexit(qh, qh_ERRmem, NULL, NULL);
+ }
+ qh->POINTSmalloc= True;
+ if (qh->JOGGLEmax == 0.0) {
+ qh->JOGGLEmax= qh_detjoggle(qh, qh->input_points, qh->num_points, qh->hull_dim);
+ qh_option(qh, "QJoggle", NULL, &qh->JOGGLEmax);
+ }
+ }else { /* repeated call */
+ if (!qh->RERUN && qh->build_cnt > qh_JOGGLEretry) {
+ if (((qh->build_cnt-qh_JOGGLEretry-1) % qh_JOGGLEagain) == 0) {
+ realT maxjoggle= qh->MAXwidth * qh_JOGGLEmaxincrease;
+ if (qh->JOGGLEmax < maxjoggle) {
+ qh->JOGGLEmax *= qh_JOGGLEincrease;
+ minimize_(qh->JOGGLEmax, maxjoggle);
+ }
+ }
+ }
+ qh_option(qh, "QJoggle", NULL, &qh->JOGGLEmax);
+ }
+ if (qh->build_cnt > 1 && qh->JOGGLEmax > fmax_(qh->MAXwidth/4, 0.1)) {
+ qh_fprintf(qh, qh->ferr, 6010, "qhull error: the current joggle for 'QJn', %.2g, is too large for the width\nof the input. If possible, recompile Qhull with higher-precision reals.\n",
+ qh->JOGGLEmax);
+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+ }
+ /* for some reason, using qh->ROTATErandom and qh_RANDOMseed does not repeat the run. Use 'TRn' instead */
+ seed= qh_RANDOMint;
+ qh_option(qh, "_joggle-seed", &seed, NULL);
+ trace0((qh, qh->ferr, 6, "qh_joggleinput: joggle input by %2.2g with seed %d\n",
+ qh->JOGGLEmax, seed));
+ inputp= qh->input_points;
+ coordp= qh->first_point;
+ randa= 2.0 * qh->JOGGLEmax/qh_RANDOMmax;
+ randb= -qh->JOGGLEmax;
+ size= qh->num_points * qh->hull_dim;
+ for (i=size; i--; ) {
+ randr= qh_RANDOMint;
+ *(coordp++)= *(inputp++) + (randr * randa + randb);
+ }
+ if (qh->DELAUNAY) {
+ qh->last_low= qh->last_high= qh->last_newhigh= REALmax;
+ qh_setdelaunay(qh, qh->hull_dim, qh->num_points, qh->first_point);
+ }
+} /* joggleinput */
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="maxabsval">-</a>
+
+ qh_maxabsval( normal, dim )
+ return pointer to maximum absolute value of a dim vector
+ returns NULL if dim=0
+*/
+realT *qh_maxabsval(realT *normal, int dim) {
+ realT maxval= -REALmax;
+ realT *maxp= NULL, *colp, absval;
+ int k;
+
+ for (k=dim, colp= normal; k--; colp++) {
+ absval= fabs_(*colp);
+ if (absval > maxval) {
+ maxval= absval;
+ maxp= colp;
+ }
+ }
+ return maxp;
+} /* maxabsval */
+
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="maxmin">-</a>
+
+ qh_maxmin(qh, points, numpoints, dimension )
+ return max/min points for each dimension
+ determine max and min coordinates
+
+ returns:
+ returns a temporary set of max and min points
+ may include duplicate points. Does not include qh.GOODpoint
+ sets qh.NEARzero, qh.MAXabs_coord, qh.MAXsumcoord, qh.MAXwidth
+ qh.MAXlastcoord, qh.MINlastcoord
+ initializes qh.max_outside, qh.min_vertex, qh.WAScoplanar, qh.ZEROall_ok
+
+ notes:
+ loop duplicated in qh_detjoggle()
+
+ design:
+ initialize global precision variables
+ checks definition of REAL...
+ for each dimension
+ for each point
+ collect maximum and minimum point
+ collect maximum of maximums and minimum of minimums
+ determine qh.NEARzero for Gaussian Elimination
+*/
+setT *qh_maxmin(qhT *qh, pointT *points, int numpoints, int dimension) {
+ int k;
+ realT maxcoord, temp;
+ pointT *minimum, *maximum, *point, *pointtemp;
+ setT *set;
+
+ qh->max_outside= 0.0;
+ qh->MAXabs_coord= 0.0;
+ qh->MAXwidth= -REALmax;
+ qh->MAXsumcoord= 0.0;
+ qh->min_vertex= 0.0;
+ qh->WAScoplanar= False;
+ if (qh->ZEROcentrum)
+ qh->ZEROall_ok= True;
+ if (REALmin < REALepsilon && REALmin < REALmax && REALmin > -REALmax
+ && REALmax > 0.0 && -REALmax < 0.0)
+ ; /* all ok */
+ else {
+ qh_fprintf(qh, qh->ferr, 6011, "qhull error: floating point constants in user.h are wrong\n\
+REALepsilon %g REALmin %g REALmax %g -REALmax %g\n",
+ REALepsilon, REALmin, REALmax, -REALmax);
+ qh_errexit(qh, qh_ERRinput, NULL, NULL);
+ }
+ set= qh_settemp(qh, 2*dimension);
+ for (k=0; k < dimension; k++) {
+ if (points == qh->GOODpointp)
+ minimum= maximum= points + dimension;
+ else
+ minimum= maximum= points;
+ FORALLpoint_(qh, points, numpoints) {
+ if (point == qh->GOODpointp)
+ continue;
+ if (maximum[k] < point[k])
+ maximum= point;
+ else if (minimum[k] > point[k])
+ minimum= point;
+ }
+ if (k == dimension-1) {
+ qh->MINlastcoord= minimum[k];
+ qh->MAXlastcoord= maximum[k];
+ }
+ if (qh->SCALElast && k == dimension-1)
+ maxcoord= qh->MAXwidth;
+ else {
+ maxcoord= fmax_(maximum[k], -minimum[k]);
+ if (qh->GOODpointp) {
+ temp= fmax_(qh->GOODpointp[k], -qh->GOODpointp[k]);
+ maximize_(maxcoord, temp);
+ }
+ temp= maximum[k] - minimum[k];
+ maximize_(qh->MAXwidth, temp);
+ }
+ maximize_(qh->MAXabs_coord, maxcoord);
+ qh->MAXsumcoord += maxcoord;
+ qh_setappend(qh, &set, maximum);
+ qh_setappend(qh, &set, minimum);
+ /* calculation of qh NEARzero is based on Golub & van Loan, 1983,
+ Eq. 4.4-13 for "Gaussian elimination with complete pivoting".
+ Golub & van Loan say that n^3 can be ignored and 10 be used in
+ place of rho */
+ qh->NEARzero[k]= 80 * qh->MAXsumcoord * REALepsilon;
+ }
+ if (qh->IStracing >=1)
+ qh_printpoints(qh, qh->ferr, "qh_maxmin: found the max and min points(by dim):", set);
+ return(set);
+} /* maxmin */
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="maxouter">-</a>
+
+ qh_maxouter(qh)
+ return maximum distance from facet to outer plane
+ normally this is qh.max_outside+qh.DISTround
+ does not include qh.JOGGLEmax
+
+ see:
+ qh_outerinner()
+
+ notes:
+ need to add another qh.DISTround if testing actual point with computation
+
+ for joggle:
+ qh_setfacetplane() updated qh.max_outer for Wnewvertexmax (max distance to vertex)
+ need to use Wnewvertexmax since could have a coplanar point for a high
+ facet that is replaced by a low facet
+ need to add qh.JOGGLEmax if testing input points
+*/
+realT qh_maxouter(qhT *qh) {
+ realT dist;
+
+ dist= fmax_(qh->max_outside, qh->DISTround);
+ dist += qh->DISTround;
+ trace4((qh, qh->ferr, 4012, "qh_maxouter: max distance from facet to outer plane is %2.2g max_outside is %2.2g\n", dist, qh->max_outside));
+ return dist;
+} /* maxouter */
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="maxsimplex">-</a>
+
+ qh_maxsimplex(qh, dim, maxpoints, points, numpoints, simplex )
+ determines maximum simplex for a set of points
+ starts from points already in simplex
+ skips qh.GOODpointp (assumes that it isn't in maxpoints)
+
+ returns:
+ simplex with dim+1 points
+
+ notes:
+ assumes at least pointsneeded points in points
+ maximizes determinate for x,y,z,w, etc.
+ uses maxpoints as long as determinate is clearly non-zero
+
+ design:
+ initialize simplex with at least two points
+ (find points with max or min x coordinate)
+ for each remaining dimension
+ add point that maximizes the determinate
+ (use points from maxpoints first)
+*/
+void qh_maxsimplex(qhT *qh, int dim, setT *maxpoints, pointT *points, int numpoints, setT **simplex) {
+ pointT *point, **pointp, *pointtemp, *maxpoint, *minx=NULL, *maxx=NULL;
+ boolT nearzero, maxnearzero= False;
+ int k, sizinit;
+ realT maxdet= -REALmax, det, mincoord= REALmax, maxcoord= -REALmax;
+
+ sizinit= qh_setsize(qh, *simplex);
+ if (sizinit < 2) {
+ if (qh_setsize(qh, maxpoints) >= 2) {
+ FOREACHpoint_(maxpoints) {
+ if (maxcoord < point[0]) {
+ maxcoord= point[0];
+ maxx= point;
+ }
+ if (mincoord > point[0]) {
+ mincoord= point[0];
+ minx= point;
+ }
+ }
+ }else {
+ FORALLpoint_(qh, points, numpoints) {
+ if (point == qh->GOODpointp)
+ continue;
+ if (maxcoord < point[0]) {
+ maxcoord= point[0];
+ maxx= point;
+ }
+ if (mincoord > point[0]) {
+ mincoord= point[0];
+ minx= point;
+ }
+ }
+ }
+ qh_setunique(qh, simplex, minx);
+ if (qh_setsize(qh, *simplex) < 2)
+ qh_setunique(qh, simplex, maxx);
+ sizinit= qh_setsize(qh, *simplex);
+ if (sizinit < 2) {
+ qh_precision(qh, "input has same x coordinate");
+ if (zzval_(Zsetplane) > qh->hull_dim+1) {
+ qh_fprintf(qh, qh->ferr, 6012, "qhull precision error (qh_maxsimplex for voronoi_center):\n%d points with the same x coordinate.\n",
+ qh_setsize(qh, maxpoints)+numpoints);
+ qh_errexit(qh, qh_ERRprec, NULL, NULL);
+ }else {
+ qh_fprintf(qh, qh->ferr, 6013, "qhull input error: input is less than %d-dimensional since it has the same x coordinate\n", qh->hull_dim);
+ qh_errexit(qh, qh_ERRinput, NULL, NULL);
+ }
+ }
+ }
+ for (k=sizinit; k < dim+1; k++) {
+ maxpoint= NULL;
+ maxdet= -REALmax;
+ FOREACHpoint_(maxpoints) {
+ if (!qh_setin(*simplex, point)) {
+ det= qh_detsimplex(qh, point, *simplex, k, &nearzero);
+ if ((det= fabs_(det)) > maxdet) {
+ maxdet= det;
+ maxpoint= point;
+ maxnearzero= nearzero;
+ }
+ }
+ }
+ if (!maxpoint || maxnearzero) {
+ zinc_(Zsearchpoints);
+ if (!maxpoint) {
+ trace0((qh, qh->ferr, 7, "qh_maxsimplex: searching all points for %d-th initial vertex.\n", k+1));
+ }else {
+ trace0((qh, qh->ferr, 8, "qh_maxsimplex: searching all points for %d-th initial vertex, better than p%d det %2.2g\n",
+ k+1, qh_pointid(qh, maxpoint), maxdet));
+ }
+ FORALLpoint_(qh, points, numpoints) {
+ if (point == qh->GOODpointp)
+ continue;
+ if (!qh_setin(*simplex, point)) {
+ det= qh_detsimplex(qh, point, *simplex, k, &nearzero);
+ if ((det= fabs_(det)) > maxdet) {
+ maxdet= det;
+ maxpoint= point;
+ maxnearzero= nearzero;
+ }
+ }
+ }
+ } /* !maxpoint */
+ if (!maxpoint) {
+ qh_fprintf(qh, qh->ferr, 6014, "qhull internal error (qh_maxsimplex): not enough points available\n");
+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+ }
+ qh_setappend(qh, simplex, maxpoint);
+ trace1((qh, qh->ferr, 1002, "qh_maxsimplex: selected point p%d for %d`th initial vertex, det=%2.2g\n",
+ qh_pointid(qh, maxpoint), k+1, maxdet));
+ } /* k */
+} /* maxsimplex */
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="minabsval">-</a>
+
+ qh_minabsval( normal, dim )
+ return minimum absolute value of a dim vector
+*/
+realT qh_minabsval(realT *normal, int dim) {
+ realT minval= 0;
+ realT maxval= 0;
+ realT *colp;
+ int k;
+
+ for (k=dim, colp=normal; k--; colp++) {
+ maximize_(maxval, *colp);
+ minimize_(minval, *colp);
+ }
+ return fmax_(maxval, -minval);
+} /* minabsval */
+
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="mindiff">-</a>
+
+ qh_mindif(qh, vecA, vecB, dim )
+ return index of min abs. difference of two vectors
+*/
+int qh_mindiff(realT *vecA, realT *vecB, int dim) {
+ realT mindiff= REALmax, diff;
+ realT *vecAp= vecA, *vecBp= vecB;
+ int k, mink= 0;
+
+ for (k=0; k < dim; k++) {
+ diff= *vecAp++ - *vecBp++;
+ diff= fabs_(diff);
+ if (diff < mindiff) {
+ mindiff= diff;
+ mink= k;
+ }
+ }
+ return mink;
+} /* mindiff */
+
+
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="orientoutside">-</a>
+
+ qh_orientoutside(qh, facet )
+ make facet outside oriented via qh.interior_point
+
+ returns:
+ True if facet reversed orientation.
+*/
+boolT qh_orientoutside(qhT *qh, facetT *facet) {
+ int k;
+ realT dist;
+
+ qh_distplane(qh, qh->interior_point, facet, &dist);
+ if (dist > 0) {
+ for (k=qh->hull_dim; k--; )
+ facet->normal[k]= -facet->normal[k];
+ facet->offset= -facet->offset;
+ return True;
+ }
+ return False;
+} /* orientoutside */
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="outerinner">-</a>
+
+ qh_outerinner(qh, facet, outerplane, innerplane )
+ if facet and qh.maxoutdone (i.e., qh_check_maxout)
+ returns outer and inner plane for facet
+ else
+ returns maximum outer and inner plane
+ accounts for qh.JOGGLEmax
+
+ see:
+ qh_maxouter(qh), qh_check_bestdist(), qh_check_points()
+
+ notes:
+ outerplaner or innerplane may be NULL
+ facet is const
+ Does not error (QhullFacet)
+
+ includes qh.DISTround for actual points
+ adds another qh.DISTround if testing with floating point arithmetic
+*/
+void qh_outerinner(qhT *qh, facetT *facet, realT *outerplane, realT *innerplane) {
+ realT dist, mindist;
+ vertexT *vertex, **vertexp;
+
+ if (outerplane) {
+ if (!qh_MAXoutside || !facet || !qh->maxoutdone) {
+ *outerplane= qh_maxouter(qh); /* includes qh.DISTround */
+ }else { /* qh_MAXoutside ... */
+#if qh_MAXoutside
+ *outerplane= facet->maxoutside + qh->DISTround;
+#endif
+
+ }
+ if (qh->JOGGLEmax < REALmax/2)
+ *outerplane += qh->JOGGLEmax * sqrt((realT)qh->hull_dim);
+ }
+ if (innerplane) {
+ if (facet) {
+ mindist= REALmax;
+ FOREACHvertex_(facet->vertices) {
+ zinc_(Zdistio);
+ qh_distplane(qh, vertex->point, facet, &dist);
+ minimize_(mindist, dist);
+ }
+ *innerplane= mindist - qh->DISTround;
+ }else
+ *innerplane= qh->min_vertex - qh->DISTround;
+ if (qh->JOGGLEmax < REALmax/2)
+ *innerplane -= qh->JOGGLEmax * sqrt((realT)qh->hull_dim);
+ }
+} /* outerinner */
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="pointdist">-</a>
+
+ qh_pointdist( point1, point2, dim )
+ return distance between two points
+
+ notes:
+ returns distance squared if 'dim' is negative
+*/
+coordT qh_pointdist(pointT *point1, pointT *point2, int dim) {
+ coordT dist, diff;
+ int k;
+
+ dist= 0.0;
+ for (k= (dim > 0 ? dim : -dim); k--; ) {
+ diff= *point1++ - *point2++;
+ dist += diff * diff;
+ }
+ if (dim > 0)
+ return(sqrt(dist));
+ return dist;
+} /* pointdist */
+
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="printmatrix">-</a>
+
+ qh_printmatrix(qh, fp, string, rows, numrow, numcol )
+ print matrix to fp given by row vectors
+ print string as header
+ qh may be NULL if fp is defined
+
+ notes:
+ print a vector by qh_printmatrix(qh, fp, "", &vect, 1, len)
+*/
+void qh_printmatrix(qhT *qh, FILE *fp, const char *string, realT **rows, int numrow, int numcol) {
+ realT *rowp;
+ realT r; /*bug fix*/
+ int i,k;
+
+ qh_fprintf(qh, fp, 9001, "%s\n", string);
+ for (i=0; i < numrow; i++) {
+ rowp= rows[i];
+ for (k=0; k < numcol; k++) {
+ r= *rowp++;
+ qh_fprintf(qh, fp, 9002, "%6.3g ", r);
+ }
+ qh_fprintf(qh, fp, 9003, "\n");
+ }
+} /* printmatrix */
+
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="printpoints">-</a>
+
+ qh_printpoints(qh, fp, string, points )
+ print pointids to fp for a set of points
+ if string, prints string and 'p' point ids
+*/
+void qh_printpoints(qhT *qh, FILE *fp, const char *string, setT *points) {
+ pointT *point, **pointp;
+
+ if (string) {
+ qh_fprintf(qh, fp, 9004, "%s", string);
+ FOREACHpoint_(points)
+ qh_fprintf(qh, fp, 9005, " p%d", qh_pointid(qh, point));
+ qh_fprintf(qh, fp, 9006, "\n");
+ }else {
+ FOREACHpoint_(points)
+ qh_fprintf(qh, fp, 9007, " %d", qh_pointid(qh, point));
+ qh_fprintf(qh, fp, 9008, "\n");
+ }
+} /* printpoints */
+
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="projectinput">-</a>
+
+ qh_projectinput(qh)
+ project input points using qh.lower_bound/upper_bound and qh->DELAUNAY
+ if qh.lower_bound[k]=qh.upper_bound[k]= 0,
+ removes dimension k
+ if halfspace intersection
+ removes dimension k from qh.feasible_point
+ input points in qh->first_point, num_points, input_dim
+
+ returns:
+ new point array in qh->first_point of qh->hull_dim coordinates
+ sets qh->POINTSmalloc
+ if qh->DELAUNAY
+ projects points to paraboloid
+ lowbound/highbound is also projected
+ if qh->ATinfinity
+ adds point "at-infinity"
+ if qh->POINTSmalloc
+ frees old point array
+
+ notes:
+ checks that qh.hull_dim agrees with qh.input_dim, PROJECTinput, and DELAUNAY
+
+
+ design:
+ sets project[k] to -1 (delete), 0 (keep), 1 (add for Delaunay)
+ determines newdim and newnum for qh->hull_dim and qh->num_points
+ projects points to newpoints
+ projects qh.lower_bound to itself
+ projects qh.upper_bound to itself
+ if qh->DELAUNAY
+ if qh->ATINFINITY
+ projects points to paraboloid
+ computes "infinity" point as vertex average and 10% above all points
+ else
+ uses qh_setdelaunay to project points to paraboloid
+*/
+void qh_projectinput(qhT *qh) {
+ int k,i;
+ int newdim= qh->input_dim, newnum= qh->num_points;
+ signed char *project;
+ int projectsize= (qh->input_dim+1)*sizeof(*project);
+ pointT *newpoints, *coord, *infinity;
+ realT paraboloid, maxboloid= 0;
+
+ project= (signed char*)qh_memalloc(qh, projectsize);
+ memset((char*)project, 0, (size_t)projectsize);
+ for (k=0; k < qh->input_dim; k++) { /* skip Delaunay bound */
+ if (qh->lower_bound[k] == 0 && qh->upper_bound[k] == 0) {
+ project[k]= -1;
+ newdim--;
+ }
+ }
+ if (qh->DELAUNAY) {
+ project[k]= 1;
+ newdim++;
+ if (qh->ATinfinity)
+ newnum++;
+ }
+ if (newdim != qh->hull_dim) {
+ qh_memfree(qh, project, projectsize);
+ qh_fprintf(qh, qh->ferr, 6015, "qhull internal error (qh_projectinput): dimension after projection %d != hull_dim %d\n", newdim, qh->hull_dim);
+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+ }
+ if (!(newpoints= qh->temp_malloc= (coordT*)qh_malloc(newnum*newdim*sizeof(coordT)))){
+ qh_memfree(qh, project, projectsize);
+ qh_fprintf(qh, qh->ferr, 6016, "qhull error: insufficient memory to project %d points\n",
+ qh->num_points);
+ qh_errexit(qh, qh_ERRmem, NULL, NULL);
+ }
+ /* qh_projectpoints throws error if mismatched dimensions */
+ qh_projectpoints(qh, project, qh->input_dim+1, qh->first_point,
+ qh->num_points, qh->input_dim, newpoints, newdim);
+ trace1((qh, qh->ferr, 1003, "qh_projectinput: updating lower and upper_bound\n"));
+ qh_projectpoints(qh, project, qh->input_dim+1, qh->lower_bound,
+ 1, qh->input_dim+1, qh->lower_bound, newdim+1);
+ qh_projectpoints(qh, project, qh->input_dim+1, qh->upper_bound,
+ 1, qh->input_dim+1, qh->upper_bound, newdim+1);
+ if (qh->HALFspace) {
+ if (!qh->feasible_point) {
+ qh_memfree(qh, project, projectsize);
+ qh_fprintf(qh, qh->ferr, 6017, "qhull internal error (qh_projectinput): HALFspace defined without qh.feasible_point\n");
+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+ }
+ qh_projectpoints(qh, project, qh->input_dim, qh->feasible_point,
+ 1, qh->input_dim, qh->feasible_point, newdim);
+ }
+ qh_memfree(qh, project, projectsize);
+ if (qh->POINTSmalloc)
+ qh_free(qh->first_point);
+ qh->first_point= newpoints;
+ qh->POINTSmalloc= True;
+ qh->temp_malloc= NULL;
+ if (qh->DELAUNAY && qh->ATinfinity) {
+ coord= qh->first_point;
+ infinity= qh->first_point + qh->hull_dim * qh->num_points;
+ for (k=qh->hull_dim-1; k--; )
+ infinity[k]= 0.0;
+ for (i=qh->num_points; i--; ) {
+ paraboloid= 0.0;
+ for (k=0; k < qh->hull_dim-1; k++) {
+ paraboloid += *coord * *coord;
+ infinity[k] += *coord;
+ coord++;
+ }
+ *(coord++)= paraboloid;
+ maximize_(maxboloid, paraboloid);
+ }
+ /* coord == infinity */
+ for (k=qh->hull_dim-1; k--; )
+ *(coord++) /= qh->num_points;
+ *(coord++)= maxboloid * 1.1;
+ qh->num_points++;
+ trace0((qh, qh->ferr, 9, "qh_projectinput: projected points to paraboloid for Delaunay\n"));
+ }else if (qh->DELAUNAY) /* !qh->ATinfinity */
+ qh_setdelaunay(qh, qh->hull_dim, qh->num_points, qh->first_point);
+} /* projectinput */
+
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="projectpoints">-</a>
+
+ qh_projectpoints(qh, project, n, points, numpoints, dim, newpoints, newdim )
+ project points/numpoints/dim to newpoints/newdim
+ if project[k] == -1
+ delete dimension k
+ if project[k] == 1
+ add dimension k by duplicating previous column
+ n is size of project
+
+ notes:
+ newpoints may be points if only adding dimension at end
+
+ design:
+ check that 'project' and 'newdim' agree
+ for each dimension
+ if project == -1
+ skip dimension
+ else
+ determine start of column in newpoints
+ determine start of column in points
+ if project == +1, duplicate previous column
+ copy dimension (column) from points to newpoints
+*/
+void qh_projectpoints(qhT *qh, signed char *project, int n, realT *points,
+ int numpoints, int dim, realT *newpoints, int newdim) {
+ int testdim= dim, oldk=0, newk=0, i,j=0,k;
+ realT *newp, *oldp;
+
+ for (k=0; k < n; k++)
+ testdim += project[k];
+ if (testdim != newdim) {
+ qh_fprintf(qh, qh->ferr, 6018, "qhull internal error (qh_projectpoints): newdim %d should be %d after projection\n",
+ newdim, testdim);
+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+ }
+ for (j=0; j<n; j++) {
+ if (project[j] == -1)
+ oldk++;
+ else {
+ newp= newpoints+newk++;
+ if (project[j] == +1) {
+ if (oldk >= dim)
+ continue;
+ oldp= points+oldk;
+ }else
+ oldp= points+oldk++;
+ for (i=numpoints; i--; ) {
+ *newp= *oldp;
+ newp += newdim;
+ oldp += dim;
+ }
+ }
+ if (oldk >= dim)
+ break;
+ }
+ trace1((qh, qh->ferr, 1004, "qh_projectpoints: projected %d points from dim %d to dim %d\n",
+ numpoints, dim, newdim));
+} /* projectpoints */
+
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="rotateinput">-</a>
+
+ qh_rotateinput(qh, rows )
+ rotate input using row matrix
+ input points given by qh->first_point, num_points, hull_dim
+ assumes rows[dim] is a scratch buffer
+ if qh->POINTSmalloc, overwrites input points, else mallocs a new array
+
+ returns:
+ rotated input
+ sets qh->POINTSmalloc
+
+ design:
+ see qh_rotatepoints
+*/
+void qh_rotateinput(qhT *qh, realT **rows) {
+
+ if (!qh->POINTSmalloc) {
+ qh->first_point= qh_copypoints(qh, qh->first_point, qh->num_points, qh->hull_dim);
+ qh->POINTSmalloc= True;
+ }
+ qh_rotatepoints(qh, qh->first_point, qh->num_points, qh->hull_dim, rows);
+} /* rotateinput */
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="rotatepoints">-</a>
+
+ qh_rotatepoints(qh, points, numpoints, dim, row )
+ rotate numpoints points by a d-dim row matrix
+ assumes rows[dim] is a scratch buffer
+
+ returns:
+ rotated points in place
+
+ design:
+ for each point
+ for each coordinate
+ use row[dim] to compute partial inner product
+ for each coordinate
+ rotate by partial inner product
+*/
+void qh_rotatepoints(qhT *qh, realT *points, int numpoints, int dim, realT **row) {
+ realT *point, *rowi, *coord= NULL, sum, *newval;
+ int i,j,k;
+
+ if (qh->IStracing >= 1)
+ qh_printmatrix(qh, qh->ferr, "qh_rotatepoints: rotate points by", row, dim, dim);
+ for (point= points, j= numpoints; j--; point += dim) {
+ newval= row[dim];
+ for (i=0; i < dim; i++) {
+ rowi= row[i];
+ coord= point;
+ for (sum= 0.0, k= dim; k--; )
+ sum += *rowi++ * *coord++;
+ *(newval++)= sum;
+ }
+ for (k=dim; k--; )
+ *(--coord)= *(--newval);
+ }
+} /* rotatepoints */
+
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="scaleinput">-</a>
+
+ qh_scaleinput(qh)
+ scale input points using qh->low_bound/high_bound
+ input points given by qh->first_point, num_points, hull_dim
+ if qh->POINTSmalloc, overwrites input points, else mallocs a new array
+
+ returns:
+ scales coordinates of points to low_bound[k], high_bound[k]
+ sets qh->POINTSmalloc
+
+ design:
+ see qh_scalepoints
+*/
+void qh_scaleinput(qhT *qh) {
+
+ if (!qh->POINTSmalloc) {
+ qh->first_point= qh_copypoints(qh, qh->first_point, qh->num_points, qh->hull_dim);
+ qh->POINTSmalloc= True;
+ }
+ qh_scalepoints(qh, qh->first_point, qh->num_points, qh->hull_dim,
+ qh->lower_bound, qh->upper_bound);
+} /* scaleinput */
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="scalelast">-</a>
+
+ qh_scalelast(qh, points, numpoints, dim, low, high, newhigh )
+ scale last coordinate to [0,m] for Delaunay triangulations
+ input points given by points, numpoints, dim
+
+ returns:
+ changes scale of last coordinate from [low, high] to [0, newhigh]
+ overwrites last coordinate of each point
+ saves low/high/newhigh in qh.last_low, etc. for qh_setdelaunay()
+
+ notes:
+ when called by qh_setdelaunay, low/high may not match actual data
+
+ design:
+ compute scale and shift factors
+ apply to last coordinate of each point
+*/
+void qh_scalelast(qhT *qh, coordT *points, int numpoints, int dim, coordT low,
+ coordT high, coordT newhigh) {
+ realT scale, shift;
+ coordT *coord;
+ int i;
+ boolT nearzero= False;
+
+ trace4((qh, qh->ferr, 4013, "qh_scalelast: scale last coordinate from [%2.2g, %2.2g] to [0,%2.2g]\n",
+ low, high, newhigh));
+ qh->last_low= low;
+ qh->last_high= high;
+ qh->last_newhigh= newhigh;
+ scale= qh_divzero(newhigh, high - low,
+ qh->MINdenom_1, &nearzero);
+ if (nearzero) {
+ if (qh->DELAUNAY)
+ qh_fprintf(qh, qh->ferr, 6019, "qhull input error: can not scale last coordinate. Input is cocircular\n or cospherical. Use option 'Qz' to add a point at infinity.\n");
+ else
+ qh_fprintf(qh, qh->ferr, 6020, "qhull input error: can not scale last coordinate. New bounds [0, %2.2g] are too wide for\nexisting bounds [%2.2g, %2.2g] (width %2.2g)\n",
+ newhigh, low, high, high-low);
+ qh_errexit(qh, qh_ERRinput, NULL, NULL);
+ }
+ shift= - low * newhigh / (high-low);
+ coord= points + dim - 1;
+ for (i=numpoints; i--; coord += dim)
+ *coord= *coord * scale + shift;
+} /* scalelast */
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="scalepoints">-</a>
+
+ qh_scalepoints(qh, points, numpoints, dim, newlows, newhighs )
+ scale points to new lowbound and highbound
+ retains old bound when newlow= -REALmax or newhigh= +REALmax
+
+ returns:
+ scaled points
+ overwrites old points
+
+ design:
+ for each coordinate
+ compute current low and high bound
+ compute scale and shift factors
+ scale all points
+ enforce new low and high bound for all points
+*/
+void qh_scalepoints(qhT *qh, pointT *points, int numpoints, int dim,
+ realT *newlows, realT *newhighs) {
+ int i,k;
+ realT shift, scale, *coord, low, high, newlow, newhigh, mincoord, maxcoord;
+ boolT nearzero= False;
+
+ for (k=0; k < dim; k++) {
+ newhigh= newhighs[k];
+ newlow= newlows[k];
+ if (newhigh > REALmax/2 && newlow < -REALmax/2)
+ continue;
+ low= REALmax;
+ high= -REALmax;
+ for (i=numpoints, coord=points+k; i--; coord += dim) {
+ minimize_(low, *coord);
+ maximize_(high, *coord);
+ }
+ if (newhigh > REALmax/2)
+ newhigh= high;
+ if (newlow < -REALmax/2)
+ newlow= low;
+ if (qh->DELAUNAY && k == dim-1 && newhigh < newlow) {
+ qh_fprintf(qh, qh->ferr, 6021, "qhull input error: 'Qb%d' or 'QB%d' inverts paraboloid since high bound %.2g < low bound %.2g\n",
+ k, k, newhigh, newlow);
+ qh_errexit(qh, qh_ERRinput, NULL, NULL);
+ }
+ scale= qh_divzero(newhigh - newlow, high - low,
+ qh->MINdenom_1, &nearzero);
+ if (nearzero) {
+ qh_fprintf(qh, qh->ferr, 6022, "qhull input error: %d'th dimension's new bounds [%2.2g, %2.2g] too wide for\nexisting bounds [%2.2g, %2.2g]\n",
+ k, newlow, newhigh, low, high);
+ qh_errexit(qh, qh_ERRinput, NULL, NULL);
+ }
+ shift= (newlow * high - low * newhigh)/(high-low);
+ coord= points+k;
+ for (i=numpoints; i--; coord += dim)
+ *coord= *coord * scale + shift;
+ coord= points+k;
+ if (newlow < newhigh) {
+ mincoord= newlow;
+ maxcoord= newhigh;
+ }else {
+ mincoord= newhigh;
+ maxcoord= newlow;
+ }
+ for (i=numpoints; i--; coord += dim) {
+ minimize_(*coord, maxcoord); /* because of roundoff error */
+ maximize_(*coord, mincoord);
+ }
+ trace0((qh, qh->ferr, 10, "qh_scalepoints: scaled %d'th coordinate [%2.2g, %2.2g] to [%.2g, %.2g] for %d points by %2.2g and shifted %2.2g\n",
+ k, low, high, newlow, newhigh, numpoints, scale, shift));
+ }
+} /* scalepoints */
+
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="setdelaunay">-</a>
+
+ qh_setdelaunay(qh, dim, count, points )
+ project count points to dim-d paraboloid for Delaunay triangulation
+
+ dim is one more than the dimension of the input set
+ assumes dim is at least 3 (i.e., at least a 2-d Delaunay triangulation)
+
+ points is a dim*count realT array. The first dim-1 coordinates
+ are the coordinates of the first input point. array[dim] is
+ the first coordinate of the second input point. array[2*dim] is
+ the first coordinate of the third input point.
+
+ if qh.last_low defined (i.e., 'Qbb' called qh_scalelast)
+ calls qh_scalelast to scale the last coordinate the same as the other points
+
+ returns:
+ for each point
+ sets point[dim-1] to sum of squares of coordinates
+ scale points to 'Qbb' if needed
+
+ notes:
+ to project one point, use
+ qh_setdelaunay(qh, qh->hull_dim, 1, point)
+
+ Do not use options 'Qbk', 'QBk', or 'QbB' since they scale
+ the coordinates after the original projection.
+
+*/
+void qh_setdelaunay(qhT *qh, int dim, int count, pointT *points) {
+ int i, k;
+ coordT *coordp, coord;
+ realT paraboloid;
+
+ trace0((qh, qh->ferr, 11, "qh_setdelaunay: project %d points to paraboloid for Delaunay triangulation\n", count));
+ coordp= points;
+ for (i=0; i < count; i++) {
+ coord= *coordp++;
+ paraboloid= coord*coord;
+ for (k=dim-2; k--; ) {
+ coord= *coordp++;
+ paraboloid += coord*coord;
+ }
+ *coordp++ = paraboloid;
+ }
+ if (qh->last_low < REALmax/2)
+ qh_scalelast(qh, points, count, dim, qh->last_low, qh->last_high, qh->last_newhigh);
+} /* setdelaunay */
+
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="sethalfspace">-</a>
+
+ qh_sethalfspace(qh, dim, coords, nextp, normal, offset, feasible )
+ set point to dual of halfspace relative to feasible point
+ halfspace is normal coefficients and offset.
+
+ returns:
+ false and prints error if feasible point is outside of hull
+ overwrites coordinates for point at dim coords
+ nextp= next point (coords)
+ does not call qh_errexit
+
+ design:
+ compute distance from feasible point to halfspace
+ divide each normal coefficient by -dist
+*/
+boolT qh_sethalfspace(qhT *qh, int dim, coordT *coords, coordT **nextp,
+ coordT *normal, coordT *offset, coordT *feasible) {
+ coordT *normp= normal, *feasiblep= feasible, *coordp= coords;
+ realT dist;
+ realT r; /*bug fix*/
+ int k;
+ boolT zerodiv;
+
+ dist= *offset;
+ for (k=dim; k--; )
+ dist += *(normp++) * *(feasiblep++);
+ if (dist > 0)
+ goto LABELerroroutside;
+ normp= normal;
+ if (dist < -qh->MINdenom) {
+ for (k=dim; k--; )
+ *(coordp++)= *(normp++) / -dist;
+ }else {
+ for (k=dim; k--; ) {
+ *(coordp++)= qh_divzero(*(normp++), -dist, qh->MINdenom_1, &zerodiv);
+ if (zerodiv)
+ goto LABELerroroutside;
+ }
+ }
+ *nextp= coordp;
+ if (qh->IStracing >= 4) {
+ qh_fprintf(qh, qh->ferr, 8021, "qh_sethalfspace: halfspace at offset %6.2g to point: ", *offset);
+ for (k=dim, coordp=coords; k--; ) {
+ r= *coordp++;
+ qh_fprintf(qh, qh->ferr, 8022, " %6.2g", r);
+ }
+ qh_fprintf(qh, qh->ferr, 8023, "\n");
+ }
+ return True;
+LABELerroroutside:
+ feasiblep= feasible;
+ normp= normal;
+ qh_fprintf(qh, qh->ferr, 6023, "qhull input error: feasible point is not clearly inside halfspace\nfeasible point: ");
+ for (k=dim; k--; )
+ qh_fprintf(qh, qh->ferr, 8024, qh_REAL_1, r=*(feasiblep++));
+ qh_fprintf(qh, qh->ferr, 8025, "\n halfspace: ");
+ for (k=dim; k--; )
+ qh_fprintf(qh, qh->ferr, 8026, qh_REAL_1, r=*(normp++));
+ qh_fprintf(qh, qh->ferr, 8027, "\n at offset: ");
+ qh_fprintf(qh, qh->ferr, 8028, qh_REAL_1, *offset);
+ qh_fprintf(qh, qh->ferr, 8029, " and distance: ");
+ qh_fprintf(qh, qh->ferr, 8030, qh_REAL_1, dist);
+ qh_fprintf(qh, qh->ferr, 8031, "\n");
+ return False;
+} /* sethalfspace */
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="sethalfspace_all">-</a>
+
+ qh_sethalfspace_all(qh, dim, count, halfspaces, feasible )
+ generate dual for halfspace intersection with feasible point
+ array of count halfspaces
+ each halfspace is normal coefficients followed by offset
+ the origin is inside the halfspace if the offset is negative
+ feasible is a point inside all halfspaces (http://www.qhull.org/html/qhalf.htm#notes)
+
+ returns:
+ malloc'd array of count X dim-1 points
+
+ notes:
+ call before qh_init_B or qh_initqhull_globals
+ free memory when done
+ unused/untested code: please email bradb@shore.net if this works ok for you
+ if using option 'Fp', qh->feasible_point must be set (e.g., to 'feasible')
+ qh->feasible_point is a malloc'd array that is freed by qh_freebuffers.
+
+ design:
+ see qh_sethalfspace
+*/
+coordT *qh_sethalfspace_all(qhT *qh, int dim, int count, coordT *halfspaces, pointT *feasible) {
+ int i, newdim;
+ pointT *newpoints;
+ coordT *coordp, *normalp, *offsetp;
+
+ trace0((qh, qh->ferr, 12, "qh_sethalfspace_all: compute dual for halfspace intersection\n"));
+ newdim= dim - 1;
+ if (!(newpoints=(coordT*)qh_malloc(count*newdim*sizeof(coordT)))){
+ qh_fprintf(qh, qh->ferr, 6024, "qhull error: insufficient memory to compute dual of %d halfspaces\n",
+ count);
+ qh_errexit(qh, qh_ERRmem, NULL, NULL);
+ }
+ coordp= newpoints;
+ normalp= halfspaces;
+ for (i=0; i < count; i++) {
+ offsetp= normalp + newdim;
+ if (!qh_sethalfspace(qh, newdim, coordp, &coordp, normalp, offsetp, feasible)) {
+ qh_free(newpoints); /* feasible is not inside halfspace as reported by qh_sethalfspace */
+ qh_fprintf(qh, qh->ferr, 8032, "The halfspace was at index %d\n", i);
+ qh_errexit(qh, qh_ERRinput, NULL, NULL);
+ }
+ normalp= offsetp + 1;
+ }
+ return newpoints;
+} /* sethalfspace_all */
+
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="sharpnewfacets">-</a>
+
+ qh_sharpnewfacets(qh)
+
+ returns:
+ true if could be an acute angle (facets in different quadrants)
+
+ notes:
+ for qh_findbest
+
+ design:
+ for all facets on qh.newfacet_list
+ if two facets are in different quadrants
+ set issharp
+*/
+boolT qh_sharpnewfacets(qhT *qh) {
+ facetT *facet;
+ boolT issharp = False;
+ int *quadrant, k;
+
+ quadrant= (int*)qh_memalloc(qh, qh->hull_dim * sizeof(int));
+ FORALLfacet_(qh->newfacet_list) {
+ if (facet == qh->newfacet_list) {
+ for (k=qh->hull_dim; k--; )
+ quadrant[ k]= (facet->normal[ k] > 0);
+ }else {
+ for (k=qh->hull_dim; k--; ) {
+ if (quadrant[ k] != (facet->normal[ k] > 0)) {
+ issharp= True;
+ break;
+ }
+ }
+ }
+ if (issharp)
+ break;
+ }
+ qh_memfree(qh, quadrant, qh->hull_dim * sizeof(int));
+ trace3((qh, qh->ferr, 3001, "qh_sharpnewfacets: %d\n", issharp));
+ return issharp;
+} /* sharpnewfacets */
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="voronoi_center">-</a>
+
+ qh_voronoi_center(qh, dim, points )
+ return Voronoi center for a set of points
+ dim is the orginal dimension of the points
+ gh.gm_matrix/qh.gm_row are scratch buffers
+
+ returns:
+ center as a temporary point (qh_memalloc)
+ if non-simplicial,
+ returns center for max simplex of points
+
+ notes:
+ only called by qh_facetcenter
+ from Bowyer & Woodwark, A Programmer's Geometry, 1983, p. 65
+
+ design:
+ if non-simplicial
+ determine max simplex for points
+ translate point0 of simplex to origin
+ compute sum of squares of diagonal
+ compute determinate
+ compute Voronoi center (see Bowyer & Woodwark)
+*/
+pointT *qh_voronoi_center(qhT *qh, int dim, setT *points) {
+ pointT *point, **pointp, *point0;
+ pointT *center= (pointT*)qh_memalloc(qh, qh->center_size);
+ setT *simplex;
+ int i, j, k, size= qh_setsize(qh, points);
+ coordT *gmcoord;
+ realT *diffp, sum2, *sum2row, *sum2p, det, factor;
+ boolT nearzero, infinite;
+
+ if (size == dim+1)
+ simplex= points;
+ else if (size < dim+1) {
+ qh_memfree(qh, center, qh->center_size);
+ qh_fprintf(qh, qh->ferr, 6025, "qhull internal error (qh_voronoi_center):\n need at least %d points to construct a Voronoi center\n",
+ dim+1);
+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+ simplex= points; /* never executed -- avoids warning */
+ }else {
+ simplex= qh_settemp(qh, dim+1);
+ qh_maxsimplex(qh, dim, points, NULL, 0, &simplex);
+ }
+ point0= SETfirstt_(simplex, pointT);
+ gmcoord= qh->gm_matrix;
+ for (k=0; k < dim; k++) {
+ qh->gm_row[k]= gmcoord;
+ FOREACHpoint_(simplex) {
+ if (point != point0)
+ *(gmcoord++)= point[k] - point0[k];
+ }
+ }
+ sum2row= gmcoord;
+ for (i=0; i < dim; i++) {
+ sum2= 0.0;
+ for (k=0; k < dim; k++) {
+ diffp= qh->gm_row[k] + i;
+ sum2 += *diffp * *diffp;
+ }
+ *(gmcoord++)= sum2;
+ }
+ det= qh_determinant(qh, qh->gm_row, dim, &nearzero);
+ factor= qh_divzero(0.5, det, qh->MINdenom, &infinite);
+ if (infinite) {
+ for (k=dim; k--; )
+ center[k]= qh_INFINITE;
+ if (qh->IStracing)
+ qh_printpoints(qh, qh->ferr, "qh_voronoi_center: at infinity for ", simplex);
+ }else {
+ for (i=0; i < dim; i++) {
+ gmcoord= qh->gm_matrix;
+ sum2p= sum2row;
+ for (k=0; k < dim; k++) {
+ qh->gm_row[k]= gmcoord;
+ if (k == i) {
+ for (j=dim; j--; )
+ *(gmcoord++)= *sum2p++;
+ }else {
+ FOREACHpoint_(simplex) {
+ if (point != point0)
+ *(gmcoord++)= point[k] - point0[k];
+ }
+ }
+ }
+ center[i]= qh_determinant(qh, qh->gm_row, dim, &nearzero)*factor + point0[i];
+ }
+#ifndef qh_NOtrace
+ if (qh->IStracing >= 3) {
+ qh_fprintf(qh, qh->ferr, 8033, "qh_voronoi_center: det %2.2g factor %2.2g ", det, factor);
+ qh_printmatrix(qh, qh->ferr, "center:", &center, 1, dim);
+ if (qh->IStracing >= 5) {
+ qh_printpoints(qh, qh->ferr, "points", simplex);
+ FOREACHpoint_(simplex)
+ qh_fprintf(qh, qh->ferr, 8034, "p%d dist %.2g, ", qh_pointid(qh, point),
+ qh_pointdist(point, center, dim));
+ qh_fprintf(qh, qh->ferr, 8035, "\n");
+ }
+ }
+#endif
+ }
+ if (simplex != points)
+ qh_settempfree(qh, &simplex);
+ return center;
+} /* voronoi_center */
+
diff --git a/xs/src/qhull/src/libqhull_r/geom_r.c b/xs/src/qhull/src/libqhull_r/geom_r.c
new file mode 100644
index 000000000..8104813ca
--- /dev/null
+++ b/xs/src/qhull/src/libqhull_r/geom_r.c
@@ -0,0 +1,1234 @@
+/*<html><pre> -<a href="qh-geom_r.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ geom_r.c
+ geometric routines of qhull
+
+ see qh-geom_r.htm and geom_r.h
+
+ Copyright (c) 1993-2015 The Geometry Center.
+ $Id: //main/2015/qhull/src/libqhull_r/geom_r.c#2 $$Change: 1995 $
+ $DateTime: 2015/10/13 21:59:42 $$Author: bbarber $
+
+ infrequent code goes into geom2_r.c
+*/
+
+#include "qhull_ra.h"
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="distplane">-</a>
+
+ qh_distplane(qh, point, facet, dist )
+ return distance from point to facet
+
+ returns:
+ dist
+ if qh.RANDOMdist, joggles result
+
+ notes:
+ dist > 0 if point is above facet (i.e., outside)
+ does not error (for qh_sortfacets, qh_outerinner)
+
+ see:
+ qh_distnorm in geom2_r.c
+ qh_distplane [geom_r.c], QhullFacet::distance, and QhullHyperplane::distance are copies
+*/
+void qh_distplane(qhT *qh, pointT *point, facetT *facet, realT *dist) {
+ coordT *normal= facet->normal, *coordp, randr;
+ int k;
+
+ switch (qh->hull_dim){
+ case 2:
+ *dist= facet->offset + point[0] * normal[0] + point[1] * normal[1];
+ break;
+ case 3:
+ *dist= facet->offset + point[0] * normal[0] + point[1] * normal[1] + point[2] * normal[2];
+ break;
+ case 4:
+ *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3];
+ break;
+ case 5:
+ *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3]+point[4]*normal[4];
+ break;
+ case 6:
+ *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3]+point[4]*normal[4]+point[5]*normal[5];
+ break;
+ case 7:
+ *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3]+point[4]*normal[4]+point[5]*normal[5]+point[6]*normal[6];
+ break;
+ case 8:
+ *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3]+point[4]*normal[4]+point[5]*normal[5]+point[6]*normal[6]+point[7]*normal[7];
+ break;
+ default:
+ *dist= facet->offset;
+ coordp= point;
+ for (k=qh->hull_dim; k--; )
+ *dist += *coordp++ * *normal++;
+ break;
+ }
+ zinc_(Zdistplane);
+ if (!qh->RANDOMdist && qh->IStracing < 4)
+ return;
+ if (qh->RANDOMdist) {
+ randr= qh_RANDOMint;
+ *dist += (2.0 * randr / qh_RANDOMmax - 1.0) *
+ qh->RANDOMfactor * qh->MAXabs_coord;
+ }
+ if (qh->IStracing >= 4) {
+ qh_fprintf(qh, qh->ferr, 8001, "qh_distplane: ");
+ qh_fprintf(qh, qh->ferr, 8002, qh_REAL_1, *dist);
+ qh_fprintf(qh, qh->ferr, 8003, "from p%d to f%d\n", qh_pointid(qh, point), facet->id);
+ }
+ return;
+} /* distplane */
+
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="findbest">-</a>
+
+ qh_findbest(qh, point, startfacet, bestoutside, qh_ISnewfacets, qh_NOupper, dist, isoutside, numpart )
+ find facet that is furthest below a point
+ for upperDelaunay facets
+ returns facet only if !qh_NOupper and clearly above
+
+ input:
+ starts search at 'startfacet' (can not be flipped)
+ if !bestoutside(qh_ALL), stops at qh.MINoutside
+
+ returns:
+ best facet (reports error if NULL)
+ early out if isoutside defined and bestdist > qh.MINoutside
+ dist is distance to facet
+ isoutside is true if point is outside of facet
+ numpart counts the number of distance tests
+
+ see also:
+ qh_findbestnew()
+
+ notes:
+ If merging (testhorizon), searches horizon facets of coplanar best facets because
+ after qh_distplane, this and qh_partitionpoint are the most expensive in 3-d
+ avoid calls to distplane, function calls, and real number operations.
+ caller traces result
+ Optimized for outside points. Tried recording a search set for qh_findhorizon.
+ Made code more complicated.
+
+ when called by qh_partitionvisible():
+ indicated by qh_ISnewfacets
+ qh.newfacet_list is list of simplicial, new facets
+ qh_findbestnew set if qh_sharpnewfacets returns True (to use qh_findbestnew)
+ qh.bestfacet_notsharp set if qh_sharpnewfacets returns False
+
+ when called by qh_findfacet(), qh_partitionpoint(), qh_partitioncoplanar(),
+ qh_check_bestdist(), qh_addpoint()
+ indicated by !qh_ISnewfacets
+ returns best facet in neighborhood of given facet
+ this is best facet overall if dist > - qh.MAXcoplanar
+ or hull has at least a "spherical" curvature
+
+ design:
+ initialize and test for early exit
+ repeat while there are better facets
+ for each neighbor of facet
+ exit if outside facet found
+ test for better facet
+ if point is inside and partitioning
+ test for new facets with a "sharp" intersection
+ if so, future calls go to qh_findbestnew()
+ test horizon facets
+*/
+facetT *qh_findbest(qhT *qh, pointT *point, facetT *startfacet,
+ boolT bestoutside, boolT isnewfacets, boolT noupper,
+ realT *dist, boolT *isoutside, int *numpart) {
+ realT bestdist= -REALmax/2 /* avoid underflow */;
+ facetT *facet, *neighbor, **neighborp;
+ facetT *bestfacet= NULL, *lastfacet= NULL;
+ int oldtrace= qh->IStracing;
+ unsigned int visitid= ++qh->visit_id;
+ int numpartnew=0;
+ boolT testhorizon = True; /* needed if precise, e.g., rbox c D6 | qhull Q0 Tv */
+
+ zinc_(Zfindbest);
+ if (qh->IStracing >= 3 || (qh->TRACElevel && qh->TRACEpoint >= 0 && qh->TRACEpoint == qh_pointid(qh, point))) {
+ if (qh->TRACElevel > qh->IStracing)
+ qh->IStracing= qh->TRACElevel;
+ qh_fprintf(qh, qh->ferr, 8004, "qh_findbest: point p%d starting at f%d isnewfacets? %d, unless %d exit if > %2.2g\n",
+ qh_pointid(qh, point), startfacet->id, isnewfacets, bestoutside, qh->MINoutside);
+ qh_fprintf(qh, qh->ferr, 8005, " testhorizon? %d noupper? %d", testhorizon, noupper);
+ qh_fprintf(qh, qh->ferr, 8006, " Last point added was p%d.", qh->furthest_id);
+ qh_fprintf(qh, qh->ferr, 8007, " Last merge was #%d. max_outside %2.2g\n", zzval_(Ztotmerge), qh->max_outside);
+ }
+ if (isoutside)
+ *isoutside= True;
+ if (!startfacet->flipped) { /* test startfacet */
+ *numpart= 1;
+ qh_distplane(qh, point, startfacet, dist); /* this code is duplicated below */
+ if (!bestoutside && *dist >= qh->MINoutside
+ && (!startfacet->upperdelaunay || !noupper)) {
+ bestfacet= startfacet;
+ goto LABELreturn_best;
+ }
+ bestdist= *dist;
+ if (!startfacet->upperdelaunay) {
+ bestfacet= startfacet;
+ }
+ }else
+ *numpart= 0;
+ startfacet->visitid= visitid;
+ facet= startfacet;
+ while (facet) {
+ trace4((qh, qh->ferr, 4001, "qh_findbest: neighbors of f%d, bestdist %2.2g f%d\n",
+ facet->id, bestdist, getid_(bestfacet)));
+ lastfacet= facet;
+ FOREACHneighbor_(facet) {
+ if (!neighbor->newfacet && isnewfacets)
+ continue;
+ if (neighbor->visitid == visitid)
+ continue;
+ neighbor->visitid= visitid;
+ if (!neighbor->flipped) { /* code duplicated above */
+ (*numpart)++;
+ qh_distplane(qh, point, neighbor, dist);
+ if (*dist > bestdist) {
+ if (!bestoutside && *dist >= qh->MINoutside
+ && (!neighbor->upperdelaunay || !noupper)) {
+ bestfacet= neighbor;
+ goto LABELreturn_best;
+ }
+ if (!neighbor->upperdelaunay) {
+ bestfacet= neighbor;
+ bestdist= *dist;
+ break; /* switch to neighbor */
+ }else if (!bestfacet) {
+ bestdist= *dist;
+ break; /* switch to neighbor */
+ }
+ } /* end of *dist>bestdist */
+ } /* end of !flipped */
+ } /* end of FOREACHneighbor */
+ facet= neighbor; /* non-NULL only if *dist>bestdist */
+ } /* end of while facet (directed search) */
+ if (isnewfacets) {
+ if (!bestfacet) {
+ bestdist= -REALmax/2;
+ bestfacet= qh_findbestnew(qh, point, startfacet->next, &bestdist, bestoutside, isoutside, &numpartnew);
+ testhorizon= False; /* qh_findbestnew calls qh_findbesthorizon */
+ }else if (!qh->findbest_notsharp && bestdist < - qh->DISTround) {
+ if (qh_sharpnewfacets(qh)) {
+ /* seldom used, qh_findbestnew will retest all facets */
+ zinc_(Zfindnewsharp);
+ bestfacet= qh_findbestnew(qh, point, bestfacet, &bestdist, bestoutside, isoutside, &numpartnew);
+ testhorizon= False; /* qh_findbestnew calls qh_findbesthorizon */
+ qh->findbestnew= True;
+ }else
+ qh->findbest_notsharp= True;
+ }
+ }
+ if (!bestfacet)
+ bestfacet= qh_findbestlower(qh, lastfacet, point, &bestdist, numpart);
+ if (testhorizon)
+ bestfacet= qh_findbesthorizon(qh, !qh_IScheckmax, point, bestfacet, noupper, &bestdist, &numpartnew);
+ *dist= bestdist;
+ if (isoutside && bestdist < qh->MINoutside)
+ *isoutside= False;
+LABELreturn_best:
+ zadd_(Zfindbesttot, *numpart);
+ zmax_(Zfindbestmax, *numpart);
+ (*numpart) += numpartnew;
+ qh->IStracing= oldtrace;
+ return bestfacet;
+} /* findbest */
+
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="findbesthorizon">-</a>
+
+ qh_findbesthorizon(qh, qh_IScheckmax, point, startfacet, qh_NOupper, &bestdist, &numpart )
+ search coplanar and better horizon facets from startfacet/bestdist
+ ischeckmax turns off statistics and minsearch update
+ all arguments must be initialized
+ returns(ischeckmax):
+ best facet
+ returns(!ischeckmax):
+ best facet that is not upperdelaunay
+ allows upperdelaunay that is clearly outside
+ returns:
+ bestdist is distance to bestfacet
+ numpart -- updates number of distance tests
+
+ notes:
+ no early out -- use qh_findbest() or qh_findbestnew()
+ Searches coplanar or better horizon facets
+
+ when called by qh_check_maxout() (qh_IScheckmax)
+ startfacet must be closest to the point
+ Otherwise, if point is beyond and below startfacet, startfacet may be a local minimum
+ even though other facets are below the point.
+ updates facet->maxoutside for good, visited facets
+ may return NULL
+
+ searchdist is qh.max_outside + 2 * DISTround
+ + max( MINvisible('Vn'), MAXcoplanar('Un'));
+ This setting is a guess. It must be at least max_outside + 2*DISTround
+ because a facet may have a geometric neighbor across a vertex
+
+ design:
+ for each horizon facet of coplanar best facets
+ continue if clearly inside
+ unless upperdelaunay or clearly outside
+ update best facet
+*/
+facetT *qh_findbesthorizon(qhT *qh, boolT ischeckmax, pointT* point, facetT *startfacet, boolT noupper, realT *bestdist, int *numpart) {
+ facetT *bestfacet= startfacet;
+ realT dist;
+ facetT *neighbor, **neighborp, *facet;
+ facetT *nextfacet= NULL; /* optimize last facet of coplanarfacetset */
+ int numpartinit= *numpart, coplanarfacetset_size;
+ unsigned int visitid= ++qh->visit_id;
+ boolT newbest= False; /* for tracing */
+ realT minsearch, searchdist; /* skip facets that are too far from point */
+
+ if (!ischeckmax) {
+ zinc_(Zfindhorizon);
+ }else {
+#if qh_MAXoutside
+ if ((!qh->ONLYgood || startfacet->good) && *bestdist > startfacet->maxoutside)
+ startfacet->maxoutside= *bestdist;
+#endif
+ }
+ searchdist= qh_SEARCHdist; /* multiple of qh.max_outside and precision constants */
+ minsearch= *bestdist - searchdist;
+ if (ischeckmax) {
+ /* Always check coplanar facets. Needed for RBOX 1000 s Z1 G1e-13 t996564279 | QHULL Tv */
+ minimize_(minsearch, -searchdist);
+ }
+ coplanarfacetset_size= 0;
+ facet= startfacet;
+ while (True) {
+ trace4((qh, qh->ferr, 4002, "qh_findbesthorizon: neighbors of f%d bestdist %2.2g f%d ischeckmax? %d noupper? %d minsearch %2.2g searchdist %2.2g\n",
+ facet->id, *bestdist, getid_(bestfacet), ischeckmax, noupper,
+ minsearch, searchdist));
+ FOREACHneighbor_(facet) {
+ if (neighbor->visitid == visitid)
+ continue;
+ neighbor->visitid= visitid;
+ if (!neighbor->flipped) {
+ qh_distplane(qh, point, neighbor, &dist);
+ (*numpart)++;
+ if (dist > *bestdist) {
+ if (!neighbor->upperdelaunay || ischeckmax || (!noupper && dist >= qh->MINoutside)) {
+ bestfacet= neighbor;
+ *bestdist= dist;
+ newbest= True;
+ if (!ischeckmax) {
+ minsearch= dist - searchdist;
+ if (dist > *bestdist + searchdist) {
+ zinc_(Zfindjump); /* everything in qh.coplanarfacetset at least searchdist below */
+ coplanarfacetset_size= 0;
+ }
+ }
+ }
+ }else if (dist < minsearch)
+ continue; /* if ischeckmax, dist can't be positive */
+#if qh_MAXoutside
+ if (ischeckmax && dist > neighbor->maxoutside)
+ neighbor->maxoutside= dist;
+#endif
+ } /* end of !flipped */
+ if (nextfacet) {
+ if (!coplanarfacetset_size++) {
+ SETfirst_(qh->coplanarfacetset)= nextfacet;
+ SETtruncate_(qh->coplanarfacetset, 1);
+ }else
+ qh_setappend(qh, &qh->coplanarfacetset, nextfacet); /* Was needed for RBOX 1000 s W1e-13 P0 t996547055 | QHULL d Qbb Qc Tv
+ and RBOX 1000 s Z1 G1e-13 t996564279 | qhull Tv */
+ }
+ nextfacet= neighbor;
+ } /* end of EACHneighbor */
+ facet= nextfacet;
+ if (facet)
+ nextfacet= NULL;
+ else if (!coplanarfacetset_size)
+ break;
+ else if (!--coplanarfacetset_size) {
+ facet= SETfirstt_(qh->coplanarfacetset, facetT);
+ SETtruncate_(qh->coplanarfacetset, 0);
+ }else
+ facet= (facetT*)qh_setdellast(qh->coplanarfacetset);
+ } /* while True, for each facet in qh.coplanarfacetset */
+ if (!ischeckmax) {
+ zadd_(Zfindhorizontot, *numpart - numpartinit);
+ zmax_(Zfindhorizonmax, *numpart - numpartinit);
+ if (newbest)
+ zinc_(Zparthorizon);
+ }
+ trace4((qh, qh->ferr, 4003, "qh_findbesthorizon: newbest? %d bestfacet f%d bestdist %2.2g\n", newbest, getid_(bestfacet), *bestdist));
+ return bestfacet;
+} /* findbesthorizon */
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="findbestnew">-</a>
+
+ qh_findbestnew(qh, point, startfacet, dist, isoutside, numpart )
+ find best newfacet for point
+ searches all of qh.newfacet_list starting at startfacet
+ searches horizon facets of coplanar best newfacets
+ searches all facets if startfacet == qh.facet_list
+ returns:
+ best new or horizon facet that is not upperdelaunay
+ early out if isoutside and not 'Qf'
+ dist is distance to facet
+ isoutside is true if point is outside of facet
+ numpart is number of distance tests
+
+ notes:
+ Always used for merged new facets (see qh_USEfindbestnew)
+ Avoids upperdelaunay facet unless (isoutside and outside)
+
+ Uses qh.visit_id, qh.coplanarfacetset.
+ If share visit_id with qh_findbest, coplanarfacetset is incorrect.
+
+ If merging (testhorizon), searches horizon facets of coplanar best facets because
+ a point maybe coplanar to the bestfacet, below its horizon facet,
+ and above a horizon facet of a coplanar newfacet. For example,
+ rbox 1000 s Z1 G1e-13 | qhull
+ rbox 1000 s W1e-13 P0 t992110337 | QHULL d Qbb Qc
+
+ qh_findbestnew() used if
+ qh_sharpnewfacets -- newfacets contains a sharp angle
+ if many merges, qh_premerge found a merge, or 'Qf' (qh.findbestnew)
+
+ see also:
+ qh_partitionall() and qh_findbest()
+
+ design:
+ for each new facet starting from startfacet
+ test distance from point to facet
+ return facet if clearly outside
+ unless upperdelaunay and a lowerdelaunay exists
+ update best facet
+ test horizon facets
+*/
+facetT *qh_findbestnew(qhT *qh, pointT *point, facetT *startfacet,
+ realT *dist, boolT bestoutside, boolT *isoutside, int *numpart) {
+ realT bestdist= -REALmax/2;
+ facetT *bestfacet= NULL, *facet;
+ int oldtrace= qh->IStracing, i;
+ unsigned int visitid= ++qh->visit_id;
+ realT distoutside= 0.0;
+ boolT isdistoutside; /* True if distoutside is defined */
+ boolT testhorizon = True; /* needed if precise, e.g., rbox c D6 | qhull Q0 Tv */
+
+ if (!startfacet) {
+ if (qh->MERGING)
+ qh_fprintf(qh, qh->ferr, 6001, "qhull precision error (qh_findbestnew): merging has formed and deleted a cone of new facets. Can not continue.\n");
+ else
+ qh_fprintf(qh, qh->ferr, 6002, "qhull internal error (qh_findbestnew): no new facets for point p%d\n",
+ qh->furthest_id);
+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+ }
+ zinc_(Zfindnew);
+ if (qh->BESToutside || bestoutside)
+ isdistoutside= False;
+ else {
+ isdistoutside= True;
+ distoutside= qh_DISToutside; /* multiple of qh.MINoutside & qh.max_outside, see user.h */
+ }
+ if (isoutside)
+ *isoutside= True;
+ *numpart= 0;
+ if (qh->IStracing >= 3 || (qh->TRACElevel && qh->TRACEpoint >= 0 && qh->TRACEpoint == qh_pointid(qh, point))) {
+ if (qh->TRACElevel > qh->IStracing)
+ qh->IStracing= qh->TRACElevel;
+ qh_fprintf(qh, qh->ferr, 8008, "qh_findbestnew: point p%d facet f%d. Stop? %d if dist > %2.2g\n",
+ qh_pointid(qh, point), startfacet->id, isdistoutside, distoutside);
+ qh_fprintf(qh, qh->ferr, 8009, " Last point added p%d visitid %d.", qh->furthest_id, visitid);
+ qh_fprintf(qh, qh->ferr, 8010, " Last merge was #%d.\n", zzval_(Ztotmerge));
+ }
+ /* visit all new facets starting with startfacet, maybe qh->facet_list */
+ for (i=0, facet=startfacet; i < 2; i++, facet= qh->newfacet_list) {
+ FORALLfacet_(facet) {
+ if (facet == startfacet && i)
+ break;
+ facet->visitid= visitid;
+ if (!facet->flipped) {
+ qh_distplane(qh, point, facet, dist);
+ (*numpart)++;
+ if (*dist > bestdist) {
+ if (!facet->upperdelaunay || *dist >= qh->MINoutside) {
+ bestfacet= facet;
+ if (isdistoutside && *dist >= distoutside)
+ goto LABELreturn_bestnew;
+ bestdist= *dist;
+ }
+ }
+ } /* end of !flipped */
+ } /* FORALLfacet from startfacet or qh->newfacet_list */
+ }
+ if (testhorizon || !bestfacet) /* testhorizon is always True. Keep the same code as qh_findbest */
+ bestfacet= qh_findbesthorizon(qh, !qh_IScheckmax, point, bestfacet ? bestfacet : startfacet,
+ !qh_NOupper, &bestdist, numpart);
+ *dist= bestdist;
+ if (isoutside && *dist < qh->MINoutside)
+ *isoutside= False;
+LABELreturn_bestnew:
+ zadd_(Zfindnewtot, *numpart);
+ zmax_(Zfindnewmax, *numpart);
+ trace4((qh, qh->ferr, 4004, "qh_findbestnew: bestfacet f%d bestdist %2.2g\n", getid_(bestfacet), *dist));
+ qh->IStracing= oldtrace;
+ return bestfacet;
+} /* findbestnew */
+
+/* ============ hyperplane functions -- keep code together [?] ============ */
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="backnormal">-</a>
+
+ qh_backnormal(qh, rows, numrow, numcol, sign, normal, nearzero )
+ given an upper-triangular rows array and a sign,
+ solve for normal equation x using back substitution over rows U
+
+ returns:
+ normal= x
+
+ if will not be able to divzero() when normalized(qh.MINdenom_2 and qh.MINdenom_1_2),
+ if fails on last row
+ this means that the hyperplane intersects [0,..,1]
+ sets last coordinate of normal to sign
+ otherwise
+ sets tail of normal to [...,sign,0,...], i.e., solves for b= [0...0]
+ sets nearzero
+
+ notes:
+ assumes numrow == numcol-1
+
+ see Golub & van Loan, 1983, Eq. 4.4-9 for "Gaussian elimination with complete pivoting"
+
+ solves Ux=b where Ax=b and PA=LU
+ b= [0,...,0,sign or 0] (sign is either -1 or +1)
+ last row of A= [0,...,0,1]
+
+ 1) Ly=Pb == y=b since P only permutes the 0's of b
+
+ design:
+ for each row from end
+ perform back substitution
+ if near zero
+ use qh_divzero for division
+ if zero divide and not last row
+ set tail of normal to 0
+*/
+void qh_backnormal(qhT *qh, realT **rows, int numrow, int numcol, boolT sign,
+ coordT *normal, boolT *nearzero) {
+ int i, j;
+ coordT *normalp, *normal_tail, *ai, *ak;
+ realT diagonal;
+ boolT waszero;
+ int zerocol= -1;
+
+ normalp= normal + numcol - 1;
+ *normalp--= (sign ? -1.0 : 1.0);
+ for (i=numrow; i--; ) {
+ *normalp= 0.0;
+ ai= rows[i] + i + 1;
+ ak= normalp+1;
+ for (j=i+1; j < numcol; j++)
+ *normalp -= *ai++ * *ak++;
+ diagonal= (rows[i])[i];
+ if (fabs_(diagonal) > qh->MINdenom_2)
+ *(normalp--) /= diagonal;
+ else {
+ waszero= False;
+ *normalp= qh_divzero(*normalp, diagonal, qh->MINdenom_1_2, &waszero);
+ if (waszero) {
+ zerocol= i;
+ *(normalp--)= (sign ? -1.0 : 1.0);
+ for (normal_tail= normalp+2; normal_tail < normal + numcol; normal_tail++)
+ *normal_tail= 0.0;
+ }else
+ normalp--;
+ }
+ }
+ if (zerocol != -1) {
+ zzinc_(Zback0);
+ *nearzero= True;
+ trace4((qh, qh->ferr, 4005, "qh_backnormal: zero diagonal at column %d.\n", i));
+ qh_precision(qh, "zero diagonal on back substitution");
+ }
+} /* backnormal */
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="gausselim">-</a>
+
+ qh_gausselim(qh, rows, numrow, numcol, sign )
+ Gaussian elimination with partial pivoting
+
+ returns:
+ rows is upper triangular (includes row exchanges)
+ flips sign for each row exchange
+ sets nearzero if pivot[k] < qh.NEARzero[k], else clears it
+
+ notes:
+ if nearzero, the determinant's sign may be incorrect.
+ assumes numrow <= numcol
+
+ design:
+ for each row
+ determine pivot and exchange rows if necessary
+ test for near zero
+ perform gaussian elimination step
+*/
+void qh_gausselim(qhT *qh, realT **rows, int numrow, int numcol, boolT *sign, boolT *nearzero) {
+ realT *ai, *ak, *rowp, *pivotrow;
+ realT n, pivot, pivot_abs= 0.0, temp;
+ int i, j, k, pivoti, flip=0;
+
+ *nearzero= False;
+ for (k=0; k < numrow; k++) {
+ pivot_abs= fabs_((rows[k])[k]);
+ pivoti= k;
+ for (i=k+1; i < numrow; i++) {
+ if ((temp= fabs_((rows[i])[k])) > pivot_abs) {
+ pivot_abs= temp;
+ pivoti= i;
+ }
+ }
+ if (pivoti != k) {
+ rowp= rows[pivoti];
+ rows[pivoti]= rows[k];
+ rows[k]= rowp;
+ *sign ^= 1;
+ flip ^= 1;
+ }
+ if (pivot_abs <= qh->NEARzero[k]) {
+ *nearzero= True;
+ if (pivot_abs == 0.0) { /* remainder of column == 0 */
+ if (qh->IStracing >= 4) {
+ qh_fprintf(qh, qh->ferr, 8011, "qh_gausselim: 0 pivot at column %d. (%2.2g < %2.2g)\n", k, pivot_abs, qh->DISTround);
+ qh_printmatrix(qh, qh->ferr, "Matrix:", rows, numrow, numcol);
+ }
+ zzinc_(Zgauss0);
+ qh_precision(qh, "zero pivot for Gaussian elimination");
+ goto LABELnextcol;
+ }
+ }
+ pivotrow= rows[k] + k;
+ pivot= *pivotrow++; /* signed value of pivot, and remainder of row */
+ for (i=k+1; i < numrow; i++) {
+ ai= rows[i] + k;
+ ak= pivotrow;
+ n= (*ai++)/pivot; /* divzero() not needed since |pivot| >= |*ai| */
+ for (j= numcol - (k+1); j--; )
+ *ai++ -= n * *ak++;
+ }
+ LABELnextcol:
+ ;
+ }
+ wmin_(Wmindenom, pivot_abs); /* last pivot element */
+ if (qh->IStracing >= 5)
+ qh_printmatrix(qh, qh->ferr, "qh_gausselem: result", rows, numrow, numcol);
+} /* gausselim */
+
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="getangle">-</a>
+
+ qh_getangle(qh, vect1, vect2 )
+ returns the dot product of two vectors
+ if qh.RANDOMdist, joggles result
+
+ notes:
+ the angle may be > 1.0 or < -1.0 because of roundoff errors
+
+*/
+realT qh_getangle(qhT *qh, pointT *vect1, pointT *vect2) {
+ realT angle= 0, randr;
+ int k;
+
+ for (k=qh->hull_dim; k--; )
+ angle += *vect1++ * *vect2++;
+ if (qh->RANDOMdist) {
+ randr= qh_RANDOMint;
+ angle += (2.0 * randr / qh_RANDOMmax - 1.0) *
+ qh->RANDOMfactor;
+ }
+ trace4((qh, qh->ferr, 4006, "qh_getangle: %2.2g\n", angle));
+ return(angle);
+} /* getangle */
+
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="getcenter">-</a>
+
+ qh_getcenter(qh, vertices )
+ returns arithmetic center of a set of vertices as a new point
+
+ notes:
+ allocates point array for center
+*/
+pointT *qh_getcenter(qhT *qh, setT *vertices) {
+ int k;
+ pointT *center, *coord;
+ vertexT *vertex, **vertexp;
+ int count= qh_setsize(qh, vertices);
+
+ if (count < 2) {
+ qh_fprintf(qh, qh->ferr, 6003, "qhull internal error (qh_getcenter): not defined for %d points\n", count);
+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+ }
+ center= (pointT *)qh_memalloc(qh, qh->normal_size);
+ for (k=0; k < qh->hull_dim; k++) {
+ coord= center+k;
+ *coord= 0.0;
+ FOREACHvertex_(vertices)
+ *coord += vertex->point[k];
+ *coord /= count; /* count>=2 by QH6003 */
+ }
+ return(center);
+} /* getcenter */
+
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="getcentrum">-</a>
+
+ qh_getcentrum(qh, facet )
+ returns the centrum for a facet as a new point
+
+ notes:
+ allocates the centrum
+*/
+pointT *qh_getcentrum(qhT *qh, facetT *facet) {
+ realT dist;
+ pointT *centrum, *point;
+
+ point= qh_getcenter(qh, facet->vertices);
+ zzinc_(Zcentrumtests);
+ qh_distplane(qh, point, facet, &dist);
+ centrum= qh_projectpoint(qh, point, facet, dist);
+ qh_memfree(qh, point, qh->normal_size);
+ trace4((qh, qh->ferr, 4007, "qh_getcentrum: for f%d, %d vertices dist= %2.2g\n",
+ facet->id, qh_setsize(qh, facet->vertices), dist));
+ return centrum;
+} /* getcentrum */
+
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="getdistance">-</a>
+
+ qh_getdistance(qh, facet, neighbor, mindist, maxdist )
+ returns the maxdist and mindist distance of any vertex from neighbor
+
+ returns:
+ the max absolute value
+
+ design:
+ for each vertex of facet that is not in neighbor
+ test the distance from vertex to neighbor
+*/
+realT qh_getdistance(qhT *qh, facetT *facet, facetT *neighbor, realT *mindist, realT *maxdist) {
+ vertexT *vertex, **vertexp;
+ realT dist, maxd, mind;
+
+ FOREACHvertex_(facet->vertices)
+ vertex->seen= False;
+ FOREACHvertex_(neighbor->vertices)
+ vertex->seen= True;
+ mind= 0.0;
+ maxd= 0.0;
+ FOREACHvertex_(facet->vertices) {
+ if (!vertex->seen) {
+ zzinc_(Zbestdist);
+ qh_distplane(qh, vertex->point, neighbor, &dist);
+ if (dist < mind)
+ mind= dist;
+ else if (dist > maxd)
+ maxd= dist;
+ }
+ }
+ *mindist= mind;
+ *maxdist= maxd;
+ mind= -mind;
+ if (maxd > mind)
+ return maxd;
+ else
+ return mind;
+} /* getdistance */
+
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="normalize">-</a>
+
+ qh_normalize(qh, normal, dim, toporient )
+ normalize a vector and report if too small
+ does not use min norm
+
+ see:
+ qh_normalize2
+*/
+void qh_normalize(qhT *qh, coordT *normal, int dim, boolT toporient) {
+ qh_normalize2(qh, normal, dim, toporient, NULL, NULL);
+} /* normalize */
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="normalize2">-</a>
+
+ qh_normalize2(qh, normal, dim, toporient, minnorm, ismin )
+ normalize a vector and report if too small
+ qh.MINdenom/MINdenom1 are the upper limits for divide overflow
+
+ returns:
+ normalized vector
+ flips sign if !toporient
+ if minnorm non-NULL,
+ sets ismin if normal < minnorm
+
+ notes:
+ if zero norm
+ sets all elements to sqrt(1.0/dim)
+ if divide by zero (divzero())
+ sets largest element to +/-1
+ bumps Znearlysingular
+
+ design:
+ computes norm
+ test for minnorm
+ if not near zero
+ normalizes normal
+ else if zero norm
+ sets normal to standard value
+ else
+ uses qh_divzero to normalize
+ if nearzero
+ sets norm to direction of maximum value
+*/
+void qh_normalize2(qhT *qh, coordT *normal, int dim, boolT toporient,
+ realT *minnorm, boolT *ismin) {
+ int k;
+ realT *colp, *maxp, norm= 0, temp, *norm1, *norm2, *norm3;
+ boolT zerodiv;
+
+ norm1= normal+1;
+ norm2= normal+2;
+ norm3= normal+3;
+ if (dim == 2)
+ norm= sqrt((*normal)*(*normal) + (*norm1)*(*norm1));
+ else if (dim == 3)
+ norm= sqrt((*normal)*(*normal) + (*norm1)*(*norm1) + (*norm2)*(*norm2));
+ else if (dim == 4) {
+ norm= sqrt((*normal)*(*normal) + (*norm1)*(*norm1) + (*norm2)*(*norm2)
+ + (*norm3)*(*norm3));
+ }else if (dim > 4) {
+ norm= (*normal)*(*normal) + (*norm1)*(*norm1) + (*norm2)*(*norm2)
+ + (*norm3)*(*norm3);
+ for (k=dim-4, colp=normal+4; k--; colp++)
+ norm += (*colp) * (*colp);
+ norm= sqrt(norm);
+ }
+ if (minnorm) {
+ if (norm < *minnorm)
+ *ismin= True;
+ else
+ *ismin= False;
+ }
+ wmin_(Wmindenom, norm);
+ if (norm > qh->MINdenom) {
+ if (!toporient)
+ norm= -norm;
+ *normal /= norm;
+ *norm1 /= norm;
+ if (dim == 2)
+ ; /* all done */
+ else if (dim == 3)
+ *norm2 /= norm;
+ else if (dim == 4) {
+ *norm2 /= norm;
+ *norm3 /= norm;
+ }else if (dim >4) {
+ *norm2 /= norm;
+ *norm3 /= norm;
+ for (k=dim-4, colp=normal+4; k--; )
+ *colp++ /= norm;
+ }
+ }else if (norm == 0.0) {
+ temp= sqrt(1.0/dim);
+ for (k=dim, colp=normal; k--; )
+ *colp++ = temp;
+ }else {
+ if (!toporient)
+ norm= -norm;
+ for (k=dim, colp=normal; k--; colp++) { /* k used below */
+ temp= qh_divzero(*colp, norm, qh->MINdenom_1, &zerodiv);
+ if (!zerodiv)
+ *colp= temp;
+ else {
+ maxp= qh_maxabsval(normal, dim);
+ temp= ((*maxp * norm >= 0.0) ? 1.0 : -1.0);
+ for (k=dim, colp=normal; k--; colp++)
+ *colp= 0.0;
+ *maxp= temp;
+ zzinc_(Znearlysingular);
+ trace0((qh, qh->ferr, 1, "qh_normalize: norm=%2.2g too small during p%d\n",
+ norm, qh->furthest_id));
+ return;
+ }
+ }
+ }
+} /* normalize */
+
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="projectpoint">-</a>
+
+ qh_projectpoint(qh, point, facet, dist )
+ project point onto a facet by dist
+
+ returns:
+ returns a new point
+
+ notes:
+ if dist= distplane(point,facet)
+ this projects point to hyperplane
+ assumes qh_memfree_() is valid for normal_size
+*/
+pointT *qh_projectpoint(qhT *qh, pointT *point, facetT *facet, realT dist) {
+ pointT *newpoint, *np, *normal;
+ int normsize= qh->normal_size;
+ int k;
+ void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */
+
+ qh_memalloc_(qh, normsize, freelistp, newpoint, pointT);
+ np= newpoint;
+ normal= facet->normal;
+ for (k=qh->hull_dim; k--; )
+ *(np++)= *point++ - dist * *normal++;
+ return(newpoint);
+} /* projectpoint */
+
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="setfacetplane">-</a>
+
+ qh_setfacetplane(qh, facet )
+ sets the hyperplane for a facet
+ if qh.RANDOMdist, joggles hyperplane
+
+ notes:
+ uses global buffers qh.gm_matrix and qh.gm_row
+ overwrites facet->normal if already defined
+ updates Wnewvertex if PRINTstatistics
+ sets facet->upperdelaunay if upper envelope of Delaunay triangulation
+
+ design:
+ copy vertex coordinates to qh.gm_matrix/gm_row
+ compute determinate
+ if nearzero
+ recompute determinate with gaussian elimination
+ if nearzero
+ force outside orientation by testing interior point
+*/
+void qh_setfacetplane(qhT *qh, facetT *facet) {
+ pointT *point;
+ vertexT *vertex, **vertexp;
+ int normsize= qh->normal_size;
+ int k,i, oldtrace= 0;
+ realT dist;
+ void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */
+ coordT *coord, *gmcoord;
+ pointT *point0= SETfirstt_(facet->vertices, vertexT)->point;
+ boolT nearzero= False;
+
+ zzinc_(Zsetplane);
+ if (!facet->normal)
+ qh_memalloc_(qh, normsize, freelistp, facet->normal, coordT);
+ if (facet == qh->tracefacet) {
+ oldtrace= qh->IStracing;
+ qh->IStracing= 5;
+ qh_fprintf(qh, qh->ferr, 8012, "qh_setfacetplane: facet f%d created.\n", facet->id);
+ qh_fprintf(qh, qh->ferr, 8013, " Last point added to hull was p%d.", qh->furthest_id);
+ if (zzval_(Ztotmerge))
+ qh_fprintf(qh, qh->ferr, 8014, " Last merge was #%d.", zzval_(Ztotmerge));
+ qh_fprintf(qh, qh->ferr, 8015, "\n\nCurrent summary is:\n");
+ qh_printsummary(qh, qh->ferr);
+ }
+ if (qh->hull_dim <= 4) {
+ i= 0;
+ if (qh->RANDOMdist) {
+ gmcoord= qh->gm_matrix;
+ FOREACHvertex_(facet->vertices) {
+ qh->gm_row[i++]= gmcoord;
+ coord= vertex->point;
+ for (k=qh->hull_dim; k--; )
+ *(gmcoord++)= *coord++ * qh_randomfactor(qh, qh->RANDOMa, qh->RANDOMb);
+ }
+ }else {
+ FOREACHvertex_(facet->vertices)
+ qh->gm_row[i++]= vertex->point;
+ }
+ qh_sethyperplane_det(qh, qh->hull_dim, qh->gm_row, point0, facet->toporient,
+ facet->normal, &facet->offset, &nearzero);
+ }
+ if (qh->hull_dim > 4 || nearzero) {
+ i= 0;
+ gmcoord= qh->gm_matrix;
+ FOREACHvertex_(facet->vertices) {
+ if (vertex->point != point0) {
+ qh->gm_row[i++]= gmcoord;
+ coord= vertex->point;
+ point= point0;
+ for (k=qh->hull_dim; k--; )
+ *(gmcoord++)= *coord++ - *point++;
+ }
+ }
+ qh->gm_row[i]= gmcoord; /* for areasimplex */
+ if (qh->RANDOMdist) {
+ gmcoord= qh->gm_matrix;
+ for (i=qh->hull_dim-1; i--; ) {
+ for (k=qh->hull_dim; k--; )
+ *(gmcoord++) *= qh_randomfactor(qh, qh->RANDOMa, qh->RANDOMb);
+ }
+ }
+ qh_sethyperplane_gauss(qh, qh->hull_dim, qh->gm_row, point0, facet->toporient,
+ facet->normal, &facet->offset, &nearzero);
+ if (nearzero) {
+ if (qh_orientoutside(qh, facet)) {
+ trace0((qh, qh->ferr, 2, "qh_setfacetplane: flipped orientation after testing interior_point during p%d\n", qh->furthest_id));
+ /* this is part of using Gaussian Elimination. For example in 5-d
+ 1 1 1 1 0
+ 1 1 1 1 1
+ 0 0 0 1 0
+ 0 1 0 0 0
+ 1 0 0 0 0
+ norm= 0.38 0.38 -0.76 0.38 0
+ has a determinate of 1, but g.e. after subtracting pt. 0 has
+ 0's in the diagonal, even with full pivoting. It does work
+ if you subtract pt. 4 instead. */
+ }
+ }
+ }
+ facet->upperdelaunay= False;
+ if (qh->DELAUNAY) {
+ if (qh->UPPERdelaunay) { /* matches qh_triangulate_facet and qh.lower_threshold in qh_initbuild */
+ if (facet->normal[qh->hull_dim -1] >= qh->ANGLEround * qh_ZEROdelaunay)
+ facet->upperdelaunay= True;
+ }else {
+ if (facet->normal[qh->hull_dim -1] > -qh->ANGLEround * qh_ZEROdelaunay)
+ facet->upperdelaunay= True;
+ }
+ }
+ if (qh->PRINTstatistics || qh->IStracing || qh->TRACElevel || qh->JOGGLEmax < REALmax) {
+ qh->old_randomdist= qh->RANDOMdist;
+ qh->RANDOMdist= False;
+ FOREACHvertex_(facet->vertices) {
+ if (vertex->point != point0) {
+ boolT istrace= False;
+ zinc_(Zdiststat);
+ qh_distplane(qh, vertex->point, facet, &dist);
+ dist= fabs_(dist);
+ zinc_(Znewvertex);
+ wadd_(Wnewvertex, dist);
+ if (dist > wwval_(Wnewvertexmax)) {
+ wwval_(Wnewvertexmax)= dist;
+ if (dist > qh->max_outside) {
+ qh->max_outside= dist; /* used by qh_maxouter(qh) */
+ if (dist > qh->TRACEdist)
+ istrace= True;
+ }
+ }else if (-dist > qh->TRACEdist)
+ istrace= True;
+ if (istrace) {
+ qh_fprintf(qh, qh->ferr, 8016, "qh_setfacetplane: ====== vertex p%d(v%d) increases max_outside to %2.2g for new facet f%d last p%d\n",
+ qh_pointid(qh, vertex->point), vertex->id, dist, facet->id, qh->furthest_id);
+ qh_errprint(qh, "DISTANT", facet, NULL, NULL, NULL);
+ }
+ }
+ }
+ qh->RANDOMdist= qh->old_randomdist;
+ }
+ if (qh->IStracing >= 3) {
+ qh_fprintf(qh, qh->ferr, 8017, "qh_setfacetplane: f%d offset %2.2g normal: ",
+ facet->id, facet->offset);
+ for (k=0; k < qh->hull_dim; k++)
+ qh_fprintf(qh, qh->ferr, 8018, "%2.2g ", facet->normal[k]);
+ qh_fprintf(qh, qh->ferr, 8019, "\n");
+ }
+ if (facet == qh->tracefacet)
+ qh->IStracing= oldtrace;
+} /* setfacetplane */
+
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="sethyperplane_det">-</a>
+
+ qh_sethyperplane_det(qh, dim, rows, point0, toporient, normal, offset, nearzero )
+ given dim X dim array indexed by rows[], one row per point,
+ toporient(flips all signs),
+ and point0 (any row)
+ set normalized hyperplane equation from oriented simplex
+
+ returns:
+ normal (normalized)
+ offset (places point0 on the hyperplane)
+ sets nearzero if hyperplane not through points
+
+ notes:
+ only defined for dim == 2..4
+ rows[] is not modified
+ solves det(P-V_0, V_n-V_0, ..., V_1-V_0)=0, i.e. every point is on hyperplane
+ see Bower & Woodworth, A programmer's geometry, Butterworths 1983.
+
+ derivation of 3-d minnorm
+ Goal: all vertices V_i within qh.one_merge of hyperplane
+ Plan: exactly translate the facet so that V_0 is the origin
+ exactly rotate the facet so that V_1 is on the x-axis and y_2=0.
+ exactly rotate the effective perturbation to only effect n_0
+ this introduces a factor of sqrt(3)
+ n_0 = ((y_2-y_0)*(z_1-z_0) - (z_2-z_0)*(y_1-y_0)) / norm
+ Let M_d be the max coordinate difference
+ Let M_a be the greater of M_d and the max abs. coordinate
+ Let u be machine roundoff and distround be max error for distance computation
+ The max error for n_0 is sqrt(3) u M_a M_d / norm. n_1 is approx. 1 and n_2 is approx. 0
+ The max error for distance of V_1 is sqrt(3) u M_a M_d M_d / norm. Offset=0 at origin
+ Then minnorm = 1.8 u M_a M_d M_d / qh.ONEmerge
+ Note that qh.one_merge is approx. 45.5 u M_a and norm is usually about M_d M_d
+
+ derivation of 4-d minnorm
+ same as above except rotate the facet so that V_1 on x-axis and w_2, y_3, w_3=0
+ [if two vertices fixed on x-axis, can rotate the other two in yzw.]
+ n_0 = det3_(...) = y_2 det2_(z_1, w_1, z_3, w_3) = - y_2 w_1 z_3
+ [all other terms contain at least two factors nearly zero.]
+ The max error for n_0 is sqrt(4) u M_a M_d M_d / norm
+ Then minnorm = 2 u M_a M_d M_d M_d / qh.ONEmerge
+ Note that qh.one_merge is approx. 82 u M_a and norm is usually about M_d M_d M_d
+*/
+void qh_sethyperplane_det(qhT *qh, int dim, coordT **rows, coordT *point0,
+ boolT toporient, coordT *normal, realT *offset, boolT *nearzero) {
+ realT maxround, dist;
+ int i;
+ pointT *point;
+
+
+ if (dim == 2) {
+ normal[0]= dY(1,0);
+ normal[1]= dX(0,1);
+ qh_normalize2(qh, normal, dim, toporient, NULL, NULL);
+ *offset= -(point0[0]*normal[0]+point0[1]*normal[1]);
+ *nearzero= False; /* since nearzero norm => incident points */
+ }else if (dim == 3) {
+ normal[0]= det2_(dY(2,0), dZ(2,0),
+ dY(1,0), dZ(1,0));
+ normal[1]= det2_(dX(1,0), dZ(1,0),
+ dX(2,0), dZ(2,0));
+ normal[2]= det2_(dX(2,0), dY(2,0),
+ dX(1,0), dY(1,0));
+ qh_normalize2(qh, normal, dim, toporient, NULL, NULL);
+ *offset= -(point0[0]*normal[0] + point0[1]*normal[1]
+ + point0[2]*normal[2]);
+ maxround= qh->DISTround;
+ for (i=dim; i--; ) {
+ point= rows[i];
+ if (point != point0) {
+ dist= *offset + (point[0]*normal[0] + point[1]*normal[1]
+ + point[2]*normal[2]);
+ if (dist > maxround || dist < -maxround) {
+ *nearzero= True;
+ break;
+ }
+ }
+ }
+ }else if (dim == 4) {
+ normal[0]= - det3_(dY(2,0), dZ(2,0), dW(2,0),
+ dY(1,0), dZ(1,0), dW(1,0),
+ dY(3,0), dZ(3,0), dW(3,0));
+ normal[1]= det3_(dX(2,0), dZ(2,0), dW(2,0),
+ dX(1,0), dZ(1,0), dW(1,0),
+ dX(3,0), dZ(3,0), dW(3,0));
+ normal[2]= - det3_(dX(2,0), dY(2,0), dW(2,0),
+ dX(1,0), dY(1,0), dW(1,0),
+ dX(3,0), dY(3,0), dW(3,0));
+ normal[3]= det3_(dX(2,0), dY(2,0), dZ(2,0),
+ dX(1,0), dY(1,0), dZ(1,0),
+ dX(3,0), dY(3,0), dZ(3,0));
+ qh_normalize2(qh, normal, dim, toporient, NULL, NULL);
+ *offset= -(point0[0]*normal[0] + point0[1]*normal[1]
+ + point0[2]*normal[2] + point0[3]*normal[3]);
+ maxround= qh->DISTround;
+ for (i=dim; i--; ) {
+ point= rows[i];
+ if (point != point0) {
+ dist= *offset + (point[0]*normal[0] + point[1]*normal[1]
+ + point[2]*normal[2] + point[3]*normal[3]);
+ if (dist > maxround || dist < -maxround) {
+ *nearzero= True;
+ break;
+ }
+ }
+ }
+ }
+ if (*nearzero) {
+ zzinc_(Zminnorm);
+ trace0((qh, qh->ferr, 3, "qh_sethyperplane_det: degenerate norm during p%d.\n", qh->furthest_id));
+ zzinc_(Znearlysingular);
+ }
+} /* sethyperplane_det */
+
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="sethyperplane_gauss">-</a>
+
+ qh_sethyperplane_gauss(qh, dim, rows, point0, toporient, normal, offset, nearzero )
+ given(dim-1) X dim array of rows[i]= V_{i+1} - V_0 (point0)
+ set normalized hyperplane equation from oriented simplex
+
+ returns:
+ normal (normalized)
+ offset (places point0 on the hyperplane)
+
+ notes:
+ if nearzero
+ orientation may be incorrect because of incorrect sign flips in gausselim
+ solves [V_n-V_0,...,V_1-V_0, 0 .. 0 1] * N == [0 .. 0 1]
+ or [V_n-V_0,...,V_1-V_0, 0 .. 0 1] * N == [0]
+ i.e., N is normal to the hyperplane, and the unnormalized
+ distance to [0 .. 1] is either 1 or 0
+
+ design:
+ perform gaussian elimination
+ flip sign for negative values
+ perform back substitution
+ normalize result
+ compute offset
+*/
+void qh_sethyperplane_gauss(qhT *qh, int dim, coordT **rows, pointT *point0,
+ boolT toporient, coordT *normal, coordT *offset, boolT *nearzero) {
+ coordT *pointcoord, *normalcoef;
+ int k;
+ boolT sign= toporient, nearzero2= False;
+
+ qh_gausselim(qh, rows, dim-1, dim, &sign, nearzero);
+ for (k=dim-1; k--; ) {
+ if ((rows[k])[k] < 0)
+ sign ^= 1;
+ }
+ if (*nearzero) {
+ zzinc_(Znearlysingular);
+ trace0((qh, qh->ferr, 4, "qh_sethyperplane_gauss: nearly singular or axis parallel hyperplane during p%d.\n", qh->furthest_id));
+ qh_backnormal(qh, rows, dim-1, dim, sign, normal, &nearzero2);
+ }else {
+ qh_backnormal(qh, rows, dim-1, dim, sign, normal, &nearzero2);
+ if (nearzero2) {
+ zzinc_(Znearlysingular);
+ trace0((qh, qh->ferr, 5, "qh_sethyperplane_gauss: singular or axis parallel hyperplane at normalization during p%d.\n", qh->furthest_id));
+ }
+ }
+ if (nearzero2)
+ *nearzero= True;
+ qh_normalize2(qh, normal, dim, True, NULL, NULL);
+ pointcoord= point0;
+ normalcoef= normal;
+ *offset= -(*pointcoord++ * *normalcoef++);
+ for (k=dim-1; k--; )
+ *offset -= *pointcoord++ * *normalcoef++;
+} /* sethyperplane_gauss */
+
+
+
diff --git a/xs/src/qhull/src/libqhull_r/geom_r.h b/xs/src/qhull/src/libqhull_r/geom_r.h
new file mode 100644
index 000000000..d73e95345
--- /dev/null
+++ b/xs/src/qhull/src/libqhull_r/geom_r.h
@@ -0,0 +1,184 @@
+/*<html><pre> -<a href="qh-geom_r.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ geom_r.h
+ header file for geometric routines
+
+ see qh-geom_r.htm and geom_r.c
+
+ Copyright (c) 1993-2015 The Geometry Center.
+ $Id: //main/2015/qhull/src/libqhull_r/geom_r.h#3 $$Change: 2079 $
+ $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
+*/
+
+#ifndef qhDEFgeom
+#define qhDEFgeom 1
+
+#include "libqhull_r.h"
+
+/* ============ -macros- ======================== */
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >--------------------------------</a><a name="fabs_">-</a>
+
+ fabs_(a)
+ returns the absolute value of a
+*/
+#define fabs_( a ) ((( a ) < 0 ) ? -( a ):( a ))
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >--------------------------------</a><a name="fmax_">-</a>
+
+ fmax_(a,b)
+ returns the maximum value of a and b
+*/
+#define fmax_( a,b ) ( ( a ) < ( b ) ? ( b ) : ( a ) )
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >--------------------------------</a><a name="fmin_">-</a>
+
+ fmin_(a,b)
+ returns the minimum value of a and b
+*/
+#define fmin_( a,b ) ( ( a ) > ( b ) ? ( b ) : ( a ) )
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >--------------------------------</a><a name="maximize_">-</a>
+
+ maximize_(maxval, val)
+ set maxval to val if val is greater than maxval
+*/
+#define maximize_( maxval, val ) { if (( maxval ) < ( val )) ( maxval )= ( val ); }
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >--------------------------------</a><a name="minimize_">-</a>
+
+ minimize_(minval, val)
+ set minval to val if val is less than minval
+*/
+#define minimize_( minval, val ) { if (( minval ) > ( val )) ( minval )= ( val ); }
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >--------------------------------</a><a name="det2_">-</a>
+
+ det2_(a1, a2,
+ b1, b2)
+
+ compute a 2-d determinate
+*/
+#define det2_( a1,a2,b1,b2 ) (( a1 )*( b2 ) - ( a2 )*( b1 ))
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >--------------------------------</a><a name="det3_">-</a>
+
+ det3_(a1, a2, a3,
+ b1, b2, b3,
+ c1, c2, c3)
+
+ compute a 3-d determinate
+*/
+#define det3_( a1,a2,a3,b1,b2,b3,c1,c2,c3 ) ( ( a1 )*det2_( b2,b3,c2,c3 ) \
+ - ( b1 )*det2_( a2,a3,c2,c3 ) + ( c1 )*det2_( a2,a3,b2,b3 ) )
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >--------------------------------</a><a name="dX">-</a>
+
+ dX( p1, p2 )
+ dY( p1, p2 )
+ dZ( p1, p2 )
+
+ given two indices into rows[],
+
+ compute the difference between X, Y, or Z coordinates
+*/
+#define dX( p1,p2 ) ( *( rows[p1] ) - *( rows[p2] ))
+#define dY( p1,p2 ) ( *( rows[p1]+1 ) - *( rows[p2]+1 ))
+#define dZ( p1,p2 ) ( *( rows[p1]+2 ) - *( rows[p2]+2 ))
+#define dW( p1,p2 ) ( *( rows[p1]+3 ) - *( rows[p2]+3 ))
+
+/*============= prototypes in alphabetical order, infrequent at end ======= */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void qh_backnormal(qhT *qh, realT **rows, int numrow, int numcol, boolT sign, coordT *normal, boolT *nearzero);
+void qh_distplane(qhT *qh, pointT *point, facetT *facet, realT *dist);
+facetT *qh_findbest(qhT *qh, pointT *point, facetT *startfacet,
+ boolT bestoutside, boolT isnewfacets, boolT noupper,
+ realT *dist, boolT *isoutside, int *numpart);
+facetT *qh_findbesthorizon(qhT *qh, boolT ischeckmax, pointT *point,
+ facetT *startfacet, boolT noupper, realT *bestdist, int *numpart);
+facetT *qh_findbestnew(qhT *qh, pointT *point, facetT *startfacet, realT *dist,
+ boolT bestoutside, boolT *isoutside, int *numpart);
+void qh_gausselim(qhT *qh, realT **rows, int numrow, int numcol, boolT *sign, boolT *nearzero);
+realT qh_getangle(qhT *qh, pointT *vect1, pointT *vect2);
+pointT *qh_getcenter(qhT *qh, setT *vertices);
+pointT *qh_getcentrum(qhT *qh, facetT *facet);
+realT qh_getdistance(qhT *qh, facetT *facet, facetT *neighbor, realT *mindist, realT *maxdist);
+void qh_normalize(qhT *qh, coordT *normal, int dim, boolT toporient);
+void qh_normalize2(qhT *qh, coordT *normal, int dim, boolT toporient,
+ realT *minnorm, boolT *ismin);
+pointT *qh_projectpoint(qhT *qh, pointT *point, facetT *facet, realT dist);
+
+void qh_setfacetplane(qhT *qh, facetT *newfacets);
+void qh_sethyperplane_det(qhT *qh, int dim, coordT **rows, coordT *point0,
+ boolT toporient, coordT *normal, realT *offset, boolT *nearzero);
+void qh_sethyperplane_gauss(qhT *qh, int dim, coordT **rows, pointT *point0,
+ boolT toporient, coordT *normal, coordT *offset, boolT *nearzero);
+boolT qh_sharpnewfacets(qhT *qh);
+
+/*========= infrequently used code in geom2_r.c =============*/
+
+coordT *qh_copypoints(qhT *qh, coordT *points, int numpoints, int dimension);
+void qh_crossproduct(int dim, realT vecA[3], realT vecB[3], realT vecC[3]);
+realT qh_determinant(qhT *qh, realT **rows, int dim, boolT *nearzero);
+realT qh_detjoggle(qhT *qh, pointT *points, int numpoints, int dimension);
+void qh_detroundoff(qhT *qh);
+realT qh_detsimplex(qhT *qh, pointT *apex, setT *points, int dim, boolT *nearzero);
+realT qh_distnorm(int dim, pointT *point, pointT *normal, realT *offsetp);
+realT qh_distround(qhT *qh, int dimension, realT maxabs, realT maxsumabs);
+realT qh_divzero(realT numer, realT denom, realT mindenom1, boolT *zerodiv);
+realT qh_facetarea(qhT *qh, facetT *facet);
+realT qh_facetarea_simplex(qhT *qh, int dim, coordT *apex, setT *vertices,
+ vertexT *notvertex, boolT toporient, coordT *normal, realT *offset);
+pointT *qh_facetcenter(qhT *qh, setT *vertices);
+facetT *qh_findgooddist(qhT *qh, pointT *point, facetT *facetA, realT *distp, facetT **facetlist);
+void qh_getarea(qhT *qh, facetT *facetlist);
+boolT qh_gram_schmidt(qhT *qh, int dim, realT **rows);
+boolT qh_inthresholds(qhT *qh, coordT *normal, realT *angle);
+void qh_joggleinput(qhT *qh);
+realT *qh_maxabsval(realT *normal, int dim);
+setT *qh_maxmin(qhT *qh, pointT *points, int numpoints, int dimension);
+realT qh_maxouter(qhT *qh);
+void qh_maxsimplex(qhT *qh, int dim, setT *maxpoints, pointT *points, int numpoints, setT **simplex);
+realT qh_minabsval(realT *normal, int dim);
+int qh_mindiff(realT *vecA, realT *vecB, int dim);
+boolT qh_orientoutside(qhT *qh, facetT *facet);
+void qh_outerinner(qhT *qh, facetT *facet, realT *outerplane, realT *innerplane);
+coordT qh_pointdist(pointT *point1, pointT *point2, int dim);
+void qh_printmatrix(qhT *qh, FILE *fp, const char *string, realT **rows, int numrow, int numcol);
+void qh_printpoints(qhT *qh, FILE *fp, const char *string, setT *points);
+void qh_projectinput(qhT *qh);
+void qh_projectpoints(qhT *qh, signed char *project, int n, realT *points,
+ int numpoints, int dim, realT *newpoints, int newdim);
+void qh_rotateinput(qhT *qh, realT **rows);
+void qh_rotatepoints(qhT *qh, realT *points, int numpoints, int dim, realT **rows);
+void qh_scaleinput(qhT *qh);
+void qh_scalelast(qhT *qh, coordT *points, int numpoints, int dim, coordT low,
+ coordT high, coordT newhigh);
+void qh_scalepoints(qhT *qh, pointT *points, int numpoints, int dim,
+ realT *newlows, realT *newhighs);
+boolT qh_sethalfspace(qhT *qh, int dim, coordT *coords, coordT **nextp,
+ coordT *normal, coordT *offset, coordT *feasible);
+coordT *qh_sethalfspace_all(qhT *qh, int dim, int count, coordT *halfspaces, pointT *feasible);
+pointT *qh_voronoi_center(qhT *qh, int dim, setT *points);
+
+#ifdef __cplusplus
+} /* extern "C"*/
+#endif
+
+#endif /* qhDEFgeom */
+
+
+
diff --git a/xs/src/qhull/src/libqhull_r/global_r.c b/xs/src/qhull/src/libqhull_r/global_r.c
new file mode 100644
index 000000000..eef465ca1
--- /dev/null
+++ b/xs/src/qhull/src/libqhull_r/global_r.c
@@ -0,0 +1,2100 @@
+
+/*<html><pre> -<a href="qh-globa_r.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ global_r.c
+ initializes all the globals of the qhull application
+
+ see README
+
+ see libqhull_r.h for qh.globals and function prototypes
+
+ see qhull_ra.h for internal functions
+
+ Copyright (c) 1993-2015 The Geometry Center.
+ $Id: //main/2015/qhull/src/libqhull_r/global_r.c#16 $$Change: 2066 $
+ $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
+ */
+
+#include "qhull_ra.h"
+
+/*========= qh->definition -- globals defined in libqhull_r.h =======================*/
+
+/*-<a href ="qh-globa_r.htm#TOC"
+ >--------------------------------</a><a name="qh_version">-</a>
+
+ qh_version
+ version string by year and date
+ qh_version2 for Unix users and -V
+
+ the revision increases on code changes only
+
+ notes:
+ change date: Changes.txt, Announce.txt, index.htm, README.txt,
+ qhull-news.html, Eudora signatures, CMakeLists.txt
+ change version: README.txt, qh-get.htm, File_id.diz, Makefile.txt, CMakeLists.txt
+ check that CmakeLists @version is the same as qh_version2
+ change year: Copying.txt
+ check download size
+ recompile user_eg_r.c, rbox_r.c, libqhull_r.c, qconvex_r.c, qdelaun_r.c qvoronoi_r.c, qhalf_r.c, testqset_r.c
+*/
+
+const char qh_version[]= "2015.2.r 2016/01/18";
+const char qh_version2[]= "qhull_r 7.2.0 (2015.2.r 2016/01/18)";
+
+/*-<a href="qh-globa_r.htm#TOC"
+ >-------------------------------</a><a name="appendprint">-</a>
+
+ qh_appendprint(qh, printFormat )
+ append printFormat to qh.PRINTout unless already defined
+*/
+void qh_appendprint(qhT *qh, qh_PRINT format) {
+ int i;
+
+ for (i=0; i < qh_PRINTEND; i++) {
+ if (qh->PRINTout[i] == format && format != qh_PRINTqhull)
+ break;
+ if (!qh->PRINTout[i]) {
+ qh->PRINTout[i]= format;
+ break;
+ }
+ }
+} /* appendprint */
+
+/*-<a href="qh-globa_r.htm#TOC"
+ >-------------------------------</a><a name="checkflags">-</a>
+
+ qh_checkflags(qh, commandStr, hiddenFlags )
+ errors if commandStr contains hiddenFlags
+ hiddenFlags starts and ends with a space and is space delimited (checked)
+
+ notes:
+ ignores first word (e.g., "qconvex i")
+ use qh_strtol/strtod since strtol/strtod may or may not skip trailing spaces
+
+ see:
+ qh_initflags() initializes Qhull according to commandStr
+*/
+void qh_checkflags(qhT *qh, char *command, char *hiddenflags) {
+ char *s= command, *t, *chkerr; /* qh_skipfilename is non-const */
+ char key, opt, prevopt;
+ char chkkey[]= " ";
+ char chkopt[]= " ";
+ char chkopt2[]= " ";
+ boolT waserr= False;
+
+ if (*hiddenflags != ' ' || hiddenflags[strlen(hiddenflags)-1] != ' ') {
+ qh_fprintf(qh, qh->ferr, 6026, "qhull error (qh_checkflags): hiddenflags must start and end with a space: \"%s\"", hiddenflags);
+ qh_errexit(qh, qh_ERRinput, NULL, NULL);
+ }
+ if (strpbrk(hiddenflags, ",\n\r\t")) {
+ qh_fprintf(qh, qh->ferr, 6027, "qhull error (qh_checkflags): hiddenflags contains commas, newlines, or tabs: \"%s\"", hiddenflags);
+ qh_errexit(qh, qh_ERRinput, NULL, NULL);
+ }
+ while (*s && !isspace(*s)) /* skip program name */
+ s++;
+ while (*s) {
+ while (*s && isspace(*s))
+ s++;
+ if (*s == '-')
+ s++;
+ if (!*s)
+ break;
+ key = *s++;
+ chkerr = NULL;
+ if (key == 'T' && (*s == 'I' || *s == 'O')) { /* TI or TO 'file name' */
+ s= qh_skipfilename(qh, ++s);
+ continue;
+ }
+ chkkey[1]= key;
+ if (strstr(hiddenflags, chkkey)) {
+ chkerr= chkkey;
+ }else if (isupper(key)) {
+ opt= ' ';
+ prevopt= ' ';
+ chkopt[1]= key;
+ chkopt2[1]= key;
+ while (!chkerr && *s && !isspace(*s)) {
+ opt= *s++;
+ if (isalpha(opt)) {
+ chkopt[2]= opt;
+ if (strstr(hiddenflags, chkopt))
+ chkerr= chkopt;
+ if (prevopt != ' ') {
+ chkopt2[2]= prevopt;
+ chkopt2[3]= opt;
+ if (strstr(hiddenflags, chkopt2))
+ chkerr= chkopt2;
+ }
+ }else if (key == 'Q' && isdigit(opt) && prevopt != 'b'
+ && (prevopt == ' ' || islower(prevopt))) {
+ chkopt[2]= opt;
+ if (strstr(hiddenflags, chkopt))
+ chkerr= chkopt;
+ }else {
+ qh_strtod(s-1, &t);
+ if (s < t)
+ s= t;
+ }
+ prevopt= opt;
+ }
+ }
+ if (chkerr) {
+ *chkerr= '\'';
+ chkerr[strlen(chkerr)-1]= '\'';
+ qh_fprintf(qh, qh->ferr, 6029, "qhull error: option %s is not used with this program.\n It may be used with qhull.\n", chkerr);
+ waserr= True;
+ }
+ }
+ if (waserr)
+ qh_errexit(qh, qh_ERRinput, NULL, NULL);
+} /* checkflags */
+
+/*-<a href="qh-globa_r.htm#TOC"
+ >-------------------------------</a><a name="qh_clear_outputflags">-</a>
+
+ qh_clear_outputflags(qh)
+ Clear output flags for QhullPoints
+*/
+void qh_clear_outputflags(qhT *qh) {
+ int i,k;
+
+ qh->ANNOTATEoutput= False;
+ qh->DOintersections= False;
+ qh->DROPdim= -1;
+ qh->FORCEoutput= False;
+ qh->GETarea= False;
+ qh->GOODpoint= 0;
+ qh->GOODpointp= NULL;
+ qh->GOODthreshold= False;
+ qh->GOODvertex= 0;
+ qh->GOODvertexp= NULL;
+ qh->IStracing= 0;
+ qh->KEEParea= False;
+ qh->KEEPmerge= False;
+ qh->KEEPminArea= REALmax;
+ qh->PRINTcentrums= False;
+ qh->PRINTcoplanar= False;
+ qh->PRINTdots= False;
+ qh->PRINTgood= False;
+ qh->PRINTinner= False;
+ qh->PRINTneighbors= False;
+ qh->PRINTnoplanes= False;
+ qh->PRINToptions1st= False;
+ qh->PRINTouter= False;
+ qh->PRINTprecision= True;
+ qh->PRINTridges= False;
+ qh->PRINTspheres= False;
+ qh->PRINTstatistics= False;
+ qh->PRINTsummary= False;
+ qh->PRINTtransparent= False;
+ qh->SPLITthresholds= False;
+ qh->TRACElevel= 0;
+ qh->TRInormals= False;
+ qh->USEstdout= False;
+ qh->VERIFYoutput= False;
+ for (k=qh->input_dim+1; k--; ) { /* duplicated in qh_initqhull_buffers and qh_clear_outputflags */
+ qh->lower_threshold[k]= -REALmax;
+ qh->upper_threshold[k]= REALmax;
+ qh->lower_bound[k]= -REALmax;
+ qh->upper_bound[k]= REALmax;
+ }
+
+ for (i=0; i < qh_PRINTEND; i++) {
+ qh->PRINTout[i]= qh_PRINTnone;
+ }
+
+ if (!qh->qhull_commandsiz2)
+ qh->qhull_commandsiz2= (int)strlen(qh->qhull_command); /* WARN64 */
+ else {
+ qh->qhull_command[qh->qhull_commandsiz2]= '\0';
+ }
+ if (!qh->qhull_optionsiz2)
+ qh->qhull_optionsiz2= (int)strlen(qh->qhull_options); /* WARN64 */
+ else {
+ qh->qhull_options[qh->qhull_optionsiz2]= '\0';
+ qh->qhull_optionlen= qh_OPTIONline; /* start a new line */
+ }
+} /* clear_outputflags */
+
+/*-<a href="qh-globa_r.htm#TOC"
+ >-------------------------------</a><a name="clock">-</a>
+
+ qh_clock()
+ return user CPU time in 100ths (qh_SECtick)
+ only defined for qh_CLOCKtype == 2
+
+ notes:
+ use first value to determine time 0
+ from Stevens '92 8.15
+*/
+unsigned long qh_clock(qhT *qh) {
+
+#if (qh_CLOCKtype == 2)
+ struct tms time;
+ static long clktck; /* initialized first call and never updated */
+ double ratio, cpu;
+ unsigned long ticks;
+
+ if (!clktck) {
+ if ((clktck= sysconf(_SC_CLK_TCK)) < 0) {
+ qh_fprintf(qh, qh->ferr, 6030, "qhull internal error (qh_clock): sysconf() failed. Use qh_CLOCKtype 1 in user.h\n");
+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+ }
+ }
+ if (times(&time) == -1) {
+ qh_fprintf(qh, qh->ferr, 6031, "qhull internal error (qh_clock): times() failed. Use qh_CLOCKtype 1 in user.h\n");
+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+ }
+ ratio= qh_SECticks / (double)clktck;
+ ticks= time.tms_utime * ratio;
+ return ticks;
+#else
+ qh_fprintf(qh, qh->ferr, 6032, "qhull internal error (qh_clock): use qh_CLOCKtype 2 in user.h\n");
+ qh_errexit(qh, qh_ERRqhull, NULL, NULL); /* never returns */
+ return 0;
+#endif
+} /* clock */
+
+/*-<a href="qh-globa_r.htm#TOC"
+ >-------------------------------</a><a name="freebuffers">-</a>
+
+ qh_freebuffers()
+ free up global memory buffers
+
+ notes:
+ must match qh_initbuffers()
+*/
+void qh_freebuffers(qhT *qh) {
+
+ trace5((qh, qh->ferr, 5001, "qh_freebuffers: freeing up global memory buffers\n"));
+ /* allocated by qh_initqhull_buffers */
+ qh_memfree(qh, qh->NEARzero, qh->hull_dim * sizeof(realT));
+ qh_memfree(qh, qh->lower_threshold, (qh->input_dim+1) * sizeof(realT));
+ qh_memfree(qh, qh->upper_threshold, (qh->input_dim+1) * sizeof(realT));
+ qh_memfree(qh, qh->lower_bound, (qh->input_dim+1) * sizeof(realT));
+ qh_memfree(qh, qh->upper_bound, (qh->input_dim+1) * sizeof(realT));
+ qh_memfree(qh, qh->gm_matrix, (qh->hull_dim+1) * qh->hull_dim * sizeof(coordT));
+ qh_memfree(qh, qh->gm_row, (qh->hull_dim+1) * sizeof(coordT *));
+ qh->NEARzero= qh->lower_threshold= qh->upper_threshold= NULL;
+ qh->lower_bound= qh->upper_bound= NULL;
+ qh->gm_matrix= NULL;
+ qh->gm_row= NULL;
+ qh_setfree(qh, &qh->other_points);
+ qh_setfree(qh, &qh->del_vertices);
+ qh_setfree(qh, &qh->coplanarfacetset);
+ if (qh->line) /* allocated by qh_readinput, freed if no error */
+ qh_free(qh->line);
+ if (qh->half_space)
+ qh_free(qh->half_space);
+ if (qh->temp_malloc)
+ qh_free(qh->temp_malloc);
+ if (qh->feasible_point) /* allocated by qh_readfeasible */
+ qh_free(qh->feasible_point);
+ if (qh->feasible_string) /* allocated by qh_initflags */
+ qh_free(qh->feasible_string);
+ qh->line= qh->feasible_string= NULL;
+ qh->half_space= qh->feasible_point= qh->temp_malloc= NULL;
+ /* usually allocated by qh_readinput */
+ if (qh->first_point && qh->POINTSmalloc) {
+ qh_free(qh->first_point);
+ qh->first_point= NULL;
+ }
+ if (qh->input_points && qh->input_malloc) { /* set by qh_joggleinput */
+ qh_free(qh->input_points);
+ qh->input_points= NULL;
+ }
+ trace5((qh, qh->ferr, 5002, "qh_freebuffers: finished\n"));
+} /* freebuffers */
+
+
+/*-<a href="qh-globa_r.htm#TOC"
+ >-------------------------------</a><a name="freebuild">-</a>
+
+ qh_freebuild(qh, allmem )
+ free global memory used by qh_initbuild and qh_buildhull
+ if !allmem,
+ does not free short memory (e.g., facetT, freed by qh_memfreeshort)
+
+ design:
+ free centrums
+ free each vertex
+ mark unattached ridges
+ for each facet
+ free ridges
+ free outside set, coplanar set, neighbor set, ridge set, vertex set
+ free facet
+ free hash table
+ free interior point
+ free merge set
+ free temporary sets
+*/
+void qh_freebuild(qhT *qh, boolT allmem) {
+ facetT *facet;
+ vertexT *vertex;
+ ridgeT *ridge, **ridgep;
+ mergeT *merge, **mergep;
+
+ trace1((qh, qh->ferr, 1005, "qh_freebuild: free memory from qh_inithull and qh_buildhull\n"));
+ if (qh->del_vertices)
+ qh_settruncate(qh, qh->del_vertices, 0);
+ if (allmem) {
+ while ((vertex= qh->vertex_list)) {
+ if (vertex->next)
+ qh_delvertex(qh, vertex);
+ else {
+ qh_memfree(qh, vertex, (int)sizeof(vertexT));
+ qh->newvertex_list= qh->vertex_list= NULL;
+ }
+ }
+ }else if (qh->VERTEXneighbors) {
+ FORALLvertices
+ qh_setfreelong(qh, &(vertex->neighbors));
+ }
+ qh->VERTEXneighbors= False;
+ qh->GOODclosest= NULL;
+ if (allmem) {
+ FORALLfacets {
+ FOREACHridge_(facet->ridges)
+ ridge->seen= False;
+ }
+ FORALLfacets {
+ if (facet->visible) {
+ FOREACHridge_(facet->ridges) {
+ if (!otherfacet_(ridge, facet)->visible)
+ ridge->seen= True; /* an unattached ridge */
+ }
+ }
+ }
+ while ((facet= qh->facet_list)) {
+ FOREACHridge_(facet->ridges) {
+ if (ridge->seen) {
+ qh_setfree(qh, &(ridge->vertices));
+ qh_memfree(qh, ridge, (int)sizeof(ridgeT));
+ }else
+ ridge->seen= True;
+ }
+ qh_setfree(qh, &(facet->outsideset));
+ qh_setfree(qh, &(facet->coplanarset));
+ qh_setfree(qh, &(facet->neighbors));
+ qh_setfree(qh, &(facet->ridges));
+ qh_setfree(qh, &(facet->vertices));
+ if (facet->next)
+ qh_delfacet(qh, facet);
+ else {
+ qh_memfree(qh, facet, (int)sizeof(facetT));
+ qh->visible_list= qh->newfacet_list= qh->facet_list= NULL;
+ }
+ }
+ }else {
+ FORALLfacets {
+ qh_setfreelong(qh, &(facet->outsideset));
+ qh_setfreelong(qh, &(facet->coplanarset));
+ if (!facet->simplicial) {
+ qh_setfreelong(qh, &(facet->neighbors));
+ qh_setfreelong(qh, &(facet->ridges));
+ qh_setfreelong(qh, &(facet->vertices));
+ }
+ }
+ }
+ qh_setfree(qh, &(qh->hash_table));
+ qh_memfree(qh, qh->interior_point, qh->normal_size);
+ qh->interior_point= NULL;
+ FOREACHmerge_(qh->facet_mergeset) /* usually empty */
+ qh_memfree(qh, merge, (int)sizeof(mergeT));
+ qh->facet_mergeset= NULL; /* temp set */
+ qh->degen_mergeset= NULL; /* temp set */
+ qh_settempfree_all(qh);
+} /* freebuild */
+
+/*-<a href="qh-globa_r.htm#TOC"
+ >-------------------------------</a><a name="freeqhull">-</a>
+
+ qh_freeqhull(qh, allmem )
+
+ free global memory and set qhT to 0
+ if !allmem,
+ does not free short memory (freed by qh_memfreeshort unless qh_NOmem)
+
+notes:
+ sets qh.NOerrexit in case caller forgets to
+ Does not throw errors
+
+see:
+ see qh_initqhull_start2()
+ For libqhull_r, qhstatT is part of qhT
+
+design:
+ free global and temporary memory from qh_initbuild and qh_buildhull
+ free buffers
+*/
+void qh_freeqhull(qhT *qh, boolT allmem) {
+
+ qh->NOerrexit= True; /* no more setjmp since called at exit and ~QhullQh */
+ trace1((qh, qh->ferr, 1006, "qh_freeqhull: free global memory\n"));
+ qh_freebuild(qh, allmem);
+ qh_freebuffers(qh);
+ /* memset is the same in qh_freeqhull() and qh_initqhull_start2() */
+ memset((char *)qh, 0, sizeof(qhT)-sizeof(qhmemT)-sizeof(qhstatT));
+ qh->NOerrexit= True;
+} /* freeqhull2 */
+
+/*-<a href="qh-globa_r.htm#TOC"
+ >-------------------------------</a><a name="init_A">-</a>
+
+ qh_init_A(qh, infile, outfile, errfile, argc, argv )
+ initialize memory and stdio files
+ convert input options to option string (qh.qhull_command)
+
+ notes:
+ infile may be NULL if qh_readpoints() is not called
+
+ errfile should always be defined. It is used for reporting
+ errors. outfile is used for output and format options.
+
+ argc/argv may be 0/NULL
+
+ called before error handling initialized
+ qh_errexit() may not be used
+*/
+void qh_init_A(qhT *qh, FILE *infile, FILE *outfile, FILE *errfile, int argc, char *argv[]) {
+ qh_meminit(qh, errfile);
+ qh_initqhull_start(qh, infile, outfile, errfile);
+ qh_init_qhull_command(qh, argc, argv);
+} /* init_A */
+
+/*-<a href="qh-globa_r.htm#TOC"
+ >-------------------------------</a><a name="init_B">-</a>
+
+ qh_init_B(qh, points, numpoints, dim, ismalloc )
+ initialize globals for points array
+
+ points has numpoints dim-dimensional points
+ points[0] is the first coordinate of the first point
+ points[1] is the second coordinate of the first point
+ points[dim] is the first coordinate of the second point
+
+ ismalloc=True
+ Qhull will call qh_free(points) on exit or input transformation
+ ismalloc=False
+ Qhull will allocate a new point array if needed for input transformation
+
+ qh.qhull_command
+ is the option string.
+ It is defined by qh_init_B(), qh_qhull_command(), or qh_initflags
+
+ returns:
+ if qh.PROJECTinput or (qh.DELAUNAY and qh.PROJECTdelaunay)
+ projects the input to a new point array
+
+ if qh.DELAUNAY,
+ qh.hull_dim is increased by one
+ if qh.ATinfinity,
+ qh_projectinput adds point-at-infinity for Delaunay tri.
+
+ if qh.SCALEinput
+ changes the upper and lower bounds of the input, see qh_scaleinput(qh)
+
+ if qh.ROTATEinput
+ rotates the input by a random rotation, see qh_rotateinput()
+ if qh.DELAUNAY
+ rotates about the last coordinate
+
+ notes:
+ called after points are defined
+ qh_errexit() may be used
+*/
+void qh_init_B(qhT *qh, coordT *points, int numpoints, int dim, boolT ismalloc) {
+ qh_initqhull_globals(qh, points, numpoints, dim, ismalloc);
+ if (qh->qhmem.LASTsize == 0)
+ qh_initqhull_mem(qh);
+ /* mem_r.c and qset_r.c are initialized */
+ qh_initqhull_buffers(qh);
+ qh_initthresholds(qh, qh->qhull_command);
+ if (qh->PROJECTinput || (qh->DELAUNAY && qh->PROJECTdelaunay))
+ qh_projectinput(qh);
+ if (qh->SCALEinput)
+ qh_scaleinput(qh);
+ if (qh->ROTATErandom >= 0) {
+ qh_randommatrix(qh, qh->gm_matrix, qh->hull_dim, qh->gm_row);
+ if (qh->DELAUNAY) {
+ int k, lastk= qh->hull_dim-1;
+ for (k=0; k < lastk; k++) {
+ qh->gm_row[k][lastk]= 0.0;
+ qh->gm_row[lastk][k]= 0.0;
+ }
+ qh->gm_row[lastk][lastk]= 1.0;
+ }
+ qh_gram_schmidt(qh, qh->hull_dim, qh->gm_row);
+ qh_rotateinput(qh, qh->gm_row);
+ }
+} /* init_B */
+
+/*-<a href="qh-globa_r.htm#TOC"
+ >-------------------------------</a><a name="init_qhull_command">-</a>
+
+ qh_init_qhull_command(qh, argc, argv )
+ build qh.qhull_command from argc/argv
+ Calls qh_exit if qhull_command is too short
+
+ returns:
+ a space-delimited string of options (just as typed)
+
+ notes:
+ makes option string easy to input and output
+
+ argc/argv may be 0/NULL
+*/
+void qh_init_qhull_command(qhT *qh, int argc, char *argv[]) {
+
+ if (!qh_argv_to_command(argc, argv, qh->qhull_command, (int)sizeof(qh->qhull_command))){
+ /* Assumes qh.ferr is defined. */
+ qh_fprintf(qh, qh->ferr, 6033, "qhull input error: more than %d characters in command line.\n",
+ (int)sizeof(qh->qhull_command));
+ qh_exit(qh_ERRinput); /* error reported, can not use qh_errexit */
+ }
+} /* init_qhull_command */
+
+/*-<a href="qh-globa_r.htm#TOC"
+ >-------------------------------</a><a name="initflags">-</a>
+
+ qh_initflags(qh, commandStr )
+ set flags and initialized constants from commandStr
+ calls qh_exit() if qh->NOerrexit
+
+ returns:
+ sets qh.qhull_command to command if needed
+
+ notes:
+ ignores first word (e.g., "qhull d")
+ use qh_strtol/strtod since strtol/strtod may or may not skip trailing spaces
+
+ see:
+ qh_initthresholds() continues processing of 'Pdn' and 'PDn'
+ 'prompt' in unix_r.c for documentation
+
+ design:
+ for each space-delimited option group
+ if top-level option
+ check syntax
+ append appropriate option to option string
+ set appropriate global variable or append printFormat to print options
+ else
+ for each sub-option
+ check syntax
+ append appropriate option to option string
+ set appropriate global variable or append printFormat to print options
+*/
+void qh_initflags(qhT *qh, char *command) {
+ int k, i, lastproject;
+ char *s= command, *t, *prev_s, *start, key;
+ boolT isgeom= False, wasproject;
+ realT r;
+
+ if(qh->NOerrexit){
+ qh_fprintf(qh, qh->ferr, 6245, "qhull initflags error: qh.NOerrexit was not cleared before calling qh_initflags(). It should be cleared after setjmp(). Exit qhull.");
+ qh_exit(6245);
+ }
+ if (command <= &qh->qhull_command[0] || command > &qh->qhull_command[0] + sizeof(qh->qhull_command)) {
+ if (command != &qh->qhull_command[0]) {
+ *qh->qhull_command= '\0';
+ strncat(qh->qhull_command, command, sizeof(qh->qhull_command)-strlen(qh->qhull_command)-1);
+ }
+ while (*s && !isspace(*s)) /* skip program name */
+ s++;
+ }
+ while (*s) {
+ while (*s && isspace(*s))
+ s++;
+ if (*s == '-')
+ s++;
+ if (!*s)
+ break;
+ prev_s= s;
+ switch (*s++) {
+ case 'd':
+ qh_option(qh, "delaunay", NULL, NULL);
+ qh->DELAUNAY= True;
+ break;
+ case 'f':
+ qh_option(qh, "facets", NULL, NULL);
+ qh_appendprint(qh, qh_PRINTfacets);
+ break;
+ case 'i':
+ qh_option(qh, "incidence", NULL, NULL);
+ qh_appendprint(qh, qh_PRINTincidences);
+ break;
+ case 'm':
+ qh_option(qh, "mathematica", NULL, NULL);
+ qh_appendprint(qh, qh_PRINTmathematica);
+ break;
+ case 'n':
+ qh_option(qh, "normals", NULL, NULL);
+ qh_appendprint(qh, qh_PRINTnormals);
+ break;
+ case 'o':
+ qh_option(qh, "offFile", NULL, NULL);
+ qh_appendprint(qh, qh_PRINToff);
+ break;
+ case 'p':
+ qh_option(qh, "points", NULL, NULL);
+ qh_appendprint(qh, qh_PRINTpoints);
+ break;
+ case 's':
+ qh_option(qh, "summary", NULL, NULL);
+ qh->PRINTsummary= True;
+ break;
+ case 'v':
+ qh_option(qh, "voronoi", NULL, NULL);
+ qh->VORONOI= True;
+ qh->DELAUNAY= True;
+ break;
+ case 'A':
+ if (!isdigit(*s) && *s != '.' && *s != '-')
+ qh_fprintf(qh, qh->ferr, 7002, "qhull warning: no maximum cosine angle given for option 'An'. Ignored.\n");
+ else {
+ if (*s == '-') {
+ qh->premerge_cos= -qh_strtod(s, &s);
+ qh_option(qh, "Angle-premerge-", NULL, &qh->premerge_cos);
+ qh->PREmerge= True;
+ }else {
+ qh->postmerge_cos= qh_strtod(s, &s);
+ qh_option(qh, "Angle-postmerge", NULL, &qh->postmerge_cos);
+ qh->POSTmerge= True;
+ }
+ qh->MERGING= True;
+ }
+ break;
+ case 'C':
+ if (!isdigit(*s) && *s != '.' && *s != '-')
+ qh_fprintf(qh, qh->ferr, 7003, "qhull warning: no centrum radius given for option 'Cn'. Ignored.\n");
+ else {
+ if (*s == '-') {
+ qh->premerge_centrum= -qh_strtod(s, &s);
+ qh_option(qh, "Centrum-premerge-", NULL, &qh->premerge_centrum);
+ qh->PREmerge= True;
+ }else {
+ qh->postmerge_centrum= qh_strtod(s, &s);
+ qh_option(qh, "Centrum-postmerge", NULL, &qh->postmerge_centrum);
+ qh->POSTmerge= True;
+ }
+ qh->MERGING= True;
+ }
+ break;
+ case 'E':
+ if (*s == '-')
+ qh_fprintf(qh, qh->ferr, 7004, "qhull warning: negative maximum roundoff given for option 'An'. Ignored.\n");
+ else if (!isdigit(*s))
+ qh_fprintf(qh, qh->ferr, 7005, "qhull warning: no maximum roundoff given for option 'En'. Ignored.\n");
+ else {
+ qh->DISTround= qh_strtod(s, &s);
+ qh_option(qh, "Distance-roundoff", NULL, &qh->DISTround);
+ qh->SETroundoff= True;
+ }
+ break;
+ case 'H':
+ start= s;
+ qh->HALFspace= True;
+ qh_strtod(s, &t);
+ while (t > s) {
+ if (*t && !isspace(*t)) {
+ if (*t == ',')
+ t++;
+ else
+ qh_fprintf(qh, qh->ferr, 7006, "qhull warning: origin for Halfspace intersection should be 'Hn,n,n,...'\n");
+ }
+ s= t;
+ qh_strtod(s, &t);
+ }
+ if (start < t) {
+ if (!(qh->feasible_string= (char*)calloc((size_t)(t-start+1), (size_t)1))) {
+ qh_fprintf(qh, qh->ferr, 6034, "qhull error: insufficient memory for 'Hn,n,n'\n");
+ qh_errexit(qh, qh_ERRmem, NULL, NULL);
+ }
+ strncpy(qh->feasible_string, start, (size_t)(t-start));
+ qh_option(qh, "Halfspace-about", NULL, NULL);
+ qh_option(qh, qh->feasible_string, NULL, NULL);
+ }else
+ qh_option(qh, "Halfspace", NULL, NULL);
+ break;
+ case 'R':
+ if (!isdigit(*s))
+ qh_fprintf(qh, qh->ferr, 7007, "qhull warning: missing random perturbation for option 'Rn'. Ignored\n");
+ else {
+ qh->RANDOMfactor= qh_strtod(s, &s);
+ qh_option(qh, "Random_perturb", NULL, &qh->RANDOMfactor);
+ qh->RANDOMdist= True;
+ }
+ break;
+ case 'V':
+ if (!isdigit(*s) && *s != '-')
+ qh_fprintf(qh, qh->ferr, 7008, "qhull warning: missing visible distance for option 'Vn'. Ignored\n");
+ else {
+ qh->MINvisible= qh_strtod(s, &s);
+ qh_option(qh, "Visible", NULL, &qh->MINvisible);
+ }
+ break;
+ case 'U':
+ if (!isdigit(*s) && *s != '-')
+ qh_fprintf(qh, qh->ferr, 7009, "qhull warning: missing coplanar distance for option 'Un'. Ignored\n");
+ else {
+ qh->MAXcoplanar= qh_strtod(s, &s);
+ qh_option(qh, "U-coplanar", NULL, &qh->MAXcoplanar);
+ }
+ break;
+ case 'W':
+ if (*s == '-')
+ qh_fprintf(qh, qh->ferr, 7010, "qhull warning: negative outside width for option 'Wn'. Ignored.\n");
+ else if (!isdigit(*s))
+ qh_fprintf(qh, qh->ferr, 7011, "qhull warning: missing outside width for option 'Wn'. Ignored\n");
+ else {
+ qh->MINoutside= qh_strtod(s, &s);
+ qh_option(qh, "W-outside", NULL, &qh->MINoutside);
+ qh->APPROXhull= True;
+ }
+ break;
+ /************ sub menus ***************/
+ case 'F':
+ while (*s && !isspace(*s)) {
+ switch (*s++) {
+ case 'a':
+ qh_option(qh, "Farea", NULL, NULL);
+ qh_appendprint(qh, qh_PRINTarea);
+ qh->GETarea= True;
+ break;
+ case 'A':
+ qh_option(qh, "FArea-total", NULL, NULL);
+ qh->GETarea= True;
+ break;
+ case 'c':
+ qh_option(qh, "Fcoplanars", NULL, NULL);
+ qh_appendprint(qh, qh_PRINTcoplanars);
+ break;
+ case 'C':
+ qh_option(qh, "FCentrums", NULL, NULL);
+ qh_appendprint(qh, qh_PRINTcentrums);
+ break;
+ case 'd':
+ qh_option(qh, "Fd-cdd-in", NULL, NULL);
+ qh->CDDinput= True;
+ break;
+ case 'D':
+ qh_option(qh, "FD-cdd-out", NULL, NULL);
+ qh->CDDoutput= True;
+ break;
+ case 'F':
+ qh_option(qh, "FFacets-xridge", NULL, NULL);
+ qh_appendprint(qh, qh_PRINTfacets_xridge);
+ break;
+ case 'i':
+ qh_option(qh, "Finner", NULL, NULL);
+ qh_appendprint(qh, qh_PRINTinner);
+ break;
+ case 'I':
+ qh_option(qh, "FIDs", NULL, NULL);
+ qh_appendprint(qh, qh_PRINTids);
+ break;
+ case 'm':
+ qh_option(qh, "Fmerges", NULL, NULL);
+ qh_appendprint(qh, qh_PRINTmerges);
+ break;
+ case 'M':
+ qh_option(qh, "FMaple", NULL, NULL);
+ qh_appendprint(qh, qh_PRINTmaple);
+ break;
+ case 'n':
+ qh_option(qh, "Fneighbors", NULL, NULL);
+ qh_appendprint(qh, qh_PRINTneighbors);
+ break;
+ case 'N':
+ qh_option(qh, "FNeighbors-vertex", NULL, NULL);
+ qh_appendprint(qh, qh_PRINTvneighbors);
+ break;
+ case 'o':
+ qh_option(qh, "Fouter", NULL, NULL);
+ qh_appendprint(qh, qh_PRINTouter);
+ break;
+ case 'O':
+ if (qh->PRINToptions1st) {
+ qh_option(qh, "FOptions", NULL, NULL);
+ qh_appendprint(qh, qh_PRINToptions);
+ }else
+ qh->PRINToptions1st= True;
+ break;
+ case 'p':
+ qh_option(qh, "Fpoint-intersect", NULL, NULL);
+ qh_appendprint(qh, qh_PRINTpointintersect);
+ break;
+ case 'P':
+ qh_option(qh, "FPoint-nearest", NULL, NULL);
+ qh_appendprint(qh, qh_PRINTpointnearest);
+ break;
+ case 'Q':
+ qh_option(qh, "FQhull", NULL, NULL);
+ qh_appendprint(qh, qh_PRINTqhull);
+ break;
+ case 's':
+ qh_option(qh, "Fsummary", NULL, NULL);
+ qh_appendprint(qh, qh_PRINTsummary);
+ break;
+ case 'S':
+ qh_option(qh, "FSize", NULL, NULL);
+ qh_appendprint(qh, qh_PRINTsize);
+ qh->GETarea= True;
+ break;
+ case 't':
+ qh_option(qh, "Ftriangles", NULL, NULL);
+ qh_appendprint(qh, qh_PRINTtriangles);
+ break;
+ case 'v':
+ /* option set in qh_initqhull_globals */
+ qh_appendprint(qh, qh_PRINTvertices);
+ break;
+ case 'V':
+ qh_option(qh, "FVertex-average", NULL, NULL);
+ qh_appendprint(qh, qh_PRINTaverage);
+ break;
+ case 'x':
+ qh_option(qh, "Fxtremes", NULL, NULL);
+ qh_appendprint(qh, qh_PRINTextremes);
+ break;
+ default:
+ s--;
+ qh_fprintf(qh, qh->ferr, 7012, "qhull warning: unknown 'F' output option %c, rest ignored\n", (int)s[0]);
+ while (*++s && !isspace(*s));
+ break;
+ }
+ }
+ break;
+ case 'G':
+ isgeom= True;
+ qh_appendprint(qh, qh_PRINTgeom);
+ while (*s && !isspace(*s)) {
+ switch (*s++) {
+ case 'a':
+ qh_option(qh, "Gall-points", NULL, NULL);
+ qh->PRINTdots= True;
+ break;
+ case 'c':
+ qh_option(qh, "Gcentrums", NULL, NULL);
+ qh->PRINTcentrums= True;
+ break;
+ case 'h':
+ qh_option(qh, "Gintersections", NULL, NULL);
+ qh->DOintersections= True;
+ break;
+ case 'i':
+ qh_option(qh, "Ginner", NULL, NULL);
+ qh->PRINTinner= True;
+ break;
+ case 'n':
+ qh_option(qh, "Gno-planes", NULL, NULL);
+ qh->PRINTnoplanes= True;
+ break;
+ case 'o':
+ qh_option(qh, "Gouter", NULL, NULL);
+ qh->PRINTouter= True;
+ break;
+ case 'p':
+ qh_option(qh, "Gpoints", NULL, NULL);
+ qh->PRINTcoplanar= True;
+ break;
+ case 'r':
+ qh_option(qh, "Gridges", NULL, NULL);
+ qh->PRINTridges= True;
+ break;
+ case 't':
+ qh_option(qh, "Gtransparent", NULL, NULL);
+ qh->PRINTtransparent= True;
+ break;
+ case 'v':
+ qh_option(qh, "Gvertices", NULL, NULL);
+ qh->PRINTspheres= True;
+ break;
+ case 'D':
+ if (!isdigit(*s))
+ qh_fprintf(qh, qh->ferr, 6035, "qhull input error: missing dimension for option 'GDn'\n");
+ else {
+ if (qh->DROPdim >= 0)
+ qh_fprintf(qh, qh->ferr, 7013, "qhull warning: can only drop one dimension. Previous 'GD%d' ignored\n",
+ qh->DROPdim);
+ qh->DROPdim= qh_strtol(s, &s);
+ qh_option(qh, "GDrop-dim", &qh->DROPdim, NULL);
+ }
+ break;
+ default:
+ s--;
+ qh_fprintf(qh, qh->ferr, 7014, "qhull warning: unknown 'G' print option %c, rest ignored\n", (int)s[0]);
+ while (*++s && !isspace(*s));
+ break;
+ }
+ }
+ break;
+ case 'P':
+ while (*s && !isspace(*s)) {
+ switch (*s++) {
+ case 'd': case 'D': /* see qh_initthresholds() */
+ key= s[-1];
+ i= qh_strtol(s, &s);
+ r= 0;
+ if (*s == ':') {
+ s++;
+ r= qh_strtod(s, &s);
+ }
+ if (key == 'd')
+ qh_option(qh, "Pdrop-facets-dim-less", &i, &r);
+ else
+ qh_option(qh, "PDrop-facets-dim-more", &i, &r);
+ break;
+ case 'g':
+ qh_option(qh, "Pgood-facets", NULL, NULL);
+ qh->PRINTgood= True;
+ break;
+ case 'G':
+ qh_option(qh, "PGood-facet-neighbors", NULL, NULL);
+ qh->PRINTneighbors= True;
+ break;
+ case 'o':
+ qh_option(qh, "Poutput-forced", NULL, NULL);
+ qh->FORCEoutput= True;
+ break;
+ case 'p':
+ qh_option(qh, "Pprecision-ignore", NULL, NULL);
+ qh->PRINTprecision= False;
+ break;
+ case 'A':
+ if (!isdigit(*s))
+ qh_fprintf(qh, qh->ferr, 6036, "qhull input error: missing facet count for keep area option 'PAn'\n");
+ else {
+ qh->KEEParea= qh_strtol(s, &s);
+ qh_option(qh, "PArea-keep", &qh->KEEParea, NULL);
+ qh->GETarea= True;
+ }
+ break;
+ case 'F':
+ if (!isdigit(*s))
+ qh_fprintf(qh, qh->ferr, 6037, "qhull input error: missing facet area for option 'PFn'\n");
+ else {
+ qh->KEEPminArea= qh_strtod(s, &s);
+ qh_option(qh, "PFacet-area-keep", NULL, &qh->KEEPminArea);
+ qh->GETarea= True;
+ }
+ break;
+ case 'M':
+ if (!isdigit(*s))
+ qh_fprintf(qh, qh->ferr, 6038, "qhull input error: missing merge count for option 'PMn'\n");
+ else {
+ qh->KEEPmerge= qh_strtol(s, &s);
+ qh_option(qh, "PMerge-keep", &qh->KEEPmerge, NULL);
+ }
+ break;
+ default:
+ s--;
+ qh_fprintf(qh, qh->ferr, 7015, "qhull warning: unknown 'P' print option %c, rest ignored\n", (int)s[0]);
+ while (*++s && !isspace(*s));
+ break;
+ }
+ }
+ break;
+ case 'Q':
+ lastproject= -1;
+ while (*s && !isspace(*s)) {
+ switch (*s++) {
+ case 'b': case 'B': /* handled by qh_initthresholds */
+ key= s[-1];
+ if (key == 'b' && *s == 'B') {
+ s++;
+ r= qh_DEFAULTbox;
+ qh->SCALEinput= True;
+ qh_option(qh, "QbBound-unit-box", NULL, &r);
+ break;
+ }
+ if (key == 'b' && *s == 'b') {
+ s++;
+ qh->SCALElast= True;
+ qh_option(qh, "Qbbound-last", NULL, NULL);
+ break;
+ }
+ k= qh_strtol(s, &s);
+ r= 0.0;
+ wasproject= False;
+ if (*s == ':') {
+ s++;
+ if ((r= qh_strtod(s, &s)) == 0.0) {
+ t= s; /* need true dimension for memory allocation */
+ while (*t && !isspace(*t)) {
+ if (toupper(*t++) == 'B'
+ && k == qh_strtol(t, &t)
+ && *t++ == ':'
+ && qh_strtod(t, &t) == 0.0) {
+ qh->PROJECTinput++;
+ trace2((qh, qh->ferr, 2004, "qh_initflags: project dimension %d\n", k));
+ qh_option(qh, "Qb-project-dim", &k, NULL);
+ wasproject= True;
+ lastproject= k;
+ break;
+ }
+ }
+ }
+ }
+ if (!wasproject) {
+ if (lastproject == k && r == 0.0)
+ lastproject= -1; /* doesn't catch all possible sequences */
+ else if (key == 'b') {
+ qh->SCALEinput= True;
+ if (r == 0.0)
+ r= -qh_DEFAULTbox;
+ qh_option(qh, "Qbound-dim-low", &k, &r);
+ }else {
+ qh->SCALEinput= True;
+ if (r == 0.0)
+ r= qh_DEFAULTbox;
+ qh_option(qh, "QBound-dim-high", &k, &r);
+ }
+ }
+ break;
+ case 'c':
+ qh_option(qh, "Qcoplanar-keep", NULL, NULL);
+ qh->KEEPcoplanar= True;
+ break;
+ case 'f':
+ qh_option(qh, "Qfurthest-outside", NULL, NULL);
+ qh->BESToutside= True;
+ break;
+ case 'g':
+ qh_option(qh, "Qgood-facets-only", NULL, NULL);
+ qh->ONLYgood= True;
+ break;
+ case 'i':
+ qh_option(qh, "Qinterior-keep", NULL, NULL);
+ qh->KEEPinside= True;
+ break;
+ case 'm':
+ qh_option(qh, "Qmax-outside-only", NULL, NULL);
+ qh->ONLYmax= True;
+ break;
+ case 'r':
+ qh_option(qh, "Qrandom-outside", NULL, NULL);
+ qh->RANDOMoutside= True;
+ break;
+ case 's':
+ qh_option(qh, "Qsearch-initial-simplex", NULL, NULL);
+ qh->ALLpoints= True;
+ break;
+ case 't':
+ qh_option(qh, "Qtriangulate", NULL, NULL);
+ qh->TRIangulate= True;
+ break;
+ case 'T':
+ qh_option(qh, "QTestPoints", NULL, NULL);
+ if (!isdigit(*s))
+ qh_fprintf(qh, qh->ferr, 6039, "qhull input error: missing number of test points for option 'QTn'\n");
+ else {
+ qh->TESTpoints= qh_strtol(s, &s);
+ qh_option(qh, "QTestPoints", &qh->TESTpoints, NULL);
+ }
+ break;
+ case 'u':
+ qh_option(qh, "QupperDelaunay", NULL, NULL);
+ qh->UPPERdelaunay= True;
+ break;
+ case 'v':
+ qh_option(qh, "Qvertex-neighbors-convex", NULL, NULL);
+ qh->TESTvneighbors= True;
+ break;
+ case 'x':
+ qh_option(qh, "Qxact-merge", NULL, NULL);
+ qh->MERGEexact= True;
+ break;
+ case 'z':
+ qh_option(qh, "Qz-infinity-point", NULL, NULL);
+ qh->ATinfinity= True;
+ break;
+ case '0':
+ qh_option(qh, "Q0-no-premerge", NULL, NULL);
+ qh->NOpremerge= True;
+ break;
+ case '1':
+ if (!isdigit(*s)) {
+ qh_option(qh, "Q1-no-angle-sort", NULL, NULL);
+ qh->ANGLEmerge= False;
+ break;
+ }
+ switch (*s++) {
+ case '0':
+ qh_option(qh, "Q10-no-narrow", NULL, NULL);
+ qh->NOnarrow= True;
+ break;
+ case '1':
+ qh_option(qh, "Q11-trinormals Qtriangulate", NULL, NULL);
+ qh->TRInormals= True;
+ qh->TRIangulate= True;
+ break;
+ case '2':
+ qh_option(qh, "Q12-no-wide-dup", NULL, NULL);
+ qh->NOwide= True;
+ break;
+ default:
+ s--;
+ qh_fprintf(qh, qh->ferr, 7016, "qhull warning: unknown 'Q' qhull option 1%c, rest ignored\n", (int)s[0]);
+ while (*++s && !isspace(*s));
+ break;
+ }
+ break;
+ case '2':
+ qh_option(qh, "Q2-no-merge-independent", NULL, NULL);
+ qh->MERGEindependent= False;
+ goto LABELcheckdigit;
+ break; /* no warnings */
+ case '3':
+ qh_option(qh, "Q3-no-merge-vertices", NULL, NULL);
+ qh->MERGEvertices= False;
+ LABELcheckdigit:
+ if (isdigit(*s))
+ qh_fprintf(qh, qh->ferr, 7017, "qhull warning: can not follow '1', '2', or '3' with a digit. '%c' skipped.\n",
+ *s++);
+ break;
+ case '4':
+ qh_option(qh, "Q4-avoid-old-into-new", NULL, NULL);
+ qh->AVOIDold= True;
+ break;
+ case '5':
+ qh_option(qh, "Q5-no-check-outer", NULL, NULL);
+ qh->SKIPcheckmax= True;
+ break;
+ case '6':
+ qh_option(qh, "Q6-no-concave-merge", NULL, NULL);
+ qh->SKIPconvex= True;
+ break;
+ case '7':
+ qh_option(qh, "Q7-no-breadth-first", NULL, NULL);
+ qh->VIRTUALmemory= True;
+ break;
+ case '8':
+ qh_option(qh, "Q8-no-near-inside", NULL, NULL);
+ qh->NOnearinside= True;
+ break;
+ case '9':
+ qh_option(qh, "Q9-pick-furthest", NULL, NULL);
+ qh->PICKfurthest= True;
+ break;
+ case 'G':
+ i= qh_strtol(s, &t);
+ if (qh->GOODpoint)
+ qh_fprintf(qh, qh->ferr, 7018, "qhull warning: good point already defined for option 'QGn'. Ignored\n");
+ else if (s == t)
+ qh_fprintf(qh, qh->ferr, 7019, "qhull warning: missing good point id for option 'QGn'. Ignored\n");
+ else if (i < 0 || *s == '-') {
+ qh->GOODpoint= i-1;
+ qh_option(qh, "QGood-if-dont-see-point", &i, NULL);
+ }else {
+ qh->GOODpoint= i+1;
+ qh_option(qh, "QGood-if-see-point", &i, NULL);
+ }
+ s= t;
+ break;
+ case 'J':
+ if (!isdigit(*s) && *s != '-')
+ qh->JOGGLEmax= 0.0;
+ else {
+ qh->JOGGLEmax= (realT) qh_strtod(s, &s);
+ qh_option(qh, "QJoggle", NULL, &qh->JOGGLEmax);
+ }
+ break;
+ case 'R':
+ if (!isdigit(*s) && *s != '-')
+ qh_fprintf(qh, qh->ferr, 7020, "qhull warning: missing random seed for option 'QRn'. Ignored\n");
+ else {
+ qh->ROTATErandom= i= qh_strtol(s, &s);
+ if (i > 0)
+ qh_option(qh, "QRotate-id", &i, NULL );
+ else if (i < -1)
+ qh_option(qh, "QRandom-seed", &i, NULL );
+ }
+ break;
+ case 'V':
+ i= qh_strtol(s, &t);
+ if (qh->GOODvertex)
+ qh_fprintf(qh, qh->ferr, 7021, "qhull warning: good vertex already defined for option 'QVn'. Ignored\n");
+ else if (s == t)
+ qh_fprintf(qh, qh->ferr, 7022, "qhull warning: no good point id given for option 'QVn'. Ignored\n");
+ else if (i < 0) {
+ qh->GOODvertex= i - 1;
+ qh_option(qh, "QV-good-facets-not-point", &i, NULL);
+ }else {
+ qh_option(qh, "QV-good-facets-point", &i, NULL);
+ qh->GOODvertex= i + 1;
+ }
+ s= t;
+ break;
+ default:
+ s--;
+ qh_fprintf(qh, qh->ferr, 7023, "qhull warning: unknown 'Q' qhull option %c, rest ignored\n", (int)s[0]);
+ while (*++s && !isspace(*s));
+ break;
+ }
+ }
+ break;
+ case 'T':
+ while (*s && !isspace(*s)) {
+ if (isdigit(*s) || *s == '-')
+ qh->IStracing= qh_strtol(s, &s);
+ else switch (*s++) {
+ case 'a':
+ qh_option(qh, "Tannotate-output", NULL, NULL);
+ qh->ANNOTATEoutput= True;
+ break;
+ case 'c':
+ qh_option(qh, "Tcheck-frequently", NULL, NULL);
+ qh->CHECKfrequently= True;
+ break;
+ case 's':
+ qh_option(qh, "Tstatistics", NULL, NULL);
+ qh->PRINTstatistics= True;
+ break;
+ case 'v':
+ qh_option(qh, "Tverify", NULL, NULL);
+ qh->VERIFYoutput= True;
+ break;
+ case 'z':
+ if (qh->ferr == qh_FILEstderr) {
+ /* The C++ interface captures the output in qh_fprint_qhull() */
+ qh_option(qh, "Tz-stdout", NULL, NULL);
+ qh->USEstdout= True;
+ }else if (!qh->fout)
+ qh_fprintf(qh, qh->ferr, 7024, "qhull warning: output file undefined(stdout). Option 'Tz' ignored.\n");
+ else {
+ qh_option(qh, "Tz-stdout", NULL, NULL);
+ qh->USEstdout= True;
+ qh->ferr= qh->fout;
+ qh->qhmem.ferr= qh->fout;
+ }
+ break;
+ case 'C':
+ if (!isdigit(*s))
+ qh_fprintf(qh, qh->ferr, 7025, "qhull warning: missing point id for cone for trace option 'TCn'. Ignored\n");
+ else {
+ i= qh_strtol(s, &s);
+ qh_option(qh, "TCone-stop", &i, NULL);
+ qh->STOPcone= i + 1;
+ }
+ break;
+ case 'F':
+ if (!isdigit(*s))
+ qh_fprintf(qh, qh->ferr, 7026, "qhull warning: missing frequency count for trace option 'TFn'. Ignored\n");
+ else {
+ qh->REPORTfreq= qh_strtol(s, &s);
+ qh_option(qh, "TFacet-log", &qh->REPORTfreq, NULL);
+ qh->REPORTfreq2= qh->REPORTfreq/2; /* for tracemerging() */
+ }
+ break;
+ case 'I':
+ if (!isspace(*s))
+ qh_fprintf(qh, qh->ferr, 7027, "qhull warning: missing space between 'TI' and filename, %s\n", s);
+ while (isspace(*s))
+ s++;
+ t= qh_skipfilename(qh, s);
+ {
+ char filename[qh_FILENAMElen];
+
+ qh_copyfilename(qh, filename, (int)sizeof(filename), s, (int)(t-s)); /* WARN64 */
+ s= t;
+ if (!freopen(filename, "r", stdin)) {
+ qh_fprintf(qh, qh->ferr, 6041, "qhull error: could not open file \"%s\".", filename);
+ qh_errexit(qh, qh_ERRinput, NULL, NULL);
+ }else {
+ qh_option(qh, "TInput-file", NULL, NULL);
+ qh_option(qh, filename, NULL, NULL);
+ }
+ }
+ break;
+ case 'O':
+ if (!isspace(*s))
+ qh_fprintf(qh, qh->ferr, 7028, "qhull warning: missing space between 'TO' and filename, %s\n", s);
+ while (isspace(*s))
+ s++;
+ t= qh_skipfilename(qh, s);
+ {
+ char filename[qh_FILENAMElen];
+
+ qh_copyfilename(qh, filename, (int)sizeof(filename), s, (int)(t-s)); /* WARN64 */
+ s= t;
+ if (!qh->fout) {
+ qh_fprintf(qh, qh->ferr, 6266, "qhull input warning: qh.fout was not set by caller. Cannot use option 'TO' to redirect output. Ignoring option 'TO'\n");
+ }else if (!freopen(filename, "w", qh->fout)) {
+ qh_fprintf(qh, qh->ferr, 6044, "qhull error: could not open file \"%s\".", filename);
+ qh_errexit(qh, qh_ERRinput, NULL, NULL);
+ }else {
+ qh_option(qh, "TOutput-file", NULL, NULL);
+ qh_option(qh, filename, NULL, NULL);
+ }
+ }
+ break;
+ case 'P':
+ if (!isdigit(*s))
+ qh_fprintf(qh, qh->ferr, 7029, "qhull warning: missing point id for trace option 'TPn'. Ignored\n");
+ else {
+ qh->TRACEpoint= qh_strtol(s, &s);
+ qh_option(qh, "Trace-point", &qh->TRACEpoint, NULL);
+ }
+ break;
+ case 'M':
+ if (!isdigit(*s))
+ qh_fprintf(qh, qh->ferr, 7030, "qhull warning: missing merge id for trace option 'TMn'. Ignored\n");
+ else {
+ qh->TRACEmerge= qh_strtol(s, &s);
+ qh_option(qh, "Trace-merge", &qh->TRACEmerge, NULL);
+ }
+ break;
+ case 'R':
+ if (!isdigit(*s))
+ qh_fprintf(qh, qh->ferr, 7031, "qhull warning: missing rerun count for trace option 'TRn'. Ignored\n");
+ else {
+ qh->RERUN= qh_strtol(s, &s);
+ qh_option(qh, "TRerun", &qh->RERUN, NULL);
+ }
+ break;
+ case 'V':
+ i= qh_strtol(s, &t);
+ if (s == t)
+ qh_fprintf(qh, qh->ferr, 7032, "qhull warning: missing furthest point id for trace option 'TVn'. Ignored\n");
+ else if (i < 0) {
+ qh->STOPpoint= i - 1;
+ qh_option(qh, "TV-stop-before-point", &i, NULL);
+ }else {
+ qh->STOPpoint= i + 1;
+ qh_option(qh, "TV-stop-after-point", &i, NULL);
+ }
+ s= t;
+ break;
+ case 'W':
+ if (!isdigit(*s))
+ qh_fprintf(qh, qh->ferr, 7033, "qhull warning: missing max width for trace option 'TWn'. Ignored\n");
+ else {
+ qh->TRACEdist= (realT) qh_strtod(s, &s);
+ qh_option(qh, "TWide-trace", NULL, &qh->TRACEdist);
+ }
+ break;
+ default:
+ s--;
+ qh_fprintf(qh, qh->ferr, 7034, "qhull warning: unknown 'T' trace option %c, rest ignored\n", (int)s[0]);
+ while (*++s && !isspace(*s));
+ break;
+ }
+ }
+ break;
+ default:
+ qh_fprintf(qh, qh->ferr, 7035, "qhull warning: unknown flag %c(%x)\n", (int)s[-1],
+ (int)s[-1]);
+ break;
+ }
+ if (s-1 == prev_s && *s && !isspace(*s)) {
+ qh_fprintf(qh, qh->ferr, 7036, "qhull warning: missing space after flag %c(%x); reserved for menu. Skipped.\n",
+ (int)*prev_s, (int)*prev_s);
+ while (*s && !isspace(*s))
+ s++;
+ }
+ }
+ if (qh->STOPcone && qh->JOGGLEmax < REALmax/2)
+ qh_fprintf(qh, qh->ferr, 7078, "qhull warning: 'TCn' (stopCone) ignored when used with 'QJn' (joggle)\n");
+ if (isgeom && !qh->FORCEoutput && qh->PRINTout[1])
+ qh_fprintf(qh, qh->ferr, 7037, "qhull warning: additional output formats are not compatible with Geomview\n");
+ /* set derived values in qh_initqhull_globals */
+} /* initflags */
+
+
+/*-<a href="qh-globa_r.htm#TOC"
+ >-------------------------------</a><a name="initqhull_buffers">-</a>
+
+ qh_initqhull_buffers(qh)
+ initialize global memory buffers
+
+ notes:
+ must match qh_freebuffers()
+*/
+void qh_initqhull_buffers(qhT *qh) {
+ int k;
+
+ qh->TEMPsize= (qh->qhmem.LASTsize - sizeof(setT))/SETelemsize;
+ if (qh->TEMPsize <= 0 || qh->TEMPsize > qh->qhmem.LASTsize)
+ qh->TEMPsize= 8; /* e.g., if qh_NOmem */
+ qh->other_points= qh_setnew(qh, qh->TEMPsize);
+ qh->del_vertices= qh_setnew(qh, qh->TEMPsize);
+ qh->coplanarfacetset= qh_setnew(qh, qh->TEMPsize);
+ qh->NEARzero= (realT *)qh_memalloc(qh, qh->hull_dim * sizeof(realT));
+ qh->lower_threshold= (realT *)qh_memalloc(qh, (qh->input_dim+1) * sizeof(realT));
+ qh->upper_threshold= (realT *)qh_memalloc(qh, (qh->input_dim+1) * sizeof(realT));
+ qh->lower_bound= (realT *)qh_memalloc(qh, (qh->input_dim+1) * sizeof(realT));
+ qh->upper_bound= (realT *)qh_memalloc(qh, (qh->input_dim+1) * sizeof(realT));
+ for (k=qh->input_dim+1; k--; ) { /* duplicated in qh_initqhull_buffers and qh_clear_outputflags */
+ qh->lower_threshold[k]= -REALmax;
+ qh->upper_threshold[k]= REALmax;
+ qh->lower_bound[k]= -REALmax;
+ qh->upper_bound[k]= REALmax;
+ }
+ qh->gm_matrix= (coordT *)qh_memalloc(qh, (qh->hull_dim+1) * qh->hull_dim * sizeof(coordT));
+ qh->gm_row= (coordT **)qh_memalloc(qh, (qh->hull_dim+1) * sizeof(coordT *));
+} /* initqhull_buffers */
+
+/*-<a href="qh-globa_r.htm#TOC"
+ >-------------------------------</a><a name="initqhull_globals">-</a>
+
+ qh_initqhull_globals(qh, points, numpoints, dim, ismalloc )
+ initialize globals
+ if ismalloc
+ points were malloc'd and qhull should free at end
+
+ returns:
+ sets qh.first_point, num_points, input_dim, hull_dim and others
+ seeds random number generator (seed=1 if tracing)
+ modifies qh.hull_dim if ((qh.DELAUNAY and qh.PROJECTdelaunay) or qh.PROJECTinput)
+ adjust user flags as needed
+ also checks DIM3 dependencies and constants
+
+ notes:
+ do not use qh_point() since an input transformation may move them elsewhere
+
+ see:
+ qh_initqhull_start() sets default values for non-zero globals
+
+ design:
+ initialize points array from input arguments
+ test for qh.ZEROcentrum
+ (i.e., use opposite vertex instead of cetrum for convexity testing)
+ initialize qh.CENTERtype, qh.normal_size,
+ qh.center_size, qh.TRACEpoint/level,
+ initialize and test random numbers
+ qh_initqhull_outputflags() -- adjust and test output flags
+*/
+void qh_initqhull_globals(qhT *qh, coordT *points, int numpoints, int dim, boolT ismalloc) {
+ int seed, pointsneeded, extra= 0, i, randi, k;
+ realT randr;
+ realT factorial;
+
+ time_t timedata;
+
+ trace0((qh, qh->ferr, 13, "qh_initqhull_globals: for %s | %s\n", qh->rbox_command,
+ qh->qhull_command));
+ qh->POINTSmalloc= ismalloc;
+ qh->first_point= points;
+ qh->num_points= numpoints;
+ qh->hull_dim= qh->input_dim= dim;
+ if (!qh->NOpremerge && !qh->MERGEexact && !qh->PREmerge && qh->JOGGLEmax > REALmax/2) {
+ qh->MERGING= True;
+ if (qh->hull_dim <= 4) {
+ qh->PREmerge= True;
+ qh_option(qh, "_pre-merge", NULL, NULL);
+ }else {
+ qh->MERGEexact= True;
+ qh_option(qh, "Qxact_merge", NULL, NULL);
+ }
+ }else if (qh->MERGEexact)
+ qh->MERGING= True;
+ if (!qh->NOpremerge && qh->JOGGLEmax > REALmax/2) {
+#ifdef qh_NOmerge
+ qh->JOGGLEmax= 0.0;
+#endif
+ }
+ if (qh->TRIangulate && qh->JOGGLEmax < REALmax/2 && qh->PRINTprecision)
+ qh_fprintf(qh, qh->ferr, 7038, "qhull warning: joggle('QJ') always produces simplicial output. Triangulated output('Qt') does nothing.\n");
+ if (qh->JOGGLEmax < REALmax/2 && qh->DELAUNAY && !qh->SCALEinput && !qh->SCALElast) {
+ qh->SCALElast= True;
+ qh_option(qh, "Qbbound-last-qj", NULL, NULL);
+ }
+ if (qh->MERGING && !qh->POSTmerge && qh->premerge_cos > REALmax/2
+ && qh->premerge_centrum == 0) {
+ qh->ZEROcentrum= True;
+ qh->ZEROall_ok= True;
+ qh_option(qh, "_zero-centrum", NULL, NULL);
+ }
+ if (qh->JOGGLEmax < REALmax/2 && REALepsilon > 2e-8 && qh->PRINTprecision)
+ qh_fprintf(qh, qh->ferr, 7039, "qhull warning: real epsilon, %2.2g, is probably too large for joggle('QJn')\nRecompile with double precision reals(see user.h).\n",
+ REALepsilon);
+#ifdef qh_NOmerge
+ if (qh->MERGING) {
+ qh_fprintf(qh, qh->ferr, 6045, "qhull input error: merging not installed(qh_NOmerge + 'Qx', 'Cn' or 'An')\n");
+ qh_errexit(qh, qh_ERRinput, NULL, NULL);
+ }
+#endif
+ if (qh->DELAUNAY && qh->KEEPcoplanar && !qh->KEEPinside) {
+ qh->KEEPinside= True;
+ qh_option(qh, "Qinterior-keep", NULL, NULL);
+ }
+ if (qh->DELAUNAY && qh->HALFspace) {
+ qh_fprintf(qh, qh->ferr, 6046, "qhull input error: can not use Delaunay('d') or Voronoi('v') with halfspace intersection('H')\n");
+ qh_errexit(qh, qh_ERRinput, NULL, NULL);
+ }
+ if (!qh->DELAUNAY && (qh->UPPERdelaunay || qh->ATinfinity)) {
+ qh_fprintf(qh, qh->ferr, 6047, "qhull input error: use upper-Delaunay('Qu') or infinity-point('Qz') with Delaunay('d') or Voronoi('v')\n");
+ qh_errexit(qh, qh_ERRinput, NULL, NULL);
+ }
+ if (qh->UPPERdelaunay && qh->ATinfinity) {
+ qh_fprintf(qh, qh->ferr, 6048, "qhull input error: can not use infinity-point('Qz') with upper-Delaunay('Qu')\n");
+ qh_errexit(qh, qh_ERRinput, NULL, NULL);
+ }
+ if (qh->SCALElast && !qh->DELAUNAY && qh->PRINTprecision)
+ qh_fprintf(qh, qh->ferr, 7040, "qhull input warning: option 'Qbb' (scale-last-coordinate) is normally used with 'd' or 'v'\n");
+ qh->DOcheckmax= (!qh->SKIPcheckmax && qh->MERGING );
+ qh->KEEPnearinside= (qh->DOcheckmax && !(qh->KEEPinside && qh->KEEPcoplanar)
+ && !qh->NOnearinside);
+ if (qh->MERGING)
+ qh->CENTERtype= qh_AScentrum;
+ else if (qh->VORONOI)
+ qh->CENTERtype= qh_ASvoronoi;
+ if (qh->TESTvneighbors && !qh->MERGING) {
+ qh_fprintf(qh, qh->ferr, 6049, "qhull input error: test vertex neighbors('Qv') needs a merge option\n");
+ qh_errexit(qh, qh_ERRinput, NULL ,NULL);
+ }
+ if (qh->PROJECTinput || (qh->DELAUNAY && qh->PROJECTdelaunay)) {
+ qh->hull_dim -= qh->PROJECTinput;
+ if (qh->DELAUNAY) {
+ qh->hull_dim++;
+ if (qh->ATinfinity)
+ extra= 1;
+ }
+ }
+ if (qh->hull_dim <= 1) {
+ qh_fprintf(qh, qh->ferr, 6050, "qhull error: dimension %d must be > 1\n", qh->hull_dim);
+ qh_errexit(qh, qh_ERRinput, NULL, NULL);
+ }
+ for (k=2, factorial=1.0; k < qh->hull_dim; k++)
+ factorial *= k;
+ qh->AREAfactor= 1.0 / factorial;
+ trace2((qh, qh->ferr, 2005, "qh_initqhull_globals: initialize globals. dim %d numpoints %d malloc? %d projected %d to hull_dim %d\n",
+ dim, numpoints, ismalloc, qh->PROJECTinput, qh->hull_dim));
+ qh->normal_size= qh->hull_dim * sizeof(coordT);
+ qh->center_size= qh->normal_size - sizeof(coordT);
+ pointsneeded= qh->hull_dim+1;
+ if (qh->hull_dim > qh_DIMmergeVertex) {
+ qh->MERGEvertices= False;
+ qh_option(qh, "Q3-no-merge-vertices-dim-high", NULL, NULL);
+ }
+ if (qh->GOODpoint)
+ pointsneeded++;
+#ifdef qh_NOtrace
+ if (qh->IStracing) {
+ qh_fprintf(qh, qh->ferr, 6051, "qhull input error: tracing is not installed(qh_NOtrace in user.h)");
+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+ }
+#endif
+ if (qh->RERUN > 1) {
+ qh->TRACElastrun= qh->IStracing; /* qh_build_withrestart duplicates next conditional */
+ if (qh->IStracing != -1)
+ qh->IStracing= 0;
+ }else if (qh->TRACEpoint != qh_IDunknown || qh->TRACEdist < REALmax/2 || qh->TRACEmerge) {
+ qh->TRACElevel= (qh->IStracing? qh->IStracing : 3);
+ qh->IStracing= 0;
+ }
+ if (qh->ROTATErandom == 0 || qh->ROTATErandom == -1) {
+ seed= (int)time(&timedata);
+ if (qh->ROTATErandom == -1) {
+ seed= -seed;
+ qh_option(qh, "QRandom-seed", &seed, NULL );
+ }else
+ qh_option(qh, "QRotate-random", &seed, NULL);
+ qh->ROTATErandom= seed;
+ }
+ seed= qh->ROTATErandom;
+ if (seed == INT_MIN) /* default value */
+ seed= 1;
+ else if (seed < 0)
+ seed= -seed;
+ qh_RANDOMseed_(qh, seed);
+ randr= 0.0;
+ for (i=1000; i--; ) {
+ randi= qh_RANDOMint;
+ randr += randi;
+ if (randi > qh_RANDOMmax) {
+ qh_fprintf(qh, qh->ferr, 8036, "\
+qhull configuration error (qh_RANDOMmax in user.h):\n\
+ random integer %d > qh_RANDOMmax(qh, %.8g)\n",
+ randi, qh_RANDOMmax);
+ qh_errexit(qh, qh_ERRinput, NULL, NULL);
+ }
+ }
+ qh_RANDOMseed_(qh, seed);
+ randr = randr/1000;
+ if (randr < qh_RANDOMmax * 0.1
+ || randr > qh_RANDOMmax * 0.9)
+ qh_fprintf(qh, qh->ferr, 8037, "\
+qhull configuration warning (qh_RANDOMmax in user.h):\n\
+ average of 1000 random integers (%.2g) is much different than expected (%.2g).\n\
+ Is qh_RANDOMmax (%.2g) wrong?\n",
+ randr, qh_RANDOMmax * 0.5, qh_RANDOMmax);
+ qh->RANDOMa= 2.0 * qh->RANDOMfactor/qh_RANDOMmax;
+ qh->RANDOMb= 1.0 - qh->RANDOMfactor;
+ if (qh_HASHfactor < 1.1) {
+ qh_fprintf(qh, qh->ferr, 6052, "qhull internal error (qh_initqhull_globals): qh_HASHfactor %d must be at least 1.1. Qhull uses linear hash probing\n",
+ qh_HASHfactor);
+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+ }
+ if (numpoints+extra < pointsneeded) {
+ qh_fprintf(qh, qh->ferr, 6214, "qhull input error: not enough points(%d) to construct initial simplex (need %d)\n",
+ numpoints, pointsneeded);
+ qh_errexit(qh, qh_ERRinput, NULL, NULL);
+ }
+ qh_initqhull_outputflags(qh);
+} /* initqhull_globals */
+
+/*-<a href="qh-globa_r.htm#TOC"
+ >-------------------------------</a><a name="initqhull_mem">-</a>
+
+ qh_initqhull_mem(qh, )
+ initialize mem_r.c for qhull
+ qh.hull_dim and qh.normal_size determine some of the allocation sizes
+ if qh.MERGING,
+ includes ridgeT
+ calls qh_user_memsizes(qh) to add up to 10 additional sizes for quick allocation
+ (see numsizes below)
+
+ returns:
+ mem_r.c already for qh_memalloc/qh_memfree (errors if called beforehand)
+
+ notes:
+ qh_produceoutput() prints memsizes
+
+*/
+void qh_initqhull_mem(qhT *qh) {
+ int numsizes;
+ int i;
+
+ numsizes= 8+10;
+ qh_meminitbuffers(qh, qh->IStracing, qh_MEMalign, numsizes,
+ qh_MEMbufsize, qh_MEMinitbuf);
+ qh_memsize(qh, (int)sizeof(vertexT));
+ if (qh->MERGING) {
+ qh_memsize(qh, (int)sizeof(ridgeT));
+ qh_memsize(qh, (int)sizeof(mergeT));
+ }
+ qh_memsize(qh, (int)sizeof(facetT));
+ i= sizeof(setT) + (qh->hull_dim - 1) * SETelemsize; /* ridge.vertices */
+ qh_memsize(qh, i);
+ qh_memsize(qh, qh->normal_size); /* normal */
+ i += SETelemsize; /* facet.vertices, .ridges, .neighbors */
+ qh_memsize(qh, i);
+ qh_user_memsizes(qh);
+ qh_memsetup(qh);
+} /* initqhull_mem */
+
+/*-<a href="qh-globa_r.htm#TOC"
+ >-------------------------------</a><a name="initqhull_outputflags">-</a>
+
+ qh_initqhull_outputflags
+ initialize flags concerned with output
+
+ returns:
+ adjust user flags as needed
+
+ see:
+ qh_clear_outputflags() resets the flags
+
+ design:
+ test for qh.PRINTgood (i.e., only print 'good' facets)
+ check for conflicting print output options
+*/
+void qh_initqhull_outputflags(qhT *qh) {
+ boolT printgeom= False, printmath= False, printcoplanar= False;
+ int i;
+
+ trace3((qh, qh->ferr, 3024, "qh_initqhull_outputflags: %s\n", qh->qhull_command));
+ if (!(qh->PRINTgood || qh->PRINTneighbors)) {
+ if (qh->KEEParea || qh->KEEPminArea < REALmax/2 || qh->KEEPmerge || qh->DELAUNAY
+ || (!qh->ONLYgood && (qh->GOODvertex || qh->GOODpoint))) {
+ qh->PRINTgood= True;
+ qh_option(qh, "Pgood", NULL, NULL);
+ }
+ }
+ if (qh->PRINTtransparent) {
+ if (qh->hull_dim != 4 || !qh->DELAUNAY || qh->VORONOI || qh->DROPdim >= 0) {
+ qh_fprintf(qh, qh->ferr, 6215, "qhull input error: transparent Delaunay('Gt') needs 3-d Delaunay('d') w/o 'GDn'\n");
+ qh_errexit(qh, qh_ERRinput, NULL, NULL);
+ }
+ qh->DROPdim = 3;
+ qh->PRINTridges = True;
+ }
+ for (i=qh_PRINTEND; i--; ) {
+ if (qh->PRINTout[i] == qh_PRINTgeom)
+ printgeom= True;
+ else if (qh->PRINTout[i] == qh_PRINTmathematica || qh->PRINTout[i] == qh_PRINTmaple)
+ printmath= True;
+ else if (qh->PRINTout[i] == qh_PRINTcoplanars)
+ printcoplanar= True;
+ else if (qh->PRINTout[i] == qh_PRINTpointnearest)
+ printcoplanar= True;
+ else if (qh->PRINTout[i] == qh_PRINTpointintersect && !qh->HALFspace) {
+ qh_fprintf(qh, qh->ferr, 6053, "qhull input error: option 'Fp' is only used for \nhalfspace intersection('Hn,n,n').\n");
+ qh_errexit(qh, qh_ERRinput, NULL, NULL);
+ }else if (qh->PRINTout[i] == qh_PRINTtriangles && (qh->HALFspace || qh->VORONOI)) {
+ qh_fprintf(qh, qh->ferr, 6054, "qhull input error: option 'Ft' is not available for Voronoi vertices or halfspace intersection\n");
+ qh_errexit(qh, qh_ERRinput, NULL, NULL);
+ }else if (qh->PRINTout[i] == qh_PRINTcentrums && qh->VORONOI) {
+ qh_fprintf(qh, qh->ferr, 6055, "qhull input error: option 'FC' is not available for Voronoi vertices('v')\n");
+ qh_errexit(qh, qh_ERRinput, NULL, NULL);
+ }else if (qh->PRINTout[i] == qh_PRINTvertices) {
+ if (qh->VORONOI)
+ qh_option(qh, "Fvoronoi", NULL, NULL);
+ else
+ qh_option(qh, "Fvertices", NULL, NULL);
+ }
+ }
+ if (printcoplanar && qh->DELAUNAY && qh->JOGGLEmax < REALmax/2) {
+ if (qh->PRINTprecision)
+ qh_fprintf(qh, qh->ferr, 7041, "qhull input warning: 'QJ' (joggle) will usually prevent coincident input sites for options 'Fc' and 'FP'\n");
+ }
+ if (printmath && (qh->hull_dim > 3 || qh->VORONOI)) {
+ qh_fprintf(qh, qh->ferr, 6056, "qhull input error: Mathematica and Maple output is only available for 2-d and 3-d convex hulls and 2-d Delaunay triangulations\n");
+ qh_errexit(qh, qh_ERRinput, NULL, NULL);
+ }
+ if (printgeom) {
+ if (qh->hull_dim > 4) {
+ qh_fprintf(qh, qh->ferr, 6057, "qhull input error: Geomview output is only available for 2-d, 3-d and 4-d\n");
+ qh_errexit(qh, qh_ERRinput, NULL, NULL);
+ }
+ if (qh->PRINTnoplanes && !(qh->PRINTcoplanar + qh->PRINTcentrums
+ + qh->PRINTdots + qh->PRINTspheres + qh->DOintersections + qh->PRINTridges)) {
+ qh_fprintf(qh, qh->ferr, 6058, "qhull input error: no output specified for Geomview\n");
+ qh_errexit(qh, qh_ERRinput, NULL, NULL);
+ }
+ if (qh->VORONOI && (qh->hull_dim > 3 || qh->DROPdim >= 0)) {
+ qh_fprintf(qh, qh->ferr, 6059, "qhull input error: Geomview output for Voronoi diagrams only for 2-d\n");
+ qh_errexit(qh, qh_ERRinput, NULL, NULL);
+ }
+ /* can not warn about furthest-site Geomview output: no lower_threshold */
+ if (qh->hull_dim == 4 && qh->DROPdim == -1 &&
+ (qh->PRINTcoplanar || qh->PRINTspheres || qh->PRINTcentrums)) {
+ qh_fprintf(qh, qh->ferr, 7042, "qhull input warning: coplanars, vertices, and centrums output not\n\
+available for 4-d output(ignored). Could use 'GDn' instead.\n");
+ qh->PRINTcoplanar= qh->PRINTspheres= qh->PRINTcentrums= False;
+ }
+ }
+ if (!qh->KEEPcoplanar && !qh->KEEPinside && !qh->ONLYgood) {
+ if ((qh->PRINTcoplanar && qh->PRINTspheres) || printcoplanar) {
+ if (qh->QHULLfinished) {
+ qh_fprintf(qh, qh->ferr, 7072, "qhull output warning: ignoring coplanar points, option 'Qc' was not set for the first run of qhull.\n");
+ }else {
+ qh->KEEPcoplanar = True;
+ qh_option(qh, "Qcoplanar", NULL, NULL);
+ }
+ }
+ }
+ qh->PRINTdim= qh->hull_dim;
+ if (qh->DROPdim >=0) { /* after Geomview checks */
+ if (qh->DROPdim < qh->hull_dim) {
+ qh->PRINTdim--;
+ if (!printgeom || qh->hull_dim < 3)
+ qh_fprintf(qh, qh->ferr, 7043, "qhull input warning: drop dimension 'GD%d' is only available for 3-d/4-d Geomview\n", qh->DROPdim);
+ }else
+ qh->DROPdim= -1;
+ }else if (qh->VORONOI) {
+ qh->DROPdim= qh->hull_dim-1;
+ qh->PRINTdim= qh->hull_dim-1;
+ }
+} /* qh_initqhull_outputflags */
+
+/*-<a href="qh-globa_r.htm#TOC"
+ >-------------------------------</a><a name="initqhull_start">-</a>
+
+ qh_initqhull_start(qh, infile, outfile, errfile )
+ allocate memory if needed and call qh_initqhull_start2()
+*/
+void qh_initqhull_start(qhT *qh, FILE *infile, FILE *outfile, FILE *errfile) {
+
+ qh_initstatistics(qh);
+ qh_initqhull_start2(qh, infile, outfile, errfile);
+} /* initqhull_start */
+
+/*-<a href="qh-globa_r.htm#TOC"
+ >-------------------------------</a><a name="initqhull_start2">-</a>
+
+ qh_initqhull_start2(qh, infile, outfile, errfile )
+ start initialization of qhull
+ initialize statistics, stdio, default values for global variables
+ assumes qh is allocated
+ notes:
+ report errors elsewhere, error handling and g_qhull_output [Qhull.cpp, QhullQh()] not in initialized
+ see:
+ qh_maxmin() determines the precision constants
+ qh_freeqhull()
+*/
+void qh_initqhull_start2(qhT *qh, FILE *infile, FILE *outfile, FILE *errfile) {
+ time_t timedata;
+ int seed;
+
+ qh_CPUclock; /* start the clock(for qh_clock). One-shot. */
+ /* memset is the same in qh_freeqhull() and qh_initqhull_start2() */
+ memset((char *)qh, 0, sizeof(qhT)-sizeof(qhmemT)-sizeof(qhstatT)); /* every field is 0, FALSE, NULL */
+ qh->NOerrexit= True;
+ qh->ANGLEmerge= True;
+ qh->DROPdim= -1;
+ qh->ferr= errfile;
+ qh->fin= infile;
+ qh->fout= outfile;
+ qh->furthest_id= qh_IDunknown;
+ qh->JOGGLEmax= REALmax;
+ qh->KEEPminArea = REALmax;
+ qh->last_low= REALmax;
+ qh->last_high= REALmax;
+ qh->last_newhigh= REALmax;
+ qh->last_random= 1;
+ qh->max_outside= 0.0;
+ qh->max_vertex= 0.0;
+ qh->MAXabs_coord= 0.0;
+ qh->MAXsumcoord= 0.0;
+ qh->MAXwidth= -REALmax;
+ qh->MERGEindependent= True;
+ qh->MINdenom_1= fmax_(1.0/REALmax, REALmin); /* used by qh_scalepoints */
+ qh->MINoutside= 0.0;
+ qh->MINvisible= REALmax;
+ qh->MAXcoplanar= REALmax;
+ qh->outside_err= REALmax;
+ qh->premerge_centrum= 0.0;
+ qh->premerge_cos= REALmax;
+ qh->PRINTprecision= True;
+ qh->PRINTradius= 0.0;
+ qh->postmerge_cos= REALmax;
+ qh->postmerge_centrum= 0.0;
+ qh->ROTATErandom= INT_MIN;
+ qh->MERGEvertices= True;
+ qh->totarea= 0.0;
+ qh->totvol= 0.0;
+ qh->TRACEdist= REALmax;
+ qh->TRACEpoint= qh_IDunknown; /* recompile or use 'TPn' */
+ qh->tracefacet_id= UINT_MAX; /* recompile to trace a facet */
+ qh->tracevertex_id= UINT_MAX; /* recompile to trace a vertex */
+ seed= (int)time(&timedata);
+ qh_RANDOMseed_(qh, seed);
+ qh->run_id= qh_RANDOMint;
+ if(!qh->run_id)
+ qh->run_id++; /* guarantee non-zero */
+ qh_option(qh, "run-id", &qh->run_id, NULL);
+ strcat(qh->qhull, "qhull");
+} /* initqhull_start2 */
+
+/*-<a href="qh-globa_r.htm#TOC"
+ >-------------------------------</a><a name="initthresholds">-</a>
+
+ qh_initthresholds(qh, commandString )
+ set thresholds for printing and scaling from commandString
+
+ returns:
+ sets qh.GOODthreshold or qh.SPLITthreshold if 'Pd0D1' used
+
+ see:
+ qh_initflags(), 'Qbk' 'QBk' 'Pdk' and 'PDk'
+ qh_inthresholds()
+
+ design:
+ for each 'Pdn' or 'PDn' option
+ check syntax
+ set qh.lower_threshold or qh.upper_threshold
+ set qh.GOODthreshold if an unbounded threshold is used
+ set qh.SPLITthreshold if a bounded threshold is used
+*/
+void qh_initthresholds(qhT *qh, char *command) {
+ realT value;
+ int idx, maxdim, k;
+ char *s= command; /* non-const due to strtol */
+ char key;
+
+ maxdim= qh->input_dim;
+ if (qh->DELAUNAY && (qh->PROJECTdelaunay || qh->PROJECTinput))
+ maxdim++;
+ while (*s) {
+ if (*s == '-')
+ s++;
+ if (*s == 'P') {
+ s++;
+ while (*s && !isspace(key= *s++)) {
+ if (key == 'd' || key == 'D') {
+ if (!isdigit(*s)) {
+ qh_fprintf(qh, qh->ferr, 7044, "qhull warning: no dimension given for Print option '%c' at: %s. Ignored\n",
+ key, s-1);
+ continue;
+ }
+ idx= qh_strtol(s, &s);
+ if (idx >= qh->hull_dim) {
+ qh_fprintf(qh, qh->ferr, 7045, "qhull warning: dimension %d for Print option '%c' is >= %d. Ignored\n",
+ idx, key, qh->hull_dim);
+ continue;
+ }
+ if (*s == ':') {
+ s++;
+ value= qh_strtod(s, &s);
+ if (fabs((double)value) > 1.0) {
+ qh_fprintf(qh, qh->ferr, 7046, "qhull warning: value %2.4g for Print option %c is > +1 or < -1. Ignored\n",
+ value, key);
+ continue;
+ }
+ }else
+ value= 0.0;
+ if (key == 'd')
+ qh->lower_threshold[idx]= value;
+ else
+ qh->upper_threshold[idx]= value;
+ }
+ }
+ }else if (*s == 'Q') {
+ s++;
+ while (*s && !isspace(key= *s++)) {
+ if (key == 'b' && *s == 'B') {
+ s++;
+ for (k=maxdim; k--; ) {
+ qh->lower_bound[k]= -qh_DEFAULTbox;
+ qh->upper_bound[k]= qh_DEFAULTbox;
+ }
+ }else if (key == 'b' && *s == 'b')
+ s++;
+ else if (key == 'b' || key == 'B') {
+ if (!isdigit(*s)) {
+ qh_fprintf(qh, qh->ferr, 7047, "qhull warning: no dimension given for Qhull option %c. Ignored\n",
+ key);
+ continue;
+ }
+ idx= qh_strtol(s, &s);
+ if (idx >= maxdim) {
+ qh_fprintf(qh, qh->ferr, 7048, "qhull warning: dimension %d for Qhull option %c is >= %d. Ignored\n",
+ idx, key, maxdim);
+ continue;
+ }
+ if (*s == ':') {
+ s++;
+ value= qh_strtod(s, &s);
+ }else if (key == 'b')
+ value= -qh_DEFAULTbox;
+ else
+ value= qh_DEFAULTbox;
+ if (key == 'b')
+ qh->lower_bound[idx]= value;
+ else
+ qh->upper_bound[idx]= value;
+ }
+ }
+ }else {
+ while (*s && !isspace(*s))
+ s++;
+ }
+ while (isspace(*s))
+ s++;
+ }
+ for (k=qh->hull_dim; k--; ) {
+ if (qh->lower_threshold[k] > -REALmax/2) {
+ qh->GOODthreshold= True;
+ if (qh->upper_threshold[k] < REALmax/2) {
+ qh->SPLITthresholds= True;
+ qh->GOODthreshold= False;
+ break;
+ }
+ }else if (qh->upper_threshold[k] < REALmax/2)
+ qh->GOODthreshold= True;
+ }
+} /* initthresholds */
+
+/*-<a href="qh-globa_r.htm#TOC"
+ >-------------------------------</a><a name="lib_check">-</a>
+
+ qh_lib_check( qhullLibraryType, qhTsize, vertexTsize, ridgeTsize, facetTsize, setTsize, qhmemTsize )
+ Report error if library does not agree with caller
+
+ notes:
+ NOerrors -- qh_lib_check can not call qh_errexit()
+*/
+void qh_lib_check(int qhullLibraryType, int qhTsize, int vertexTsize, int ridgeTsize, int facetTsize, int setTsize, int qhmemTsize) {
+ boolT iserror= False;
+
+#if defined(_MSC_VER) && defined(_DEBUG) && defined(QHULL_CRTDBG) /* user_r.h */
+ // _CrtSetBreakAlloc(744); /* Break at memalloc {744}, or 'watch' _crtBreakAlloc */
+ _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_DELAY_FREE_MEM_DF | _CRTDBG_LEAK_CHECK_DF | _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) );
+ _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG );
+ _CrtSetReportFile( _CRT_ERROR, _CRTDBG_FILE_STDERR );
+ _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG );
+ _CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDERR );
+ _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG );
+ _CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDERR );
+#endif
+
+ if (qhullLibraryType==QHULL_NON_REENTRANT) { /* 0 */
+ qh_fprintf_stderr(6257, "qh_lib_check: Incorrect qhull library called. Caller uses non-reentrant Qhull with a static qhT. Library is reentrant.\n");
+ iserror= True;
+ }else if (qhullLibraryType==QHULL_QH_POINTER) { /* 1 */
+ qh_fprintf_stderr(6258, "qh_lib_check: Incorrect qhull library called. Caller uses non-reentrant Qhull with a dynamic qhT via qh_QHpointer. Library is reentrant.\n");
+ iserror= True;
+ }else if (qhullLibraryType!=QHULL_REENTRANT) { /* 2 */
+ qh_fprintf_stderr(6262, "qh_lib_check: Expecting qhullLibraryType QHULL_NON_REENTRANT(0), QHULL_QH_POINTER(1), or QHULL_REENTRANT(2). Got %d\n", qhullLibraryType);
+ iserror= True;
+ }
+ if (qhTsize != sizeof(qhT)) {
+ qh_fprintf_stderr(6249, "qh_lib_check: Incorrect qhull library called. Size of qhT for caller is %d, but for library is %d.\n", qhTsize, sizeof(qhT));
+ iserror= True;
+ }
+ if (vertexTsize != sizeof(vertexT)) {
+ qh_fprintf_stderr(6250, "qh_lib_check: Incorrect qhull library called. Size of vertexT for caller is %d, but for library is %d.\n", vertexTsize, sizeof(vertexT));
+ iserror= True;
+ }
+ if (ridgeTsize != sizeof(ridgeT)) {
+ qh_fprintf_stderr(6251, "qh_lib_check: Incorrect qhull library called. Size of ridgeT for caller is %d, but for library is %d.\n", ridgeTsize, sizeof(ridgeT));
+ iserror= True;
+ }
+ if (facetTsize != sizeof(facetT)) {
+ qh_fprintf_stderr(6252, "qh_lib_check: Incorrect qhull library called. Size of facetT for caller is %d, but for library is %d.\n", facetTsize, sizeof(facetT));
+ iserror= True;
+ }
+ if (setTsize && setTsize != sizeof(setT)) {
+ qh_fprintf_stderr(6253, "qh_lib_check: Incorrect qhull library called. Size of setT for caller is %d, but for library is %d.\n", setTsize, sizeof(setT));
+ iserror= True;
+ }
+ if (qhmemTsize && qhmemTsize != sizeof(qhmemT)) {
+ qh_fprintf_stderr(6254, "qh_lib_check: Incorrect qhull library called. Size of qhmemT for caller is %d, but for library is %d.\n", qhmemTsize, sizeof(qhmemT));
+ iserror= True;
+ }
+ if (iserror) {
+ qh_fprintf_stderr(6259, "qh_lib_check: Cannot continue. Library '%s' is reentrant (e.g., qhull_r.so)\n", qh_version2);
+ qh_exit(qh_ERRqhull); /* can not use qh_errexit() */
+ }
+} /* lib_check */
+
+/*-<a href="qh-globa_r.htm#TOC"
+ >-------------------------------</a><a name="option">-</a>
+
+ qh_option(qh, option, intVal, realVal )
+ add an option description to qh.qhull_options
+
+ notes:
+ NOerrors -- qh_option can not call qh_errexit() [qh_initqhull_start2]
+ will be printed with statistics ('Ts') and errors
+ strlen(option) < 40
+*/
+void qh_option(qhT *qh, const char *option, int *i, realT *r) {
+ char buf[200];
+ int len, maxlen;
+
+ sprintf(buf, " %s", option);
+ if (i)
+ sprintf(buf+strlen(buf), " %d", *i);
+ if (r)
+ sprintf(buf+strlen(buf), " %2.2g", *r);
+ len= (int)strlen(buf); /* WARN64 */
+ qh->qhull_optionlen += len;
+ maxlen= sizeof(qh->qhull_options) - len -1;
+ maximize_(maxlen, 0);
+ if (qh->qhull_optionlen >= qh_OPTIONline && maxlen > 0) {
+ qh->qhull_optionlen= len;
+ strncat(qh->qhull_options, "\n", (size_t)(maxlen--));
+ }
+ strncat(qh->qhull_options, buf, (size_t)maxlen);
+} /* option */
+
+/*-<a href="qh-globa_r.htm#TOC"
+ >-------------------------------</a><a name="zero">-</a>
+
+ qh_zero( qh, errfile )
+ Initialize and zero Qhull's memory for qh_new_qhull()
+
+ notes:
+ Not needed in global.c because static variables are initialized to zero
+*/
+void qh_zero(qhT *qh, FILE *errfile) {
+ memset((char *)qh, 0, sizeof(qhT)); /* every field is 0, FALSE, NULL */
+ qh->NOerrexit= True;
+ qh_meminit(qh, errfile);
+} /* zero */
+
diff --git a/xs/src/qhull/src/libqhull_r/index.htm b/xs/src/qhull/src/libqhull_r/index.htm
new file mode 100644
index 000000000..c62030e06
--- /dev/null
+++ b/xs/src/qhull/src/libqhull_r/index.htm
@@ -0,0 +1,266 @@
+<!-- Do not edit with Front Page, it adds too many spaces -->
+<html>
+<head>
+<meta http-equiv="Content-Type"
+content="text/html; charset=iso-8859-1">
+<title>Reentrant Qhull functions, macros, and data structures</title>
+</head>
+
+<body>
+<!-- Navigation links -->
+<p><a name="TOP"><b>Up:</b></a> <a
+href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual</a>: Table of Contents <br>
+<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a>
+&#149; <a href="../../html/qh-quick.htm#options">Options</a>
+&#149; <a href="../../html/qh-opto.htm#output">Output</a>
+&#149; <a href="../../html/qh-optf.htm#format">Formats</a>
+&#149; <a href="../../html/qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="../../html/qh-optp.htm#print">Print</a>
+&#149; <a href="../../html/qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="../../html/qh-optc.htm#prec">Precision</a>
+&#149; <a href="../../html/qh-optt.htm#trace">Trace</a>
+&#149; <a href="index.htm">Functions</a><br>
+<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code</a><br>
+<b>To:</b> <a href="#TOC">Qhull files</a><br>
+<b>To:</b> <a href="qh-geom_r.htm">Geom</a> &#149; <a href="qh-globa_r.htm">Global</a>
+&#149; <a href="qh-io_r.htm">Io</a> &#149; <a href="qh-mem_r.htm">Mem</a>
+&#149; <a href="qh-merge_r.htm">Merge</a> &#149; <a href="qh-poly_r.htm">Poly</a>
+&#149; <a href="qh-qhull_r.htm">Qhull</a> &#149; <a href="qh-set_r.htm">Set</a>
+&#149; <a href="qh-stat_r.htm">Stat</a> &#149; <a href="qh-user_r.htm">User</a>
+
+<hr>
+<!-- Main text of document. -->
+
+<h1>Reentrant Qhull functions, macros, and data structures</h1>
+<blockquote>
+<p>The following sections provide an overview and index to
+reentrant Qhull's functions, macros, and data structures.
+Each section starts with an introduction.
+See also <a href=../../html/qh-code.htm#library>Calling
+Qhull from C programs</a> and <a href="../../html/qh-code.htm#cpp">Calling Qhull from C++ programs</a>.</p>
+
+<p>Qhull uses the following conventions:</p>
+<blockquote>
+
+<ul>
+<li>in code, global variables start with &quot;qh &quot;
+<li>in documentation, global variables start with 'qh.'
+<li>constants start with an upper case word
+<li>important globals include an '_'
+<li>functions, macros, and constants start with &quot;qh_&quot;</li>
+<li>data types end in &quot;T&quot;</li>
+<li>macros with arguments end in &quot;_&quot;</li>
+<li>iterators are macros that use local variables</li>
+<li>iterators for sets start with &quot;FOREACH&quot;</li>
+<li>iterators for lists start with &quot;FORALL&quot;</li>
+<li>qhull options are in single quotes (e.g., 'Pdn')</li>
+<li>lists are sorted alphabetically</li>
+<li>preprocessor directives on left margin for older compilers</li>
+</ul>
+</blockquote>
+<p>
+Reentrant Qhull is nearly the same as non-reentrant Qhull. In reentrant
+Qhull, the qhT data structure is the first parameter to most functions. Qhull accesses
+this data structure with 'qh->...'.
+In non-reentrant Qhull, the global data structure is either a struct (qh_QHpointer==0)
+or a pointer (qh_QHpointer==1). The non-reentrant code looks different because this data
+structure is accessed via the 'qh' macro. This macro expands to 'qh_qh.' or 'qh_qh->' (resp.).
+<p>
+When reading code with an editor, a search for
+'<i>&quot;function</i>'
+will locate the header of <i>qh_function</i>. A search for '<i>* function</i>'
+will locate the tail of <i>qh_function</i>.
+
+<p>A useful starting point is <a href="libqhull_r.h">libqhull_r.h</a>. It defines most
+of Qhull data structures and top-level functions. Search for <i>'PFn'</i> to
+determine the corresponding constant in Qhull. Search for <i>'Fp'</i> to
+determine the corresponding <a href="libqhull_r.h#qh_PRINT">qh_PRINT...</a> constant.
+Search <a href="io_r.c">io_r.c</a> to learn how the print function is implemented.</p>
+
+<p>If your web browser is configured for .c and .h files, the function, macro, and data type links
+go to the corresponding source location. To configure your web browser for .c and .h files.
+<ul>
+<li>In the Download Preferences or Options panel, add file extensions 'c' and 'h' to mime type 'text/html'.
+<li>Opera 12.10
+<ol>
+<li>In Tools > Preferences > Advanced > Downloads
+<li>Uncheck 'Hide file types opened with Opera'
+<li>Quick find 'html'
+<li>Select 'text/html' > Edit
+<li>Add File extensions 'c,h,'
+<li>Click 'OK'
+</ol>
+<li>Internet Explorer -- Mime types are not available from 'Internet Options'. Is there a registry key for these settings?
+<li>Firefox -- Mime types are not available from 'Preferences'. Is there an add-on to change the file extensions for a mime type?
+<li>Chrome -- Can Chrome be configured?
+</ul>
+
+<p>
+Please report documentation and link errors
+to <a href="mailto:qhull-bug@qhull.org">qhull-bug@qhull.org</a>.
+</blockquote>
+
+<p><b>Copyright &copy; 1997-2015 C.B. Barber</b></p>
+
+<hr>
+
+<h2><a href="#TOP">&#187;</a><a name="TOC">Qhull files</a> </h2>
+<blockquote>
+
+<p>This sections lists the .c and .h files for Qhull. Please
+refer to these files for detailed information.</p>
+<blockquote>
+
+<dl>
+<dt><a href="../../Makefile"><b>Makefile</b></a><b>, </b><a href="../../CMakeLists.txt"><b>CMakeLists.txt</b></a></dt>
+<dd><tt>Makefile</tt> is preconfigured for gcc. <tt>CMakeLists.txt</tt> supports multiple
+platforms with <a href=http://www.cmake.org/>CMake</a>.
+Qhull includes project files for Visual Studio and Qt.
+</dd>
+
+<dt>&nbsp;</dt>
+<dt><a href="libqhull_r.h"><b>libqhull_r.h</b></a> </dt>
+<dd>Include file for the Qhull library (<tt>libqhull.so</tt>, <tt>qhull.dll</tt>, <tt>libqhullstatic.a</tt>).
+Data structures are documented under <a href="qh-poly_r.htm">Poly</a>.
+Global variables are documented under <a href="qh-globa_r.htm">Global</a>.
+Other data structures and variables are documented under
+<a href="qh-qhull_r.htm#TOC">Qhull</a> or <a href="qh-geom_r.htm"><b>Geom</b></a><b>.</b></dd>
+
+<dt>&nbsp;</dt>
+<dt><a href="qh-geom_r.htm"><b>Geom</b></a><b>, </b>
+<a href="geom_r.h"><b>geom_r.h</b></a><b>, </b>
+<a href="geom_r.c"><b>geom_r.c</b></a><b>, </b>
+<a href="geom2_r.c"><b>geom2_r.c</b></a><b>, </b>
+<a href="random_r.c"><b>random_r.c</b></a><b>, </b>
+<a href="random_r.h"><b>random_r.h</b></a></dt>
+<dd>Geometric routines. These routines implement mathematical
+functions such as Gaussian elimination and geometric
+routines needed for Qhull. Frequently used routines are
+in <tt>geom_r.c</tt> while infrequent ones are in <tt>geom2_r.c</tt>.
+</dd>
+
+<dt>&nbsp;</dt>
+<dt><a href="qh-globa_r.htm"><b>Global</b></a><b>, </b>
+<a href="global_r.c"><b>global_r.c</b></a><b>, </b>
+<a href="libqhull_r.h"><b>libqhull_r.h</b></a> </dt>
+<dd>Global routines. Qhull uses a global data structure, <tt>qh</tt>,
+to store globally defined constants, lists, sets, and
+variables.
+<tt>global_r.c</tt> initializes and frees these
+structures. </dd>
+
+<dt>&nbsp;</dt>
+<dt><a href="qh-io_r.htm"><b>Io</b></a><b>, </b><a href="io_r.h"><b>io_r.h</b></a><b>,
+</b><a href="io_r.c"><b>io_r.c</b></a> </dt>
+<dd>Input and output routines. Qhull provides a wide range of
+input and output options.</dd>
+
+<dt>&nbsp;</dt>
+<dt><a href="qh-mem_r.htm"><b>Mem</b></a><b>, </b>
+<a href="mem_r.h"><b>mem_r.h</b></a><b>, </b>
+<a href="mem_r.c"><b>mem_r.c</b></a> </dt>
+<dd>Memory routines. Qhull provides memory allocation and
+deallocation. It uses quick-fit allocation.</dd>
+
+<dt>&nbsp;</dt>
+<dt><a href="qh-merge_r.htm"><b>Merge</b></a><b>, </b>
+<a href="merge_r.h"><b>merge_r.h</b></a><b>, </b>
+<a href="merge_r.c"><b>merge_r.c</b></a> </dt>
+<dd>Merge routines. Qhull handles precision problems by
+merged facets or joggled input. These routines merge simplicial facets,
+merge non-simplicial facets, merge cycles of facets, and
+rename redundant vertices.</dd>
+
+<dt>&nbsp;</dt>
+<dt><a href="qh-poly_r.htm"><b>Poly</b></a><b>, </b>
+<a href="poly_r.h"><b>poly_r.h</b></a><b>, </b>
+<a href="poly_r.c"><b>poly_r.c</b></a><b>, </b>
+<a href="poly2_r.c"><b>poly2_r.c</b></a><b>, </b>
+<a href="libqhull_r.h"><b>libqhull_r.h</b></a> </dt>
+<dd>Polyhedral routines. Qhull produces a polyhedron as a
+list of facets with vertices, neighbors, ridges, and
+geometric information. <tt>libqhull_r.h</tt> defines the main
+data structures. Frequently used routines are in <tt>poly_r.c</tt>
+while infrequent ones are in <tt>poly2_r.c</tt>.</dd>
+
+<dt>&nbsp;</dt>
+<dt><a href="qh-qhull_r.htm#TOC"><b>Qhull</b></a><b>, </b>
+<a href="libqhull_r.c"><b>libqhull_r.c</b></a><b>, </b>
+<a href="libqhull_r.h"><b>libqhull_r.h</b></a><b>, </b>
+<a href="qhull_ra.h"><b>qhull_ra.h</b></a><b>, </b>
+<a href="../qhull/unix_r.c"><b>unix_r.c</b></a> <b>, </b>
+<a href="../qconvex/qconvex_r.c"><b>qconvex_r.c</b></a> <b>, </b>
+<a href="../qdelaunay/qdelaun_r.c"><b>qdelaun_r.c</b></a> <b>, </b>
+<a href="../qhalf/qhalf_r.c"><b>qhalf_r.c</b></a> <b>, </b>
+<a href="../qvoronoi/qvoronoi_r.c"><b>qvoronoi_r.c</b></a> </dt>
+<dd>Top-level routines. The Quickhull algorithm is
+implemented by <tt>libqhull_r.c</tt>. <tt>qhull_ra.h</tt>
+includes all header files. </dd>
+
+<dt>&nbsp;</dt>
+<dt><a href="qh-set_r.htm"><b>Set</b></a><b>, </b>
+<a href="qset_r.h"><b>qset_r.h</b></a><b>, </b>
+<a href="qset_r.c"><b>qset_r.c</b></a> </dt>
+<dd>Set routines. Qhull implements its data structures as
+sets. A set is an array of pointers that is expanded as
+needed. This is a separate package that may be used in
+other applications. </dd>
+
+<dt>&nbsp;</dt>
+<dt><a href="qh-stat_r.htm"><b>Stat</b></a><b>, </b>
+<a href="stat_r.h"><b>stat_r.h</b></a><b>, </b>
+<a href="stat_r.c"><b>stat_r.c</b></a> </dt>
+<dd>Statistical routines. Qhull maintains statistics about
+its implementation. </dd>
+
+<dt>&nbsp;</dt>
+<dt><a href="qh-user_r.htm"><b>User</b></a><b>, </b>
+<a href="user_r.h"><b>user_r.h</b></a><b>, </b>
+<a href="user_r.c"><b>user_r.c</b></a><b>, </b>
+<a href="../user_eg/user_eg_r.c"><b>user_eg_r.c</b></a><b>, </b>
+<a href="../user_eg2/user_eg2_r.c"><b>user_eg2_r.c</b></a><b>, </b>
+<a href="../user_eg3/user_eg3_r.cpp"><b>user_eg3_r.cpp</b></a><b>, </b>
+</dt>
+<dd>User-defined routines. Qhull allows the user to configure
+the code with defined constants and specialized routines.
+</dd>
+</dl>
+</blockquote>
+
+</blockquote>
+<p><!-- Navigation links --> </p>
+<hr>
+<p><b>Up:</b>
+<a href="http://www.qhull.org">Home page for
+Qhull</a> <br>
+<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual: Table of Contents</a> <br>
+<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a>
+&#149; <a href="../../html/qh-quick.htm#options">Options</a>
+&#149; <a href="../../html/qh-opto.htm#output">Output</a>
+&#149; <a href="../../html/qh-optf.htm#format">Formats</a>
+&#149; <a href="../../html/qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="../../html/qh-optp.htm#print">Print</a>
+&#149; <a href="../../html/qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="../../html/qh-optc.htm#prec">Precision</a>
+&#149; <a href="../../html/qh-optt.htm#trace">Trace</a>
+&#149; <a href="index.htm">Functions</a><br>
+<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a> <br>
+<b>To:</b> <a href="#TOC">Qhull files</a><br>
+<b>To:</b> <a href="qh-geom_r.htm">Geom</a> &#149;
+<a href="qh-globa_r.htm">Global</a> &#149; <a href="qh-io_r.htm">Io</a>
+&#149; <a href="qh-mem_r.htm">Mem</a> &#149; <a href="qh-merge_r.htm">Merge</a>
+&#149; <a href="qh-poly_r.htm">Poly</a> &#149; <a href="qh-qhull_r.htm#TOC">Qhull</a>
+&#149; <a href="qh-set_r.htm">Set</a> &#149; <a href="qh-stat_r.htm">Stat</a>
+&#149; <a href="qh-user_r.htm">User</a><br>
+
+<p><!-- GC common information --> </p>
+<hr>
+<p><a href="http://www.geom.uiuc.edu/"><img
+src="../../html/qh--geom.gif" align="middle" width="40" height="40"></a><i>The
+Geometry Center Home Page </i></p>
+<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a>
+</a><br>
+Created: May 2, 1997 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p>
+</body>
+</html>
diff --git a/xs/src/qhull/src/libqhull_r/io_r.c b/xs/src/qhull/src/libqhull_r/io_r.c
new file mode 100644
index 000000000..9721a000d
--- /dev/null
+++ b/xs/src/qhull/src/libqhull_r/io_r.c
@@ -0,0 +1,4062 @@
+/*<html><pre> -<a href="qh-io_r.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ io_r.c
+ Input/Output routines of qhull application
+
+ see qh-io_r.htm and io_r.h
+
+ see user_r.c for qh_errprint and qh_printfacetlist
+
+ unix_r.c calls qh_readpoints and qh_produce_output
+
+ unix_r.c and user_r.c are the only callers of io_r.c functions
+ This allows the user to avoid loading io_r.o from qhull.a
+
+ Copyright (c) 1993-2015 The Geometry Center.
+ $Id: //main/2015/qhull/src/libqhull_r/io_r.c#4 $$Change: 2064 $
+ $DateTime: 2016/01/18 12:36:08 $$Author: bbarber $
+*/
+
+#include "qhull_ra.h"
+
+/*========= -functions in alphabetical order after qh_produce_output(qh) =====*/
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="produce_output">-</a>
+
+ qh_produce_output(qh)
+ qh_produce_output2(qh)
+ prints out the result of qhull in desired format
+ qh_produce_output2(qh) does not call qh_prepare_output(qh)
+ if qh.GETarea
+ computes and prints area and volume
+ qh.PRINTout[] is an array of output formats
+
+ notes:
+ prints output in qh.PRINTout order
+*/
+void qh_produce_output(qhT *qh) {
+ int tempsize= qh_setsize(qh, qh->qhmem.tempstack);
+
+ qh_prepare_output(qh);
+ qh_produce_output2(qh);
+ if (qh_setsize(qh, qh->qhmem.tempstack) != tempsize) {
+ qh_fprintf(qh, qh->ferr, 6206, "qhull internal error (qh_produce_output): temporary sets not empty(%d)\n",
+ qh_setsize(qh, qh->qhmem.tempstack));
+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+ }
+} /* produce_output */
+
+
+void qh_produce_output2(qhT *qh) {
+ int i, tempsize= qh_setsize(qh, qh->qhmem.tempstack), d_1;
+
+ if (qh->PRINTsummary)
+ qh_printsummary(qh, qh->ferr);
+ else if (qh->PRINTout[0] == qh_PRINTnone)
+ qh_printsummary(qh, qh->fout);
+ for (i=0; i < qh_PRINTEND; i++)
+ qh_printfacets(qh, qh->fout, qh->PRINTout[i], qh->facet_list, NULL, !qh_ALL);
+ qh_allstatistics(qh);
+ if (qh->PRINTprecision && !qh->MERGING && (qh->JOGGLEmax > REALmax/2 || qh->RERUN))
+ qh_printstats(qh, qh->ferr, qh->qhstat.precision, NULL);
+ if (qh->VERIFYoutput && (zzval_(Zridge) > 0 || zzval_(Zridgemid) > 0))
+ qh_printstats(qh, qh->ferr, qh->qhstat.vridges, NULL);
+ if (qh->PRINTstatistics) {
+ qh_printstatistics(qh, qh->ferr, "");
+ qh_memstatistics(qh, qh->ferr);
+ d_1= sizeof(setT) + (qh->hull_dim - 1) * SETelemsize;
+ qh_fprintf(qh, qh->ferr, 8040, "\
+ size in bytes: merge %d ridge %d vertex %d facet %d\n\
+ normal %d ridge vertices %d facet vertices or neighbors %d\n",
+ (int)sizeof(mergeT), (int)sizeof(ridgeT),
+ (int)sizeof(vertexT), (int)sizeof(facetT),
+ qh->normal_size, d_1, d_1 + SETelemsize);
+ }
+ if (qh_setsize(qh, qh->qhmem.tempstack) != tempsize) {
+ qh_fprintf(qh, qh->ferr, 6065, "qhull internal error (qh_produce_output2): temporary sets not empty(%d)\n",
+ qh_setsize(qh, qh->qhmem.tempstack));
+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+ }
+} /* produce_output2 */
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="dfacet">-</a>
+
+ qh_dfacet(qh, id )
+ print facet by id, for debugging
+
+*/
+void qh_dfacet(qhT *qh, unsigned id) {
+ facetT *facet;
+
+ FORALLfacets {
+ if (facet->id == id) {
+ qh_printfacet(qh, qh->fout, facet);
+ break;
+ }
+ }
+} /* dfacet */
+
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="dvertex">-</a>
+
+ qh_dvertex(qh, id )
+ print vertex by id, for debugging
+*/
+void qh_dvertex(qhT *qh, unsigned id) {
+ vertexT *vertex;
+
+ FORALLvertices {
+ if (vertex->id == id) {
+ qh_printvertex(qh, qh->fout, vertex);
+ break;
+ }
+ }
+} /* dvertex */
+
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="compare_facetarea">-</a>
+
+ qh_compare_facetarea(p1, p2 )
+ used by qsort() to order facets by area
+*/
+int qh_compare_facetarea(const void *p1, const void *p2) {
+ const facetT *a= *((facetT *const*)p1), *b= *((facetT *const*)p2);
+
+ if (!a->isarea)
+ return -1;
+ if (!b->isarea)
+ return 1;
+ if (a->f.area > b->f.area)
+ return 1;
+ else if (a->f.area == b->f.area)
+ return 0;
+ return -1;
+} /* compare_facetarea */
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="compare_facetmerge">-</a>
+
+ qh_compare_facetmerge(p1, p2 )
+ used by qsort() to order facets by number of merges
+*/
+int qh_compare_facetmerge(const void *p1, const void *p2) {
+ const facetT *a= *((facetT *const*)p1), *b= *((facetT *const*)p2);
+
+ return(a->nummerge - b->nummerge);
+} /* compare_facetvisit */
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="compare_facetvisit">-</a>
+
+ qh_compare_facetvisit(p1, p2 )
+ used by qsort() to order facets by visit id or id
+*/
+int qh_compare_facetvisit(const void *p1, const void *p2) {
+ const facetT *a= *((facetT *const*)p1), *b= *((facetT *const*)p2);
+ int i,j;
+
+ if (!(i= a->visitid))
+ i= 0 - a->id; /* do not convert to int, sign distinguishes id from visitid */
+ if (!(j= b->visitid))
+ j= 0 - b->id;
+ return(i - j);
+} /* compare_facetvisit */
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="compare_vertexpoint">-</a>
+
+ qh_compare_vertexpoint( p1, p2 )
+ used by qsort() to order vertices by point id
+
+ Not usable in qhulllib_r since qh_pointid depends on qh
+
+ int qh_compare_vertexpoint(const void *p1, const void *p2) {
+ const vertexT *a= *((vertexT *const*)p1), *b= *((vertexT *const*)p2);
+
+ return((qh_pointid(qh, a->point) > qh_pointid(qh, b->point)?1:-1));
+}*/
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="copyfilename">-</a>
+
+ qh_copyfilename(qh, dest, size, source, length )
+ copy filename identified by qh_skipfilename()
+
+ notes:
+ see qh_skipfilename() for syntax
+*/
+void qh_copyfilename(qhT *qh, char *filename, int size, const char* source, int length) {
+ char c= *source;
+
+ if (length > size + 1) {
+ qh_fprintf(qh, qh->ferr, 6040, "qhull error: filename is more than %d characters, %s\n", size-1, source);
+ qh_errexit(qh, qh_ERRinput, NULL, NULL);
+ }
+ strncpy(filename, source, length);
+ filename[length]= '\0';
+ if (c == '\'' || c == '"') {
+ char *s= filename + 1;
+ char *t= filename;
+ while (*s) {
+ if (*s == c) {
+ if (s[-1] == '\\')
+ t[-1]= c;
+ }else
+ *t++= *s;
+ s++;
+ }
+ *t= '\0';
+ }
+} /* copyfilename */
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="countfacets">-</a>
+
+ qh_countfacets(qh, facetlist, facets, printall,
+ numfacets, numsimplicial, totneighbors, numridges, numcoplanar, numtricoplanars )
+ count good facets for printing and set visitid
+ if allfacets, ignores qh_skipfacet()
+
+ notes:
+ qh_printsummary and qh_countfacets must match counts
+
+ returns:
+ numfacets, numsimplicial, total neighbors, numridges, coplanars
+ each facet with ->visitid indicating 1-relative position
+ ->visitid==0 indicates not good
+
+ notes
+ numfacets >= numsimplicial
+ if qh.NEWfacets,
+ does not count visible facets (matches qh_printafacet)
+
+ design:
+ for all facets on facetlist and in facets set
+ unless facet is skipped or visible (i.e., will be deleted)
+ mark facet->visitid
+ update counts
+*/
+void qh_countfacets(qhT *qh, facetT *facetlist, setT *facets, boolT printall,
+ int *numfacetsp, int *numsimplicialp, int *totneighborsp, int *numridgesp, int *numcoplanarsp, int *numtricoplanarsp) {
+ facetT *facet, **facetp;
+ int numfacets= 0, numsimplicial= 0, numridges= 0, totneighbors= 0, numcoplanars= 0, numtricoplanars= 0;
+
+ FORALLfacet_(facetlist) {
+ if ((facet->visible && qh->NEWfacets)
+ || (!printall && qh_skipfacet(qh, facet)))
+ facet->visitid= 0;
+ else {
+ facet->visitid= ++numfacets;
+ totneighbors += qh_setsize(qh, facet->neighbors);
+ if (facet->simplicial) {
+ numsimplicial++;
+ if (facet->keepcentrum && facet->tricoplanar)
+ numtricoplanars++;
+ }else
+ numridges += qh_setsize(qh, facet->ridges);
+ if (facet->coplanarset)
+ numcoplanars += qh_setsize(qh, facet->coplanarset);
+ }
+ }
+
+ FOREACHfacet_(facets) {
+ if ((facet->visible && qh->NEWfacets)
+ || (!printall && qh_skipfacet(qh, facet)))
+ facet->visitid= 0;
+ else {
+ facet->visitid= ++numfacets;
+ totneighbors += qh_setsize(qh, facet->neighbors);
+ if (facet->simplicial){
+ numsimplicial++;
+ if (facet->keepcentrum && facet->tricoplanar)
+ numtricoplanars++;
+ }else
+ numridges += qh_setsize(qh, facet->ridges);
+ if (facet->coplanarset)
+ numcoplanars += qh_setsize(qh, facet->coplanarset);
+ }
+ }
+ qh->visit_id += numfacets+1;
+ *numfacetsp= numfacets;
+ *numsimplicialp= numsimplicial;
+ *totneighborsp= totneighbors;
+ *numridgesp= numridges;
+ *numcoplanarsp= numcoplanars;
+ *numtricoplanarsp= numtricoplanars;
+} /* countfacets */
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="detvnorm">-</a>
+
+ qh_detvnorm(qh, vertex, vertexA, centers, offset )
+ compute separating plane of the Voronoi diagram for a pair of input sites
+ centers= set of facets (i.e., Voronoi vertices)
+ facet->visitid= 0 iff vertex-at-infinity (i.e., unbounded)
+
+ assumes:
+ qh_ASvoronoi and qh_vertexneighbors() already set
+
+ returns:
+ norm
+ a pointer into qh.gm_matrix to qh.hull_dim-1 reals
+ copy the data before reusing qh.gm_matrix
+ offset
+ if 'QVn'
+ sign adjusted so that qh.GOODvertexp is inside
+ else
+ sign adjusted so that vertex is inside
+
+ qh.gm_matrix= simplex of points from centers relative to first center
+
+ notes:
+ in io_r.c so that code for 'v Tv' can be removed by removing io_r.c
+ returns pointer into qh.gm_matrix to avoid tracking of temporary memory
+
+ design:
+ determine midpoint of input sites
+ build points as the set of Voronoi vertices
+ select a simplex from points (if necessary)
+ include midpoint if the Voronoi region is unbounded
+ relocate the first vertex of the simplex to the origin
+ compute the normalized hyperplane through the simplex
+ orient the hyperplane toward 'QVn' or 'vertex'
+ if 'Tv' or 'Ts'
+ if bounded
+ test that hyperplane is the perpendicular bisector of the input sites
+ test that Voronoi vertices not in the simplex are still on the hyperplane
+ free up temporary memory
+*/
+pointT *qh_detvnorm(qhT *qh, vertexT *vertex, vertexT *vertexA, setT *centers, realT *offsetp) {
+ facetT *facet, **facetp;
+ int i, k, pointid, pointidA, point_i, point_n;
+ setT *simplex= NULL;
+ pointT *point, **pointp, *point0, *midpoint, *normal, *inpoint;
+ coordT *coord, *gmcoord, *normalp;
+ setT *points= qh_settemp(qh, qh->TEMPsize);
+ boolT nearzero= False;
+ boolT unbounded= False;
+ int numcenters= 0;
+ int dim= qh->hull_dim - 1;
+ realT dist, offset, angle, zero= 0.0;
+
+ midpoint= qh->gm_matrix + qh->hull_dim * qh->hull_dim; /* last row */
+ for (k=0; k < dim; k++)
+ midpoint[k]= (vertex->point[k] + vertexA->point[k])/2;
+ FOREACHfacet_(centers) {
+ numcenters++;
+ if (!facet->visitid)
+ unbounded= True;
+ else {
+ if (!facet->center)
+ facet->center= qh_facetcenter(qh, facet->vertices);
+ qh_setappend(qh, &points, facet->center);
+ }
+ }
+ if (numcenters > dim) {
+ simplex= qh_settemp(qh, qh->TEMPsize);
+ qh_setappend(qh, &simplex, vertex->point);
+ if (unbounded)
+ qh_setappend(qh, &simplex, midpoint);
+ qh_maxsimplex(qh, dim, points, NULL, 0, &simplex);
+ qh_setdelnth(qh, simplex, 0);
+ }else if (numcenters == dim) {
+ if (unbounded)
+ qh_setappend(qh, &points, midpoint);
+ simplex= points;
+ }else {
+ qh_fprintf(qh, qh->ferr, 6216, "qhull internal error (qh_detvnorm): too few points(%d) to compute separating plane\n", numcenters);
+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+ }
+ i= 0;
+ gmcoord= qh->gm_matrix;
+ point0= SETfirstt_(simplex, pointT);
+ FOREACHpoint_(simplex) {
+ if (qh->IStracing >= 4)
+ qh_printmatrix(qh, qh->ferr, "qh_detvnorm: Voronoi vertex or midpoint",
+ &point, 1, dim);
+ if (point != point0) {
+ qh->gm_row[i++]= gmcoord;
+ coord= point0;
+ for (k=dim; k--; )
+ *(gmcoord++)= *point++ - *coord++;
+ }
+ }
+ qh->gm_row[i]= gmcoord; /* does not overlap midpoint, may be used later for qh_areasimplex */
+ normal= gmcoord;
+ qh_sethyperplane_gauss(qh, dim, qh->gm_row, point0, True,
+ normal, &offset, &nearzero);
+ if (qh->GOODvertexp == vertexA->point)
+ inpoint= vertexA->point;
+ else
+ inpoint= vertex->point;
+ zinc_(Zdistio);
+ dist= qh_distnorm(dim, inpoint, normal, &offset);
+ if (dist > 0) {
+ offset= -offset;
+ normalp= normal;
+ for (k=dim; k--; ) {
+ *normalp= -(*normalp);
+ normalp++;
+ }
+ }
+ if (qh->VERIFYoutput || qh->PRINTstatistics) {
+ pointid= qh_pointid(qh, vertex->point);
+ pointidA= qh_pointid(qh, vertexA->point);
+ if (!unbounded) {
+ zinc_(Zdiststat);
+ dist= qh_distnorm(dim, midpoint, normal, &offset);
+ if (dist < 0)
+ dist= -dist;
+ zzinc_(Zridgemid);
+ wwmax_(Wridgemidmax, dist);
+ wwadd_(Wridgemid, dist);
+ trace4((qh, qh->ferr, 4014, "qh_detvnorm: points %d %d midpoint dist %2.2g\n",
+ pointid, pointidA, dist));
+ for (k=0; k < dim; k++)
+ midpoint[k]= vertexA->point[k] - vertex->point[k]; /* overwrites midpoint! */
+ qh_normalize(qh, midpoint, dim, False);
+ angle= qh_distnorm(dim, midpoint, normal, &zero); /* qh_detangle uses dim+1 */
+ if (angle < 0.0)
+ angle= angle + 1.0;
+ else
+ angle= angle - 1.0;
+ if (angle < 0.0)
+ angle -= angle;
+ trace4((qh, qh->ferr, 4015, "qh_detvnorm: points %d %d angle %2.2g nearzero %d\n",
+ pointid, pointidA, angle, nearzero));
+ if (nearzero) {
+ zzinc_(Zridge0);
+ wwmax_(Wridge0max, angle);
+ wwadd_(Wridge0, angle);
+ }else {
+ zzinc_(Zridgeok)
+ wwmax_(Wridgeokmax, angle);
+ wwadd_(Wridgeok, angle);
+ }
+ }
+ if (simplex != points) {
+ FOREACHpoint_i_(qh, points) {
+ if (!qh_setin(simplex, point)) {
+ facet= SETelemt_(centers, point_i, facetT);
+ zinc_(Zdiststat);
+ dist= qh_distnorm(dim, point, normal, &offset);
+ if (dist < 0)
+ dist= -dist;
+ zzinc_(Zridge);
+ wwmax_(Wridgemax, dist);
+ wwadd_(Wridge, dist);
+ trace4((qh, qh->ferr, 4016, "qh_detvnorm: points %d %d Voronoi vertex %d dist %2.2g\n",
+ pointid, pointidA, facet->visitid, dist));
+ }
+ }
+ }
+ }
+ *offsetp= offset;
+ if (simplex != points)
+ qh_settempfree(qh, &simplex);
+ qh_settempfree(qh, &points);
+ return normal;
+} /* detvnorm */
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="detvridge">-</a>
+
+ qh_detvridge(qh, vertexA )
+ determine Voronoi ridge from 'seen' neighbors of vertexA
+ include one vertex-at-infinite if an !neighbor->visitid
+
+ returns:
+ temporary set of centers (facets, i.e., Voronoi vertices)
+ sorted by center id
+*/
+setT *qh_detvridge(qhT *qh, vertexT *vertex) {
+ setT *centers= qh_settemp(qh, qh->TEMPsize);
+ setT *tricenters= qh_settemp(qh, qh->TEMPsize);
+ facetT *neighbor, **neighborp;
+ boolT firstinf= True;
+
+ FOREACHneighbor_(vertex) {
+ if (neighbor->seen) {
+ if (neighbor->visitid) {
+ if (!neighbor->tricoplanar || qh_setunique(qh, &tricenters, neighbor->center))
+ qh_setappend(qh, &centers, neighbor);
+ }else if (firstinf) {
+ firstinf= False;
+ qh_setappend(qh, &centers, neighbor);
+ }
+ }
+ }
+ qsort(SETaddr_(centers, facetT), (size_t)qh_setsize(qh, centers),
+ sizeof(facetT *), qh_compare_facetvisit);
+ qh_settempfree(qh, &tricenters);
+ return centers;
+} /* detvridge */
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="detvridge3">-</a>
+
+ qh_detvridge3(qh, atvertex, vertex )
+ determine 3-d Voronoi ridge from 'seen' neighbors of atvertex and vertex
+ include one vertex-at-infinite for !neighbor->visitid
+ assumes all facet->seen2= True
+
+ returns:
+ temporary set of centers (facets, i.e., Voronoi vertices)
+ listed in adjacency order (!oriented)
+ all facet->seen2= True
+
+ design:
+ mark all neighbors of atvertex
+ for each adjacent neighbor of both atvertex and vertex
+ if neighbor selected
+ add neighbor to set of Voronoi vertices
+*/
+setT *qh_detvridge3(qhT *qh, vertexT *atvertex, vertexT *vertex) {
+ setT *centers= qh_settemp(qh, qh->TEMPsize);
+ setT *tricenters= qh_settemp(qh, qh->TEMPsize);
+ facetT *neighbor, **neighborp, *facet= NULL;
+ boolT firstinf= True;
+
+ FOREACHneighbor_(atvertex)
+ neighbor->seen2= False;
+ FOREACHneighbor_(vertex) {
+ if (!neighbor->seen2) {
+ facet= neighbor;
+ break;
+ }
+ }
+ while (facet) {
+ facet->seen2= True;
+ if (neighbor->seen) {
+ if (facet->visitid) {
+ if (!facet->tricoplanar || qh_setunique(qh, &tricenters, facet->center))
+ qh_setappend(qh, &centers, facet);
+ }else if (firstinf) {
+ firstinf= False;
+ qh_setappend(qh, &centers, facet);
+ }
+ }
+ FOREACHneighbor_(facet) {
+ if (!neighbor->seen2) {
+ if (qh_setin(vertex->neighbors, neighbor))
+ break;
+ else
+ neighbor->seen2= True;
+ }
+ }
+ facet= neighbor;
+ }
+ if (qh->CHECKfrequently) {
+ FOREACHneighbor_(vertex) {
+ if (!neighbor->seen2) {
+ qh_fprintf(qh, qh->ferr, 6217, "qhull internal error (qh_detvridge3): neighbors of vertex p%d are not connected at facet %d\n",
+ qh_pointid(qh, vertex->point), neighbor->id);
+ qh_errexit(qh, qh_ERRqhull, neighbor, NULL);
+ }
+ }
+ }
+ FOREACHneighbor_(atvertex)
+ neighbor->seen2= True;
+ qh_settempfree(qh, &tricenters);
+ return centers;
+} /* detvridge3 */
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="eachvoronoi">-</a>
+
+ qh_eachvoronoi(qh, fp, printvridge, vertex, visitall, innerouter, inorder )
+ if visitall,
+ visit all Voronoi ridges for vertex (i.e., an input site)
+ else
+ visit all unvisited Voronoi ridges for vertex
+ all vertex->seen= False if unvisited
+ assumes
+ all facet->seen= False
+ all facet->seen2= True (for qh_detvridge3)
+ all facet->visitid == 0 if vertex_at_infinity
+ == index of Voronoi vertex
+ >= qh.num_facets if ignored
+ innerouter:
+ qh_RIDGEall-- both inner (bounded) and outer(unbounded) ridges
+ qh_RIDGEinner- only inner
+ qh_RIDGEouter- only outer
+
+ if inorder
+ orders vertices for 3-d Voronoi diagrams
+
+ returns:
+ number of visited ridges (does not include previously visited ridges)
+
+ if printvridge,
+ calls printvridge( fp, vertex, vertexA, centers)
+ fp== any pointer (assumes FILE*)
+ vertex,vertexA= pair of input sites that define a Voronoi ridge
+ centers= set of facets (i.e., Voronoi vertices)
+ ->visitid == index or 0 if vertex_at_infinity
+ ordered for 3-d Voronoi diagram
+ notes:
+ uses qh.vertex_visit
+
+ see:
+ qh_eachvoronoi_all()
+
+ design:
+ mark selected neighbors of atvertex
+ for each selected neighbor (either Voronoi vertex or vertex-at-infinity)
+ for each unvisited vertex
+ if atvertex and vertex share more than d-1 neighbors
+ bump totalcount
+ if printvridge defined
+ build the set of shared neighbors (i.e., Voronoi vertices)
+ call printvridge
+*/
+int qh_eachvoronoi(qhT *qh, FILE *fp, printvridgeT printvridge, vertexT *atvertex, boolT visitall, qh_RIDGE innerouter, boolT inorder) {
+ boolT unbounded;
+ int count;
+ facetT *neighbor, **neighborp, *neighborA, **neighborAp;
+ setT *centers;
+ setT *tricenters= qh_settemp(qh, qh->TEMPsize);
+
+ vertexT *vertex, **vertexp;
+ boolT firstinf;
+ unsigned int numfacets= (unsigned int)qh->num_facets;
+ int totridges= 0;
+
+ qh->vertex_visit++;
+ atvertex->seen= True;
+ if (visitall) {
+ FORALLvertices
+ vertex->seen= False;
+ }
+ FOREACHneighbor_(atvertex) {
+ if (neighbor->visitid < numfacets)
+ neighbor->seen= True;
+ }
+ FOREACHneighbor_(atvertex) {
+ if (neighbor->seen) {
+ FOREACHvertex_(neighbor->vertices) {
+ if (vertex->visitid != qh->vertex_visit && !vertex->seen) {
+ vertex->visitid= qh->vertex_visit;
+ count= 0;
+ firstinf= True;
+ qh_settruncate(qh, tricenters, 0);
+ FOREACHneighborA_(vertex) {
+ if (neighborA->seen) {
+ if (neighborA->visitid) {
+ if (!neighborA->tricoplanar || qh_setunique(qh, &tricenters, neighborA->center))
+ count++;
+ }else if (firstinf) {
+ count++;
+ firstinf= False;
+ }
+ }
+ }
+ if (count >= qh->hull_dim - 1) { /* e.g., 3 for 3-d Voronoi */
+ if (firstinf) {
+ if (innerouter == qh_RIDGEouter)
+ continue;
+ unbounded= False;
+ }else {
+ if (innerouter == qh_RIDGEinner)
+ continue;
+ unbounded= True;
+ }
+ totridges++;
+ trace4((qh, qh->ferr, 4017, "qh_eachvoronoi: Voronoi ridge of %d vertices between sites %d and %d\n",
+ count, qh_pointid(qh, atvertex->point), qh_pointid(qh, vertex->point)));
+ if (printvridge && fp) {
+ if (inorder && qh->hull_dim == 3+1) /* 3-d Voronoi diagram */
+ centers= qh_detvridge3(qh, atvertex, vertex);
+ else
+ centers= qh_detvridge(qh, vertex);
+ (*printvridge)(qh, fp, atvertex, vertex, centers, unbounded);
+ qh_settempfree(qh, &centers);
+ }
+ }
+ }
+ }
+ }
+ }
+ FOREACHneighbor_(atvertex)
+ neighbor->seen= False;
+ qh_settempfree(qh, &tricenters);
+ return totridges;
+} /* eachvoronoi */
+
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="eachvoronoi_all">-</a>
+
+ qh_eachvoronoi_all(qh, fp, printvridge, isUpper, innerouter, inorder )
+ visit all Voronoi ridges
+
+ innerouter:
+ see qh_eachvoronoi()
+
+ if inorder
+ orders vertices for 3-d Voronoi diagrams
+
+ returns
+ total number of ridges
+
+ if isUpper == facet->upperdelaunay (i.e., a Vornoi vertex)
+ facet->visitid= Voronoi vertex index(same as 'o' format)
+ else
+ facet->visitid= 0
+
+ if printvridge,
+ calls printvridge( fp, vertex, vertexA, centers)
+ [see qh_eachvoronoi]
+
+ notes:
+ Not used for qhull.exe
+ same effect as qh_printvdiagram but ridges not sorted by point id
+*/
+int qh_eachvoronoi_all(qhT *qh, FILE *fp, printvridgeT printvridge, boolT isUpper, qh_RIDGE innerouter, boolT inorder) {
+ facetT *facet;
+ vertexT *vertex;
+ int numcenters= 1; /* vertex 0 is vertex-at-infinity */
+ int totridges= 0;
+
+ qh_clearcenters(qh, qh_ASvoronoi);
+ qh_vertexneighbors(qh);
+ maximize_(qh->visit_id, (unsigned) qh->num_facets);
+ FORALLfacets {
+ facet->visitid= 0;
+ facet->seen= False;
+ facet->seen2= True;
+ }
+ FORALLfacets {
+ if (facet->upperdelaunay == isUpper)
+ facet->visitid= numcenters++;
+ }
+ FORALLvertices
+ vertex->seen= False;
+ FORALLvertices {
+ if (qh->GOODvertex > 0 && qh_pointid(qh, vertex->point)+1 != qh->GOODvertex)
+ continue;
+ totridges += qh_eachvoronoi(qh, fp, printvridge, vertex,
+ !qh_ALL, innerouter, inorder);
+ }
+ return totridges;
+} /* eachvoronoi_all */
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="facet2point">-</a>
+
+ qh_facet2point(qh, facet, point0, point1, mindist )
+ return two projected temporary vertices for a 2-d facet
+ may be non-simplicial
+
+ returns:
+ point0 and point1 oriented and projected to the facet
+ returns mindist (maximum distance below plane)
+*/
+void qh_facet2point(qhT *qh, facetT *facet, pointT **point0, pointT **point1, realT *mindist) {
+ vertexT *vertex0, *vertex1;
+ realT dist;
+
+ if (facet->toporient ^ qh_ORIENTclock) {
+ vertex0= SETfirstt_(facet->vertices, vertexT);
+ vertex1= SETsecondt_(facet->vertices, vertexT);
+ }else {
+ vertex1= SETfirstt_(facet->vertices, vertexT);
+ vertex0= SETsecondt_(facet->vertices, vertexT);
+ }
+ zadd_(Zdistio, 2);
+ qh_distplane(qh, vertex0->point, facet, &dist);
+ *mindist= dist;
+ *point0= qh_projectpoint(qh, vertex0->point, facet, dist);
+ qh_distplane(qh, vertex1->point, facet, &dist);
+ minimize_(*mindist, dist);
+ *point1= qh_projectpoint(qh, vertex1->point, facet, dist);
+} /* facet2point */
+
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="facetvertices">-</a>
+
+ qh_facetvertices(qh, facetlist, facets, allfacets )
+ returns temporary set of vertices in a set and/or list of facets
+ if allfacets, ignores qh_skipfacet()
+
+ returns:
+ vertices with qh.vertex_visit
+
+ notes:
+ optimized for allfacets of facet_list
+
+ design:
+ if allfacets of facet_list
+ create vertex set from vertex_list
+ else
+ for each selected facet in facets or facetlist
+ append unvisited vertices to vertex set
+*/
+setT *qh_facetvertices(qhT *qh, facetT *facetlist, setT *facets, boolT allfacets) {
+ setT *vertices;
+ facetT *facet, **facetp;
+ vertexT *vertex, **vertexp;
+
+ qh->vertex_visit++;
+ if (facetlist == qh->facet_list && allfacets && !facets) {
+ vertices= qh_settemp(qh, qh->num_vertices);
+ FORALLvertices {
+ vertex->visitid= qh->vertex_visit;
+ qh_setappend(qh, &vertices, vertex);
+ }
+ }else {
+ vertices= qh_settemp(qh, qh->TEMPsize);
+ FORALLfacet_(facetlist) {
+ if (!allfacets && qh_skipfacet(qh, facet))
+ continue;
+ FOREACHvertex_(facet->vertices) {
+ if (vertex->visitid != qh->vertex_visit) {
+ vertex->visitid= qh->vertex_visit;
+ qh_setappend(qh, &vertices, vertex);
+ }
+ }
+ }
+ }
+ FOREACHfacet_(facets) {
+ if (!allfacets && qh_skipfacet(qh, facet))
+ continue;
+ FOREACHvertex_(facet->vertices) {
+ if (vertex->visitid != qh->vertex_visit) {
+ vertex->visitid= qh->vertex_visit;
+ qh_setappend(qh, &vertices, vertex);
+ }
+ }
+ }
+ return vertices;
+} /* facetvertices */
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="geomplanes">-</a>
+
+ qh_geomplanes(qh, facet, outerplane, innerplane )
+ return outer and inner planes for Geomview
+ qh.PRINTradius is size of vertices and points (includes qh.JOGGLEmax)
+
+ notes:
+ assume precise calculations in io.c with roundoff covered by qh_GEOMepsilon
+*/
+void qh_geomplanes(qhT *qh, facetT *facet, realT *outerplane, realT *innerplane) {
+ realT radius;
+
+ if (qh->MERGING || qh->JOGGLEmax < REALmax/2) {
+ qh_outerinner(qh, facet, outerplane, innerplane);
+ radius= qh->PRINTradius;
+ if (qh->JOGGLEmax < REALmax/2)
+ radius -= qh->JOGGLEmax * sqrt((realT)qh->hull_dim); /* already accounted for in qh_outerinner() */
+ *outerplane += radius;
+ *innerplane -= radius;
+ if (qh->PRINTcoplanar || qh->PRINTspheres) {
+ *outerplane += qh->MAXabs_coord * qh_GEOMepsilon;
+ *innerplane -= qh->MAXabs_coord * qh_GEOMepsilon;
+ }
+ }else
+ *innerplane= *outerplane= 0;
+} /* geomplanes */
+
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="markkeep">-</a>
+
+ qh_markkeep(qh, facetlist )
+ mark good facets that meet qh.KEEParea, qh.KEEPmerge, and qh.KEEPminArea
+ ignores visible facets (!part of convex hull)
+
+ returns:
+ may clear facet->good
+ recomputes qh.num_good
+
+ design:
+ get set of good facets
+ if qh.KEEParea
+ sort facets by area
+ clear facet->good for all but n largest facets
+ if qh.KEEPmerge
+ sort facets by merge count
+ clear facet->good for all but n most merged facets
+ if qh.KEEPminarea
+ clear facet->good if area too small
+ update qh.num_good
+*/
+void qh_markkeep(qhT *qh, facetT *facetlist) {
+ facetT *facet, **facetp;
+ setT *facets= qh_settemp(qh, qh->num_facets);
+ int size, count;
+
+ trace2((qh, qh->ferr, 2006, "qh_markkeep: only keep %d largest and/or %d most merged facets and/or min area %.2g\n",
+ qh->KEEParea, qh->KEEPmerge, qh->KEEPminArea));
+ FORALLfacet_(facetlist) {
+ if (!facet->visible && facet->good)
+ qh_setappend(qh, &facets, facet);
+ }
+ size= qh_setsize(qh, facets);
+ if (qh->KEEParea) {
+ qsort(SETaddr_(facets, facetT), (size_t)size,
+ sizeof(facetT *), qh_compare_facetarea);
+ if ((count= size - qh->KEEParea) > 0) {
+ FOREACHfacet_(facets) {
+ facet->good= False;
+ if (--count == 0)
+ break;
+ }
+ }
+ }
+ if (qh->KEEPmerge) {
+ qsort(SETaddr_(facets, facetT), (size_t)size,
+ sizeof(facetT *), qh_compare_facetmerge);
+ if ((count= size - qh->KEEPmerge) > 0) {
+ FOREACHfacet_(facets) {
+ facet->good= False;
+ if (--count == 0)
+ break;
+ }
+ }
+ }
+ if (qh->KEEPminArea < REALmax/2) {
+ FOREACHfacet_(facets) {
+ if (!facet->isarea || facet->f.area < qh->KEEPminArea)
+ facet->good= False;
+ }
+ }
+ qh_settempfree(qh, &facets);
+ count= 0;
+ FORALLfacet_(facetlist) {
+ if (facet->good)
+ count++;
+ }
+ qh->num_good= count;
+} /* markkeep */
+
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="markvoronoi">-</a>
+
+ qh_markvoronoi(qh, facetlist, facets, printall, isLower, numcenters )
+ mark voronoi vertices for printing by site pairs
+
+ returns:
+ temporary set of vertices indexed by pointid
+ isLower set if printing lower hull (i.e., at least one facet is lower hull)
+ numcenters= total number of Voronoi vertices
+ bumps qh.printoutnum for vertex-at-infinity
+ clears all facet->seen and sets facet->seen2
+
+ if selected
+ facet->visitid= Voronoi vertex id
+ else if upper hull (or 'Qu' and lower hull)
+ facet->visitid= 0
+ else
+ facet->visitid >= qh->num_facets
+
+ notes:
+ ignores qh.ATinfinity, if defined
+*/
+setT *qh_markvoronoi(qhT *qh, facetT *facetlist, setT *facets, boolT printall, boolT *isLowerp, int *numcentersp) {
+ int numcenters=0;
+ facetT *facet, **facetp;
+ setT *vertices;
+ boolT isLower= False;
+
+ qh->printoutnum++;
+ qh_clearcenters(qh, qh_ASvoronoi); /* in case, qh_printvdiagram2 called by user */
+ qh_vertexneighbors(qh);
+ vertices= qh_pointvertex(qh);
+ if (qh->ATinfinity)
+ SETelem_(vertices, qh->num_points-1)= NULL;
+ qh->visit_id++;
+ maximize_(qh->visit_id, (unsigned) qh->num_facets);
+ FORALLfacet_(facetlist) {
+ if (printall || !qh_skipfacet(qh, facet)) {
+ if (!facet->upperdelaunay) {
+ isLower= True;
+ break;
+ }
+ }
+ }
+ FOREACHfacet_(facets) {
+ if (printall || !qh_skipfacet(qh, facet)) {
+ if (!facet->upperdelaunay) {
+ isLower= True;
+ break;
+ }
+ }
+ }
+ FORALLfacets {
+ if (facet->normal && (facet->upperdelaunay == isLower))
+ facet->visitid= 0; /* facetlist or facets may overwrite */
+ else
+ facet->visitid= qh->visit_id;
+ facet->seen= False;
+ facet->seen2= True;
+ }
+ numcenters++; /* qh_INFINITE */
+ FORALLfacet_(facetlist) {
+ if (printall || !qh_skipfacet(qh, facet))
+ facet->visitid= numcenters++;
+ }
+ FOREACHfacet_(facets) {
+ if (printall || !qh_skipfacet(qh, facet))
+ facet->visitid= numcenters++;
+ }
+ *isLowerp= isLower;
+ *numcentersp= numcenters;
+ trace2((qh, qh->ferr, 2007, "qh_markvoronoi: isLower %d numcenters %d\n", isLower, numcenters));
+ return vertices;
+} /* markvoronoi */
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="order_vertexneighbors">-</a>
+
+ qh_order_vertexneighbors(qh, vertex )
+ order facet neighbors of a 2-d or 3-d vertex by adjacency
+
+ notes:
+ does not orient the neighbors
+
+ design:
+ initialize a new neighbor set with the first facet in vertex->neighbors
+ while vertex->neighbors non-empty
+ select next neighbor in the previous facet's neighbor set
+ set vertex->neighbors to the new neighbor set
+*/
+void qh_order_vertexneighbors(qhT *qh, vertexT *vertex) {
+ setT *newset;
+ facetT *facet, *neighbor, **neighborp;
+
+ trace4((qh, qh->ferr, 4018, "qh_order_vertexneighbors: order neighbors of v%d for 3-d\n", vertex->id));
+ newset= qh_settemp(qh, qh_setsize(qh, vertex->neighbors));
+ facet= (facetT*)qh_setdellast(vertex->neighbors);
+ qh_setappend(qh, &newset, facet);
+ while (qh_setsize(qh, vertex->neighbors)) {
+ FOREACHneighbor_(vertex) {
+ if (qh_setin(facet->neighbors, neighbor)) {
+ qh_setdel(vertex->neighbors, neighbor);
+ qh_setappend(qh, &newset, neighbor);
+ facet= neighbor;
+ break;
+ }
+ }
+ if (!neighbor) {
+ qh_fprintf(qh, qh->ferr, 6066, "qhull internal error (qh_order_vertexneighbors): no neighbor of v%d for f%d\n",
+ vertex->id, facet->id);
+ qh_errexit(qh, qh_ERRqhull, facet, NULL);
+ }
+ }
+ qh_setfree(qh, &vertex->neighbors);
+ qh_settemppop(qh);
+ vertex->neighbors= newset;
+} /* order_vertexneighbors */
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="prepare_output">-</a>
+
+ qh_prepare_output(qh, )
+ prepare for qh_produce_output2(qh) according to
+ qh.KEEPminArea, KEEParea, KEEPmerge, GOODvertex, GOODthreshold, GOODpoint, ONLYgood, SPLITthresholds
+ does not reset facet->good
+
+ notes
+ except for PRINTstatistics, no-op if previously called with same options
+*/
+void qh_prepare_output(qhT *qh) {
+ if (qh->VORONOI) {
+ qh_clearcenters(qh, qh_ASvoronoi); /* must be before qh_triangulate */
+ qh_vertexneighbors(qh);
+ }
+ if (qh->TRIangulate && !qh->hasTriangulation) {
+ qh_triangulate(qh);
+ if (qh->VERIFYoutput && !qh->CHECKfrequently)
+ qh_checkpolygon(qh, qh->facet_list);
+ }
+ qh_findgood_all(qh, qh->facet_list);
+ if (qh->GETarea)
+ qh_getarea(qh, qh->facet_list);
+ if (qh->KEEParea || qh->KEEPmerge || qh->KEEPminArea < REALmax/2)
+ qh_markkeep(qh, qh->facet_list);
+ if (qh->PRINTstatistics)
+ qh_collectstatistics(qh);
+}
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="printafacet">-</a>
+
+ qh_printafacet(qh, fp, format, facet, printall )
+ print facet to fp in given output format (see qh.PRINTout)
+
+ returns:
+ nop if !printall and qh_skipfacet()
+ nop if visible facet and NEWfacets and format != PRINTfacets
+ must match qh_countfacets
+
+ notes
+ preserves qh.visit_id
+ facet->normal may be null if PREmerge/MERGEexact and STOPcone before merge
+
+ see
+ qh_printbegin() and qh_printend()
+
+ design:
+ test for printing facet
+ call appropriate routine for format
+ or output results directly
+*/
+void qh_printafacet(qhT *qh, FILE *fp, qh_PRINT format, facetT *facet, boolT printall) {
+ realT color[4], offset, dist, outerplane, innerplane;
+ boolT zerodiv;
+ coordT *point, *normp, *coordp, **pointp, *feasiblep;
+ int k;
+ vertexT *vertex, **vertexp;
+ facetT *neighbor, **neighborp;
+
+ if (!printall && qh_skipfacet(qh, facet))
+ return;
+ if (facet->visible && qh->NEWfacets && format != qh_PRINTfacets)
+ return;
+ qh->printoutnum++;
+ switch (format) {
+ case qh_PRINTarea:
+ if (facet->isarea) {
+ qh_fprintf(qh, fp, 9009, qh_REAL_1, facet->f.area);
+ qh_fprintf(qh, fp, 9010, "\n");
+ }else
+ qh_fprintf(qh, fp, 9011, "0\n");
+ break;
+ case qh_PRINTcoplanars:
+ qh_fprintf(qh, fp, 9012, "%d", qh_setsize(qh, facet->coplanarset));
+ FOREACHpoint_(facet->coplanarset)
+ qh_fprintf(qh, fp, 9013, " %d", qh_pointid(qh, point));
+ qh_fprintf(qh, fp, 9014, "\n");
+ break;
+ case qh_PRINTcentrums:
+ qh_printcenter(qh, fp, format, NULL, facet);
+ break;
+ case qh_PRINTfacets:
+ qh_printfacet(qh, fp, facet);
+ break;
+ case qh_PRINTfacets_xridge:
+ qh_printfacetheader(qh, fp, facet);
+ break;
+ case qh_PRINTgeom: /* either 2 , 3, or 4-d by qh_printbegin */
+ if (!facet->normal)
+ break;
+ for (k=qh->hull_dim; k--; ) {
+ color[k]= (facet->normal[k]+1.0)/2.0;
+ maximize_(color[k], -1.0);
+ minimize_(color[k], +1.0);
+ }
+ qh_projectdim3(qh, color, color);
+ if (qh->PRINTdim != qh->hull_dim)
+ qh_normalize2(qh, color, 3, True, NULL, NULL);
+ if (qh->hull_dim <= 2)
+ qh_printfacet2geom(qh, fp, facet, color);
+ else if (qh->hull_dim == 3) {
+ if (facet->simplicial)
+ qh_printfacet3geom_simplicial(qh, fp, facet, color);
+ else
+ qh_printfacet3geom_nonsimplicial(qh, fp, facet, color);
+ }else {
+ if (facet->simplicial)
+ qh_printfacet4geom_simplicial(qh, fp, facet, color);
+ else
+ qh_printfacet4geom_nonsimplicial(qh, fp, facet, color);
+ }
+ break;
+ case qh_PRINTids:
+ qh_fprintf(qh, fp, 9015, "%d\n", facet->id);
+ break;
+ case qh_PRINTincidences:
+ case qh_PRINToff:
+ case qh_PRINTtriangles:
+ if (qh->hull_dim == 3 && format != qh_PRINTtriangles)
+ qh_printfacet3vertex(qh, fp, facet, format);
+ else if (facet->simplicial || qh->hull_dim == 2 || format == qh_PRINToff)
+ qh_printfacetNvertex_simplicial(qh, fp, facet, format);
+ else
+ qh_printfacetNvertex_nonsimplicial(qh, fp, facet, qh->printoutvar++, format);
+ break;
+ case qh_PRINTinner:
+ qh_outerinner(qh, facet, NULL, &innerplane);
+ offset= facet->offset - innerplane;
+ goto LABELprintnorm;
+ break; /* prevent warning */
+ case qh_PRINTmerges:
+ qh_fprintf(qh, fp, 9016, "%d\n", facet->nummerge);
+ break;
+ case qh_PRINTnormals:
+ offset= facet->offset;
+ goto LABELprintnorm;
+ break; /* prevent warning */
+ case qh_PRINTouter:
+ qh_outerinner(qh, facet, &outerplane, NULL);
+ offset= facet->offset - outerplane;
+ LABELprintnorm:
+ if (!facet->normal) {
+ qh_fprintf(qh, fp, 9017, "no normal for facet f%d\n", facet->id);
+ break;
+ }
+ if (qh->CDDoutput) {
+ qh_fprintf(qh, fp, 9018, qh_REAL_1, -offset);
+ for (k=0; k < qh->hull_dim; k++)
+ qh_fprintf(qh, fp, 9019, qh_REAL_1, -facet->normal[k]);
+ }else {
+ for (k=0; k < qh->hull_dim; k++)
+ qh_fprintf(qh, fp, 9020, qh_REAL_1, facet->normal[k]);
+ qh_fprintf(qh, fp, 9021, qh_REAL_1, offset);
+ }
+ qh_fprintf(qh, fp, 9022, "\n");
+ break;
+ case qh_PRINTmathematica: /* either 2 or 3-d by qh_printbegin */
+ case qh_PRINTmaple:
+ if (qh->hull_dim == 2)
+ qh_printfacet2math(qh, fp, facet, format, qh->printoutvar++);
+ else
+ qh_printfacet3math(qh, fp, facet, format, qh->printoutvar++);
+ break;
+ case qh_PRINTneighbors:
+ qh_fprintf(qh, fp, 9023, "%d", qh_setsize(qh, facet->neighbors));
+ FOREACHneighbor_(facet)
+ qh_fprintf(qh, fp, 9024, " %d",
+ neighbor->visitid ? neighbor->visitid - 1: 0 - neighbor->id);
+ qh_fprintf(qh, fp, 9025, "\n");
+ break;
+ case qh_PRINTpointintersect:
+ if (!qh->feasible_point) {
+ qh_fprintf(qh, qh->ferr, 6067, "qhull input error (qh_printafacet): option 'Fp' needs qh->feasible_point\n");
+ qh_errexit(qh, qh_ERRinput, NULL, NULL);
+ }
+ if (facet->offset > 0)
+ goto LABELprintinfinite;
+ point= coordp= (coordT*)qh_memalloc(qh, qh->normal_size);
+ normp= facet->normal;
+ feasiblep= qh->feasible_point;
+ if (facet->offset < -qh->MINdenom) {
+ for (k=qh->hull_dim; k--; )
+ *(coordp++)= (*(normp++) / - facet->offset) + *(feasiblep++);
+ }else {
+ for (k=qh->hull_dim; k--; ) {
+ *(coordp++)= qh_divzero(*(normp++), facet->offset, qh->MINdenom_1,
+ &zerodiv) + *(feasiblep++);
+ if (zerodiv) {
+ qh_memfree(qh, point, qh->normal_size);
+ goto LABELprintinfinite;
+ }
+ }
+ }
+ qh_printpoint(qh, fp, NULL, point);
+ qh_memfree(qh, point, qh->normal_size);
+ break;
+ LABELprintinfinite:
+ for (k=qh->hull_dim; k--; )
+ qh_fprintf(qh, fp, 9026, qh_REAL_1, qh_INFINITE);
+ qh_fprintf(qh, fp, 9027, "\n");
+ break;
+ case qh_PRINTpointnearest:
+ FOREACHpoint_(facet->coplanarset) {
+ int id, id2;
+ vertex= qh_nearvertex(qh, facet, point, &dist);
+ id= qh_pointid(qh, vertex->point);
+ id2= qh_pointid(qh, point);
+ qh_fprintf(qh, fp, 9028, "%d %d %d " qh_REAL_1 "\n", id, id2, facet->id, dist);
+ }
+ break;
+ case qh_PRINTpoints: /* VORONOI only by qh_printbegin */
+ if (qh->CDDoutput)
+ qh_fprintf(qh, fp, 9029, "1 ");
+ qh_printcenter(qh, fp, format, NULL, facet);
+ break;
+ case qh_PRINTvertices:
+ qh_fprintf(qh, fp, 9030, "%d", qh_setsize(qh, facet->vertices));
+ FOREACHvertex_(facet->vertices)
+ qh_fprintf(qh, fp, 9031, " %d", qh_pointid(qh, vertex->point));
+ qh_fprintf(qh, fp, 9032, "\n");
+ break;
+ default:
+ break;
+ }
+} /* printafacet */
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="printbegin">-</a>
+
+ qh_printbegin(qh, )
+ prints header for all output formats
+
+ returns:
+ checks for valid format
+
+ notes:
+ uses qh.visit_id for 3/4off
+ changes qh.interior_point if printing centrums
+ qh_countfacets clears facet->visitid for non-good facets
+
+ see
+ qh_printend() and qh_printafacet()
+
+ design:
+ count facets and related statistics
+ print header for format
+*/
+void qh_printbegin(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) {
+ int numfacets, numsimplicial, numridges, totneighbors, numcoplanars, numtricoplanars;
+ int i, num;
+ facetT *facet, **facetp;
+ vertexT *vertex, **vertexp;
+ setT *vertices;
+ pointT *point, **pointp, *pointtemp;
+
+ qh->printoutnum= 0;
+ qh_countfacets(qh, facetlist, facets, printall, &numfacets, &numsimplicial,
+ &totneighbors, &numridges, &numcoplanars, &numtricoplanars);
+ switch (format) {
+ case qh_PRINTnone:
+ break;
+ case qh_PRINTarea:
+ qh_fprintf(qh, fp, 9033, "%d\n", numfacets);
+ break;
+ case qh_PRINTcoplanars:
+ qh_fprintf(qh, fp, 9034, "%d\n", numfacets);
+ break;
+ case qh_PRINTcentrums:
+ if (qh->CENTERtype == qh_ASnone)
+ qh_clearcenters(qh, qh_AScentrum);
+ qh_fprintf(qh, fp, 9035, "%d\n%d\n", qh->hull_dim, numfacets);
+ break;
+ case qh_PRINTfacets:
+ case qh_PRINTfacets_xridge:
+ if (facetlist)
+ qh_printvertexlist(qh, fp, "Vertices and facets:\n", facetlist, facets, printall);
+ break;
+ case qh_PRINTgeom:
+ if (qh->hull_dim > 4) /* qh_initqhull_globals also checks */
+ goto LABELnoformat;
+ if (qh->VORONOI && qh->hull_dim > 3) /* PRINTdim == DROPdim == hull_dim-1 */
+ goto LABELnoformat;
+ if (qh->hull_dim == 2 && (qh->PRINTridges || qh->DOintersections))
+ qh_fprintf(qh, qh->ferr, 7049, "qhull warning: output for ridges and intersections not implemented in 2-d\n");
+ if (qh->hull_dim == 4 && (qh->PRINTinner || qh->PRINTouter ||
+ (qh->PRINTdim == 4 && qh->PRINTcentrums)))
+ qh_fprintf(qh, qh->ferr, 7050, "qhull warning: output for outer/inner planes and centrums not implemented in 4-d\n");
+ if (qh->PRINTdim == 4 && (qh->PRINTspheres))
+ qh_fprintf(qh, qh->ferr, 7051, "qhull warning: output for vertices not implemented in 4-d\n");
+ if (qh->PRINTdim == 4 && qh->DOintersections && qh->PRINTnoplanes)
+ qh_fprintf(qh, qh->ferr, 7052, "qhull warning: 'Gnh' generates no output in 4-d\n");
+ if (qh->PRINTdim == 2) {
+ qh_fprintf(qh, fp, 9036, "{appearance {linewidth 3} LIST # %s | %s\n",
+ qh->rbox_command, qh->qhull_command);
+ }else if (qh->PRINTdim == 3) {
+ qh_fprintf(qh, fp, 9037, "{appearance {+edge -evert linewidth 2} LIST # %s | %s\n",
+ qh->rbox_command, qh->qhull_command);
+ }else if (qh->PRINTdim == 4) {
+ qh->visit_id++;
+ num= 0;
+ FORALLfacet_(facetlist) /* get number of ridges to be printed */
+ qh_printend4geom(qh, NULL, facet, &num, printall);
+ FOREACHfacet_(facets)
+ qh_printend4geom(qh, NULL, facet, &num, printall);
+ qh->ridgeoutnum= num;
+ qh->printoutvar= 0; /* counts number of ridges in output */
+ qh_fprintf(qh, fp, 9038, "LIST # %s | %s\n", qh->rbox_command, qh->qhull_command);
+ }
+
+ if (qh->PRINTdots) {
+ qh->printoutnum++;
+ num= qh->num_points + qh_setsize(qh, qh->other_points);
+ if (qh->DELAUNAY && qh->ATinfinity)
+ num--;
+ if (qh->PRINTdim == 4)
+ qh_fprintf(qh, fp, 9039, "4VECT %d %d 1\n", num, num);
+ else
+ qh_fprintf(qh, fp, 9040, "VECT %d %d 1\n", num, num);
+
+ for (i=num; i--; ) {
+ if (i % 20 == 0)
+ qh_fprintf(qh, fp, 9041, "\n");
+ qh_fprintf(qh, fp, 9042, "1 ");
+ }
+ qh_fprintf(qh, fp, 9043, "# 1 point per line\n1 ");
+ for (i=num-1; i--; ) { /* num at least 3 for D2 */
+ if (i % 20 == 0)
+ qh_fprintf(qh, fp, 9044, "\n");
+ qh_fprintf(qh, fp, 9045, "0 ");
+ }
+ qh_fprintf(qh, fp, 9046, "# 1 color for all\n");
+ FORALLpoints {
+ if (!qh->DELAUNAY || !qh->ATinfinity || qh_pointid(qh, point) != qh->num_points-1) {
+ if (qh->PRINTdim == 4)
+ qh_printpoint(qh, fp, NULL, point);
+ else
+ qh_printpoint3(qh, fp, point);
+ }
+ }
+ FOREACHpoint_(qh->other_points) {
+ if (qh->PRINTdim == 4)
+ qh_printpoint(qh, fp, NULL, point);
+ else
+ qh_printpoint3(qh, fp, point);
+ }
+ qh_fprintf(qh, fp, 9047, "0 1 1 1 # color of points\n");
+ }
+
+ if (qh->PRINTdim == 4 && !qh->PRINTnoplanes)
+ /* 4dview loads up multiple 4OFF objects slowly */
+ qh_fprintf(qh, fp, 9048, "4OFF %d %d 1\n", 3*qh->ridgeoutnum, qh->ridgeoutnum);
+ qh->PRINTcradius= 2 * qh->DISTround; /* include test DISTround */
+ if (qh->PREmerge) {
+ maximize_(qh->PRINTcradius, qh->premerge_centrum + qh->DISTround);
+ }else if (qh->POSTmerge)
+ maximize_(qh->PRINTcradius, qh->postmerge_centrum + qh->DISTround);
+ qh->PRINTradius= qh->PRINTcradius;
+ if (qh->PRINTspheres + qh->PRINTcoplanar)
+ maximize_(qh->PRINTradius, qh->MAXabs_coord * qh_MINradius);
+ if (qh->premerge_cos < REALmax/2) {
+ maximize_(qh->PRINTradius, (1- qh->premerge_cos) * qh->MAXabs_coord);
+ }else if (!qh->PREmerge && qh->POSTmerge && qh->postmerge_cos < REALmax/2) {
+ maximize_(qh->PRINTradius, (1- qh->postmerge_cos) * qh->MAXabs_coord);
+ }
+ maximize_(qh->PRINTradius, qh->MINvisible);
+ if (qh->JOGGLEmax < REALmax/2)
+ qh->PRINTradius += qh->JOGGLEmax * sqrt((realT)qh->hull_dim);
+ if (qh->PRINTdim != 4 &&
+ (qh->PRINTcoplanar || qh->PRINTspheres || qh->PRINTcentrums)) {
+ vertices= qh_facetvertices(qh, facetlist, facets, printall);
+ if (qh->PRINTspheres && qh->PRINTdim <= 3)
+ qh_printspheres(qh, fp, vertices, qh->PRINTradius);
+ if (qh->PRINTcoplanar || qh->PRINTcentrums) {
+ qh->firstcentrum= True;
+ if (qh->PRINTcoplanar&& !qh->PRINTspheres) {
+ FOREACHvertex_(vertices)
+ qh_printpointvect2(qh, fp, vertex->point, NULL, qh->interior_point, qh->PRINTradius);
+ }
+ FORALLfacet_(facetlist) {
+ if (!printall && qh_skipfacet(qh, facet))
+ continue;
+ if (!facet->normal)
+ continue;
+ if (qh->PRINTcentrums && qh->PRINTdim <= 3)
+ qh_printcentrum(qh, fp, facet, qh->PRINTcradius);
+ if (!qh->PRINTcoplanar)
+ continue;
+ FOREACHpoint_(facet->coplanarset)
+ qh_printpointvect2(qh, fp, point, facet->normal, NULL, qh->PRINTradius);
+ FOREACHpoint_(facet->outsideset)
+ qh_printpointvect2(qh, fp, point, facet->normal, NULL, qh->PRINTradius);
+ }
+ FOREACHfacet_(facets) {
+ if (!printall && qh_skipfacet(qh, facet))
+ continue;
+ if (!facet->normal)
+ continue;
+ if (qh->PRINTcentrums && qh->PRINTdim <= 3)
+ qh_printcentrum(qh, fp, facet, qh->PRINTcradius);
+ if (!qh->PRINTcoplanar)
+ continue;
+ FOREACHpoint_(facet->coplanarset)
+ qh_printpointvect2(qh, fp, point, facet->normal, NULL, qh->PRINTradius);
+ FOREACHpoint_(facet->outsideset)
+ qh_printpointvect2(qh, fp, point, facet->normal, NULL, qh->PRINTradius);
+ }
+ }
+ qh_settempfree(qh, &vertices);
+ }
+ qh->visit_id++; /* for printing hyperplane intersections */
+ break;
+ case qh_PRINTids:
+ qh_fprintf(qh, fp, 9049, "%d\n", numfacets);
+ break;
+ case qh_PRINTincidences:
+ if (qh->VORONOI && qh->PRINTprecision)
+ qh_fprintf(qh, qh->ferr, 7053, "qhull warning: writing Delaunay. Use 'p' or 'o' for Voronoi centers\n");
+ qh->printoutvar= qh->vertex_id; /* centrum id for non-simplicial facets */
+ if (qh->hull_dim <= 3)
+ qh_fprintf(qh, fp, 9050, "%d\n", numfacets);
+ else
+ qh_fprintf(qh, fp, 9051, "%d\n", numsimplicial+numridges);
+ break;
+ case qh_PRINTinner:
+ case qh_PRINTnormals:
+ case qh_PRINTouter:
+ if (qh->CDDoutput)
+ qh_fprintf(qh, fp, 9052, "%s | %s\nbegin\n %d %d real\n", qh->rbox_command,
+ qh->qhull_command, numfacets, qh->hull_dim+1);
+ else
+ qh_fprintf(qh, fp, 9053, "%d\n%d\n", qh->hull_dim+1, numfacets);
+ break;
+ case qh_PRINTmathematica:
+ case qh_PRINTmaple:
+ if (qh->hull_dim > 3) /* qh_initbuffers also checks */
+ goto LABELnoformat;
+ if (qh->VORONOI)
+ qh_fprintf(qh, qh->ferr, 7054, "qhull warning: output is the Delaunay triangulation\n");
+ if (format == qh_PRINTmaple) {
+ if (qh->hull_dim == 2)
+ qh_fprintf(qh, fp, 9054, "PLOT(CURVES(\n");
+ else
+ qh_fprintf(qh, fp, 9055, "PLOT3D(POLYGONS(\n");
+ }else
+ qh_fprintf(qh, fp, 9056, "{\n");
+ qh->printoutvar= 0; /* counts number of facets for notfirst */
+ break;
+ case qh_PRINTmerges:
+ qh_fprintf(qh, fp, 9057, "%d\n", numfacets);
+ break;
+ case qh_PRINTpointintersect:
+ qh_fprintf(qh, fp, 9058, "%d\n%d\n", qh->hull_dim, numfacets);
+ break;
+ case qh_PRINTneighbors:
+ qh_fprintf(qh, fp, 9059, "%d\n", numfacets);
+ break;
+ case qh_PRINToff:
+ case qh_PRINTtriangles:
+ if (qh->VORONOI)
+ goto LABELnoformat;
+ num = qh->hull_dim;
+ if (format == qh_PRINToff || qh->hull_dim == 2)
+ qh_fprintf(qh, fp, 9060, "%d\n%d %d %d\n", num,
+ qh->num_points+qh_setsize(qh, qh->other_points), numfacets, totneighbors/2);
+ else { /* qh_PRINTtriangles */
+ qh->printoutvar= qh->num_points+qh_setsize(qh, qh->other_points); /* first centrum */
+ if (qh->DELAUNAY)
+ num--; /* drop last dimension */
+ qh_fprintf(qh, fp, 9061, "%d\n%d %d %d\n", num, qh->printoutvar
+ + numfacets - numsimplicial, numsimplicial + numridges, totneighbors/2);
+ }
+ FORALLpoints
+ qh_printpointid(qh, qh->fout, NULL, num, point, qh_IDunknown);
+ FOREACHpoint_(qh->other_points)
+ qh_printpointid(qh, qh->fout, NULL, num, point, qh_IDunknown);
+ if (format == qh_PRINTtriangles && qh->hull_dim > 2) {
+ FORALLfacets {
+ if (!facet->simplicial && facet->visitid)
+ qh_printcenter(qh, qh->fout, format, NULL, facet);
+ }
+ }
+ break;
+ case qh_PRINTpointnearest:
+ qh_fprintf(qh, fp, 9062, "%d\n", numcoplanars);
+ break;
+ case qh_PRINTpoints:
+ if (!qh->VORONOI)
+ goto LABELnoformat;
+ if (qh->CDDoutput)
+ qh_fprintf(qh, fp, 9063, "%s | %s\nbegin\n%d %d real\n", qh->rbox_command,
+ qh->qhull_command, numfacets, qh->hull_dim);
+ else
+ qh_fprintf(qh, fp, 9064, "%d\n%d\n", qh->hull_dim-1, numfacets);
+ break;
+ case qh_PRINTvertices:
+ qh_fprintf(qh, fp, 9065, "%d\n", numfacets);
+ break;
+ case qh_PRINTsummary:
+ default:
+ LABELnoformat:
+ qh_fprintf(qh, qh->ferr, 6068, "qhull internal error (qh_printbegin): can not use this format for dimension %d\n",
+ qh->hull_dim);
+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+ }
+} /* printbegin */
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="printcenter">-</a>
+
+ qh_printcenter(qh, fp, string, facet )
+ print facet->center as centrum or Voronoi center
+ string may be NULL. Don't include '%' codes.
+ nop if qh->CENTERtype neither CENTERvoronoi nor CENTERcentrum
+ if upper envelope of Delaunay triangulation and point at-infinity
+ prints qh_INFINITE instead;
+
+ notes:
+ defines facet->center if needed
+ if format=PRINTgeom, adds a 0 if would otherwise be 2-d
+ Same as QhullFacet::printCenter
+*/
+void qh_printcenter(qhT *qh, FILE *fp, qh_PRINT format, const char *string, facetT *facet) {
+ int k, num;
+
+ if (qh->CENTERtype != qh_ASvoronoi && qh->CENTERtype != qh_AScentrum)
+ return;
+ if (string)
+ qh_fprintf(qh, fp, 9066, string);
+ if (qh->CENTERtype == qh_ASvoronoi) {
+ num= qh->hull_dim-1;
+ if (!facet->normal || !facet->upperdelaunay || !qh->ATinfinity) {
+ if (!facet->center)
+ facet->center= qh_facetcenter(qh, facet->vertices);
+ for (k=0; k < num; k++)
+ qh_fprintf(qh, fp, 9067, qh_REAL_1, facet->center[k]);
+ }else {
+ for (k=0; k < num; k++)
+ qh_fprintf(qh, fp, 9068, qh_REAL_1, qh_INFINITE);
+ }
+ }else /* qh->CENTERtype == qh_AScentrum */ {
+ num= qh->hull_dim;
+ if (format == qh_PRINTtriangles && qh->DELAUNAY)
+ num--;
+ if (!facet->center)
+ facet->center= qh_getcentrum(qh, facet);
+ for (k=0; k < num; k++)
+ qh_fprintf(qh, fp, 9069, qh_REAL_1, facet->center[k]);
+ }
+ if (format == qh_PRINTgeom && num == 2)
+ qh_fprintf(qh, fp, 9070, " 0\n");
+ else
+ qh_fprintf(qh, fp, 9071, "\n");
+} /* printcenter */
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="printcentrum">-</a>
+
+ qh_printcentrum(qh, fp, facet, radius )
+ print centrum for a facet in OOGL format
+ radius defines size of centrum
+ 2-d or 3-d only
+
+ returns:
+ defines facet->center if needed
+*/
+void qh_printcentrum(qhT *qh, FILE *fp, facetT *facet, realT radius) {
+ pointT *centrum, *projpt;
+ boolT tempcentrum= False;
+ realT xaxis[4], yaxis[4], normal[4], dist;
+ realT green[3]={0, 1, 0};
+ vertexT *apex;
+ int k;
+
+ if (qh->CENTERtype == qh_AScentrum) {
+ if (!facet->center)
+ facet->center= qh_getcentrum(qh, facet);
+ centrum= facet->center;
+ }else {
+ centrum= qh_getcentrum(qh, facet);
+ tempcentrum= True;
+ }
+ qh_fprintf(qh, fp, 9072, "{appearance {-normal -edge normscale 0} ");
+ if (qh->firstcentrum) {
+ qh->firstcentrum= False;
+ qh_fprintf(qh, fp, 9073, "{INST geom { define centrum CQUAD # f%d\n\
+-0.3 -0.3 0.0001 0 0 1 1\n\
+ 0.3 -0.3 0.0001 0 0 1 1\n\
+ 0.3 0.3 0.0001 0 0 1 1\n\
+-0.3 0.3 0.0001 0 0 1 1 } transform { \n", facet->id);
+ }else
+ qh_fprintf(qh, fp, 9074, "{INST geom { : centrum } transform { # f%d\n", facet->id);
+ apex= SETfirstt_(facet->vertices, vertexT);
+ qh_distplane(qh, apex->point, facet, &dist);
+ projpt= qh_projectpoint(qh, apex->point, facet, dist);
+ for (k=qh->hull_dim; k--; ) {
+ xaxis[k]= projpt[k] - centrum[k];
+ normal[k]= facet->normal[k];
+ }
+ if (qh->hull_dim == 2) {
+ xaxis[2]= 0;
+ normal[2]= 0;
+ }else if (qh->hull_dim == 4) {
+ qh_projectdim3(qh, xaxis, xaxis);
+ qh_projectdim3(qh, normal, normal);
+ qh_normalize2(qh, normal, qh->PRINTdim, True, NULL, NULL);
+ }
+ qh_crossproduct(3, xaxis, normal, yaxis);
+ qh_fprintf(qh, fp, 9075, "%8.4g %8.4g %8.4g 0\n", xaxis[0], xaxis[1], xaxis[2]);
+ qh_fprintf(qh, fp, 9076, "%8.4g %8.4g %8.4g 0\n", yaxis[0], yaxis[1], yaxis[2]);
+ qh_fprintf(qh, fp, 9077, "%8.4g %8.4g %8.4g 0\n", normal[0], normal[1], normal[2]);
+ qh_printpoint3(qh, fp, centrum);
+ qh_fprintf(qh, fp, 9078, "1 }}}\n");
+ qh_memfree(qh, projpt, qh->normal_size);
+ qh_printpointvect(qh, fp, centrum, facet->normal, NULL, radius, green);
+ if (tempcentrum)
+ qh_memfree(qh, centrum, qh->normal_size);
+} /* printcentrum */
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="printend">-</a>
+
+ qh_printend(qh, fp, format )
+ prints trailer for all output formats
+
+ see:
+ qh_printbegin() and qh_printafacet()
+
+*/
+void qh_printend(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) {
+ int num;
+ facetT *facet, **facetp;
+
+ if (!qh->printoutnum)
+ qh_fprintf(qh, qh->ferr, 7055, "qhull warning: no facets printed\n");
+ switch (format) {
+ case qh_PRINTgeom:
+ if (qh->hull_dim == 4 && qh->DROPdim < 0 && !qh->PRINTnoplanes) {
+ qh->visit_id++;
+ num= 0;
+ FORALLfacet_(facetlist)
+ qh_printend4geom(qh, fp, facet,&num, printall);
+ FOREACHfacet_(facets)
+ qh_printend4geom(qh, fp, facet, &num, printall);
+ if (num != qh->ridgeoutnum || qh->printoutvar != qh->ridgeoutnum) {
+ qh_fprintf(qh, qh->ferr, 6069, "qhull internal error (qh_printend): number of ridges %d != number printed %d and at end %d\n", qh->ridgeoutnum, qh->printoutvar, num);
+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+ }
+ }else
+ qh_fprintf(qh, fp, 9079, "}\n");
+ break;
+ case qh_PRINTinner:
+ case qh_PRINTnormals:
+ case qh_PRINTouter:
+ if (qh->CDDoutput)
+ qh_fprintf(qh, fp, 9080, "end\n");
+ break;
+ case qh_PRINTmaple:
+ qh_fprintf(qh, fp, 9081, "));\n");
+ break;
+ case qh_PRINTmathematica:
+ qh_fprintf(qh, fp, 9082, "}\n");
+ break;
+ case qh_PRINTpoints:
+ if (qh->CDDoutput)
+ qh_fprintf(qh, fp, 9083, "end\n");
+ break;
+ default:
+ break;
+ }
+} /* printend */
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="printend4geom">-</a>
+
+ qh_printend4geom(qh, fp, facet, numridges, printall )
+ helper function for qh_printbegin/printend
+
+ returns:
+ number of printed ridges
+
+ notes:
+ just counts printed ridges if fp=NULL
+ uses facet->visitid
+ must agree with qh_printfacet4geom...
+
+ design:
+ computes color for facet from its normal
+ prints each ridge of facet
+*/
+void qh_printend4geom(qhT *qh, FILE *fp, facetT *facet, int *nump, boolT printall) {
+ realT color[3];
+ int i, num= *nump;
+ facetT *neighbor, **neighborp;
+ ridgeT *ridge, **ridgep;
+
+ if (!printall && qh_skipfacet(qh, facet))
+ return;
+ if (qh->PRINTnoplanes || (facet->visible && qh->NEWfacets))
+ return;
+ if (!facet->normal)
+ return;
+ if (fp) {
+ for (i=0; i < 3; i++) {
+ color[i]= (facet->normal[i]+1.0)/2.0;
+ maximize_(color[i], -1.0);
+ minimize_(color[i], +1.0);
+ }
+ }
+ facet->visitid= qh->visit_id;
+ if (facet->simplicial) {
+ FOREACHneighbor_(facet) {
+ if (neighbor->visitid != qh->visit_id) {
+ if (fp)
+ qh_fprintf(qh, fp, 9084, "3 %d %d %d %8.4g %8.4g %8.4g 1 # f%d f%d\n",
+ 3*num, 3*num+1, 3*num+2, color[0], color[1], color[2],
+ facet->id, neighbor->id);
+ num++;
+ }
+ }
+ }else {
+ FOREACHridge_(facet->ridges) {
+ neighbor= otherfacet_(ridge, facet);
+ if (neighbor->visitid != qh->visit_id) {
+ if (fp)
+ qh_fprintf(qh, fp, 9085, "3 %d %d %d %8.4g %8.4g %8.4g 1 #r%d f%d f%d\n",
+ 3*num, 3*num+1, 3*num+2, color[0], color[1], color[2],
+ ridge->id, facet->id, neighbor->id);
+ num++;
+ }
+ }
+ }
+ *nump= num;
+} /* printend4geom */
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="printextremes">-</a>
+
+ qh_printextremes(qh, fp, facetlist, facets, printall )
+ print extreme points for convex hulls or halfspace intersections
+
+ notes:
+ #points, followed by ids, one per line
+
+ sorted by id
+ same order as qh_printpoints_out if no coplanar/interior points
+*/
+void qh_printextremes(qhT *qh, FILE *fp, facetT *facetlist, setT *facets, boolT printall) {
+ setT *vertices, *points;
+ pointT *point;
+ vertexT *vertex, **vertexp;
+ int id;
+ int numpoints=0, point_i, point_n;
+ int allpoints= qh->num_points + qh_setsize(qh, qh->other_points);
+
+ points= qh_settemp(qh, allpoints);
+ qh_setzero(qh, points, 0, allpoints);
+ vertices= qh_facetvertices(qh, facetlist, facets, printall);
+ FOREACHvertex_(vertices) {
+ id= qh_pointid(qh, vertex->point);
+ if (id >= 0) {
+ SETelem_(points, id)= vertex->point;
+ numpoints++;
+ }
+ }
+ qh_settempfree(qh, &vertices);
+ qh_fprintf(qh, fp, 9086, "%d\n", numpoints);
+ FOREACHpoint_i_(qh, points) {
+ if (point)
+ qh_fprintf(qh, fp, 9087, "%d\n", point_i);
+ }
+ qh_settempfree(qh, &points);
+} /* printextremes */
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="printextremes_2d">-</a>
+
+ qh_printextremes_2d(qh, fp, facetlist, facets, printall )
+ prints point ids for facets in qh_ORIENTclock order
+
+ notes:
+ #points, followed by ids, one per line
+ if facetlist/facets are disjoint than the output includes skips
+ errors if facets form a loop
+ does not print coplanar points
+*/
+void qh_printextremes_2d(qhT *qh, FILE *fp, facetT *facetlist, setT *facets, boolT printall) {
+ int numfacets, numridges, totneighbors, numcoplanars, numsimplicial, numtricoplanars;
+ setT *vertices;
+ facetT *facet, *startfacet, *nextfacet;
+ vertexT *vertexA, *vertexB;
+
+ qh_countfacets(qh, facetlist, facets, printall, &numfacets, &numsimplicial,
+ &totneighbors, &numridges, &numcoplanars, &numtricoplanars); /* marks qh->visit_id */
+ vertices= qh_facetvertices(qh, facetlist, facets, printall);
+ qh_fprintf(qh, fp, 9088, "%d\n", qh_setsize(qh, vertices));
+ qh_settempfree(qh, &vertices);
+ if (!numfacets)
+ return;
+ facet= startfacet= facetlist ? facetlist : SETfirstt_(facets, facetT);
+ qh->vertex_visit++;
+ qh->visit_id++;
+ do {
+ if (facet->toporient ^ qh_ORIENTclock) {
+ vertexA= SETfirstt_(facet->vertices, vertexT);
+ vertexB= SETsecondt_(facet->vertices, vertexT);
+ nextfacet= SETfirstt_(facet->neighbors, facetT);
+ }else {
+ vertexA= SETsecondt_(facet->vertices, vertexT);
+ vertexB= SETfirstt_(facet->vertices, vertexT);
+ nextfacet= SETsecondt_(facet->neighbors, facetT);
+ }
+ if (facet->visitid == qh->visit_id) {
+ qh_fprintf(qh, qh->ferr, 6218, "Qhull internal error (qh_printextremes_2d): loop in facet list. facet %d nextfacet %d\n",
+ facet->id, nextfacet->id);
+ qh_errexit2(qh, qh_ERRqhull, facet, nextfacet);
+ }
+ if (facet->visitid) {
+ if (vertexA->visitid != qh->vertex_visit) {
+ vertexA->visitid= qh->vertex_visit;
+ qh_fprintf(qh, fp, 9089, "%d\n", qh_pointid(qh, vertexA->point));
+ }
+ if (vertexB->visitid != qh->vertex_visit) {
+ vertexB->visitid= qh->vertex_visit;
+ qh_fprintf(qh, fp, 9090, "%d\n", qh_pointid(qh, vertexB->point));
+ }
+ }
+ facet->visitid= qh->visit_id;
+ facet= nextfacet;
+ }while (facet && facet != startfacet);
+} /* printextremes_2d */
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="printextremes_d">-</a>
+
+ qh_printextremes_d(qh, fp, facetlist, facets, printall )
+ print extreme points of input sites for Delaunay triangulations
+
+ notes:
+ #points, followed by ids, one per line
+
+ unordered
+*/
+void qh_printextremes_d(qhT *qh, FILE *fp, facetT *facetlist, setT *facets, boolT printall) {
+ setT *vertices;
+ vertexT *vertex, **vertexp;
+ boolT upperseen, lowerseen;
+ facetT *neighbor, **neighborp;
+ int numpoints=0;
+
+ vertices= qh_facetvertices(qh, facetlist, facets, printall);
+ qh_vertexneighbors(qh);
+ FOREACHvertex_(vertices) {
+ upperseen= lowerseen= False;
+ FOREACHneighbor_(vertex) {
+ if (neighbor->upperdelaunay)
+ upperseen= True;
+ else
+ lowerseen= True;
+ }
+ if (upperseen && lowerseen) {
+ vertex->seen= True;
+ numpoints++;
+ }else
+ vertex->seen= False;
+ }
+ qh_fprintf(qh, fp, 9091, "%d\n", numpoints);
+ FOREACHvertex_(vertices) {
+ if (vertex->seen)
+ qh_fprintf(qh, fp, 9092, "%d\n", qh_pointid(qh, vertex->point));
+ }
+ qh_settempfree(qh, &vertices);
+} /* printextremes_d */
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="printfacet">-</a>
+
+ qh_printfacet(qh, fp, facet )
+ prints all fields of a facet to fp
+
+ notes:
+ ridges printed in neighbor order
+*/
+void qh_printfacet(qhT *qh, FILE *fp, facetT *facet) {
+
+ qh_printfacetheader(qh, fp, facet);
+ if (facet->ridges)
+ qh_printfacetridges(qh, fp, facet);
+} /* printfacet */
+
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="printfacet2geom">-</a>
+
+ qh_printfacet2geom(qh, fp, facet, color )
+ print facet as part of a 2-d VECT for Geomview
+
+ notes:
+ assume precise calculations in io_r.c with roundoff covered by qh_GEOMepsilon
+ mindist is calculated within io_r.c. maxoutside is calculated elsewhere
+ so a DISTround error may have occurred.
+*/
+void qh_printfacet2geom(qhT *qh, FILE *fp, facetT *facet, realT color[3]) {
+ pointT *point0, *point1;
+ realT mindist, innerplane, outerplane;
+ int k;
+
+ qh_facet2point(qh, facet, &point0, &point1, &mindist);
+ qh_geomplanes(qh, facet, &outerplane, &innerplane);
+ if (qh->PRINTouter || (!qh->PRINTnoplanes && !qh->PRINTinner))
+ qh_printfacet2geom_points(qh, fp, point0, point1, facet, outerplane, color);
+ if (qh->PRINTinner || (!qh->PRINTnoplanes && !qh->PRINTouter &&
+ outerplane - innerplane > 2 * qh->MAXabs_coord * qh_GEOMepsilon)) {
+ for (k=3; k--; )
+ color[k]= 1.0 - color[k];
+ qh_printfacet2geom_points(qh, fp, point0, point1, facet, innerplane, color);
+ }
+ qh_memfree(qh, point1, qh->normal_size);
+ qh_memfree(qh, point0, qh->normal_size);
+} /* printfacet2geom */
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="printfacet2geom_points">-</a>
+
+ qh_printfacet2geom_points(qh, fp, point1, point2, facet, offset, color )
+ prints a 2-d facet as a VECT with 2 points at some offset.
+ The points are on the facet's plane.
+*/
+void qh_printfacet2geom_points(qhT *qh, FILE *fp, pointT *point1, pointT *point2,
+ facetT *facet, realT offset, realT color[3]) {
+ pointT *p1= point1, *p2= point2;
+
+ qh_fprintf(qh, fp, 9093, "VECT 1 2 1 2 1 # f%d\n", facet->id);
+ if (offset != 0.0) {
+ p1= qh_projectpoint(qh, p1, facet, -offset);
+ p2= qh_projectpoint(qh, p2, facet, -offset);
+ }
+ qh_fprintf(qh, fp, 9094, "%8.4g %8.4g %8.4g\n%8.4g %8.4g %8.4g\n",
+ p1[0], p1[1], 0.0, p2[0], p2[1], 0.0);
+ if (offset != 0.0) {
+ qh_memfree(qh, p1, qh->normal_size);
+ qh_memfree(qh, p2, qh->normal_size);
+ }
+ qh_fprintf(qh, fp, 9095, "%8.4g %8.4g %8.4g 1.0\n", color[0], color[1], color[2]);
+} /* printfacet2geom_points */
+
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="printfacet2math">-</a>
+
+ qh_printfacet2math(qh, fp, facet, format, notfirst )
+ print 2-d Maple or Mathematica output for a facet
+ may be non-simplicial
+
+ notes:
+ use %16.8f since Mathematica 2.2 does not handle exponential format
+ see qh_printfacet3math
+*/
+void qh_printfacet2math(qhT *qh, FILE *fp, facetT *facet, qh_PRINT format, int notfirst) {
+ pointT *point0, *point1;
+ realT mindist;
+ const char *pointfmt;
+
+ qh_facet2point(qh, facet, &point0, &point1, &mindist);
+ if (notfirst)
+ qh_fprintf(qh, fp, 9096, ",");
+ if (format == qh_PRINTmaple)
+ pointfmt= "[[%16.8f, %16.8f], [%16.8f, %16.8f]]\n";
+ else
+ pointfmt= "Line[{{%16.8f, %16.8f}, {%16.8f, %16.8f}}]\n";
+ qh_fprintf(qh, fp, 9097, pointfmt, point0[0], point0[1], point1[0], point1[1]);
+ qh_memfree(qh, point1, qh->normal_size);
+ qh_memfree(qh, point0, qh->normal_size);
+} /* printfacet2math */
+
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="printfacet3geom_nonsimplicial">-</a>
+
+ qh_printfacet3geom_nonsimplicial(qh, fp, facet, color )
+ print Geomview OFF for a 3-d nonsimplicial facet.
+ if DOintersections, prints ridges to unvisited neighbors(qh->visit_id)
+
+ notes
+ uses facet->visitid for intersections and ridges
+*/
+void qh_printfacet3geom_nonsimplicial(qhT *qh, FILE *fp, facetT *facet, realT color[3]) {
+ ridgeT *ridge, **ridgep;
+ setT *projectedpoints, *vertices;
+ vertexT *vertex, **vertexp, *vertexA, *vertexB;
+ pointT *projpt, *point, **pointp;
+ facetT *neighbor;
+ realT dist, outerplane, innerplane;
+ int cntvertices, k;
+ realT black[3]={0, 0, 0}, green[3]={0, 1, 0};
+
+ qh_geomplanes(qh, facet, &outerplane, &innerplane);
+ vertices= qh_facet3vertex(qh, facet); /* oriented */
+ cntvertices= qh_setsize(qh, vertices);
+ projectedpoints= qh_settemp(qh, cntvertices);
+ FOREACHvertex_(vertices) {
+ zinc_(Zdistio);
+ qh_distplane(qh, vertex->point, facet, &dist);
+ projpt= qh_projectpoint(qh, vertex->point, facet, dist);
+ qh_setappend(qh, &projectedpoints, projpt);
+ }
+ if (qh->PRINTouter || (!qh->PRINTnoplanes && !qh->PRINTinner))
+ qh_printfacet3geom_points(qh, fp, projectedpoints, facet, outerplane, color);
+ if (qh->PRINTinner || (!qh->PRINTnoplanes && !qh->PRINTouter &&
+ outerplane - innerplane > 2 * qh->MAXabs_coord * qh_GEOMepsilon)) {
+ for (k=3; k--; )
+ color[k]= 1.0 - color[k];
+ qh_printfacet3geom_points(qh, fp, projectedpoints, facet, innerplane, color);
+ }
+ FOREACHpoint_(projectedpoints)
+ qh_memfree(qh, point, qh->normal_size);
+ qh_settempfree(qh, &projectedpoints);
+ qh_settempfree(qh, &vertices);
+ if ((qh->DOintersections || qh->PRINTridges)
+ && (!facet->visible || !qh->NEWfacets)) {
+ facet->visitid= qh->visit_id;
+ FOREACHridge_(facet->ridges) {
+ neighbor= otherfacet_(ridge, facet);
+ if (neighbor->visitid != qh->visit_id) {
+ if (qh->DOintersections)
+ qh_printhyperplaneintersection(qh, fp, facet, neighbor, ridge->vertices, black);
+ if (qh->PRINTridges) {
+ vertexA= SETfirstt_(ridge->vertices, vertexT);
+ vertexB= SETsecondt_(ridge->vertices, vertexT);
+ qh_printline3geom(qh, fp, vertexA->point, vertexB->point, green);
+ }
+ }
+ }
+ }
+} /* printfacet3geom_nonsimplicial */
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="printfacet3geom_points">-</a>
+
+ qh_printfacet3geom_points(qh, fp, points, facet, offset )
+ prints a 3-d facet as OFF Geomview object.
+ offset is relative to the facet's hyperplane
+ Facet is determined as a list of points
+*/
+void qh_printfacet3geom_points(qhT *qh, FILE *fp, setT *points, facetT *facet, realT offset, realT color[3]) {
+ int k, n= qh_setsize(qh, points), i;
+ pointT *point, **pointp;
+ setT *printpoints;
+
+ qh_fprintf(qh, fp, 9098, "{ OFF %d 1 1 # f%d\n", n, facet->id);
+ if (offset != 0.0) {
+ printpoints= qh_settemp(qh, n);
+ FOREACHpoint_(points)
+ qh_setappend(qh, &printpoints, qh_projectpoint(qh, point, facet, -offset));
+ }else
+ printpoints= points;
+ FOREACHpoint_(printpoints) {
+ for (k=0; k < qh->hull_dim; k++) {
+ if (k == qh->DROPdim)
+ qh_fprintf(qh, fp, 9099, "0 ");
+ else
+ qh_fprintf(qh, fp, 9100, "%8.4g ", point[k]);
+ }
+ if (printpoints != points)
+ qh_memfree(qh, point, qh->normal_size);
+ qh_fprintf(qh, fp, 9101, "\n");
+ }
+ if (printpoints != points)
+ qh_settempfree(qh, &printpoints);
+ qh_fprintf(qh, fp, 9102, "%d ", n);
+ for (i=0; i < n; i++)
+ qh_fprintf(qh, fp, 9103, "%d ", i);
+ qh_fprintf(qh, fp, 9104, "%8.4g %8.4g %8.4g 1.0 }\n", color[0], color[1], color[2]);
+} /* printfacet3geom_points */
+
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="printfacet3geom_simplicial">-</a>
+
+ qh_printfacet3geom_simplicial(qh, )
+ print Geomview OFF for a 3-d simplicial facet.
+
+ notes:
+ may flip color
+ uses facet->visitid for intersections and ridges
+
+ assume precise calculations in io_r.c with roundoff covered by qh_GEOMepsilon
+ innerplane may be off by qh->DISTround. Maxoutside is calculated elsewhere
+ so a DISTround error may have occurred.
+*/
+void qh_printfacet3geom_simplicial(qhT *qh, FILE *fp, facetT *facet, realT color[3]) {
+ setT *points, *vertices;
+ vertexT *vertex, **vertexp, *vertexA, *vertexB;
+ facetT *neighbor, **neighborp;
+ realT outerplane, innerplane;
+ realT black[3]={0, 0, 0}, green[3]={0, 1, 0};
+ int k;
+
+ qh_geomplanes(qh, facet, &outerplane, &innerplane);
+ vertices= qh_facet3vertex(qh, facet);
+ points= qh_settemp(qh, qh->TEMPsize);
+ FOREACHvertex_(vertices)
+ qh_setappend(qh, &points, vertex->point);
+ if (qh->PRINTouter || (!qh->PRINTnoplanes && !qh->PRINTinner))
+ qh_printfacet3geom_points(qh, fp, points, facet, outerplane, color);
+ if (qh->PRINTinner || (!qh->PRINTnoplanes && !qh->PRINTouter &&
+ outerplane - innerplane > 2 * qh->MAXabs_coord * qh_GEOMepsilon)) {
+ for (k=3; k--; )
+ color[k]= 1.0 - color[k];
+ qh_printfacet3geom_points(qh, fp, points, facet, innerplane, color);
+ }
+ qh_settempfree(qh, &points);
+ qh_settempfree(qh, &vertices);
+ if ((qh->DOintersections || qh->PRINTridges)
+ && (!facet->visible || !qh->NEWfacets)) {
+ facet->visitid= qh->visit_id;
+ FOREACHneighbor_(facet) {
+ if (neighbor->visitid != qh->visit_id) {
+ vertices= qh_setnew_delnthsorted(qh, facet->vertices, qh->hull_dim,
+ SETindex_(facet->neighbors, neighbor), 0);
+ if (qh->DOintersections)
+ qh_printhyperplaneintersection(qh, fp, facet, neighbor, vertices, black);
+ if (qh->PRINTridges) {
+ vertexA= SETfirstt_(vertices, vertexT);
+ vertexB= SETsecondt_(vertices, vertexT);
+ qh_printline3geom(qh, fp, vertexA->point, vertexB->point, green);
+ }
+ qh_setfree(qh, &vertices);
+ }
+ }
+ }
+} /* printfacet3geom_simplicial */
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="printfacet3math">-</a>
+
+ qh_printfacet3math(qh, fp, facet, notfirst )
+ print 3-d Maple or Mathematica output for a facet
+
+ notes:
+ may be non-simplicial
+ use %16.8f since Mathematica 2.2 does not handle exponential format
+ see qh_printfacet2math
+*/
+void qh_printfacet3math(qhT *qh, FILE *fp, facetT *facet, qh_PRINT format, int notfirst) {
+ vertexT *vertex, **vertexp;
+ setT *points, *vertices;
+ pointT *point, **pointp;
+ boolT firstpoint= True;
+ realT dist;
+ const char *pointfmt, *endfmt;
+
+ if (notfirst)
+ qh_fprintf(qh, fp, 9105, ",\n");
+ vertices= qh_facet3vertex(qh, facet);
+ points= qh_settemp(qh, qh_setsize(qh, vertices));
+ FOREACHvertex_(vertices) {
+ zinc_(Zdistio);
+ qh_distplane(qh, vertex->point, facet, &dist);
+ point= qh_projectpoint(qh, vertex->point, facet, dist);
+ qh_setappend(qh, &points, point);
+ }
+ if (format == qh_PRINTmaple) {
+ qh_fprintf(qh, fp, 9106, "[");
+ pointfmt= "[%16.8f, %16.8f, %16.8f]";
+ endfmt= "]";
+ }else {
+ qh_fprintf(qh, fp, 9107, "Polygon[{");
+ pointfmt= "{%16.8f, %16.8f, %16.8f}";
+ endfmt= "}]";
+ }
+ FOREACHpoint_(points) {
+ if (firstpoint)
+ firstpoint= False;
+ else
+ qh_fprintf(qh, fp, 9108, ",\n");
+ qh_fprintf(qh, fp, 9109, pointfmt, point[0], point[1], point[2]);
+ }
+ FOREACHpoint_(points)
+ qh_memfree(qh, point, qh->normal_size);
+ qh_settempfree(qh, &points);
+ qh_settempfree(qh, &vertices);
+ qh_fprintf(qh, fp, 9110, "%s", endfmt);
+} /* printfacet3math */
+
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="printfacet3vertex">-</a>
+
+ qh_printfacet3vertex(qh, fp, facet, format )
+ print vertices in a 3-d facet as point ids
+
+ notes:
+ prints number of vertices first if format == qh_PRINToff
+ the facet may be non-simplicial
+*/
+void qh_printfacet3vertex(qhT *qh, FILE *fp, facetT *facet, qh_PRINT format) {
+ vertexT *vertex, **vertexp;
+ setT *vertices;
+
+ vertices= qh_facet3vertex(qh, facet);
+ if (format == qh_PRINToff)
+ qh_fprintf(qh, fp, 9111, "%d ", qh_setsize(qh, vertices));
+ FOREACHvertex_(vertices)
+ qh_fprintf(qh, fp, 9112, "%d ", qh_pointid(qh, vertex->point));
+ qh_fprintf(qh, fp, 9113, "\n");
+ qh_settempfree(qh, &vertices);
+} /* printfacet3vertex */
+
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="printfacet4geom_nonsimplicial">-</a>
+
+ qh_printfacet4geom_nonsimplicial(qh, )
+ print Geomview 4OFF file for a 4d nonsimplicial facet
+ prints all ridges to unvisited neighbors (qh.visit_id)
+ if qh.DROPdim
+ prints in OFF format
+
+ notes:
+ must agree with printend4geom()
+*/
+void qh_printfacet4geom_nonsimplicial(qhT *qh, FILE *fp, facetT *facet, realT color[3]) {
+ facetT *neighbor;
+ ridgeT *ridge, **ridgep;
+ vertexT *vertex, **vertexp;
+ pointT *point;
+ int k;
+ realT dist;
+
+ facet->visitid= qh->visit_id;
+ if (qh->PRINTnoplanes || (facet->visible && qh->NEWfacets))
+ return;
+ FOREACHridge_(facet->ridges) {
+ neighbor= otherfacet_(ridge, facet);
+ if (neighbor->visitid == qh->visit_id)
+ continue;
+ if (qh->PRINTtransparent && !neighbor->good)
+ continue;
+ if (qh->DOintersections)
+ qh_printhyperplaneintersection(qh, fp, facet, neighbor, ridge->vertices, color);
+ else {
+ if (qh->DROPdim >= 0)
+ qh_fprintf(qh, fp, 9114, "OFF 3 1 1 # f%d\n", facet->id);
+ else {
+ qh->printoutvar++;
+ qh_fprintf(qh, fp, 9115, "# r%d between f%d f%d\n", ridge->id, facet->id, neighbor->id);
+ }
+ FOREACHvertex_(ridge->vertices) {
+ zinc_(Zdistio);
+ qh_distplane(qh, vertex->point,facet, &dist);
+ point=qh_projectpoint(qh, vertex->point,facet, dist);
+ for (k=0; k < qh->hull_dim; k++) {
+ if (k != qh->DROPdim)
+ qh_fprintf(qh, fp, 9116, "%8.4g ", point[k]);
+ }
+ qh_fprintf(qh, fp, 9117, "\n");
+ qh_memfree(qh, point, qh->normal_size);
+ }
+ if (qh->DROPdim >= 0)
+ qh_fprintf(qh, fp, 9118, "3 0 1 2 %8.4g %8.4g %8.4g\n", color[0], color[1], color[2]);
+ }
+ }
+} /* printfacet4geom_nonsimplicial */
+
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="printfacet4geom_simplicial">-</a>
+
+ qh_printfacet4geom_simplicial(qh, fp, facet, color )
+ print Geomview 4OFF file for a 4d simplicial facet
+ prints triangles for unvisited neighbors (qh.visit_id)
+
+ notes:
+ must agree with printend4geom()
+*/
+void qh_printfacet4geom_simplicial(qhT *qh, FILE *fp, facetT *facet, realT color[3]) {
+ setT *vertices;
+ facetT *neighbor, **neighborp;
+ vertexT *vertex, **vertexp;
+ int k;
+
+ facet->visitid= qh->visit_id;
+ if (qh->PRINTnoplanes || (facet->visible && qh->NEWfacets))
+ return;
+ FOREACHneighbor_(facet) {
+ if (neighbor->visitid == qh->visit_id)
+ continue;
+ if (qh->PRINTtransparent && !neighbor->good)
+ continue;
+ vertices= qh_setnew_delnthsorted(qh, facet->vertices, qh->hull_dim,
+ SETindex_(facet->neighbors, neighbor), 0);
+ if (qh->DOintersections)
+ qh_printhyperplaneintersection(qh, fp, facet, neighbor, vertices, color);
+ else {
+ if (qh->DROPdim >= 0)
+ qh_fprintf(qh, fp, 9119, "OFF 3 1 1 # ridge between f%d f%d\n",
+ facet->id, neighbor->id);
+ else {
+ qh->printoutvar++;
+ qh_fprintf(qh, fp, 9120, "# ridge between f%d f%d\n", facet->id, neighbor->id);
+ }
+ FOREACHvertex_(vertices) {
+ for (k=0; k < qh->hull_dim; k++) {
+ if (k != qh->DROPdim)
+ qh_fprintf(qh, fp, 9121, "%8.4g ", vertex->point[k]);
+ }
+ qh_fprintf(qh, fp, 9122, "\n");
+ }
+ if (qh->DROPdim >= 0)
+ qh_fprintf(qh, fp, 9123, "3 0 1 2 %8.4g %8.4g %8.4g\n", color[0], color[1], color[2]);
+ }
+ qh_setfree(qh, &vertices);
+ }
+} /* printfacet4geom_simplicial */
+
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="printfacetNvertex_nonsimplicial">-</a>
+
+ qh_printfacetNvertex_nonsimplicial(qh, fp, facet, id, format )
+ print vertices for an N-d non-simplicial facet
+ triangulates each ridge to the id
+*/
+void qh_printfacetNvertex_nonsimplicial(qhT *qh, FILE *fp, facetT *facet, int id, qh_PRINT format) {
+ vertexT *vertex, **vertexp;
+ ridgeT *ridge, **ridgep;
+
+ if (facet->visible && qh->NEWfacets)
+ return;
+ FOREACHridge_(facet->ridges) {
+ if (format == qh_PRINTtriangles)
+ qh_fprintf(qh, fp, 9124, "%d ", qh->hull_dim);
+ qh_fprintf(qh, fp, 9125, "%d ", id);
+ if ((ridge->top == facet) ^ qh_ORIENTclock) {
+ FOREACHvertex_(ridge->vertices)
+ qh_fprintf(qh, fp, 9126, "%d ", qh_pointid(qh, vertex->point));
+ }else {
+ FOREACHvertexreverse12_(ridge->vertices)
+ qh_fprintf(qh, fp, 9127, "%d ", qh_pointid(qh, vertex->point));
+ }
+ qh_fprintf(qh, fp, 9128, "\n");
+ }
+} /* printfacetNvertex_nonsimplicial */
+
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="printfacetNvertex_simplicial">-</a>
+
+ qh_printfacetNvertex_simplicial(qh, fp, facet, format )
+ print vertices for an N-d simplicial facet
+ prints vertices for non-simplicial facets
+ 2-d facets (orientation preserved by qh_mergefacet2d)
+ PRINToff ('o') for 4-d and higher
+*/
+void qh_printfacetNvertex_simplicial(qhT *qh, FILE *fp, facetT *facet, qh_PRINT format) {
+ vertexT *vertex, **vertexp;
+
+ if (format == qh_PRINToff || format == qh_PRINTtriangles)
+ qh_fprintf(qh, fp, 9129, "%d ", qh_setsize(qh, facet->vertices));
+ if ((facet->toporient ^ qh_ORIENTclock)
+ || (qh->hull_dim > 2 && !facet->simplicial)) {
+ FOREACHvertex_(facet->vertices)
+ qh_fprintf(qh, fp, 9130, "%d ", qh_pointid(qh, vertex->point));
+ }else {
+ FOREACHvertexreverse12_(facet->vertices)
+ qh_fprintf(qh, fp, 9131, "%d ", qh_pointid(qh, vertex->point));
+ }
+ qh_fprintf(qh, fp, 9132, "\n");
+} /* printfacetNvertex_simplicial */
+
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="printfacetheader">-</a>
+
+ qh_printfacetheader(qh, fp, facet )
+ prints header fields of a facet to fp
+
+ notes:
+ for 'f' output and debugging
+ Same as QhullFacet::printHeader()
+*/
+void qh_printfacetheader(qhT *qh, FILE *fp, facetT *facet) {
+ pointT *point, **pointp, *furthest;
+ facetT *neighbor, **neighborp;
+ realT dist;
+
+ if (facet == qh_MERGEridge) {
+ qh_fprintf(qh, fp, 9133, " MERGEridge\n");
+ return;
+ }else if (facet == qh_DUPLICATEridge) {
+ qh_fprintf(qh, fp, 9134, " DUPLICATEridge\n");
+ return;
+ }else if (!facet) {
+ qh_fprintf(qh, fp, 9135, " NULLfacet\n");
+ return;
+ }
+ qh->old_randomdist= qh->RANDOMdist;
+ qh->RANDOMdist= False;
+ qh_fprintf(qh, fp, 9136, "- f%d\n", facet->id);
+ qh_fprintf(qh, fp, 9137, " - flags:");
+ if (facet->toporient)
+ qh_fprintf(qh, fp, 9138, " top");
+ else
+ qh_fprintf(qh, fp, 9139, " bottom");
+ if (facet->simplicial)
+ qh_fprintf(qh, fp, 9140, " simplicial");
+ if (facet->tricoplanar)
+ qh_fprintf(qh, fp, 9141, " tricoplanar");
+ if (facet->upperdelaunay)
+ qh_fprintf(qh, fp, 9142, " upperDelaunay");
+ if (facet->visible)
+ qh_fprintf(qh, fp, 9143, " visible");
+ if (facet->newfacet)
+ qh_fprintf(qh, fp, 9144, " new");
+ if (facet->tested)
+ qh_fprintf(qh, fp, 9145, " tested");
+ if (!facet->good)
+ qh_fprintf(qh, fp, 9146, " notG");
+ if (facet->seen)
+ qh_fprintf(qh, fp, 9147, " seen");
+ if (facet->coplanar)
+ qh_fprintf(qh, fp, 9148, " coplanar");
+ if (facet->mergehorizon)
+ qh_fprintf(qh, fp, 9149, " mergehorizon");
+ if (facet->keepcentrum)
+ qh_fprintf(qh, fp, 9150, " keepcentrum");
+ if (facet->dupridge)
+ qh_fprintf(qh, fp, 9151, " dupridge");
+ if (facet->mergeridge && !facet->mergeridge2)
+ qh_fprintf(qh, fp, 9152, " mergeridge1");
+ if (facet->mergeridge2)
+ qh_fprintf(qh, fp, 9153, " mergeridge2");
+ if (facet->newmerge)
+ qh_fprintf(qh, fp, 9154, " newmerge");
+ if (facet->flipped)
+ qh_fprintf(qh, fp, 9155, " flipped");
+ if (facet->notfurthest)
+ qh_fprintf(qh, fp, 9156, " notfurthest");
+ if (facet->degenerate)
+ qh_fprintf(qh, fp, 9157, " degenerate");
+ if (facet->redundant)
+ qh_fprintf(qh, fp, 9158, " redundant");
+ qh_fprintf(qh, fp, 9159, "\n");
+ if (facet->isarea)
+ qh_fprintf(qh, fp, 9160, " - area: %2.2g\n", facet->f.area);
+ else if (qh->NEWfacets && facet->visible && facet->f.replace)
+ qh_fprintf(qh, fp, 9161, " - replacement: f%d\n", facet->f.replace->id);
+ else if (facet->newfacet) {
+ if (facet->f.samecycle && facet->f.samecycle != facet)
+ qh_fprintf(qh, fp, 9162, " - shares same visible/horizon as f%d\n", facet->f.samecycle->id);
+ }else if (facet->tricoplanar /* !isarea */) {
+ if (facet->f.triowner)
+ qh_fprintf(qh, fp, 9163, " - owner of normal & centrum is facet f%d\n", facet->f.triowner->id);
+ }else if (facet->f.newcycle)
+ qh_fprintf(qh, fp, 9164, " - was horizon to f%d\n", facet->f.newcycle->id);
+ if (facet->nummerge)
+ qh_fprintf(qh, fp, 9165, " - merges: %d\n", facet->nummerge);
+ qh_printpointid(qh, fp, " - normal: ", qh->hull_dim, facet->normal, qh_IDunknown);
+ qh_fprintf(qh, fp, 9166, " - offset: %10.7g\n", facet->offset);
+ if (qh->CENTERtype == qh_ASvoronoi || facet->center)
+ qh_printcenter(qh, fp, qh_PRINTfacets, " - center: ", facet);
+#if qh_MAXoutside
+ if (facet->maxoutside > qh->DISTround)
+ qh_fprintf(qh, fp, 9167, " - maxoutside: %10.7g\n", facet->maxoutside);
+#endif
+ if (!SETempty_(facet->outsideset)) {
+ furthest= (pointT*)qh_setlast(facet->outsideset);
+ if (qh_setsize(qh, facet->outsideset) < 6) {
+ qh_fprintf(qh, fp, 9168, " - outside set(furthest p%d):\n", qh_pointid(qh, furthest));
+ FOREACHpoint_(facet->outsideset)
+ qh_printpoint(qh, fp, " ", point);
+ }else if (qh_setsize(qh, facet->outsideset) < 21) {
+ qh_printpoints(qh, fp, " - outside set:", facet->outsideset);
+ }else {
+ qh_fprintf(qh, fp, 9169, " - outside set: %d points.", qh_setsize(qh, facet->outsideset));
+ qh_printpoint(qh, fp, " Furthest", furthest);
+ }
+#if !qh_COMPUTEfurthest
+ qh_fprintf(qh, fp, 9170, " - furthest distance= %2.2g\n", facet->furthestdist);
+#endif
+ }
+ if (!SETempty_(facet->coplanarset)) {
+ furthest= (pointT*)qh_setlast(facet->coplanarset);
+ if (qh_setsize(qh, facet->coplanarset) < 6) {
+ qh_fprintf(qh, fp, 9171, " - coplanar set(furthest p%d):\n", qh_pointid(qh, furthest));
+ FOREACHpoint_(facet->coplanarset)
+ qh_printpoint(qh, fp, " ", point);
+ }else if (qh_setsize(qh, facet->coplanarset) < 21) {
+ qh_printpoints(qh, fp, " - coplanar set:", facet->coplanarset);
+ }else {
+ qh_fprintf(qh, fp, 9172, " - coplanar set: %d points.", qh_setsize(qh, facet->coplanarset));
+ qh_printpoint(qh, fp, " Furthest", furthest);
+ }
+ zinc_(Zdistio);
+ qh_distplane(qh, furthest, facet, &dist);
+ qh_fprintf(qh, fp, 9173, " furthest distance= %2.2g\n", dist);
+ }
+ qh_printvertices(qh, fp, " - vertices:", facet->vertices);
+ qh_fprintf(qh, fp, 9174, " - neighboring facets:");
+ FOREACHneighbor_(facet) {
+ if (neighbor == qh_MERGEridge)
+ qh_fprintf(qh, fp, 9175, " MERGE");
+ else if (neighbor == qh_DUPLICATEridge)
+ qh_fprintf(qh, fp, 9176, " DUP");
+ else
+ qh_fprintf(qh, fp, 9177, " f%d", neighbor->id);
+ }
+ qh_fprintf(qh, fp, 9178, "\n");
+ qh->RANDOMdist= qh->old_randomdist;
+} /* printfacetheader */
+
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="printfacetridges">-</a>
+
+ qh_printfacetridges(qh, fp, facet )
+ prints ridges of a facet to fp
+
+ notes:
+ ridges printed in neighbor order
+ assumes the ridges exist
+ for 'f' output
+ same as QhullFacet::printRidges
+*/
+void qh_printfacetridges(qhT *qh, FILE *fp, facetT *facet) {
+ facetT *neighbor, **neighborp;
+ ridgeT *ridge, **ridgep;
+ int numridges= 0;
+
+
+ if (facet->visible && qh->NEWfacets) {
+ qh_fprintf(qh, fp, 9179, " - ridges(ids may be garbage):");
+ FOREACHridge_(facet->ridges)
+ qh_fprintf(qh, fp, 9180, " r%d", ridge->id);
+ qh_fprintf(qh, fp, 9181, "\n");
+ }else {
+ qh_fprintf(qh, fp, 9182, " - ridges:\n");
+ FOREACHridge_(facet->ridges)
+ ridge->seen= False;
+ if (qh->hull_dim == 3) {
+ ridge= SETfirstt_(facet->ridges, ridgeT);
+ while (ridge && !ridge->seen) {
+ ridge->seen= True;
+ qh_printridge(qh, fp, ridge);
+ numridges++;
+ ridge= qh_nextridge3d(ridge, facet, NULL);
+ }
+ }else {
+ FOREACHneighbor_(facet) {
+ FOREACHridge_(facet->ridges) {
+ if (otherfacet_(ridge,facet) == neighbor) {
+ ridge->seen= True;
+ qh_printridge(qh, fp, ridge);
+ numridges++;
+ }
+ }
+ }
+ }
+ if (numridges != qh_setsize(qh, facet->ridges)) {
+ qh_fprintf(qh, fp, 9183, " - all ridges:");
+ FOREACHridge_(facet->ridges)
+ qh_fprintf(qh, fp, 9184, " r%d", ridge->id);
+ qh_fprintf(qh, fp, 9185, "\n");
+ }
+ FOREACHridge_(facet->ridges) {
+ if (!ridge->seen)
+ qh_printridge(qh, fp, ridge);
+ }
+ }
+} /* printfacetridges */
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="printfacets">-</a>
+
+ qh_printfacets(qh, fp, format, facetlist, facets, printall )
+ prints facetlist and/or facet set in output format
+
+ notes:
+ also used for specialized formats ('FO' and summary)
+ turns off 'Rn' option since want actual numbers
+*/
+void qh_printfacets(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) {
+ int numfacets, numsimplicial, numridges, totneighbors, numcoplanars, numtricoplanars;
+ facetT *facet, **facetp;
+ setT *vertices;
+ coordT *center;
+ realT outerplane, innerplane;
+
+ qh->old_randomdist= qh->RANDOMdist;
+ qh->RANDOMdist= False;
+ if (qh->CDDoutput && (format == qh_PRINTcentrums || format == qh_PRINTpointintersect || format == qh_PRINToff))
+ qh_fprintf(qh, qh->ferr, 7056, "qhull warning: CDD format is not available for centrums, halfspace\nintersections, and OFF file format.\n");
+ if (format == qh_PRINTnone)
+ ; /* print nothing */
+ else if (format == qh_PRINTaverage) {
+ vertices= qh_facetvertices(qh, facetlist, facets, printall);
+ center= qh_getcenter(qh, vertices);
+ qh_fprintf(qh, fp, 9186, "%d 1\n", qh->hull_dim);
+ qh_printpointid(qh, fp, NULL, qh->hull_dim, center, qh_IDunknown);
+ qh_memfree(qh, center, qh->normal_size);
+ qh_settempfree(qh, &vertices);
+ }else if (format == qh_PRINTextremes) {
+ if (qh->DELAUNAY)
+ qh_printextremes_d(qh, fp, facetlist, facets, printall);
+ else if (qh->hull_dim == 2)
+ qh_printextremes_2d(qh, fp, facetlist, facets, printall);
+ else
+ qh_printextremes(qh, fp, facetlist, facets, printall);
+ }else if (format == qh_PRINToptions)
+ qh_fprintf(qh, fp, 9187, "Options selected for Qhull %s:\n%s\n", qh_version, qh->qhull_options);
+ else if (format == qh_PRINTpoints && !qh->VORONOI)
+ qh_printpoints_out(qh, fp, facetlist, facets, printall);
+ else if (format == qh_PRINTqhull)
+ qh_fprintf(qh, fp, 9188, "%s | %s\n", qh->rbox_command, qh->qhull_command);
+ else if (format == qh_PRINTsize) {
+ qh_fprintf(qh, fp, 9189, "0\n2 ");
+ qh_fprintf(qh, fp, 9190, qh_REAL_1, qh->totarea);
+ qh_fprintf(qh, fp, 9191, qh_REAL_1, qh->totvol);
+ qh_fprintf(qh, fp, 9192, "\n");
+ }else if (format == qh_PRINTsummary) {
+ qh_countfacets(qh, facetlist, facets, printall, &numfacets, &numsimplicial,
+ &totneighbors, &numridges, &numcoplanars, &numtricoplanars);
+ vertices= qh_facetvertices(qh, facetlist, facets, printall);
+ qh_fprintf(qh, fp, 9193, "10 %d %d %d %d %d %d %d %d %d %d\n2 ", qh->hull_dim,
+ qh->num_points + qh_setsize(qh, qh->other_points),
+ qh->num_vertices, qh->num_facets - qh->num_visible,
+ qh_setsize(qh, vertices), numfacets, numcoplanars,
+ numfacets - numsimplicial, zzval_(Zdelvertextot),
+ numtricoplanars);
+ qh_settempfree(qh, &vertices);
+ qh_outerinner(qh, NULL, &outerplane, &innerplane);
+ qh_fprintf(qh, fp, 9194, qh_REAL_2n, outerplane, innerplane);
+ }else if (format == qh_PRINTvneighbors)
+ qh_printvneighbors(qh, fp, facetlist, facets, printall);
+ else if (qh->VORONOI && format == qh_PRINToff)
+ qh_printvoronoi(qh, fp, format, facetlist, facets, printall);
+ else if (qh->VORONOI && format == qh_PRINTgeom) {
+ qh_printbegin(qh, fp, format, facetlist, facets, printall);
+ qh_printvoronoi(qh, fp, format, facetlist, facets, printall);
+ qh_printend(qh, fp, format, facetlist, facets, printall);
+ }else if (qh->VORONOI
+ && (format == qh_PRINTvertices || format == qh_PRINTinner || format == qh_PRINTouter))
+ qh_printvdiagram(qh, fp, format, facetlist, facets, printall);
+ else {
+ qh_printbegin(qh, fp, format, facetlist, facets, printall);
+ FORALLfacet_(facetlist)
+ qh_printafacet(qh, fp, format, facet, printall);
+ FOREACHfacet_(facets)
+ qh_printafacet(qh, fp, format, facet, printall);
+ qh_printend(qh, fp, format, facetlist, facets, printall);
+ }
+ qh->RANDOMdist= qh->old_randomdist;
+} /* printfacets */
+
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="printhyperplaneintersection">-</a>
+
+ qh_printhyperplaneintersection(qh, fp, facet1, facet2, vertices, color )
+ print Geomview OFF or 4OFF for the intersection of two hyperplanes in 3-d or 4-d
+*/
+void qh_printhyperplaneintersection(qhT *qh, FILE *fp, facetT *facet1, facetT *facet2,
+ setT *vertices, realT color[3]) {
+ realT costheta, denominator, dist1, dist2, s, t, mindenom, p[4];
+ vertexT *vertex, **vertexp;
+ int i, k;
+ boolT nearzero1, nearzero2;
+
+ costheta= qh_getangle(qh, facet1->normal, facet2->normal);
+ denominator= 1 - costheta * costheta;
+ i= qh_setsize(qh, vertices);
+ if (qh->hull_dim == 3)
+ qh_fprintf(qh, fp, 9195, "VECT 1 %d 1 %d 1 ", i, i);
+ else if (qh->hull_dim == 4 && qh->DROPdim >= 0)
+ qh_fprintf(qh, fp, 9196, "OFF 3 1 1 ");
+ else
+ qh->printoutvar++;
+ qh_fprintf(qh, fp, 9197, "# intersect f%d f%d\n", facet1->id, facet2->id);
+ mindenom= 1 / (10.0 * qh->MAXabs_coord);
+ FOREACHvertex_(vertices) {
+ zadd_(Zdistio, 2);
+ qh_distplane(qh, vertex->point, facet1, &dist1);
+ qh_distplane(qh, vertex->point, facet2, &dist2);
+ s= qh_divzero(-dist1 + costheta * dist2, denominator,mindenom,&nearzero1);
+ t= qh_divzero(-dist2 + costheta * dist1, denominator,mindenom,&nearzero2);
+ if (nearzero1 || nearzero2)
+ s= t= 0.0;
+ for (k=qh->hull_dim; k--; )
+ p[k]= vertex->point[k] + facet1->normal[k] * s + facet2->normal[k] * t;
+ if (qh->PRINTdim <= 3) {
+ qh_projectdim3(qh, p, p);
+ qh_fprintf(qh, fp, 9198, "%8.4g %8.4g %8.4g # ", p[0], p[1], p[2]);
+ }else
+ qh_fprintf(qh, fp, 9199, "%8.4g %8.4g %8.4g %8.4g # ", p[0], p[1], p[2], p[3]);
+ if (nearzero1+nearzero2)
+ qh_fprintf(qh, fp, 9200, "p%d(coplanar facets)\n", qh_pointid(qh, vertex->point));
+ else
+ qh_fprintf(qh, fp, 9201, "projected p%d\n", qh_pointid(qh, vertex->point));
+ }
+ if (qh->hull_dim == 3)
+ qh_fprintf(qh, fp, 9202, "%8.4g %8.4g %8.4g 1.0\n", color[0], color[1], color[2]);
+ else if (qh->hull_dim == 4 && qh->DROPdim >= 0)
+ qh_fprintf(qh, fp, 9203, "3 0 1 2 %8.4g %8.4g %8.4g 1.0\n", color[0], color[1], color[2]);
+} /* printhyperplaneintersection */
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="printline3geom">-</a>
+
+ qh_printline3geom(qh, fp, pointA, pointB, color )
+ prints a line as a VECT
+ prints 0's for qh.DROPdim
+
+ notes:
+ if pointA == pointB,
+ it's a 1 point VECT
+*/
+void qh_printline3geom(qhT *qh, FILE *fp, pointT *pointA, pointT *pointB, realT color[3]) {
+ int k;
+ realT pA[4], pB[4];
+
+ qh_projectdim3(qh, pointA, pA);
+ qh_projectdim3(qh, pointB, pB);
+ if ((fabs(pA[0] - pB[0]) > 1e-3) ||
+ (fabs(pA[1] - pB[1]) > 1e-3) ||
+ (fabs(pA[2] - pB[2]) > 1e-3)) {
+ qh_fprintf(qh, fp, 9204, "VECT 1 2 1 2 1\n");
+ for (k=0; k < 3; k++)
+ qh_fprintf(qh, fp, 9205, "%8.4g ", pB[k]);
+ qh_fprintf(qh, fp, 9206, " # p%d\n", qh_pointid(qh, pointB));
+ }else
+ qh_fprintf(qh, fp, 9207, "VECT 1 1 1 1 1\n");
+ for (k=0; k < 3; k++)
+ qh_fprintf(qh, fp, 9208, "%8.4g ", pA[k]);
+ qh_fprintf(qh, fp, 9209, " # p%d\n", qh_pointid(qh, pointA));
+ qh_fprintf(qh, fp, 9210, "%8.4g %8.4g %8.4g 1\n", color[0], color[1], color[2]);
+}
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="printneighborhood">-</a>
+
+ qh_printneighborhood(qh, fp, format, facetA, facetB, printall )
+ print neighborhood of one or two facets
+
+ notes:
+ calls qh_findgood_all()
+ bumps qh.visit_id
+*/
+void qh_printneighborhood(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetA, facetT *facetB, boolT printall) {
+ facetT *neighbor, **neighborp, *facet;
+ setT *facets;
+
+ if (format == qh_PRINTnone)
+ return;
+ qh_findgood_all(qh, qh->facet_list);
+ if (facetA == facetB)
+ facetB= NULL;
+ facets= qh_settemp(qh, 2*(qh_setsize(qh, facetA->neighbors)+1));
+ qh->visit_id++;
+ for (facet= facetA; facet; facet= ((facet == facetA) ? facetB : NULL)) {
+ if (facet->visitid != qh->visit_id) {
+ facet->visitid= qh->visit_id;
+ qh_setappend(qh, &facets, facet);
+ }
+ FOREACHneighbor_(facet) {
+ if (neighbor->visitid == qh->visit_id)
+ continue;
+ neighbor->visitid= qh->visit_id;
+ if (printall || !qh_skipfacet(qh, neighbor))
+ qh_setappend(qh, &facets, neighbor);
+ }
+ }
+ qh_printfacets(qh, fp, format, NULL, facets, printall);
+ qh_settempfree(qh, &facets);
+} /* printneighborhood */
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="printpoint">-</a>
+
+ qh_printpoint(qh, fp, string, point )
+ qh_printpointid(qh, fp, string, dim, point, id )
+ prints the coordinates of a point
+
+ returns:
+ if string is defined
+ prints 'string p%d'. Skips p%d if id=qh_IDunknown(-1) or qh_IDnone(-3)
+
+ notes:
+ nop if point is NULL
+ Same as QhullPoint's printPoint
+*/
+void qh_printpoint(qhT *qh, FILE *fp, const char *string, pointT *point) {
+ int id= qh_pointid(qh, point);
+
+ qh_printpointid(qh, fp, string, qh->hull_dim, point, id);
+} /* printpoint */
+
+void qh_printpointid(qhT *qh, FILE *fp, const char *string, int dim, pointT *point, int id) {
+ int k;
+ realT r; /*bug fix*/
+
+ if (!point)
+ return;
+ if (string) {
+ qh_fprintf(qh, fp, 9211, "%s", string);
+ if (id != qh_IDunknown && id != qh_IDnone)
+ qh_fprintf(qh, fp, 9212, " p%d: ", id);
+ }
+ for (k=dim; k--; ) {
+ r= *point++;
+ if (string)
+ qh_fprintf(qh, fp, 9213, " %8.4g", r);
+ else
+ qh_fprintf(qh, fp, 9214, qh_REAL_1, r);
+ }
+ qh_fprintf(qh, fp, 9215, "\n");
+} /* printpointid */
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="printpoint3">-</a>
+
+ qh_printpoint3(qh, fp, point )
+ prints 2-d, 3-d, or 4-d point as Geomview 3-d coordinates
+*/
+void qh_printpoint3(qhT *qh, FILE *fp, pointT *point) {
+ int k;
+ realT p[4];
+
+ qh_projectdim3(qh, point, p);
+ for (k=0; k < 3; k++)
+ qh_fprintf(qh, fp, 9216, "%8.4g ", p[k]);
+ qh_fprintf(qh, fp, 9217, " # p%d\n", qh_pointid(qh, point));
+} /* printpoint3 */
+
+/*----------------------------------------
+-printpoints- print pointids for a set of points starting at index
+ see geom_r.c
+*/
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="printpoints_out">-</a>
+
+ qh_printpoints_out(qh, fp, facetlist, facets, printall )
+ prints vertices, coplanar/inside points, for facets by their point coordinates
+ allows qh.CDDoutput
+
+ notes:
+ same format as qhull input
+ if no coplanar/interior points,
+ same order as qh_printextremes
+*/
+void qh_printpoints_out(qhT *qh, FILE *fp, facetT *facetlist, setT *facets, boolT printall) {
+ int allpoints= qh->num_points + qh_setsize(qh, qh->other_points);
+ int numpoints=0, point_i, point_n;
+ setT *vertices, *points;
+ facetT *facet, **facetp;
+ pointT *point, **pointp;
+ vertexT *vertex, **vertexp;
+ int id;
+
+ points= qh_settemp(qh, allpoints);
+ qh_setzero(qh, points, 0, allpoints);
+ vertices= qh_facetvertices(qh, facetlist, facets, printall);
+ FOREACHvertex_(vertices) {
+ id= qh_pointid(qh, vertex->point);
+ if (id >= 0)
+ SETelem_(points, id)= vertex->point;
+ }
+ if (qh->KEEPinside || qh->KEEPcoplanar || qh->KEEPnearinside) {
+ FORALLfacet_(facetlist) {
+ if (!printall && qh_skipfacet(qh, facet))
+ continue;
+ FOREACHpoint_(facet->coplanarset) {
+ id= qh_pointid(qh, point);
+ if (id >= 0)
+ SETelem_(points, id)= point;
+ }
+ }
+ FOREACHfacet_(facets) {
+ if (!printall && qh_skipfacet(qh, facet))
+ continue;
+ FOREACHpoint_(facet->coplanarset) {
+ id= qh_pointid(qh, point);
+ if (id >= 0)
+ SETelem_(points, id)= point;
+ }
+ }
+ }
+ qh_settempfree(qh, &vertices);
+ FOREACHpoint_i_(qh, points) {
+ if (point)
+ numpoints++;
+ }
+ if (qh->CDDoutput)
+ qh_fprintf(qh, fp, 9218, "%s | %s\nbegin\n%d %d real\n", qh->rbox_command,
+ qh->qhull_command, numpoints, qh->hull_dim + 1);
+ else
+ qh_fprintf(qh, fp, 9219, "%d\n%d\n", qh->hull_dim, numpoints);
+ FOREACHpoint_i_(qh, points) {
+ if (point) {
+ if (qh->CDDoutput)
+ qh_fprintf(qh, fp, 9220, "1 ");
+ qh_printpoint(qh, fp, NULL, point);
+ }
+ }
+ if (qh->CDDoutput)
+ qh_fprintf(qh, fp, 9221, "end\n");
+ qh_settempfree(qh, &points);
+} /* printpoints_out */
+
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="printpointvect">-</a>
+
+ qh_printpointvect(qh, fp, point, normal, center, radius, color )
+ prints a 2-d, 3-d, or 4-d point as 3-d VECT's relative to normal or to center point
+*/
+void qh_printpointvect(qhT *qh, FILE *fp, pointT *point, coordT *normal, pointT *center, realT radius, realT color[3]) {
+ realT diff[4], pointA[4];
+ int k;
+
+ for (k=qh->hull_dim; k--; ) {
+ if (center)
+ diff[k]= point[k]-center[k];
+ else if (normal)
+ diff[k]= normal[k];
+ else
+ diff[k]= 0;
+ }
+ if (center)
+ qh_normalize2(qh, diff, qh->hull_dim, True, NULL, NULL);
+ for (k=qh->hull_dim; k--; )
+ pointA[k]= point[k]+diff[k] * radius;
+ qh_printline3geom(qh, fp, point, pointA, color);
+} /* printpointvect */
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="printpointvect2">-</a>
+
+ qh_printpointvect2(qh, fp, point, normal, center, radius )
+ prints a 2-d, 3-d, or 4-d point as 2 3-d VECT's for an imprecise point
+*/
+void qh_printpointvect2(qhT *qh, FILE *fp, pointT *point, coordT *normal, pointT *center, realT radius) {
+ realT red[3]={1, 0, 0}, yellow[3]={1, 1, 0};
+
+ qh_printpointvect(qh, fp, point, normal, center, radius, red);
+ qh_printpointvect(qh, fp, point, normal, center, -radius, yellow);
+} /* printpointvect2 */
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="printridge">-</a>
+
+ qh_printridge(qh, fp, ridge )
+ prints the information in a ridge
+
+ notes:
+ for qh_printfacetridges()
+ same as operator<< [QhullRidge.cpp]
+*/
+void qh_printridge(qhT *qh, FILE *fp, ridgeT *ridge) {
+
+ qh_fprintf(qh, fp, 9222, " - r%d", ridge->id);
+ if (ridge->tested)
+ qh_fprintf(qh, fp, 9223, " tested");
+ if (ridge->nonconvex)
+ qh_fprintf(qh, fp, 9224, " nonconvex");
+ qh_fprintf(qh, fp, 9225, "\n");
+ qh_printvertices(qh, fp, " vertices:", ridge->vertices);
+ if (ridge->top && ridge->bottom)
+ qh_fprintf(qh, fp, 9226, " between f%d and f%d\n",
+ ridge->top->id, ridge->bottom->id);
+} /* printridge */
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="printspheres">-</a>
+
+ qh_printspheres(qh, fp, vertices, radius )
+ prints 3-d vertices as OFF spheres
+
+ notes:
+ inflated octahedron from Stuart Levy earth/mksphere2
+*/
+void qh_printspheres(qhT *qh, FILE *fp, setT *vertices, realT radius) {
+ vertexT *vertex, **vertexp;
+
+ qh->printoutnum++;
+ qh_fprintf(qh, fp, 9227, "{appearance {-edge -normal normscale 0} {\n\
+INST geom {define vsphere OFF\n\
+18 32 48\n\
+\n\
+0 0 1\n\
+1 0 0\n\
+0 1 0\n\
+-1 0 0\n\
+0 -1 0\n\
+0 0 -1\n\
+0.707107 0 0.707107\n\
+0 -0.707107 0.707107\n\
+0.707107 -0.707107 0\n\
+-0.707107 0 0.707107\n\
+-0.707107 -0.707107 0\n\
+0 0.707107 0.707107\n\
+-0.707107 0.707107 0\n\
+0.707107 0.707107 0\n\
+0.707107 0 -0.707107\n\
+0 0.707107 -0.707107\n\
+-0.707107 0 -0.707107\n\
+0 -0.707107 -0.707107\n\
+\n\
+3 0 6 11\n\
+3 0 7 6 \n\
+3 0 9 7 \n\
+3 0 11 9\n\
+3 1 6 8 \n\
+3 1 8 14\n\
+3 1 13 6\n\
+3 1 14 13\n\
+3 2 11 13\n\
+3 2 12 11\n\
+3 2 13 15\n\
+3 2 15 12\n\
+3 3 9 12\n\
+3 3 10 9\n\
+3 3 12 16\n\
+3 3 16 10\n\
+3 4 7 10\n\
+3 4 8 7\n\
+3 4 10 17\n\
+3 4 17 8\n\
+3 5 14 17\n\
+3 5 15 14\n\
+3 5 16 15\n\
+3 5 17 16\n\
+3 6 13 11\n\
+3 7 8 6\n\
+3 9 10 7\n\
+3 11 12 9\n\
+3 14 8 17\n\
+3 15 13 14\n\
+3 16 12 15\n\
+3 17 10 16\n} transforms { TLIST\n");
+ FOREACHvertex_(vertices) {
+ qh_fprintf(qh, fp, 9228, "%8.4g 0 0 0 # v%d\n 0 %8.4g 0 0\n0 0 %8.4g 0\n",
+ radius, vertex->id, radius, radius);
+ qh_printpoint3(qh, fp, vertex->point);
+ qh_fprintf(qh, fp, 9229, "1\n");
+ }
+ qh_fprintf(qh, fp, 9230, "}}}\n");
+} /* printspheres */
+
+
+/*----------------------------------------------
+-printsummary-
+ see libqhull_r.c
+*/
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="printvdiagram">-</a>
+
+ qh_printvdiagram(qh, fp, format, facetlist, facets, printall )
+ print voronoi diagram
+ # of pairs of input sites
+ #indices site1 site2 vertex1 ...
+
+ sites indexed by input point id
+ point 0 is the first input point
+ vertices indexed by 'o' and 'p' order
+ vertex 0 is the 'vertex-at-infinity'
+ vertex 1 is the first Voronoi vertex
+
+ see:
+ qh_printvoronoi()
+ qh_eachvoronoi_all()
+
+ notes:
+ if all facets are upperdelaunay,
+ prints upper hull (furthest-site Voronoi diagram)
+*/
+void qh_printvdiagram(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) {
+ setT *vertices;
+ int totcount, numcenters;
+ boolT isLower;
+ qh_RIDGE innerouter= qh_RIDGEall;
+ printvridgeT printvridge= NULL;
+
+ if (format == qh_PRINTvertices) {
+ innerouter= qh_RIDGEall;
+ printvridge= qh_printvridge;
+ }else if (format == qh_PRINTinner) {
+ innerouter= qh_RIDGEinner;
+ printvridge= qh_printvnorm;
+ }else if (format == qh_PRINTouter) {
+ innerouter= qh_RIDGEouter;
+ printvridge= qh_printvnorm;
+ }else {
+ qh_fprintf(qh, qh->ferr, 6219, "Qhull internal error (qh_printvdiagram): unknown print format %d.\n", format);
+ qh_errexit(qh, qh_ERRinput, NULL, NULL);
+ }
+ vertices= qh_markvoronoi(qh, facetlist, facets, printall, &isLower, &numcenters);
+ totcount= qh_printvdiagram2(qh, NULL, NULL, vertices, innerouter, False);
+ qh_fprintf(qh, fp, 9231, "%d\n", totcount);
+ totcount= qh_printvdiagram2(qh, fp, printvridge, vertices, innerouter, True /* inorder*/);
+ qh_settempfree(qh, &vertices);
+#if 0 /* for testing qh_eachvoronoi_all */
+ qh_fprintf(qh, fp, 9232, "\n");
+ totcount= qh_eachvoronoi_all(qh, fp, printvridge, qh->UPPERdelaunay, innerouter, True /* inorder*/);
+ qh_fprintf(qh, fp, 9233, "%d\n", totcount);
+#endif
+} /* printvdiagram */
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="printvdiagram2">-</a>
+
+ qh_printvdiagram2(qh, fp, printvridge, vertices, innerouter, inorder )
+ visit all pairs of input sites (vertices) for selected Voronoi vertices
+ vertices may include NULLs
+
+ innerouter:
+ qh_RIDGEall print inner ridges(bounded) and outer ridges(unbounded)
+ qh_RIDGEinner print only inner ridges
+ qh_RIDGEouter print only outer ridges
+
+ inorder:
+ print 3-d Voronoi vertices in order
+
+ assumes:
+ qh_markvoronoi marked facet->visitid for Voronoi vertices
+ all facet->seen= False
+ all facet->seen2= True
+
+ returns:
+ total number of Voronoi ridges
+ if printvridge,
+ calls printvridge( fp, vertex, vertexA, centers) for each ridge
+ [see qh_eachvoronoi()]
+
+ see:
+ qh_eachvoronoi_all()
+*/
+int qh_printvdiagram2(qhT *qh, FILE *fp, printvridgeT printvridge, setT *vertices, qh_RIDGE innerouter, boolT inorder) {
+ int totcount= 0;
+ int vertex_i, vertex_n;
+ vertexT *vertex;
+
+ FORALLvertices
+ vertex->seen= False;
+ FOREACHvertex_i_(qh, vertices) {
+ if (vertex) {
+ if (qh->GOODvertex > 0 && qh_pointid(qh, vertex->point)+1 != qh->GOODvertex)
+ continue;
+ totcount += qh_eachvoronoi(qh, fp, printvridge, vertex, !qh_ALL, innerouter, inorder);
+ }
+ }
+ return totcount;
+} /* printvdiagram2 */
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="printvertex">-</a>
+
+ qh_printvertex(qh, fp, vertex )
+ prints the information in a vertex
+ Duplicated as operator<< [QhullVertex.cpp]
+*/
+void qh_printvertex(qhT *qh, FILE *fp, vertexT *vertex) {
+ pointT *point;
+ int k, count= 0;
+ facetT *neighbor, **neighborp;
+ realT r; /*bug fix*/
+
+ if (!vertex) {
+ qh_fprintf(qh, fp, 9234, " NULLvertex\n");
+ return;
+ }
+ qh_fprintf(qh, fp, 9235, "- p%d(v%d):", qh_pointid(qh, vertex->point), vertex->id);
+ point= vertex->point;
+ if (point) {
+ for (k=qh->hull_dim; k--; ) {
+ r= *point++;
+ qh_fprintf(qh, fp, 9236, " %5.2g", r);
+ }
+ }
+ if (vertex->deleted)
+ qh_fprintf(qh, fp, 9237, " deleted");
+ if (vertex->delridge)
+ qh_fprintf(qh, fp, 9238, " ridgedeleted");
+ qh_fprintf(qh, fp, 9239, "\n");
+ if (vertex->neighbors) {
+ qh_fprintf(qh, fp, 9240, " neighbors:");
+ FOREACHneighbor_(vertex) {
+ if (++count % 100 == 0)
+ qh_fprintf(qh, fp, 9241, "\n ");
+ qh_fprintf(qh, fp, 9242, " f%d", neighbor->id);
+ }
+ qh_fprintf(qh, fp, 9243, "\n");
+ }
+} /* printvertex */
+
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="printvertexlist">-</a>
+
+ qh_printvertexlist(qh, fp, string, facetlist, facets, printall )
+ prints vertices used by a facetlist or facet set
+ tests qh_skipfacet() if !printall
+*/
+void qh_printvertexlist(qhT *qh, FILE *fp, const char* string, facetT *facetlist,
+ setT *facets, boolT printall) {
+ vertexT *vertex, **vertexp;
+ setT *vertices;
+
+ vertices= qh_facetvertices(qh, facetlist, facets, printall);
+ qh_fprintf(qh, fp, 9244, "%s", string);
+ FOREACHvertex_(vertices)
+ qh_printvertex(qh, fp, vertex);
+ qh_settempfree(qh, &vertices);
+} /* printvertexlist */
+
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="printvertices">-</a>
+
+ qh_printvertices(qh, fp, string, vertices )
+ prints vertices in a set
+ duplicated as printVertexSet [QhullVertex.cpp]
+*/
+void qh_printvertices(qhT *qh, FILE *fp, const char* string, setT *vertices) {
+ vertexT *vertex, **vertexp;
+
+ qh_fprintf(qh, fp, 9245, "%s", string);
+ FOREACHvertex_(vertices)
+ qh_fprintf(qh, fp, 9246, " p%d(v%d)", qh_pointid(qh, vertex->point), vertex->id);
+ qh_fprintf(qh, fp, 9247, "\n");
+} /* printvertices */
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="printvneighbors">-</a>
+
+ qh_printvneighbors(qh, fp, facetlist, facets, printall )
+ print vertex neighbors of vertices in facetlist and facets ('FN')
+
+ notes:
+ qh_countfacets clears facet->visitid for non-printed facets
+
+ design:
+ collect facet count and related statistics
+ if necessary, build neighbor sets for each vertex
+ collect vertices in facetlist and facets
+ build a point array for point->vertex and point->coplanar facet
+ for each point
+ list vertex neighbors or coplanar facet
+*/
+void qh_printvneighbors(qhT *qh, FILE *fp, facetT* facetlist, setT *facets, boolT printall) {
+ int numfacets, numsimplicial, numridges, totneighbors, numneighbors, numcoplanars, numtricoplanars;
+ setT *vertices, *vertex_points, *coplanar_points;
+ int numpoints= qh->num_points + qh_setsize(qh, qh->other_points);
+ vertexT *vertex, **vertexp;
+ int vertex_i, vertex_n;
+ facetT *facet, **facetp, *neighbor, **neighborp;
+ pointT *point, **pointp;
+
+ qh_countfacets(qh, facetlist, facets, printall, &numfacets, &numsimplicial,
+ &totneighbors, &numridges, &numcoplanars, &numtricoplanars); /* sets facet->visitid */
+ qh_fprintf(qh, fp, 9248, "%d\n", numpoints);
+ qh_vertexneighbors(qh);
+ vertices= qh_facetvertices(qh, facetlist, facets, printall);
+ vertex_points= qh_settemp(qh, numpoints);
+ coplanar_points= qh_settemp(qh, numpoints);
+ qh_setzero(qh, vertex_points, 0, numpoints);
+ qh_setzero(qh, coplanar_points, 0, numpoints);
+ FOREACHvertex_(vertices)
+ qh_point_add(qh, vertex_points, vertex->point, vertex);
+ FORALLfacet_(facetlist) {
+ FOREACHpoint_(facet->coplanarset)
+ qh_point_add(qh, coplanar_points, point, facet);
+ }
+ FOREACHfacet_(facets) {
+ FOREACHpoint_(facet->coplanarset)
+ qh_point_add(qh, coplanar_points, point, facet);
+ }
+ FOREACHvertex_i_(qh, vertex_points) {
+ if (vertex) {
+ numneighbors= qh_setsize(qh, vertex->neighbors);
+ qh_fprintf(qh, fp, 9249, "%d", numneighbors);
+ if (qh->hull_dim == 3)
+ qh_order_vertexneighbors(qh, vertex);
+ else if (qh->hull_dim >= 4)
+ qsort(SETaddr_(vertex->neighbors, facetT), (size_t)numneighbors,
+ sizeof(facetT *), qh_compare_facetvisit);
+ FOREACHneighbor_(vertex)
+ qh_fprintf(qh, fp, 9250, " %d",
+ neighbor->visitid ? neighbor->visitid - 1 : 0 - neighbor->id);
+ qh_fprintf(qh, fp, 9251, "\n");
+ }else if ((facet= SETelemt_(coplanar_points, vertex_i, facetT)))
+ qh_fprintf(qh, fp, 9252, "1 %d\n",
+ facet->visitid ? facet->visitid - 1 : 0 - facet->id);
+ else
+ qh_fprintf(qh, fp, 9253, "0\n");
+ }
+ qh_settempfree(qh, &coplanar_points);
+ qh_settempfree(qh, &vertex_points);
+ qh_settempfree(qh, &vertices);
+} /* printvneighbors */
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="printvoronoi">-</a>
+
+ qh_printvoronoi(qh, fp, format, facetlist, facets, printall )
+ print voronoi diagram in 'o' or 'G' format
+ for 'o' format
+ prints voronoi centers for each facet and for infinity
+ for each vertex, lists ids of printed facets or infinity
+ assumes facetlist and facets are disjoint
+ for 'G' format
+ prints an OFF object
+ adds a 0 coordinate to center
+ prints infinity but does not list in vertices
+
+ see:
+ qh_printvdiagram()
+
+ notes:
+ if 'o',
+ prints a line for each point except "at-infinity"
+ if all facets are upperdelaunay,
+ reverses lower and upper hull
+*/
+void qh_printvoronoi(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) {
+ int k, numcenters, numvertices= 0, numneighbors, numinf, vid=1, vertex_i, vertex_n;
+ facetT *facet, **facetp, *neighbor, **neighborp;
+ setT *vertices;
+ vertexT *vertex;
+ boolT isLower;
+ unsigned int numfacets= (unsigned int) qh->num_facets;
+
+ vertices= qh_markvoronoi(qh, facetlist, facets, printall, &isLower, &numcenters);
+ FOREACHvertex_i_(qh, vertices) {
+ if (vertex) {
+ numvertices++;
+ numneighbors = numinf = 0;
+ FOREACHneighbor_(vertex) {
+ if (neighbor->visitid == 0)
+ numinf= 1;
+ else if (neighbor->visitid < numfacets)
+ numneighbors++;
+ }
+ if (numinf && !numneighbors) {
+ SETelem_(vertices, vertex_i)= NULL;
+ numvertices--;
+ }
+ }
+ }
+ if (format == qh_PRINTgeom)
+ qh_fprintf(qh, fp, 9254, "{appearance {+edge -face} OFF %d %d 1 # Voronoi centers and cells\n",
+ numcenters, numvertices);
+ else
+ qh_fprintf(qh, fp, 9255, "%d\n%d %d 1\n", qh->hull_dim-1, numcenters, qh_setsize(qh, vertices));
+ if (format == qh_PRINTgeom) {
+ for (k=qh->hull_dim-1; k--; )
+ qh_fprintf(qh, fp, 9256, qh_REAL_1, 0.0);
+ qh_fprintf(qh, fp, 9257, " 0 # infinity not used\n");
+ }else {
+ for (k=qh->hull_dim-1; k--; )
+ qh_fprintf(qh, fp, 9258, qh_REAL_1, qh_INFINITE);
+ qh_fprintf(qh, fp, 9259, "\n");
+ }
+ FORALLfacet_(facetlist) {
+ if (facet->visitid && facet->visitid < numfacets) {
+ if (format == qh_PRINTgeom)
+ qh_fprintf(qh, fp, 9260, "# %d f%d\n", vid++, facet->id);
+ qh_printcenter(qh, fp, format, NULL, facet);
+ }
+ }
+ FOREACHfacet_(facets) {
+ if (facet->visitid && facet->visitid < numfacets) {
+ if (format == qh_PRINTgeom)
+ qh_fprintf(qh, fp, 9261, "# %d f%d\n", vid++, facet->id);
+ qh_printcenter(qh, fp, format, NULL, facet);
+ }
+ }
+ FOREACHvertex_i_(qh, vertices) {
+ numneighbors= 0;
+ numinf=0;
+ if (vertex) {
+ if (qh->hull_dim == 3)
+ qh_order_vertexneighbors(qh, vertex);
+ else if (qh->hull_dim >= 4)
+ qsort(SETaddr_(vertex->neighbors, facetT),
+ (size_t)qh_setsize(qh, vertex->neighbors),
+ sizeof(facetT *), qh_compare_facetvisit);
+ FOREACHneighbor_(vertex) {
+ if (neighbor->visitid == 0)
+ numinf= 1;
+ else if (neighbor->visitid < numfacets)
+ numneighbors++;
+ }
+ }
+ if (format == qh_PRINTgeom) {
+ if (vertex) {
+ qh_fprintf(qh, fp, 9262, "%d", numneighbors);
+ FOREACHneighbor_(vertex) {
+ if (neighbor->visitid && neighbor->visitid < numfacets)
+ qh_fprintf(qh, fp, 9263, " %d", neighbor->visitid);
+ }
+ qh_fprintf(qh, fp, 9264, " # p%d(v%d)\n", vertex_i, vertex->id);
+ }else
+ qh_fprintf(qh, fp, 9265, " # p%d is coplanar or isolated\n", vertex_i);
+ }else {
+ if (numinf)
+ numneighbors++;
+ qh_fprintf(qh, fp, 9266, "%d", numneighbors);
+ if (vertex) {
+ FOREACHneighbor_(vertex) {
+ if (neighbor->visitid == 0) {
+ if (numinf) {
+ numinf= 0;
+ qh_fprintf(qh, fp, 9267, " %d", neighbor->visitid);
+ }
+ }else if (neighbor->visitid < numfacets)
+ qh_fprintf(qh, fp, 9268, " %d", neighbor->visitid);
+ }
+ }
+ qh_fprintf(qh, fp, 9269, "\n");
+ }
+ }
+ if (format == qh_PRINTgeom)
+ qh_fprintf(qh, fp, 9270, "}\n");
+ qh_settempfree(qh, &vertices);
+} /* printvoronoi */
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="printvnorm">-</a>
+
+ qh_printvnorm(qh, fp, vertex, vertexA, centers, unbounded )
+ print one separating plane of the Voronoi diagram for a pair of input sites
+ unbounded==True if centers includes vertex-at-infinity
+
+ assumes:
+ qh_ASvoronoi and qh_vertexneighbors() already set
+
+ note:
+ parameter unbounded is UNUSED by this callback
+
+ see:
+ qh_printvdiagram()
+ qh_eachvoronoi()
+*/
+void qh_printvnorm(qhT *qh, FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded) {
+ pointT *normal;
+ realT offset;
+ int k;
+ QHULL_UNUSED(unbounded);
+
+ normal= qh_detvnorm(qh, vertex, vertexA, centers, &offset);
+ qh_fprintf(qh, fp, 9271, "%d %d %d ",
+ 2+qh->hull_dim, qh_pointid(qh, vertex->point), qh_pointid(qh, vertexA->point));
+ for (k=0; k< qh->hull_dim-1; k++)
+ qh_fprintf(qh, fp, 9272, qh_REAL_1, normal[k]);
+ qh_fprintf(qh, fp, 9273, qh_REAL_1, offset);
+ qh_fprintf(qh, fp, 9274, "\n");
+} /* printvnorm */
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="printvridge">-</a>
+
+ qh_printvridge(qh, fp, vertex, vertexA, centers, unbounded )
+ print one ridge of the Voronoi diagram for a pair of input sites
+ unbounded==True if centers includes vertex-at-infinity
+
+ see:
+ qh_printvdiagram()
+
+ notes:
+ the user may use a different function
+ parameter unbounded is UNUSED
+*/
+void qh_printvridge(qhT *qh, FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded) {
+ facetT *facet, **facetp;
+ QHULL_UNUSED(unbounded);
+
+ qh_fprintf(qh, fp, 9275, "%d %d %d", qh_setsize(qh, centers)+2,
+ qh_pointid(qh, vertex->point), qh_pointid(qh, vertexA->point));
+ FOREACHfacet_(centers)
+ qh_fprintf(qh, fp, 9276, " %d", facet->visitid);
+ qh_fprintf(qh, fp, 9277, "\n");
+} /* printvridge */
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="projectdim3">-</a>
+
+ qh_projectdim3(qh, source, destination )
+ project 2-d 3-d or 4-d point to a 3-d point
+ uses qh.DROPdim and qh.hull_dim
+ source and destination may be the same
+
+ notes:
+ allocate 4 elements to destination just in case
+*/
+void qh_projectdim3(qhT *qh, pointT *source, pointT *destination) {
+ int i,k;
+
+ for (k=0, i=0; k < qh->hull_dim; k++) {
+ if (qh->hull_dim == 4) {
+ if (k != qh->DROPdim)
+ destination[i++]= source[k];
+ }else if (k == qh->DROPdim)
+ destination[i++]= 0;
+ else
+ destination[i++]= source[k];
+ }
+ while (i < 3)
+ destination[i++]= 0.0;
+} /* projectdim3 */
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="readfeasible">-</a>
+
+ qh_readfeasible(qh, dim, curline )
+ read feasible point from current line and qh.fin
+
+ returns:
+ number of lines read from qh.fin
+ sets qh.feasible_point with malloc'd coordinates
+
+ notes:
+ checks for qh.HALFspace
+ assumes dim > 1
+
+ see:
+ qh_setfeasible
+*/
+int qh_readfeasible(qhT *qh, int dim, const char *curline) {
+ boolT isfirst= True;
+ int linecount= 0, tokcount= 0;
+ const char *s;
+ char *t, firstline[qh_MAXfirst+1];
+ coordT *coords, value;
+
+ if (!qh->HALFspace) {
+ qh_fprintf(qh, qh->ferr, 6070, "qhull input error: feasible point(dim 1 coords) is only valid for halfspace intersection\n");
+ qh_errexit(qh, qh_ERRinput, NULL, NULL);
+ }
+ if (qh->feasible_string)
+ qh_fprintf(qh, qh->ferr, 7057, "qhull input warning: feasible point(dim 1 coords) overrides 'Hn,n,n' feasible point for halfspace intersection\n");
+ if (!(qh->feasible_point= (coordT*)qh_malloc(dim* sizeof(coordT)))) {
+ qh_fprintf(qh, qh->ferr, 6071, "qhull error: insufficient memory for feasible point\n");
+ qh_errexit(qh, qh_ERRmem, NULL, NULL);
+ }
+ coords= qh->feasible_point;
+ while ((s= (isfirst ? curline : fgets(firstline, qh_MAXfirst, qh->fin)))) {
+ if (isfirst)
+ isfirst= False;
+ else
+ linecount++;
+ while (*s) {
+ while (isspace(*s))
+ s++;
+ value= qh_strtod(s, &t);
+ if (s == t)
+ break;
+ s= t;
+ *(coords++)= value;
+ if (++tokcount == dim) {
+ while (isspace(*s))
+ s++;
+ qh_strtod(s, &t);
+ if (s != t) {
+ qh_fprintf(qh, qh->ferr, 6072, "qhull input error: coordinates for feasible point do not finish out the line: %s\n",
+ s);
+ qh_errexit(qh, qh_ERRinput, NULL, NULL);
+ }
+ return linecount;
+ }
+ }
+ }
+ qh_fprintf(qh, qh->ferr, 6073, "qhull input error: only %d coordinates. Could not read %d-d feasible point.\n",
+ tokcount, dim);
+ qh_errexit(qh, qh_ERRinput, NULL, NULL);
+ return 0;
+} /* readfeasible */
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="readpoints">-</a>
+
+ qh_readpoints(qh, numpoints, dimension, ismalloc )
+ read points from qh.fin into qh.first_point, qh.num_points
+ qh.fin is lines of coordinates, one per vertex, first line number of points
+ if 'rbox D4',
+ gives message
+ if qh.ATinfinity,
+ adds point-at-infinity for Delaunay triangulations
+
+ returns:
+ number of points, array of point coordinates, dimension, ismalloc True
+ if qh.DELAUNAY & !qh.PROJECTinput, projects points to paraboloid
+ and clears qh.PROJECTdelaunay
+ if qh.HALFspace, reads optional feasible point, reads halfspaces,
+ converts to dual.
+
+ for feasible point in "cdd format" in 3-d:
+ 3 1
+ coordinates
+ comments
+ begin
+ n 4 real/integer
+ ...
+ end
+
+ notes:
+ dimension will change in qh_initqhull_globals if qh.PROJECTinput
+ uses malloc() since qh_mem not initialized
+ FIXUP QH11012: qh_readpoints needs rewriting, too long
+*/
+coordT *qh_readpoints(qhT *qh, int *numpoints, int *dimension, boolT *ismalloc) {
+ coordT *points, *coords, *infinity= NULL;
+ realT paraboloid, maxboloid= -REALmax, value;
+ realT *coordp= NULL, *offsetp= NULL, *normalp= NULL;
+ char *s= 0, *t, firstline[qh_MAXfirst+1];
+ int diminput=0, numinput=0, dimfeasible= 0, newnum, k, tempi;
+ int firsttext=0, firstshort=0, firstlong=0, firstpoint=0;
+ int tokcount= 0, linecount=0, maxcount, coordcount=0;
+ boolT islong, isfirst= True, wasbegin= False;
+ boolT isdelaunay= qh->DELAUNAY && !qh->PROJECTinput;
+
+ if (qh->CDDinput) {
+ while ((s= fgets(firstline, qh_MAXfirst, qh->fin))) {
+ linecount++;
+ if (qh->HALFspace && linecount == 1 && isdigit(*s)) {
+ dimfeasible= qh_strtol(s, &s);
+ while (isspace(*s))
+ s++;
+ if (qh_strtol(s, &s) == 1)
+ linecount += qh_readfeasible(qh, dimfeasible, s);
+ else
+ dimfeasible= 0;
+ }else if (!memcmp(firstline, "begin", (size_t)5) || !memcmp(firstline, "BEGIN", (size_t)5))
+ break;
+ else if (!*qh->rbox_command)
+ strncat(qh->rbox_command, s, sizeof(qh->rbox_command)-1);
+ }
+ if (!s) {
+ qh_fprintf(qh, qh->ferr, 6074, "qhull input error: missing \"begin\" for cdd-formated input\n");
+ qh_errexit(qh, qh_ERRinput, NULL, NULL);
+ }
+ }
+ while (!numinput && (s= fgets(firstline, qh_MAXfirst, qh->fin))) {
+ linecount++;
+ if (!memcmp(s, "begin", (size_t)5) || !memcmp(s, "BEGIN", (size_t)5))
+ wasbegin= True;
+ while (*s) {
+ while (isspace(*s))
+ s++;
+ if (!*s)
+ break;
+ if (!isdigit(*s)) {
+ if (!*qh->rbox_command) {
+ strncat(qh->rbox_command, s, sizeof(qh->rbox_command)-1);
+ firsttext= linecount;
+ }
+ break;
+ }
+ if (!diminput)
+ diminput= qh_strtol(s, &s);
+ else {
+ numinput= qh_strtol(s, &s);
+ if (numinput == 1 && diminput >= 2 && qh->HALFspace && !qh->CDDinput) {
+ linecount += qh_readfeasible(qh, diminput, s); /* checks if ok */
+ dimfeasible= diminput;
+ diminput= numinput= 0;
+ }else
+ break;
+ }
+ }
+ }
+ if (!s) {
+ qh_fprintf(qh, qh->ferr, 6075, "qhull input error: short input file. Did not find dimension and number of points\n");
+ qh_errexit(qh, qh_ERRinput, NULL, NULL);
+ }
+ if (diminput > numinput) {
+ tempi= diminput; /* exchange dim and n, e.g., for cdd input format */
+ diminput= numinput;
+ numinput= tempi;
+ }
+ if (diminput < 2) {
+ qh_fprintf(qh, qh->ferr, 6220,"qhull input error: dimension %d(first number) should be at least 2\n",
+ diminput);
+ qh_errexit(qh, qh_ERRinput, NULL, NULL);
+ }
+ if (isdelaunay) {
+ qh->PROJECTdelaunay= False;
+ if (qh->CDDinput)
+ *dimension= diminput;
+ else
+ *dimension= diminput+1;
+ *numpoints= numinput;
+ if (qh->ATinfinity)
+ (*numpoints)++;
+ }else if (qh->HALFspace) {
+ *dimension= diminput - 1;
+ *numpoints= numinput;
+ if (diminput < 3) {
+ qh_fprintf(qh, qh->ferr, 6221,"qhull input error: dimension %d(first number, includes offset) should be at least 3 for halfspaces\n",
+ diminput);
+ qh_errexit(qh, qh_ERRinput, NULL, NULL);
+ }
+ if (dimfeasible) {
+ if (dimfeasible != *dimension) {
+ qh_fprintf(qh, qh->ferr, 6222,"qhull input error: dimension %d of feasible point is not one less than dimension %d for halfspaces\n",
+ dimfeasible, diminput);
+ qh_errexit(qh, qh_ERRinput, NULL, NULL);
+ }
+ }else
+ qh_setfeasible(qh, *dimension);
+ }else {
+ if (qh->CDDinput)
+ *dimension= diminput-1;
+ else
+ *dimension= diminput;
+ *numpoints= numinput;
+ }
+ qh->normal_size= *dimension * sizeof(coordT); /* for tracing with qh_printpoint */
+ if (qh->HALFspace) {
+ qh->half_space= coordp= (coordT*)qh_malloc(qh->normal_size + sizeof(coordT));
+ if (qh->CDDinput) {
+ offsetp= qh->half_space;
+ normalp= offsetp + 1;
+ }else {
+ normalp= qh->half_space;
+ offsetp= normalp + *dimension;
+ }
+ }
+ qh->maxline= diminput * (qh_REALdigits + 5);
+ maximize_(qh->maxline, 500);
+ qh->line= (char*)qh_malloc((qh->maxline+1) * sizeof(char));
+ *ismalloc= True; /* use malloc since memory not setup */
+ coords= points= qh->temp_malloc= /* numinput and diminput >=2 by QH6220 */
+ (coordT*)qh_malloc((*numpoints)*(*dimension)*sizeof(coordT));
+ if (!coords || !qh->line || (qh->HALFspace && !qh->half_space)) {
+ qh_fprintf(qh, qh->ferr, 6076, "qhull error: insufficient memory to read %d points\n",
+ numinput);
+ qh_errexit(qh, qh_ERRmem, NULL, NULL);
+ }
+ if (isdelaunay && qh->ATinfinity) {
+ infinity= points + numinput * (*dimension);
+ for (k= (*dimension) - 1; k--; )
+ infinity[k]= 0.0;
+ }
+ maxcount= numinput * diminput;
+ paraboloid= 0.0;
+ while ((s= (isfirst ? s : fgets(qh->line, qh->maxline, qh->fin)))) {
+ if (!isfirst) {
+ linecount++;
+ if (*s == 'e' || *s == 'E') {
+ if (!memcmp(s, "end", (size_t)3) || !memcmp(s, "END", (size_t)3)) {
+ if (qh->CDDinput )
+ break;
+ else if (wasbegin)
+ qh_fprintf(qh, qh->ferr, 7058, "qhull input warning: the input appears to be in cdd format. If so, use 'Fd'\n");
+ }
+ }
+ }
+ islong= False;
+ while (*s) {
+ while (isspace(*s))
+ s++;
+ value= qh_strtod(s, &t);
+ if (s == t) {
+ if (!*qh->rbox_command)
+ strncat(qh->rbox_command, s, sizeof(qh->rbox_command)-1);
+ if (*s && !firsttext)
+ firsttext= linecount;
+ if (!islong && !firstshort && coordcount)
+ firstshort= linecount;
+ break;
+ }
+ if (!firstpoint)
+ firstpoint= linecount;
+ s= t;
+ if (++tokcount > maxcount)
+ continue;
+ if (qh->HALFspace) {
+ if (qh->CDDinput)
+ *(coordp++)= -value; /* both coefficients and offset */
+ else
+ *(coordp++)= value;
+ }else {
+ *(coords++)= value;
+ if (qh->CDDinput && !coordcount) {
+ if (value != 1.0) {
+ qh_fprintf(qh, qh->ferr, 6077, "qhull input error: for cdd format, point at line %d does not start with '1'\n",
+ linecount);
+ qh_errexit(qh, qh_ERRinput, NULL, NULL);
+ }
+ coords--;
+ }else if (isdelaunay) {
+ paraboloid += value * value;
+ if (qh->ATinfinity) {
+ if (qh->CDDinput)
+ infinity[coordcount-1] += value;
+ else
+ infinity[coordcount] += value;
+ }
+ }
+ }
+ if (++coordcount == diminput) {
+ coordcount= 0;
+ if (isdelaunay) {
+ *(coords++)= paraboloid;
+ maximize_(maxboloid, paraboloid);
+ paraboloid= 0.0;
+ }else if (qh->HALFspace) {
+ if (!qh_sethalfspace(qh, *dimension, coords, &coords, normalp, offsetp, qh->feasible_point)) {
+ qh_fprintf(qh, qh->ferr, 8048, "The halfspace was on line %d\n", linecount);
+ if (wasbegin)
+ qh_fprintf(qh, qh->ferr, 8049, "The input appears to be in cdd format. If so, you should use option 'Fd'\n");
+ qh_errexit(qh, qh_ERRinput, NULL, NULL);
+ }
+ coordp= qh->half_space;
+ }
+ while (isspace(*s))
+ s++;
+ if (*s) {
+ islong= True;
+ if (!firstlong)
+ firstlong= linecount;
+ }
+ }
+ }
+ if (!islong && !firstshort && coordcount)
+ firstshort= linecount;
+ if (!isfirst && s - qh->line >= qh->maxline) {
+ qh_fprintf(qh, qh->ferr, 6078, "qhull input error: line %d contained more than %d characters\n",
+ linecount, (int) (s - qh->line)); /* WARN64 */
+ qh_errexit(qh, qh_ERRinput, NULL, NULL);
+ }
+ isfirst= False;
+ }
+ if (tokcount != maxcount) {
+ newnum= fmin_(numinput, tokcount/diminput);
+ qh_fprintf(qh, qh->ferr, 7073,"\
+qhull warning: instead of %d %d-dimensional points, input contains\n\
+%d points and %d extra coordinates. Line %d is the first\npoint",
+ numinput, diminput, tokcount/diminput, tokcount % diminput, firstpoint);
+ if (firsttext)
+ qh_fprintf(qh, qh->ferr, 8051, ", line %d is the first comment", firsttext);
+ if (firstshort)
+ qh_fprintf(qh, qh->ferr, 8052, ", line %d is the first short\nline", firstshort);
+ if (firstlong)
+ qh_fprintf(qh, qh->ferr, 8053, ", line %d is the first long line", firstlong);
+ qh_fprintf(qh, qh->ferr, 8054, ". Continue with %d points.\n", newnum);
+ numinput= newnum;
+ if (isdelaunay && qh->ATinfinity) {
+ for (k= tokcount % diminput; k--; )
+ infinity[k] -= *(--coords);
+ *numpoints= newnum+1;
+ }else {
+ coords -= tokcount % diminput;
+ *numpoints= newnum;
+ }
+ }
+ if (isdelaunay && qh->ATinfinity) {
+ for (k= (*dimension) -1; k--; )
+ infinity[k] /= numinput;
+ if (coords == infinity)
+ coords += (*dimension) -1;
+ else {
+ for (k=0; k < (*dimension) -1; k++)
+ *(coords++)= infinity[k];
+ }
+ *(coords++)= maxboloid * 1.1;
+ }
+ if (qh->rbox_command[0]) {
+ qh->rbox_command[strlen(qh->rbox_command)-1]= '\0';
+ if (!strcmp(qh->rbox_command, "./rbox D4"))
+ qh_fprintf(qh, qh->ferr, 8055, "\n\
+This is the qhull test case. If any errors or core dumps occur,\n\
+recompile qhull with 'make new'. If errors still occur, there is\n\
+an incompatibility. You should try a different compiler. You can also\n\
+change the choices in user.h. If you discover the source of the problem,\n\
+please send mail to qhull_bug@qhull.org.\n\
+\n\
+Type 'qhull' for a short list of options.\n");
+ }
+ qh_free(qh->line);
+ qh->line= NULL;
+ if (qh->half_space) {
+ qh_free(qh->half_space);
+ qh->half_space= NULL;
+ }
+ qh->temp_malloc= NULL;
+ trace1((qh, qh->ferr, 1008,"qh_readpoints: read in %d %d-dimensional points\n",
+ numinput, diminput));
+ return(points);
+} /* readpoints */
+
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="setfeasible">-</a>
+
+ qh_setfeasible(qh, dim )
+ set qh.feasible_point from qh.feasible_string in "n,n,n" or "n n n" format
+
+ notes:
+ "n,n,n" already checked by qh_initflags()
+ see qh_readfeasible()
+ called only once from qh_new_qhull, otherwise leaks memory
+*/
+void qh_setfeasible(qhT *qh, int dim) {
+ int tokcount= 0;
+ char *s;
+ coordT *coords, value;
+
+ if (!(s= qh->feasible_string)) {
+ qh_fprintf(qh, qh->ferr, 6223, "\
+qhull input error: halfspace intersection needs a feasible point.\n\
+Either prepend the input with 1 point or use 'Hn,n,n'. See manual.\n");
+ qh_errexit(qh, qh_ERRinput, NULL, NULL);
+ }
+ if (!(qh->feasible_point= (pointT*)qh_malloc(dim * sizeof(coordT)))) {
+ qh_fprintf(qh, qh->ferr, 6079, "qhull error: insufficient memory for 'Hn,n,n'\n");
+ qh_errexit(qh, qh_ERRmem, NULL, NULL);
+ }
+ coords= qh->feasible_point;
+ while (*s) {
+ value= qh_strtod(s, &s);
+ if (++tokcount > dim) {
+ qh_fprintf(qh, qh->ferr, 7059, "qhull input warning: more coordinates for 'H%s' than dimension %d\n",
+ qh->feasible_string, dim);
+ break;
+ }
+ *(coords++)= value;
+ if (*s)
+ s++;
+ }
+ while (++tokcount <= dim)
+ *(coords++)= 0.0;
+} /* setfeasible */
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="skipfacet">-</a>
+
+ qh_skipfacet(qh, facet )
+ returns 'True' if this facet is not to be printed
+
+ notes:
+ based on the user provided slice thresholds and 'good' specifications
+*/
+boolT qh_skipfacet(qhT *qh, facetT *facet) {
+ facetT *neighbor, **neighborp;
+
+ if (qh->PRINTneighbors) {
+ if (facet->good)
+ return !qh->PRINTgood;
+ FOREACHneighbor_(facet) {
+ if (neighbor->good)
+ return False;
+ }
+ return True;
+ }else if (qh->PRINTgood)
+ return !facet->good;
+ else if (!facet->normal)
+ return True;
+ return(!qh_inthresholds(qh, facet->normal, NULL));
+} /* skipfacet */
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="skipfilename">-</a>
+
+ qh_skipfilename(qh, string )
+ returns pointer to character after filename
+
+ notes:
+ skips leading spaces
+ ends with spacing or eol
+ if starts with ' or " ends with the same, skipping \' or \"
+ For qhull, qh_argv_to_command() only uses double quotes
+*/
+char *qh_skipfilename(qhT *qh, char *filename) {
+ char *s= filename; /* non-const due to return */
+ char c;
+
+ while (*s && isspace(*s))
+ s++;
+ c= *s++;
+ if (c == '\0') {
+ qh_fprintf(qh, qh->ferr, 6204, "qhull input error: filename expected, none found.\n");
+ qh_errexit(qh, qh_ERRinput, NULL, NULL);
+ }
+ if (c == '\'' || c == '"') {
+ while (*s !=c || s[-1] == '\\') {
+ if (!*s) {
+ qh_fprintf(qh, qh->ferr, 6203, "qhull input error: missing quote after filename -- %s\n", filename);
+ qh_errexit(qh, qh_ERRinput, NULL, NULL);
+ }
+ s++;
+ }
+ s++;
+ }
+ else while (*s && !isspace(*s))
+ s++;
+ return s;
+} /* skipfilename */
+
diff --git a/xs/src/qhull/src/libqhull_r/io_r.h b/xs/src/qhull/src/libqhull_r/io_r.h
new file mode 100644
index 000000000..12e05ae7a
--- /dev/null
+++ b/xs/src/qhull/src/libqhull_r/io_r.h
@@ -0,0 +1,167 @@
+/*<html><pre> -<a href="qh-io_r.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ io_r.h
+ declarations of Input/Output functions
+
+ see README, libqhull_r.h and io_r.c
+
+ Copyright (c) 1993-2015 The Geometry Center.
+ $Id: //main/2015/qhull/src/libqhull_r/io_r.h#3 $$Change: 2079 $
+ $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
+*/
+
+#ifndef qhDEFio
+#define qhDEFio 1
+
+#include "libqhull_r.h"
+
+/*============ constants and flags ==================*/
+
+/*-<a href="qh-io_r.htm#TOC"
+ >--------------------------------</a><a name="qh_MAXfirst">-</a>
+
+ qh_MAXfirst
+ maximum length of first two lines of stdin
+*/
+#define qh_MAXfirst 200
+
+/*-<a href="qh-io_r.htm#TOC"
+ >--------------------------------</a><a name="qh_MINradius">-</a>
+
+ qh_MINradius
+ min radius for Gp and Gv, fraction of maxcoord
+*/
+#define qh_MINradius 0.02
+
+/*-<a href="qh-io_r.htm#TOC"
+ >--------------------------------</a><a name="qh_GEOMepsilon">-</a>
+
+ qh_GEOMepsilon
+ adjust outer planes for 'lines closer' and geomview roundoff.
+ This prevents bleed through.
+*/
+#define qh_GEOMepsilon 2e-3
+
+/*-<a href="qh-io_r.htm#TOC"
+ >--------------------------------</a><a name="qh_WHITESPACE">-</a>
+
+ qh_WHITESPACE
+ possible values of white space
+*/
+#define qh_WHITESPACE " \n\t\v\r\f"
+
+
+/*-<a href="qh-io_r.htm#TOC"
+ >--------------------------------</a><a name="RIDGE">-</a>
+
+ qh_RIDGE
+ to select which ridges to print in qh_eachvoronoi
+*/
+typedef enum
+{
+ qh_RIDGEall = 0, qh_RIDGEinner, qh_RIDGEouter
+}
+qh_RIDGE;
+
+/*-<a href="qh-io_r.htm#TOC"
+ >--------------------------------</a><a name="printvridgeT">-</a>
+
+ printvridgeT
+ prints results of qh_printvdiagram
+
+ see:
+ <a href="io_r.c#printvridge">qh_printvridge</a> for an example
+*/
+typedef void (*printvridgeT)(qhT *qh, FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded);
+
+/*============== -prototypes in alphabetical order =========*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void qh_dfacet(qhT *qh, unsigned id);
+void qh_dvertex(qhT *qh, unsigned id);
+int qh_compare_facetarea(const void *p1, const void *p2);
+int qh_compare_facetmerge(const void *p1, const void *p2);
+int qh_compare_facetvisit(const void *p1, const void *p2);
+/* int qh_compare_vertexpoint(const void *p1, const void *p2); Not useable since it depends on qh */
+void qh_copyfilename(qhT *qh, char *filename, int size, const char* source, int length);
+void qh_countfacets(qhT *qh, facetT *facetlist, setT *facets, boolT printall,
+ int *numfacetsp, int *numsimplicialp, int *totneighborsp,
+ int *numridgesp, int *numcoplanarsp, int *numnumtricoplanarsp);
+pointT *qh_detvnorm(qhT *qh, vertexT *vertex, vertexT *vertexA, setT *centers, realT *offsetp);
+setT *qh_detvridge(qhT *qh, vertexT *vertex);
+setT *qh_detvridge3(qhT *qh, vertexT *atvertex, vertexT *vertex);
+int qh_eachvoronoi(qhT *qh, FILE *fp, printvridgeT printvridge, vertexT *atvertex, boolT visitall, qh_RIDGE innerouter, boolT inorder);
+int qh_eachvoronoi_all(qhT *qh, FILE *fp, printvridgeT printvridge, boolT isUpper, qh_RIDGE innerouter, boolT inorder);
+void qh_facet2point(qhT *qh, facetT *facet, pointT **point0, pointT **point1, realT *mindist);
+setT *qh_facetvertices(qhT *qh, facetT *facetlist, setT *facets, boolT allfacets);
+void qh_geomplanes(qhT *qh, facetT *facet, realT *outerplane, realT *innerplane);
+void qh_markkeep(qhT *qh, facetT *facetlist);
+setT *qh_markvoronoi(qhT *qh, facetT *facetlist, setT *facets, boolT printall, boolT *isLowerp, int *numcentersp);
+void qh_order_vertexneighbors(qhT *qh, vertexT *vertex);
+void qh_prepare_output(qhT *qh);
+void qh_printafacet(qhT *qh, FILE *fp, qh_PRINT format, facetT *facet, boolT printall);
+void qh_printbegin(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall);
+void qh_printcenter(qhT *qh, FILE *fp, qh_PRINT format, const char *string, facetT *facet);
+void qh_printcentrum(qhT *qh, FILE *fp, facetT *facet, realT radius);
+void qh_printend(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall);
+void qh_printend4geom(qhT *qh, FILE *fp, facetT *facet, int *num, boolT printall);
+void qh_printextremes(qhT *qh, FILE *fp, facetT *facetlist, setT *facets, boolT printall);
+void qh_printextremes_2d(qhT *qh, FILE *fp, facetT *facetlist, setT *facets, boolT printall);
+void qh_printextremes_d(qhT *qh, FILE *fp, facetT *facetlist, setT *facets, boolT printall);
+void qh_printfacet(qhT *qh, FILE *fp, facetT *facet);
+void qh_printfacet2math(qhT *qh, FILE *fp, facetT *facet, qh_PRINT format, int notfirst);
+void qh_printfacet2geom(qhT *qh, FILE *fp, facetT *facet, realT color[3]);
+void qh_printfacet2geom_points(qhT *qh, FILE *fp, pointT *point1, pointT *point2,
+ facetT *facet, realT offset, realT color[3]);
+void qh_printfacet3math(qhT *qh, FILE *fp, facetT *facet, qh_PRINT format, int notfirst);
+void qh_printfacet3geom_nonsimplicial(qhT *qh, FILE *fp, facetT *facet, realT color[3]);
+void qh_printfacet3geom_points(qhT *qh, FILE *fp, setT *points, facetT *facet, realT offset, realT color[3]);
+void qh_printfacet3geom_simplicial(qhT *qh, FILE *fp, facetT *facet, realT color[3]);
+void qh_printfacet3vertex(qhT *qh, FILE *fp, facetT *facet, qh_PRINT format);
+void qh_printfacet4geom_nonsimplicial(qhT *qh, FILE *fp, facetT *facet, realT color[3]);
+void qh_printfacet4geom_simplicial(qhT *qh, FILE *fp, facetT *facet, realT color[3]);
+void qh_printfacetNvertex_nonsimplicial(qhT *qh, FILE *fp, facetT *facet, int id, qh_PRINT format);
+void qh_printfacetNvertex_simplicial(qhT *qh, FILE *fp, facetT *facet, qh_PRINT format);
+void qh_printfacetheader(qhT *qh, FILE *fp, facetT *facet);
+void qh_printfacetridges(qhT *qh, FILE *fp, facetT *facet);
+void qh_printfacets(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall);
+void qh_printhyperplaneintersection(qhT *qh, FILE *fp, facetT *facet1, facetT *facet2,
+ setT *vertices, realT color[3]);
+void qh_printneighborhood(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetA, facetT *facetB, boolT printall);
+void qh_printline3geom(qhT *qh, FILE *fp, pointT *pointA, pointT *pointB, realT color[3]);
+void qh_printpoint(qhT *qh, FILE *fp, const char *string, pointT *point);
+void qh_printpointid(qhT *qh, FILE *fp, const char *string, int dim, pointT *point, int id);
+void qh_printpoint3(qhT *qh, FILE *fp, pointT *point);
+void qh_printpoints_out(qhT *qh, FILE *fp, facetT *facetlist, setT *facets, boolT printall);
+void qh_printpointvect(qhT *qh, FILE *fp, pointT *point, coordT *normal, pointT *center, realT radius, realT color[3]);
+void qh_printpointvect2(qhT *qh, FILE *fp, pointT *point, coordT *normal, pointT *center, realT radius);
+void qh_printridge(qhT *qh, FILE *fp, ridgeT *ridge);
+void qh_printspheres(qhT *qh, FILE *fp, setT *vertices, realT radius);
+void qh_printvdiagram(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall);
+int qh_printvdiagram2(qhT *qh, FILE *fp, printvridgeT printvridge, setT *vertices, qh_RIDGE innerouter, boolT inorder);
+void qh_printvertex(qhT *qh, FILE *fp, vertexT *vertex);
+void qh_printvertexlist(qhT *qh, FILE *fp, const char* string, facetT *facetlist,
+ setT *facets, boolT printall);
+void qh_printvertices(qhT *qh, FILE *fp, const char* string, setT *vertices);
+void qh_printvneighbors(qhT *qh, FILE *fp, facetT* facetlist, setT *facets, boolT printall);
+void qh_printvoronoi(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall);
+void qh_printvnorm(qhT *qh, FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded);
+void qh_printvridge(qhT *qh, FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded);
+void qh_produce_output(qhT *qh);
+void qh_produce_output2(qhT *qh);
+void qh_projectdim3(qhT *qh, pointT *source, pointT *destination);
+int qh_readfeasible(qhT *qh, int dim, const char *curline);
+coordT *qh_readpoints(qhT *qh, int *numpoints, int *dimension, boolT *ismalloc);
+void qh_setfeasible(qhT *qh, int dim);
+boolT qh_skipfacet(qhT *qh, facetT *facet);
+char *qh_skipfilename(qhT *qh, char *filename);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* qhDEFio */
diff --git a/xs/src/qhull/src/libqhull_r/libqhull_r.c b/xs/src/qhull/src/libqhull_r/libqhull_r.c
new file mode 100644
index 000000000..0fe0c980d
--- /dev/null
+++ b/xs/src/qhull/src/libqhull_r/libqhull_r.c
@@ -0,0 +1,1403 @@
+/*<html><pre> -<a href="qh-qhull_r.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ libqhull_r.c
+ Quickhull algorithm for convex hulls
+
+ qhull() and top-level routines
+
+ see qh-qhull_r.htm, libqhull.h, unix_r.c
+
+ see qhull_ra.h for internal functions
+
+ Copyright (c) 1993-2015 The Geometry Center.
+ $Id: //main/2015/qhull/src/libqhull_r/libqhull_r.c#2 $$Change: 2047 $
+ $DateTime: 2016/01/04 22:03:18 $$Author: bbarber $
+*/
+
+#include "qhull_ra.h"
+
+/*============= functions in alphabetic order after qhull() =======*/
+
+/*-<a href="qh-qhull_r.htm#TOC"
+ >-------------------------------</a><a name="qhull">-</a>
+
+ qh_qhull(qh)
+ compute DIM3 convex hull of qh.num_points starting at qh.first_point
+ qh->contains all global options and variables
+
+ returns:
+ returns polyhedron
+ qh.facet_list, qh.num_facets, qh.vertex_list, qh.num_vertices,
+
+ returns global variables
+ qh.hulltime, qh.max_outside, qh.interior_point, qh.max_vertex, qh.min_vertex
+
+ returns precision constants
+ qh.ANGLEround, centrum_radius, cos_max, DISTround, MAXabs_coord, ONEmerge
+
+ notes:
+ unless needed for output
+ qh.max_vertex and qh.min_vertex are max/min due to merges
+
+ see:
+ to add individual points to either qh.num_points
+ use qh_addpoint()
+
+ if qh.GETarea
+ qh_produceoutput() returns qh.totarea and qh.totvol via qh_getarea()
+
+ design:
+ record starting time
+ initialize hull and partition points
+ build convex hull
+ unless early termination
+ update facet->maxoutside for vertices, coplanar, and near-inside points
+ error if temporary sets exist
+ record end time
+*/
+
+void qh_qhull(qhT *qh) {
+ int numoutside;
+
+ qh->hulltime= qh_CPUclock;
+ if (qh->RERUN || qh->JOGGLEmax < REALmax/2)
+ qh_build_withrestart(qh);
+ else {
+ qh_initbuild(qh);
+ qh_buildhull(qh);
+ }
+ if (!qh->STOPpoint && !qh->STOPcone) {
+ if (qh->ZEROall_ok && !qh->TESTvneighbors && qh->MERGEexact)
+ qh_checkzero(qh, qh_ALL);
+ if (qh->ZEROall_ok && !qh->TESTvneighbors && !qh->WAScoplanar) {
+ trace2((qh, qh->ferr, 2055, "qh_qhull: all facets are clearly convex and no coplanar points. Post-merging and check of maxout not needed.\n"));
+ qh->DOcheckmax= False;
+ }else {
+ if (qh->MERGEexact || (qh->hull_dim > qh_DIMreduceBuild && qh->PREmerge))
+ qh_postmerge(qh, "First post-merge", qh->premerge_centrum, qh->premerge_cos,
+ (qh->POSTmerge ? False : qh->TESTvneighbors));
+ else if (!qh->POSTmerge && qh->TESTvneighbors)
+ qh_postmerge(qh, "For testing vertex neighbors", qh->premerge_centrum,
+ qh->premerge_cos, True);
+ if (qh->POSTmerge)
+ qh_postmerge(qh, "For post-merging", qh->postmerge_centrum,
+ qh->postmerge_cos, qh->TESTvneighbors);
+ if (qh->visible_list == qh->facet_list) { /* i.e., merging done */
+ qh->findbestnew= True;
+ qh_partitionvisible(qh /*qh.visible_list*/, !qh_ALL, &numoutside);
+ qh->findbestnew= False;
+ qh_deletevisible(qh /*qh.visible_list*/);
+ qh_resetlists(qh, False, qh_RESETvisible /*qh.visible_list newvertex_list newfacet_list */);
+ }
+ }
+ if (qh->DOcheckmax){
+ if (qh->REPORTfreq) {
+ qh_buildtracing(qh, NULL, NULL);
+ qh_fprintf(qh, qh->ferr, 8115, "\nTesting all coplanar points.\n");
+ }
+ qh_check_maxout(qh);
+ }
+ if (qh->KEEPnearinside && !qh->maxoutdone)
+ qh_nearcoplanar(qh);
+ }
+ if (qh_setsize(qh, qh->qhmem.tempstack) != 0) {
+ qh_fprintf(qh, qh->ferr, 6164, "qhull internal error (qh_qhull): temporary sets not empty(%d)\n",
+ qh_setsize(qh, qh->qhmem.tempstack));
+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+ }
+ qh->hulltime= qh_CPUclock - qh->hulltime;
+ qh->QHULLfinished= True;
+ trace1((qh, qh->ferr, 1036, "Qhull: algorithm completed\n"));
+} /* qhull */
+
+/*-<a href="qh-qhull_r.htm#TOC"
+ >-------------------------------</a><a name="addpoint">-</a>
+
+ qh_addpoint(qh, furthest, facet, checkdist )
+ add point (usually furthest point) above facet to hull
+ if checkdist,
+ check that point is above facet.
+ if point is not outside of the hull, uses qh_partitioncoplanar()
+ assumes that facet is defined by qh_findbestfacet()
+ else if facet specified,
+ assumes that point is above facet (major damage if below)
+ for Delaunay triangulations,
+ Use qh_setdelaunay() to lift point to paraboloid and scale by 'Qbb' if needed
+ Do not use options 'Qbk', 'QBk', or 'QbB' since they scale the coordinates.
+
+ returns:
+ returns False if user requested an early termination
+ qh.visible_list, newfacet_list, delvertex_list, NEWfacets may be defined
+ updates qh.facet_list, qh.num_facets, qh.vertex_list, qh.num_vertices
+ clear qh.maxoutdone (will need to call qh_check_maxout() for facet->maxoutside)
+ if unknown point, adds a pointer to qh.other_points
+ do not deallocate the point's coordinates
+
+ notes:
+ assumes point is near its best facet and not at a local minimum of a lens
+ distributions. Use qh_findbestfacet to avoid this case.
+ uses qh.visible_list, qh.newfacet_list, qh.delvertex_list, qh.NEWfacets
+
+ see also:
+ qh_triangulate() -- triangulate non-simplicial facets
+
+ design:
+ add point to other_points if needed
+ if checkdist
+ if point not above facet
+ partition coplanar point
+ exit
+ exit if pre STOPpoint requested
+ find horizon and visible facets for point
+ make new facets for point to horizon
+ make hyperplanes for point
+ compute balance statistics
+ match neighboring new facets
+ update vertex neighbors and delete interior vertices
+ exit if STOPcone requested
+ merge non-convex new facets
+ if merge found, many merges, or 'Qf'
+ use qh_findbestnew() instead of qh_findbest()
+ partition outside points from visible facets
+ delete visible facets
+ check polyhedron if requested
+ exit if post STOPpoint requested
+ reset working lists of facets and vertices
+*/
+boolT qh_addpoint(qhT *qh, pointT *furthest, facetT *facet, boolT checkdist) {
+ int goodvisible, goodhorizon;
+ vertexT *vertex;
+ facetT *newfacet;
+ realT dist, newbalance, pbalance;
+ boolT isoutside= False;
+ int numpart, numpoints, numnew, firstnew;
+
+ qh->maxoutdone= False;
+ if (qh_pointid(qh, furthest) == qh_IDunknown)
+ qh_setappend(qh, &qh->other_points, furthest);
+ if (!facet) {
+ qh_fprintf(qh, qh->ferr, 6213, "qhull internal error (qh_addpoint): NULL facet. Need to call qh_findbestfacet first\n");
+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+ }
+ if (checkdist) {
+ facet= qh_findbest(qh, furthest, facet, !qh_ALL, !qh_ISnewfacets, !qh_NOupper,
+ &dist, &isoutside, &numpart);
+ zzadd_(Zpartition, numpart);
+ if (!isoutside) {
+ zinc_(Znotmax); /* last point of outsideset is no longer furthest. */
+ facet->notfurthest= True;
+ qh_partitioncoplanar(qh, furthest, facet, &dist);
+ return True;
+ }
+ }
+ qh_buildtracing(qh, furthest, facet);
+ if (qh->STOPpoint < 0 && qh->furthest_id == -qh->STOPpoint-1) {
+ facet->notfurthest= True;
+ return False;
+ }
+ qh_findhorizon(qh, furthest, facet, &goodvisible, &goodhorizon);
+ if (qh->ONLYgood && !(goodvisible+goodhorizon) && !qh->GOODclosest) {
+ zinc_(Znotgood);
+ facet->notfurthest= True;
+ /* last point of outsideset is no longer furthest. This is ok
+ since all points of the outside are likely to be bad */
+ qh_resetlists(qh, False, qh_RESETvisible /*qh.visible_list newvertex_list newfacet_list */);
+ return True;
+ }
+ zzinc_(Zprocessed);
+ firstnew= qh->facet_id;
+ vertex= qh_makenewfacets(qh, furthest /*visible_list, attaches if !ONLYgood */);
+ qh_makenewplanes(qh /* newfacet_list */);
+ numnew= qh->facet_id - firstnew;
+ newbalance= numnew - (realT) (qh->num_facets-qh->num_visible)
+ * qh->hull_dim/qh->num_vertices;
+ wadd_(Wnewbalance, newbalance);
+ wadd_(Wnewbalance2, newbalance * newbalance);
+ if (qh->ONLYgood
+ && !qh_findgood(qh, qh->newfacet_list, goodhorizon) && !qh->GOODclosest) {
+ FORALLnew_facets
+ qh_delfacet(qh, newfacet);
+ qh_delvertex(qh, vertex);
+ qh_resetlists(qh, True, qh_RESETvisible /*qh.visible_list newvertex_list newfacet_list */);
+ zinc_(Znotgoodnew);
+ facet->notfurthest= True;
+ return True;
+ }
+ if (qh->ONLYgood)
+ qh_attachnewfacets(qh /*visible_list*/);
+ qh_matchnewfacets(qh);
+ qh_updatevertices(qh);
+ if (qh->STOPcone && qh->furthest_id == qh->STOPcone-1) {
+ facet->notfurthest= True;
+ return False; /* visible_list etc. still defined */
+ }
+ qh->findbestnew= False;
+ if (qh->PREmerge || qh->MERGEexact) {
+ qh_premerge(qh, vertex, qh->premerge_centrum, qh->premerge_cos);
+ if (qh_USEfindbestnew)
+ qh->findbestnew= True;
+ else {
+ FORALLnew_facets {
+ if (!newfacet->simplicial) {
+ qh->findbestnew= True; /* use qh_findbestnew instead of qh_findbest*/
+ break;
+ }
+ }
+ }
+ }else if (qh->BESToutside)
+ qh->findbestnew= True;
+ qh_partitionvisible(qh /*qh.visible_list*/, !qh_ALL, &numpoints);
+ qh->findbestnew= False;
+ qh->findbest_notsharp= False;
+ zinc_(Zpbalance);
+ pbalance= numpoints - (realT) qh->hull_dim /* assumes all points extreme */
+ * (qh->num_points - qh->num_vertices)/qh->num_vertices;
+ wadd_(Wpbalance, pbalance);
+ wadd_(Wpbalance2, pbalance * pbalance);
+ qh_deletevisible(qh /*qh.visible_list*/);
+ zmax_(Zmaxvertex, qh->num_vertices);
+ qh->NEWfacets= False;
+ if (qh->IStracing >= 4) {
+ if (qh->num_facets < 2000)
+ qh_printlists(qh);
+ qh_printfacetlist(qh, qh->newfacet_list, NULL, True);
+ qh_checkpolygon(qh, qh->facet_list);
+ }else if (qh->CHECKfrequently) {
+ if (qh->num_facets < 50)
+ qh_checkpolygon(qh, qh->facet_list);
+ else
+ qh_checkpolygon(qh, qh->newfacet_list);
+ }
+ if (qh->STOPpoint > 0 && qh->furthest_id == qh->STOPpoint-1)
+ return False;
+ qh_resetlists(qh, True, qh_RESETvisible /*qh.visible_list newvertex_list newfacet_list */);
+ /* qh_triangulate(qh); to test qh.TRInormals */
+ trace2((qh, qh->ferr, 2056, "qh_addpoint: added p%d new facets %d new balance %2.2g point balance %2.2g\n",
+ qh_pointid(qh, furthest), numnew, newbalance, pbalance));
+ return True;
+} /* addpoint */
+
+/*-<a href="qh-qhull_r.htm#TOC"
+ >-------------------------------</a><a name="build_withrestart">-</a>
+
+ qh_build_withrestart(qh)
+ allow restarts due to qh.JOGGLEmax while calling qh_buildhull()
+ qh_errexit always undoes qh_build_withrestart()
+ qh.FIRSTpoint/qh.NUMpoints is point array
+ it may be moved by qh_joggleinput(qh)
+*/
+void qh_build_withrestart(qhT *qh) {
+ int restart;
+
+ qh->ALLOWrestart= True;
+ while (True) {
+ restart= setjmp(qh->restartexit); /* simple statement for CRAY J916 */
+ if (restart) { /* only from qh_precision() */
+ zzinc_(Zretry);
+ wmax_(Wretrymax, qh->JOGGLEmax);
+ /* QH7078 warns about using 'TCn' with 'QJn' */
+ qh->STOPcone= qh_IDunknown; /* if break from joggle, prevents normal output */
+ }
+ if (!qh->RERUN && qh->JOGGLEmax < REALmax/2) {
+ if (qh->build_cnt > qh_JOGGLEmaxretry) {
+ qh_fprintf(qh, qh->ferr, 6229, "qhull precision error: %d attempts to construct a convex hull\n\
+ with joggled input. Increase joggle above 'QJ%2.2g'\n\
+ or modify qh_JOGGLE... parameters in user.h\n",
+ qh->build_cnt, qh->JOGGLEmax);
+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+ }
+ if (qh->build_cnt && !restart)
+ break;
+ }else if (qh->build_cnt && qh->build_cnt >= qh->RERUN)
+ break;
+ qh->STOPcone= 0;
+ qh_freebuild(qh, True); /* first call is a nop */
+ qh->build_cnt++;
+ if (!qh->qhull_optionsiz)
+ qh->qhull_optionsiz= (int)strlen(qh->qhull_options); /* WARN64 */
+ else {
+ qh->qhull_options [qh->qhull_optionsiz]= '\0';
+ qh->qhull_optionlen= qh_OPTIONline; /* starts a new line */
+ }
+ qh_option(qh, "_run", &qh->build_cnt, NULL);
+ if (qh->build_cnt == qh->RERUN) {
+ qh->IStracing= qh->TRACElastrun; /* duplicated from qh_initqhull_globals */
+ if (qh->TRACEpoint != qh_IDunknown || qh->TRACEdist < REALmax/2 || qh->TRACEmerge) {
+ qh->TRACElevel= (qh->IStracing? qh->IStracing : 3);
+ qh->IStracing= 0;
+ }
+ qh->qhmem.IStracing= qh->IStracing;
+ }
+ if (qh->JOGGLEmax < REALmax/2)
+ qh_joggleinput(qh);
+ qh_initbuild(qh);
+ qh_buildhull(qh);
+ if (qh->JOGGLEmax < REALmax/2 && !qh->MERGING)
+ qh_checkconvex(qh, qh->facet_list, qh_ALGORITHMfault);
+ }
+ qh->ALLOWrestart= False;
+} /* qh_build_withrestart */
+
+/*-<a href="qh-qhull_r.htm#TOC"
+ >-------------------------------</a><a name="buildhull">-</a>
+
+ qh_buildhull(qh)
+ construct a convex hull by adding outside points one at a time
+
+ returns:
+
+ notes:
+ may be called multiple times
+ checks facet and vertex lists for incorrect flags
+ to recover from STOPcone, call qh_deletevisible and qh_resetlists
+
+ design:
+ check visible facet and newfacet flags
+ check newlist vertex flags and qh.STOPcone/STOPpoint
+ for each facet with a furthest outside point
+ add point to facet
+ exit if qh.STOPcone or qh.STOPpoint requested
+ if qh.NARROWhull for initial simplex
+ partition remaining outside points to coplanar sets
+*/
+void qh_buildhull(qhT *qh) {
+ facetT *facet;
+ pointT *furthest;
+ vertexT *vertex;
+ int id;
+
+ trace1((qh, qh->ferr, 1037, "qh_buildhull: start build hull\n"));
+ FORALLfacets {
+ if (facet->visible || facet->newfacet) {
+ qh_fprintf(qh, qh->ferr, 6165, "qhull internal error (qh_buildhull): visible or new facet f%d in facet list\n",
+ facet->id);
+ qh_errexit(qh, qh_ERRqhull, facet, NULL);
+ }
+ }
+ FORALLvertices {
+ if (vertex->newlist) {
+ qh_fprintf(qh, qh->ferr, 6166, "qhull internal error (qh_buildhull): new vertex f%d in vertex list\n",
+ vertex->id);
+ qh_errprint(qh, "ERRONEOUS", NULL, NULL, NULL, vertex);
+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+ }
+ id= qh_pointid(qh, vertex->point);
+ if ((qh->STOPpoint>0 && id == qh->STOPpoint-1) ||
+ (qh->STOPpoint<0 && id == -qh->STOPpoint-1) ||
+ (qh->STOPcone>0 && id == qh->STOPcone-1)) {
+ trace1((qh, qh->ferr, 1038,"qh_buildhull: stop point or cone P%d in initial hull\n", id));
+ return;
+ }
+ }
+ qh->facet_next= qh->facet_list; /* advance facet when processed */
+ while ((furthest= qh_nextfurthest(qh, &facet))) {
+ qh->num_outside--; /* if ONLYmax, furthest may not be outside */
+ if (!qh_addpoint(qh, furthest, facet, qh->ONLYmax))
+ break;
+ }
+ if (qh->NARROWhull) /* move points from outsideset to coplanarset */
+ qh_outcoplanar(qh /* facet_list */ );
+ if (qh->num_outside && !furthest) {
+ qh_fprintf(qh, qh->ferr, 6167, "qhull internal error (qh_buildhull): %d outside points were never processed.\n", qh->num_outside);
+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+ }
+ trace1((qh, qh->ferr, 1039, "qh_buildhull: completed the hull construction\n"));
+} /* buildhull */
+
+
+/*-<a href="qh-qhull_r.htm#TOC"
+ >-------------------------------</a><a name="buildtracing">-</a>
+
+ qh_buildtracing(qh, furthest, facet )
+ trace an iteration of qh_buildhull() for furthest point and facet
+ if !furthest, prints progress message
+
+ returns:
+ tracks progress with qh.lastreport
+ updates qh.furthest_id (-3 if furthest is NULL)
+ also resets visit_id, vertext_visit on wrap around
+
+ see:
+ qh_tracemerging()
+
+ design:
+ if !furthest
+ print progress message
+ exit
+ if 'TFn' iteration
+ print progress message
+ else if tracing
+ trace furthest point and facet
+ reset qh.visit_id and qh.vertex_visit if overflow may occur
+ set qh.furthest_id for tracing
+*/
+void qh_buildtracing(qhT *qh, pointT *furthest, facetT *facet) {
+ realT dist= 0;
+ float cpu;
+ int total, furthestid;
+ time_t timedata;
+ struct tm *tp;
+ vertexT *vertex;
+
+ qh->old_randomdist= qh->RANDOMdist;
+ qh->RANDOMdist= False;
+ if (!furthest) {
+ time(&timedata);
+ tp= localtime(&timedata);
+ cpu= (float)qh_CPUclock - (float)qh->hulltime;
+ cpu /= (float)qh_SECticks;
+ total= zzval_(Ztotmerge) - zzval_(Zcyclehorizon) + zzval_(Zcyclefacettot);
+ qh_fprintf(qh, qh->ferr, 8118, "\n\
+At %02d:%02d:%02d & %2.5g CPU secs, qhull has created %d facets and merged %d.\n\
+ The current hull contains %d facets and %d vertices. Last point was p%d\n",
+ tp->tm_hour, tp->tm_min, tp->tm_sec, cpu, qh->facet_id -1,
+ total, qh->num_facets, qh->num_vertices, qh->furthest_id);
+ return;
+ }
+ furthestid= qh_pointid(qh, furthest);
+ if (qh->TRACEpoint == furthestid) {
+ qh->IStracing= qh->TRACElevel;
+ qh->qhmem.IStracing= qh->TRACElevel;
+ }else if (qh->TRACEpoint != qh_IDunknown && qh->TRACEdist < REALmax/2) {
+ qh->IStracing= 0;
+ qh->qhmem.IStracing= 0;
+ }
+ if (qh->REPORTfreq && (qh->facet_id-1 > qh->lastreport+qh->REPORTfreq)) {
+ qh->lastreport= qh->facet_id-1;
+ time(&timedata);
+ tp= localtime(&timedata);
+ cpu= (float)qh_CPUclock - (float)qh->hulltime;
+ cpu /= (float)qh_SECticks;
+ total= zzval_(Ztotmerge) - zzval_(Zcyclehorizon) + zzval_(Zcyclefacettot);
+ zinc_(Zdistio);
+ qh_distplane(qh, furthest, facet, &dist);
+ qh_fprintf(qh, qh->ferr, 8119, "\n\
+At %02d:%02d:%02d & %2.5g CPU secs, qhull has created %d facets and merged %d.\n\
+ The current hull contains %d facets and %d vertices. There are %d\n\
+ outside points. Next is point p%d(v%d), %2.2g above f%d.\n",
+ tp->tm_hour, tp->tm_min, tp->tm_sec, cpu, qh->facet_id -1,
+ total, qh->num_facets, qh->num_vertices, qh->num_outside+1,
+ furthestid, qh->vertex_id, dist, getid_(facet));
+ }else if (qh->IStracing >=1) {
+ cpu= (float)qh_CPUclock - (float)qh->hulltime;
+ cpu /= (float)qh_SECticks;
+ qh_distplane(qh, furthest, facet, &dist);
+ qh_fprintf(qh, qh->ferr, 8120, "qh_addpoint: add p%d(v%d) to hull of %d facets(%2.2g above f%d) and %d outside at %4.4g CPU secs. Previous was p%d.\n",
+ furthestid, qh->vertex_id, qh->num_facets, dist,
+ getid_(facet), qh->num_outside+1, cpu, qh->furthest_id);
+ }
+ zmax_(Zvisit2max, (int)qh->visit_id/2);
+ if (qh->visit_id > (unsigned) INT_MAX) { /* 31 bits */
+ zinc_(Zvisit);
+ qh->visit_id= 0;
+ FORALLfacets
+ facet->visitid= 0;
+ }
+ zmax_(Zvvisit2max, (int)qh->vertex_visit/2);
+ if (qh->vertex_visit > (unsigned) INT_MAX) { /* 31 bits */
+ zinc_(Zvvisit);
+ qh->vertex_visit= 0;
+ FORALLvertices
+ vertex->visitid= 0;
+ }
+ qh->furthest_id= furthestid;
+ qh->RANDOMdist= qh->old_randomdist;
+} /* buildtracing */
+
+/*-<a href="qh-qhull_r.htm#TOC"
+ >-------------------------------</a><a name="errexit2">-</a>
+
+ qh_errexit2(qh, exitcode, facet, otherfacet )
+ return exitcode to system after an error
+ report two facets
+
+ returns:
+ assumes exitcode non-zero
+
+ see:
+ normally use qh_errexit() in user.c(reports a facet and a ridge)
+*/
+void qh_errexit2(qhT *qh, int exitcode, facetT *facet, facetT *otherfacet) {
+
+ qh_errprint(qh, "ERRONEOUS", facet, otherfacet, NULL, NULL);
+ qh_errexit(qh, exitcode, NULL, NULL);
+} /* errexit2 */
+
+
+/*-<a href="qh-qhull_r.htm#TOC"
+ >-------------------------------</a><a name="findhorizon">-</a>
+
+ qh_findhorizon(qh, point, facet, goodvisible, goodhorizon )
+ given a visible facet, find the point's horizon and visible facets
+ for all facets, !facet-visible
+
+ returns:
+ returns qh.visible_list/num_visible with all visible facets
+ marks visible facets with ->visible
+ updates count of good visible and good horizon facets
+ updates qh.max_outside, qh.max_vertex, facet->maxoutside
+
+ see:
+ similar to qh_delpoint()
+
+ design:
+ move facet to qh.visible_list at end of qh.facet_list
+ for all visible facets
+ for each unvisited neighbor of a visible facet
+ compute distance of point to neighbor
+ if point above neighbor
+ move neighbor to end of qh.visible_list
+ else if point is coplanar with neighbor
+ update qh.max_outside, qh.max_vertex, neighbor->maxoutside
+ mark neighbor coplanar (will create a samecycle later)
+ update horizon statistics
+*/
+void qh_findhorizon(qhT *qh, pointT *point, facetT *facet, int *goodvisible, int *goodhorizon) {
+ facetT *neighbor, **neighborp, *visible;
+ int numhorizon= 0, coplanar= 0;
+ realT dist;
+
+ trace1((qh, qh->ferr, 1040,"qh_findhorizon: find horizon for point p%d facet f%d\n",qh_pointid(qh, point),facet->id));
+ *goodvisible= *goodhorizon= 0;
+ zinc_(Ztotvisible);
+ qh_removefacet(qh, facet); /* visible_list at end of qh->facet_list */
+ qh_appendfacet(qh, facet);
+ qh->num_visible= 1;
+ if (facet->good)
+ (*goodvisible)++;
+ qh->visible_list= facet;
+ facet->visible= True;
+ facet->f.replace= NULL;
+ if (qh->IStracing >=4)
+ qh_errprint(qh, "visible", facet, NULL, NULL, NULL);
+ qh->visit_id++;
+ FORALLvisible_facets {
+ if (visible->tricoplanar && !qh->TRInormals) {
+ qh_fprintf(qh, qh->ferr, 6230, "Qhull internal error (qh_findhorizon): does not work for tricoplanar facets. Use option 'Q11'\n");
+ qh_errexit(qh, qh_ERRqhull, visible, NULL);
+ }
+ visible->visitid= qh->visit_id;
+ FOREACHneighbor_(visible) {
+ if (neighbor->visitid == qh->visit_id)
+ continue;
+ neighbor->visitid= qh->visit_id;
+ zzinc_(Znumvisibility);
+ qh_distplane(qh, point, neighbor, &dist);
+ if (dist > qh->MINvisible) {
+ zinc_(Ztotvisible);
+ qh_removefacet(qh, neighbor); /* append to end of qh->visible_list */
+ qh_appendfacet(qh, neighbor);
+ neighbor->visible= True;
+ neighbor->f.replace= NULL;
+ qh->num_visible++;
+ if (neighbor->good)
+ (*goodvisible)++;
+ if (qh->IStracing >=4)
+ qh_errprint(qh, "visible", neighbor, NULL, NULL, NULL);
+ }else {
+ if (dist > - qh->MAXcoplanar) {
+ neighbor->coplanar= True;
+ zzinc_(Zcoplanarhorizon);
+ qh_precision(qh, "coplanar horizon");
+ coplanar++;
+ if (qh->MERGING) {
+ if (dist > 0) {
+ maximize_(qh->max_outside, dist);
+ maximize_(qh->max_vertex, dist);
+#if qh_MAXoutside
+ maximize_(neighbor->maxoutside, dist);
+#endif
+ }else
+ minimize_(qh->min_vertex, dist); /* due to merge later */
+ }
+ trace2((qh, qh->ferr, 2057, "qh_findhorizon: point p%d is coplanar to horizon f%d, dist=%2.7g < qh->MINvisible(%2.7g)\n",
+ qh_pointid(qh, point), neighbor->id, dist, qh->MINvisible));
+ }else
+ neighbor->coplanar= False;
+ zinc_(Ztothorizon);
+ numhorizon++;
+ if (neighbor->good)
+ (*goodhorizon)++;
+ if (qh->IStracing >=4)
+ qh_errprint(qh, "horizon", neighbor, NULL, NULL, NULL);
+ }
+ }
+ }
+ if (!numhorizon) {
+ qh_precision(qh, "empty horizon");
+ qh_fprintf(qh, qh->ferr, 6168, "qhull precision error (qh_findhorizon): empty horizon\n\
+QhullPoint p%d was above all facets.\n", qh_pointid(qh, point));
+ qh_printfacetlist(qh, qh->facet_list, NULL, True);
+ qh_errexit(qh, qh_ERRprec, NULL, NULL);
+ }
+ trace1((qh, qh->ferr, 1041, "qh_findhorizon: %d horizon facets(good %d), %d visible(good %d), %d coplanar\n",
+ numhorizon, *goodhorizon, qh->num_visible, *goodvisible, coplanar));
+ if (qh->IStracing >= 4 && qh->num_facets < 50)
+ qh_printlists(qh);
+} /* findhorizon */
+
+/*-<a href="qh-qhull_r.htm#TOC"
+ >-------------------------------</a><a name="nextfurthest">-</a>
+
+ qh_nextfurthest(qh, visible )
+ returns next furthest point and visible facet for qh_addpoint()
+ starts search at qh.facet_next
+
+ returns:
+ removes furthest point from outside set
+ NULL if none available
+ advances qh.facet_next over facets with empty outside sets
+
+ design:
+ for each facet from qh.facet_next
+ if empty outside set
+ advance qh.facet_next
+ else if qh.NARROWhull
+ determine furthest outside point
+ if furthest point is not outside
+ advance qh.facet_next(point will be coplanar)
+ remove furthest point from outside set
+*/
+pointT *qh_nextfurthest(qhT *qh, facetT **visible) {
+ facetT *facet;
+ int size, idx;
+ realT randr, dist;
+ pointT *furthest;
+
+ while ((facet= qh->facet_next) != qh->facet_tail) {
+ if (!facet->outsideset) {
+ qh->facet_next= facet->next;
+ continue;
+ }
+ SETreturnsize_(facet->outsideset, size);
+ if (!size) {
+ qh_setfree(qh, &facet->outsideset);
+ qh->facet_next= facet->next;
+ continue;
+ }
+ if (qh->NARROWhull) {
+ if (facet->notfurthest)
+ qh_furthestout(qh, facet);
+ furthest= (pointT*)qh_setlast(facet->outsideset);
+#if qh_COMPUTEfurthest
+ qh_distplane(qh, furthest, facet, &dist);
+ zinc_(Zcomputefurthest);
+#else
+ dist= facet->furthestdist;
+#endif
+ if (dist < qh->MINoutside) { /* remainder of outside set is coplanar for qh_outcoplanar */
+ qh->facet_next= facet->next;
+ continue;
+ }
+ }
+ if (!qh->RANDOMoutside && !qh->VIRTUALmemory) {
+ if (qh->PICKfurthest) {
+ qh_furthestnext(qh /* qh->facet_list */);
+ facet= qh->facet_next;
+ }
+ *visible= facet;
+ return((pointT*)qh_setdellast(facet->outsideset));
+ }
+ if (qh->RANDOMoutside) {
+ int outcoplanar = 0;
+ if (qh->NARROWhull) {
+ FORALLfacets {
+ if (facet == qh->facet_next)
+ break;
+ if (facet->outsideset)
+ outcoplanar += qh_setsize(qh, facet->outsideset);
+ }
+ }
+ randr= qh_RANDOMint;
+ randr= randr/(qh_RANDOMmax+1);
+ idx= (int)floor((qh->num_outside - outcoplanar) * randr);
+ FORALLfacet_(qh->facet_next) {
+ if (facet->outsideset) {
+ SETreturnsize_(facet->outsideset, size);
+ if (!size)
+ qh_setfree(qh, &facet->outsideset);
+ else if (size > idx) {
+ *visible= facet;
+ return((pointT*)qh_setdelnth(qh, facet->outsideset, idx));
+ }else
+ idx -= size;
+ }
+ }
+ qh_fprintf(qh, qh->ferr, 6169, "qhull internal error (qh_nextfurthest): num_outside %d is too low\nby at least %d, or a random real %g >= 1.0\n",
+ qh->num_outside, idx+1, randr);
+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+ }else { /* VIRTUALmemory */
+ facet= qh->facet_tail->previous;
+ if (!(furthest= (pointT*)qh_setdellast(facet->outsideset))) {
+ if (facet->outsideset)
+ qh_setfree(qh, &facet->outsideset);
+ qh_removefacet(qh, facet);
+ qh_prependfacet(qh, facet, &qh->facet_list);
+ continue;
+ }
+ *visible= facet;
+ return furthest;
+ }
+ }
+ return NULL;
+} /* nextfurthest */
+
+/*-<a href="qh-qhull_r.htm#TOC"
+ >-------------------------------</a><a name="partitionall">-</a>
+
+ qh_partitionall(qh, vertices, points, numpoints )
+ partitions all points in points/numpoints to the outsidesets of facets
+ vertices= vertices in qh.facet_list(!partitioned)
+
+ returns:
+ builds facet->outsideset
+ does not partition qh.GOODpoint
+ if qh.ONLYgood && !qh.MERGING,
+ does not partition qh.GOODvertex
+
+ notes:
+ faster if qh.facet_list sorted by anticipated size of outside set
+
+ design:
+ initialize pointset with all points
+ remove vertices from pointset
+ remove qh.GOODpointp from pointset (unless it's qh.STOPcone or qh.STOPpoint)
+ for all facets
+ for all remaining points in pointset
+ compute distance from point to facet
+ if point is outside facet
+ remove point from pointset (by not reappending)
+ update bestpoint
+ append point or old bestpoint to facet's outside set
+ append bestpoint to facet's outside set (furthest)
+ for all points remaining in pointset
+ partition point into facets' outside sets and coplanar sets
+*/
+void qh_partitionall(qhT *qh, setT *vertices, pointT *points, int numpoints){
+ setT *pointset;
+ vertexT *vertex, **vertexp;
+ pointT *point, **pointp, *bestpoint;
+ int size, point_i, point_n, point_end, remaining, i, id;
+ facetT *facet;
+ realT bestdist= -REALmax, dist, distoutside;
+
+ trace1((qh, qh->ferr, 1042, "qh_partitionall: partition all points into outside sets\n"));
+ pointset= qh_settemp(qh, numpoints);
+ qh->num_outside= 0;
+ pointp= SETaddr_(pointset, pointT);
+ for (i=numpoints, point= points; i--; point += qh->hull_dim)
+ *(pointp++)= point;
+ qh_settruncate(qh, pointset, numpoints);
+ FOREACHvertex_(vertices) {
+ if ((id= qh_pointid(qh, vertex->point)) >= 0)
+ SETelem_(pointset, id)= NULL;
+ }
+ id= qh_pointid(qh, qh->GOODpointp);
+ if (id >=0 && qh->STOPcone-1 != id && -qh->STOPpoint-1 != id)
+ SETelem_(pointset, id)= NULL;
+ if (qh->GOODvertexp && qh->ONLYgood && !qh->MERGING) { /* matches qhull()*/
+ if ((id= qh_pointid(qh, qh->GOODvertexp)) >= 0)
+ SETelem_(pointset, id)= NULL;
+ }
+ if (!qh->BESToutside) { /* matches conditional for qh_partitionpoint below */
+ distoutside= qh_DISToutside; /* multiple of qh.MINoutside & qh.max_outside, see user.h */
+ zval_(Ztotpartition)= qh->num_points - qh->hull_dim - 1; /*misses GOOD... */
+ remaining= qh->num_facets;
+ point_end= numpoints;
+ FORALLfacets {
+ size= point_end/(remaining--) + 100;
+ facet->outsideset= qh_setnew(qh, size);
+ bestpoint= NULL;
+ point_end= 0;
+ FOREACHpoint_i_(qh, pointset) {
+ if (point) {
+ zzinc_(Zpartitionall);
+ qh_distplane(qh, point, facet, &dist);
+ if (dist < distoutside)
+ SETelem_(pointset, point_end++)= point;
+ else {
+ qh->num_outside++;
+ if (!bestpoint) {
+ bestpoint= point;
+ bestdist= dist;
+ }else if (dist > bestdist) {
+ qh_setappend(qh, &facet->outsideset, bestpoint);
+ bestpoint= point;
+ bestdist= dist;
+ }else
+ qh_setappend(qh, &facet->outsideset, point);
+ }
+ }
+ }
+ if (bestpoint) {
+ qh_setappend(qh, &facet->outsideset, bestpoint);
+#if !qh_COMPUTEfurthest
+ facet->furthestdist= bestdist;
+#endif
+ }else
+ qh_setfree(qh, &facet->outsideset);
+ qh_settruncate(qh, pointset, point_end);
+ }
+ }
+ /* if !qh->BESToutside, pointset contains points not assigned to outsideset */
+ if (qh->BESToutside || qh->MERGING || qh->KEEPcoplanar || qh->KEEPinside) {
+ qh->findbestnew= True;
+ FOREACHpoint_i_(qh, pointset) {
+ if (point)
+ qh_partitionpoint(qh, point, qh->facet_list);
+ }
+ qh->findbestnew= False;
+ }
+ zzadd_(Zpartitionall, zzval_(Zpartition));
+ zzval_(Zpartition)= 0;
+ qh_settempfree(qh, &pointset);
+ if (qh->IStracing >= 4)
+ qh_printfacetlist(qh, qh->facet_list, NULL, True);
+} /* partitionall */
+
+
+/*-<a href="qh-qhull_r.htm#TOC"
+ >-------------------------------</a><a name="partitioncoplanar">-</a>
+
+ qh_partitioncoplanar(qh, point, facet, dist )
+ partition coplanar point to a facet
+ dist is distance from point to facet
+ if dist NULL,
+ searches for bestfacet and does nothing if inside
+ if qh.findbestnew set,
+ searches new facets instead of using qh_findbest()
+
+ returns:
+ qh.max_ouside updated
+ if qh.KEEPcoplanar or qh.KEEPinside
+ point assigned to best coplanarset
+
+ notes:
+ facet->maxoutside is updated at end by qh_check_maxout
+
+ design:
+ if dist undefined
+ find best facet for point
+ if point sufficiently below facet (depends on qh.NEARinside and qh.KEEPinside)
+ exit
+ if keeping coplanar/nearinside/inside points
+ if point is above furthest coplanar point
+ append point to coplanar set (it is the new furthest)
+ update qh.max_outside
+ else
+ append point one before end of coplanar set
+ else if point is clearly outside of qh.max_outside and bestfacet->coplanarset
+ and bestfacet is more than perpendicular to facet
+ repartition the point using qh_findbest() -- it may be put on an outsideset
+ else
+ update qh.max_outside
+*/
+void qh_partitioncoplanar(qhT *qh, pointT *point, facetT *facet, realT *dist) {
+ facetT *bestfacet;
+ pointT *oldfurthest;
+ realT bestdist, dist2= 0, angle;
+ int numpart= 0, oldfindbest;
+ boolT isoutside;
+
+ qh->WAScoplanar= True;
+ if (!dist) {
+ if (qh->findbestnew)
+ bestfacet= qh_findbestnew(qh, point, facet, &bestdist, qh_ALL, &isoutside, &numpart);
+ else
+ bestfacet= qh_findbest(qh, point, facet, qh_ALL, !qh_ISnewfacets, qh->DELAUNAY,
+ &bestdist, &isoutside, &numpart);
+ zinc_(Ztotpartcoplanar);
+ zzadd_(Zpartcoplanar, numpart);
+ if (!qh->DELAUNAY && !qh->KEEPinside) { /* for 'd', bestdist skips upperDelaunay facets */
+ if (qh->KEEPnearinside) {
+ if (bestdist < -qh->NEARinside) {
+ zinc_(Zcoplanarinside);
+ trace4((qh, qh->ferr, 4062, "qh_partitioncoplanar: point p%d is more than near-inside facet f%d dist %2.2g findbestnew %d\n",
+ qh_pointid(qh, point), bestfacet->id, bestdist, qh->findbestnew));
+ return;
+ }
+ }else if (bestdist < -qh->MAXcoplanar) {
+ trace4((qh, qh->ferr, 4063, "qh_partitioncoplanar: point p%d is inside facet f%d dist %2.2g findbestnew %d\n",
+ qh_pointid(qh, point), bestfacet->id, bestdist, qh->findbestnew));
+ zinc_(Zcoplanarinside);
+ return;
+ }
+ }
+ }else {
+ bestfacet= facet;
+ bestdist= *dist;
+ }
+ if (bestdist > qh->max_outside) {
+ if (!dist && facet != bestfacet) {
+ zinc_(Zpartangle);
+ angle= qh_getangle(qh, facet->normal, bestfacet->normal);
+ if (angle < 0) {
+ /* typically due to deleted vertex and coplanar facets, e.g.,
+ RBOX 1000 s Z1 G1e-13 t1001185205 | QHULL Tv */
+ zinc_(Zpartflip);
+ trace2((qh, qh->ferr, 2058, "qh_partitioncoplanar: repartition point p%d from f%d. It is above flipped facet f%d dist %2.2g\n",
+ qh_pointid(qh, point), facet->id, bestfacet->id, bestdist));
+ oldfindbest= qh->findbestnew;
+ qh->findbestnew= False;
+ qh_partitionpoint(qh, point, bestfacet);
+ qh->findbestnew= oldfindbest;
+ return;
+ }
+ }
+ qh->max_outside= bestdist;
+ if (bestdist > qh->TRACEdist) {
+ qh_fprintf(qh, qh->ferr, 8122, "qh_partitioncoplanar: ====== p%d from f%d increases max_outside to %2.2g of f%d last p%d\n",
+ qh_pointid(qh, point), facet->id, bestdist, bestfacet->id, qh->furthest_id);
+ qh_errprint(qh, "DISTANT", facet, bestfacet, NULL, NULL);
+ }
+ }
+ if (qh->KEEPcoplanar + qh->KEEPinside + qh->KEEPnearinside) {
+ oldfurthest= (pointT*)qh_setlast(bestfacet->coplanarset);
+ if (oldfurthest) {
+ zinc_(Zcomputefurthest);
+ qh_distplane(qh, oldfurthest, bestfacet, &dist2);
+ }
+ if (!oldfurthest || dist2 < bestdist)
+ qh_setappend(qh, &bestfacet->coplanarset, point);
+ else
+ qh_setappend2ndlast(qh, &bestfacet->coplanarset, point);
+ }
+ trace4((qh, qh->ferr, 4064, "qh_partitioncoplanar: point p%d is coplanar with facet f%d(or inside) dist %2.2g\n",
+ qh_pointid(qh, point), bestfacet->id, bestdist));
+} /* partitioncoplanar */
+
+/*-<a href="qh-qhull_r.htm#TOC"
+ >-------------------------------</a><a name="partitionpoint">-</a>
+
+ qh_partitionpoint(qh, point, facet )
+ assigns point to an outside set, coplanar set, or inside set (i.e., dropt)
+ if qh.findbestnew
+ uses qh_findbestnew() to search all new facets
+ else
+ uses qh_findbest()
+
+ notes:
+ after qh_distplane(), this and qh_findbest() are most expensive in 3-d
+
+ design:
+ find best facet for point
+ (either exhaustive search of new facets or directed search from facet)
+ if qh.NARROWhull
+ retain coplanar and nearinside points as outside points
+ if point is outside bestfacet
+ if point above furthest point for bestfacet
+ append point to outside set (it becomes the new furthest)
+ if outside set was empty
+ move bestfacet to end of qh.facet_list (i.e., after qh.facet_next)
+ update bestfacet->furthestdist
+ else
+ append point one before end of outside set
+ else if point is coplanar to bestfacet
+ if keeping coplanar points or need to update qh.max_outside
+ partition coplanar point into bestfacet
+ else if near-inside point
+ partition as coplanar point into bestfacet
+ else is an inside point
+ if keeping inside points
+ partition as coplanar point into bestfacet
+*/
+void qh_partitionpoint(qhT *qh, pointT *point, facetT *facet) {
+ realT bestdist;
+ boolT isoutside;
+ facetT *bestfacet;
+ int numpart;
+#if qh_COMPUTEfurthest
+ realT dist;
+#endif
+
+ if (qh->findbestnew)
+ bestfacet= qh_findbestnew(qh, point, facet, &bestdist, qh->BESToutside, &isoutside, &numpart);
+ else
+ bestfacet= qh_findbest(qh, point, facet, qh->BESToutside, qh_ISnewfacets, !qh_NOupper,
+ &bestdist, &isoutside, &numpart);
+ zinc_(Ztotpartition);
+ zzadd_(Zpartition, numpart);
+ if (qh->NARROWhull) {
+ if (qh->DELAUNAY && !isoutside && bestdist >= -qh->MAXcoplanar)
+ qh_precision(qh, "nearly incident point(narrow hull)");
+ if (qh->KEEPnearinside) {
+ if (bestdist >= -qh->NEARinside)
+ isoutside= True;
+ }else if (bestdist >= -qh->MAXcoplanar)
+ isoutside= True;
+ }
+
+ if (isoutside) {
+ if (!bestfacet->outsideset
+ || !qh_setlast(bestfacet->outsideset)) {
+ qh_setappend(qh, &(bestfacet->outsideset), point);
+ if (!bestfacet->newfacet) {
+ qh_removefacet(qh, bestfacet); /* make sure it's after qh->facet_next */
+ qh_appendfacet(qh, bestfacet);
+ }
+#if !qh_COMPUTEfurthest
+ bestfacet->furthestdist= bestdist;
+#endif
+ }else {
+#if qh_COMPUTEfurthest
+ zinc_(Zcomputefurthest);
+ qh_distplane(qh, oldfurthest, bestfacet, &dist);
+ if (dist < bestdist)
+ qh_setappend(qh, &(bestfacet->outsideset), point);
+ else
+ qh_setappend2ndlast(qh, &(bestfacet->outsideset), point);
+#else
+ if (bestfacet->furthestdist < bestdist) {
+ qh_setappend(qh, &(bestfacet->outsideset), point);
+ bestfacet->furthestdist= bestdist;
+ }else
+ qh_setappend2ndlast(qh, &(bestfacet->outsideset), point);
+#endif
+ }
+ qh->num_outside++;
+ trace4((qh, qh->ferr, 4065, "qh_partitionpoint: point p%d is outside facet f%d new? %d (or narrowhull)\n",
+ qh_pointid(qh, point), bestfacet->id, bestfacet->newfacet));
+ }else if (qh->DELAUNAY || bestdist >= -qh->MAXcoplanar) { /* for 'd', bestdist skips upperDelaunay facets */
+ zzinc_(Zcoplanarpart);
+ if (qh->DELAUNAY)
+ qh_precision(qh, "nearly incident point");
+ if ((qh->KEEPcoplanar + qh->KEEPnearinside) || bestdist > qh->max_outside)
+ qh_partitioncoplanar(qh, point, bestfacet, &bestdist);
+ else {
+ trace4((qh, qh->ferr, 4066, "qh_partitionpoint: point p%d is coplanar to facet f%d (dropped)\n",
+ qh_pointid(qh, point), bestfacet->id));
+ }
+ }else if (qh->KEEPnearinside && bestdist > -qh->NEARinside) {
+ zinc_(Zpartnear);
+ qh_partitioncoplanar(qh, point, bestfacet, &bestdist);
+ }else {
+ zinc_(Zpartinside);
+ trace4((qh, qh->ferr, 4067, "qh_partitionpoint: point p%d is inside all facets, closest to f%d dist %2.2g\n",
+ qh_pointid(qh, point), bestfacet->id, bestdist));
+ if (qh->KEEPinside)
+ qh_partitioncoplanar(qh, point, bestfacet, &bestdist);
+ }
+} /* partitionpoint */
+
+/*-<a href="qh-qhull_r.htm#TOC"
+ >-------------------------------</a><a name="partitionvisible">-</a>
+
+ qh_partitionvisible(qh, allpoints, numoutside )
+ partitions points in visible facets to qh.newfacet_list
+ qh.visible_list= visible facets
+ for visible facets
+ 1st neighbor (if any) points to a horizon facet or a new facet
+ if allpoints(!used),
+ repartitions coplanar points
+
+ returns:
+ updates outside sets and coplanar sets of qh.newfacet_list
+ updates qh.num_outside (count of outside points)
+
+ notes:
+ qh.findbest_notsharp should be clear (extra work if set)
+
+ design:
+ for all visible facets with outside set or coplanar set
+ select a newfacet for visible facet
+ if outside set
+ partition outside set into new facets
+ if coplanar set and keeping coplanar/near-inside/inside points
+ if allpoints
+ partition coplanar set into new facets, may be assigned outside
+ else
+ partition coplanar set into coplanar sets of new facets
+ for each deleted vertex
+ if allpoints
+ partition vertex into new facets, may be assigned outside
+ else
+ partition vertex into coplanar sets of new facets
+*/
+void qh_partitionvisible(qhT *qh /*qh.visible_list*/, boolT allpoints, int *numoutside) {
+ facetT *visible, *newfacet;
+ pointT *point, **pointp;
+ int coplanar=0, size;
+ unsigned count;
+ vertexT *vertex, **vertexp;
+
+ if (qh->ONLYmax)
+ maximize_(qh->MINoutside, qh->max_vertex);
+ *numoutside= 0;
+ FORALLvisible_facets {
+ if (!visible->outsideset && !visible->coplanarset)
+ continue;
+ newfacet= visible->f.replace;
+ count= 0;
+ while (newfacet && newfacet->visible) {
+ newfacet= newfacet->f.replace;
+ if (count++ > qh->facet_id)
+ qh_infiniteloop(qh, visible);
+ }
+ if (!newfacet)
+ newfacet= qh->newfacet_list;
+ if (newfacet == qh->facet_tail) {
+ qh_fprintf(qh, qh->ferr, 6170, "qhull precision error (qh_partitionvisible): all new facets deleted as\n degenerate facets. Can not continue.\n");
+ qh_errexit(qh, qh_ERRprec, NULL, NULL);
+ }
+ if (visible->outsideset) {
+ size= qh_setsize(qh, visible->outsideset);
+ *numoutside += size;
+ qh->num_outside -= size;
+ FOREACHpoint_(visible->outsideset)
+ qh_partitionpoint(qh, point, newfacet);
+ }
+ if (visible->coplanarset && (qh->KEEPcoplanar + qh->KEEPinside + qh->KEEPnearinside)) {
+ size= qh_setsize(qh, visible->coplanarset);
+ coplanar += size;
+ FOREACHpoint_(visible->coplanarset) {
+ if (allpoints) /* not used */
+ qh_partitionpoint(qh, point, newfacet);
+ else
+ qh_partitioncoplanar(qh, point, newfacet, NULL);
+ }
+ }
+ }
+ FOREACHvertex_(qh->del_vertices) {
+ if (vertex->point) {
+ if (allpoints) /* not used */
+ qh_partitionpoint(qh, vertex->point, qh->newfacet_list);
+ else
+ qh_partitioncoplanar(qh, vertex->point, qh->newfacet_list, NULL);
+ }
+ }
+ trace1((qh, qh->ferr, 1043,"qh_partitionvisible: partitioned %d points from outsidesets and %d points from coplanarsets\n", *numoutside, coplanar));
+} /* partitionvisible */
+
+
+
+/*-<a href="qh-qhull_r.htm#TOC"
+ >-------------------------------</a><a name="precision">-</a>
+
+ qh_precision(qh, reason )
+ restart on precision errors if not merging and if 'QJn'
+*/
+void qh_precision(qhT *qh, const char *reason) {
+
+ if (qh->ALLOWrestart && !qh->PREmerge && !qh->MERGEexact) {
+ if (qh->JOGGLEmax < REALmax/2) {
+ trace0((qh, qh->ferr, 26, "qh_precision: qhull restart because of %s\n", reason));
+ /* May be called repeatedly if qh->ALLOWrestart */
+ longjmp(qh->restartexit, qh_ERRprec);
+ }
+ }
+} /* qh_precision */
+
+/*-<a href="qh-qhull_r.htm#TOC"
+ >-------------------------------</a><a name="printsummary">-</a>
+
+ qh_printsummary(qh, fp )
+ prints summary to fp
+
+ notes:
+ not in io_r.c so that user_eg.c can prevent io_r.c from loading
+ qh_printsummary and qh_countfacets must match counts
+
+ design:
+ determine number of points, vertices, and coplanar points
+ print summary
+*/
+void qh_printsummary(qhT *qh, FILE *fp) {
+ realT ratio, outerplane, innerplane;
+ float cpu;
+ int size, id, nummerged, numvertices, numcoplanars= 0, nonsimplicial=0;
+ int goodused;
+ facetT *facet;
+ const char *s;
+ int numdel= zzval_(Zdelvertextot);
+ int numtricoplanars= 0;
+
+ size= qh->num_points + qh_setsize(qh, qh->other_points);
+ numvertices= qh->num_vertices - qh_setsize(qh, qh->del_vertices);
+ id= qh_pointid(qh, qh->GOODpointp);
+ FORALLfacets {
+ if (facet->coplanarset)
+ numcoplanars += qh_setsize(qh, facet->coplanarset);
+ if (facet->good) {
+ if (facet->simplicial) {
+ if (facet->keepcentrum && facet->tricoplanar)
+ numtricoplanars++;
+ }else if (qh_setsize(qh, facet->vertices) != qh->hull_dim)
+ nonsimplicial++;
+ }
+ }
+ if (id >=0 && qh->STOPcone-1 != id && -qh->STOPpoint-1 != id)
+ size--;
+ if (qh->STOPcone || qh->STOPpoint)
+ qh_fprintf(qh, fp, 9288, "\nAt a premature exit due to 'TVn', 'TCn', 'TRn', or precision error with 'QJn'.");
+ if (qh->UPPERdelaunay)
+ goodused= qh->GOODvertex + qh->GOODpoint + qh->SPLITthresholds;
+ else if (qh->DELAUNAY)
+ goodused= qh->GOODvertex + qh->GOODpoint + qh->GOODthreshold;
+ else
+ goodused= qh->num_good;
+ nummerged= zzval_(Ztotmerge) - zzval_(Zcyclehorizon) + zzval_(Zcyclefacettot);
+ if (qh->VORONOI) {
+ if (qh->UPPERdelaunay)
+ qh_fprintf(qh, fp, 9289, "\n\
+Furthest-site Voronoi vertices by the convex hull of %d points in %d-d:\n\n", size, qh->hull_dim);
+ else
+ qh_fprintf(qh, fp, 9290, "\n\
+Voronoi diagram by the convex hull of %d points in %d-d:\n\n", size, qh->hull_dim);
+ qh_fprintf(qh, fp, 9291, " Number of Voronoi regions%s: %d\n",
+ qh->ATinfinity ? " and at-infinity" : "", numvertices);
+ if (numdel)
+ qh_fprintf(qh, fp, 9292, " Total number of deleted points due to merging: %d\n", numdel);
+ if (numcoplanars - numdel > 0)
+ qh_fprintf(qh, fp, 9293, " Number of nearly incident points: %d\n", numcoplanars - numdel);
+ else if (size - numvertices - numdel > 0)
+ qh_fprintf(qh, fp, 9294, " Total number of nearly incident points: %d\n", size - numvertices - numdel);
+ qh_fprintf(qh, fp, 9295, " Number of%s Voronoi vertices: %d\n",
+ goodused ? " 'good'" : "", qh->num_good);
+ if (nonsimplicial)
+ qh_fprintf(qh, fp, 9296, " Number of%s non-simplicial Voronoi vertices: %d\n",
+ goodused ? " 'good'" : "", nonsimplicial);
+ }else if (qh->DELAUNAY) {
+ if (qh->UPPERdelaunay)
+ qh_fprintf(qh, fp, 9297, "\n\
+Furthest-site Delaunay triangulation by the convex hull of %d points in %d-d:\n\n", size, qh->hull_dim);
+ else
+ qh_fprintf(qh, fp, 9298, "\n\
+Delaunay triangulation by the convex hull of %d points in %d-d:\n\n", size, qh->hull_dim);
+ qh_fprintf(qh, fp, 9299, " Number of input sites%s: %d\n",
+ qh->ATinfinity ? " and at-infinity" : "", numvertices);
+ if (numdel)
+ qh_fprintf(qh, fp, 9300, " Total number of deleted points due to merging: %d\n", numdel);
+ if (numcoplanars - numdel > 0)
+ qh_fprintf(qh, fp, 9301, " Number of nearly incident points: %d\n", numcoplanars - numdel);
+ else if (size - numvertices - numdel > 0)
+ qh_fprintf(qh, fp, 9302, " Total number of nearly incident points: %d\n", size - numvertices - numdel);
+ qh_fprintf(qh, fp, 9303, " Number of%s Delaunay regions: %d\n",
+ goodused ? " 'good'" : "", qh->num_good);
+ if (nonsimplicial)
+ qh_fprintf(qh, fp, 9304, " Number of%s non-simplicial Delaunay regions: %d\n",
+ goodused ? " 'good'" : "", nonsimplicial);
+ }else if (qh->HALFspace) {
+ qh_fprintf(qh, fp, 9305, "\n\
+Halfspace intersection by the convex hull of %d points in %d-d:\n\n", size, qh->hull_dim);
+ qh_fprintf(qh, fp, 9306, " Number of halfspaces: %d\n", size);
+ qh_fprintf(qh, fp, 9307, " Number of non-redundant halfspaces: %d\n", numvertices);
+ if (numcoplanars) {
+ if (qh->KEEPinside && qh->KEEPcoplanar)
+ s= "similar and redundant";
+ else if (qh->KEEPinside)
+ s= "redundant";
+ else
+ s= "similar";
+ qh_fprintf(qh, fp, 9308, " Number of %s halfspaces: %d\n", s, numcoplanars);
+ }
+ qh_fprintf(qh, fp, 9309, " Number of intersection points: %d\n", qh->num_facets - qh->num_visible);
+ if (goodused)
+ qh_fprintf(qh, fp, 9310, " Number of 'good' intersection points: %d\n", qh->num_good);
+ if (nonsimplicial)
+ qh_fprintf(qh, fp, 9311, " Number of%s non-simplicial intersection points: %d\n",
+ goodused ? " 'good'" : "", nonsimplicial);
+ }else {
+ qh_fprintf(qh, fp, 9312, "\n\
+Convex hull of %d points in %d-d:\n\n", size, qh->hull_dim);
+ qh_fprintf(qh, fp, 9313, " Number of vertices: %d\n", numvertices);
+ if (numcoplanars) {
+ if (qh->KEEPinside && qh->KEEPcoplanar)
+ s= "coplanar and interior";
+ else if (qh->KEEPinside)
+ s= "interior";
+ else
+ s= "coplanar";
+ qh_fprintf(qh, fp, 9314, " Number of %s points: %d\n", s, numcoplanars);
+ }
+ qh_fprintf(qh, fp, 9315, " Number of facets: %d\n", qh->num_facets - qh->num_visible);
+ if (goodused)
+ qh_fprintf(qh, fp, 9316, " Number of 'good' facets: %d\n", qh->num_good);
+ if (nonsimplicial)
+ qh_fprintf(qh, fp, 9317, " Number of%s non-simplicial facets: %d\n",
+ goodused ? " 'good'" : "", nonsimplicial);
+ }
+ if (numtricoplanars)
+ qh_fprintf(qh, fp, 9318, " Number of triangulated facets: %d\n", numtricoplanars);
+ qh_fprintf(qh, fp, 9319, "\nStatistics for: %s | %s",
+ qh->rbox_command, qh->qhull_command);
+ if (qh->ROTATErandom != INT_MIN)
+ qh_fprintf(qh, fp, 9320, " QR%d\n\n", qh->ROTATErandom);
+ else
+ qh_fprintf(qh, fp, 9321, "\n\n");
+ qh_fprintf(qh, fp, 9322, " Number of points processed: %d\n", zzval_(Zprocessed));
+ qh_fprintf(qh, fp, 9323, " Number of hyperplanes created: %d\n", zzval_(Zsetplane));
+ if (qh->DELAUNAY)
+ qh_fprintf(qh, fp, 9324, " Number of facets in hull: %d\n", qh->num_facets - qh->num_visible);
+ qh_fprintf(qh, fp, 9325, " Number of distance tests for qhull: %d\n", zzval_(Zpartition)+
+ zzval_(Zpartitionall)+zzval_(Znumvisibility)+zzval_(Zpartcoplanar));
+#if 0 /* NOTE: must print before printstatistics() */
+ {realT stddev, ave;
+ qh_fprintf(qh, fp, 9326, " average new facet balance: %2.2g\n",
+ wval_(Wnewbalance)/zval_(Zprocessed));
+ stddev= qh_stddev(zval_(Zprocessed), wval_(Wnewbalance),
+ wval_(Wnewbalance2), &ave);
+ qh_fprintf(qh, fp, 9327, " new facet standard deviation: %2.2g\n", stddev);
+ qh_fprintf(qh, fp, 9328, " average partition balance: %2.2g\n",
+ wval_(Wpbalance)/zval_(Zpbalance));
+ stddev= qh_stddev(zval_(Zpbalance), wval_(Wpbalance),
+ wval_(Wpbalance2), &ave);
+ qh_fprintf(qh, fp, 9329, " partition standard deviation: %2.2g\n", stddev);
+ }
+#endif
+ if (nummerged) {
+ qh_fprintf(qh, fp, 9330," Number of distance tests for merging: %d\n",zzval_(Zbestdist)+
+ zzval_(Zcentrumtests)+zzval_(Zdistconvex)+zzval_(Zdistcheck)+
+ zzval_(Zdistzero));
+ qh_fprintf(qh, fp, 9331," Number of distance tests for checking: %d\n",zzval_(Zcheckpart));
+ qh_fprintf(qh, fp, 9332," Number of merged facets: %d\n", nummerged);
+ }
+ if (!qh->RANDOMoutside && qh->QHULLfinished) {
+ cpu= (float)qh->hulltime;
+ cpu /= (float)qh_SECticks;
+ wval_(Wcpu)= cpu;
+ qh_fprintf(qh, fp, 9333, " CPU seconds to compute hull (after input): %2.4g\n", cpu);
+ }
+ if (qh->RERUN) {
+ if (!qh->PREmerge && !qh->MERGEexact)
+ qh_fprintf(qh, fp, 9334, " Percentage of runs with precision errors: %4.1f\n",
+ zzval_(Zretry)*100.0/qh->build_cnt); /* careful of order */
+ }else if (qh->JOGGLEmax < REALmax/2) {
+ if (zzval_(Zretry))
+ qh_fprintf(qh, fp, 9335, " After %d retries, input joggled by: %2.2g\n",
+ zzval_(Zretry), qh->JOGGLEmax);
+ else
+ qh_fprintf(qh, fp, 9336, " Input joggled by: %2.2g\n", qh->JOGGLEmax);
+ }
+ if (qh->totarea != 0.0)
+ qh_fprintf(qh, fp, 9337, " %s facet area: %2.8g\n",
+ zzval_(Ztotmerge) ? "Approximate" : "Total", qh->totarea);
+ if (qh->totvol != 0.0)
+ qh_fprintf(qh, fp, 9338, " %s volume: %2.8g\n",
+ zzval_(Ztotmerge) ? "Approximate" : "Total", qh->totvol);
+ if (qh->MERGING) {
+ qh_outerinner(qh, NULL, &outerplane, &innerplane);
+ if (outerplane > 2 * qh->DISTround) {
+ qh_fprintf(qh, fp, 9339, " Maximum distance of %spoint above facet: %2.2g",
+ (qh->QHULLfinished ? "" : "merged "), outerplane);
+ ratio= outerplane/(qh->ONEmerge + qh->DISTround);
+ /* don't report ratio if MINoutside is large */
+ if (ratio > 0.05 && 2* qh->ONEmerge > qh->MINoutside && qh->JOGGLEmax > REALmax/2)
+ qh_fprintf(qh, fp, 9340, " (%.1fx)\n", ratio);
+ else
+ qh_fprintf(qh, fp, 9341, "\n");
+ }
+ if (innerplane < -2 * qh->DISTround) {
+ qh_fprintf(qh, fp, 9342, " Maximum distance of %svertex below facet: %2.2g",
+ (qh->QHULLfinished ? "" : "merged "), innerplane);
+ ratio= -innerplane/(qh->ONEmerge+qh->DISTround);
+ if (ratio > 0.05 && qh->JOGGLEmax > REALmax/2)
+ qh_fprintf(qh, fp, 9343, " (%.1fx)\n", ratio);
+ else
+ qh_fprintf(qh, fp, 9344, "\n");
+ }
+ }
+ qh_fprintf(qh, fp, 9345, "\n");
+} /* printsummary */
+
+
diff --git a/xs/src/qhull/src/libqhull_r/libqhull_r.h b/xs/src/qhull/src/libqhull_r/libqhull_r.h
new file mode 100644
index 000000000..363e6da6a
--- /dev/null
+++ b/xs/src/qhull/src/libqhull_r/libqhull_r.h
@@ -0,0 +1,1134 @@
+/*<html><pre> -<a href="qh-qhull_r.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ libqhull_r.h
+ user-level header file for using qhull.a library
+
+ see qh-qhull_r.htm, qhull_ra.h
+
+ Copyright (c) 1993-2015 The Geometry Center.
+ $Id: //main/2015/qhull/src/libqhull_r/libqhull_r.h#8 $$Change: 2079 $
+ $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
+
+ includes function prototypes for libqhull_r.c, geom_r.c, global_r.c, io_r.c, user.c
+
+ use mem_r.h for mem_r.c
+ use qset_r.h for qset_r.c
+
+ see unix_r.c for an example of using libqhull_r.h
+
+ recompile qhull if you change this file
+*/
+
+#ifndef qhDEFlibqhull
+#define qhDEFlibqhull 1
+
+/*=========================== -included files ==============*/
+
+/* user_r.h first for QHULL_CRTDBG */
+#include "user_r.h" /* user definable constants (e.g., realT). */
+
+#include "mem_r.h" /* Needed for qhT in libqhull_r.h */
+#include "qset_r.h" /* Needed for QHULL_LIB_CHECK */
+/* include stat_r.h after defining boolT. statT needed for qhT in libqhull_r.h */
+
+#include <setjmp.h>
+#include <float.h>
+#include <time.h>
+#include <stdio.h>
+
+#ifndef __STDC__
+#ifndef __cplusplus
+#if !_MSC_VER
+#error Neither __STDC__ nor __cplusplus is defined. Please use strict ANSI C or C++ to compile
+#error Qhull. You may need to turn off compiler extensions in your project configuration. If
+#error your compiler is a standard C compiler, you can delete this warning from libqhull_r.h
+#endif
+#endif
+#endif
+
+/*============ constants and basic types ====================*/
+
+extern const char qh_version[]; /* defined in global_r.c */
+extern const char qh_version2[]; /* defined in global_r.c */
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >--------------------------------</a><a name="coordT">-</a>
+
+ coordT
+ coordinates and coefficients are stored as realT (i.e., double)
+
+ notes:
+ Qhull works well if realT is 'float'. If so joggle (QJ) is not effective.
+
+ Could use 'float' for data and 'double' for calculations (realT vs. coordT)
+ This requires many type casts, and adjusted error bounds.
+ Also C compilers may do expressions in double anyway.
+*/
+#define coordT realT
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >--------------------------------</a><a name="pointT">-</a>
+
+ pointT
+ a point is an array of coordinates, usually qh.hull_dim
+ qh_pointid returns
+ qh_IDnone if point==0 or qh is undefined
+ qh_IDinterior for qh.interior_point
+ qh_IDunknown if point is neither in qh.first_point... nor qh.other_points
+
+ notes:
+ qh.STOPcone and qh.STOPpoint assume that qh_IDunknown==-1 (other negative numbers indicate points)
+ qh_IDunknown is also returned by getid_() for unknown facet, ridge, or vertex
+*/
+#define pointT coordT
+typedef enum
+{
+ qh_IDnone = -3, qh_IDinterior = -2, qh_IDunknown = -1
+}
+qh_pointT;
+
+/*-<a href="qh-qhull_r.htm#TOC"
+ >--------------------------------</a><a name="flagT">-</a>
+
+ flagT
+ Boolean flag as a bit
+*/
+#define flagT unsigned int
+
+/*-<a href="qh-qhull_r.htm#TOC"
+ >--------------------------------</a><a name="boolT">-</a>
+
+ boolT
+ boolean value, either True or False
+
+ notes:
+ needed for portability
+ Use qh_False/qh_True as synonyms
+*/
+#define boolT unsigned int
+#ifdef False
+#undef False
+#endif
+#ifdef True
+#undef True
+#endif
+#define False 0
+#define True 1
+#define qh_False 0
+#define qh_True 1
+
+#include "stat_r.h" /* needs boolT */
+
+/*-<a href="qh-qhull_r.htm#TOC"
+ >--------------------------------</a><a name="CENTERtype">-</a>
+
+ qh_CENTER
+ to distinguish facet->center
+*/
+typedef enum
+{
+ qh_ASnone = 0, /* If not MERGING and not VORONOI */
+ qh_ASvoronoi, /* Set by qh_clearcenters on qh_prepare_output, or if not MERGING and VORONOI */
+ qh_AScentrum /* If MERGING (assumed during merging) */
+}
+qh_CENTER;
+
+/*-<a href="qh-qhull_r.htm#TOC"
+ >--------------------------------</a><a name="qh_PRINT">-</a>
+
+ qh_PRINT
+ output formats for printing (qh.PRINTout).
+ 'Fa' 'FV' 'Fc' 'FC'
+
+
+ notes:
+ some of these names are similar to qhT names. The similar names are only
+ used in switch statements in qh_printbegin() etc.
+*/
+typedef enum {qh_PRINTnone= 0,
+ qh_PRINTarea, qh_PRINTaverage, /* 'Fa' 'FV' 'Fc' 'FC' */
+ qh_PRINTcoplanars, qh_PRINTcentrums,
+ qh_PRINTfacets, qh_PRINTfacets_xridge, /* 'f' 'FF' 'G' 'FI' 'Fi' 'Fn' */
+ qh_PRINTgeom, qh_PRINTids, qh_PRINTinner, qh_PRINTneighbors,
+ qh_PRINTnormals, qh_PRINTouter, qh_PRINTmaple, /* 'n' 'Fo' 'i' 'm' 'Fm' 'FM', 'o' */
+ qh_PRINTincidences, qh_PRINTmathematica, qh_PRINTmerges, qh_PRINToff,
+ qh_PRINToptions, qh_PRINTpointintersect, /* 'FO' 'Fp' 'FP' 'p' 'FQ' 'FS' */
+ qh_PRINTpointnearest, qh_PRINTpoints, qh_PRINTqhull, qh_PRINTsize,
+ qh_PRINTsummary, qh_PRINTtriangles, /* 'Fs' 'Ft' 'Fv' 'FN' 'Fx' */
+ qh_PRINTvertices, qh_PRINTvneighbors, qh_PRINTextremes,
+ qh_PRINTEND} qh_PRINT;
+
+/*-<a href="qh-qhull_r.htm#TOC"
+ >--------------------------------</a><a name="qh_ALL">-</a>
+
+ qh_ALL
+ argument flag for selecting everything
+*/
+#define qh_ALL True
+#define qh_NOupper True /* argument for qh_findbest */
+#define qh_IScheckmax True /* argument for qh_findbesthorizon */
+#define qh_ISnewfacets True /* argument for qh_findbest */
+#define qh_RESETvisible True /* argument for qh_resetlists */
+
+/*-<a href="qh-qhull_r.htm#TOC"
+ >--------------------------------</a><a name="qh_ERR">-</a>
+
+ qh_ERR
+ Qhull exit codes, for indicating errors
+ See: MSG_ERROR and MSG_WARNING [user.h]
+*/
+#define qh_ERRnone 0 /* no error occurred during qhull */
+#define qh_ERRinput 1 /* input inconsistency */
+#define qh_ERRsingular 2 /* singular input data */
+#define qh_ERRprec 3 /* precision error */
+#define qh_ERRmem 4 /* insufficient memory, matches mem_r.h */
+#define qh_ERRqhull 5 /* internal error detected, matches mem_r.h */
+
+/*-<a href="qh-qhull_r.htm#TOC"
+>--------------------------------</a><a name="qh_FILEstderr">-</a>
+
+qh_FILEstderr
+Fake stderr to distinguish error output from normal output
+For C++ interface. Must redefine qh_fprintf_qhull
+*/
+#define qh_FILEstderr ((FILE*)1)
+
+/* ============ -structures- ====================
+ each of the following structures is defined by a typedef
+ all realT and coordT fields occur at the beginning of a structure
+ (otherwise space may be wasted due to alignment)
+ define all flags together and pack into 32-bit number
+
+ DEFqhT and DEFsetT are likewise defined in
+ mem_r.h, qset_r.h, and stat_r.h.
+
+*/
+
+typedef struct vertexT vertexT;
+typedef struct ridgeT ridgeT;
+typedef struct facetT facetT;
+
+#ifndef DEFqhT
+#define DEFqhT 1
+typedef struct qhT qhT; /* defined below */
+#endif
+
+#ifndef DEFsetT
+#define DEFsetT 1
+typedef struct setT setT; /* defined in qset_r.h */
+#endif
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >--------------------------------</a><a name="facetT">-</a>
+
+ facetT
+ defines a facet
+
+ notes:
+ qhull() generates the hull as a list of facets.
+
+ topological information:
+ f.previous,next doubly-linked list of facets
+ f.vertices set of vertices
+ f.ridges set of ridges
+ f.neighbors set of neighbors
+ f.toporient True if facet has top-orientation (else bottom)
+
+ geometric information:
+ f.offset,normal hyperplane equation
+ f.maxoutside offset to outer plane -- all points inside
+ f.center centrum for testing convexity or Voronoi center for output
+ f.simplicial True if facet is simplicial
+ f.flipped True if facet does not include qh.interior_point
+
+ for constructing hull:
+ f.visible True if facet on list of visible facets (will be deleted)
+ f.newfacet True if facet on list of newly created facets
+ f.coplanarset set of points coplanar with this facet
+ (includes near-inside points for later testing)
+ f.outsideset set of points outside of this facet
+ f.furthestdist distance to furthest point of outside set
+ f.visitid marks visited facets during a loop
+ f.replace replacement facet for to-be-deleted, visible facets
+ f.samecycle,newcycle cycle of facets for merging into horizon facet
+
+ see below for other flags and fields
+*/
+struct facetT {
+#if !qh_COMPUTEfurthest
+ coordT furthestdist;/* distance to furthest point of outsideset */
+#endif
+#if qh_MAXoutside
+ coordT maxoutside; /* max computed distance of point to facet
+ Before QHULLfinished this is an approximation
+ since maxdist not always set for mergefacet
+ Actual outer plane is +DISTround and
+ computed outer plane is +2*DISTround */
+#endif
+ coordT offset; /* exact offset of hyperplane from origin */
+ coordT *normal; /* normal of hyperplane, hull_dim coefficients */
+ /* if ->tricoplanar, shared with a neighbor */
+ union { /* in order of testing */
+ realT area; /* area of facet, only in io_r.c if ->isarea */
+ facetT *replace; /* replacement facet if ->visible and NEWfacets
+ is NULL only if qh_mergedegen_redundant or interior */
+ facetT *samecycle; /* cycle of facets from the same visible/horizon intersection,
+ if ->newfacet */
+ facetT *newcycle; /* in horizon facet, current samecycle of new facets */
+ facetT *trivisible; /* visible facet for ->tricoplanar facets during qh_triangulate() */
+ facetT *triowner; /* owner facet for ->tricoplanar, !isarea facets w/ ->keepcentrum */
+ }f;
+ coordT *center; /* set according to qh.CENTERtype */
+ /* qh_ASnone: no center (not MERGING) */
+ /* qh_AScentrum: centrum for testing convexity (qh_getcentrum) */
+ /* assumed qh_AScentrum while merging */
+ /* qh_ASvoronoi: Voronoi center (qh_facetcenter) */
+ /* after constructing the hull, it may be changed (qh_clearcenter) */
+ /* if tricoplanar and !keepcentrum, shared with a neighbor */
+ facetT *previous; /* previous facet in the facet_list */
+ facetT *next; /* next facet in the facet_list */
+ setT *vertices; /* vertices for this facet, inverse sorted by ID
+ if simplicial, 1st vertex was apex/furthest */
+ setT *ridges; /* explicit ridges for nonsimplicial facets.
+ for simplicial facets, neighbors define the ridges */
+ setT *neighbors; /* neighbors of the facet. If simplicial, the kth
+ neighbor is opposite the kth vertex, and the first
+ neighbor is the horizon facet for the first vertex*/
+ setT *outsideset; /* set of points outside this facet
+ if non-empty, last point is furthest
+ if NARROWhull, includes coplanars for partitioning*/
+ setT *coplanarset; /* set of points coplanar with this facet
+ > qh.min_vertex and <= facet->max_outside
+ a point is assigned to the furthest facet
+ if non-empty, last point is furthest away */
+ unsigned visitid; /* visit_id, for visiting all neighbors,
+ all uses are independent */
+ unsigned id; /* unique identifier from qh.facet_id */
+ unsigned nummerge:9; /* number of merges */
+#define qh_MAXnummerge 511 /* 2^9-1, 32 flags total, see "flags:" in io_r.c */
+ flagT tricoplanar:1; /* True if TRIangulate and simplicial and coplanar with a neighbor */
+ /* all tricoplanars share the same apex */
+ /* all tricoplanars share the same ->center, ->normal, ->offset, ->maxoutside */
+ /* ->keepcentrum is true for the owner. It has the ->coplanareset */
+ /* if ->degenerate, does not span facet (one logical ridge) */
+ /* during qh_triangulate, f.trivisible points to original facet */
+ flagT newfacet:1; /* True if facet on qh.newfacet_list (new or merged) */
+ flagT visible:1; /* True if visible facet (will be deleted) */
+ flagT toporient:1; /* True if created with top orientation
+ after merging, use ridge orientation */
+ flagT simplicial:1;/* True if simplicial facet, ->ridges may be implicit */
+ flagT seen:1; /* used to perform operations only once, like visitid */
+ flagT seen2:1; /* used to perform operations only once, like visitid */
+ flagT flipped:1; /* True if facet is flipped */
+ flagT upperdelaunay:1; /* True if facet is upper envelope of Delaunay triangulation */
+ flagT notfurthest:1; /* True if last point of outsideset is not furthest*/
+
+/*-------- flags primarily for output ---------*/
+ flagT good:1; /* True if a facet marked good for output */
+ flagT isarea:1; /* True if facet->f.area is defined */
+
+/*-------- flags for merging ------------------*/
+ flagT dupridge:1; /* True if duplicate ridge in facet */
+ flagT mergeridge:1; /* True if facet or neighbor contains a qh_MERGEridge
+ ->normal defined (also defined for mergeridge2) */
+ flagT mergeridge2:1; /* True if neighbor contains a qh_MERGEridge (qhT *qh, mark_dupridges */
+ flagT coplanar:1; /* True if horizon facet is coplanar at last use */
+ flagT mergehorizon:1; /* True if will merge into horizon (->coplanar) */
+ flagT cycledone:1;/* True if mergecycle_all already done */
+ flagT tested:1; /* True if facet convexity has been tested (false after merge */
+ flagT keepcentrum:1; /* True if keep old centrum after a merge, or marks owner for ->tricoplanar */
+ flagT newmerge:1; /* True if facet is newly merged for reducevertices */
+ flagT degenerate:1; /* True if facet is degenerate (degen_mergeset or ->tricoplanar) */
+ flagT redundant:1; /* True if facet is redundant (degen_mergeset) */
+};
+
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >--------------------------------</a><a name="ridgeT">-</a>
+
+ ridgeT
+ defines a ridge
+
+ notes:
+ a ridge is hull_dim-1 simplex between two neighboring facets. If the
+ facets are non-simplicial, there may be more than one ridge between
+ two facets. E.G. a 4-d hypercube has two triangles between each pair
+ of neighboring facets.
+
+ topological information:
+ vertices a set of vertices
+ top,bottom neighboring facets with orientation
+
+ geometric information:
+ tested True if ridge is clearly convex
+ nonconvex True if ridge is non-convex
+*/
+struct ridgeT {
+ setT *vertices; /* vertices belonging to this ridge, inverse sorted by ID
+ NULL if a degen ridge (matchsame) */
+ facetT *top; /* top facet this ridge is part of */
+ facetT *bottom; /* bottom facet this ridge is part of */
+ unsigned id; /* unique identifier. Same size as vertex_id and ridge_id */
+ flagT seen:1; /* used to perform operations only once */
+ flagT tested:1; /* True when ridge is tested for convexity */
+ flagT nonconvex:1; /* True if getmergeset detected a non-convex neighbor
+ only one ridge between neighbors may have nonconvex */
+};
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >--------------------------------</a><a name="vertexT">-</a>
+
+ vertexT
+ defines a vertex
+
+ topological information:
+ next,previous doubly-linked list of all vertices
+ neighbors set of adjacent facets (only if qh.VERTEXneighbors)
+
+ geometric information:
+ point array of DIM3 coordinates
+*/
+struct vertexT {
+ vertexT *next; /* next vertex in vertex_list */
+ vertexT *previous; /* previous vertex in vertex_list */
+ pointT *point; /* hull_dim coordinates (coordT) */
+ setT *neighbors; /* neighboring facets of vertex, qh_vertexneighbors()
+ inits in io_r.c or after first merge */
+ unsigned id; /* unique identifier. Same size as qh.vertex_id and qh.ridge_id */
+ unsigned visitid; /* for use with qh.vertex_visit, size must match */
+ flagT seen:1; /* used to perform operations only once */
+ flagT seen2:1; /* another seen flag */
+ flagT delridge:1; /* vertex was part of a deleted ridge */
+ flagT deleted:1; /* true if vertex on qh.del_vertices */
+ flagT newlist:1; /* true if vertex on qh.newvertex_list */
+};
+
+/*======= -global variables -qh ============================*/
+
+/*-<a href="qh-globa_r.htm#TOC"
+ >--------------------------------</a><a name="qh">-</a>
+
+ qhT
+ All global variables for qhull are in qhT. It includes qhmemT, qhstatT, and rbox globals
+
+ This version of Qhull is reentrant, but it is not thread-safe.
+
+ Do not run separate threads on the same instance of qhT.
+
+ QHULL_LIB_CHECK checks that a program and the corresponding
+ qhull library were built with the same type of header files.
+*/
+
+#define QHULL_NON_REENTRANT 0
+#define QHULL_QH_POINTER 1
+#define QHULL_REENTRANT 2
+
+#define QHULL_LIB_TYPE QHULL_REENTRANT
+
+#define QHULL_LIB_CHECK qh_lib_check(QHULL_LIB_TYPE, sizeof(qhT), sizeof(vertexT), sizeof(ridgeT), sizeof(facetT), sizeof(setT), sizeof(qhmemT));
+#define QHULL_LIB_CHECK_RBOX qh_lib_check(QHULL_LIB_TYPE, sizeof(qhT), sizeof(vertexT), sizeof(ridgeT), sizeof(facetT), 0, 0);
+
+struct qhT {
+
+/*-<a href="qh-globa_r.htm#TOC"
+ >--------------------------------</a><a name="qh-const">-</a>
+
+ qh constants
+ configuration flags and constants for Qhull
+
+ notes:
+ The user configures Qhull by defining flags. They are
+ copied into qh by qh_setflags(). qh-quick_r.htm#options defines the flags.
+*/
+ boolT ALLpoints; /* true 'Qs' if search all points for initial simplex */
+ boolT ANGLEmerge; /* true 'Qa' if sort potential merges by angle */
+ boolT APPROXhull; /* true 'Wn' if MINoutside set */
+ realT MINoutside; /* 'Wn' min. distance for an outside point */
+ boolT ANNOTATEoutput; /* true 'Ta' if annotate output with message codes */
+ boolT ATinfinity; /* true 'Qz' if point num_points-1 is "at-infinity"
+ for improving precision in Delaunay triangulations */
+ boolT AVOIDold; /* true 'Q4' if avoid old->new merges */
+ boolT BESToutside; /* true 'Qf' if partition points into best outsideset */
+ boolT CDDinput; /* true 'Pc' if input uses CDD format (1.0/offset first) */
+ boolT CDDoutput; /* true 'PC' if print normals in CDD format (offset first) */
+ boolT CHECKfrequently; /* true 'Tc' if checking frequently */
+ realT premerge_cos; /* 'A-n' cos_max when pre merging */
+ realT postmerge_cos; /* 'An' cos_max when post merging */
+ boolT DELAUNAY; /* true 'd' if computing DELAUNAY triangulation */
+ boolT DOintersections; /* true 'Gh' if print hyperplane intersections */
+ int DROPdim; /* drops dim 'GDn' for 4-d -> 3-d output */
+ boolT FORCEoutput; /* true 'Po' if forcing output despite degeneracies */
+ int GOODpoint; /* 1+n for 'QGn', good facet if visible/not(-) from point n*/
+ pointT *GOODpointp; /* the actual point */
+ boolT GOODthreshold; /* true if qh.lower_threshold/upper_threshold defined
+ false if qh.SPLITthreshold */
+ int GOODvertex; /* 1+n, good facet if vertex for point n */
+ pointT *GOODvertexp; /* the actual point */
+ boolT HALFspace; /* true 'Hn,n,n' if halfspace intersection */
+ boolT ISqhullQh; /* Set by Qhull.cpp on initialization */
+ int IStracing; /* trace execution, 0=none, 1=least, 4=most, -1=events */
+ int KEEParea; /* 'PAn' number of largest facets to keep */
+ boolT KEEPcoplanar; /* true 'Qc' if keeping nearest facet for coplanar points */
+ boolT KEEPinside; /* true 'Qi' if keeping nearest facet for inside points
+ set automatically if 'd Qc' */
+ int KEEPmerge; /* 'PMn' number of facets to keep with most merges */
+ realT KEEPminArea; /* 'PFn' minimum facet area to keep */
+ realT MAXcoplanar; /* 'Un' max distance below a facet to be coplanar*/
+ boolT MERGEexact; /* true 'Qx' if exact merges (coplanar, degen, dupridge, flipped) */
+ boolT MERGEindependent; /* true 'Q2' if merging independent sets */
+ boolT MERGING; /* true if exact-, pre- or post-merging, with angle and centrum tests */
+ realT premerge_centrum; /* 'C-n' centrum_radius when pre merging. Default is round-off */
+ realT postmerge_centrum; /* 'Cn' centrum_radius when post merging. Default is round-off */
+ boolT MERGEvertices; /* true 'Q3' if merging redundant vertices */
+ realT MINvisible; /* 'Vn' min. distance for a facet to be visible */
+ boolT NOnarrow; /* true 'Q10' if no special processing for narrow distributions */
+ boolT NOnearinside; /* true 'Q8' if ignore near-inside points when partitioning */
+ boolT NOpremerge; /* true 'Q0' if no defaults for C-0 or Qx */
+ boolT NOwide; /* true 'Q12' if no error on wide merge due to duplicate ridge */
+ boolT ONLYgood; /* true 'Qg' if process points with good visible or horizon facets */
+ boolT ONLYmax; /* true 'Qm' if only process points that increase max_outside */
+ boolT PICKfurthest; /* true 'Q9' if process furthest of furthest points*/
+ boolT POSTmerge; /* true if merging after buildhull (Cn or An) */
+ boolT PREmerge; /* true if merging during buildhull (C-n or A-n) */
+ /* NOTE: some of these names are similar to qh_PRINT names */
+ boolT PRINTcentrums; /* true 'Gc' if printing centrums */
+ boolT PRINTcoplanar; /* true 'Gp' if printing coplanar points */
+ int PRINTdim; /* print dimension for Geomview output */
+ boolT PRINTdots; /* true 'Ga' if printing all points as dots */
+ boolT PRINTgood; /* true 'Pg' if printing good facets */
+ boolT PRINTinner; /* true 'Gi' if printing inner planes */
+ boolT PRINTneighbors; /* true 'PG' if printing neighbors of good facets */
+ boolT PRINTnoplanes; /* true 'Gn' if printing no planes */
+ boolT PRINToptions1st; /* true 'FO' if printing options to stderr */
+ boolT PRINTouter; /* true 'Go' if printing outer planes */
+ boolT PRINTprecision; /* false 'Pp' if not reporting precision problems */
+ qh_PRINT PRINTout[qh_PRINTEND]; /* list of output formats to print */
+ boolT PRINTridges; /* true 'Gr' if print ridges */
+ boolT PRINTspheres; /* true 'Gv' if print vertices as spheres */
+ boolT PRINTstatistics; /* true 'Ts' if printing statistics to stderr */
+ boolT PRINTsummary; /* true 's' if printing summary to stderr */
+ boolT PRINTtransparent; /* true 'Gt' if print transparent outer ridges */
+ boolT PROJECTdelaunay; /* true if DELAUNAY, no readpoints() and
+ need projectinput() for Delaunay in qh_init_B */
+ int PROJECTinput; /* number of projected dimensions 'bn:0Bn:0' */
+ boolT QUICKhelp; /* true if quick help message for degen input */
+ boolT RANDOMdist; /* true if randomly change distplane and setfacetplane */
+ realT RANDOMfactor; /* maximum random perturbation */
+ realT RANDOMa; /* qh_randomfactor is randr * RANDOMa + RANDOMb */
+ realT RANDOMb;
+ boolT RANDOMoutside; /* true if select a random outside point */
+ int REPORTfreq; /* buildtracing reports every n facets */
+ int REPORTfreq2; /* tracemerging reports every REPORTfreq/2 facets */
+ int RERUN; /* 'TRn' rerun qhull n times (qh.build_cnt) */
+ int ROTATErandom; /* 'QRn' seed, 0 time, >= rotate input */
+ boolT SCALEinput; /* true 'Qbk' if scaling input */
+ boolT SCALElast; /* true 'Qbb' if scale last coord to max prev coord */
+ boolT SETroundoff; /* true 'E' if qh.DISTround is predefined */
+ boolT SKIPcheckmax; /* true 'Q5' if skip qh_check_maxout */
+ boolT SKIPconvex; /* true 'Q6' if skip convexity testing during pre-merge */
+ boolT SPLITthresholds; /* true if upper_/lower_threshold defines a region
+ used only for printing (!for qh.ONLYgood) */
+ int STOPcone; /* 'TCn' 1+n for stopping after cone for point n */
+ /* also used by qh_build_withresart for err exit*/
+ int STOPpoint; /* 'TVn' 'TV-n' 1+n for stopping after/before(-)
+ adding point n */
+ int TESTpoints; /* 'QTn' num of test points after qh.num_points. Test points always coplanar. */
+ boolT TESTvneighbors; /* true 'Qv' if test vertex neighbors at end */
+ int TRACElevel; /* 'Tn' conditional IStracing level */
+ int TRACElastrun; /* qh.TRACElevel applies to last qh.RERUN */
+ int TRACEpoint; /* 'TPn' start tracing when point n is a vertex */
+ realT TRACEdist; /* 'TWn' start tracing when merge distance too big */
+ int TRACEmerge; /* 'TMn' start tracing before this merge */
+ boolT TRIangulate; /* true 'Qt' if triangulate non-simplicial facets */
+ boolT TRInormals; /* true 'Q11' if triangulate duplicates ->normal and ->center (sets Qt) */
+ boolT UPPERdelaunay; /* true 'Qu' if computing furthest-site Delaunay */
+ boolT USEstdout; /* true 'Tz' if using stdout instead of stderr */
+ boolT VERIFYoutput; /* true 'Tv' if verify output at end of qhull */
+ boolT VIRTUALmemory; /* true 'Q7' if depth-first processing in buildhull */
+ boolT VORONOI; /* true 'v' if computing Voronoi diagram */
+
+ /*--------input constants ---------*/
+ realT AREAfactor; /* 1/(hull_dim-1)! for converting det's to area */
+ boolT DOcheckmax; /* true if calling qh_check_maxout (qhT *qh, qh_initqhull_globals) */
+ char *feasible_string; /* feasible point 'Hn,n,n' for halfspace intersection */
+ coordT *feasible_point; /* as coordinates, both malloc'd */
+ boolT GETarea; /* true 'Fa', 'FA', 'FS', 'PAn', 'PFn' if compute facet area/Voronoi volume in io_r.c */
+ boolT KEEPnearinside; /* true if near-inside points in coplanarset */
+ int hull_dim; /* dimension of hull, set by initbuffers */
+ int input_dim; /* dimension of input, set by initbuffers */
+ int num_points; /* number of input points */
+ pointT *first_point; /* array of input points, see POINTSmalloc */
+ boolT POINTSmalloc; /* true if qh.first_point/num_points allocated */
+ pointT *input_points; /* copy of original qh.first_point for input points for qh_joggleinput */
+ boolT input_malloc; /* true if qh.input_points malloc'd */
+ char qhull_command[256];/* command line that invoked this program */
+ int qhull_commandsiz2; /* size of qhull_command at qh_clear_outputflags */
+ char rbox_command[256]; /* command line that produced the input points */
+ char qhull_options[512];/* descriptive list of options */
+ int qhull_optionlen; /* length of last line */
+ int qhull_optionsiz; /* size of qhull_options at qh_build_withrestart */
+ int qhull_optionsiz2; /* size of qhull_options at qh_clear_outputflags */
+ int run_id; /* non-zero, random identifier for this instance of qhull */
+ boolT VERTEXneighbors; /* true if maintaining vertex neighbors */
+ boolT ZEROcentrum; /* true if 'C-0' or 'C-0 Qx'. sets ZEROall_ok */
+ realT *upper_threshold; /* don't print if facet->normal[k]>=upper_threshold[k]
+ must set either GOODthreshold or SPLITthreshold
+ if Delaunay, default is 0.0 for upper envelope */
+ realT *lower_threshold; /* don't print if facet->normal[k] <=lower_threshold[k] */
+ realT *upper_bound; /* scale point[k] to new upper bound */
+ realT *lower_bound; /* scale point[k] to new lower bound
+ project if both upper_ and lower_bound == 0 */
+
+/*-<a href="qh-globa_r.htm#TOC"
+ >--------------------------------</a><a name="qh-prec">-</a>
+
+ qh precision constants
+ precision constants for Qhull
+
+ notes:
+ qh_detroundoff(qh) computes the maximum roundoff error for distance
+ and other computations. It also sets default values for the
+ qh constants above.
+*/
+ realT ANGLEround; /* max round off error for angles */
+ realT centrum_radius; /* max centrum radius for convexity (roundoff added) */
+ realT cos_max; /* max cosine for convexity (roundoff added) */
+ realT DISTround; /* max round off error for distances, 'E' overrides qh_distround() */
+ realT MAXabs_coord; /* max absolute coordinate */
+ realT MAXlastcoord; /* max last coordinate for qh_scalelast */
+ realT MAXsumcoord; /* max sum of coordinates */
+ realT MAXwidth; /* max rectilinear width of point coordinates */
+ realT MINdenom_1; /* min. abs. value for 1/x */
+ realT MINdenom; /* use divzero if denominator < MINdenom */
+ realT MINdenom_1_2; /* min. abs. val for 1/x that allows normalization */
+ realT MINdenom_2; /* use divzero if denominator < MINdenom_2 */
+ realT MINlastcoord; /* min. last coordinate for qh_scalelast */
+ boolT NARROWhull; /* set in qh_initialhull if angle < qh_MAXnarrow */
+ realT *NEARzero; /* hull_dim array for near zero in gausselim */
+ realT NEARinside; /* keep points for qh_check_maxout if close to facet */
+ realT ONEmerge; /* max distance for merging simplicial facets */
+ realT outside_err; /* application's epsilon for coplanar points
+ qh_check_bestdist() qh_check_points() reports error if point outside */
+ realT WIDEfacet; /* size of wide facet for skipping ridge in
+ area computation and locking centrum */
+
+/*-<a href="qh-globa_r.htm#TOC"
+ >--------------------------------</a><a name="qh-codetern">-</a>
+
+ qh internal constants
+ internal constants for Qhull
+*/
+ char qhull[sizeof("qhull")]; /* "qhull" for checking ownership while debugging */
+ jmp_buf errexit; /* exit label for qh_errexit, defined by setjmp() and NOerrexit */
+ char jmpXtra[40]; /* extra bytes in case jmp_buf is defined wrong by compiler */
+ jmp_buf restartexit; /* restart label for qh_errexit, defined by setjmp() and ALLOWrestart */
+ char jmpXtra2[40]; /* extra bytes in case jmp_buf is defined wrong by compiler*/
+ FILE *fin; /* pointer to input file, init by qh_initqhull_start */
+ FILE *fout; /* pointer to output file */
+ FILE *ferr; /* pointer to error file */
+ pointT *interior_point; /* center point of the initial simplex*/
+ int normal_size; /* size in bytes for facet normals and point coords*/
+ int center_size; /* size in bytes for Voronoi centers */
+ int TEMPsize; /* size for small, temporary sets (in quick mem) */
+
+/*-<a href="qh-globa_r.htm#TOC"
+ >--------------------------------</a><a name="qh-lists">-</a>
+
+ qh facet and vertex lists
+ defines lists of facets, new facets, visible facets, vertices, and
+ new vertices. Includes counts, next ids, and trace ids.
+ see:
+ qh_resetlists()
+*/
+ facetT *facet_list; /* first facet */
+ facetT *facet_tail; /* end of facet_list (dummy facet) */
+ facetT *facet_next; /* next facet for buildhull()
+ previous facets do not have outside sets
+ NARROWhull: previous facets may have coplanar outside sets for qh_outcoplanar */
+ facetT *newfacet_list; /* list of new facets to end of facet_list */
+ facetT *visible_list; /* list of visible facets preceding newfacet_list,
+ facet->visible set */
+ int num_visible; /* current number of visible facets */
+ unsigned tracefacet_id; /* set at init, then can print whenever */
+ facetT *tracefacet; /* set in newfacet/mergefacet, undone in delfacet*/
+ unsigned tracevertex_id; /* set at buildtracing, can print whenever */
+ vertexT *tracevertex; /* set in newvertex, undone in delvertex*/
+ vertexT *vertex_list; /* list of all vertices, to vertex_tail */
+ vertexT *vertex_tail; /* end of vertex_list (dummy vertex) */
+ vertexT *newvertex_list; /* list of vertices in newfacet_list, to vertex_tail
+ all vertices have 'newlist' set */
+ int num_facets; /* number of facets in facet_list
+ includes visible faces (num_visible) */
+ int num_vertices; /* number of vertices in facet_list */
+ int num_outside; /* number of points in outsidesets (for tracing and RANDOMoutside)
+ includes coplanar outsideset points for NARROWhull/qh_outcoplanar() */
+ int num_good; /* number of good facets (after findgood_all) */
+ unsigned facet_id; /* ID of next, new facet from newfacet() */
+ unsigned ridge_id; /* ID of next, new ridge from newridge() */
+ unsigned vertex_id; /* ID of next, new vertex from newvertex() */
+
+/*-<a href="qh-globa_r.htm#TOC"
+ >--------------------------------</a><a name="qh-var">-</a>
+
+ qh global variables
+ defines minimum and maximum distances, next visit ids, several flags,
+ and other global variables.
+ initialize in qh_initbuild or qh_maxmin if used in qh_buildhull
+*/
+ unsigned long hulltime; /* ignore time to set up input and randomize */
+ /* use unsigned to avoid wrap-around errors */
+ boolT ALLOWrestart; /* true if qh_precision can use qh.restartexit */
+ int build_cnt; /* number of calls to qh_initbuild */
+ qh_CENTER CENTERtype; /* current type of facet->center, qh_CENTER */
+ int furthest_id; /* pointid of furthest point, for tracing */
+ facetT *GOODclosest; /* closest facet to GOODthreshold in qh_findgood */
+ boolT hasAreaVolume; /* true if totarea, totvol was defined by qh_getarea */
+ boolT hasTriangulation; /* true if triangulation created by qh_triangulate */
+ realT JOGGLEmax; /* set 'QJn' if randomly joggle input */
+ boolT maxoutdone; /* set qh_check_maxout(), cleared by qh_addpoint() */
+ realT max_outside; /* maximum distance from a point to a facet,
+ before roundoff, not simplicial vertices
+ actual outer plane is +DISTround and
+ computed outer plane is +2*DISTround */
+ realT max_vertex; /* maximum distance (>0) from vertex to a facet,
+ before roundoff, due to a merge */
+ realT min_vertex; /* minimum distance (<0) from vertex to a facet,
+ before roundoff, due to a merge
+ if qh.JOGGLEmax, qh_makenewplanes sets it
+ recomputed if qh.DOcheckmax, default -qh.DISTround */
+ boolT NEWfacets; /* true while visible facets invalid due to new or merge
+ from makecone/attachnewfacets to deletevisible */
+ boolT findbestnew; /* true if partitioning calls qh_findbestnew */
+ boolT findbest_notsharp; /* true if new facets are at least 90 degrees */
+ boolT NOerrexit; /* true if qh.errexit is not available, cleared after setjmp */
+ realT PRINTcradius; /* radius for printing centrums */
+ realT PRINTradius; /* radius for printing vertex spheres and points */
+ boolT POSTmerging; /* true when post merging */
+ int printoutvar; /* temporary variable for qh_printbegin, etc. */
+ int printoutnum; /* number of facets printed */
+ boolT QHULLfinished; /* True after qhull() is finished */
+ realT totarea; /* 'FA': total facet area computed by qh_getarea, hasAreaVolume */
+ realT totvol; /* 'FA': total volume computed by qh_getarea, hasAreaVolume */
+ unsigned int visit_id; /* unique ID for searching neighborhoods, */
+ unsigned int vertex_visit; /* unique ID for searching vertices, reset with qh_buildtracing */
+ boolT ZEROall_ok; /* True if qh_checkzero always succeeds */
+ boolT WAScoplanar; /* True if qh_partitioncoplanar (qhT *qh, qh_check_maxout) */
+
+/*-<a href="qh-globa_r.htm#TOC"
+ >--------------------------------</a><a name="qh-set">-</a>
+
+ qh global sets
+ defines sets for merging, initial simplex, hashing, extra input points,
+ and deleted vertices
+*/
+ setT *facet_mergeset; /* temporary set of merges to be done */
+ setT *degen_mergeset; /* temporary set of degenerate and redundant merges */
+ setT *hash_table; /* hash table for matching ridges in qh_matchfacets
+ size is setsize() */
+ setT *other_points; /* additional points */
+ setT *del_vertices; /* vertices to partition and delete with visible
+ facets. Have deleted set for checkfacet */
+
+/*-<a href="qh-globa_r.htm#TOC"
+ >--------------------------------</a><a name="qh-buf">-</a>
+
+ qh global buffers
+ defines buffers for maxtrix operations, input, and error messages
+*/
+ coordT *gm_matrix; /* (dim+1)Xdim matrix for geom_r.c */
+ coordT **gm_row; /* array of gm_matrix rows */
+ char* line; /* malloc'd input line of maxline+1 chars */
+ int maxline;
+ coordT *half_space; /* malloc'd input array for halfspace (qh.normal_size+coordT) */
+ coordT *temp_malloc; /* malloc'd input array for points */
+
+/*-<a href="qh-globa_r.htm#TOC"
+ >--------------------------------</a><a name="qh-static">-</a>
+
+ qh static variables
+ defines static variables for individual functions
+
+ notes:
+ do not use 'static' within a function. Multiple instances of qhull
+ may exist.
+
+ do not assume zero initialization, 'QPn' may cause a restart
+*/
+ boolT ERREXITcalled; /* true during qh_errexit (qhT *qh, prevents duplicate calls */
+ boolT firstcentrum; /* for qh_printcentrum */
+ boolT old_randomdist; /* save RANDOMdist flag during io, tracing, or statistics */
+ setT *coplanarfacetset; /* set of coplanar facets for searching qh_findbesthorizon() */
+ realT last_low; /* qh_scalelast parameters for qh_setdelaunay */
+ realT last_high;
+ realT last_newhigh;
+ unsigned lastreport; /* for qh_buildtracing */
+ int mergereport; /* for qh_tracemerging */
+ setT *old_tempstack; /* for saving qh->qhmem.tempstack in save_qhull */
+ int ridgeoutnum; /* number of ridges for 4OFF output (qh_printbegin,etc) */
+
+/*-<a href="qh-globa_r.htm#TOC"
+ >--------------------------------</a><a name="qh-const">-</a>
+
+ qh memory management, rbox globals, and statistics
+
+ Replaces global data structures defined for libqhull
+*/
+ int last_random; /* Last random number from qh_rand (random_r.c) */
+ jmp_buf rbox_errexit; /* errexit from rboxlib_r.c, defined by qh_rboxpoints() only */
+ char jmpXtra3[40]; /* extra bytes in case jmp_buf is defined wrong by compiler */
+ int rbox_isinteger;
+ double rbox_out_offset;
+ void * cpp_object; /* C++ pointer. Currently used by RboxPoints.qh_fprintf_rbox */
+
+ /* Last, otherwise zero'd by qh_initqhull_start2 (global_r.c */
+ qhmemT qhmem; /* Qhull managed memory (mem_r.h) */
+ /* After qhmem because its size depends on the number of statistics */
+ qhstatT qhstat; /* Qhull statistics (stat_r.h) */
+};
+
+/*=========== -macros- =========================*/
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >--------------------------------</a><a name="otherfacet_">-</a>
+
+ otherfacet_(ridge, facet)
+ return neighboring facet for a ridge in facet
+*/
+#define otherfacet_(ridge, facet) \
+ (((ridge)->top == (facet)) ? (ridge)->bottom : (ridge)->top)
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >--------------------------------</a><a name="getid_">-</a>
+
+ getid_(p)
+ return int ID for facet, ridge, or vertex
+ return qh_IDunknown(-1) if NULL
+*/
+#define getid_(p) ((p) ? (int)((p)->id) : qh_IDunknown)
+
+/*============== FORALL macros ===================*/
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >--------------------------------</a><a name="FORALLfacets">-</a>
+
+ FORALLfacets { ... }
+ assign 'facet' to each facet in qh.facet_list
+
+ notes:
+ uses 'facetT *facet;'
+ assumes last facet is a sentinel
+ assumes qh defined
+
+ see:
+ FORALLfacet_( facetlist )
+*/
+#define FORALLfacets for (facet=qh->facet_list;facet && facet->next;facet=facet->next)
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >--------------------------------</a><a name="FORALLpoints">-</a>
+
+ FORALLpoints { ... }
+ assign 'point' to each point in qh.first_point, qh.num_points
+
+ notes:
+ assumes qh defined
+
+ declare:
+ coordT *point, *pointtemp;
+*/
+#define FORALLpoints FORALLpoint_(qh, qh->first_point, qh->num_points)
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >--------------------------------</a><a name="FORALLpoint_">-</a>
+
+ FORALLpoint_( qh, points, num) { ... }
+ assign 'point' to each point in points array of num points
+
+ declare:
+ coordT *point, *pointtemp;
+*/
+#define FORALLpoint_(qh, points, num) for (point= (points), \
+ pointtemp= (points)+qh->hull_dim*(num); point < pointtemp; point += qh->hull_dim)
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >--------------------------------</a><a name="FORALLvertices">-</a>
+
+ FORALLvertices { ... }
+ assign 'vertex' to each vertex in qh.vertex_list
+
+ declare:
+ vertexT *vertex;
+
+ notes:
+ assumes qh.vertex_list terminated with a sentinel
+ assumes qh defined
+*/
+#define FORALLvertices for (vertex=qh->vertex_list;vertex && vertex->next;vertex= vertex->next)
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >--------------------------------</a><a name="FOREACHfacet_">-</a>
+
+ FOREACHfacet_( facets ) { ... }
+ assign 'facet' to each facet in facets
+
+ declare:
+ facetT *facet, **facetp;
+
+ see:
+ <a href="qset_r.h#FOREACHsetelement_">FOREACHsetelement_</a>
+*/
+#define FOREACHfacet_(facets) FOREACHsetelement_(facetT, facets, facet)
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >--------------------------------</a><a name="FOREACHneighbor_">-</a>
+
+ FOREACHneighbor_( facet ) { ... }
+ assign 'neighbor' to each neighbor in facet->neighbors
+
+ FOREACHneighbor_( vertex ) { ... }
+ assign 'neighbor' to each neighbor in vertex->neighbors
+
+ declare:
+ facetT *neighbor, **neighborp;
+
+ see:
+ <a href="qset_r.h#FOREACHsetelement_">FOREACHsetelement_</a>
+*/
+#define FOREACHneighbor_(facet) FOREACHsetelement_(facetT, facet->neighbors, neighbor)
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >--------------------------------</a><a name="FOREACHpoint_">-</a>
+
+ FOREACHpoint_( points ) { ... }
+ assign 'point' to each point in points set
+
+ declare:
+ pointT *point, **pointp;
+
+ see:
+ <a href="qset_r.h#FOREACHsetelement_">FOREACHsetelement_</a>
+*/
+#define FOREACHpoint_(points) FOREACHsetelement_(pointT, points, point)
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >--------------------------------</a><a name="FOREACHridge_">-</a>
+
+ FOREACHridge_( ridges ) { ... }
+ assign 'ridge' to each ridge in ridges set
+
+ declare:
+ ridgeT *ridge, **ridgep;
+
+ see:
+ <a href="qset_r.h#FOREACHsetelement_">FOREACHsetelement_</a>
+*/
+#define FOREACHridge_(ridges) FOREACHsetelement_(ridgeT, ridges, ridge)
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >--------------------------------</a><a name="FOREACHvertex_">-</a>
+
+ FOREACHvertex_( vertices ) { ... }
+ assign 'vertex' to each vertex in vertices set
+
+ declare:
+ vertexT *vertex, **vertexp;
+
+ see:
+ <a href="qset_r.h#FOREACHsetelement_">FOREACHsetelement_</a>
+*/
+#define FOREACHvertex_(vertices) FOREACHsetelement_(vertexT, vertices,vertex)
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >--------------------------------</a><a name="FOREACHfacet_i_">-</a>
+
+ FOREACHfacet_i_( qh, facets ) { ... }
+ assign 'facet' and 'facet_i' for each facet in facets set
+
+ declare:
+ facetT *facet;
+ int facet_n, facet_i;
+
+ see:
+ <a href="qset_r.h#FOREACHsetelement_i_">FOREACHsetelement_i_</a>
+*/
+#define FOREACHfacet_i_(qh, facets) FOREACHsetelement_i_(qh, facetT, facets, facet)
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >--------------------------------</a><a name="FOREACHneighbor_i_">-</a>
+
+ FOREACHneighbor_i_( qh, facet ) { ... }
+ assign 'neighbor' and 'neighbor_i' for each neighbor in facet->neighbors
+
+ FOREACHneighbor_i_( qh, vertex ) { ... }
+ assign 'neighbor' and 'neighbor_i' for each neighbor in vertex->neighbors
+
+ declare:
+ facetT *neighbor;
+ int neighbor_n, neighbor_i;
+
+ see:
+ <a href="qset_r.h#FOREACHsetelement_i_">FOREACHsetelement_i_</a>
+*/
+#define FOREACHneighbor_i_(qh, facet) FOREACHsetelement_i_(qh, facetT, facet->neighbors, neighbor)
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >--------------------------------</a><a name="FOREACHpoint_i_">-</a>
+
+ FOREACHpoint_i_( qh, points ) { ... }
+ assign 'point' and 'point_i' for each point in points set
+
+ declare:
+ pointT *point;
+ int point_n, point_i;
+
+ see:
+ <a href="qset_r.h#FOREACHsetelement_i_">FOREACHsetelement_i_</a>
+*/
+#define FOREACHpoint_i_(qh, points) FOREACHsetelement_i_(qh, pointT, points, point)
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >--------------------------------</a><a name="FOREACHridge_i_">-</a>
+
+ FOREACHridge_i_( qh, ridges ) { ... }
+ assign 'ridge' and 'ridge_i' for each ridge in ridges set
+
+ declare:
+ ridgeT *ridge;
+ int ridge_n, ridge_i;
+
+ see:
+ <a href="qset_r.h#FOREACHsetelement_i_">FOREACHsetelement_i_</a>
+*/
+#define FOREACHridge_i_(qh, ridges) FOREACHsetelement_i_(qh, ridgeT, ridges, ridge)
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >--------------------------------</a><a name="FOREACHvertex_i_">-</a>
+
+ FOREACHvertex_i_( qh, vertices ) { ... }
+ assign 'vertex' and 'vertex_i' for each vertex in vertices set
+
+ declare:
+ vertexT *vertex;
+ int vertex_n, vertex_i;
+
+ see:
+ <a href="qset_r.h#FOREACHsetelement_i_">FOREACHsetelement_i_</a>
+*/
+#define FOREACHvertex_i_(qh, vertices) FOREACHsetelement_i_(qh, vertexT, vertices,vertex)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/********* -libqhull_r.c prototypes (duplicated from qhull_ra.h) **********************/
+
+void qh_qhull(qhT *qh);
+boolT qh_addpoint(qhT *qh, pointT *furthest, facetT *facet, boolT checkdist);
+void qh_printsummary(qhT *qh, FILE *fp);
+
+/********* -user.c prototypes (alphabetical) **********************/
+
+void qh_errexit(qhT *qh, int exitcode, facetT *facet, ridgeT *ridge);
+void qh_errprint(qhT *qh, const char* string, facetT *atfacet, facetT *otherfacet, ridgeT *atridge, vertexT *atvertex);
+int qh_new_qhull(qhT *qh, int dim, int numpoints, coordT *points, boolT ismalloc,
+ char *qhull_cmd, FILE *outfile, FILE *errfile);
+void qh_printfacetlist(qhT *qh, facetT *facetlist, setT *facets, boolT printall);
+void qh_printhelp_degenerate(qhT *qh, FILE *fp);
+void qh_printhelp_narrowhull(qhT *qh, FILE *fp, realT minangle);
+void qh_printhelp_singular(qhT *qh, FILE *fp);
+void qh_user_memsizes(qhT *qh);
+
+/********* -usermem_r.c prototypes (alphabetical) **********************/
+void qh_exit(int exitcode);
+void qh_fprintf_stderr(int msgcode, const char *fmt, ... );
+void qh_free(void *mem);
+void *qh_malloc(size_t size);
+
+/********* -userprintf_r.c and userprintf_rbox_r.c prototypes **********************/
+void qh_fprintf(qhT *qh, FILE *fp, int msgcode, const char *fmt, ... );
+void qh_fprintf_rbox(qhT *qh, FILE *fp, int msgcode, const char *fmt, ... );
+
+/***** -geom_r.c/geom2_r.c/random_r.c prototypes (duplicated from geom_r.h, random_r.h) ****************/
+
+facetT *qh_findbest(qhT *qh, pointT *point, facetT *startfacet,
+ boolT bestoutside, boolT newfacets, boolT noupper,
+ realT *dist, boolT *isoutside, int *numpart);
+facetT *qh_findbestnew(qhT *qh, pointT *point, facetT *startfacet,
+ realT *dist, boolT bestoutside, boolT *isoutside, int *numpart);
+boolT qh_gram_schmidt(qhT *qh, int dim, realT **rows);
+void qh_outerinner(qhT *qh, facetT *facet, realT *outerplane, realT *innerplane);
+void qh_printsummary(qhT *qh, FILE *fp);
+void qh_projectinput(qhT *qh);
+void qh_randommatrix(qhT *qh, realT *buffer, int dim, realT **row);
+void qh_rotateinput(qhT *qh, realT **rows);
+void qh_scaleinput(qhT *qh);
+void qh_setdelaunay(qhT *qh, int dim, int count, pointT *points);
+coordT *qh_sethalfspace_all(qhT *qh, int dim, int count, coordT *halfspaces, pointT *feasible);
+
+/***** -global_r.c prototypes (alphabetical) ***********************/
+
+unsigned long qh_clock(qhT *qh);
+void qh_checkflags(qhT *qh, char *command, char *hiddenflags);
+void qh_clear_outputflags(qhT *qh);
+void qh_freebuffers(qhT *qh);
+void qh_freeqhull(qhT *qh, boolT allmem);
+void qh_init_A(qhT *qh, FILE *infile, FILE *outfile, FILE *errfile, int argc, char *argv[]);
+void qh_init_B(qhT *qh, coordT *points, int numpoints, int dim, boolT ismalloc);
+void qh_init_qhull_command(qhT *qh, int argc, char *argv[]);
+void qh_initbuffers(qhT *qh, coordT *points, int numpoints, int dim, boolT ismalloc);
+void qh_initflags(qhT *qh, char *command);
+void qh_initqhull_buffers(qhT *qh);
+void qh_initqhull_globals(qhT *qh, coordT *points, int numpoints, int dim, boolT ismalloc);
+void qh_initqhull_mem(qhT *qh);
+void qh_initqhull_outputflags(qhT *qh);
+void qh_initqhull_start(qhT *qh, FILE *infile, FILE *outfile, FILE *errfile);
+void qh_initqhull_start2(qhT *qh, FILE *infile, FILE *outfile, FILE *errfile);
+void qh_initthresholds(qhT *qh, char *command);
+void qh_lib_check(int qhullLibraryType, int qhTsize, int vertexTsize, int ridgeTsize, int facetTsize, int setTsize, int qhmemTsize);
+void qh_option(qhT *qh, const char *option, int *i, realT *r);
+void qh_zero(qhT *qh, FILE *errfile);
+
+/***** -io_r.c prototypes (duplicated from io_r.h) ***********************/
+
+void qh_dfacet(qhT *qh, unsigned id);
+void qh_dvertex(qhT *qh, unsigned id);
+void qh_printneighborhood(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetA, facetT *facetB, boolT printall);
+void qh_produce_output(qhT *qh);
+coordT *qh_readpoints(qhT *qh, int *numpoints, int *dimension, boolT *ismalloc);
+
+
+/********* -mem_r.c prototypes (duplicated from mem_r.h) **********************/
+
+void qh_meminit(qhT *qh, FILE *ferr);
+void qh_memfreeshort(qhT *qh, int *curlong, int *totlong);
+
+/********* -poly_r.c/poly2_r.c prototypes (duplicated from poly_r.h) **********************/
+
+void qh_check_output(qhT *qh);
+void qh_check_points(qhT *qh);
+setT *qh_facetvertices(qhT *qh, facetT *facetlist, setT *facets, boolT allfacets);
+facetT *qh_findbestfacet(qhT *qh, pointT *point, boolT bestoutside,
+ realT *bestdist, boolT *isoutside);
+vertexT *qh_nearvertex(qhT *qh, facetT *facet, pointT *point, realT *bestdistp);
+pointT *qh_point(qhT *qh, int id);
+setT *qh_pointfacet(qhT *qh /*qh.facet_list*/);
+int qh_pointid(qhT *qh, pointT *point);
+setT *qh_pointvertex(qhT *qh /*qh.facet_list*/);
+void qh_setvoronoi_all(qhT *qh);
+void qh_triangulate(qhT *qh /*qh.facet_list*/);
+
+/********* -rboxpoints_r.c prototypes **********************/
+int qh_rboxpoints(qhT *qh, char* rbox_command);
+void qh_errexit_rbox(qhT *qh, int exitcode);
+
+/********* -stat_r.c prototypes (duplicated from stat_r.h) **********************/
+
+void qh_collectstatistics(qhT *qh);
+void qh_printallstatistics(qhT *qh, FILE *fp, const char *string);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* qhDEFlibqhull */
diff --git a/xs/src/qhull/src/libqhull_r/libqhull_r.pro b/xs/src/qhull/src/libqhull_r/libqhull_r.pro
new file mode 100644
index 000000000..6b8db44b7
--- /dev/null
+++ b/xs/src/qhull/src/libqhull_r/libqhull_r.pro
@@ -0,0 +1,67 @@
+# -------------------------------------------------
+# libqhull_r.pro -- Qt project for Qhull shared library
+#
+# It uses reentrant Qhull
+# -------------------------------------------------
+
+include(../qhull-warn.pri)
+
+DESTDIR = ../../lib
+DLLDESTDIR = ../../bin
+TEMPLATE = lib
+CONFIG += shared warn_on
+CONFIG -= qt
+
+build_pass:CONFIG(debug, debug|release):{
+ TARGET = qhull_rd
+ OBJECTS_DIR = Debug
+}else:build_pass:CONFIG(release, debug|release):{
+ TARGET = qhull_r
+ OBJECTS_DIR = Release
+}
+win32-msvc* : QMAKE_LFLAGS += /INCREMENTAL:NO
+
+win32-msvc* : DEF_FILE += ../../src/libqhull_r/qhull_r-exports.def
+
+# libqhull_r/libqhull_r.pro and ../qhull-libqhull-src_r.pri have the same SOURCES and HEADERS
+
+SOURCES += ../libqhull_r/global_r.c
+SOURCES += ../libqhull_r/stat_r.c
+SOURCES += ../libqhull_r/geom2_r.c
+SOURCES += ../libqhull_r/poly2_r.c
+SOURCES += ../libqhull_r/merge_r.c
+SOURCES += ../libqhull_r/libqhull_r.c
+SOURCES += ../libqhull_r/geom_r.c
+SOURCES += ../libqhull_r/poly_r.c
+SOURCES += ../libqhull_r/qset_r.c
+SOURCES += ../libqhull_r/mem_r.c
+SOURCES += ../libqhull_r/random_r.c
+SOURCES += ../libqhull_r/usermem_r.c
+SOURCES += ../libqhull_r/userprintf_r.c
+SOURCES += ../libqhull_r/io_r.c
+SOURCES += ../libqhull_r/user_r.c
+SOURCES += ../libqhull_r/rboxlib_r.c
+SOURCES += ../libqhull_r/userprintf_rbox_r.c
+
+HEADERS += ../libqhull_r/geom_r.h
+HEADERS += ../libqhull_r/io_r.h
+HEADERS += ../libqhull_r/libqhull_r.h
+HEADERS += ../libqhull_r/mem_r.h
+HEADERS += ../libqhull_r/merge_r.h
+HEADERS += ../libqhull_r/poly_r.h
+HEADERS += ../libqhull_r/random_r.h
+HEADERS += ../libqhull_r/qhull_ra.h
+HEADERS += ../libqhull_r/qset_r.h
+HEADERS += ../libqhull_r/stat_r.h
+HEADERS += ../libqhull_r/user_r.h
+
+OTHER_FILES += qh-geom_r.htm
+OTHER_FILES += qh-globa_r.htm
+OTHER_FILES += qh-io_r.htm
+OTHER_FILES += qh-mem_r.htm
+OTHER_FILES += qh-merge_r.htm
+OTHER_FILES += qh-poly_r.htm
+OTHER_FILES += qh-qhull_r.htm
+OTHER_FILES += qh-set_r.htm
+OTHER_FILES += qh-stat_r.htm
+OTHER_FILES += qh-user_r.htm
diff --git a/xs/src/qhull/src/libqhull_r/mem_r.c b/xs/src/qhull/src/libqhull_r/mem_r.c
new file mode 100644
index 000000000..801a8c76a
--- /dev/null
+++ b/xs/src/qhull/src/libqhull_r/mem_r.c
@@ -0,0 +1,562 @@
+/*<html><pre> -<a href="qh-mem_r.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ mem_r.c
+ memory management routines for qhull
+
+ See libqhull/mem_r.c for a standalone program.
+
+ To initialize memory:
+
+ qh_meminit(qh, stderr);
+ qh_meminitbuffers(qh, qh->IStracing, qh_MEMalign, 7, qh_MEMbufsize,qh_MEMinitbuf);
+ qh_memsize(qh, (int)sizeof(facetT));
+ qh_memsize(qh, (int)sizeof(facetT));
+ ...
+ qh_memsetup(qh);
+
+ To free up all memory buffers:
+ qh_memfreeshort(qh, &curlong, &totlong);
+
+ if qh_NOmem,
+ malloc/free is used instead of mem.c
+
+ notes:
+ uses Quickfit algorithm (freelists for commonly allocated sizes)
+ assumes small sizes for freelists (it discards the tail of memory buffers)
+
+ see:
+ qh-mem_r.htm and mem_r.h
+ global_r.c (qh_initbuffers) for an example of using mem_r.c
+
+ Copyright (c) 1993-2015 The Geometry Center.
+ $Id: //main/2015/qhull/src/libqhull_r/mem_r.c#5 $$Change: 2065 $
+ $DateTime: 2016/01/18 13:51:04 $$Author: bbarber $
+*/
+
+#include "libqhull_r.h" /* includes user_r.h and mem_r.h */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifndef qh_NOmem
+
+/*============= internal functions ==============*/
+
+static int qh_intcompare(const void *i, const void *j);
+
+/*========== functions in alphabetical order ======== */
+
+/*-<a href="qh-mem_r.htm#TOC"
+ >-------------------------------</a><a name="intcompare">-</a>
+
+ qh_intcompare( i, j )
+ used by qsort and bsearch to compare two integers
+*/
+static int qh_intcompare(const void *i, const void *j) {
+ return(*((const int *)i) - *((const int *)j));
+} /* intcompare */
+
+
+/*-<a href="qh-mem_r.htm#TOC"
+ >--------------------------------</a><a name="memalloc">-</a>
+
+ qh_memalloc( qh, insize )
+ returns object of insize bytes
+ qhmem is the global memory structure
+
+ returns:
+ pointer to allocated memory
+ errors if insufficient memory
+
+ notes:
+ use explicit type conversion to avoid type warnings on some compilers
+ actual object may be larger than insize
+ use qh_memalloc_() for inline code for quick allocations
+ logs allocations if 'T5'
+ caller is responsible for freeing the memory.
+ short memory is freed on shutdown by qh_memfreeshort unless qh_NOmem
+
+ design:
+ if size < qh->qhmem.LASTsize
+ if qh->qhmem.freelists[size] non-empty
+ return first object on freelist
+ else
+ round up request to size of qh->qhmem.freelists[size]
+ allocate new allocation buffer if necessary
+ allocate object from allocation buffer
+ else
+ allocate object with qh_malloc() in user.c
+*/
+void *qh_memalloc(qhT *qh, int insize) {
+ void **freelistp, *newbuffer;
+ int idx, size, n;
+ int outsize, bufsize;
+ void *object;
+
+ if (insize<0) {
+ qh_fprintf(qh, qh->qhmem.ferr, 6235, "qhull error (qh_memalloc): negative request size (%d). Did int overflow due to high-D?\n", insize); /* WARN64 */
+ qh_errexit(qh, qhmem_ERRmem, NULL, NULL);
+ }
+ if (insize>=0 && insize <= qh->qhmem.LASTsize) {
+ idx= qh->qhmem.indextable[insize];
+ outsize= qh->qhmem.sizetable[idx];
+ qh->qhmem.totshort += outsize;
+ freelistp= qh->qhmem.freelists+idx;
+ if ((object= *freelistp)) {
+ qh->qhmem.cntquick++;
+ qh->qhmem.totfree -= outsize;
+ *freelistp= *((void **)*freelistp); /* replace freelist with next object */
+#ifdef qh_TRACEshort
+ n= qh->qhmem.cntshort+qh->qhmem.cntquick+qh->qhmem.freeshort;
+ if (qh->qhmem.IStracing >= 5)
+ qh_fprintf(qh, qh->qhmem.ferr, 8141, "qh_mem %p n %8d alloc quick: %d bytes (tot %d cnt %d)\n", object, n, outsize, qh->qhmem.totshort, qh->qhmem.cntshort+qh->qhmem.cntquick-qh->qhmem.freeshort);
+#endif
+ return(object);
+ }else {
+ qh->qhmem.cntshort++;
+ if (outsize > qh->qhmem.freesize) {
+ qh->qhmem.totdropped += qh->qhmem.freesize;
+ if (!qh->qhmem.curbuffer)
+ bufsize= qh->qhmem.BUFinit;
+ else
+ bufsize= qh->qhmem.BUFsize;
+ if (!(newbuffer= qh_malloc((size_t)bufsize))) {
+ qh_fprintf(qh, qh->qhmem.ferr, 6080, "qhull error (qh_memalloc): insufficient memory to allocate short memory buffer (%d bytes)\n", bufsize);
+ qh_errexit(qh, qhmem_ERRmem, NULL, NULL);
+ }
+ *((void **)newbuffer)= qh->qhmem.curbuffer; /* prepend newbuffer to curbuffer
+ list. newbuffer!=0 by QH6080 */
+ qh->qhmem.curbuffer= newbuffer;
+ size= (sizeof(void **) + qh->qhmem.ALIGNmask) & ~qh->qhmem.ALIGNmask;
+ qh->qhmem.freemem= (void *)((char *)newbuffer+size);
+ qh->qhmem.freesize= bufsize - size;
+ qh->qhmem.totbuffer += bufsize - size; /* easier to check */
+ /* Periodically test totbuffer. It matches at beginning and exit of every call */
+ n = qh->qhmem.totshort + qh->qhmem.totfree + qh->qhmem.totdropped + qh->qhmem.freesize - outsize;
+ if (qh->qhmem.totbuffer != n) {
+ qh_fprintf(qh, qh->qhmem.ferr, 6212, "qh_memalloc internal error: short totbuffer %d != totshort+totfree... %d\n", qh->qhmem.totbuffer, n);
+ qh_errexit(qh, qhmem_ERRmem, NULL, NULL);
+ }
+ }
+ object= qh->qhmem.freemem;
+ qh->qhmem.freemem= (void *)((char *)qh->qhmem.freemem + outsize);
+ qh->qhmem.freesize -= outsize;
+ qh->qhmem.totunused += outsize - insize;
+#ifdef qh_TRACEshort
+ n= qh->qhmem.cntshort+qh->qhmem.cntquick+qh->qhmem.freeshort;
+ if (qh->qhmem.IStracing >= 5)
+ qh_fprintf(qh, qh->qhmem.ferr, 8140, "qh_mem %p n %8d alloc short: %d bytes (tot %d cnt %d)\n", object, n, outsize, qh->qhmem.totshort, qh->qhmem.cntshort+qh->qhmem.cntquick-qh->qhmem.freeshort);
+#endif
+ return object;
+ }
+ }else { /* long allocation */
+ if (!qh->qhmem.indextable) {
+ qh_fprintf(qh, qh->qhmem.ferr, 6081, "qhull internal error (qh_memalloc): qhmem has not been initialized.\n");
+ qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);
+ }
+ outsize= insize;
+ qh->qhmem.cntlong++;
+ qh->qhmem.totlong += outsize;
+ if (qh->qhmem.maxlong < qh->qhmem.totlong)
+ qh->qhmem.maxlong= qh->qhmem.totlong;
+ if (!(object= qh_malloc((size_t)outsize))) {
+ qh_fprintf(qh, qh->qhmem.ferr, 6082, "qhull error (qh_memalloc): insufficient memory to allocate %d bytes\n", outsize);
+ qh_errexit(qh, qhmem_ERRmem, NULL, NULL);
+ }
+ if (qh->qhmem.IStracing >= 5)
+ qh_fprintf(qh, qh->qhmem.ferr, 8057, "qh_mem %p n %8d alloc long: %d bytes (tot %d cnt %d)\n", object, qh->qhmem.cntlong+qh->qhmem.freelong, outsize, qh->qhmem.totlong, qh->qhmem.cntlong-qh->qhmem.freelong);
+ }
+ return(object);
+} /* memalloc */
+
+
+/*-<a href="qh-mem_r.htm#TOC"
+ >--------------------------------</a><a name="memcheck">-</a>
+
+ qh_memcheck(qh)
+*/
+void qh_memcheck(qhT *qh) {
+ int i, count, totfree= 0;
+ void *object;
+
+ if (!qh) {
+ qh_fprintf_stderr(6243, "qh_memcheck(qh) error: qh is 0. It does not point to a qhT");
+ qh_exit(qhmem_ERRqhull); /* can not use qh_errexit() */
+ }
+ if (qh->qhmem.ferr == 0 || qh->qhmem.IStracing < 0 || qh->qhmem.IStracing > 10 || (((qh->qhmem.ALIGNmask+1) & qh->qhmem.ALIGNmask) != 0)) {
+ qh_fprintf_stderr(6244, "qh_memcheck error: either qh->qhmem is overwritten or qh->qhmem is not initialized. Call qh_mem_new() or qh_new_qhull() before calling qh_mem routines. ferr 0x%x IsTracing %d ALIGNmask 0x%x", qh->qhmem.ferr, qh->qhmem.IStracing, qh->qhmem.ALIGNmask);
+ qh_exit(qhmem_ERRqhull); /* can not use qh_errexit() */
+ }
+ if (qh->qhmem.IStracing != 0)
+ qh_fprintf(qh, qh->qhmem.ferr, 8143, "qh_memcheck: check size of freelists on qh->qhmem\nqh_memcheck: A segmentation fault indicates an overwrite of qh->qhmem\n");
+ for (i=0; i < qh->qhmem.TABLEsize; i++) {
+ count=0;
+ for (object= qh->qhmem.freelists[i]; object; object= *((void **)object))
+ count++;
+ totfree += qh->qhmem.sizetable[i] * count;
+ }
+ if (totfree != qh->qhmem.totfree) {
+ qh_fprintf(qh, qh->qhmem.ferr, 6211, "Qhull internal error (qh_memcheck): totfree %d not equal to freelist total %d\n", qh->qhmem.totfree, totfree);
+ qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);
+ }
+ if (qh->qhmem.IStracing != 0)
+ qh_fprintf(qh, qh->qhmem.ferr, 8144, "qh_memcheck: total size of freelists totfree is the same as qh->qhmem.totfree\n", totfree);
+} /* memcheck */
+
+/*-<a href="qh-mem_r.htm#TOC"
+ >--------------------------------</a><a name="memfree">-</a>
+
+ qh_memfree(qh, object, insize )
+ free up an object of size bytes
+ size is insize from qh_memalloc
+
+ notes:
+ object may be NULL
+ type checking warns if using (void **)object
+ use qh_memfree_() for quick free's of small objects
+
+ design:
+ if size <= qh->qhmem.LASTsize
+ append object to corresponding freelist
+ else
+ call qh_free(object)
+*/
+void qh_memfree(qhT *qh, void *object, int insize) {
+ void **freelistp;
+ int idx, outsize;
+
+ if (!object)
+ return;
+ if (insize <= qh->qhmem.LASTsize) {
+ qh->qhmem.freeshort++;
+ idx= qh->qhmem.indextable[insize];
+ outsize= qh->qhmem.sizetable[idx];
+ qh->qhmem.totfree += outsize;
+ qh->qhmem.totshort -= outsize;
+ freelistp= qh->qhmem.freelists + idx;
+ *((void **)object)= *freelistp;
+ *freelistp= object;
+#ifdef qh_TRACEshort
+ idx= qh->qhmem.cntshort+qh->qhmem.cntquick+qh->qhmem.freeshort;
+ if (qh->qhmem.IStracing >= 5)
+ qh_fprintf(qh, qh->qhmem.ferr, 8142, "qh_mem %p n %8d free short: %d bytes (tot %d cnt %d)\n", object, idx, outsize, qh->qhmem.totshort, qh->qhmem.cntshort+qh->qhmem.cntquick-qh->qhmem.freeshort);
+#endif
+ }else {
+ qh->qhmem.freelong++;
+ qh->qhmem.totlong -= insize;
+ if (qh->qhmem.IStracing >= 5)
+ qh_fprintf(qh, qh->qhmem.ferr, 8058, "qh_mem %p n %8d free long: %d bytes (tot %d cnt %d)\n", object, qh->qhmem.cntlong+qh->qhmem.freelong, insize, qh->qhmem.totlong, qh->qhmem.cntlong-qh->qhmem.freelong);
+ qh_free(object);
+ }
+} /* memfree */
+
+
+/*-<a href="qh-mem_r.htm#TOC"
+ >-------------------------------</a><a name="memfreeshort">-</a>
+
+ qh_memfreeshort(qh, curlong, totlong )
+ frees up all short and qhmem memory allocations
+
+ returns:
+ number and size of current long allocations
+
+ notes:
+ if qh_NOmem (qh_malloc() for all allocations),
+ short objects (e.g., facetT) are not recovered.
+ use qh_freeqhull(qh, qh_ALL) instead.
+
+ see:
+ qh_freeqhull(qh, allMem)
+ qh_memtotal(qh, curlong, totlong, curshort, totshort, maxlong, totbuffer);
+*/
+void qh_memfreeshort(qhT *qh, int *curlong, int *totlong) {
+ void *buffer, *nextbuffer;
+ FILE *ferr;
+
+ *curlong= qh->qhmem.cntlong - qh->qhmem.freelong;
+ *totlong= qh->qhmem.totlong;
+ for (buffer= qh->qhmem.curbuffer; buffer; buffer= nextbuffer) {
+ nextbuffer= *((void **) buffer);
+ qh_free(buffer);
+ }
+ qh->qhmem.curbuffer= NULL;
+ if (qh->qhmem.LASTsize) {
+ qh_free(qh->qhmem.indextable);
+ qh_free(qh->qhmem.freelists);
+ qh_free(qh->qhmem.sizetable);
+ }
+ ferr= qh->qhmem.ferr;
+ memset((char *)&qh->qhmem, 0, sizeof(qh->qhmem)); /* every field is 0, FALSE, NULL */
+ qh->qhmem.ferr= ferr;
+} /* memfreeshort */
+
+
+/*-<a href="qh-mem_r.htm#TOC"
+ >--------------------------------</a><a name="meminit">-</a>
+
+ qh_meminit(qh, ferr )
+ initialize qhmem and test sizeof( void*)
+ Does not throw errors. qh_exit on failure
+*/
+void qh_meminit(qhT *qh, FILE *ferr) {
+
+ memset((char *)&qh->qhmem, 0, sizeof(qh->qhmem)); /* every field is 0, FALSE, NULL */
+ if (ferr)
+ qh->qhmem.ferr= ferr;
+ else
+ qh->qhmem.ferr= stderr;
+ if (sizeof(void*) < sizeof(int)) {
+ qh_fprintf(qh, qh->qhmem.ferr, 6083, "qhull internal error (qh_meminit): sizeof(void*) %d < sizeof(int) %d. qset.c will not work\n", (int)sizeof(void*), (int)sizeof(int));
+ qh_exit(qhmem_ERRqhull); /* can not use qh_errexit() */
+ }
+ if (sizeof(void*) > sizeof(ptr_intT)) {
+ qh_fprintf(qh, qh->qhmem.ferr, 6084, "qhull internal error (qh_meminit): sizeof(void*) %d > sizeof(ptr_intT) %d. Change ptr_intT in mem.h to 'long long'\n", (int)sizeof(void*), (int)sizeof(ptr_intT));
+ qh_exit(qhmem_ERRqhull); /* can not use qh_errexit() */
+ }
+ qh_memcheck(qh);
+} /* meminit */
+
+/*-<a href="qh-mem_r.htm#TOC"
+ >-------------------------------</a><a name="meminitbuffers">-</a>
+
+ qh_meminitbuffers(qh, tracelevel, alignment, numsizes, bufsize, bufinit )
+ initialize qhmem
+ if tracelevel >= 5, trace memory allocations
+ alignment= desired address alignment for memory allocations
+ numsizes= number of freelists
+ bufsize= size of additional memory buffers for short allocations
+ bufinit= size of initial memory buffer for short allocations
+*/
+void qh_meminitbuffers(qhT *qh, int tracelevel, int alignment, int numsizes, int bufsize, int bufinit) {
+
+ qh->qhmem.IStracing= tracelevel;
+ qh->qhmem.NUMsizes= numsizes;
+ qh->qhmem.BUFsize= bufsize;
+ qh->qhmem.BUFinit= bufinit;
+ qh->qhmem.ALIGNmask= alignment-1;
+ if (qh->qhmem.ALIGNmask & ~qh->qhmem.ALIGNmask) {
+ qh_fprintf(qh, qh->qhmem.ferr, 6085, "qhull internal error (qh_meminit): memory alignment %d is not a power of 2\n", alignment);
+ qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);
+ }
+ qh->qhmem.sizetable= (int *) calloc((size_t)numsizes, sizeof(int));
+ qh->qhmem.freelists= (void **) calloc((size_t)numsizes, sizeof(void *));
+ if (!qh->qhmem.sizetable || !qh->qhmem.freelists) {
+ qh_fprintf(qh, qh->qhmem.ferr, 6086, "qhull error (qh_meminit): insufficient memory\n");
+ qh_errexit(qh, qhmem_ERRmem, NULL, NULL);
+ }
+ if (qh->qhmem.IStracing >= 1)
+ qh_fprintf(qh, qh->qhmem.ferr, 8059, "qh_meminitbuffers: memory initialized with alignment %d\n", alignment);
+} /* meminitbuffers */
+
+/*-<a href="qh-mem_r.htm#TOC"
+ >-------------------------------</a><a name="memsetup">-</a>
+
+ qh_memsetup(qh)
+ set up memory after running memsize()
+*/
+void qh_memsetup(qhT *qh) {
+ int k,i;
+
+ qsort(qh->qhmem.sizetable, (size_t)qh->qhmem.TABLEsize, sizeof(int), qh_intcompare);
+ qh->qhmem.LASTsize= qh->qhmem.sizetable[qh->qhmem.TABLEsize-1];
+ if(qh->qhmem.LASTsize >= qh->qhmem.BUFsize || qh->qhmem.LASTsize >= qh->qhmem.BUFinit) {
+ qh_fprintf(qh, qh->qhmem.ferr, 6087, "qhull error (qh_memsetup): largest mem size %d is >= buffer size %d or initial buffer size %d\n",
+ qh->qhmem.LASTsize, qh->qhmem.BUFsize, qh->qhmem.BUFinit);
+ qh_errexit(qh, qhmem_ERRmem, NULL, NULL);
+ }
+ if (!(qh->qhmem.indextable= (int *)qh_malloc((qh->qhmem.LASTsize+1) * sizeof(int)))) {
+ qh_fprintf(qh, qh->qhmem.ferr, 6088, "qhull error (qh_memsetup): insufficient memory\n");
+ qh_errexit(qh, qhmem_ERRmem, NULL, NULL);
+ }
+ for (k=qh->qhmem.LASTsize+1; k--; )
+ qh->qhmem.indextable[k]= k;
+ i= 0;
+ for (k=0; k <= qh->qhmem.LASTsize; k++) {
+ if (qh->qhmem.indextable[k] <= qh->qhmem.sizetable[i])
+ qh->qhmem.indextable[k]= i;
+ else
+ qh->qhmem.indextable[k]= ++i;
+ }
+} /* memsetup */
+
+/*-<a href="qh-mem_r.htm#TOC"
+ >-------------------------------</a><a name="memsize">-</a>
+
+ qh_memsize(qh, size )
+ define a free list for this size
+*/
+void qh_memsize(qhT *qh, int size) {
+ int k;
+
+ if(qh->qhmem.LASTsize) {
+ qh_fprintf(qh, qh->qhmem.ferr, 6089, "qhull error (qh_memsize): called after qhmem_setup\n");
+ qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);
+ }
+ size= (size + qh->qhmem.ALIGNmask) & ~qh->qhmem.ALIGNmask;
+ for (k=qh->qhmem.TABLEsize; k--; ) {
+ if (qh->qhmem.sizetable[k] == size)
+ return;
+ }
+ if (qh->qhmem.TABLEsize < qh->qhmem.NUMsizes)
+ qh->qhmem.sizetable[qh->qhmem.TABLEsize++]= size;
+ else
+ qh_fprintf(qh, qh->qhmem.ferr, 7060, "qhull warning (memsize): free list table has room for only %d sizes\n", qh->qhmem.NUMsizes);
+} /* memsize */
+
+
+/*-<a href="qh-mem_r.htm#TOC"
+ >-------------------------------</a><a name="memstatistics">-</a>
+
+ qh_memstatistics(qh, fp )
+ print out memory statistics
+
+ Verifies that qh->qhmem.totfree == sum of freelists
+*/
+void qh_memstatistics(qhT *qh, FILE *fp) {
+ int i;
+ int count;
+ void *object;
+
+ qh_memcheck(qh);
+ qh_fprintf(qh, fp, 9278, "\nmemory statistics:\n\
+%7d quick allocations\n\
+%7d short allocations\n\
+%7d long allocations\n\
+%7d short frees\n\
+%7d long frees\n\
+%7d bytes of short memory in use\n\
+%7d bytes of short memory in freelists\n\
+%7d bytes of dropped short memory\n\
+%7d bytes of unused short memory (estimated)\n\
+%7d bytes of long memory allocated (max, except for input)\n\
+%7d bytes of long memory in use (in %d pieces)\n\
+%7d bytes of short memory buffers (minus links)\n\
+%7d bytes per short memory buffer (initially %d bytes)\n",
+ qh->qhmem.cntquick, qh->qhmem.cntshort, qh->qhmem.cntlong,
+ qh->qhmem.freeshort, qh->qhmem.freelong,
+ qh->qhmem.totshort, qh->qhmem.totfree,
+ qh->qhmem.totdropped + qh->qhmem.freesize, qh->qhmem.totunused,
+ qh->qhmem.maxlong, qh->qhmem.totlong, qh->qhmem.cntlong - qh->qhmem.freelong,
+ qh->qhmem.totbuffer, qh->qhmem.BUFsize, qh->qhmem.BUFinit);
+ if (qh->qhmem.cntlarger) {
+ qh_fprintf(qh, fp, 9279, "%7d calls to qh_setlarger\n%7.2g average copy size\n",
+ qh->qhmem.cntlarger, ((float)qh->qhmem.totlarger)/(float)qh->qhmem.cntlarger);
+ qh_fprintf(qh, fp, 9280, " freelists(bytes->count):");
+ }
+ for (i=0; i < qh->qhmem.TABLEsize; i++) {
+ count=0;
+ for (object= qh->qhmem.freelists[i]; object; object= *((void **)object))
+ count++;
+ qh_fprintf(qh, fp, 9281, " %d->%d", qh->qhmem.sizetable[i], count);
+ }
+ qh_fprintf(qh, fp, 9282, "\n\n");
+} /* memstatistics */
+
+
+/*-<a href="qh-mem_r.htm#TOC"
+ >-------------------------------</a><a name="NOmem">-</a>
+
+ qh_NOmem
+ turn off quick-fit memory allocation
+
+ notes:
+ uses qh_malloc() and qh_free() instead
+*/
+#else /* qh_NOmem */
+
+void *qh_memalloc(qhT *qh, int insize) {
+ void *object;
+
+ if (!(object= qh_malloc((size_t)insize))) {
+ qh_fprintf(qh, qh->qhmem.ferr, 6090, "qhull error (qh_memalloc): insufficient memory\n");
+ qh_errexit(qh, qhmem_ERRmem, NULL, NULL);
+ }
+ qh->qhmem.cntlong++;
+ qh->qhmem.totlong += insize;
+ if (qh->qhmem.maxlong < qh->qhmem.totlong)
+ qh->qhmem.maxlong= qh->qhmem.totlong;
+ if (qh->qhmem.IStracing >= 5)
+ qh_fprintf(qh, qh->qhmem.ferr, 8060, "qh_mem %p n %8d alloc long: %d bytes (tot %d cnt %d)\n", object, qh->qhmem.cntlong+qh->qhmem.freelong, insize, qh->qhmem.totlong, qh->qhmem.cntlong-qh->qhmem.freelong);
+ return object;
+}
+
+void qh_memfree(qhT *qh, void *object, int insize) {
+
+ if (!object)
+ return;
+ qh_free(object);
+ qh->qhmem.freelong++;
+ qh->qhmem.totlong -= insize;
+ if (qh->qhmem.IStracing >= 5)
+ qh_fprintf(qh, qh->qhmem.ferr, 8061, "qh_mem %p n %8d free long: %d bytes (tot %d cnt %d)\n", object, qh->qhmem.cntlong+qh->qhmem.freelong, insize, qh->qhmem.totlong, qh->qhmem.cntlong-qh->qhmem.freelong);
+}
+
+void qh_memfreeshort(qhT *qh, int *curlong, int *totlong) {
+ *totlong= qh->qhmem.totlong;
+ *curlong= qh->qhmem.cntlong - qh->qhmem.freelong;
+ memset((char *)&qh->qhmem, 0, sizeof(qh->qhmem)); /* every field is 0, FALSE, NULL */
+}
+
+void qh_meminit(qhT *qh, FILE *ferr) {
+
+ memset((char *)&qh->qhmem, 0, sizeof(qh->qhmem)); /* every field is 0, FALSE, NULL */
+ if (ferr)
+ qh->qhmem.ferr= ferr;
+ else
+ qh->qhmem.ferr= stderr;
+ if (sizeof(void*) < sizeof(int)) {
+ qh_fprintf(qh, qh->qhmem.ferr, 6091, "qhull internal error (qh_meminit): sizeof(void*) %d < sizeof(int) %d. qset.c will not work\n", (int)sizeof(void*), (int)sizeof(int));
+ qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);
+ }
+}
+
+void qh_meminitbuffers(qhT *qh, int tracelevel, int alignment, int numsizes, int bufsize, int bufinit) {
+
+ qh->qhmem.IStracing= tracelevel;
+}
+
+void qh_memsetup(qhT *qh) {
+
+}
+
+void qh_memsize(qhT *qh, int size) {
+
+}
+
+void qh_memstatistics(qhT *qh, FILE *fp) {
+
+ qh_fprintf(qh, fp, 9409, "\nmemory statistics:\n\
+%7d long allocations\n\
+%7d long frees\n\
+%7d bytes of long memory allocated (max, except for input)\n\
+%7d bytes of long memory in use (in %d pieces)\n",
+ qh->qhmem.cntlong,
+ qh->qhmem.freelong,
+ qh->qhmem.maxlong, qh->qhmem.totlong, qh->qhmem.cntlong - qh->qhmem.freelong);
+}
+
+#endif /* qh_NOmem */
+
+/*-<a href="qh-mem_r.htm#TOC"
+>-------------------------------</a><a name="memtotlong">-</a>
+
+ qh_memtotal(qh, totlong, curlong, totshort, curshort, maxlong, totbuffer )
+ Return the total, allocated long and short memory
+
+ returns:
+ Returns the total current bytes of long and short allocations
+ Returns the current count of long and short allocations
+ Returns the maximum long memory and total short buffer (minus one link per buffer)
+ Does not error (for deprecated UsingLibQhull.cpp (libqhullpcpp))
+*/
+void qh_memtotal(qhT *qh, int *totlong, int *curlong, int *totshort, int *curshort, int *maxlong, int *totbuffer) {
+ *totlong= qh->qhmem.totlong;
+ *curlong= qh->qhmem.cntlong - qh->qhmem.freelong;
+ *totshort= qh->qhmem.totshort;
+ *curshort= qh->qhmem.cntshort + qh->qhmem.cntquick - qh->qhmem.freeshort;
+ *maxlong= qh->qhmem.maxlong;
+ *totbuffer= qh->qhmem.totbuffer;
+} /* memtotlong */
+
diff --git a/xs/src/qhull/src/libqhull_r/mem_r.h b/xs/src/qhull/src/libqhull_r/mem_r.h
new file mode 100644
index 000000000..25b551333
--- /dev/null
+++ b/xs/src/qhull/src/libqhull_r/mem_r.h
@@ -0,0 +1,234 @@
+/*<html><pre> -<a href="qh-mem_r.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ mem_r.h
+ prototypes for memory management functions
+
+ see qh-mem_r.htm, mem_r.c and qset_r.h
+
+ for error handling, writes message and calls
+ qh_errexit(qhT *qh, qhmem_ERRmem, NULL, NULL) if insufficient memory
+ and
+ qh_errexit(qhT *qh, qhmem_ERRqhull, NULL, NULL) otherwise
+
+ Copyright (c) 1993-2015 The Geometry Center.
+ $Id: //main/2015/qhull/src/libqhull_r/mem_r.h#4 $$Change: 2079 $
+ $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
+*/
+
+#ifndef qhDEFmem
+#define qhDEFmem 1
+
+#include <stdio.h>
+
+#ifndef DEFsetT
+#define DEFsetT 1
+typedef struct setT setT; /* defined in qset_r.h */
+#endif
+
+#ifndef DEFqhT
+#define DEFqhT 1
+typedef struct qhT qhT; /* defined in libqhull_r.h */
+#endif
+
+/*-<a href="qh-mem_r.htm#TOC"
+ >-------------------------------</a><a name="NOmem">-</a>
+
+ qh_NOmem
+ turn off quick-fit memory allocation
+
+ notes:
+ mem_r.c implements Quickfit memory allocation for about 20% time
+ savings. If it fails on your machine, try to locate the
+ problem, and send the answer to qhull@qhull.org. If this can
+ not be done, define qh_NOmem to use malloc/free instead.
+
+ #define qh_NOmem
+*/
+
+/*-<a href="qh-mem_r.htm#TOC"
+>-------------------------------</a><a name="TRACEshort">-</a>
+
+qh_TRACEshort
+Trace short and quick memory allocations at T5
+
+*/
+#define qh_TRACEshort
+
+/*-------------------------------------------
+ to avoid bus errors, memory allocation must consider alignment requirements.
+ malloc() automatically takes care of alignment. Since mem_r.c manages
+ its own memory, we need to explicitly specify alignment in
+ qh_meminitbuffers().
+
+ A safe choice is sizeof(double). sizeof(float) may be used if doubles
+ do not occur in data structures and pointers are the same size. Be careful
+ of machines (e.g., DEC Alpha) with large pointers. If gcc is available,
+ use __alignof__(double) or fmax_(__alignof__(float), __alignof__(void *)).
+
+ see <a href="user.h#MEMalign">qh_MEMalign</a> in user.h for qhull's alignment
+*/
+
+#define qhmem_ERRmem 4 /* matches qh_ERRmem in libqhull_r.h */
+#define qhmem_ERRqhull 5 /* matches qh_ERRqhull in libqhull_r.h */
+
+/*-<a href="qh-mem_r.htm#TOC"
+ >--------------------------------</a><a name="ptr_intT">-</a>
+
+ ptr_intT
+ for casting a void * to an integer-type that holds a pointer
+ Used for integer expressions (e.g., computing qh_gethash() in poly_r.c)
+
+ notes:
+ WARN64 -- these notes indicate 64-bit issues
+ On 64-bit machines, a pointer may be larger than an 'int'.
+ qh_meminit()/mem_r.c checks that 'ptr_intT' holds a 'void*'
+ ptr_intT is typically a signed value, but not necessarily so
+ size_t is typically unsigned, but should match the parameter type
+ Qhull uses int instead of size_t except for system calls such as malloc, qsort, qh_malloc, etc.
+ This matches Qt convention and is easier to work with.
+*/
+#if (defined(__MINGW64__)) && defined(_WIN64)
+typedef long long ptr_intT;
+#elif (_MSC_VER) && defined(_WIN64)
+typedef long long ptr_intT;
+#else
+typedef long ptr_intT;
+#endif
+
+/*-<a href="qh-mem_r.htm#TOC"
+ >--------------------------------</a><a name="qhmemT">-</a>
+
+ qhmemT
+ global memory structure for mem_r.c
+
+ notes:
+ users should ignore qhmem except for writing extensions
+ qhmem is allocated in mem_r.c
+
+ qhmem could be swapable like qh and qhstat, but then
+ multiple qh's and qhmem's would need to keep in synch.
+ A swapable qhmem would also waste memory buffers. As long
+ as memory operations are atomic, there is no problem with
+ multiple qh structures being active at the same time.
+ If you need separate address spaces, you can swap the
+ contents of qh->qhmem.
+*/
+typedef struct qhmemT qhmemT;
+
+/* Update qhmem in mem_r.c if add or remove fields */
+struct qhmemT { /* global memory management variables */
+ int BUFsize; /* size of memory allocation buffer */
+ int BUFinit; /* initial size of memory allocation buffer */
+ int TABLEsize; /* actual number of sizes in free list table */
+ int NUMsizes; /* maximum number of sizes in free list table */
+ int LASTsize; /* last size in free list table */
+ int ALIGNmask; /* worst-case alignment, must be 2^n-1 */
+ void **freelists; /* free list table, linked by offset 0 */
+ int *sizetable; /* size of each freelist */
+ int *indextable; /* size->index table */
+ void *curbuffer; /* current buffer, linked by offset 0 */
+ void *freemem; /* free memory in curbuffer */
+ int freesize; /* size of freemem in bytes */
+ setT *tempstack; /* stack of temporary memory, managed by users */
+ FILE *ferr; /* file for reporting errors when 'qh' may be undefined */
+ int IStracing; /* =5 if tracing memory allocations */
+ int cntquick; /* count of quick allocations */
+ /* Note: removing statistics doesn't effect speed */
+ int cntshort; /* count of short allocations */
+ int cntlong; /* count of long allocations */
+ int freeshort; /* count of short memfrees */
+ int freelong; /* count of long memfrees */
+ int totbuffer; /* total short memory buffers minus buffer links */
+ int totdropped; /* total dropped memory at end of short memory buffers (e.g., freesize) */
+ int totfree; /* total size of free, short memory on freelists */
+ int totlong; /* total size of long memory in use */
+ int maxlong; /* maximum totlong */
+ int totshort; /* total size of short memory in use */
+ int totunused; /* total unused short memory (estimated, short size - request size of first allocations) */
+ int cntlarger; /* count of setlarger's */
+ int totlarger; /* total copied by setlarger */
+};
+
+
+/*==================== -macros ====================*/
+
+/*-<a href="qh-mem_r.htm#TOC"
+ >--------------------------------</a><a name="memalloc_">-</a>
+
+ qh_memalloc_(qh, insize, freelistp, object, type)
+ returns object of size bytes
+ assumes size<=qh->qhmem.LASTsize and void **freelistp is a temp
+*/
+
+#if defined qh_NOmem
+#define qh_memalloc_(qh, insize, freelistp, object, type) {\
+ object= (type*)qh_memalloc(qh, insize); }
+#elif defined qh_TRACEshort
+#define qh_memalloc_(qh, insize, freelistp, object, type) {\
+ freelistp= NULL; /* Avoid warnings */ \
+ object= (type*)qh_memalloc(qh, insize); }
+#else /* !qh_NOmem */
+
+#define qh_memalloc_(qh, insize, freelistp, object, type) {\
+ freelistp= qh->qhmem.freelists + qh->qhmem.indextable[insize];\
+ if ((object= (type*)*freelistp)) {\
+ qh->qhmem.totshort += qh->qhmem.sizetable[qh->qhmem.indextable[insize]]; \
+ qh->qhmem.totfree -= qh->qhmem.sizetable[qh->qhmem.indextable[insize]]; \
+ qh->qhmem.cntquick++; \
+ *freelistp= *((void **)*freelistp);\
+ }else object= (type*)qh_memalloc(qh, insize);}
+#endif
+
+/*-<a href="qh-mem_r.htm#TOC"
+ >--------------------------------</a><a name="memfree_">-</a>
+
+ qh_memfree_(qh, object, insize, freelistp)
+ free up an object
+
+ notes:
+ object may be NULL
+ assumes size<=qh->qhmem.LASTsize and void **freelistp is a temp
+*/
+#if defined qh_NOmem
+#define qh_memfree_(qh, object, insize, freelistp) {\
+ qh_memfree(qh, object, insize); }
+#elif defined qh_TRACEshort
+#define qh_memfree_(qh, object, insize, freelistp) {\
+ freelistp= NULL; /* Avoid warnings */ \
+ qh_memfree(qh, object, insize); }
+#else /* !qh_NOmem */
+
+#define qh_memfree_(qh, object, insize, freelistp) {\
+ if (object) { \
+ qh->qhmem.freeshort++;\
+ freelistp= qh->qhmem.freelists + qh->qhmem.indextable[insize];\
+ qh->qhmem.totshort -= qh->qhmem.sizetable[qh->qhmem.indextable[insize]]; \
+ qh->qhmem.totfree += qh->qhmem.sizetable[qh->qhmem.indextable[insize]]; \
+ *((void **)object)= *freelistp;\
+ *freelistp= object;}}
+#endif
+
+/*=============== prototypes in alphabetical order ============*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void *qh_memalloc(qhT *qh, int insize);
+void qh_memcheck(qhT *qh);
+void qh_memfree(qhT *qh, void *object, int insize);
+void qh_memfreeshort(qhT *qh, int *curlong, int *totlong);
+void qh_meminit(qhT *qh, FILE *ferr);
+void qh_meminitbuffers(qhT *qh, int tracelevel, int alignment, int numsizes,
+ int bufsize, int bufinit);
+void qh_memsetup(qhT *qh);
+void qh_memsize(qhT *qh, int size);
+void qh_memstatistics(qhT *qh, FILE *fp);
+void qh_memtotal(qhT *qh, int *totlong, int *curlong, int *totshort, int *curshort, int *maxlong, int *totbuffer);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* qhDEFmem */
diff --git a/xs/src/qhull/src/libqhull_r/merge_r.c b/xs/src/qhull/src/libqhull_r/merge_r.c
new file mode 100644
index 000000000..e5823de8d
--- /dev/null
+++ b/xs/src/qhull/src/libqhull_r/merge_r.c
@@ -0,0 +1,3627 @@
+/*<html><pre> -<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ merge_r.c
+ merges non-convex facets
+
+ see qh-merge_r.htm and merge_r.h
+
+ other modules call qh_premerge() and qh_postmerge()
+
+ the user may call qh_postmerge() to perform additional merges.
+
+ To remove deleted facets and vertices (qhull() in libqhull_r.c):
+ qh_partitionvisible(qh, !qh_ALL, &numoutside); // visible_list, newfacet_list
+ qh_deletevisible(); // qh.visible_list
+ qh_resetlists(qh, False, qh_RESETvisible); // qh.visible_list newvertex_list newfacet_list
+
+ assumes qh.CENTERtype= centrum
+
+ merges occur in qh_mergefacet and in qh_mergecycle
+ vertex->neighbors not set until the first merge occurs
+
+ Copyright (c) 1993-2015 C.B. Barber.
+ $Id: //main/2015/qhull/src/libqhull_r/merge_r.c#5 $$Change: 2064 $
+ $DateTime: 2016/01/18 12:36:08 $$Author: bbarber $
+*/
+
+#include "qhull_ra.h"
+
+#ifndef qh_NOmerge
+
+/*===== functions(alphabetical after premerge and postmerge) ======*/
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="premerge">-</a>
+
+ qh_premerge(qh, apex, maxcentrum )
+ pre-merge nonconvex facets in qh.newfacet_list for apex
+ maxcentrum defines coplanar and concave (qh_test_appendmerge)
+
+ returns:
+ deleted facets added to qh.visible_list with facet->visible set
+
+ notes:
+ uses globals, qh.MERGEexact, qh.PREmerge
+
+ design:
+ mark duplicate ridges in qh.newfacet_list
+ merge facet cycles in qh.newfacet_list
+ merge duplicate ridges and concave facets in qh.newfacet_list
+ check merged facet cycles for degenerate and redundant facets
+ merge degenerate and redundant facets
+ collect coplanar and concave facets
+ merge concave, coplanar, degenerate, and redundant facets
+*/
+void qh_premerge(qhT *qh, vertexT *apex, realT maxcentrum, realT maxangle) {
+ boolT othermerge= False;
+ facetT *newfacet;
+
+ if (qh->ZEROcentrum && qh_checkzero(qh, !qh_ALL))
+ return;
+ trace2((qh, qh->ferr, 2008, "qh_premerge: premerge centrum %2.2g angle %2.2g for apex v%d facetlist f%d\n",
+ maxcentrum, maxangle, apex->id, getid_(qh->newfacet_list)));
+ if (qh->IStracing >= 4 && qh->num_facets < 50)
+ qh_printlists(qh);
+ qh->centrum_radius= maxcentrum;
+ qh->cos_max= maxangle;
+ qh->degen_mergeset= qh_settemp(qh, qh->TEMPsize);
+ qh->facet_mergeset= qh_settemp(qh, qh->TEMPsize);
+ if (qh->hull_dim >=3) {
+ qh_mark_dupridges(qh, qh->newfacet_list); /* facet_mergeset */
+ qh_mergecycle_all(qh, qh->newfacet_list, &othermerge);
+ qh_forcedmerges(qh, &othermerge /* qh->facet_mergeset */);
+ FORALLnew_facets { /* test samecycle merges */
+ if (!newfacet->simplicial && !newfacet->mergeridge)
+ qh_degen_redundant_neighbors(qh, newfacet, NULL);
+ }
+ if (qh_merge_degenredundant(qh))
+ othermerge= True;
+ }else /* qh->hull_dim == 2 */
+ qh_mergecycle_all(qh, qh->newfacet_list, &othermerge);
+ qh_flippedmerges(qh, qh->newfacet_list, &othermerge);
+ if (!qh->MERGEexact || zzval_(Ztotmerge)) {
+ zinc_(Zpremergetot);
+ qh->POSTmerging= False;
+ qh_getmergeset_initial(qh, qh->newfacet_list);
+ qh_all_merges(qh, othermerge, False);
+ }
+ qh_settempfree(qh, &qh->facet_mergeset);
+ qh_settempfree(qh, &qh->degen_mergeset);
+} /* premerge */
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="postmerge">-</a>
+
+ qh_postmerge(qh, reason, maxcentrum, maxangle, vneighbors )
+ post-merge nonconvex facets as defined by maxcentrum and maxangle
+ 'reason' is for reporting progress
+ if vneighbors,
+ calls qh_test_vneighbors at end of qh_all_merge
+ if firstmerge,
+ calls qh_reducevertices before qh_getmergeset
+
+ returns:
+ if first call (qh.visible_list != qh.facet_list),
+ builds qh.facet_newlist, qh.newvertex_list
+ deleted facets added to qh.visible_list with facet->visible
+ qh.visible_list == qh.facet_list
+
+ notes:
+
+
+ design:
+ if first call
+ set qh.visible_list and qh.newfacet_list to qh.facet_list
+ add all facets to qh.newfacet_list
+ mark non-simplicial facets, facet->newmerge
+ set qh.newvertext_list to qh.vertex_list
+ add all vertices to qh.newvertex_list
+ if a pre-merge occured
+ set vertex->delridge {will retest the ridge}
+ if qh.MERGEexact
+ call qh_reducevertices()
+ if no pre-merging
+ merge flipped facets
+ determine non-convex facets
+ merge all non-convex facets
+*/
+void qh_postmerge(qhT *qh, const char *reason, realT maxcentrum, realT maxangle,
+ boolT vneighbors) {
+ facetT *newfacet;
+ boolT othermerges= False;
+ vertexT *vertex;
+
+ if (qh->REPORTfreq || qh->IStracing) {
+ qh_buildtracing(qh, NULL, NULL);
+ qh_printsummary(qh, qh->ferr);
+ if (qh->PRINTstatistics)
+ qh_printallstatistics(qh, qh->ferr, "reason");
+ qh_fprintf(qh, qh->ferr, 8062, "\n%s with 'C%.2g' and 'A%.2g'\n",
+ reason, maxcentrum, maxangle);
+ }
+ trace2((qh, qh->ferr, 2009, "qh_postmerge: postmerge. test vneighbors? %d\n",
+ vneighbors));
+ qh->centrum_radius= maxcentrum;
+ qh->cos_max= maxangle;
+ qh->POSTmerging= True;
+ qh->degen_mergeset= qh_settemp(qh, qh->TEMPsize);
+ qh->facet_mergeset= qh_settemp(qh, qh->TEMPsize);
+ if (qh->visible_list != qh->facet_list) { /* first call */
+ qh->NEWfacets= True;
+ qh->visible_list= qh->newfacet_list= qh->facet_list;
+ FORALLnew_facets {
+ newfacet->newfacet= True;
+ if (!newfacet->simplicial)
+ newfacet->newmerge= True;
+ zinc_(Zpostfacets);
+ }
+ qh->newvertex_list= qh->vertex_list;
+ FORALLvertices
+ vertex->newlist= True;
+ if (qh->VERTEXneighbors) { /* a merge has occurred */
+ FORALLvertices
+ vertex->delridge= True; /* test for redundant, needed? */
+ if (qh->MERGEexact) {
+ if (qh->hull_dim <= qh_DIMreduceBuild)
+ qh_reducevertices(qh); /* was skipped during pre-merging */
+ }
+ }
+ if (!qh->PREmerge && !qh->MERGEexact)
+ qh_flippedmerges(qh, qh->newfacet_list, &othermerges);
+ }
+ qh_getmergeset_initial(qh, qh->newfacet_list);
+ qh_all_merges(qh, False, vneighbors);
+ qh_settempfree(qh, &qh->facet_mergeset);
+ qh_settempfree(qh, &qh->degen_mergeset);
+} /* post_merge */
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="all_merges">-</a>
+
+ qh_all_merges(qh, othermerge, vneighbors )
+ merge all non-convex facets
+
+ set othermerge if already merged facets (for qh_reducevertices)
+ if vneighbors
+ tests vertex neighbors for convexity at end
+ qh.facet_mergeset lists the non-convex ridges in qh_newfacet_list
+ qh.degen_mergeset is defined
+ if qh.MERGEexact && !qh.POSTmerging,
+ does not merge coplanar facets
+
+ returns:
+ deleted facets added to qh.visible_list with facet->visible
+ deleted vertices added qh.delvertex_list with vertex->delvertex
+
+ notes:
+ unless !qh.MERGEindependent,
+ merges facets in independent sets
+ uses qh.newfacet_list as argument since merges call qh_removefacet()
+
+ design:
+ while merges occur
+ for each merge in qh.facet_mergeset
+ unless one of the facets was already merged in this pass
+ merge the facets
+ test merged facets for additional merges
+ add merges to qh.facet_mergeset
+ if vertices record neighboring facets
+ rename redundant vertices
+ update qh.facet_mergeset
+ if vneighbors ??
+ tests vertex neighbors for convexity at end
+*/
+void qh_all_merges(qhT *qh, boolT othermerge, boolT vneighbors) {
+ facetT *facet1, *facet2;
+ mergeT *merge;
+ boolT wasmerge= True, isreduce;
+ void **freelistp; /* used if !qh_NOmem by qh_memfree_() */
+ vertexT *vertex;
+ mergeType mergetype;
+ int numcoplanar=0, numconcave=0, numdegenredun= 0, numnewmerges= 0;
+
+ trace2((qh, qh->ferr, 2010, "qh_all_merges: starting to merge facets beginning from f%d\n",
+ getid_(qh->newfacet_list)));
+ while (True) {
+ wasmerge= False;
+ while (qh_setsize(qh, qh->facet_mergeset)) {
+ while ((merge= (mergeT*)qh_setdellast(qh->facet_mergeset))) {
+ facet1= merge->facet1;
+ facet2= merge->facet2;
+ mergetype= merge->type;
+ qh_memfree_(qh, merge, (int)sizeof(mergeT), freelistp);
+ if (facet1->visible || facet2->visible) /*deleted facet*/
+ continue;
+ if ((facet1->newfacet && !facet1->tested)
+ || (facet2->newfacet && !facet2->tested)) {
+ if (qh->MERGEindependent && mergetype <= MRGanglecoplanar)
+ continue; /* perform independent sets of merges */
+ }
+ qh_merge_nonconvex(qh, facet1, facet2, mergetype);
+ numdegenredun += qh_merge_degenredundant(qh);
+ numnewmerges++;
+ wasmerge= True;
+ if (mergetype == MRGconcave)
+ numconcave++;
+ else /* MRGcoplanar or MRGanglecoplanar */
+ numcoplanar++;
+ } /* while setdellast */
+ if (qh->POSTmerging && qh->hull_dim <= qh_DIMreduceBuild
+ && numnewmerges > qh_MAXnewmerges) {
+ numnewmerges= 0;
+ qh_reducevertices(qh); /* otherwise large post merges too slow */
+ }
+ qh_getmergeset(qh, qh->newfacet_list); /* facet_mergeset */
+ } /* while mergeset */
+ if (qh->VERTEXneighbors) {
+ isreduce= False;
+ if (qh->hull_dim >=4 && qh->POSTmerging) {
+ FORALLvertices
+ vertex->delridge= True;
+ isreduce= True;
+ }
+ if ((wasmerge || othermerge) && (!qh->MERGEexact || qh->POSTmerging)
+ && qh->hull_dim <= qh_DIMreduceBuild) {
+ othermerge= False;
+ isreduce= True;
+ }
+ if (isreduce) {
+ if (qh_reducevertices(qh)) {
+ qh_getmergeset(qh, qh->newfacet_list); /* facet_mergeset */
+ continue;
+ }
+ }
+ }
+ if (vneighbors && qh_test_vneighbors(qh /* qh->newfacet_list */))
+ continue;
+ break;
+ } /* while (True) */
+ if (qh->CHECKfrequently && !qh->MERGEexact) {
+ qh->old_randomdist= qh->RANDOMdist;
+ qh->RANDOMdist= False;
+ qh_checkconvex(qh, qh->newfacet_list, qh_ALGORITHMfault);
+ /* qh_checkconnect(qh); [this is slow and it changes the facet order] */
+ qh->RANDOMdist= qh->old_randomdist;
+ }
+ trace1((qh, qh->ferr, 1009, "qh_all_merges: merged %d coplanar facets %d concave facets and %d degen or redundant facets.\n",
+ numcoplanar, numconcave, numdegenredun));
+ if (qh->IStracing >= 4 && qh->num_facets < 50)
+ qh_printlists(qh);
+} /* all_merges */
+
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="appendmergeset">-</a>
+
+ qh_appendmergeset(qh, facet, neighbor, mergetype, angle )
+ appends an entry to qh.facet_mergeset or qh.degen_mergeset
+
+ angle ignored if NULL or !qh.ANGLEmerge
+
+ returns:
+ merge appended to facet_mergeset or degen_mergeset
+ sets ->degenerate or ->redundant if degen_mergeset
+
+ see:
+ qh_test_appendmerge()
+
+ design:
+ allocate merge entry
+ if regular merge
+ append to qh.facet_mergeset
+ else if degenerate merge and qh.facet_mergeset is all degenerate
+ append to qh.degen_mergeset
+ else if degenerate merge
+ prepend to qh.degen_mergeset
+ else if redundant merge
+ append to qh.degen_mergeset
+*/
+void qh_appendmergeset(qhT *qh, facetT *facet, facetT *neighbor, mergeType mergetype, realT *angle) {
+ mergeT *merge, *lastmerge;
+ void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */
+
+ if (facet->redundant)
+ return;
+ if (facet->degenerate && mergetype == MRGdegen)
+ return;
+ qh_memalloc_(qh, (int)sizeof(mergeT), freelistp, merge, mergeT);
+ merge->facet1= facet;
+ merge->facet2= neighbor;
+ merge->type= mergetype;
+ if (angle && qh->ANGLEmerge)
+ merge->angle= *angle;
+ if (mergetype < MRGdegen)
+ qh_setappend(qh, &(qh->facet_mergeset), merge);
+ else if (mergetype == MRGdegen) {
+ facet->degenerate= True;
+ if (!(lastmerge= (mergeT*)qh_setlast(qh->degen_mergeset))
+ || lastmerge->type == MRGdegen)
+ qh_setappend(qh, &(qh->degen_mergeset), merge);
+ else
+ qh_setaddnth(qh, &(qh->degen_mergeset), 0, merge);
+ }else if (mergetype == MRGredundant) {
+ facet->redundant= True;
+ qh_setappend(qh, &(qh->degen_mergeset), merge);
+ }else /* mergetype == MRGmirror */ {
+ if (facet->redundant || neighbor->redundant) {
+ qh_fprintf(qh, qh->ferr, 6092, "qhull error (qh_appendmergeset): facet f%d or f%d is already a mirrored facet\n",
+ facet->id, neighbor->id);
+ qh_errexit2(qh, qh_ERRqhull, facet, neighbor);
+ }
+ if (!qh_setequal(facet->vertices, neighbor->vertices)) {
+ qh_fprintf(qh, qh->ferr, 6093, "qhull error (qh_appendmergeset): mirrored facets f%d and f%d do not have the same vertices\n",
+ facet->id, neighbor->id);
+ qh_errexit2(qh, qh_ERRqhull, facet, neighbor);
+ }
+ facet->redundant= True;
+ neighbor->redundant= True;
+ qh_setappend(qh, &(qh->degen_mergeset), merge);
+ }
+} /* appendmergeset */
+
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="basevertices">-</a>
+
+ qh_basevertices(qh, samecycle )
+ return temporary set of base vertices for samecycle
+ samecycle is first facet in the cycle
+ assumes apex is SETfirst_( samecycle->vertices )
+
+ returns:
+ vertices(settemp)
+ all ->seen are cleared
+
+ notes:
+ uses qh_vertex_visit;
+
+ design:
+ for each facet in samecycle
+ for each unseen vertex in facet->vertices
+ append to result
+*/
+setT *qh_basevertices(qhT *qh, facetT *samecycle) {
+ facetT *same;
+ vertexT *apex, *vertex, **vertexp;
+ setT *vertices= qh_settemp(qh, qh->TEMPsize);
+
+ apex= SETfirstt_(samecycle->vertices, vertexT);
+ apex->visitid= ++qh->vertex_visit;
+ FORALLsame_cycle_(samecycle) {
+ if (same->mergeridge)
+ continue;
+ FOREACHvertex_(same->vertices) {
+ if (vertex->visitid != qh->vertex_visit) {
+ qh_setappend(qh, &vertices, vertex);
+ vertex->visitid= qh->vertex_visit;
+ vertex->seen= False;
+ }
+ }
+ }
+ trace4((qh, qh->ferr, 4019, "qh_basevertices: found %d vertices\n",
+ qh_setsize(qh, vertices)));
+ return vertices;
+} /* basevertices */
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="checkconnect">-</a>
+
+ qh_checkconnect(qh)
+ check that new facets are connected
+ new facets are on qh.newfacet_list
+
+ notes:
+ this is slow and it changes the order of the facets
+ uses qh.visit_id
+
+ design:
+ move first new facet to end of qh.facet_list
+ for all newly appended facets
+ append unvisited neighbors to end of qh.facet_list
+ for all new facets
+ report error if unvisited
+*/
+void qh_checkconnect(qhT *qh /* qh->newfacet_list */) {
+ facetT *facet, *newfacet, *errfacet= NULL, *neighbor, **neighborp;
+
+ facet= qh->newfacet_list;
+ qh_removefacet(qh, facet);
+ qh_appendfacet(qh, facet);
+ facet->visitid= ++qh->visit_id;
+ FORALLfacet_(facet) {
+ FOREACHneighbor_(facet) {
+ if (neighbor->visitid != qh->visit_id) {
+ qh_removefacet(qh, neighbor);
+ qh_appendfacet(qh, neighbor);
+ neighbor->visitid= qh->visit_id;
+ }
+ }
+ }
+ FORALLnew_facets {
+ if (newfacet->visitid == qh->visit_id)
+ break;
+ qh_fprintf(qh, qh->ferr, 6094, "qhull error: f%d is not attached to the new facets\n",
+ newfacet->id);
+ errfacet= newfacet;
+ }
+ if (errfacet)
+ qh_errexit(qh, qh_ERRqhull, errfacet, NULL);
+} /* checkconnect */
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="checkzero">-</a>
+
+ qh_checkzero(qh, testall )
+ check that facets are clearly convex for qh.DISTround with qh.MERGEexact
+
+ if testall,
+ test all facets for qh.MERGEexact post-merging
+ else
+ test qh.newfacet_list
+
+ if qh.MERGEexact,
+ allows coplanar ridges
+ skips convexity test while qh.ZEROall_ok
+
+ returns:
+ True if all facets !flipped, !dupridge, normal
+ if all horizon facets are simplicial
+ if all vertices are clearly below neighbor
+ if all opposite vertices of horizon are below
+ clears qh.ZEROall_ok if any problems or coplanar facets
+
+ notes:
+ uses qh.vertex_visit
+ horizon facets may define multiple new facets
+
+ design:
+ for all facets in qh.newfacet_list or qh.facet_list
+ check for flagged faults (flipped, etc.)
+ for all facets in qh.newfacet_list or qh.facet_list
+ for each neighbor of facet
+ skip horizon facets for qh.newfacet_list
+ test the opposite vertex
+ if qh.newfacet_list
+ test the other vertices in the facet's horizon facet
+*/
+boolT qh_checkzero(qhT *qh, boolT testall) {
+ facetT *facet, *neighbor, **neighborp;
+ facetT *horizon, *facetlist;
+ int neighbor_i;
+ vertexT *vertex, **vertexp;
+ realT dist;
+
+ if (testall)
+ facetlist= qh->facet_list;
+ else {
+ facetlist= qh->newfacet_list;
+ FORALLfacet_(facetlist) {
+ horizon= SETfirstt_(facet->neighbors, facetT);
+ if (!horizon->simplicial)
+ goto LABELproblem;
+ if (facet->flipped || facet->dupridge || !facet->normal)
+ goto LABELproblem;
+ }
+ if (qh->MERGEexact && qh->ZEROall_ok) {
+ trace2((qh, qh->ferr, 2011, "qh_checkzero: skip convexity check until first pre-merge\n"));
+ return True;
+ }
+ }
+ FORALLfacet_(facetlist) {
+ qh->vertex_visit++;
+ neighbor_i= 0;
+ horizon= NULL;
+ FOREACHneighbor_(facet) {
+ if (!neighbor_i && !testall) {
+ horizon= neighbor;
+ neighbor_i++;
+ continue; /* horizon facet tested in qh_findhorizon */
+ }
+ vertex= SETelemt_(facet->vertices, neighbor_i++, vertexT);
+ vertex->visitid= qh->vertex_visit;
+ zzinc_(Zdistzero);
+ qh_distplane(qh, vertex->point, neighbor, &dist);
+ if (dist >= -qh->DISTround) {
+ qh->ZEROall_ok= False;
+ if (!qh->MERGEexact || testall || dist > qh->DISTround)
+ goto LABELnonconvex;
+ }
+ }
+ if (!testall && horizon) {
+ FOREACHvertex_(horizon->vertices) {
+ if (vertex->visitid != qh->vertex_visit) {
+ zzinc_(Zdistzero);
+ qh_distplane(qh, vertex->point, facet, &dist);
+ if (dist >= -qh->DISTround) {
+ qh->ZEROall_ok= False;
+ if (!qh->MERGEexact || dist > qh->DISTround)
+ goto LABELnonconvex;
+ }
+ break;
+ }
+ }
+ }
+ }
+ trace2((qh, qh->ferr, 2012, "qh_checkzero: testall %d, facets are %s\n", testall,
+ (qh->MERGEexact && !testall) ?
+ "not concave, flipped, or duplicate ridged" : "clearly convex"));
+ return True;
+
+ LABELproblem:
+ qh->ZEROall_ok= False;
+ trace2((qh, qh->ferr, 2013, "qh_checkzero: facet f%d needs pre-merging\n",
+ facet->id));
+ return False;
+
+ LABELnonconvex:
+ trace2((qh, qh->ferr, 2014, "qh_checkzero: facet f%d and f%d are not clearly convex. v%d dist %.2g\n",
+ facet->id, neighbor->id, vertex->id, dist));
+ return False;
+} /* checkzero */
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="compareangle">-</a>
+
+ qh_compareangle(angle1, angle2 )
+ used by qsort() to order merges by angle
+*/
+int qh_compareangle(const void *p1, const void *p2) {
+ const mergeT *a= *((mergeT *const*)p1), *b= *((mergeT *const*)p2);
+
+ return((a->angle > b->angle) ? 1 : -1);
+} /* compareangle */
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="comparemerge">-</a>
+
+ qh_comparemerge(merge1, merge2 )
+ used by qsort() to order merges
+*/
+int qh_comparemerge(const void *p1, const void *p2) {
+ const mergeT *a= *((mergeT *const*)p1), *b= *((mergeT *const*)p2);
+
+ return(a->type - b->type);
+} /* comparemerge */
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="comparevisit">-</a>
+
+ qh_comparevisit(vertex1, vertex2 )
+ used by qsort() to order vertices by their visitid
+*/
+int qh_comparevisit(const void *p1, const void *p2) {
+ const vertexT *a= *((vertexT *const*)p1), *b= *((vertexT *const*)p2);
+
+ return(a->visitid - b->visitid);
+} /* comparevisit */
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="copynonconvex">-</a>
+
+ qh_copynonconvex(qh, atridge )
+ set non-convex flag on other ridges (if any) between same neighbors
+
+ notes:
+ may be faster if use smaller ridge set
+
+ design:
+ for each ridge of atridge's top facet
+ if ridge shares the same neighbor
+ set nonconvex flag
+*/
+void qh_copynonconvex(qhT *qh, ridgeT *atridge) {
+ facetT *facet, *otherfacet;
+ ridgeT *ridge, **ridgep;
+
+ facet= atridge->top;
+ otherfacet= atridge->bottom;
+ FOREACHridge_(facet->ridges) {
+ if (otherfacet == otherfacet_(ridge, facet) && ridge != atridge) {
+ ridge->nonconvex= True;
+ trace4((qh, qh->ferr, 4020, "qh_copynonconvex: moved nonconvex flag from r%d to r%d\n",
+ atridge->id, ridge->id));
+ break;
+ }
+ }
+} /* copynonconvex */
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="degen_redundant_facet">-</a>
+
+ qh_degen_redundant_facet(qh, facet )
+ check facet for degen. or redundancy
+
+ notes:
+ bumps vertex_visit
+ called if a facet was redundant but no longer is (qh_merge_degenredundant)
+ qh_appendmergeset() only appends first reference to facet (i.e., redundant)
+
+ see:
+ qh_degen_redundant_neighbors()
+
+ design:
+ test for redundant neighbor
+ test for degenerate facet
+*/
+void qh_degen_redundant_facet(qhT *qh, facetT *facet) {
+ vertexT *vertex, **vertexp;
+ facetT *neighbor, **neighborp;
+
+ trace4((qh, qh->ferr, 4021, "qh_degen_redundant_facet: test facet f%d for degen/redundant\n",
+ facet->id));
+ FOREACHneighbor_(facet) {
+ qh->vertex_visit++;
+ FOREACHvertex_(neighbor->vertices)
+ vertex->visitid= qh->vertex_visit;
+ FOREACHvertex_(facet->vertices) {
+ if (vertex->visitid != qh->vertex_visit)
+ break;
+ }
+ if (!vertex) {
+ qh_appendmergeset(qh, facet, neighbor, MRGredundant, NULL);
+ trace2((qh, qh->ferr, 2015, "qh_degen_redundant_facet: f%d is contained in f%d. merge\n", facet->id, neighbor->id));
+ return;
+ }
+ }
+ if (qh_setsize(qh, facet->neighbors) < qh->hull_dim) {
+ qh_appendmergeset(qh, facet, facet, MRGdegen, NULL);
+ trace2((qh, qh->ferr, 2016, "qh_degen_redundant_neighbors: f%d is degenerate.\n", facet->id));
+ }
+} /* degen_redundant_facet */
+
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="degen_redundant_neighbors">-</a>
+
+ qh_degen_redundant_neighbors(qh, facet, delfacet, )
+ append degenerate and redundant neighbors to facet_mergeset
+ if delfacet,
+ only checks neighbors of both delfacet and facet
+ also checks current facet for degeneracy
+
+ notes:
+ bumps vertex_visit
+ called for each qh_mergefacet() and qh_mergecycle()
+ merge and statistics occur in merge_nonconvex
+ qh_appendmergeset() only appends first reference to facet (i.e., redundant)
+ it appends redundant facets after degenerate ones
+
+ a degenerate facet has fewer than hull_dim neighbors
+ a redundant facet's vertices is a subset of its neighbor's vertices
+ tests for redundant merges first (appendmergeset is nop for others)
+ in a merge, only needs to test neighbors of merged facet
+
+ see:
+ qh_merge_degenredundant() and qh_degen_redundant_facet()
+
+ design:
+ test for degenerate facet
+ test for redundant neighbor
+ test for degenerate neighbor
+*/
+void qh_degen_redundant_neighbors(qhT *qh, facetT *facet, facetT *delfacet) {
+ vertexT *vertex, **vertexp;
+ facetT *neighbor, **neighborp;
+ int size;
+
+ trace4((qh, qh->ferr, 4022, "qh_degen_redundant_neighbors: test neighbors of f%d with delfacet f%d\n",
+ facet->id, getid_(delfacet)));
+ if ((size= qh_setsize(qh, facet->neighbors)) < qh->hull_dim) {
+ qh_appendmergeset(qh, facet, facet, MRGdegen, NULL);
+ trace2((qh, qh->ferr, 2017, "qh_degen_redundant_neighbors: f%d is degenerate with %d neighbors.\n", facet->id, size));
+ }
+ if (!delfacet)
+ delfacet= facet;
+ qh->vertex_visit++;
+ FOREACHvertex_(facet->vertices)
+ vertex->visitid= qh->vertex_visit;
+ FOREACHneighbor_(delfacet) {
+ /* uses early out instead of checking vertex count */
+ if (neighbor == facet)
+ continue;
+ FOREACHvertex_(neighbor->vertices) {
+ if (vertex->visitid != qh->vertex_visit)
+ break;
+ }
+ if (!vertex) {
+ qh_appendmergeset(qh, neighbor, facet, MRGredundant, NULL);
+ trace2((qh, qh->ferr, 2018, "qh_degen_redundant_neighbors: f%d is contained in f%d. merge\n", neighbor->id, facet->id));
+ }
+ }
+ FOREACHneighbor_(delfacet) { /* redundant merges occur first */
+ if (neighbor == facet)
+ continue;
+ if ((size= qh_setsize(qh, neighbor->neighbors)) < qh->hull_dim) {
+ qh_appendmergeset(qh, neighbor, neighbor, MRGdegen, NULL);
+ trace2((qh, qh->ferr, 2019, "qh_degen_redundant_neighbors: f%d is degenerate with %d neighbors. Neighbor of f%d.\n", neighbor->id, size, facet->id));
+ }
+ }
+} /* degen_redundant_neighbors */
+
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="find_newvertex">-</a>
+
+ qh_find_newvertex(qh, oldvertex, vertices, ridges )
+ locate new vertex for renaming old vertex
+ vertices is a set of possible new vertices
+ vertices sorted by number of deleted ridges
+
+ returns:
+ newvertex or NULL
+ each ridge includes both vertex and oldvertex
+ vertices sorted by number of deleted ridges
+
+ notes:
+ modifies vertex->visitid
+ new vertex is in one of the ridges
+ renaming will not cause a duplicate ridge
+ renaming will minimize the number of deleted ridges
+ newvertex may not be adjacent in the dual (though unlikely)
+
+ design:
+ for each vertex in vertices
+ set vertex->visitid to number of references in ridges
+ remove unvisited vertices
+ set qh.vertex_visit above all possible values
+ sort vertices by number of references in ridges
+ add each ridge to qh.hash_table
+ for each vertex in vertices
+ look for a vertex that would not cause a duplicate ridge after a rename
+*/
+vertexT *qh_find_newvertex(qhT *qh, vertexT *oldvertex, setT *vertices, setT *ridges) {
+ vertexT *vertex, **vertexp;
+ setT *newridges;
+ ridgeT *ridge, **ridgep;
+ int size, hashsize;
+ int hash;
+
+#ifndef qh_NOtrace
+ if (qh->IStracing >= 4) {
+ qh_fprintf(qh, qh->ferr, 8063, "qh_find_newvertex: find new vertex for v%d from ",
+ oldvertex->id);
+ FOREACHvertex_(vertices)
+ qh_fprintf(qh, qh->ferr, 8064, "v%d ", vertex->id);
+ FOREACHridge_(ridges)
+ qh_fprintf(qh, qh->ferr, 8065, "r%d ", ridge->id);
+ qh_fprintf(qh, qh->ferr, 8066, "\n");
+ }
+#endif
+ FOREACHvertex_(vertices)
+ vertex->visitid= 0;
+ FOREACHridge_(ridges) {
+ FOREACHvertex_(ridge->vertices)
+ vertex->visitid++;
+ }
+ FOREACHvertex_(vertices) {
+ if (!vertex->visitid) {
+ qh_setdelnth(qh, vertices, SETindex_(vertices,vertex));
+ vertexp--; /* repeat since deleted this vertex */
+ }
+ }
+ qh->vertex_visit += (unsigned int)qh_setsize(qh, ridges);
+ if (!qh_setsize(qh, vertices)) {
+ trace4((qh, qh->ferr, 4023, "qh_find_newvertex: vertices not in ridges for v%d\n",
+ oldvertex->id));
+ return NULL;
+ }
+ qsort(SETaddr_(vertices, vertexT), (size_t)qh_setsize(qh, vertices),
+ sizeof(vertexT *), qh_comparevisit);
+ /* can now use qh->vertex_visit */
+ if (qh->PRINTstatistics) {
+ size= qh_setsize(qh, vertices);
+ zinc_(Zintersect);
+ zadd_(Zintersecttot, size);
+ zmax_(Zintersectmax, size);
+ }
+ hashsize= qh_newhashtable(qh, qh_setsize(qh, ridges));
+ FOREACHridge_(ridges)
+ qh_hashridge(qh, qh->hash_table, hashsize, ridge, oldvertex);
+ FOREACHvertex_(vertices) {
+ newridges= qh_vertexridges(qh, vertex);
+ FOREACHridge_(newridges) {
+ if (qh_hashridge_find(qh, qh->hash_table, hashsize, ridge, vertex, oldvertex, &hash)) {
+ zinc_(Zdupridge);
+ break;
+ }
+ }
+ qh_settempfree(qh, &newridges);
+ if (!ridge)
+ break; /* found a rename */
+ }
+ if (vertex) {
+ /* counted in qh_renamevertex */
+ trace2((qh, qh->ferr, 2020, "qh_find_newvertex: found v%d for old v%d from %d vertices and %d ridges.\n",
+ vertex->id, oldvertex->id, qh_setsize(qh, vertices), qh_setsize(qh, ridges)));
+ }else {
+ zinc_(Zfindfail);
+ trace0((qh, qh->ferr, 14, "qh_find_newvertex: no vertex for renaming v%d(all duplicated ridges) during p%d\n",
+ oldvertex->id, qh->furthest_id));
+ }
+ qh_setfree(qh, &qh->hash_table);
+ return vertex;
+} /* find_newvertex */
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="findbest_test">-</a>
+
+ qh_findbest_test(qh, testcentrum, facet, neighbor, bestfacet, dist, mindist, maxdist )
+ test neighbor of facet for qh_findbestneighbor()
+ if testcentrum,
+ tests centrum (assumes it is defined)
+ else
+ tests vertices
+
+ returns:
+ if a better facet (i.e., vertices/centrum of facet closer to neighbor)
+ updates bestfacet, dist, mindist, and maxdist
+*/
+void qh_findbest_test(qhT *qh, boolT testcentrum, facetT *facet, facetT *neighbor,
+ facetT **bestfacet, realT *distp, realT *mindistp, realT *maxdistp) {
+ realT dist, mindist, maxdist;
+
+ if (testcentrum) {
+ zzinc_(Zbestdist);
+ qh_distplane(qh, facet->center, neighbor, &dist);
+ dist *= qh->hull_dim; /* estimate furthest vertex */
+ if (dist < 0) {
+ maxdist= 0;
+ mindist= dist;
+ dist= -dist;
+ }else {
+ mindist= 0;
+ maxdist= dist;
+ }
+ }else
+ dist= qh_getdistance(qh, facet, neighbor, &mindist, &maxdist);
+ if (dist < *distp) {
+ *bestfacet= neighbor;
+ *mindistp= mindist;
+ *maxdistp= maxdist;
+ *distp= dist;
+ }
+} /* findbest_test */
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="findbestneighbor">-</a>
+
+ qh_findbestneighbor(qh, facet, dist, mindist, maxdist )
+ finds best neighbor (least dist) of a facet for merging
+
+ returns:
+ returns min and max distances and their max absolute value
+
+ notes:
+ error if qh_ASvoronoi
+ avoids merging old into new
+ assumes ridge->nonconvex only set on one ridge between a pair of facets
+ could use an early out predicate but not worth it
+
+ design:
+ if a large facet
+ will test centrum
+ else
+ will test vertices
+ if a large facet
+ test nonconvex neighbors for best merge
+ else
+ test all neighbors for the best merge
+ if testing centrum
+ get distance information
+*/
+facetT *qh_findbestneighbor(qhT *qh, facetT *facet, realT *distp, realT *mindistp, realT *maxdistp) {
+ facetT *neighbor, **neighborp, *bestfacet= NULL;
+ ridgeT *ridge, **ridgep;
+ boolT nonconvex= True, testcentrum= False;
+ int size= qh_setsize(qh, facet->vertices);
+
+ if(qh->CENTERtype==qh_ASvoronoi){
+ qh_fprintf(qh, qh->ferr, 6272, "qhull error: cannot call qh_findbestneighor for f%d while qh.CENTERtype is qh_ASvoronoi\n", facet->id);
+ qh_errexit(qh, qh_ERRqhull, facet, NULL);
+ }
+ *distp= REALmax;
+ if (size > qh_BESTcentrum2 * qh->hull_dim + qh_BESTcentrum) {
+ testcentrum= True;
+ zinc_(Zbestcentrum);
+ if (!facet->center)
+ facet->center= qh_getcentrum(qh, facet);
+ }
+ if (size > qh->hull_dim + qh_BESTnonconvex) {
+ FOREACHridge_(facet->ridges) {
+ if (ridge->nonconvex) {
+ neighbor= otherfacet_(ridge, facet);
+ qh_findbest_test(qh, testcentrum, facet, neighbor,
+ &bestfacet, distp, mindistp, maxdistp);
+ }
+ }
+ }
+ if (!bestfacet) {
+ nonconvex= False;
+ FOREACHneighbor_(facet)
+ qh_findbest_test(qh, testcentrum, facet, neighbor,
+ &bestfacet, distp, mindistp, maxdistp);
+ }
+ if (!bestfacet) {
+ qh_fprintf(qh, qh->ferr, 6095, "qhull internal error (qh_findbestneighbor): no neighbors for f%d\n", facet->id);
+ qh_errexit(qh, qh_ERRqhull, facet, NULL);
+ }
+ if (testcentrum)
+ qh_getdistance(qh, facet, bestfacet, mindistp, maxdistp);
+ trace3((qh, qh->ferr, 3002, "qh_findbestneighbor: f%d is best neighbor for f%d testcentrum? %d nonconvex? %d dist %2.2g min %2.2g max %2.2g\n",
+ bestfacet->id, facet->id, testcentrum, nonconvex, *distp, *mindistp, *maxdistp));
+ return(bestfacet);
+} /* findbestneighbor */
+
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="flippedmerges">-</a>
+
+ qh_flippedmerges(qh, facetlist, wasmerge )
+ merge flipped facets into best neighbor
+ assumes qh.facet_mergeset at top of temporary stack
+
+ returns:
+ no flipped facets on facetlist
+ sets wasmerge if merge occurred
+ degen/redundant merges passed through
+
+ notes:
+ othermerges not needed since qh.facet_mergeset is empty before & after
+ keep it in case of change
+
+ design:
+ append flipped facets to qh.facetmergeset
+ for each flipped merge
+ find best neighbor
+ merge facet into neighbor
+ merge degenerate and redundant facets
+ remove flipped merges from qh.facet_mergeset
+*/
+void qh_flippedmerges(qhT *qh, facetT *facetlist, boolT *wasmerge) {
+ facetT *facet, *neighbor, *facet1;
+ realT dist, mindist, maxdist;
+ mergeT *merge, **mergep;
+ setT *othermerges;
+ int nummerge=0;
+
+ trace4((qh, qh->ferr, 4024, "qh_flippedmerges: begin\n"));
+ FORALLfacet_(facetlist) {
+ if (facet->flipped && !facet->visible)
+ qh_appendmergeset(qh, facet, facet, MRGflip, NULL);
+ }
+ othermerges= qh_settemppop(qh); /* was facet_mergeset */
+ qh->facet_mergeset= qh_settemp(qh, qh->TEMPsize);
+ qh_settemppush(qh, othermerges);
+ FOREACHmerge_(othermerges) {
+ facet1= merge->facet1;
+ if (merge->type != MRGflip || facet1->visible)
+ continue;
+ if (qh->TRACEmerge-1 == zzval_(Ztotmerge))
+ qh->qhmem.IStracing= qh->IStracing= qh->TRACElevel;
+ neighbor= qh_findbestneighbor(qh, facet1, &dist, &mindist, &maxdist);
+ trace0((qh, qh->ferr, 15, "qh_flippedmerges: merge flipped f%d into f%d dist %2.2g during p%d\n",
+ facet1->id, neighbor->id, dist, qh->furthest_id));
+ qh_mergefacet(qh, facet1, neighbor, &mindist, &maxdist, !qh_MERGEapex);
+ nummerge++;
+ if (qh->PRINTstatistics) {
+ zinc_(Zflipped);
+ wadd_(Wflippedtot, dist);
+ wmax_(Wflippedmax, dist);
+ }
+ qh_merge_degenredundant(qh);
+ }
+ FOREACHmerge_(othermerges) {
+ if (merge->facet1->visible || merge->facet2->visible)
+ qh_memfree(qh, merge, (int)sizeof(mergeT));
+ else
+ qh_setappend(qh, &qh->facet_mergeset, merge);
+ }
+ qh_settempfree(qh, &othermerges);
+ if (nummerge)
+ *wasmerge= True;
+ trace1((qh, qh->ferr, 1010, "qh_flippedmerges: merged %d flipped facets into a good neighbor\n", nummerge));
+} /* flippedmerges */
+
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="forcedmerges">-</a>
+
+ qh_forcedmerges(qh, wasmerge )
+ merge duplicated ridges
+
+ returns:
+ removes all duplicate ridges on facet_mergeset
+ wasmerge set if merge
+ qh.facet_mergeset may include non-forced merges(none for now)
+ qh.degen_mergeset includes degen/redun merges
+
+ notes:
+ duplicate ridges occur when the horizon is pinched,
+ i.e. a subridge occurs in more than two horizon ridges.
+ could rename vertices that pinch the horizon
+ assumes qh_merge_degenredundant() has not be called
+ othermerges isn't needed since facet_mergeset is empty afterwards
+ keep it in case of change
+
+ design:
+ for each duplicate ridge
+ find current facets by chasing f.replace links
+ check for wide merge due to duplicate ridge
+ determine best direction for facet
+ merge one facet into the other
+ remove duplicate ridges from qh.facet_mergeset
+*/
+void qh_forcedmerges(qhT *qh, boolT *wasmerge) {
+ facetT *facet1, *facet2;
+ mergeT *merge, **mergep;
+ realT dist1, dist2, mindist1, mindist2, maxdist1, maxdist2;
+ setT *othermerges;
+ int nummerge=0, numflip=0;
+
+ if (qh->TRACEmerge-1 == zzval_(Ztotmerge))
+ qh->qhmem.IStracing= qh->IStracing= qh->TRACElevel;
+ trace4((qh, qh->ferr, 4025, "qh_forcedmerges: begin\n"));
+ othermerges= qh_settemppop(qh); /* was facet_mergeset */
+ qh->facet_mergeset= qh_settemp(qh, qh->TEMPsize);
+ qh_settemppush(qh, othermerges);
+ FOREACHmerge_(othermerges) {
+ if (merge->type != MRGridge)
+ continue;
+ if (qh->TRACEmerge-1 == zzval_(Ztotmerge))
+ qh->qhmem.IStracing= qh->IStracing= qh->TRACElevel;
+ facet1= merge->facet1;
+ facet2= merge->facet2;
+ while (facet1->visible) /* must exist, no qh_merge_degenredunant */
+ facet1= facet1->f.replace; /* previously merged facet */
+ while (facet2->visible)
+ facet2= facet2->f.replace; /* previously merged facet */
+ if (facet1 == facet2)
+ continue;
+ if (!qh_setin(facet2->neighbors, facet1)) {
+ qh_fprintf(qh, qh->ferr, 6096, "qhull internal error (qh_forcedmerges): f%d and f%d had a duplicate ridge but as f%d and f%d they are no longer neighbors\n",
+ merge->facet1->id, merge->facet2->id, facet1->id, facet2->id);
+ qh_errexit2(qh, qh_ERRqhull, facet1, facet2);
+ }
+ dist1= qh_getdistance(qh, facet1, facet2, &mindist1, &maxdist1);
+ dist2= qh_getdistance(qh, facet2, facet1, &mindist2, &maxdist2);
+ qh_check_dupridge(qh, facet1, dist1, facet2, dist2);
+ if (dist1 < dist2)
+ qh_mergefacet(qh, facet1, facet2, &mindist1, &maxdist1, !qh_MERGEapex);
+ else {
+ qh_mergefacet(qh, facet2, facet1, &mindist2, &maxdist2, !qh_MERGEapex);
+ dist1= dist2;
+ facet1= facet2;
+ }
+ if (facet1->flipped) {
+ zinc_(Zmergeflipdup);
+ numflip++;
+ }else
+ nummerge++;
+ if (qh->PRINTstatistics) {
+ zinc_(Zduplicate);
+ wadd_(Wduplicatetot, dist1);
+ wmax_(Wduplicatemax, dist1);
+ }
+ }
+ FOREACHmerge_(othermerges) {
+ if (merge->type == MRGridge)
+ qh_memfree(qh, merge, (int)sizeof(mergeT));
+ else
+ qh_setappend(qh, &qh->facet_mergeset, merge);
+ }
+ qh_settempfree(qh, &othermerges);
+ if (nummerge)
+ *wasmerge= True;
+ trace1((qh, qh->ferr, 1011, "qh_forcedmerges: merged %d facets and %d flipped facets across duplicated ridges\n",
+ nummerge, numflip));
+} /* forcedmerges */
+
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="getmergeset">-</a>
+
+ qh_getmergeset(qh, facetlist )
+ determines nonconvex facets on facetlist
+ tests !tested ridges and nonconvex ridges of !tested facets
+
+ returns:
+ returns sorted qh.facet_mergeset of facet-neighbor pairs to be merged
+ all ridges tested
+
+ notes:
+ assumes no nonconvex ridges with both facets tested
+ uses facet->tested/ridge->tested to prevent duplicate tests
+ can not limit tests to modified ridges since the centrum changed
+ uses qh.visit_id
+
+ see:
+ qh_getmergeset_initial()
+
+ design:
+ for each facet on facetlist
+ for each ridge of facet
+ if untested ridge
+ test ridge for convexity
+ if non-convex
+ append ridge to qh.facet_mergeset
+ sort qh.facet_mergeset by angle
+*/
+void qh_getmergeset(qhT *qh, facetT *facetlist) {
+ facetT *facet, *neighbor, **neighborp;
+ ridgeT *ridge, **ridgep;
+ int nummerges;
+
+ nummerges= qh_setsize(qh, qh->facet_mergeset);
+ trace4((qh, qh->ferr, 4026, "qh_getmergeset: started.\n"));
+ qh->visit_id++;
+ FORALLfacet_(facetlist) {
+ if (facet->tested)
+ continue;
+ facet->visitid= qh->visit_id;
+ facet->tested= True; /* must be non-simplicial due to merge */
+ FOREACHneighbor_(facet)
+ neighbor->seen= False;
+ FOREACHridge_(facet->ridges) {
+ if (ridge->tested && !ridge->nonconvex)
+ continue;
+ /* if tested & nonconvex, need to append merge */
+ neighbor= otherfacet_(ridge, facet);
+ if (neighbor->seen) {
+ ridge->tested= True;
+ ridge->nonconvex= False;
+ }else if (neighbor->visitid != qh->visit_id) {
+ ridge->tested= True;
+ ridge->nonconvex= False;
+ neighbor->seen= True; /* only one ridge is marked nonconvex */
+ if (qh_test_appendmerge(qh, facet, neighbor))
+ ridge->nonconvex= True;
+ }
+ }
+ }
+ nummerges= qh_setsize(qh, qh->facet_mergeset);
+ if (qh->ANGLEmerge)
+ qsort(SETaddr_(qh->facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_compareangle);
+ else
+ qsort(SETaddr_(qh->facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_comparemerge);
+ if (qh->POSTmerging) {
+ zadd_(Zmergesettot2, nummerges);
+ }else {
+ zadd_(Zmergesettot, nummerges);
+ zmax_(Zmergesetmax, nummerges);
+ }
+ trace2((qh, qh->ferr, 2021, "qh_getmergeset: %d merges found\n", nummerges));
+} /* getmergeset */
+
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="getmergeset_initial">-</a>
+
+ qh_getmergeset_initial(qh, facetlist )
+ determine initial qh.facet_mergeset for facets
+ tests all facet/neighbor pairs on facetlist
+
+ returns:
+ sorted qh.facet_mergeset with nonconvex ridges
+ sets facet->tested, ridge->tested, and ridge->nonconvex
+
+ notes:
+ uses visit_id, assumes ridge->nonconvex is False
+
+ see:
+ qh_getmergeset()
+
+ design:
+ for each facet on facetlist
+ for each untested neighbor of facet
+ test facet and neighbor for convexity
+ if non-convex
+ append merge to qh.facet_mergeset
+ mark one of the ridges as nonconvex
+ sort qh.facet_mergeset by angle
+*/
+void qh_getmergeset_initial(qhT *qh, facetT *facetlist) {
+ facetT *facet, *neighbor, **neighborp;
+ ridgeT *ridge, **ridgep;
+ int nummerges;
+
+ qh->visit_id++;
+ FORALLfacet_(facetlist) {
+ facet->visitid= qh->visit_id;
+ facet->tested= True;
+ FOREACHneighbor_(facet) {
+ if (neighbor->visitid != qh->visit_id) {
+ if (qh_test_appendmerge(qh, facet, neighbor)) {
+ FOREACHridge_(neighbor->ridges) {
+ if (facet == otherfacet_(ridge, neighbor)) {
+ ridge->nonconvex= True;
+ break; /* only one ridge is marked nonconvex */
+ }
+ }
+ }
+ }
+ }
+ FOREACHridge_(facet->ridges)
+ ridge->tested= True;
+ }
+ nummerges= qh_setsize(qh, qh->facet_mergeset);
+ if (qh->ANGLEmerge)
+ qsort(SETaddr_(qh->facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_compareangle);
+ else
+ qsort(SETaddr_(qh->facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_comparemerge);
+ if (qh->POSTmerging) {
+ zadd_(Zmergeinittot2, nummerges);
+ }else {
+ zadd_(Zmergeinittot, nummerges);
+ zmax_(Zmergeinitmax, nummerges);
+ }
+ trace2((qh, qh->ferr, 2022, "qh_getmergeset_initial: %d merges found\n", nummerges));
+} /* getmergeset_initial */
+
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="hashridge">-</a>
+
+ qh_hashridge(qh, hashtable, hashsize, ridge, oldvertex )
+ add ridge to hashtable without oldvertex
+
+ notes:
+ assumes hashtable is large enough
+
+ design:
+ determine hash value for ridge without oldvertex
+ find next empty slot for ridge
+*/
+void qh_hashridge(qhT *qh, setT *hashtable, int hashsize, ridgeT *ridge, vertexT *oldvertex) {
+ int hash;
+ ridgeT *ridgeA;
+
+ hash= qh_gethash(qh, hashsize, ridge->vertices, qh->hull_dim-1, 0, oldvertex);
+ while (True) {
+ if (!(ridgeA= SETelemt_(hashtable, hash, ridgeT))) {
+ SETelem_(hashtable, hash)= ridge;
+ break;
+ }else if (ridgeA == ridge)
+ break;
+ if (++hash == hashsize)
+ hash= 0;
+ }
+} /* hashridge */
+
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="hashridge_find">-</a>
+
+ qh_hashridge_find(qh, hashtable, hashsize, ridge, vertex, oldvertex, hashslot )
+ returns matching ridge without oldvertex in hashtable
+ for ridge without vertex
+ if oldvertex is NULL
+ matches with any one skip
+
+ returns:
+ matching ridge or NULL
+ if no match,
+ if ridge already in table
+ hashslot= -1
+ else
+ hashslot= next NULL index
+
+ notes:
+ assumes hashtable is large enough
+ can't match ridge to itself
+
+ design:
+ get hash value for ridge without vertex
+ for each hashslot
+ return match if ridge matches ridgeA without oldvertex
+*/
+ridgeT *qh_hashridge_find(qhT *qh, setT *hashtable, int hashsize, ridgeT *ridge,
+ vertexT *vertex, vertexT *oldvertex, int *hashslot) {
+ int hash;
+ ridgeT *ridgeA;
+
+ *hashslot= 0;
+ zinc_(Zhashridge);
+ hash= qh_gethash(qh, hashsize, ridge->vertices, qh->hull_dim-1, 0, vertex);
+ while ((ridgeA= SETelemt_(hashtable, hash, ridgeT))) {
+ if (ridgeA == ridge)
+ *hashslot= -1;
+ else {
+ zinc_(Zhashridgetest);
+ if (qh_setequal_except(ridge->vertices, vertex, ridgeA->vertices, oldvertex))
+ return ridgeA;
+ }
+ if (++hash == hashsize)
+ hash= 0;
+ }
+ if (!*hashslot)
+ *hashslot= hash;
+ return NULL;
+} /* hashridge_find */
+
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="makeridges">-</a>
+
+ qh_makeridges(qh, facet )
+ creates explicit ridges between simplicial facets
+
+ returns:
+ facet with ridges and without qh_MERGEridge
+ ->simplicial is False
+
+ notes:
+ allows qh_MERGEridge flag
+ uses existing ridges
+ duplicate neighbors ok if ridges already exist (qh_mergecycle_ridges)
+
+ see:
+ qh_mergecycle_ridges()
+
+ design:
+ look for qh_MERGEridge neighbors
+ mark neighbors that already have ridges
+ for each unprocessed neighbor of facet
+ create a ridge for neighbor and facet
+ if any qh_MERGEridge neighbors
+ delete qh_MERGEridge flags (already handled by qh_mark_dupridges)
+*/
+void qh_makeridges(qhT *qh, facetT *facet) {
+ facetT *neighbor, **neighborp;
+ ridgeT *ridge, **ridgep;
+ int neighbor_i, neighbor_n;
+ boolT toporient, mergeridge= False;
+
+ if (!facet->simplicial)
+ return;
+ trace4((qh, qh->ferr, 4027, "qh_makeridges: make ridges for f%d\n", facet->id));
+ facet->simplicial= False;
+ FOREACHneighbor_(facet) {
+ if (neighbor == qh_MERGEridge)
+ mergeridge= True;
+ else
+ neighbor->seen= False;
+ }
+ FOREACHridge_(facet->ridges)
+ otherfacet_(ridge, facet)->seen= True;
+ FOREACHneighbor_i_(qh, facet) {
+ if (neighbor == qh_MERGEridge)
+ continue; /* fixed by qh_mark_dupridges */
+ else if (!neighbor->seen) { /* no current ridges */
+ ridge= qh_newridge(qh);
+ ridge->vertices= qh_setnew_delnthsorted(qh, facet->vertices, qh->hull_dim,
+ neighbor_i, 0);
+ toporient= facet->toporient ^ (neighbor_i & 0x1);
+ if (toporient) {
+ ridge->top= facet;
+ ridge->bottom= neighbor;
+ }else {
+ ridge->top= neighbor;
+ ridge->bottom= facet;
+ }
+#if 0 /* this also works */
+ flip= (facet->toporient ^ neighbor->toporient)^(skip1 & 0x1) ^ (skip2 & 0x1);
+ if (facet->toporient ^ (skip1 & 0x1) ^ flip) {
+ ridge->top= neighbor;
+ ridge->bottom= facet;
+ }else {
+ ridge->top= facet;
+ ridge->bottom= neighbor;
+ }
+#endif
+ qh_setappend(qh, &(facet->ridges), ridge);
+ qh_setappend(qh, &(neighbor->ridges), ridge);
+ }
+ }
+ if (mergeridge) {
+ while (qh_setdel(facet->neighbors, qh_MERGEridge))
+ ; /* delete each one */
+ }
+} /* makeridges */
+
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="mark_dupridges">-</a>
+
+ qh_mark_dupridges(qh, facetlist )
+ add duplicated ridges to qh.facet_mergeset
+ facet->dupridge is true
+
+ returns:
+ duplicate ridges on qh.facet_mergeset
+ ->mergeridge/->mergeridge2 set
+ duplicate ridges marked by qh_MERGEridge and both sides facet->dupridge
+ no MERGEridges in neighbor sets
+
+ notes:
+ duplicate ridges occur when the horizon is pinched,
+ i.e. a subridge occurs in more than two horizon ridges.
+ could rename vertices that pinch the horizon (thus removing subridge)
+ uses qh.visit_id
+
+ design:
+ for all facets on facetlist
+ if facet contains a duplicate ridge
+ for each neighbor of facet
+ if neighbor marked qh_MERGEridge (one side of the merge)
+ set facet->mergeridge
+ else
+ if neighbor contains a duplicate ridge
+ and the back link is qh_MERGEridge
+ append duplicate ridge to qh.facet_mergeset
+ for each duplicate ridge
+ make ridge sets in preparation for merging
+ remove qh_MERGEridge from neighbor set
+ for each duplicate ridge
+ restore the missing neighbor from the neighbor set that was qh_MERGEridge
+ add the missing ridge for this neighbor
+*/
+void qh_mark_dupridges(qhT *qh, facetT *facetlist) {
+ facetT *facet, *neighbor, **neighborp;
+ int nummerge=0;
+ mergeT *merge, **mergep;
+
+
+ trace4((qh, qh->ferr, 4028, "qh_mark_dupridges: identify duplicate ridges\n"));
+ FORALLfacet_(facetlist) {
+ if (facet->dupridge) {
+ FOREACHneighbor_(facet) {
+ if (neighbor == qh_MERGEridge) {
+ facet->mergeridge= True;
+ continue;
+ }
+ if (neighbor->dupridge
+ && !qh_setin(neighbor->neighbors, facet)) { /* qh_MERGEridge */
+ qh_appendmergeset(qh, facet, neighbor, MRGridge, NULL);
+ facet->mergeridge2= True;
+ facet->mergeridge= True;
+ nummerge++;
+ }
+ }
+ }
+ }
+ if (!nummerge)
+ return;
+ FORALLfacet_(facetlist) { /* gets rid of qh_MERGEridge */
+ if (facet->mergeridge && !facet->mergeridge2)
+ qh_makeridges(qh, facet);
+ }
+ FOREACHmerge_(qh->facet_mergeset) { /* restore the missing neighbors */
+ if (merge->type == MRGridge) {
+ qh_setappend(qh, &merge->facet2->neighbors, merge->facet1);
+ qh_makeridges(qh, merge->facet1); /* and the missing ridges */
+ }
+ }
+ trace1((qh, qh->ferr, 1012, "qh_mark_dupridges: found %d duplicated ridges\n",
+ nummerge));
+} /* mark_dupridges */
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="maydropneighbor">-</a>
+
+ qh_maydropneighbor(qh, facet )
+ drop neighbor relationship if no ridge between facet and neighbor
+
+ returns:
+ neighbor sets updated
+ appends degenerate facets to qh.facet_mergeset
+
+ notes:
+ won't cause redundant facets since vertex inclusion is the same
+ may drop vertex and neighbor if no ridge
+ uses qh.visit_id
+
+ design:
+ visit all neighbors with ridges
+ for each unvisited neighbor of facet
+ delete neighbor and facet from the neighbor sets
+ if neighbor becomes degenerate
+ append neighbor to qh.degen_mergeset
+ if facet is degenerate
+ append facet to qh.degen_mergeset
+*/
+void qh_maydropneighbor(qhT *qh, facetT *facet) {
+ ridgeT *ridge, **ridgep;
+ realT angledegen= qh_ANGLEdegen;
+ facetT *neighbor, **neighborp;
+
+ qh->visit_id++;
+ trace4((qh, qh->ferr, 4029, "qh_maydropneighbor: test f%d for no ridges to a neighbor\n",
+ facet->id));
+ FOREACHridge_(facet->ridges) {
+ ridge->top->visitid= qh->visit_id;
+ ridge->bottom->visitid= qh->visit_id;
+ }
+ FOREACHneighbor_(facet) {
+ if (neighbor->visitid != qh->visit_id) {
+ trace0((qh, qh->ferr, 17, "qh_maydropneighbor: facets f%d and f%d are no longer neighbors during p%d\n",
+ facet->id, neighbor->id, qh->furthest_id));
+ zinc_(Zdropneighbor);
+ qh_setdel(facet->neighbors, neighbor);
+ neighborp--; /* repeat, deleted a neighbor */
+ qh_setdel(neighbor->neighbors, facet);
+ if (qh_setsize(qh, neighbor->neighbors) < qh->hull_dim) {
+ zinc_(Zdropdegen);
+ qh_appendmergeset(qh, neighbor, neighbor, MRGdegen, &angledegen);
+ trace2((qh, qh->ferr, 2023, "qh_maydropneighbors: f%d is degenerate.\n", neighbor->id));
+ }
+ }
+ }
+ if (qh_setsize(qh, facet->neighbors) < qh->hull_dim) {
+ zinc_(Zdropdegen);
+ qh_appendmergeset(qh, facet, facet, MRGdegen, &angledegen);
+ trace2((qh, qh->ferr, 2024, "qh_maydropneighbors: f%d is degenerate.\n", facet->id));
+ }
+} /* maydropneighbor */
+
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="merge_degenredundant">-</a>
+
+ qh_merge_degenredundant(qh)
+ merge all degenerate and redundant facets
+ qh.degen_mergeset contains merges from qh_degen_redundant_neighbors()
+
+ returns:
+ number of merges performed
+ resets facet->degenerate/redundant
+ if deleted (visible) facet has no neighbors
+ sets ->f.replace to NULL
+
+ notes:
+ redundant merges happen before degenerate ones
+ merging and renaming vertices can result in degen/redundant facets
+
+ design:
+ for each merge on qh.degen_mergeset
+ if redundant merge
+ if non-redundant facet merged into redundant facet
+ recheck facet for redundancy
+ else
+ merge redundant facet into other facet
+*/
+int qh_merge_degenredundant(qhT *qh) {
+ int size;
+ mergeT *merge;
+ facetT *bestneighbor, *facet1, *facet2;
+ realT dist, mindist, maxdist;
+ vertexT *vertex, **vertexp;
+ int nummerges= 0;
+ mergeType mergetype;
+
+ while ((merge= (mergeT*)qh_setdellast(qh->degen_mergeset))) {
+ facet1= merge->facet1;
+ facet2= merge->facet2;
+ mergetype= merge->type;
+ qh_memfree(qh, merge, (int)sizeof(mergeT));
+ if (facet1->visible)
+ continue;
+ facet1->degenerate= False;
+ facet1->redundant= False;
+ if (qh->TRACEmerge-1 == zzval_(Ztotmerge))
+ qh->qhmem.IStracing= qh->IStracing= qh->TRACElevel;
+ if (mergetype == MRGredundant) {
+ zinc_(Zneighbor);
+ while (facet2->visible) {
+ if (!facet2->f.replace) {
+ qh_fprintf(qh, qh->ferr, 6097, "qhull internal error (qh_merge_degenredunant): f%d redundant but f%d has no replacement\n",
+ facet1->id, facet2->id);
+ qh_errexit2(qh, qh_ERRqhull, facet1, facet2);
+ }
+ facet2= facet2->f.replace;
+ }
+ if (facet1 == facet2) {
+ qh_degen_redundant_facet(qh, facet1); /* in case of others */
+ continue;
+ }
+ trace2((qh, qh->ferr, 2025, "qh_merge_degenredundant: facet f%d is contained in f%d, will merge\n",
+ facet1->id, facet2->id));
+ qh_mergefacet(qh, facet1, facet2, NULL, NULL, !qh_MERGEapex);
+ /* merge distance is already accounted for */
+ nummerges++;
+ }else { /* mergetype == MRGdegen, other merges may have fixed */
+ if (!(size= qh_setsize(qh, facet1->neighbors))) {
+ zinc_(Zdelfacetdup);
+ trace2((qh, qh->ferr, 2026, "qh_merge_degenredundant: facet f%d has no neighbors. Deleted\n", facet1->id));
+ qh_willdelete(qh, facet1, NULL);
+ FOREACHvertex_(facet1->vertices) {
+ qh_setdel(vertex->neighbors, facet1);
+ if (!SETfirst_(vertex->neighbors)) {
+ zinc_(Zdegenvertex);
+ trace2((qh, qh->ferr, 2027, "qh_merge_degenredundant: deleted v%d because f%d has no neighbors\n",
+ vertex->id, facet1->id));
+ vertex->deleted= True;
+ qh_setappend(qh, &qh->del_vertices, vertex);
+ }
+ }
+ nummerges++;
+ }else if (size < qh->hull_dim) {
+ bestneighbor= qh_findbestneighbor(qh, facet1, &dist, &mindist, &maxdist);
+ trace2((qh, qh->ferr, 2028, "qh_merge_degenredundant: facet f%d has %d neighbors, merge into f%d dist %2.2g\n",
+ facet1->id, size, bestneighbor->id, dist));
+ qh_mergefacet(qh, facet1, bestneighbor, &mindist, &maxdist, !qh_MERGEapex);
+ nummerges++;
+ if (qh->PRINTstatistics) {
+ zinc_(Zdegen);
+ wadd_(Wdegentot, dist);
+ wmax_(Wdegenmax, dist);
+ }
+ } /* else, another merge fixed the degeneracy and redundancy tested */
+ }
+ }
+ return nummerges;
+} /* merge_degenredundant */
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="merge_nonconvex">-</a>
+
+ qh_merge_nonconvex(qh, facet1, facet2, mergetype )
+ remove non-convex ridge between facet1 into facet2
+ mergetype gives why the facet's are non-convex
+
+ returns:
+ merges one of the facets into the best neighbor
+
+ design:
+ if one of the facets is a new facet
+ prefer merging new facet into old facet
+ find best neighbors for both facets
+ merge the nearest facet into its best neighbor
+ update the statistics
+*/
+void qh_merge_nonconvex(qhT *qh, facetT *facet1, facetT *facet2, mergeType mergetype) {
+ facetT *bestfacet, *bestneighbor, *neighbor;
+ realT dist, dist2, mindist, mindist2, maxdist, maxdist2;
+
+ if (qh->TRACEmerge-1 == zzval_(Ztotmerge))
+ qh->qhmem.IStracing= qh->IStracing= qh->TRACElevel;
+ trace3((qh, qh->ferr, 3003, "qh_merge_nonconvex: merge #%d for f%d and f%d type %d\n",
+ zzval_(Ztotmerge) + 1, facet1->id, facet2->id, mergetype));
+ /* concave or coplanar */
+ if (!facet1->newfacet) {
+ bestfacet= facet2; /* avoid merging old facet if new is ok */
+ facet2= facet1;
+ facet1= bestfacet;
+ }else
+ bestfacet= facet1;
+ bestneighbor= qh_findbestneighbor(qh, bestfacet, &dist, &mindist, &maxdist);
+ neighbor= qh_findbestneighbor(qh, facet2, &dist2, &mindist2, &maxdist2);
+ if (dist < dist2) {
+ qh_mergefacet(qh, bestfacet, bestneighbor, &mindist, &maxdist, !qh_MERGEapex);
+ }else if (qh->AVOIDold && !facet2->newfacet
+ && ((mindist >= -qh->MAXcoplanar && maxdist <= qh->max_outside)
+ || dist * 1.5 < dist2)) {
+ zinc_(Zavoidold);
+ wadd_(Wavoidoldtot, dist);
+ wmax_(Wavoidoldmax, dist);
+ trace2((qh, qh->ferr, 2029, "qh_merge_nonconvex: avoid merging old facet f%d dist %2.2g. Use f%d dist %2.2g instead\n",
+ facet2->id, dist2, facet1->id, dist2));
+ qh_mergefacet(qh, bestfacet, bestneighbor, &mindist, &maxdist, !qh_MERGEapex);
+ }else {
+ qh_mergefacet(qh, facet2, neighbor, &mindist2, &maxdist2, !qh_MERGEapex);
+ dist= dist2;
+ }
+ if (qh->PRINTstatistics) {
+ if (mergetype == MRGanglecoplanar) {
+ zinc_(Zacoplanar);
+ wadd_(Wacoplanartot, dist);
+ wmax_(Wacoplanarmax, dist);
+ }else if (mergetype == MRGconcave) {
+ zinc_(Zconcave);
+ wadd_(Wconcavetot, dist);
+ wmax_(Wconcavemax, dist);
+ }else { /* MRGcoplanar */
+ zinc_(Zcoplanar);
+ wadd_(Wcoplanartot, dist);
+ wmax_(Wcoplanarmax, dist);
+ }
+ }
+} /* merge_nonconvex */
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="mergecycle">-</a>
+
+ qh_mergecycle(qh, samecycle, newfacet )
+ merge a cycle of facets starting at samecycle into a newfacet
+ newfacet is a horizon facet with ->normal
+ samecycle facets are simplicial from an apex
+
+ returns:
+ initializes vertex neighbors on first merge
+ samecycle deleted (placed on qh.visible_list)
+ newfacet at end of qh.facet_list
+ deleted vertices on qh.del_vertices
+
+ see:
+ qh_mergefacet()
+ called by qh_mergecycle_all() for multiple, same cycle facets
+
+ design:
+ make vertex neighbors if necessary
+ make ridges for newfacet
+ merge neighbor sets of samecycle into newfacet
+ merge ridges of samecycle into newfacet
+ merge vertex neighbors of samecycle into newfacet
+ make apex of samecycle the apex of newfacet
+ if newfacet wasn't a new facet
+ add its vertices to qh.newvertex_list
+ delete samecycle facets a make newfacet a newfacet
+*/
+void qh_mergecycle(qhT *qh, facetT *samecycle, facetT *newfacet) {
+ int traceonce= False, tracerestore= 0;
+ vertexT *apex;
+#ifndef qh_NOtrace
+ facetT *same;
+#endif
+
+ if (newfacet->tricoplanar) {
+ if (!qh->TRInormals) {
+ qh_fprintf(qh, qh->ferr, 6224, "Qhull internal error (qh_mergecycle): does not work for tricoplanar facets. Use option 'Q11'\n");
+ qh_errexit(qh, qh_ERRqhull, newfacet, NULL);
+ }
+ newfacet->tricoplanar= False;
+ newfacet->keepcentrum= False;
+ }
+ if (!qh->VERTEXneighbors)
+ qh_vertexneighbors(qh);
+ zzinc_(Ztotmerge);
+ if (qh->REPORTfreq2 && qh->POSTmerging) {
+ if (zzval_(Ztotmerge) > qh->mergereport + qh->REPORTfreq2)
+ qh_tracemerging(qh);
+ }
+#ifndef qh_NOtrace
+ if (qh->TRACEmerge == zzval_(Ztotmerge))
+ qh->qhmem.IStracing= qh->IStracing= qh->TRACElevel;
+ trace2((qh, qh->ferr, 2030, "qh_mergecycle: merge #%d for facets from cycle f%d into coplanar horizon f%d\n",
+ zzval_(Ztotmerge), samecycle->id, newfacet->id));
+ if (newfacet == qh->tracefacet) {
+ tracerestore= qh->IStracing;
+ qh->IStracing= 4;
+ qh_fprintf(qh, qh->ferr, 8068, "qh_mergecycle: ========= trace merge %d of samecycle %d into trace f%d, furthest is p%d\n",
+ zzval_(Ztotmerge), samecycle->id, newfacet->id, qh->furthest_id);
+ traceonce= True;
+ }
+ if (qh->IStracing >=4) {
+ qh_fprintf(qh, qh->ferr, 8069, " same cycle:");
+ FORALLsame_cycle_(samecycle)
+ qh_fprintf(qh, qh->ferr, 8070, " f%d", same->id);
+ qh_fprintf(qh, qh->ferr, 8071, "\n");
+ }
+ if (qh->IStracing >=4)
+ qh_errprint(qh, "MERGING CYCLE", samecycle, newfacet, NULL, NULL);
+#endif /* !qh_NOtrace */
+ apex= SETfirstt_(samecycle->vertices, vertexT);
+ qh_makeridges(qh, newfacet);
+ qh_mergecycle_neighbors(qh, samecycle, newfacet);
+ qh_mergecycle_ridges(qh, samecycle, newfacet);
+ qh_mergecycle_vneighbors(qh, samecycle, newfacet);
+ if (SETfirstt_(newfacet->vertices, vertexT) != apex)
+ qh_setaddnth(qh, &newfacet->vertices, 0, apex); /* apex has last id */
+ if (!newfacet->newfacet)
+ qh_newvertices(qh, newfacet->vertices);
+ qh_mergecycle_facets(qh, samecycle, newfacet);
+ qh_tracemerge(qh, samecycle, newfacet);
+ /* check for degen_redundant_neighbors after qh_forcedmerges() */
+ if (traceonce) {
+ qh_fprintf(qh, qh->ferr, 8072, "qh_mergecycle: end of trace facet\n");
+ qh->IStracing= tracerestore;
+ }
+} /* mergecycle */
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="mergecycle_all">-</a>
+
+ qh_mergecycle_all(qh, facetlist, wasmerge )
+ merge all samecycles of coplanar facets into horizon
+ don't merge facets with ->mergeridge (these already have ->normal)
+ all facets are simplicial from apex
+ all facet->cycledone == False
+
+ returns:
+ all newfacets merged into coplanar horizon facets
+ deleted vertices on qh.del_vertices
+ sets wasmerge if any merge
+
+ see:
+ calls qh_mergecycle for multiple, same cycle facets
+
+ design:
+ for each facet on facetlist
+ skip facets with duplicate ridges and normals
+ check that facet is in a samecycle (->mergehorizon)
+ if facet only member of samecycle
+ sets vertex->delridge for all vertices except apex
+ merge facet into horizon
+ else
+ mark all facets in samecycle
+ remove facets with duplicate ridges from samecycle
+ merge samecycle into horizon (deletes facets from facetlist)
+*/
+void qh_mergecycle_all(qhT *qh, facetT *facetlist, boolT *wasmerge) {
+ facetT *facet, *same, *prev, *horizon;
+ facetT *samecycle= NULL, *nextfacet, *nextsame;
+ vertexT *apex, *vertex, **vertexp;
+ int cycles=0, total=0, facets, nummerge;
+
+ trace2((qh, qh->ferr, 2031, "qh_mergecycle_all: begin\n"));
+ for (facet= facetlist; facet && (nextfacet= facet->next); facet= nextfacet) {
+ if (facet->normal)
+ continue;
+ if (!facet->mergehorizon) {
+ qh_fprintf(qh, qh->ferr, 6225, "Qhull internal error (qh_mergecycle_all): f%d without normal\n", facet->id);
+ qh_errexit(qh, qh_ERRqhull, facet, NULL);
+ }
+ horizon= SETfirstt_(facet->neighbors, facetT);
+ if (facet->f.samecycle == facet) {
+ zinc_(Zonehorizon);
+ /* merge distance done in qh_findhorizon */
+ apex= SETfirstt_(facet->vertices, vertexT);
+ FOREACHvertex_(facet->vertices) {
+ if (vertex != apex)
+ vertex->delridge= True;
+ }
+ horizon->f.newcycle= NULL;
+ qh_mergefacet(qh, facet, horizon, NULL, NULL, qh_MERGEapex);
+ }else {
+ samecycle= facet;
+ facets= 0;
+ prev= facet;
+ for (same= facet->f.samecycle; same; /* FORALLsame_cycle_(facet) */
+ same= (same == facet ? NULL :nextsame)) { /* ends at facet */
+ nextsame= same->f.samecycle;
+ if (same->cycledone || same->visible)
+ qh_infiniteloop(qh, same);
+ same->cycledone= True;
+ if (same->normal) {
+ prev->f.samecycle= same->f.samecycle; /* unlink ->mergeridge */
+ same->f.samecycle= NULL;
+ }else {
+ prev= same;
+ facets++;
+ }
+ }
+ while (nextfacet && nextfacet->cycledone) /* will delete samecycle */
+ nextfacet= nextfacet->next;
+ horizon->f.newcycle= NULL;
+ qh_mergecycle(qh, samecycle, horizon);
+ nummerge= horizon->nummerge + facets;
+ if (nummerge > qh_MAXnummerge)
+ horizon->nummerge= qh_MAXnummerge;
+ else
+ horizon->nummerge= (short unsigned int)nummerge;
+ zzinc_(Zcyclehorizon);
+ total += facets;
+ zzadd_(Zcyclefacettot, facets);
+ zmax_(Zcyclefacetmax, facets);
+ }
+ cycles++;
+ }
+ if (cycles)
+ *wasmerge= True;
+ trace1((qh, qh->ferr, 1013, "qh_mergecycle_all: merged %d same cycles or facets into coplanar horizons\n", cycles));
+} /* mergecycle_all */
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="mergecycle_facets">-</a>
+
+ qh_mergecycle_facets(qh, samecycle, newfacet )
+ finish merge of samecycle into newfacet
+
+ returns:
+ samecycle prepended to visible_list for later deletion and partitioning
+ each facet->f.replace == newfacet
+
+ newfacet moved to end of qh.facet_list
+ makes newfacet a newfacet (get's facet1->id if it was old)
+ sets newfacet->newmerge
+ clears newfacet->center (unless merging into a large facet)
+ clears newfacet->tested and ridge->tested for facet1
+
+ adds neighboring facets to facet_mergeset if redundant or degenerate
+
+ design:
+ make newfacet a new facet and set its flags
+ move samecycle facets to qh.visible_list for later deletion
+ unless newfacet is large
+ remove its centrum
+*/
+void qh_mergecycle_facets(qhT *qh, facetT *samecycle, facetT *newfacet) {
+ facetT *same, *next;
+
+ trace4((qh, qh->ferr, 4030, "qh_mergecycle_facets: make newfacet new and samecycle deleted\n"));
+ qh_removefacet(qh, newfacet); /* append as a newfacet to end of qh->facet_list */
+ qh_appendfacet(qh, newfacet);
+ newfacet->newfacet= True;
+ newfacet->simplicial= False;
+ newfacet->newmerge= True;
+
+ for (same= samecycle->f.samecycle; same; same= (same == samecycle ? NULL : next)) {
+ next= same->f.samecycle; /* reused by willdelete */
+ qh_willdelete(qh, same, newfacet);
+ }
+ if (newfacet->center
+ && qh_setsize(qh, newfacet->vertices) <= qh->hull_dim + qh_MAXnewcentrum) {
+ qh_memfree(qh, newfacet->center, qh->normal_size);
+ newfacet->center= NULL;
+ }
+ trace3((qh, qh->ferr, 3004, "qh_mergecycle_facets: merged facets from cycle f%d into f%d\n",
+ samecycle->id, newfacet->id));
+} /* mergecycle_facets */
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="mergecycle_neighbors">-</a>
+
+ qh_mergecycle_neighbors(qh, samecycle, newfacet )
+ add neighbors for samecycle facets to newfacet
+
+ returns:
+ newfacet with updated neighbors and vice-versa
+ newfacet has ridges
+ all neighbors of newfacet marked with qh.visit_id
+ samecycle facets marked with qh.visit_id-1
+ ridges updated for simplicial neighbors of samecycle with a ridge
+
+ notes:
+ assumes newfacet not in samecycle
+ usually, samecycle facets are new, simplicial facets without internal ridges
+ not so if horizon facet is coplanar to two different samecycles
+
+ see:
+ qh_mergeneighbors()
+
+ design:
+ check samecycle
+ delete neighbors from newfacet that are also in samecycle
+ for each neighbor of a facet in samecycle
+ if neighbor is simplicial
+ if first visit
+ move the neighbor relation to newfacet
+ update facet links for its ridges
+ else
+ make ridges for neighbor
+ remove samecycle reference
+ else
+ update neighbor sets
+*/
+void qh_mergecycle_neighbors(qhT *qh, facetT *samecycle, facetT *newfacet) {
+ facetT *same, *neighbor, **neighborp;
+ int delneighbors= 0, newneighbors= 0;
+ unsigned int samevisitid;
+ ridgeT *ridge, **ridgep;
+
+ samevisitid= ++qh->visit_id;
+ FORALLsame_cycle_(samecycle) {
+ if (same->visitid == samevisitid || same->visible)
+ qh_infiniteloop(qh, samecycle);
+ same->visitid= samevisitid;
+ }
+ newfacet->visitid= ++qh->visit_id;
+ trace4((qh, qh->ferr, 4031, "qh_mergecycle_neighbors: delete shared neighbors from newfacet\n"));
+ FOREACHneighbor_(newfacet) {
+ if (neighbor->visitid == samevisitid) {
+ SETref_(neighbor)= NULL; /* samecycle neighbors deleted */
+ delneighbors++;
+ }else
+ neighbor->visitid= qh->visit_id;
+ }
+ qh_setcompact(qh, newfacet->neighbors);
+
+ trace4((qh, qh->ferr, 4032, "qh_mergecycle_neighbors: update neighbors\n"));
+ FORALLsame_cycle_(samecycle) {
+ FOREACHneighbor_(same) {
+ if (neighbor->visitid == samevisitid)
+ continue;
+ if (neighbor->simplicial) {
+ if (neighbor->visitid != qh->visit_id) {
+ qh_setappend(qh, &newfacet->neighbors, neighbor);
+ qh_setreplace(qh, neighbor->neighbors, same, newfacet);
+ newneighbors++;
+ neighbor->visitid= qh->visit_id;
+ FOREACHridge_(neighbor->ridges) { /* update ridge in case of qh_makeridges */
+ if (ridge->top == same) {
+ ridge->top= newfacet;
+ break;
+ }else if (ridge->bottom == same) {
+ ridge->bottom= newfacet;
+ break;
+ }
+ }
+ }else {
+ qh_makeridges(qh, neighbor);
+ qh_setdel(neighbor->neighbors, same);
+ /* same can't be horizon facet for neighbor */
+ }
+ }else { /* non-simplicial neighbor */
+ qh_setdel(neighbor->neighbors, same);
+ if (neighbor->visitid != qh->visit_id) {
+ qh_setappend(qh, &neighbor->neighbors, newfacet);
+ qh_setappend(qh, &newfacet->neighbors, neighbor);
+ neighbor->visitid= qh->visit_id;
+ newneighbors++;
+ }
+ }
+ }
+ }
+ trace2((qh, qh->ferr, 2032, "qh_mergecycle_neighbors: deleted %d neighbors and added %d\n",
+ delneighbors, newneighbors));
+} /* mergecycle_neighbors */
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="mergecycle_ridges">-</a>
+
+ qh_mergecycle_ridges(qh, samecycle, newfacet )
+ add ridges/neighbors for facets in samecycle to newfacet
+ all new/old neighbors of newfacet marked with qh.visit_id
+ facets in samecycle marked with qh.visit_id-1
+ newfacet marked with qh.visit_id
+
+ returns:
+ newfacet has merged ridges
+
+ notes:
+ ridge already updated for simplicial neighbors of samecycle with a ridge
+
+ see:
+ qh_mergeridges()
+ qh_makeridges()
+
+ design:
+ remove ridges between newfacet and samecycle
+ for each facet in samecycle
+ for each ridge in facet
+ update facet pointers in ridge
+ skip ridges processed in qh_mergecycle_neighors
+ free ridges between newfacet and samecycle
+ free ridges between facets of samecycle (on 2nd visit)
+ append remaining ridges to newfacet
+ if simpilicial facet
+ for each neighbor of facet
+ if simplicial facet
+ and not samecycle facet or newfacet
+ make ridge between neighbor and newfacet
+*/
+void qh_mergecycle_ridges(qhT *qh, facetT *samecycle, facetT *newfacet) {
+ facetT *same, *neighbor= NULL;
+ int numold=0, numnew=0;
+ int neighbor_i, neighbor_n;
+ unsigned int samevisitid;
+ ridgeT *ridge, **ridgep;
+ boolT toporient;
+ void **freelistp; /* used if !qh_NOmem by qh_memfree_() */
+
+ trace4((qh, qh->ferr, 4033, "qh_mergecycle_ridges: delete shared ridges from newfacet\n"));
+ samevisitid= qh->visit_id -1;
+ FOREACHridge_(newfacet->ridges) {
+ neighbor= otherfacet_(ridge, newfacet);
+ if (neighbor->visitid == samevisitid)
+ SETref_(ridge)= NULL; /* ridge free'd below */
+ }
+ qh_setcompact(qh, newfacet->ridges);
+
+ trace4((qh, qh->ferr, 4034, "qh_mergecycle_ridges: add ridges to newfacet\n"));
+ FORALLsame_cycle_(samecycle) {
+ FOREACHridge_(same->ridges) {
+ if (ridge->top == same) {
+ ridge->top= newfacet;
+ neighbor= ridge->bottom;
+ }else if (ridge->bottom == same) {
+ ridge->bottom= newfacet;
+ neighbor= ridge->top;
+ }else if (ridge->top == newfacet || ridge->bottom == newfacet) {
+ qh_setappend(qh, &newfacet->ridges, ridge);
+ numold++; /* already set by qh_mergecycle_neighbors */
+ continue;
+ }else {
+ qh_fprintf(qh, qh->ferr, 6098, "qhull internal error (qh_mergecycle_ridges): bad ridge r%d\n", ridge->id);
+ qh_errexit(qh, qh_ERRqhull, NULL, ridge);
+ }
+ if (neighbor == newfacet) {
+ qh_setfree(qh, &(ridge->vertices));
+ qh_memfree_(qh, ridge, (int)sizeof(ridgeT), freelistp);
+ numold++;
+ }else if (neighbor->visitid == samevisitid) {
+ qh_setdel(neighbor->ridges, ridge);
+ qh_setfree(qh, &(ridge->vertices));
+ qh_memfree_(qh, ridge, (int)sizeof(ridgeT), freelistp);
+ numold++;
+ }else {
+ qh_setappend(qh, &newfacet->ridges, ridge);
+ numold++;
+ }
+ }
+ if (same->ridges)
+ qh_settruncate(qh, same->ridges, 0);
+ if (!same->simplicial)
+ continue;
+ FOREACHneighbor_i_(qh, same) { /* note: !newfact->simplicial */
+ if (neighbor->visitid != samevisitid && neighbor->simplicial) {
+ ridge= qh_newridge(qh);
+ ridge->vertices= qh_setnew_delnthsorted(qh, same->vertices, qh->hull_dim,
+ neighbor_i, 0);
+ toporient= same->toporient ^ (neighbor_i & 0x1);
+ if (toporient) {
+ ridge->top= newfacet;
+ ridge->bottom= neighbor;
+ }else {
+ ridge->top= neighbor;
+ ridge->bottom= newfacet;
+ }
+ qh_setappend(qh, &(newfacet->ridges), ridge);
+ qh_setappend(qh, &(neighbor->ridges), ridge);
+ numnew++;
+ }
+ }
+ }
+
+ trace2((qh, qh->ferr, 2033, "qh_mergecycle_ridges: found %d old ridges and %d new ones\n",
+ numold, numnew));
+} /* mergecycle_ridges */
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="mergecycle_vneighbors">-</a>
+
+ qh_mergecycle_vneighbors(qh, samecycle, newfacet )
+ create vertex neighbors for newfacet from vertices of facets in samecycle
+ samecycle marked with visitid == qh.visit_id - 1
+
+ returns:
+ newfacet vertices with updated neighbors
+ marks newfacet with qh.visit_id-1
+ deletes vertices that are merged away
+ sets delridge on all vertices (faster here than in mergecycle_ridges)
+
+ see:
+ qh_mergevertex_neighbors()
+
+ design:
+ for each vertex of samecycle facet
+ set vertex->delridge
+ delete samecycle facets from vertex neighbors
+ append newfacet to vertex neighbors
+ if vertex only in newfacet
+ delete it from newfacet
+ add it to qh.del_vertices for later deletion
+*/
+void qh_mergecycle_vneighbors(qhT *qh, facetT *samecycle, facetT *newfacet) {
+ facetT *neighbor, **neighborp;
+ unsigned int mergeid;
+ vertexT *vertex, **vertexp, *apex;
+ setT *vertices;
+
+ trace4((qh, qh->ferr, 4035, "qh_mergecycle_vneighbors: update vertex neighbors for newfacet\n"));
+ mergeid= qh->visit_id - 1;
+ newfacet->visitid= mergeid;
+ vertices= qh_basevertices(qh, samecycle); /* temp */
+ apex= SETfirstt_(samecycle->vertices, vertexT);
+ qh_setappend(qh, &vertices, apex);
+ FOREACHvertex_(vertices) {
+ vertex->delridge= True;
+ FOREACHneighbor_(vertex) {
+ if (neighbor->visitid == mergeid)
+ SETref_(neighbor)= NULL;
+ }
+ qh_setcompact(qh, vertex->neighbors);
+ qh_setappend(qh, &vertex->neighbors, newfacet);
+ if (!SETsecond_(vertex->neighbors)) {
+ zinc_(Zcyclevertex);
+ trace2((qh, qh->ferr, 2034, "qh_mergecycle_vneighbors: deleted v%d when merging cycle f%d into f%d\n",
+ vertex->id, samecycle->id, newfacet->id));
+ qh_setdelsorted(newfacet->vertices, vertex);
+ vertex->deleted= True;
+ qh_setappend(qh, &qh->del_vertices, vertex);
+ }
+ }
+ qh_settempfree(qh, &vertices);
+ trace3((qh, qh->ferr, 3005, "qh_mergecycle_vneighbors: merged vertices from cycle f%d into f%d\n",
+ samecycle->id, newfacet->id));
+} /* mergecycle_vneighbors */
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="mergefacet">-</a>
+
+ qh_mergefacet(qh, facet1, facet2, mindist, maxdist, mergeapex )
+ merges facet1 into facet2
+ mergeapex==qh_MERGEapex if merging new facet into coplanar horizon
+
+ returns:
+ qh.max_outside and qh.min_vertex updated
+ initializes vertex neighbors on first merge
+
+ returns:
+ facet2 contains facet1's vertices, neighbors, and ridges
+ facet2 moved to end of qh.facet_list
+ makes facet2 a newfacet
+ sets facet2->newmerge set
+ clears facet2->center (unless merging into a large facet)
+ clears facet2->tested and ridge->tested for facet1
+
+ facet1 prepended to visible_list for later deletion and partitioning
+ facet1->f.replace == facet2
+
+ adds neighboring facets to facet_mergeset if redundant or degenerate
+
+ notes:
+ mindist/maxdist may be NULL (only if both NULL)
+ traces merge if fmax_(maxdist,-mindist) > TRACEdist
+
+ see:
+ qh_mergecycle()
+
+ design:
+ trace merge and check for degenerate simplex
+ make ridges for both facets
+ update qh.max_outside, qh.max_vertex, qh.min_vertex
+ update facet2->maxoutside and keepcentrum
+ update facet2->nummerge
+ update tested flags for facet2
+ if facet1 is simplicial
+ merge facet1 into facet2
+ else
+ merge facet1's neighbors into facet2
+ merge facet1's ridges into facet2
+ merge facet1's vertices into facet2
+ merge facet1's vertex neighbors into facet2
+ add facet2's vertices to qh.new_vertexlist
+ unless qh_MERGEapex
+ test facet2 for degenerate or redundant neighbors
+ move facet1 to qh.visible_list for later deletion
+ move facet2 to end of qh.newfacet_list
+*/
+void qh_mergefacet(qhT *qh, facetT *facet1, facetT *facet2, realT *mindist, realT *maxdist, boolT mergeapex) {
+ boolT traceonce= False;
+ vertexT *vertex, **vertexp;
+ int tracerestore=0, nummerge;
+
+ if (facet1->tricoplanar || facet2->tricoplanar) {
+ if (!qh->TRInormals) {
+ qh_fprintf(qh, qh->ferr, 6226, "Qhull internal error (qh_mergefacet): does not work for tricoplanar facets. Use option 'Q11'\n");
+ qh_errexit2(qh, qh_ERRqhull, facet1, facet2);
+ }
+ if (facet2->tricoplanar) {
+ facet2->tricoplanar= False;
+ facet2->keepcentrum= False;
+ }
+ }
+ zzinc_(Ztotmerge);
+ if (qh->REPORTfreq2 && qh->POSTmerging) {
+ if (zzval_(Ztotmerge) > qh->mergereport + qh->REPORTfreq2)
+ qh_tracemerging(qh);
+ }
+#ifndef qh_NOtrace
+ if (qh->build_cnt >= qh->RERUN) {
+ if (mindist && (-*mindist > qh->TRACEdist || *maxdist > qh->TRACEdist)) {
+ tracerestore= 0;
+ qh->IStracing= qh->TRACElevel;
+ traceonce= True;
+ qh_fprintf(qh, qh->ferr, 8075, "qh_mergefacet: ========= trace wide merge #%d(%2.2g) for f%d into f%d, last point was p%d\n", zzval_(Ztotmerge),
+ fmax_(-*mindist, *maxdist), facet1->id, facet2->id, qh->furthest_id);
+ }else if (facet1 == qh->tracefacet || facet2 == qh->tracefacet) {
+ tracerestore= qh->IStracing;
+ qh->IStracing= 4;
+ traceonce= True;
+ qh_fprintf(qh, qh->ferr, 8076, "qh_mergefacet: ========= trace merge #%d involving f%d, furthest is p%d\n",
+ zzval_(Ztotmerge), qh->tracefacet_id, qh->furthest_id);
+ }
+ }
+ if (qh->IStracing >= 2) {
+ realT mergemin= -2;
+ realT mergemax= -2;
+
+ if (mindist) {
+ mergemin= *mindist;
+ mergemax= *maxdist;
+ }
+ qh_fprintf(qh, qh->ferr, 8077, "qh_mergefacet: #%d merge f%d into f%d, mindist= %2.2g, maxdist= %2.2g\n",
+ zzval_(Ztotmerge), facet1->id, facet2->id, mergemin, mergemax);
+ }
+#endif /* !qh_NOtrace */
+ if (facet1 == facet2 || facet1->visible || facet2->visible) {
+ qh_fprintf(qh, qh->ferr, 6099, "qhull internal error (qh_mergefacet): either f%d and f%d are the same or one is a visible facet\n",
+ facet1->id, facet2->id);
+ qh_errexit2(qh, qh_ERRqhull, facet1, facet2);
+ }
+ if (qh->num_facets - qh->num_visible <= qh->hull_dim + 1) {
+ qh_fprintf(qh, qh->ferr, 6227, "\n\
+qhull precision error: Only %d facets remain. Can not merge another\n\
+pair. The input is too degenerate or the convexity constraints are\n\
+too strong.\n", qh->hull_dim+1);
+ if (qh->hull_dim >= 5 && !qh->MERGEexact)
+ qh_fprintf(qh, qh->ferr, 8079, "Option 'Qx' may avoid this problem.\n");
+ qh_errexit(qh, qh_ERRprec, NULL, NULL);
+ }
+ if (!qh->VERTEXneighbors)
+ qh_vertexneighbors(qh);
+ qh_makeridges(qh, facet1);
+ qh_makeridges(qh, facet2);
+ if (qh->IStracing >=4)
+ qh_errprint(qh, "MERGING", facet1, facet2, NULL, NULL);
+ if (mindist) {
+ maximize_(qh->max_outside, *maxdist);
+ maximize_(qh->max_vertex, *maxdist);
+#if qh_MAXoutside
+ maximize_(facet2->maxoutside, *maxdist);
+#endif
+ minimize_(qh->min_vertex, *mindist);
+ if (!facet2->keepcentrum
+ && (*maxdist > qh->WIDEfacet || *mindist < -qh->WIDEfacet)) {
+ facet2->keepcentrum= True;
+ zinc_(Zwidefacet);
+ }
+ }
+ nummerge= facet1->nummerge + facet2->nummerge + 1;
+ if (nummerge >= qh_MAXnummerge)
+ facet2->nummerge= qh_MAXnummerge;
+ else
+ facet2->nummerge= (short unsigned int)nummerge;
+ facet2->newmerge= True;
+ facet2->dupridge= False;
+ qh_updatetested(qh, facet1, facet2);
+ if (qh->hull_dim > 2 && qh_setsize(qh, facet1->vertices) == qh->hull_dim)
+ qh_mergesimplex(qh, facet1, facet2, mergeapex);
+ else {
+ qh->vertex_visit++;
+ FOREACHvertex_(facet2->vertices)
+ vertex->visitid= qh->vertex_visit;
+ if (qh->hull_dim == 2)
+ qh_mergefacet2d(qh, facet1, facet2);
+ else {
+ qh_mergeneighbors(qh, facet1, facet2);
+ qh_mergevertices(qh, facet1->vertices, &facet2->vertices);
+ }
+ qh_mergeridges(qh, facet1, facet2);
+ qh_mergevertex_neighbors(qh, facet1, facet2);
+ if (!facet2->newfacet)
+ qh_newvertices(qh, facet2->vertices);
+ }
+ if (!mergeapex)
+ qh_degen_redundant_neighbors(qh, facet2, facet1);
+ if (facet2->coplanar || !facet2->newfacet) {
+ zinc_(Zmergeintohorizon);
+ }else if (!facet1->newfacet && facet2->newfacet) {
+ zinc_(Zmergehorizon);
+ }else {
+ zinc_(Zmergenew);
+ }
+ qh_willdelete(qh, facet1, facet2);
+ qh_removefacet(qh, facet2); /* append as a newfacet to end of qh->facet_list */
+ qh_appendfacet(qh, facet2);
+ facet2->newfacet= True;
+ facet2->tested= False;
+ qh_tracemerge(qh, facet1, facet2);
+ if (traceonce) {
+ qh_fprintf(qh, qh->ferr, 8080, "qh_mergefacet: end of wide tracing\n");
+ qh->IStracing= tracerestore;
+ }
+} /* mergefacet */
+
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="mergefacet2d">-</a>
+
+ qh_mergefacet2d(qh, facet1, facet2 )
+ in 2d, merges neighbors and vertices of facet1 into facet2
+
+ returns:
+ build ridges for neighbors if necessary
+ facet2 looks like a simplicial facet except for centrum, ridges
+ neighbors are opposite the corresponding vertex
+ maintains orientation of facet2
+
+ notes:
+ qh_mergefacet() retains non-simplicial structures
+ they are not needed in 2d, but later routines may use them
+ preserves qh.vertex_visit for qh_mergevertex_neighbors()
+
+ design:
+ get vertices and neighbors
+ determine new vertices and neighbors
+ set new vertices and neighbors and adjust orientation
+ make ridges for new neighbor if needed
+*/
+void qh_mergefacet2d(qhT *qh, facetT *facet1, facetT *facet2) {
+ vertexT *vertex1A, *vertex1B, *vertex2A, *vertex2B, *vertexA, *vertexB;
+ facetT *neighbor1A, *neighbor1B, *neighbor2A, *neighbor2B, *neighborA, *neighborB;
+
+ vertex1A= SETfirstt_(facet1->vertices, vertexT);
+ vertex1B= SETsecondt_(facet1->vertices, vertexT);
+ vertex2A= SETfirstt_(facet2->vertices, vertexT);
+ vertex2B= SETsecondt_(facet2->vertices, vertexT);
+ neighbor1A= SETfirstt_(facet1->neighbors, facetT);
+ neighbor1B= SETsecondt_(facet1->neighbors, facetT);
+ neighbor2A= SETfirstt_(facet2->neighbors, facetT);
+ neighbor2B= SETsecondt_(facet2->neighbors, facetT);
+ if (vertex1A == vertex2A) {
+ vertexA= vertex1B;
+ vertexB= vertex2B;
+ neighborA= neighbor2A;
+ neighborB= neighbor1A;
+ }else if (vertex1A == vertex2B) {
+ vertexA= vertex1B;
+ vertexB= vertex2A;
+ neighborA= neighbor2B;
+ neighborB= neighbor1A;
+ }else if (vertex1B == vertex2A) {
+ vertexA= vertex1A;
+ vertexB= vertex2B;
+ neighborA= neighbor2A;
+ neighborB= neighbor1B;
+ }else { /* 1B == 2B */
+ vertexA= vertex1A;
+ vertexB= vertex2A;
+ neighborA= neighbor2B;
+ neighborB= neighbor1B;
+ }
+ /* vertexB always from facet2, neighborB always from facet1 */
+ if (vertexA->id > vertexB->id) {
+ SETfirst_(facet2->vertices)= vertexA;
+ SETsecond_(facet2->vertices)= vertexB;
+ if (vertexB == vertex2A)
+ facet2->toporient= !facet2->toporient;
+ SETfirst_(facet2->neighbors)= neighborA;
+ SETsecond_(facet2->neighbors)= neighborB;
+ }else {
+ SETfirst_(facet2->vertices)= vertexB;
+ SETsecond_(facet2->vertices)= vertexA;
+ if (vertexB == vertex2B)
+ facet2->toporient= !facet2->toporient;
+ SETfirst_(facet2->neighbors)= neighborB;
+ SETsecond_(facet2->neighbors)= neighborA;
+ }
+ qh_makeridges(qh, neighborB);
+ qh_setreplace(qh, neighborB->neighbors, facet1, facet2);
+ trace4((qh, qh->ferr, 4036, "qh_mergefacet2d: merged v%d and neighbor f%d of f%d into f%d\n",
+ vertexA->id, neighborB->id, facet1->id, facet2->id));
+} /* mergefacet2d */
+
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="mergeneighbors">-</a>
+
+ qh_mergeneighbors(qh, facet1, facet2 )
+ merges the neighbors of facet1 into facet2
+
+ see:
+ qh_mergecycle_neighbors()
+
+ design:
+ for each neighbor of facet1
+ if neighbor is also a neighbor of facet2
+ if neighbor is simpilicial
+ make ridges for later deletion as a degenerate facet
+ update its neighbor set
+ else
+ move the neighbor relation to facet2
+ remove the neighbor relation for facet1 and facet2
+*/
+void qh_mergeneighbors(qhT *qh, facetT *facet1, facetT *facet2) {
+ facetT *neighbor, **neighborp;
+
+ trace4((qh, qh->ferr, 4037, "qh_mergeneighbors: merge neighbors of f%d and f%d\n",
+ facet1->id, facet2->id));
+ qh->visit_id++;
+ FOREACHneighbor_(facet2) {
+ neighbor->visitid= qh->visit_id;
+ }
+ FOREACHneighbor_(facet1) {
+ if (neighbor->visitid == qh->visit_id) {
+ if (neighbor->simplicial) /* is degen, needs ridges */
+ qh_makeridges(qh, neighbor);
+ if (SETfirstt_(neighbor->neighbors, facetT) != facet1) /*keep newfacet->horizon*/
+ qh_setdel(neighbor->neighbors, facet1);
+ else {
+ qh_setdel(neighbor->neighbors, facet2);
+ qh_setreplace(qh, neighbor->neighbors, facet1, facet2);
+ }
+ }else if (neighbor != facet2) {
+ qh_setappend(qh, &(facet2->neighbors), neighbor);
+ qh_setreplace(qh, neighbor->neighbors, facet1, facet2);
+ }
+ }
+ qh_setdel(facet1->neighbors, facet2); /* here for makeridges */
+ qh_setdel(facet2->neighbors, facet1);
+} /* mergeneighbors */
+
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="mergeridges">-</a>
+
+ qh_mergeridges(qh, facet1, facet2 )
+ merges the ridge set of facet1 into facet2
+
+ returns:
+ may delete all ridges for a vertex
+ sets vertex->delridge on deleted ridges
+
+ see:
+ qh_mergecycle_ridges()
+
+ design:
+ delete ridges between facet1 and facet2
+ mark (delridge) vertices on these ridges for later testing
+ for each remaining ridge
+ rename facet1 to facet2
+*/
+void qh_mergeridges(qhT *qh, facetT *facet1, facetT *facet2) {
+ ridgeT *ridge, **ridgep;
+ vertexT *vertex, **vertexp;
+
+ trace4((qh, qh->ferr, 4038, "qh_mergeridges: merge ridges of f%d and f%d\n",
+ facet1->id, facet2->id));
+ FOREACHridge_(facet2->ridges) {
+ if ((ridge->top == facet1) || (ridge->bottom == facet1)) {
+ FOREACHvertex_(ridge->vertices)
+ vertex->delridge= True;
+ qh_delridge(qh, ridge); /* expensive in high-d, could rebuild */
+ ridgep--; /*repeat*/
+ }
+ }
+ FOREACHridge_(facet1->ridges) {
+ if (ridge->top == facet1)
+ ridge->top= facet2;
+ else
+ ridge->bottom= facet2;
+ qh_setappend(qh, &(facet2->ridges), ridge);
+ }
+} /* mergeridges */
+
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="mergesimplex">-</a>
+
+ qh_mergesimplex(qh, facet1, facet2, mergeapex )
+ merge simplicial facet1 into facet2
+ mergeapex==qh_MERGEapex if merging samecycle into horizon facet
+ vertex id is latest (most recently created)
+ facet1 may be contained in facet2
+ ridges exist for both facets
+
+ returns:
+ facet2 with updated vertices, ridges, neighbors
+ updated neighbors for facet1's vertices
+ facet1 not deleted
+ sets vertex->delridge on deleted ridges
+
+ notes:
+ special case code since this is the most common merge
+ called from qh_mergefacet()
+
+ design:
+ if qh_MERGEapex
+ add vertices of facet2 to qh.new_vertexlist if necessary
+ add apex to facet2
+ else
+ for each ridge between facet1 and facet2
+ set vertex->delridge
+ determine the apex for facet1 (i.e., vertex to be merged)
+ unless apex already in facet2
+ insert apex into vertices for facet2
+ add vertices of facet2 to qh.new_vertexlist if necessary
+ add apex to qh.new_vertexlist if necessary
+ for each vertex of facet1
+ if apex
+ rename facet1 to facet2 in its vertex neighbors
+ else
+ delete facet1 from vertex neighors
+ if only in facet2
+ add vertex to qh.del_vertices for later deletion
+ for each ridge of facet1
+ delete ridges between facet1 and facet2
+ append other ridges to facet2 after renaming facet to facet2
+*/
+void qh_mergesimplex(qhT *qh, facetT *facet1, facetT *facet2, boolT mergeapex) {
+ vertexT *vertex, **vertexp, *apex;
+ ridgeT *ridge, **ridgep;
+ boolT issubset= False;
+ int vertex_i= -1, vertex_n;
+ facetT *neighbor, **neighborp, *otherfacet;
+
+ if (mergeapex) {
+ if (!facet2->newfacet)
+ qh_newvertices(qh, facet2->vertices); /* apex is new */
+ apex= SETfirstt_(facet1->vertices, vertexT);
+ if (SETfirstt_(facet2->vertices, vertexT) != apex)
+ qh_setaddnth(qh, &facet2->vertices, 0, apex); /* apex has last id */
+ else
+ issubset= True;
+ }else {
+ zinc_(Zmergesimplex);
+ FOREACHvertex_(facet1->vertices)
+ vertex->seen= False;
+ FOREACHridge_(facet1->ridges) {
+ if (otherfacet_(ridge, facet1) == facet2) {
+ FOREACHvertex_(ridge->vertices) {
+ vertex->seen= True;
+ vertex->delridge= True;
+ }
+ break;
+ }
+ }
+ FOREACHvertex_(facet1->vertices) {
+ if (!vertex->seen)
+ break; /* must occur */
+ }
+ apex= vertex;
+ trace4((qh, qh->ferr, 4039, "qh_mergesimplex: merge apex v%d of f%d into facet f%d\n",
+ apex->id, facet1->id, facet2->id));
+ FOREACHvertex_i_(qh, facet2->vertices) {
+ if (vertex->id < apex->id) {
+ break;
+ }else if (vertex->id == apex->id) {
+ issubset= True;
+ break;
+ }
+ }
+ if (!issubset)
+ qh_setaddnth(qh, &facet2->vertices, vertex_i, apex);
+ if (!facet2->newfacet)
+ qh_newvertices(qh, facet2->vertices);
+ else if (!apex->newlist) {
+ qh_removevertex(qh, apex);
+ qh_appendvertex(qh, apex);
+ }
+ }
+ trace4((qh, qh->ferr, 4040, "qh_mergesimplex: update vertex neighbors of f%d\n",
+ facet1->id));
+ FOREACHvertex_(facet1->vertices) {
+ if (vertex == apex && !issubset)
+ qh_setreplace(qh, vertex->neighbors, facet1, facet2);
+ else {
+ qh_setdel(vertex->neighbors, facet1);
+ if (!SETsecond_(vertex->neighbors))
+ qh_mergevertex_del(qh, vertex, facet1, facet2);
+ }
+ }
+ trace4((qh, qh->ferr, 4041, "qh_mergesimplex: merge ridges and neighbors of f%d into f%d\n",
+ facet1->id, facet2->id));
+ qh->visit_id++;
+ FOREACHneighbor_(facet2)
+ neighbor->visitid= qh->visit_id;
+ FOREACHridge_(facet1->ridges) {
+ otherfacet= otherfacet_(ridge, facet1);
+ if (otherfacet == facet2) {
+ qh_setdel(facet2->ridges, ridge);
+ qh_setfree(qh, &(ridge->vertices));
+ qh_memfree(qh, ridge, (int)sizeof(ridgeT));
+ qh_setdel(facet2->neighbors, facet1);
+ }else {
+ qh_setappend(qh, &facet2->ridges, ridge);
+ if (otherfacet->visitid != qh->visit_id) {
+ qh_setappend(qh, &facet2->neighbors, otherfacet);
+ qh_setreplace(qh, otherfacet->neighbors, facet1, facet2);
+ otherfacet->visitid= qh->visit_id;
+ }else {
+ if (otherfacet->simplicial) /* is degen, needs ridges */
+ qh_makeridges(qh, otherfacet);
+ if (SETfirstt_(otherfacet->neighbors, facetT) != facet1)
+ qh_setdel(otherfacet->neighbors, facet1);
+ else { /*keep newfacet->neighbors->horizon*/
+ qh_setdel(otherfacet->neighbors, facet2);
+ qh_setreplace(qh, otherfacet->neighbors, facet1, facet2);
+ }
+ }
+ if (ridge->top == facet1) /* wait until after qh_makeridges */
+ ridge->top= facet2;
+ else
+ ridge->bottom= facet2;
+ }
+ }
+ SETfirst_(facet1->ridges)= NULL; /* it will be deleted */
+ trace3((qh, qh->ferr, 3006, "qh_mergesimplex: merged simplex f%d apex v%d into facet f%d\n",
+ facet1->id, getid_(apex), facet2->id));
+} /* mergesimplex */
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="mergevertex_del">-</a>
+
+ qh_mergevertex_del(qh, vertex, facet1, facet2 )
+ delete a vertex because of merging facet1 into facet2
+
+ returns:
+ deletes vertex from facet2
+ adds vertex to qh.del_vertices for later deletion
+*/
+void qh_mergevertex_del(qhT *qh, vertexT *vertex, facetT *facet1, facetT *facet2) {
+
+ zinc_(Zmergevertex);
+ trace2((qh, qh->ferr, 2035, "qh_mergevertex_del: deleted v%d when merging f%d into f%d\n",
+ vertex->id, facet1->id, facet2->id));
+ qh_setdelsorted(facet2->vertices, vertex);
+ vertex->deleted= True;
+ qh_setappend(qh, &qh->del_vertices, vertex);
+} /* mergevertex_del */
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="mergevertex_neighbors">-</a>
+
+ qh_mergevertex_neighbors(qh, facet1, facet2 )
+ merge the vertex neighbors of facet1 to facet2
+
+ returns:
+ if vertex is current qh.vertex_visit
+ deletes facet1 from vertex->neighbors
+ else
+ renames facet1 to facet2 in vertex->neighbors
+ deletes vertices if only one neighbor
+
+ notes:
+ assumes vertex neighbor sets are good
+*/
+void qh_mergevertex_neighbors(qhT *qh, facetT *facet1, facetT *facet2) {
+ vertexT *vertex, **vertexp;
+
+ trace4((qh, qh->ferr, 4042, "qh_mergevertex_neighbors: merge vertex neighbors of f%d and f%d\n",
+ facet1->id, facet2->id));
+ if (qh->tracevertex) {
+ qh_fprintf(qh, qh->ferr, 8081, "qh_mergevertex_neighbors: of f%d and f%d at furthest p%d f0= %p\n",
+ facet1->id, facet2->id, qh->furthest_id, qh->tracevertex->neighbors->e[0].p);
+ qh_errprint(qh, "TRACE", NULL, NULL, NULL, qh->tracevertex);
+ }
+ FOREACHvertex_(facet1->vertices) {
+ if (vertex->visitid != qh->vertex_visit)
+ qh_setreplace(qh, vertex->neighbors, facet1, facet2);
+ else {
+ qh_setdel(vertex->neighbors, facet1);
+ if (!SETsecond_(vertex->neighbors))
+ qh_mergevertex_del(qh, vertex, facet1, facet2);
+ }
+ }
+ if (qh->tracevertex)
+ qh_errprint(qh, "TRACE", NULL, NULL, NULL, qh->tracevertex);
+} /* mergevertex_neighbors */
+
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="mergevertices">-</a>
+
+ qh_mergevertices(qh, vertices1, vertices2 )
+ merges the vertex set of facet1 into facet2
+
+ returns:
+ replaces vertices2 with merged set
+ preserves vertex_visit for qh_mergevertex_neighbors
+ updates qh.newvertex_list
+
+ design:
+ create a merged set of both vertices (in inverse id order)
+*/
+void qh_mergevertices(qhT *qh, setT *vertices1, setT **vertices2) {
+ int newsize= qh_setsize(qh, vertices1)+qh_setsize(qh, *vertices2) - qh->hull_dim + 1;
+ setT *mergedvertices;
+ vertexT *vertex, **vertexp, **vertex2= SETaddr_(*vertices2, vertexT);
+
+ mergedvertices= qh_settemp(qh, newsize);
+ FOREACHvertex_(vertices1) {
+ if (!*vertex2 || vertex->id > (*vertex2)->id)
+ qh_setappend(qh, &mergedvertices, vertex);
+ else {
+ while (*vertex2 && (*vertex2)->id > vertex->id)
+ qh_setappend(qh, &mergedvertices, *vertex2++);
+ if (!*vertex2 || (*vertex2)->id < vertex->id)
+ qh_setappend(qh, &mergedvertices, vertex);
+ else
+ qh_setappend(qh, &mergedvertices, *vertex2++);
+ }
+ }
+ while (*vertex2)
+ qh_setappend(qh, &mergedvertices, *vertex2++);
+ if (newsize < qh_setsize(qh, mergedvertices)) {
+ qh_fprintf(qh, qh->ferr, 6100, "qhull internal error (qh_mergevertices): facets did not share a ridge\n");
+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+ }
+ qh_setfree(qh, vertices2);
+ *vertices2= mergedvertices;
+ qh_settemppop(qh);
+} /* mergevertices */
+
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="neighbor_intersections">-</a>
+
+ qh_neighbor_intersections(qh, vertex )
+ return intersection of all vertices in vertex->neighbors except for vertex
+
+ returns:
+ returns temporary set of vertices
+ does not include vertex
+ NULL if a neighbor is simplicial
+ NULL if empty set
+
+ notes:
+ used for renaming vertices
+
+ design:
+ initialize the intersection set with vertices of the first two neighbors
+ delete vertex from the intersection
+ for each remaining neighbor
+ intersect its vertex set with the intersection set
+ return NULL if empty
+ return the intersection set
+*/
+setT *qh_neighbor_intersections(qhT *qh, vertexT *vertex) {
+ facetT *neighbor, **neighborp, *neighborA, *neighborB;
+ setT *intersect;
+ int neighbor_i, neighbor_n;
+
+ FOREACHneighbor_(vertex) {
+ if (neighbor->simplicial)
+ return NULL;
+ }
+ neighborA= SETfirstt_(vertex->neighbors, facetT);
+ neighborB= SETsecondt_(vertex->neighbors, facetT);
+ zinc_(Zintersectnum);
+ if (!neighborA)
+ return NULL;
+ if (!neighborB)
+ intersect= qh_setcopy(qh, neighborA->vertices, 0);
+ else
+ intersect= qh_vertexintersect_new(qh, neighborA->vertices, neighborB->vertices);
+ qh_settemppush(qh, intersect);
+ qh_setdelsorted(intersect, vertex);
+ FOREACHneighbor_i_(qh, vertex) {
+ if (neighbor_i >= 2) {
+ zinc_(Zintersectnum);
+ qh_vertexintersect(qh, &intersect, neighbor->vertices);
+ if (!SETfirst_(intersect)) {
+ zinc_(Zintersectfail);
+ qh_settempfree(qh, &intersect);
+ return NULL;
+ }
+ }
+ }
+ trace3((qh, qh->ferr, 3007, "qh_neighbor_intersections: %d vertices in neighbor intersection of v%d\n",
+ qh_setsize(qh, intersect), vertex->id));
+ return intersect;
+} /* neighbor_intersections */
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="newvertices">-</a>
+
+ qh_newvertices(qh, vertices )
+ add vertices to end of qh.vertex_list (marks as new vertices)
+
+ returns:
+ vertices on qh.newvertex_list
+ vertex->newlist set
+*/
+void qh_newvertices(qhT *qh, setT *vertices) {
+ vertexT *vertex, **vertexp;
+
+ FOREACHvertex_(vertices) {
+ if (!vertex->newlist) {
+ qh_removevertex(qh, vertex);
+ qh_appendvertex(qh, vertex);
+ }
+ }
+} /* newvertices */
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="reducevertices">-</a>
+
+ qh_reducevertices(qh)
+ reduce extra vertices, shared vertices, and redundant vertices
+ facet->newmerge is set if merged since last call
+ if !qh.MERGEvertices, only removes extra vertices
+
+ returns:
+ True if also merged degen_redundant facets
+ vertices are renamed if possible
+ clears facet->newmerge and vertex->delridge
+
+ notes:
+ ignored if 2-d
+
+ design:
+ merge any degenerate or redundant facets
+ for each newly merged facet
+ remove extra vertices
+ if qh.MERGEvertices
+ for each newly merged facet
+ for each vertex
+ if vertex was on a deleted ridge
+ rename vertex if it is shared
+ remove delridge flag from new vertices
+*/
+boolT qh_reducevertices(qhT *qh) {
+ int numshare=0, numrename= 0;
+ boolT degenredun= False;
+ facetT *newfacet;
+ vertexT *vertex, **vertexp;
+
+ if (qh->hull_dim == 2)
+ return False;
+ if (qh_merge_degenredundant(qh))
+ degenredun= True;
+ LABELrestart:
+ FORALLnew_facets {
+ if (newfacet->newmerge) {
+ if (!qh->MERGEvertices)
+ newfacet->newmerge= False;
+ qh_remove_extravertices(qh, newfacet);
+ }
+ }
+ if (!qh->MERGEvertices)
+ return False;
+ FORALLnew_facets {
+ if (newfacet->newmerge) {
+ newfacet->newmerge= False;
+ FOREACHvertex_(newfacet->vertices) {
+ if (vertex->delridge) {
+ if (qh_rename_sharedvertex(qh, vertex, newfacet)) {
+ numshare++;
+ vertexp--; /* repeat since deleted vertex */
+ }
+ }
+ }
+ }
+ }
+ FORALLvertex_(qh->newvertex_list) {
+ if (vertex->delridge && !vertex->deleted) {
+ vertex->delridge= False;
+ if (qh->hull_dim >= 4 && qh_redundant_vertex(qh, vertex)) {
+ numrename++;
+ if (qh_merge_degenredundant(qh)) {
+ degenredun= True;
+ goto LABELrestart;
+ }
+ }
+ }
+ }
+ trace1((qh, qh->ferr, 1014, "qh_reducevertices: renamed %d shared vertices and %d redundant vertices. Degen? %d\n",
+ numshare, numrename, degenredun));
+ return degenredun;
+} /* reducevertices */
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="redundant_vertex">-</a>
+
+ qh_redundant_vertex(qh, vertex )
+ detect and rename a redundant vertex
+ vertices have full vertex->neighbors
+
+ returns:
+ returns true if find a redundant vertex
+ deletes vertex(vertex->deleted)
+
+ notes:
+ only needed if vertex->delridge and hull_dim >= 4
+ may add degenerate facets to qh.facet_mergeset
+ doesn't change vertex->neighbors or create redundant facets
+
+ design:
+ intersect vertices of all facet neighbors of vertex
+ determine ridges for these vertices
+ if find a new vertex for vertex amoung these ridges and vertices
+ rename vertex to the new vertex
+*/
+vertexT *qh_redundant_vertex(qhT *qh, vertexT *vertex) {
+ vertexT *newvertex= NULL;
+ setT *vertices, *ridges;
+
+ trace3((qh, qh->ferr, 3008, "qh_redundant_vertex: check if v%d can be renamed\n", vertex->id));
+ if ((vertices= qh_neighbor_intersections(qh, vertex))) {
+ ridges= qh_vertexridges(qh, vertex);
+ if ((newvertex= qh_find_newvertex(qh, vertex, vertices, ridges)))
+ qh_renamevertex(qh, vertex, newvertex, ridges, NULL, NULL);
+ qh_settempfree(qh, &ridges);
+ qh_settempfree(qh, &vertices);
+ }
+ return newvertex;
+} /* redundant_vertex */
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="remove_extravertices">-</a>
+
+ qh_remove_extravertices(qh, facet )
+ remove extra vertices from non-simplicial facets
+
+ returns:
+ returns True if it finds them
+
+ design:
+ for each vertex in facet
+ if vertex not in a ridge (i.e., no longer used)
+ delete vertex from facet
+ delete facet from vertice's neighbors
+ unless vertex in another facet
+ add vertex to qh.del_vertices for later deletion
+*/
+boolT qh_remove_extravertices(qhT *qh, facetT *facet) {
+ ridgeT *ridge, **ridgep;
+ vertexT *vertex, **vertexp;
+ boolT foundrem= False;
+
+ trace4((qh, qh->ferr, 4043, "qh_remove_extravertices: test f%d for extra vertices\n",
+ facet->id));
+ FOREACHvertex_(facet->vertices)
+ vertex->seen= False;
+ FOREACHridge_(facet->ridges) {
+ FOREACHvertex_(ridge->vertices)
+ vertex->seen= True;
+ }
+ FOREACHvertex_(facet->vertices) {
+ if (!vertex->seen) {
+ foundrem= True;
+ zinc_(Zremvertex);
+ qh_setdelsorted(facet->vertices, vertex);
+ qh_setdel(vertex->neighbors, facet);
+ if (!qh_setsize(qh, vertex->neighbors)) {
+ vertex->deleted= True;
+ qh_setappend(qh, &qh->del_vertices, vertex);
+ zinc_(Zremvertexdel);
+ trace2((qh, qh->ferr, 2036, "qh_remove_extravertices: v%d deleted because it's lost all ridges\n", vertex->id));
+ }else
+ trace3((qh, qh->ferr, 3009, "qh_remove_extravertices: v%d removed from f%d because it's lost all ridges\n", vertex->id, facet->id));
+ vertexp--; /*repeat*/
+ }
+ }
+ return foundrem;
+} /* remove_extravertices */
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="rename_sharedvertex">-</a>
+
+ qh_rename_sharedvertex(qh, vertex, facet )
+ detect and rename if shared vertex in facet
+ vertices have full ->neighbors
+
+ returns:
+ newvertex or NULL
+ the vertex may still exist in other facets (i.e., a neighbor was pinched)
+ does not change facet->neighbors
+ updates vertex->neighbors
+
+ notes:
+ a shared vertex for a facet is only in ridges to one neighbor
+ this may undo a pinched facet
+
+ it does not catch pinches involving multiple facets. These appear
+ to be difficult to detect, since an exhaustive search is too expensive.
+
+ design:
+ if vertex only has two neighbors
+ determine the ridges that contain the vertex
+ determine the vertices shared by both neighbors
+ if can find a new vertex in this set
+ rename the vertex to the new vertex
+*/
+vertexT *qh_rename_sharedvertex(qhT *qh, vertexT *vertex, facetT *facet) {
+ facetT *neighbor, **neighborp, *neighborA= NULL;
+ setT *vertices, *ridges;
+ vertexT *newvertex;
+
+ if (qh_setsize(qh, vertex->neighbors) == 2) {
+ neighborA= SETfirstt_(vertex->neighbors, facetT);
+ if (neighborA == facet)
+ neighborA= SETsecondt_(vertex->neighbors, facetT);
+ }else if (qh->hull_dim == 3)
+ return NULL;
+ else {
+ qh->visit_id++;
+ FOREACHneighbor_(facet)
+ neighbor->visitid= qh->visit_id;
+ FOREACHneighbor_(vertex) {
+ if (neighbor->visitid == qh->visit_id) {
+ if (neighborA)
+ return NULL;
+ neighborA= neighbor;
+ }
+ }
+ if (!neighborA) {
+ qh_fprintf(qh, qh->ferr, 6101, "qhull internal error (qh_rename_sharedvertex): v%d's neighbors not in f%d\n",
+ vertex->id, facet->id);
+ qh_errprint(qh, "ERRONEOUS", facet, NULL, NULL, vertex);
+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+ }
+ }
+ /* the vertex is shared by facet and neighborA */
+ ridges= qh_settemp(qh, qh->TEMPsize);
+ neighborA->visitid= ++qh->visit_id;
+ qh_vertexridges_facet(qh, vertex, facet, &ridges);
+ trace2((qh, qh->ferr, 2037, "qh_rename_sharedvertex: p%d(v%d) is shared by f%d(%d ridges) and f%d\n",
+ qh_pointid(qh, vertex->point), vertex->id, facet->id, qh_setsize(qh, ridges), neighborA->id));
+ zinc_(Zintersectnum);
+ vertices= qh_vertexintersect_new(qh, facet->vertices, neighborA->vertices);
+ qh_setdel(vertices, vertex);
+ qh_settemppush(qh, vertices);
+ if ((newvertex= qh_find_newvertex(qh, vertex, vertices, ridges)))
+ qh_renamevertex(qh, vertex, newvertex, ridges, facet, neighborA);
+ qh_settempfree(qh, &vertices);
+ qh_settempfree(qh, &ridges);
+ return newvertex;
+} /* rename_sharedvertex */
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="renameridgevertex">-</a>
+
+ qh_renameridgevertex(qh, ridge, oldvertex, newvertex )
+ renames oldvertex as newvertex in ridge
+
+ returns:
+
+ design:
+ delete oldvertex from ridge
+ if newvertex already in ridge
+ copy ridge->noconvex to another ridge if possible
+ delete the ridge
+ else
+ insert newvertex into the ridge
+ adjust the ridge's orientation
+*/
+void qh_renameridgevertex(qhT *qh, ridgeT *ridge, vertexT *oldvertex, vertexT *newvertex) {
+ int nth= 0, oldnth;
+ facetT *temp;
+ vertexT *vertex, **vertexp;
+
+ oldnth= qh_setindex(ridge->vertices, oldvertex);
+ qh_setdelnthsorted(qh, ridge->vertices, oldnth);
+ FOREACHvertex_(ridge->vertices) {
+ if (vertex == newvertex) {
+ zinc_(Zdelridge);
+ if (ridge->nonconvex) /* only one ridge has nonconvex set */
+ qh_copynonconvex(qh, ridge);
+ trace2((qh, qh->ferr, 2038, "qh_renameridgevertex: ridge r%d deleted. It contained both v%d and v%d\n",
+ ridge->id, oldvertex->id, newvertex->id));
+ qh_delridge(qh, ridge);
+ return;
+ }
+ if (vertex->id < newvertex->id)
+ break;
+ nth++;
+ }
+ qh_setaddnth(qh, &ridge->vertices, nth, newvertex);
+ if (abs(oldnth - nth)%2) {
+ trace3((qh, qh->ferr, 3010, "qh_renameridgevertex: swapped the top and bottom of ridge r%d\n",
+ ridge->id));
+ temp= ridge->top;
+ ridge->top= ridge->bottom;
+ ridge->bottom= temp;
+ }
+} /* renameridgevertex */
+
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="renamevertex">-</a>
+
+ qh_renamevertex(qh, oldvertex, newvertex, ridges, oldfacet, neighborA )
+ renames oldvertex as newvertex in ridges
+ gives oldfacet/neighborA if oldvertex is shared between two facets
+
+ returns:
+ oldvertex may still exist afterwards
+
+
+ notes:
+ can not change neighbors of newvertex (since it's a subset)
+
+ design:
+ for each ridge in ridges
+ rename oldvertex to newvertex and delete degenerate ridges
+ if oldfacet not defined
+ for each neighbor of oldvertex
+ delete oldvertex from neighbor's vertices
+ remove extra vertices from neighbor
+ add oldvertex to qh.del_vertices
+ else if oldvertex only between oldfacet and neighborA
+ delete oldvertex from oldfacet and neighborA
+ add oldvertex to qh.del_vertices
+ else oldvertex is in oldfacet and neighborA and other facets (i.e., pinched)
+ delete oldvertex from oldfacet
+ delete oldfacet from oldvertice's neighbors
+ remove extra vertices (e.g., oldvertex) from neighborA
+*/
+void qh_renamevertex(qhT *qh, vertexT *oldvertex, vertexT *newvertex, setT *ridges, facetT *oldfacet, facetT *neighborA) {
+ facetT *neighbor, **neighborp;
+ ridgeT *ridge, **ridgep;
+ boolT istrace= False;
+
+ if (qh->IStracing >= 2 || oldvertex->id == qh->tracevertex_id ||
+ newvertex->id == qh->tracevertex_id)
+ istrace= True;
+ FOREACHridge_(ridges)
+ qh_renameridgevertex(qh, ridge, oldvertex, newvertex);
+ if (!oldfacet) {
+ zinc_(Zrenameall);
+ if (istrace)
+ qh_fprintf(qh, qh->ferr, 8082, "qh_renamevertex: renamed v%d to v%d in several facets\n",
+ oldvertex->id, newvertex->id);
+ FOREACHneighbor_(oldvertex) {
+ qh_maydropneighbor(qh, neighbor);
+ qh_setdelsorted(neighbor->vertices, oldvertex);
+ if (qh_remove_extravertices(qh, neighbor))
+ neighborp--; /* neighbor may be deleted */
+ }
+ if (!oldvertex->deleted) {
+ oldvertex->deleted= True;
+ qh_setappend(qh, &qh->del_vertices, oldvertex);
+ }
+ }else if (qh_setsize(qh, oldvertex->neighbors) == 2) {
+ zinc_(Zrenameshare);
+ if (istrace)
+ qh_fprintf(qh, qh->ferr, 8083, "qh_renamevertex: renamed v%d to v%d in oldfacet f%d\n",
+ oldvertex->id, newvertex->id, oldfacet->id);
+ FOREACHneighbor_(oldvertex)
+ qh_setdelsorted(neighbor->vertices, oldvertex);
+ oldvertex->deleted= True;
+ qh_setappend(qh, &qh->del_vertices, oldvertex);
+ }else {
+ zinc_(Zrenamepinch);
+ if (istrace || qh->IStracing)
+ qh_fprintf(qh, qh->ferr, 8084, "qh_renamevertex: renamed pinched v%d to v%d between f%d and f%d\n",
+ oldvertex->id, newvertex->id, oldfacet->id, neighborA->id);
+ qh_setdelsorted(oldfacet->vertices, oldvertex);
+ qh_setdel(oldvertex->neighbors, oldfacet);
+ qh_remove_extravertices(qh, neighborA);
+ }
+} /* renamevertex */
+
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="test_appendmerge">-</a>
+
+ qh_test_appendmerge(qh, facet, neighbor )
+ tests facet/neighbor for convexity
+ appends to mergeset if non-convex
+ if pre-merging,
+ nop if qh.SKIPconvex, or qh.MERGEexact and coplanar
+
+ returns:
+ true if appends facet/neighbor to mergeset
+ sets facet->center as needed
+ does not change facet->seen
+
+ design:
+ if qh.cos_max is defined
+ if the angle between facet normals is too shallow
+ append an angle-coplanar merge to qh.mergeset
+ return True
+ make facet's centrum if needed
+ if facet's centrum is above the neighbor
+ set isconcave
+ else
+ if facet's centrum is not below the neighbor
+ set iscoplanar
+ make neighbor's centrum if needed
+ if neighbor's centrum is above the facet
+ set isconcave
+ else if neighbor's centrum is not below the facet
+ set iscoplanar
+ if isconcave or iscoplanar
+ get angle if needed
+ append concave or coplanar merge to qh.mergeset
+*/
+boolT qh_test_appendmerge(qhT *qh, facetT *facet, facetT *neighbor) {
+ realT dist, dist2= -REALmax, angle= -REALmax;
+ boolT isconcave= False, iscoplanar= False, okangle= False;
+
+ if (qh->SKIPconvex && !qh->POSTmerging)
+ return False;
+ if ((!qh->MERGEexact || qh->POSTmerging) && qh->cos_max < REALmax/2) {
+ angle= qh_getangle(qh, facet->normal, neighbor->normal);
+ zinc_(Zangletests);
+ if (angle > qh->cos_max) {
+ zinc_(Zcoplanarangle);
+ qh_appendmergeset(qh, facet, neighbor, MRGanglecoplanar, &angle);
+ trace2((qh, qh->ferr, 2039, "qh_test_appendmerge: coplanar angle %4.4g between f%d and f%d\n",
+ angle, facet->id, neighbor->id));
+ return True;
+ }else
+ okangle= True;
+ }
+ if (!facet->center)
+ facet->center= qh_getcentrum(qh, facet);
+ zzinc_(Zcentrumtests);
+ qh_distplane(qh, facet->center, neighbor, &dist);
+ if (dist > qh->centrum_radius)
+ isconcave= True;
+ else {
+ if (dist > -qh->centrum_radius)
+ iscoplanar= True;
+ if (!neighbor->center)
+ neighbor->center= qh_getcentrum(qh, neighbor);
+ zzinc_(Zcentrumtests);
+ qh_distplane(qh, neighbor->center, facet, &dist2);
+ if (dist2 > qh->centrum_radius)
+ isconcave= True;
+ else if (!iscoplanar && dist2 > -qh->centrum_radius)
+ iscoplanar= True;
+ }
+ if (!isconcave && (!iscoplanar || (qh->MERGEexact && !qh->POSTmerging)))
+ return False;
+ if (!okangle && qh->ANGLEmerge) {
+ angle= qh_getangle(qh, facet->normal, neighbor->normal);
+ zinc_(Zangletests);
+ }
+ if (isconcave) {
+ zinc_(Zconcaveridge);
+ if (qh->ANGLEmerge)
+ angle += qh_ANGLEconcave + 0.5;
+ qh_appendmergeset(qh, facet, neighbor, MRGconcave, &angle);
+ trace0((qh, qh->ferr, 18, "qh_test_appendmerge: concave f%d to f%d dist %4.4g and reverse dist %4.4g angle %4.4g during p%d\n",
+ facet->id, neighbor->id, dist, dist2, angle, qh->furthest_id));
+ }else /* iscoplanar */ {
+ zinc_(Zcoplanarcentrum);
+ qh_appendmergeset(qh, facet, neighbor, MRGcoplanar, &angle);
+ trace2((qh, qh->ferr, 2040, "qh_test_appendmerge: coplanar f%d to f%d dist %4.4g, reverse dist %4.4g angle %4.4g\n",
+ facet->id, neighbor->id, dist, dist2, angle));
+ }
+ return True;
+} /* test_appendmerge */
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="test_vneighbors">-</a>
+
+ qh_test_vneighbors(qh)
+ test vertex neighbors for convexity
+ tests all facets on qh.newfacet_list
+
+ returns:
+ true if non-convex vneighbors appended to qh.facet_mergeset
+ initializes vertex neighbors if needed
+
+ notes:
+ assumes all facet neighbors have been tested
+ this can be expensive
+ this does not guarantee that a centrum is below all facets
+ but it is unlikely
+ uses qh.visit_id
+
+ design:
+ build vertex neighbors if necessary
+ for all new facets
+ for all vertices
+ for each unvisited facet neighbor of the vertex
+ test new facet and neighbor for convexity
+*/
+boolT qh_test_vneighbors(qhT *qh /* qh->newfacet_list */) {
+ facetT *newfacet, *neighbor, **neighborp;
+ vertexT *vertex, **vertexp;
+ int nummerges= 0;
+
+ trace1((qh, qh->ferr, 1015, "qh_test_vneighbors: testing vertex neighbors for convexity\n"));
+ if (!qh->VERTEXneighbors)
+ qh_vertexneighbors(qh);
+ FORALLnew_facets
+ newfacet->seen= False;
+ FORALLnew_facets {
+ newfacet->seen= True;
+ newfacet->visitid= qh->visit_id++;
+ FOREACHneighbor_(newfacet)
+ newfacet->visitid= qh->visit_id;
+ FOREACHvertex_(newfacet->vertices) {
+ FOREACHneighbor_(vertex) {
+ if (neighbor->seen || neighbor->visitid == qh->visit_id)
+ continue;
+ if (qh_test_appendmerge(qh, newfacet, neighbor))
+ nummerges++;
+ }
+ }
+ }
+ zadd_(Ztestvneighbor, nummerges);
+ trace1((qh, qh->ferr, 1016, "qh_test_vneighbors: found %d non-convex, vertex neighbors\n",
+ nummerges));
+ return (nummerges > 0);
+} /* test_vneighbors */
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="tracemerge">-</a>
+
+ qh_tracemerge(qh, facet1, facet2 )
+ print trace message after merge
+*/
+void qh_tracemerge(qhT *qh, facetT *facet1, facetT *facet2) {
+ boolT waserror= False;
+
+#ifndef qh_NOtrace
+ if (qh->IStracing >= 4)
+ qh_errprint(qh, "MERGED", facet2, NULL, NULL, NULL);
+ if (facet2 == qh->tracefacet || (qh->tracevertex && qh->tracevertex->newlist)) {
+ qh_fprintf(qh, qh->ferr, 8085, "qh_tracemerge: trace facet and vertex after merge of f%d and f%d, furthest p%d\n", facet1->id, facet2->id, qh->furthest_id);
+ if (facet2 != qh->tracefacet)
+ qh_errprint(qh, "TRACE", qh->tracefacet,
+ (qh->tracevertex && qh->tracevertex->neighbors) ?
+ SETfirstt_(qh->tracevertex->neighbors, facetT) : NULL,
+ NULL, qh->tracevertex);
+ }
+ if (qh->tracevertex) {
+ if (qh->tracevertex->deleted)
+ qh_fprintf(qh, qh->ferr, 8086, "qh_tracemerge: trace vertex deleted at furthest p%d\n",
+ qh->furthest_id);
+ else
+ qh_checkvertex(qh, qh->tracevertex);
+ }
+ if (qh->tracefacet) {
+ qh_checkfacet(qh, qh->tracefacet, True, &waserror);
+ if (waserror)
+ qh_errexit(qh, qh_ERRqhull, qh->tracefacet, NULL);
+ }
+#endif /* !qh_NOtrace */
+ if (qh->CHECKfrequently || qh->IStracing >= 4) { /* can't check polygon here */
+ qh_checkfacet(qh, facet2, True, &waserror);
+ if (waserror)
+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+ }
+} /* tracemerge */
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="tracemerging">-</a>
+
+ qh_tracemerging(qh)
+ print trace message during POSTmerging
+
+ returns:
+ updates qh.mergereport
+
+ notes:
+ called from qh_mergecycle() and qh_mergefacet()
+
+ see:
+ qh_buildtracing()
+*/
+void qh_tracemerging(qhT *qh) {
+ realT cpu;
+ int total;
+ time_t timedata;
+ struct tm *tp;
+
+ qh->mergereport= zzval_(Ztotmerge);
+ time(&timedata);
+ tp= localtime(&timedata);
+ cpu= qh_CPUclock;
+ cpu /= qh_SECticks;
+ total= zzval_(Ztotmerge) - zzval_(Zcyclehorizon) + zzval_(Zcyclefacettot);
+ qh_fprintf(qh, qh->ferr, 8087, "\n\
+At %d:%d:%d & %2.5g CPU secs, qhull has merged %d facets. The hull\n\
+ contains %d facets and %d vertices.\n",
+ tp->tm_hour, tp->tm_min, tp->tm_sec, cpu,
+ total, qh->num_facets - qh->num_visible,
+ qh->num_vertices-qh_setsize(qh, qh->del_vertices));
+} /* tracemerging */
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="updatetested">-</a>
+
+ qh_updatetested(qh, facet1, facet2 )
+ clear facet2->tested and facet1->ridge->tested for merge
+
+ returns:
+ deletes facet2->center unless it's already large
+ if so, clears facet2->ridge->tested
+
+ design:
+ clear facet2->tested
+ clear ridge->tested for facet1's ridges
+ if facet2 has a centrum
+ if facet2 is large
+ set facet2->keepcentrum
+ else if facet2 has 3 vertices due to many merges, or not large and post merging
+ clear facet2->keepcentrum
+ unless facet2->keepcentrum
+ clear facet2->center to recompute centrum later
+ clear ridge->tested for facet2's ridges
+*/
+void qh_updatetested(qhT *qh, facetT *facet1, facetT *facet2) {
+ ridgeT *ridge, **ridgep;
+ int size;
+
+ facet2->tested= False;
+ FOREACHridge_(facet1->ridges)
+ ridge->tested= False;
+ if (!facet2->center)
+ return;
+ size= qh_setsize(qh, facet2->vertices);
+ if (!facet2->keepcentrum) {
+ if (size > qh->hull_dim + qh_MAXnewcentrum) {
+ facet2->keepcentrum= True;
+ zinc_(Zwidevertices);
+ }
+ }else if (size <= qh->hull_dim + qh_MAXnewcentrum) {
+ /* center and keepcentrum was set */
+ if (size == qh->hull_dim || qh->POSTmerging)
+ facet2->keepcentrum= False; /* if many merges need to recompute centrum */
+ }
+ if (!facet2->keepcentrum) {
+ qh_memfree(qh, facet2->center, qh->normal_size);
+ facet2->center= NULL;
+ FOREACHridge_(facet2->ridges)
+ ridge->tested= False;
+ }
+} /* updatetested */
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="vertexridges">-</a>
+
+ qh_vertexridges(qh, vertex )
+ return temporary set of ridges adjacent to a vertex
+ vertex->neighbors defined
+
+ ntoes:
+ uses qh.visit_id
+ does not include implicit ridges for simplicial facets
+
+ design:
+ for each neighbor of vertex
+ add ridges that include the vertex to ridges
+*/
+setT *qh_vertexridges(qhT *qh, vertexT *vertex) {
+ facetT *neighbor, **neighborp;
+ setT *ridges= qh_settemp(qh, qh->TEMPsize);
+ int size;
+
+ qh->visit_id++;
+ FOREACHneighbor_(vertex)
+ neighbor->visitid= qh->visit_id;
+ FOREACHneighbor_(vertex) {
+ if (*neighborp) /* no new ridges in last neighbor */
+ qh_vertexridges_facet(qh, vertex, neighbor, &ridges);
+ }
+ if (qh->PRINTstatistics || qh->IStracing) {
+ size= qh_setsize(qh, ridges);
+ zinc_(Zvertexridge);
+ zadd_(Zvertexridgetot, size);
+ zmax_(Zvertexridgemax, size);
+ trace3((qh, qh->ferr, 3011, "qh_vertexridges: found %d ridges for v%d\n",
+ size, vertex->id));
+ }
+ return ridges;
+} /* vertexridges */
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="vertexridges_facet">-</a>
+
+ qh_vertexridges_facet(qh, vertex, facet, ridges )
+ add adjacent ridges for vertex in facet
+ neighbor->visitid==qh.visit_id if it hasn't been visited
+
+ returns:
+ ridges updated
+ sets facet->visitid to qh.visit_id-1
+
+ design:
+ for each ridge of facet
+ if ridge of visited neighbor (i.e., unprocessed)
+ if vertex in ridge
+ append ridge to vertex
+ mark facet processed
+*/
+void qh_vertexridges_facet(qhT *qh, vertexT *vertex, facetT *facet, setT **ridges) {
+ ridgeT *ridge, **ridgep;
+ facetT *neighbor;
+
+ FOREACHridge_(facet->ridges) {
+ neighbor= otherfacet_(ridge, facet);
+ if (neighbor->visitid == qh->visit_id
+ && qh_setin(ridge->vertices, vertex))
+ qh_setappend(qh, ridges, ridge);
+ }
+ facet->visitid= qh->visit_id-1;
+} /* vertexridges_facet */
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >-------------------------------</a><a name="willdelete">-</a>
+
+ qh_willdelete(qh, facet, replace )
+ moves facet to visible list
+ sets facet->f.replace to replace (may be NULL)
+
+ returns:
+ bumps qh.num_visible
+*/
+void qh_willdelete(qhT *qh, facetT *facet, facetT *replace) {
+
+ qh_removefacet(qh, facet);
+ qh_prependfacet(qh, facet, &qh->visible_list);
+ qh->num_visible++;
+ facet->visible= True;
+ facet->f.replace= replace;
+} /* willdelete */
+
+#else /* qh_NOmerge */
+void qh_premerge(qhT *qh, vertexT *apex, realT maxcentrum, realT maxangle) {
+}
+void qh_postmerge(qhT *qh, const char *reason, realT maxcentrum, realT maxangle,
+ boolT vneighbors) {
+}
+boolT qh_checkzero(qhT *qh, boolT testall) {
+ }
+#endif /* qh_NOmerge */
+
diff --git a/xs/src/qhull/src/libqhull_r/merge_r.h b/xs/src/qhull/src/libqhull_r/merge_r.h
new file mode 100644
index 000000000..30a51815d
--- /dev/null
+++ b/xs/src/qhull/src/libqhull_r/merge_r.h
@@ -0,0 +1,186 @@
+/*<html><pre> -<a href="qh-merge_r.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ merge_r.h
+ header file for merge_r.c
+
+ see qh-merge_r.htm and merge_r.c
+
+ Copyright (c) 1993-2015 C.B. Barber.
+ $Id: //main/2015/qhull/src/libqhull_r/merge_r.h#3 $$Change: 2079 $
+ $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
+*/
+
+#ifndef qhDEFmerge
+#define qhDEFmerge 1
+
+#include "libqhull_r.h"
+
+
+/*============ -constants- ==============*/
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >--------------------------------</a><a name="qh_ANGLEredundant">-</a>
+
+ qh_ANGLEredundant
+ indicates redundant merge in mergeT->angle
+*/
+#define qh_ANGLEredundant 6.0
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >--------------------------------</a><a name="qh_ANGLEdegen">-</a>
+
+ qh_ANGLEdegen
+ indicates degenerate facet in mergeT->angle
+*/
+#define qh_ANGLEdegen 5.0
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >--------------------------------</a><a name="qh_ANGLEconcave">-</a>
+
+ qh_ANGLEconcave
+ offset to indicate concave facets in mergeT->angle
+
+ notes:
+ concave facets are assigned the range of [2,4] in mergeT->angle
+ roundoff error may make the angle less than 2
+*/
+#define qh_ANGLEconcave 1.5
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >--------------------------------</a><a name="MRG">-</a>
+
+ MRG... (mergeType)
+ indicates the type of a merge (mergeT->type)
+*/
+typedef enum { /* in sort order for facet_mergeset */
+ MRGnone= 0,
+ MRGcoplanar, /* centrum coplanar */
+ MRGanglecoplanar, /* angle coplanar */
+ /* could detect half concave ridges */
+ MRGconcave, /* concave ridge */
+ MRGflip, /* flipped facet. facet1 == facet2 */
+ MRGridge, /* duplicate ridge (qh_MERGEridge) */
+ /* degen and redundant go onto degen_mergeset */
+ MRGdegen, /* degenerate facet (!enough neighbors) facet1 == facet2 */
+ MRGredundant, /* redundant facet (vertex subset) */
+ /* merge_degenredundant assumes degen < redundant */
+ MRGmirror, /* mirror facet from qh_triangulate */
+ ENDmrg
+} mergeType;
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >--------------------------------</a><a name="qh_MERGEapex">-</a>
+
+ qh_MERGEapex
+ flag for qh_mergefacet() to indicate an apex merge
+*/
+#define qh_MERGEapex True
+
+/*============ -structures- ====================*/
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >--------------------------------</a><a name="mergeT">-</a>
+
+ mergeT
+ structure used to merge facets
+*/
+
+typedef struct mergeT mergeT;
+struct mergeT { /* initialize in qh_appendmergeset */
+ realT angle; /* angle between normals of facet1 and facet2 */
+ facetT *facet1; /* will merge facet1 into facet2 */
+ facetT *facet2;
+ mergeType type;
+};
+
+
+/*=========== -macros- =========================*/
+
+/*-<a href="qh-merge_r.htm#TOC"
+ >--------------------------------</a><a name="FOREACHmerge_">-</a>
+
+ FOREACHmerge_( merges ) {...}
+ assign 'merge' to each merge in merges
+
+ notes:
+ uses 'mergeT *merge, **mergep;'
+ if qh_mergefacet(),
+ restart since qh.facet_mergeset may change
+ see <a href="qset_r.h#FOREACHsetelement_">FOREACHsetelement_</a>
+*/
+#define FOREACHmerge_( merges ) FOREACHsetelement_(mergeT, merges, merge)
+
+/*============ prototypes in alphabetical order after pre/postmerge =======*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void qh_premerge(qhT *qh, vertexT *apex, realT maxcentrum, realT maxangle);
+void qh_postmerge(qhT *qh, const char *reason, realT maxcentrum, realT maxangle,
+ boolT vneighbors);
+void qh_all_merges(qhT *qh, boolT othermerge, boolT vneighbors);
+void qh_appendmergeset(qhT *qh, facetT *facet, facetT *neighbor, mergeType mergetype, realT *angle);
+setT *qh_basevertices(qhT *qh, facetT *samecycle);
+void qh_checkconnect(qhT *qh /* qh.new_facets */);
+boolT qh_checkzero(qhT *qh, boolT testall);
+int qh_compareangle(const void *p1, const void *p2);
+int qh_comparemerge(const void *p1, const void *p2);
+int qh_comparevisit(const void *p1, const void *p2);
+void qh_copynonconvex(qhT *qh, ridgeT *atridge);
+void qh_degen_redundant_facet(qhT *qh, facetT *facet);
+void qh_degen_redundant_neighbors(qhT *qh, facetT *facet, facetT *delfacet);
+vertexT *qh_find_newvertex(qhT *qh, vertexT *oldvertex, setT *vertices, setT *ridges);
+void qh_findbest_test(qhT *qh, boolT testcentrum, facetT *facet, facetT *neighbor,
+ facetT **bestfacet, realT *distp, realT *mindistp, realT *maxdistp);
+facetT *qh_findbestneighbor(qhT *qh, facetT *facet, realT *distp, realT *mindistp, realT *maxdistp);
+void qh_flippedmerges(qhT *qh, facetT *facetlist, boolT *wasmerge);
+void qh_forcedmerges(qhT *qh, boolT *wasmerge);
+void qh_getmergeset(qhT *qh, facetT *facetlist);
+void qh_getmergeset_initial(qhT *qh, facetT *facetlist);
+void qh_hashridge(qhT *qh, setT *hashtable, int hashsize, ridgeT *ridge, vertexT *oldvertex);
+ridgeT *qh_hashridge_find(qhT *qh, setT *hashtable, int hashsize, ridgeT *ridge,
+ vertexT *vertex, vertexT *oldvertex, int *hashslot);
+void qh_makeridges(qhT *qh, facetT *facet);
+void qh_mark_dupridges(qhT *qh, facetT *facetlist);
+void qh_maydropneighbor(qhT *qh, facetT *facet);
+int qh_merge_degenredundant(qhT *qh);
+void qh_merge_nonconvex(qhT *qh, facetT *facet1, facetT *facet2, mergeType mergetype);
+void qh_mergecycle(qhT *qh, facetT *samecycle, facetT *newfacet);
+void qh_mergecycle_all(qhT *qh, facetT *facetlist, boolT *wasmerge);
+void qh_mergecycle_facets(qhT *qh, facetT *samecycle, facetT *newfacet);
+void qh_mergecycle_neighbors(qhT *qh, facetT *samecycle, facetT *newfacet);
+void qh_mergecycle_ridges(qhT *qh, facetT *samecycle, facetT *newfacet);
+void qh_mergecycle_vneighbors(qhT *qh, facetT *samecycle, facetT *newfacet);
+void qh_mergefacet(qhT *qh, facetT *facet1, facetT *facet2, realT *mindist, realT *maxdist, boolT mergeapex);
+void qh_mergefacet2d(qhT *qh, facetT *facet1, facetT *facet2);
+void qh_mergeneighbors(qhT *qh, facetT *facet1, facetT *facet2);
+void qh_mergeridges(qhT *qh, facetT *facet1, facetT *facet2);
+void qh_mergesimplex(qhT *qh, facetT *facet1, facetT *facet2, boolT mergeapex);
+void qh_mergevertex_del(qhT *qh, vertexT *vertex, facetT *facet1, facetT *facet2);
+void qh_mergevertex_neighbors(qhT *qh, facetT *facet1, facetT *facet2);
+void qh_mergevertices(qhT *qh, setT *vertices1, setT **vertices);
+setT *qh_neighbor_intersections(qhT *qh, vertexT *vertex);
+void qh_newvertices(qhT *qh, setT *vertices);
+boolT qh_reducevertices(qhT *qh);
+vertexT *qh_redundant_vertex(qhT *qh, vertexT *vertex);
+boolT qh_remove_extravertices(qhT *qh, facetT *facet);
+vertexT *qh_rename_sharedvertex(qhT *qh, vertexT *vertex, facetT *facet);
+void qh_renameridgevertex(qhT *qh, ridgeT *ridge, vertexT *oldvertex, vertexT *newvertex);
+void qh_renamevertex(qhT *qh, vertexT *oldvertex, vertexT *newvertex, setT *ridges,
+ facetT *oldfacet, facetT *neighborA);
+boolT qh_test_appendmerge(qhT *qh, facetT *facet, facetT *neighbor);
+boolT qh_test_vneighbors(qhT *qh /* qh.newfacet_list */);
+void qh_tracemerge(qhT *qh, facetT *facet1, facetT *facet2);
+void qh_tracemerging(qhT *qh);
+void qh_updatetested(qhT *qh, facetT *facet1, facetT *facet2);
+setT *qh_vertexridges(qhT *qh, vertexT *vertex);
+void qh_vertexridges_facet(qhT *qh, vertexT *vertex, facetT *facet, setT **ridges);
+void qh_willdelete(qhT *qh, facetT *facet, facetT *replace);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* qhDEFmerge */
diff --git a/xs/src/qhull/src/libqhull_r/poly2_r.c b/xs/src/qhull/src/libqhull_r/poly2_r.c
new file mode 100644
index 000000000..b8ae9af9f
--- /dev/null
+++ b/xs/src/qhull/src/libqhull_r/poly2_r.c
@@ -0,0 +1,3222 @@
+/*<html><pre> -<a href="qh-poly_r.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ poly2_r.c
+ implements polygons and simplicies
+
+ see qh-poly_r.htm, poly_r.h and libqhull_r.h
+
+ frequently used code is in poly_r.c
+
+ Copyright (c) 1993-2015 The Geometry Center.
+ $Id: //main/2015/qhull/src/libqhull_r/poly2_r.c#10 $$Change: 2069 $
+ $DateTime: 2016/01/18 22:05:03 $$Author: bbarber $
+*/
+
+#include "qhull_ra.h"
+
+/*======== functions in alphabetical order ==========*/
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="addhash">-</a>
+
+ qh_addhash( newelem, hashtable, hashsize, hash )
+ add newelem to linear hash table at hash if not already there
+*/
+void qh_addhash(void *newelem, setT *hashtable, int hashsize, int hash) {
+ int scan;
+ void *elem;
+
+ for (scan= (int)hash; (elem= SETelem_(hashtable, scan));
+ scan= (++scan >= hashsize ? 0 : scan)) {
+ if (elem == newelem)
+ break;
+ }
+ /* loop terminates because qh_HASHfactor >= 1.1 by qh_initbuffers */
+ if (!elem)
+ SETelem_(hashtable, scan)= newelem;
+} /* addhash */
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="check_bestdist">-</a>
+
+ qh_check_bestdist(qh)
+ check that all points are within max_outside of the nearest facet
+ if qh.ONLYgood,
+ ignores !good facets
+
+ see:
+ qh_check_maxout(), qh_outerinner()
+
+ notes:
+ only called from qh_check_points()
+ seldom used since qh.MERGING is almost always set
+ if notverified>0 at end of routine
+ some points were well inside the hull. If the hull contains
+ a lens-shaped component, these points were not verified. Use
+ options 'Qi Tv' to verify all points. (Exhaustive check also verifies)
+
+ design:
+ determine facet for each point (if any)
+ for each point
+ start with the assigned facet or with the first facet
+ find the best facet for the point and check all coplanar facets
+ error if point is outside of facet
+*/
+void qh_check_bestdist(qhT *qh) {
+ boolT waserror= False, unassigned;
+ facetT *facet, *bestfacet, *errfacet1= NULL, *errfacet2= NULL;
+ facetT *facetlist;
+ realT dist, maxoutside, maxdist= -REALmax;
+ pointT *point;
+ int numpart= 0, facet_i, facet_n, notgood= 0, notverified= 0;
+ setT *facets;
+
+ trace1((qh, qh->ferr, 1020, "qh_check_bestdist: check points below nearest facet. Facet_list f%d\n",
+ qh->facet_list->id));
+ maxoutside= qh_maxouter(qh);
+ maxoutside += qh->DISTround;
+ /* one more qh.DISTround for check computation */
+ trace1((qh, qh->ferr, 1021, "qh_check_bestdist: check that all points are within %2.2g of best facet\n", maxoutside));
+ facets= qh_pointfacet(qh /*qh.facet_list*/);
+ if (!qh_QUICKhelp && qh->PRINTprecision)
+ qh_fprintf(qh, qh->ferr, 8091, "\n\
+qhull output completed. Verifying that %d points are\n\
+below %2.2g of the nearest %sfacet.\n",
+ qh_setsize(qh, facets), maxoutside, (qh->ONLYgood ? "good " : ""));
+ FOREACHfacet_i_(qh, facets) { /* for each point with facet assignment */
+ if (facet)
+ unassigned= False;
+ else {
+ unassigned= True;
+ facet= qh->facet_list;
+ }
+ point= qh_point(qh, facet_i);
+ if (point == qh->GOODpointp)
+ continue;
+ qh_distplane(qh, point, facet, &dist);
+ numpart++;
+ bestfacet= qh_findbesthorizon(qh, !qh_IScheckmax, point, facet, qh_NOupper, &dist, &numpart);
+ /* occurs after statistics reported */
+ maximize_(maxdist, dist);
+ if (dist > maxoutside) {
+ if (qh->ONLYgood && !bestfacet->good
+ && !((bestfacet= qh_findgooddist(qh, point, bestfacet, &dist, &facetlist))
+ && dist > maxoutside))
+ notgood++;
+ else {
+ waserror= True;
+ qh_fprintf(qh, qh->ferr, 6109, "qhull precision error: point p%d is outside facet f%d, distance= %6.8g maxoutside= %6.8g\n",
+ facet_i, bestfacet->id, dist, maxoutside);
+ if (errfacet1 != bestfacet) {
+ errfacet2= errfacet1;
+ errfacet1= bestfacet;
+ }
+ }
+ }else if (unassigned && dist < -qh->MAXcoplanar)
+ notverified++;
+ }
+ qh_settempfree(qh, &facets);
+ if (notverified && !qh->DELAUNAY && !qh_QUICKhelp && qh->PRINTprecision)
+ qh_fprintf(qh, qh->ferr, 8092, "\n%d points were well inside the hull. If the hull contains\n\
+a lens-shaped component, these points were not verified. Use\n\
+options 'Qci Tv' to verify all points.\n", notverified);
+ if (maxdist > qh->outside_err) {
+ qh_fprintf(qh, qh->ferr, 6110, "qhull precision error (qh_check_bestdist): a coplanar point is %6.2g from convex hull. The maximum value(qh.outside_err) is %6.2g\n",
+ maxdist, qh->outside_err);
+ qh_errexit2(qh, qh_ERRprec, errfacet1, errfacet2);
+ }else if (waserror && qh->outside_err > REALmax/2)
+ qh_errexit2(qh, qh_ERRprec, errfacet1, errfacet2);
+ /* else if waserror, the error was logged to qh.ferr but does not effect the output */
+ trace0((qh, qh->ferr, 20, "qh_check_bestdist: max distance outside %2.2g\n", maxdist));
+} /* check_bestdist */
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="check_dupridge">-</a>
+
+ qh_check_dupridge(qh, facet1, dist1, facet2, dist2)
+ Check duplicate ridge between facet1 and facet2 for wide merge
+ dist1 is the maximum distance of facet1's vertices to facet2
+ dist2 is the maximum distance of facet2's vertices to facet1
+
+ Returns
+ Level 1 log of the duplicate ridge with the minimum distance between vertices
+ Throws error if the merge will increase the maximum facet width by qh_WIDEduplicate (100x)
+
+ called from:
+ qh_forcedmerges()
+*/
+#ifndef qh_NOmerge
+void qh_check_dupridge(qhT *qh, facetT *facet1, realT dist1, facetT *facet2, realT dist2) {
+ vertexT *vertex, **vertexp, *vertexA, **vertexAp;
+ realT dist, innerplane, mergedist, outerplane, prevdist, ratio;
+ realT minvertex= REALmax;
+
+ mergedist= fmin_(dist1, dist2);
+ qh_outerinner(qh, NULL, &outerplane, &innerplane); /* ratio from qh_printsummary */
+ prevdist= fmax_(outerplane, innerplane);
+ maximize_(prevdist, qh->ONEmerge + qh->DISTround);
+ maximize_(prevdist, qh->MINoutside + qh->DISTround);
+ ratio= mergedist/prevdist;
+ FOREACHvertex_(facet1->vertices) { /* The duplicate ridge is between facet1 and facet2, so either facet can be tested */
+ FOREACHvertexA_(facet1->vertices) {
+ if (vertex > vertexA){ /* Test each pair once */
+ dist= qh_pointdist(vertex->point, vertexA->point, qh->hull_dim);
+ minimize_(minvertex, dist);
+ }
+ }
+ }
+ trace0((qh, qh->ferr, 16, "qh_check_dupridge: duplicate ridge between f%d and f%d due to nearly-coincident vertices (%2.2g), dist %2.2g, reverse dist %2.2g, ratio %2.2g while processing p%d\n",
+ facet1->id, facet2->id, minvertex, dist1, dist2, ratio, qh->furthest_id));
+ if (ratio > qh_WIDEduplicate) {
+ qh_fprintf(qh, qh->ferr, 6271, "qhull precision error (qh_check_dupridge): wide merge (%.0f times wider) due to duplicate ridge with nearly coincident points (%2.2g) between f%d and f%d, merge dist %2.2g, while processing p%d\n- Ignore error with option 'Q12'\n- To be fixed in a later version of Qhull\n",
+ ratio, minvertex, facet1->id, facet2->id, mergedist, qh->furthest_id);
+ if (qh->DELAUNAY)
+ qh_fprintf(qh, qh->ferr, 8145, "- A bounding box for the input sites may alleviate this error.\n");
+ if(minvertex > qh_WIDEduplicate*prevdist)
+ qh_fprintf(qh, qh->ferr, 8146, "- Vertex distance %2.2g is greater than %d times maximum distance %2.2g\n Please report to bradb@shore.net with steps to reproduce and all output\n",
+ minvertex, qh_WIDEduplicate, prevdist);
+ if (!qh->NOwide)
+ qh_errexit2(qh, qh_ERRqhull, facet1, facet2);
+ }
+} /* check_dupridge */
+#endif
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="check_maxout">-</a>
+
+ qh_check_maxout(qh)
+ updates qh.max_outside by checking all points against bestfacet
+ if qh.ONLYgood, ignores !good facets
+
+ returns:
+ updates facet->maxoutside via qh_findbesthorizon()
+ sets qh.maxoutdone
+ if printing qh.min_vertex (qh_outerinner),
+ it is updated to the current vertices
+ removes inside/coplanar points from coplanarset as needed
+
+ notes:
+ defines coplanar as min_vertex instead of MAXcoplanar
+ may not need to check near-inside points because of qh.MAXcoplanar
+ and qh.KEEPnearinside (before it was -DISTround)
+
+ see also:
+ qh_check_bestdist()
+
+ design:
+ if qh.min_vertex is needed
+ for all neighbors of all vertices
+ test distance from vertex to neighbor
+ determine facet for each point (if any)
+ for each point with an assigned facet
+ find the best facet for the point and check all coplanar facets
+ (updates outer planes)
+ remove near-inside points from coplanar sets
+*/
+#ifndef qh_NOmerge
+void qh_check_maxout(qhT *qh) {
+ facetT *facet, *bestfacet, *neighbor, **neighborp, *facetlist;
+ realT dist, maxoutside, minvertex, old_maxoutside;
+ pointT *point;
+ int numpart= 0, facet_i, facet_n, notgood= 0;
+ setT *facets, *vertices;
+ vertexT *vertex;
+
+ trace1((qh, qh->ferr, 1022, "qh_check_maxout: check and update maxoutside for each facet.\n"));
+ maxoutside= minvertex= 0;
+ if (qh->VERTEXneighbors
+ && (qh->PRINTsummary || qh->KEEPinside || qh->KEEPcoplanar
+ || qh->TRACElevel || qh->PRINTstatistics
+ || qh->PRINTout[0] == qh_PRINTsummary || qh->PRINTout[0] == qh_PRINTnone)) {
+ trace1((qh, qh->ferr, 1023, "qh_check_maxout: determine actual maxoutside and minvertex\n"));
+ vertices= qh_pointvertex(qh /*qh.facet_list*/);
+ FORALLvertices {
+ FOREACHneighbor_(vertex) {
+ zinc_(Zdistvertex); /* distance also computed by main loop below */
+ qh_distplane(qh, vertex->point, neighbor, &dist);
+ minimize_(minvertex, dist);
+ if (-dist > qh->TRACEdist || dist > qh->TRACEdist
+ || neighbor == qh->tracefacet || vertex == qh->tracevertex)
+ qh_fprintf(qh, qh->ferr, 8093, "qh_check_maxout: p%d(v%d) is %.2g from f%d\n",
+ qh_pointid(qh, vertex->point), vertex->id, dist, neighbor->id);
+ }
+ }
+ if (qh->MERGING) {
+ wmin_(Wminvertex, qh->min_vertex);
+ }
+ qh->min_vertex= minvertex;
+ qh_settempfree(qh, &vertices);
+ }
+ facets= qh_pointfacet(qh /*qh.facet_list*/);
+ do {
+ old_maxoutside= fmax_(qh->max_outside, maxoutside);
+ FOREACHfacet_i_(qh, facets) { /* for each point with facet assignment */
+ if (facet) {
+ point= qh_point(qh, facet_i);
+ if (point == qh->GOODpointp)
+ continue;
+ zzinc_(Ztotcheck);
+ qh_distplane(qh, point, facet, &dist);
+ numpart++;
+ bestfacet= qh_findbesthorizon(qh, qh_IScheckmax, point, facet, !qh_NOupper, &dist, &numpart);
+ if (bestfacet && dist > maxoutside) {
+ if (qh->ONLYgood && !bestfacet->good
+ && !((bestfacet= qh_findgooddist(qh, point, bestfacet, &dist, &facetlist))
+ && dist > maxoutside))
+ notgood++;
+ else
+ maxoutside= dist;
+ }
+ if (dist > qh->TRACEdist || (bestfacet && bestfacet == qh->tracefacet))
+ qh_fprintf(qh, qh->ferr, 8094, "qh_check_maxout: p%d is %.2g above f%d\n",
+ qh_pointid(qh, point), dist, (bestfacet ? bestfacet->id : UINT_MAX));
+ }
+ }
+ }while
+ (maxoutside > 2*old_maxoutside);
+ /* if qh.maxoutside increases substantially, qh_SEARCHdist is not valid
+ e.g., RBOX 5000 s Z1 G1e-13 t1001200614 | qhull */
+ zzadd_(Zcheckpart, numpart);
+ qh_settempfree(qh, &facets);
+ wval_(Wmaxout)= maxoutside - qh->max_outside;
+ wmax_(Wmaxoutside, qh->max_outside);
+ qh->max_outside= maxoutside;
+ qh_nearcoplanar(qh /*qh.facet_list*/);
+ qh->maxoutdone= True;
+ trace1((qh, qh->ferr, 1024, "qh_check_maxout: maxoutside %2.2g, min_vertex %2.2g, outside of not good %d\n",
+ maxoutside, qh->min_vertex, notgood));
+} /* check_maxout */
+#else /* qh_NOmerge */
+void qh_check_maxout(qhT *qh) {
+}
+#endif
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="check_output">-</a>
+
+ qh_check_output(qh)
+ performs the checks at the end of qhull algorithm
+ Maybe called after voronoi output. Will recompute otherwise centrums are Voronoi centers instead
+*/
+void qh_check_output(qhT *qh) {
+ int i;
+
+ if (qh->STOPcone)
+ return;
+ if (qh->VERIFYoutput | qh->IStracing | qh->CHECKfrequently) {
+ qh_checkpolygon(qh, qh->facet_list);
+ qh_checkflipped_all(qh, qh->facet_list);
+ qh_checkconvex(qh, qh->facet_list, qh_ALGORITHMfault);
+ }else if (!qh->MERGING && qh_newstats(qh, qh->qhstat.precision, &i)) {
+ qh_checkflipped_all(qh, qh->facet_list);
+ qh_checkconvex(qh, qh->facet_list, qh_ALGORITHMfault);
+ }
+} /* check_output */
+
+
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="check_point">-</a>
+
+ qh_check_point(qh, point, facet, maxoutside, maxdist, errfacet1, errfacet2 )
+ check that point is less than maxoutside from facet
+*/
+void qh_check_point(qhT *qh, pointT *point, facetT *facet, realT *maxoutside, realT *maxdist, facetT **errfacet1, facetT **errfacet2) {
+ realT dist;
+
+ /* occurs after statistics reported */
+ qh_distplane(qh, point, facet, &dist);
+ if (dist > *maxoutside) {
+ if (*errfacet1 != facet) {
+ *errfacet2= *errfacet1;
+ *errfacet1= facet;
+ }
+ qh_fprintf(qh, qh->ferr, 6111, "qhull precision error: point p%d is outside facet f%d, distance= %6.8g maxoutside= %6.8g\n",
+ qh_pointid(qh, point), facet->id, dist, *maxoutside);
+ }
+ maximize_(*maxdist, dist);
+} /* qh_check_point */
+
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="check_points">-</a>
+
+ qh_check_points(qh)
+ checks that all points are inside all facets
+
+ notes:
+ if many points and qh_check_maxout not called (i.e., !qh.MERGING),
+ calls qh_findbesthorizon (seldom done).
+ ignores flipped facets
+ maxoutside includes 2 qh.DISTrounds
+ one qh.DISTround for the computed distances in qh_check_points
+ qh_printafacet and qh_printsummary needs only one qh.DISTround
+ the computation for qh.VERIFYdirect does not account for qh.other_points
+
+ design:
+ if many points
+ use qh_check_bestdist()
+ else
+ for all facets
+ for all points
+ check that point is inside facet
+*/
+void qh_check_points(qhT *qh) {
+ facetT *facet, *errfacet1= NULL, *errfacet2= NULL;
+ realT total, maxoutside, maxdist= -REALmax;
+ pointT *point, **pointp, *pointtemp;
+ boolT testouter;
+
+ maxoutside= qh_maxouter(qh);
+ maxoutside += qh->DISTround;
+ /* one more qh.DISTround for check computation */
+ trace1((qh, qh->ferr, 1025, "qh_check_points: check all points below %2.2g of all facet planes\n",
+ maxoutside));
+ if (qh->num_good) /* miss counts other_points and !good facets */
+ total= (float)qh->num_good * (float)qh->num_points;
+ else
+ total= (float)qh->num_facets * (float)qh->num_points;
+ if (total >= qh_VERIFYdirect && !qh->maxoutdone) {
+ if (!qh_QUICKhelp && qh->SKIPcheckmax && qh->MERGING)
+ qh_fprintf(qh, qh->ferr, 7075, "qhull input warning: merging without checking outer planes('Q5' or 'Po').\n\
+Verify may report that a point is outside of a facet.\n");
+ qh_check_bestdist(qh);
+ }else {
+ if (qh_MAXoutside && qh->maxoutdone)
+ testouter= True;
+ else
+ testouter= False;
+ if (!qh_QUICKhelp) {
+ if (qh->MERGEexact)
+ qh_fprintf(qh, qh->ferr, 7076, "qhull input warning: exact merge ('Qx'). Verify may report that a point\n\
+is outside of a facet. See qh-optq.htm#Qx\n");
+ else if (qh->SKIPcheckmax || qh->NOnearinside)
+ qh_fprintf(qh, qh->ferr, 7077, "qhull input warning: no outer plane check ('Q5') or no processing of\n\
+near-inside points ('Q8'). Verify may report that a point is outside\n\
+of a facet.\n");
+ }
+ if (qh->PRINTprecision) {
+ if (testouter)
+ qh_fprintf(qh, qh->ferr, 8098, "\n\
+Output completed. Verifying that all points are below outer planes of\n\
+all %sfacets. Will make %2.0f distance computations.\n",
+ (qh->ONLYgood ? "good " : ""), total);
+ else
+ qh_fprintf(qh, qh->ferr, 8099, "\n\
+Output completed. Verifying that all points are below %2.2g of\n\
+all %sfacets. Will make %2.0f distance computations.\n",
+ maxoutside, (qh->ONLYgood ? "good " : ""), total);
+ }
+ FORALLfacets {
+ if (!facet->good && qh->ONLYgood)
+ continue;
+ if (facet->flipped)
+ continue;
+ if (!facet->normal) {
+ qh_fprintf(qh, qh->ferr, 7061, "qhull warning (qh_check_points): missing normal for facet f%d\n", facet->id);
+ continue;
+ }
+ if (testouter) {
+#if qh_MAXoutside
+ maxoutside= facet->maxoutside + 2* qh->DISTround;
+ /* one DISTround to actual point and another to computed point */
+#endif
+ }
+ FORALLpoints {
+ if (point != qh->GOODpointp)
+ qh_check_point(qh, point, facet, &maxoutside, &maxdist, &errfacet1, &errfacet2);
+ }
+ FOREACHpoint_(qh->other_points) {
+ if (point != qh->GOODpointp)
+ qh_check_point(qh, point, facet, &maxoutside, &maxdist, &errfacet1, &errfacet2);
+ }
+ }
+ if (maxdist > qh->outside_err) {
+ qh_fprintf(qh, qh->ferr, 6112, "qhull precision error (qh_check_points): a coplanar point is %6.2g from convex hull. The maximum value(qh.outside_err) is %6.2g\n",
+ maxdist, qh->outside_err );
+ qh_errexit2(qh, qh_ERRprec, errfacet1, errfacet2 );
+ }else if (errfacet1 && qh->outside_err > REALmax/2)
+ qh_errexit2(qh, qh_ERRprec, errfacet1, errfacet2 );
+ /* else if errfacet1, the error was logged to qh.ferr but does not effect the output */
+ trace0((qh, qh->ferr, 21, "qh_check_points: max distance outside %2.2g\n", maxdist));
+ }
+} /* check_points */
+
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="checkconvex">-</a>
+
+ qh_checkconvex(qh, facetlist, fault )
+ check that each ridge in facetlist is convex
+ fault = qh_DATAfault if reporting errors
+ = qh_ALGORITHMfault otherwise
+
+ returns:
+ counts Zconcaveridges and Zcoplanarridges
+ errors if concaveridge or if merging an coplanar ridge
+
+ note:
+ if not merging,
+ tests vertices for neighboring simplicial facets
+ else if ZEROcentrum,
+ tests vertices for neighboring simplicial facets
+ else
+ tests centrums of neighboring facets
+
+ design:
+ for all facets
+ report flipped facets
+ if ZEROcentrum and simplicial neighbors
+ test vertices for neighboring simplicial facets
+ else
+ test centrum against all neighbors
+*/
+void qh_checkconvex(qhT *qh, facetT *facetlist, int fault) {
+ facetT *facet, *neighbor, **neighborp, *errfacet1=NULL, *errfacet2=NULL;
+ vertexT *vertex;
+ realT dist;
+ pointT *centrum;
+ boolT waserror= False, centrum_warning= False, tempcentrum= False, allsimplicial;
+ int neighbor_i;
+
+ trace1((qh, qh->ferr, 1026, "qh_checkconvex: check all ridges are convex\n"));
+ if (!qh->RERUN) {
+ zzval_(Zconcaveridges)= 0;
+ zzval_(Zcoplanarridges)= 0;
+ }
+ FORALLfacet_(facetlist) {
+ if (facet->flipped) {
+ qh_precision(qh, "flipped facet");
+ qh_fprintf(qh, qh->ferr, 6113, "qhull precision error: f%d is flipped(interior point is outside)\n",
+ facet->id);
+ errfacet1= facet;
+ waserror= True;
+ continue;
+ }
+ if (qh->MERGING && (!qh->ZEROcentrum || !facet->simplicial || facet->tricoplanar))
+ allsimplicial= False;
+ else {
+ allsimplicial= True;
+ neighbor_i= 0;
+ FOREACHneighbor_(facet) {
+ vertex= SETelemt_(facet->vertices, neighbor_i++, vertexT);
+ if (!neighbor->simplicial || neighbor->tricoplanar) {
+ allsimplicial= False;
+ continue;
+ }
+ qh_distplane(qh, vertex->point, neighbor, &dist);
+ if (dist > -qh->DISTround) {
+ if (fault == qh_DATAfault) {
+ qh_precision(qh, "coplanar or concave ridge");
+ qh_fprintf(qh, qh->ferr, 6114, "qhull precision error: initial simplex is not convex. Distance=%.2g\n", dist);
+ qh_errexit(qh, qh_ERRsingular, NULL, NULL);
+ }
+ if (dist > qh->DISTround) {
+ zzinc_(Zconcaveridges);
+ qh_precision(qh, "concave ridge");
+ qh_fprintf(qh, qh->ferr, 6115, "qhull precision error: f%d is concave to f%d, since p%d(v%d) is %6.4g above\n",
+ facet->id, neighbor->id, qh_pointid(qh, vertex->point), vertex->id, dist);
+ errfacet1= facet;
+ errfacet2= neighbor;
+ waserror= True;
+ }else if (qh->ZEROcentrum) {
+ if (dist > 0) { /* qh_checkzero checks that dist < - qh->DISTround */
+ zzinc_(Zcoplanarridges);
+ qh_precision(qh, "coplanar ridge");
+ qh_fprintf(qh, qh->ferr, 6116, "qhull precision error: f%d is clearly not convex to f%d, since p%d(v%d) is %6.4g above\n",
+ facet->id, neighbor->id, qh_pointid(qh, vertex->point), vertex->id, dist);
+ errfacet1= facet;
+ errfacet2= neighbor;
+ waserror= True;
+ }
+ }else {
+ zzinc_(Zcoplanarridges);
+ qh_precision(qh, "coplanar ridge");
+ trace0((qh, qh->ferr, 22, "qhull precision error: f%d may be coplanar to f%d, since p%d(v%d) is within %6.4g during p%d\n",
+ facet->id, neighbor->id, qh_pointid(qh, vertex->point), vertex->id, dist, qh->furthest_id));
+ }
+ }
+ }
+ }
+ if (!allsimplicial) {
+ if (qh->CENTERtype == qh_AScentrum) {
+ if (!facet->center)
+ facet->center= qh_getcentrum(qh, facet);
+ centrum= facet->center;
+ }else {
+ if (!centrum_warning && (!facet->simplicial || facet->tricoplanar)) {
+ centrum_warning= True;
+ qh_fprintf(qh, qh->ferr, 7062, "qhull warning: recomputing centrums for convexity test. This may lead to false, precision errors.\n");
+ }
+ centrum= qh_getcentrum(qh, facet);
+ tempcentrum= True;
+ }
+ FOREACHneighbor_(facet) {
+ if (qh->ZEROcentrum && facet->simplicial && neighbor->simplicial)
+ continue;
+ if (facet->tricoplanar || neighbor->tricoplanar)
+ continue;
+ zzinc_(Zdistconvex);
+ qh_distplane(qh, centrum, neighbor, &dist);
+ if (dist > qh->DISTround) {
+ zzinc_(Zconcaveridges);
+ qh_precision(qh, "concave ridge");
+ qh_fprintf(qh, qh->ferr, 6117, "qhull precision error: f%d is concave to f%d. Centrum of f%d is %6.4g above f%d\n",
+ facet->id, neighbor->id, facet->id, dist, neighbor->id);
+ errfacet1= facet;
+ errfacet2= neighbor;
+ waserror= True;
+ }else if (dist >= 0.0) { /* if arithmetic always rounds the same,
+ can test against centrum radius instead */
+ zzinc_(Zcoplanarridges);
+ qh_precision(qh, "coplanar ridge");
+ qh_fprintf(qh, qh->ferr, 6118, "qhull precision error: f%d is coplanar or concave to f%d. Centrum of f%d is %6.4g above f%d\n",
+ facet->id, neighbor->id, facet->id, dist, neighbor->id);
+ errfacet1= facet;
+ errfacet2= neighbor;
+ waserror= True;
+ }
+ }
+ if (tempcentrum)
+ qh_memfree(qh, centrum, qh->normal_size);
+ }
+ }
+ if (waserror && !qh->FORCEoutput)
+ qh_errexit2(qh, qh_ERRprec, errfacet1, errfacet2);
+} /* checkconvex */
+
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="checkfacet">-</a>
+
+ qh_checkfacet(qh, facet, newmerge, waserror )
+ checks for consistency errors in facet
+ newmerge set if from merge_r.c
+
+ returns:
+ sets waserror if any error occurs
+
+ checks:
+ vertex ids are inverse sorted
+ unless newmerge, at least hull_dim neighbors and vertices (exactly if simplicial)
+ if non-simplicial, at least as many ridges as neighbors
+ neighbors are not duplicated
+ ridges are not duplicated
+ in 3-d, ridges=verticies
+ (qh.hull_dim-1) ridge vertices
+ neighbors are reciprocated
+ ridge neighbors are facet neighbors and a ridge for every neighbor
+ simplicial neighbors match facetintersect
+ vertex intersection matches vertices of common ridges
+ vertex neighbors and facet vertices agree
+ all ridges have distinct vertex sets
+
+ notes:
+ uses neighbor->seen
+
+ design:
+ check sets
+ check vertices
+ check sizes of neighbors and vertices
+ check for qh_MERGEridge and qh_DUPLICATEridge flags
+ check neighbor set
+ check ridge set
+ check ridges, neighbors, and vertices
+*/
+void qh_checkfacet(qhT *qh, facetT *facet, boolT newmerge, boolT *waserrorp) {
+ facetT *neighbor, **neighborp, *errother=NULL;
+ ridgeT *ridge, **ridgep, *errridge= NULL, *ridge2;
+ vertexT *vertex, **vertexp;
+ unsigned previousid= INT_MAX;
+ int numneighbors, numvertices, numridges=0, numRvertices=0;
+ boolT waserror= False;
+ int skipA, skipB, ridge_i, ridge_n, i;
+ setT *intersection;
+
+ if (facet->visible) {
+ qh_fprintf(qh, qh->ferr, 6119, "qhull internal error (qh_checkfacet): facet f%d is on the visible_list\n",
+ facet->id);
+ qh_errexit(qh, qh_ERRqhull, facet, NULL);
+ }
+ if (!facet->normal) {
+ qh_fprintf(qh, qh->ferr, 6120, "qhull internal error (qh_checkfacet): facet f%d does not have a normal\n",
+ facet->id);
+ waserror= True;
+ }
+ qh_setcheck(qh, facet->vertices, "vertices for f", facet->id);
+ qh_setcheck(qh, facet->ridges, "ridges for f", facet->id);
+ qh_setcheck(qh, facet->outsideset, "outsideset for f", facet->id);
+ qh_setcheck(qh, facet->coplanarset, "coplanarset for f", facet->id);
+ qh_setcheck(qh, facet->neighbors, "neighbors for f", facet->id);
+ FOREACHvertex_(facet->vertices) {
+ if (vertex->deleted) {
+ qh_fprintf(qh, qh->ferr, 6121, "qhull internal error (qh_checkfacet): deleted vertex v%d in f%d\n", vertex->id, facet->id);
+ qh_errprint(qh, "ERRONEOUS", NULL, NULL, NULL, vertex);
+ waserror= True;
+ }
+ if (vertex->id >= previousid) {
+ qh_fprintf(qh, qh->ferr, 6122, "qhull internal error (qh_checkfacet): vertices of f%d are not in descending id order at v%d\n", facet->id, vertex->id);
+ waserror= True;
+ break;
+ }
+ previousid= vertex->id;
+ }
+ numneighbors= qh_setsize(qh, facet->neighbors);
+ numvertices= qh_setsize(qh, facet->vertices);
+ numridges= qh_setsize(qh, facet->ridges);
+ if (facet->simplicial) {
+ if (numvertices+numneighbors != 2*qh->hull_dim
+ && !facet->degenerate && !facet->redundant) {
+ qh_fprintf(qh, qh->ferr, 6123, "qhull internal error (qh_checkfacet): for simplicial facet f%d, #vertices %d + #neighbors %d != 2*qh->hull_dim\n",
+ facet->id, numvertices, numneighbors);
+ qh_setprint(qh, qh->ferr, "", facet->neighbors);
+ waserror= True;
+ }
+ }else { /* non-simplicial */
+ if (!newmerge
+ &&(numvertices < qh->hull_dim || numneighbors < qh->hull_dim)
+ && !facet->degenerate && !facet->redundant) {
+ qh_fprintf(qh, qh->ferr, 6124, "qhull internal error (qh_checkfacet): for facet f%d, #vertices %d or #neighbors %d < qh->hull_dim\n",
+ facet->id, numvertices, numneighbors);
+ waserror= True;
+ }
+ /* in 3-d, can get a vertex twice in an edge list, e.g., RBOX 1000 s W1e-13 t995849315 D2 | QHULL d Tc Tv TP624 TW1e-13 T4 */
+ if (numridges < numneighbors
+ ||(qh->hull_dim == 3 && numvertices > numridges && !qh->NEWfacets)
+ ||(qh->hull_dim == 2 && numridges + numvertices + numneighbors != 6)) {
+ if (!facet->degenerate && !facet->redundant) {
+ qh_fprintf(qh, qh->ferr, 6125, "qhull internal error (qh_checkfacet): for facet f%d, #ridges %d < #neighbors %d or(3-d) > #vertices %d or(2-d) not all 2\n",
+ facet->id, numridges, numneighbors, numvertices);
+ waserror= True;
+ }
+ }
+ }
+ FOREACHneighbor_(facet) {
+ if (neighbor == qh_MERGEridge || neighbor == qh_DUPLICATEridge) {
+ qh_fprintf(qh, qh->ferr, 6126, "qhull internal error (qh_checkfacet): facet f%d still has a MERGE or DUP neighbor\n", facet->id);
+ qh_errexit(qh, qh_ERRqhull, facet, NULL);
+ }
+ neighbor->seen= True;
+ }
+ FOREACHneighbor_(facet) {
+ if (!qh_setin(neighbor->neighbors, facet)) {
+ qh_fprintf(qh, qh->ferr, 6127, "qhull internal error (qh_checkfacet): facet f%d has neighbor f%d, but f%d does not have neighbor f%d\n",
+ facet->id, neighbor->id, neighbor->id, facet->id);
+ errother= neighbor;
+ waserror= True;
+ }
+ if (!neighbor->seen) {
+ qh_fprintf(qh, qh->ferr, 6128, "qhull internal error (qh_checkfacet): facet f%d has a duplicate neighbor f%d\n",
+ facet->id, neighbor->id);
+ errother= neighbor;
+ waserror= True;
+ }
+ neighbor->seen= False;
+ }
+ FOREACHridge_(facet->ridges) {
+ qh_setcheck(qh, ridge->vertices, "vertices for r", ridge->id);
+ ridge->seen= False;
+ }
+ FOREACHridge_(facet->ridges) {
+ if (ridge->seen) {
+ qh_fprintf(qh, qh->ferr, 6129, "qhull internal error (qh_checkfacet): facet f%d has a duplicate ridge r%d\n",
+ facet->id, ridge->id);
+ errridge= ridge;
+ waserror= True;
+ }
+ ridge->seen= True;
+ numRvertices= qh_setsize(qh, ridge->vertices);
+ if (numRvertices != qh->hull_dim - 1) {
+ qh_fprintf(qh, qh->ferr, 6130, "qhull internal error (qh_checkfacet): ridge between f%d and f%d has %d vertices\n",
+ ridge->top->id, ridge->bottom->id, numRvertices);
+ errridge= ridge;
+ waserror= True;
+ }
+ neighbor= otherfacet_(ridge, facet);
+ neighbor->seen= True;
+ if (!qh_setin(facet->neighbors, neighbor)) {
+ qh_fprintf(qh, qh->ferr, 6131, "qhull internal error (qh_checkfacet): for facet f%d, neighbor f%d of ridge r%d not in facet\n",
+ facet->id, neighbor->id, ridge->id);
+ errridge= ridge;
+ waserror= True;
+ }
+ }
+ if (!facet->simplicial) {
+ FOREACHneighbor_(facet) {
+ if (!neighbor->seen) {
+ qh_fprintf(qh, qh->ferr, 6132, "qhull internal error (qh_checkfacet): facet f%d does not have a ridge for neighbor f%d\n",
+ facet->id, neighbor->id);
+ errother= neighbor;
+ waserror= True;
+ }
+ intersection= qh_vertexintersect_new(qh, facet->vertices, neighbor->vertices);
+ qh_settemppush(qh, intersection);
+ FOREACHvertex_(facet->vertices) {
+ vertex->seen= False;
+ vertex->seen2= False;
+ }
+ FOREACHvertex_(intersection)
+ vertex->seen= True;
+ FOREACHridge_(facet->ridges) {
+ if (neighbor != otherfacet_(ridge, facet))
+ continue;
+ FOREACHvertex_(ridge->vertices) {
+ if (!vertex->seen) {
+ qh_fprintf(qh, qh->ferr, 6133, "qhull internal error (qh_checkfacet): vertex v%d in r%d not in f%d intersect f%d\n",
+ vertex->id, ridge->id, facet->id, neighbor->id);
+ qh_errexit(qh, qh_ERRqhull, facet, ridge);
+ }
+ vertex->seen2= True;
+ }
+ }
+ if (!newmerge) {
+ FOREACHvertex_(intersection) {
+ if (!vertex->seen2) {
+ if (qh->IStracing >=3 || !qh->MERGING) {
+ qh_fprintf(qh, qh->ferr, 6134, "qhull precision error (qh_checkfacet): vertex v%d in f%d intersect f%d but\n\
+ not in a ridge. This is ok under merging. Last point was p%d\n",
+ vertex->id, facet->id, neighbor->id, qh->furthest_id);
+ if (!qh->FORCEoutput && !qh->MERGING) {
+ qh_errprint(qh, "ERRONEOUS", facet, neighbor, NULL, vertex);
+ if (!qh->MERGING)
+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+ }
+ }
+ }
+ }
+ }
+ qh_settempfree(qh, &intersection);
+ }
+ }else { /* simplicial */
+ FOREACHneighbor_(facet) {
+ if (neighbor->simplicial) {
+ skipA= SETindex_(facet->neighbors, neighbor);
+ skipB= qh_setindex(neighbor->neighbors, facet);
+ if (skipA<0 || skipB<0 || !qh_setequal_skip(facet->vertices, skipA, neighbor->vertices, skipB)) {
+ qh_fprintf(qh, qh->ferr, 6135, "qhull internal error (qh_checkfacet): facet f%d skip %d and neighbor f%d skip %d do not match \n",
+ facet->id, skipA, neighbor->id, skipB);
+ errother= neighbor;
+ waserror= True;
+ }
+ }
+ }
+ }
+ if (qh->hull_dim < 5 && (qh->IStracing > 2 || qh->CHECKfrequently)) {
+ FOREACHridge_i_(qh, facet->ridges) { /* expensive */
+ for (i=ridge_i+1; i < ridge_n; i++) {
+ ridge2= SETelemt_(facet->ridges, i, ridgeT);
+ if (qh_setequal(ridge->vertices, ridge2->vertices)) {
+ qh_fprintf(qh, qh->ferr, 6227, "Qhull internal error (qh_checkfacet): ridges r%d and r%d have the same vertices\n",
+ ridge->id, ridge2->id);
+ errridge= ridge;
+ waserror= True;
+ }
+ }
+ }
+ }
+ if (waserror) {
+ qh_errprint(qh, "ERRONEOUS", facet, errother, errridge, NULL);
+ *waserrorp= True;
+ }
+} /* checkfacet */
+
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="checkflipped_all">-</a>
+
+ qh_checkflipped_all(qh, facetlist )
+ checks orientation of facets in list against interior point
+*/
+void qh_checkflipped_all(qhT *qh, facetT *facetlist) {
+ facetT *facet;
+ boolT waserror= False;
+ realT dist;
+
+ if (facetlist == qh->facet_list)
+ zzval_(Zflippedfacets)= 0;
+ FORALLfacet_(facetlist) {
+ if (facet->normal && !qh_checkflipped(qh, facet, &dist, !qh_ALL)) {
+ qh_fprintf(qh, qh->ferr, 6136, "qhull precision error: facet f%d is flipped, distance= %6.12g\n",
+ facet->id, dist);
+ if (!qh->FORCEoutput) {
+ qh_errprint(qh, "ERRONEOUS", facet, NULL, NULL, NULL);
+ waserror= True;
+ }
+ }
+ }
+ if (waserror) {
+ qh_fprintf(qh, qh->ferr, 8101, "\n\
+A flipped facet occurs when its distance to the interior point is\n\
+greater than %2.2g, the maximum roundoff error.\n", -qh->DISTround);
+ qh_errexit(qh, qh_ERRprec, NULL, NULL);
+ }
+} /* checkflipped_all */
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="checkpolygon">-</a>
+
+ qh_checkpolygon(qh, facetlist )
+ checks the correctness of the structure
+
+ notes:
+ call with either qh.facet_list or qh.newfacet_list
+ checks num_facets and num_vertices if qh.facet_list
+
+ design:
+ for each facet
+ checks facet and outside set
+ initializes vertexlist
+ for each facet
+ checks vertex set
+ if checking all facets(qh.facetlist)
+ check facet count
+ if qh.VERTEXneighbors
+ check vertex neighbors and count
+ check vertex count
+*/
+void qh_checkpolygon(qhT *qh, facetT *facetlist) {
+ facetT *facet;
+ vertexT *vertex, **vertexp, *vertexlist;
+ int numfacets= 0, numvertices= 0, numridges= 0;
+ int totvneighbors= 0, totvertices= 0;
+ boolT waserror= False, nextseen= False, visibleseen= False;
+
+ trace1((qh, qh->ferr, 1027, "qh_checkpolygon: check all facets from f%d\n", facetlist->id));
+ if (facetlist != qh->facet_list || qh->ONLYgood)
+ nextseen= True;
+ FORALLfacet_(facetlist) {
+ if (facet == qh->visible_list)
+ visibleseen= True;
+ if (!facet->visible) {
+ if (!nextseen) {
+ if (facet == qh->facet_next)
+ nextseen= True;
+ else if (qh_setsize(qh, facet->outsideset)) {
+ if (!qh->NARROWhull
+#if !qh_COMPUTEfurthest
+ || facet->furthestdist >= qh->MINoutside
+#endif
+ ) {
+ qh_fprintf(qh, qh->ferr, 6137, "qhull internal error (qh_checkpolygon): f%d has outside points before qh->facet_next\n",
+ facet->id);
+ qh_errexit(qh, qh_ERRqhull, facet, NULL);
+ }
+ }
+ }
+ numfacets++;
+ qh_checkfacet(qh, facet, False, &waserror);
+ }
+ }
+ if (qh->visible_list && !visibleseen && facetlist == qh->facet_list) {
+ qh_fprintf(qh, qh->ferr, 6138, "qhull internal error (qh_checkpolygon): visible list f%d no longer on facet list\n", qh->visible_list->id);
+ qh_printlists(qh);
+ qh_errexit(qh, qh_ERRqhull, qh->visible_list, NULL);
+ }
+ if (facetlist == qh->facet_list)
+ vertexlist= qh->vertex_list;
+ else if (facetlist == qh->newfacet_list)
+ vertexlist= qh->newvertex_list;
+ else
+ vertexlist= NULL;
+ FORALLvertex_(vertexlist) {
+ vertex->seen= False;
+ vertex->visitid= 0;
+ }
+ FORALLfacet_(facetlist) {
+ if (facet->visible)
+ continue;
+ if (facet->simplicial)
+ numridges += qh->hull_dim;
+ else
+ numridges += qh_setsize(qh, facet->ridges);
+ FOREACHvertex_(facet->vertices) {
+ vertex->visitid++;
+ if (!vertex->seen) {
+ vertex->seen= True;
+ numvertices++;
+ if (qh_pointid(qh, vertex->point) == qh_IDunknown) {
+ qh_fprintf(qh, qh->ferr, 6139, "qhull internal error (qh_checkpolygon): unknown point %p for vertex v%d first_point %p\n",
+ vertex->point, vertex->id, qh->first_point);
+ waserror= True;
+ }
+ }
+ }
+ }
+ qh->vertex_visit += (unsigned int)numfacets;
+ if (facetlist == qh->facet_list) {
+ if (numfacets != qh->num_facets - qh->num_visible) {
+ qh_fprintf(qh, qh->ferr, 6140, "qhull internal error (qh_checkpolygon): actual number of facets is %d, cumulative facet count is %d - %d visible facets\n",
+ numfacets, qh->num_facets, qh->num_visible);
+ waserror= True;
+ }
+ qh->vertex_visit++;
+ if (qh->VERTEXneighbors) {
+ FORALLvertices {
+ qh_setcheck(qh, vertex->neighbors, "neighbors for v", vertex->id);
+ if (vertex->deleted)
+ continue;
+ totvneighbors += qh_setsize(qh, vertex->neighbors);
+ }
+ FORALLfacet_(facetlist)
+ totvertices += qh_setsize(qh, facet->vertices);
+ if (totvneighbors != totvertices) {
+ qh_fprintf(qh, qh->ferr, 6141, "qhull internal error (qh_checkpolygon): vertex neighbors inconsistent. Totvneighbors %d, totvertices %d\n",
+ totvneighbors, totvertices);
+ waserror= True;
+ }
+ }
+ if (numvertices != qh->num_vertices - qh_setsize(qh, qh->del_vertices)) {
+ qh_fprintf(qh, qh->ferr, 6142, "qhull internal error (qh_checkpolygon): actual number of vertices is %d, cumulative vertex count is %d\n",
+ numvertices, qh->num_vertices - qh_setsize(qh, qh->del_vertices));
+ waserror= True;
+ }
+ if (qh->hull_dim == 2 && numvertices != numfacets) {
+ qh_fprintf(qh, qh->ferr, 6143, "qhull internal error (qh_checkpolygon): #vertices %d != #facets %d\n",
+ numvertices, numfacets);
+ waserror= True;
+ }
+ if (qh->hull_dim == 3 && numvertices + numfacets - numridges/2 != 2) {
+ qh_fprintf(qh, qh->ferr, 7063, "qhull warning: #vertices %d + #facets %d - #edges %d != 2\n\
+ A vertex appears twice in a edge list. May occur during merging.",
+ numvertices, numfacets, numridges/2);
+ /* occurs if lots of merging and a vertex ends up twice in an edge list. e.g., RBOX 1000 s W1e-13 t995849315 D2 | QHULL d Tc Tv */
+ }
+ }
+ if (waserror)
+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+} /* checkpolygon */
+
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="checkvertex">-</a>
+
+ qh_checkvertex(qh, vertex )
+ check vertex for consistency
+ checks vertex->neighbors
+
+ notes:
+ neighbors checked efficiently in checkpolygon
+*/
+void qh_checkvertex(qhT *qh, vertexT *vertex) {
+ boolT waserror= False;
+ facetT *neighbor, **neighborp, *errfacet=NULL;
+
+ if (qh_pointid(qh, vertex->point) == qh_IDunknown) {
+ qh_fprintf(qh, qh->ferr, 6144, "qhull internal error (qh_checkvertex): unknown point id %p\n", vertex->point);
+ waserror= True;
+ }
+ if (vertex->id >= qh->vertex_id) {
+ qh_fprintf(qh, qh->ferr, 6145, "qhull internal error (qh_checkvertex): unknown vertex id %d\n", vertex->id);
+ waserror= True;
+ }
+ if (!waserror && !vertex->deleted) {
+ if (qh_setsize(qh, vertex->neighbors)) {
+ FOREACHneighbor_(vertex) {
+ if (!qh_setin(neighbor->vertices, vertex)) {
+ qh_fprintf(qh, qh->ferr, 6146, "qhull internal error (qh_checkvertex): neighbor f%d does not contain v%d\n", neighbor->id, vertex->id);
+ errfacet= neighbor;
+ waserror= True;
+ }
+ }
+ }
+ }
+ if (waserror) {
+ qh_errprint(qh, "ERRONEOUS", NULL, NULL, NULL, vertex);
+ qh_errexit(qh, qh_ERRqhull, errfacet, NULL);
+ }
+} /* checkvertex */
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="clearcenters">-</a>
+
+ qh_clearcenters(qh, type )
+ clear old data from facet->center
+
+ notes:
+ sets new centertype
+ nop if CENTERtype is the same
+*/
+void qh_clearcenters(qhT *qh, qh_CENTER type) {
+ facetT *facet;
+
+ if (qh->CENTERtype != type) {
+ FORALLfacets {
+ if (facet->tricoplanar && !facet->keepcentrum)
+ facet->center= NULL; /* center is owned by the ->keepcentrum facet */
+ else if (qh->CENTERtype == qh_ASvoronoi){
+ if (facet->center) {
+ qh_memfree(qh, facet->center, qh->center_size);
+ facet->center= NULL;
+ }
+ }else /* qh->CENTERtype == qh_AScentrum */ {
+ if (facet->center) {
+ qh_memfree(qh, facet->center, qh->normal_size);
+ facet->center= NULL;
+ }
+ }
+ }
+ qh->CENTERtype= type;
+ }
+ trace2((qh, qh->ferr, 2043, "qh_clearcenters: switched to center type %d\n", type));
+} /* clearcenters */
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="createsimplex">-</a>
+
+ qh_createsimplex(qh, vertices )
+ creates a simplex from a set of vertices
+
+ returns:
+ initializes qh.facet_list to the simplex
+ initializes qh.newfacet_list, .facet_tail
+ initializes qh.vertex_list, .newvertex_list, .vertex_tail
+
+ design:
+ initializes lists
+ for each vertex
+ create a new facet
+ for each new facet
+ create its neighbor set
+*/
+void qh_createsimplex(qhT *qh, setT *vertices) {
+ facetT *facet= NULL, *newfacet;
+ boolT toporient= True;
+ int vertex_i, vertex_n, nth;
+ setT *newfacets= qh_settemp(qh, qh->hull_dim+1);
+ vertexT *vertex;
+
+ qh->facet_list= qh->newfacet_list= qh->facet_tail= qh_newfacet(qh);
+ qh->num_facets= qh->num_vertices= qh->num_visible= 0;
+ qh->vertex_list= qh->newvertex_list= qh->vertex_tail= qh_newvertex(qh, NULL);
+ FOREACHvertex_i_(qh, vertices) {
+ newfacet= qh_newfacet(qh);
+ newfacet->vertices= qh_setnew_delnthsorted(qh, vertices, vertex_n,
+ vertex_i, 0);
+ newfacet->toporient= (unsigned char)toporient;
+ qh_appendfacet(qh, newfacet);
+ newfacet->newfacet= True;
+ qh_appendvertex(qh, vertex);
+ qh_setappend(qh, &newfacets, newfacet);
+ toporient ^= True;
+ }
+ FORALLnew_facets {
+ nth= 0;
+ FORALLfacet_(qh->newfacet_list) {
+ if (facet != newfacet)
+ SETelem_(newfacet->neighbors, nth++)= facet;
+ }
+ qh_settruncate(qh, newfacet->neighbors, qh->hull_dim);
+ }
+ qh_settempfree(qh, &newfacets);
+ trace1((qh, qh->ferr, 1028, "qh_createsimplex: created simplex\n"));
+} /* createsimplex */
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="delridge">-</a>
+
+ qh_delridge(qh, ridge )
+ deletes ridge from data structures it belongs to
+ frees up its memory
+
+ notes:
+ in merge_r.c, caller sets vertex->delridge for each vertex
+ ridges also freed in qh_freeqhull
+*/
+void qh_delridge(qhT *qh, ridgeT *ridge) {
+ void **freelistp; /* used if !qh_NOmem by qh_memfree_() */
+
+ qh_setdel(ridge->top->ridges, ridge);
+ qh_setdel(ridge->bottom->ridges, ridge);
+ qh_setfree(qh, &(ridge->vertices));
+ qh_memfree_(qh, ridge, (int)sizeof(ridgeT), freelistp);
+} /* delridge */
+
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="delvertex">-</a>
+
+ qh_delvertex(qh, vertex )
+ deletes a vertex and frees its memory
+
+ notes:
+ assumes vertex->adjacencies have been updated if needed
+ unlinks from vertex_list
+*/
+void qh_delvertex(qhT *qh, vertexT *vertex) {
+
+ if (vertex == qh->tracevertex)
+ qh->tracevertex= NULL;
+ qh_removevertex(qh, vertex);
+ qh_setfree(qh, &vertex->neighbors);
+ qh_memfree(qh, vertex, (int)sizeof(vertexT));
+} /* delvertex */
+
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="facet3vertex">-</a>
+
+ qh_facet3vertex(qh, )
+ return temporary set of 3-d vertices in qh_ORIENTclock order
+
+ design:
+ if simplicial facet
+ build set from facet->vertices with facet->toporient
+ else
+ for each ridge in order
+ build set from ridge's vertices
+*/
+setT *qh_facet3vertex(qhT *qh, facetT *facet) {
+ ridgeT *ridge, *firstridge;
+ vertexT *vertex;
+ int cntvertices, cntprojected=0;
+ setT *vertices;
+
+ cntvertices= qh_setsize(qh, facet->vertices);
+ vertices= qh_settemp(qh, cntvertices);
+ if (facet->simplicial) {
+ if (cntvertices != 3) {
+ qh_fprintf(qh, qh->ferr, 6147, "qhull internal error (qh_facet3vertex): only %d vertices for simplicial facet f%d\n",
+ cntvertices, facet->id);
+ qh_errexit(qh, qh_ERRqhull, facet, NULL);
+ }
+ qh_setappend(qh, &vertices, SETfirst_(facet->vertices));
+ if (facet->toporient ^ qh_ORIENTclock)
+ qh_setappend(qh, &vertices, SETsecond_(facet->vertices));
+ else
+ qh_setaddnth(qh, &vertices, 0, SETsecond_(facet->vertices));
+ qh_setappend(qh, &vertices, SETelem_(facet->vertices, 2));
+ }else {
+ ridge= firstridge= SETfirstt_(facet->ridges, ridgeT); /* no infinite */
+ while ((ridge= qh_nextridge3d(ridge, facet, &vertex))) {
+ qh_setappend(qh, &vertices, vertex);
+ if (++cntprojected > cntvertices || ridge == firstridge)
+ break;
+ }
+ if (!ridge || cntprojected != cntvertices) {
+ qh_fprintf(qh, qh->ferr, 6148, "qhull internal error (qh_facet3vertex): ridges for facet %d don't match up. got at least %d\n",
+ facet->id, cntprojected);
+ qh_errexit(qh, qh_ERRqhull, facet, ridge);
+ }
+ }
+ return vertices;
+} /* facet3vertex */
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="findbestfacet">-</a>
+
+ qh_findbestfacet(qh, point, bestoutside, bestdist, isoutside )
+ find facet that is furthest below a point
+
+ for Delaunay triangulations,
+ Use qh_setdelaunay() to lift point to paraboloid and scale by 'Qbb' if needed
+ Do not use options 'Qbk', 'QBk', or 'QbB' since they scale the coordinates.
+
+ returns:
+ if bestoutside is set (e.g., qh_ALL)
+ returns best facet that is not upperdelaunay
+ if Delaunay and inside, point is outside circumsphere of bestfacet
+ else
+ returns first facet below point
+ if point is inside, returns nearest, !upperdelaunay facet
+ distance to facet
+ isoutside set if outside of facet
+
+ notes:
+ For tricoplanar facets, this finds one of the tricoplanar facets closest
+ to the point. For Delaunay triangulations, the point may be inside a
+ different tricoplanar facet. See <a href="../html/qh-code.htm#findfacet">locate a facet with qh_findbestfacet()</a>
+
+ If inside, qh_findbestfacet performs an exhaustive search
+ this may be too conservative. Sometimes it is clearly required.
+
+ qh_findbestfacet is not used by qhull.
+ uses qh.visit_id and qh.coplanarset
+
+ see:
+ <a href="geom_r.c#findbest">qh_findbest</a>
+*/
+facetT *qh_findbestfacet(qhT *qh, pointT *point, boolT bestoutside,
+ realT *bestdist, boolT *isoutside) {
+ facetT *bestfacet= NULL;
+ int numpart, totpart= 0;
+
+ bestfacet= qh_findbest(qh, point, qh->facet_list,
+ bestoutside, !qh_ISnewfacets, bestoutside /* qh_NOupper */,
+ bestdist, isoutside, &totpart);
+ if (*bestdist < -qh->DISTround) {
+ bestfacet= qh_findfacet_all(qh, point, bestdist, isoutside, &numpart);
+ totpart += numpart;
+ if ((isoutside && *isoutside && bestoutside)
+ || (isoutside && !*isoutside && bestfacet->upperdelaunay)) {
+ bestfacet= qh_findbest(qh, point, bestfacet,
+ bestoutside, False, bestoutside,
+ bestdist, isoutside, &totpart);
+ totpart += numpart;
+ }
+ }
+ trace3((qh, qh->ferr, 3014, "qh_findbestfacet: f%d dist %2.2g isoutside %d totpart %d\n",
+ bestfacet->id, *bestdist, (isoutside ? *isoutside : UINT_MAX), totpart));
+ return bestfacet;
+} /* findbestfacet */
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="findbestlower">-</a>
+
+ qh_findbestlower(qh, facet, point, bestdist, numpart )
+ returns best non-upper, non-flipped neighbor of facet for point
+ if needed, searches vertex neighbors
+
+ returns:
+ returns bestdist and updates numpart
+
+ notes:
+ if Delaunay and inside, point is outside of circumsphere of bestfacet
+ called by qh_findbest() for points above an upperdelaunay facet
+
+*/
+facetT *qh_findbestlower(qhT *qh, facetT *upperfacet, pointT *point, realT *bestdistp, int *numpart) {
+ facetT *neighbor, **neighborp, *bestfacet= NULL;
+ realT bestdist= -REALmax/2 /* avoid underflow */;
+ realT dist;
+ vertexT *vertex;
+ boolT isoutside= False; /* not used */
+
+ zinc_(Zbestlower);
+ FOREACHneighbor_(upperfacet) {
+ if (neighbor->upperdelaunay || neighbor->flipped)
+ continue;
+ (*numpart)++;
+ qh_distplane(qh, point, neighbor, &dist);
+ if (dist > bestdist) {
+ bestfacet= neighbor;
+ bestdist= dist;
+ }
+ }
+ if (!bestfacet) {
+ zinc_(Zbestlowerv);
+ /* rarely called, numpart does not count nearvertex computations */
+ vertex= qh_nearvertex(qh, upperfacet, point, &dist);
+ qh_vertexneighbors(qh);
+ FOREACHneighbor_(vertex) {
+ if (neighbor->upperdelaunay || neighbor->flipped)
+ continue;
+ (*numpart)++;
+ qh_distplane(qh, point, neighbor, &dist);
+ if (dist > bestdist) {
+ bestfacet= neighbor;
+ bestdist= dist;
+ }
+ }
+ }
+ if (!bestfacet) {
+ zinc_(Zbestlowerall); /* invoked once per point in outsideset */
+ zmax_(Zbestloweralln, qh->num_facets);
+ /* [dec'15] Previously reported as QH6228 */
+ trace3((qh, qh->ferr, 3025, "qh_findbestlower: all neighbors of facet %d are flipped or upper Delaunay. Search all facets\n",
+ upperfacet->id));
+ /* rarely called */
+ bestfacet= qh_findfacet_all(qh, point, &bestdist, &isoutside, numpart);
+ }
+ *bestdistp= bestdist;
+ trace3((qh, qh->ferr, 3015, "qh_findbestlower: f%d dist %2.2g for f%d p%d\n",
+ bestfacet->id, bestdist, upperfacet->id, qh_pointid(qh, point)));
+ return bestfacet;
+} /* findbestlower */
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="findfacet_all">-</a>
+
+ qh_findfacet_all(qh, point, bestdist, isoutside, numpart )
+ exhaustive search for facet below a point
+
+ for Delaunay triangulations,
+ Use qh_setdelaunay() to lift point to paraboloid and scale by 'Qbb' if needed
+ Do not use options 'Qbk', 'QBk', or 'QbB' since they scale the coordinates.
+
+ returns:
+ returns first facet below point
+ if point is inside,
+ returns nearest facet
+ distance to facet
+ isoutside if point is outside of the hull
+ number of distance tests
+
+ notes:
+ primarily for library users, rarely used by Qhull
+*/
+facetT *qh_findfacet_all(qhT *qh, pointT *point, realT *bestdist, boolT *isoutside,
+ int *numpart) {
+ facetT *bestfacet= NULL, *facet;
+ realT dist;
+ int totpart= 0;
+
+ *bestdist= -REALmax;
+ *isoutside= False;
+ FORALLfacets {
+ if (facet->flipped || !facet->normal)
+ continue;
+ totpart++;
+ qh_distplane(qh, point, facet, &dist);
+ if (dist > *bestdist) {
+ *bestdist= dist;
+ bestfacet= facet;
+ if (dist > qh->MINoutside) {
+ *isoutside= True;
+ break;
+ }
+ }
+ }
+ *numpart= totpart;
+ trace3((qh, qh->ferr, 3016, "qh_findfacet_all: f%d dist %2.2g isoutside %d totpart %d\n",
+ getid_(bestfacet), *bestdist, *isoutside, totpart));
+ return bestfacet;
+} /* findfacet_all */
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="findgood">-</a>
+
+ qh_findgood(qh, facetlist, goodhorizon )
+ identify good facets for qh.PRINTgood
+ if qh.GOODvertex>0
+ facet includes point as vertex
+ if !match, returns goodhorizon
+ inactive if qh.MERGING
+ if qh.GOODpoint
+ facet is visible or coplanar (>0) or not visible (<0)
+ if qh.GOODthreshold
+ facet->normal matches threshold
+ if !goodhorizon and !match,
+ selects facet with closest angle
+ sets GOODclosest
+
+ returns:
+ number of new, good facets found
+ determines facet->good
+ may update qh.GOODclosest
+
+ notes:
+ qh_findgood_all further reduces the good region
+
+ design:
+ count good facets
+ mark good facets for qh.GOODpoint
+ mark good facets for qh.GOODthreshold
+ if necessary
+ update qh.GOODclosest
+*/
+int qh_findgood(qhT *qh, facetT *facetlist, int goodhorizon) {
+ facetT *facet, *bestfacet= NULL;
+ realT angle, bestangle= REALmax, dist;
+ int numgood=0;
+
+ FORALLfacet_(facetlist) {
+ if (facet->good)
+ numgood++;
+ }
+ if (qh->GOODvertex>0 && !qh->MERGING) {
+ FORALLfacet_(facetlist) {
+ if (!qh_isvertex(qh->GOODvertexp, facet->vertices)) {
+ facet->good= False;
+ numgood--;
+ }
+ }
+ }
+ if (qh->GOODpoint && numgood) {
+ FORALLfacet_(facetlist) {
+ if (facet->good && facet->normal) {
+ zinc_(Zdistgood);
+ qh_distplane(qh, qh->GOODpointp, facet, &dist);
+ if ((qh->GOODpoint > 0) ^ (dist > 0.0)) {
+ facet->good= False;
+ numgood--;
+ }
+ }
+ }
+ }
+ if (qh->GOODthreshold && (numgood || goodhorizon || qh->GOODclosest)) {
+ FORALLfacet_(facetlist) {
+ if (facet->good && facet->normal) {
+ if (!qh_inthresholds(qh, facet->normal, &angle)) {
+ facet->good= False;
+ numgood--;
+ if (angle < bestangle) {
+ bestangle= angle;
+ bestfacet= facet;
+ }
+ }
+ }
+ }
+ if (!numgood && (!goodhorizon || qh->GOODclosest)) {
+ if (qh->GOODclosest) {
+ if (qh->GOODclosest->visible)
+ qh->GOODclosest= NULL;
+ else {
+ qh_inthresholds(qh, qh->GOODclosest->normal, &angle);
+ if (angle < bestangle)
+ bestfacet= qh->GOODclosest;
+ }
+ }
+ if (bestfacet && bestfacet != qh->GOODclosest) {
+ if (qh->GOODclosest)
+ qh->GOODclosest->good= False;
+ qh->GOODclosest= bestfacet;
+ bestfacet->good= True;
+ numgood++;
+ trace2((qh, qh->ferr, 2044, "qh_findgood: f%d is closest(%2.2g) to thresholds\n",
+ bestfacet->id, bestangle));
+ return numgood;
+ }
+ }else if (qh->GOODclosest) { /* numgood > 0 */
+ qh->GOODclosest->good= False;
+ qh->GOODclosest= NULL;
+ }
+ }
+ zadd_(Zgoodfacet, numgood);
+ trace2((qh, qh->ferr, 2045, "qh_findgood: found %d good facets with %d good horizon\n",
+ numgood, goodhorizon));
+ if (!numgood && qh->GOODvertex>0 && !qh->MERGING)
+ return goodhorizon;
+ return numgood;
+} /* findgood */
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="findgood_all">-</a>
+
+ qh_findgood_all(qh, facetlist )
+ apply other constraints for good facets (used by qh.PRINTgood)
+ if qh.GOODvertex
+ facet includes (>0) or doesn't include (<0) point as vertex
+ if last good facet and ONLYgood, prints warning and continues
+ if qh.SPLITthresholds
+ facet->normal matches threshold, or if none, the closest one
+ calls qh_findgood
+ nop if good not used
+
+ returns:
+ clears facet->good if not good
+ sets qh.num_good
+
+ notes:
+ this is like qh_findgood but more restrictive
+
+ design:
+ uses qh_findgood to mark good facets
+ marks facets for qh.GOODvertex
+ marks facets for qh.SPLITthreholds
+*/
+void qh_findgood_all(qhT *qh, facetT *facetlist) {
+ facetT *facet, *bestfacet=NULL;
+ realT angle, bestangle= REALmax;
+ int numgood=0, startgood;
+
+ if (!qh->GOODvertex && !qh->GOODthreshold && !qh->GOODpoint
+ && !qh->SPLITthresholds)
+ return;
+ if (!qh->ONLYgood)
+ qh_findgood(qh, qh->facet_list, 0);
+ FORALLfacet_(facetlist) {
+ if (facet->good)
+ numgood++;
+ }
+ if (qh->GOODvertex <0 || (qh->GOODvertex > 0 && qh->MERGING)) {
+ FORALLfacet_(facetlist) {
+ if (facet->good && ((qh->GOODvertex > 0) ^ !!qh_isvertex(qh->GOODvertexp, facet->vertices))) {
+ if (!--numgood) {
+ if (qh->ONLYgood) {
+ qh_fprintf(qh, qh->ferr, 7064, "qhull warning: good vertex p%d does not match last good facet f%d. Ignored.\n",
+ qh_pointid(qh, qh->GOODvertexp), facet->id);
+ return;
+ }else if (qh->GOODvertex > 0)
+ qh_fprintf(qh, qh->ferr, 7065, "qhull warning: point p%d is not a vertex('QV%d').\n",
+ qh->GOODvertex-1, qh->GOODvertex-1);
+ else
+ qh_fprintf(qh, qh->ferr, 7066, "qhull warning: point p%d is a vertex for every facet('QV-%d').\n",
+ -qh->GOODvertex - 1, -qh->GOODvertex - 1);
+ }
+ facet->good= False;
+ }
+ }
+ }
+ startgood= numgood;
+ if (qh->SPLITthresholds) {
+ FORALLfacet_(facetlist) {
+ if (facet->good) {
+ if (!qh_inthresholds(qh, facet->normal, &angle)) {
+ facet->good= False;
+ numgood--;
+ if (angle < bestangle) {
+ bestangle= angle;
+ bestfacet= facet;
+ }
+ }
+ }
+ }
+ if (!numgood && bestfacet) {
+ bestfacet->good= True;
+ numgood++;
+ trace0((qh, qh->ferr, 23, "qh_findgood_all: f%d is closest(%2.2g) to thresholds\n",
+ bestfacet->id, bestangle));
+ return;
+ }
+ }
+ qh->num_good= numgood;
+ trace0((qh, qh->ferr, 24, "qh_findgood_all: %d good facets remain out of %d facets\n",
+ numgood, startgood));
+} /* findgood_all */
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="furthestnext">-</a>
+
+ qh_furthestnext()
+ set qh.facet_next to facet with furthest of all furthest points
+ searches all facets on qh.facet_list
+
+ notes:
+ this may help avoid precision problems
+*/
+void qh_furthestnext(qhT *qh /* qh->facet_list */) {
+ facetT *facet, *bestfacet= NULL;
+ realT dist, bestdist= -REALmax;
+
+ FORALLfacets {
+ if (facet->outsideset) {
+#if qh_COMPUTEfurthest
+ pointT *furthest;
+ furthest= (pointT*)qh_setlast(facet->outsideset);
+ zinc_(Zcomputefurthest);
+ qh_distplane(qh, furthest, facet, &dist);
+#else
+ dist= facet->furthestdist;
+#endif
+ if (dist > bestdist) {
+ bestfacet= facet;
+ bestdist= dist;
+ }
+ }
+ }
+ if (bestfacet) {
+ qh_removefacet(qh, bestfacet);
+ qh_prependfacet(qh, bestfacet, &qh->facet_next);
+ trace1((qh, qh->ferr, 1029, "qh_furthestnext: made f%d next facet(dist %.2g)\n",
+ bestfacet->id, bestdist));
+ }
+} /* furthestnext */
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="furthestout">-</a>
+
+ qh_furthestout(qh, facet )
+ make furthest outside point the last point of outsideset
+
+ returns:
+ updates facet->outsideset
+ clears facet->notfurthest
+ sets facet->furthestdist
+
+ design:
+ determine best point of outsideset
+ make it the last point of outsideset
+*/
+void qh_furthestout(qhT *qh, facetT *facet) {
+ pointT *point, **pointp, *bestpoint= NULL;
+ realT dist, bestdist= -REALmax;
+
+ FOREACHpoint_(facet->outsideset) {
+ qh_distplane(qh, point, facet, &dist);
+ zinc_(Zcomputefurthest);
+ if (dist > bestdist) {
+ bestpoint= point;
+ bestdist= dist;
+ }
+ }
+ if (bestpoint) {
+ qh_setdel(facet->outsideset, point);
+ qh_setappend(qh, &facet->outsideset, point);
+#if !qh_COMPUTEfurthest
+ facet->furthestdist= bestdist;
+#endif
+ }
+ facet->notfurthest= False;
+ trace3((qh, qh->ferr, 3017, "qh_furthestout: p%d is furthest outside point of f%d\n",
+ qh_pointid(qh, point), facet->id));
+} /* furthestout */
+
+
+/*-<a href="qh-qhull_r.htm#TOC"
+ >-------------------------------</a><a name="infiniteloop">-</a>
+
+ qh_infiniteloop(qh, facet )
+ report infinite loop error due to facet
+*/
+void qh_infiniteloop(qhT *qh, facetT *facet) {
+
+ qh_fprintf(qh, qh->ferr, 6149, "qhull internal error (qh_infiniteloop): potential infinite loop detected\n");
+ qh_errexit(qh, qh_ERRqhull, facet, NULL);
+} /* qh_infiniteloop */
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="initbuild">-</a>
+
+ qh_initbuild()
+ initialize hull and outside sets with point array
+ qh.FIRSTpoint/qh.NUMpoints is point array
+ if qh.GOODpoint
+ adds qh.GOODpoint to initial hull
+
+ returns:
+ qh_facetlist with initial hull
+ points partioned into outside sets, coplanar sets, or inside
+ initializes qh.GOODpointp, qh.GOODvertexp,
+
+ design:
+ initialize global variables used during qh_buildhull
+ determine precision constants and points with max/min coordinate values
+ if qh.SCALElast, scale last coordinate(for 'd')
+ build initial simplex
+ partition input points into facets of initial simplex
+ set up lists
+ if qh.ONLYgood
+ check consistency
+ add qh.GOODvertex if defined
+*/
+void qh_initbuild(qhT *qh) {
+ setT *maxpoints, *vertices;
+ facetT *facet;
+ int i, numpart;
+ realT dist;
+ boolT isoutside;
+
+ qh->furthest_id= qh_IDunknown;
+ qh->lastreport= 0;
+ qh->facet_id= qh->vertex_id= qh->ridge_id= 0;
+ qh->visit_id= qh->vertex_visit= 0;
+ qh->maxoutdone= False;
+
+ if (qh->GOODpoint > 0)
+ qh->GOODpointp= qh_point(qh, qh->GOODpoint-1);
+ else if (qh->GOODpoint < 0)
+ qh->GOODpointp= qh_point(qh, -qh->GOODpoint-1);
+ if (qh->GOODvertex > 0)
+ qh->GOODvertexp= qh_point(qh, qh->GOODvertex-1);
+ else if (qh->GOODvertex < 0)
+ qh->GOODvertexp= qh_point(qh, -qh->GOODvertex-1);
+ if ((qh->GOODpoint
+ && (qh->GOODpointp < qh->first_point /* also catches !GOODpointp */
+ || qh->GOODpointp > qh_point(qh, qh->num_points-1)))
+ || (qh->GOODvertex
+ && (qh->GOODvertexp < qh->first_point /* also catches !GOODvertexp */
+ || qh->GOODvertexp > qh_point(qh, qh->num_points-1)))) {
+ qh_fprintf(qh, qh->ferr, 6150, "qhull input error: either QGn or QVn point is > p%d\n",
+ qh->num_points-1);
+ qh_errexit(qh, qh_ERRinput, NULL, NULL);
+ }
+ maxpoints= qh_maxmin(qh, qh->first_point, qh->num_points, qh->hull_dim);
+ if (qh->SCALElast)
+ qh_scalelast(qh, qh->first_point, qh->num_points, qh->hull_dim,
+ qh->MINlastcoord, qh->MAXlastcoord, qh->MAXwidth);
+ qh_detroundoff(qh);
+ if (qh->DELAUNAY && qh->upper_threshold[qh->hull_dim-1] > REALmax/2
+ && qh->lower_threshold[qh->hull_dim-1] < -REALmax/2) {
+ for (i=qh_PRINTEND; i--; ) {
+ if (qh->PRINTout[i] == qh_PRINTgeom && qh->DROPdim < 0
+ && !qh->GOODthreshold && !qh->SPLITthresholds)
+ break; /* in this case, don't set upper_threshold */
+ }
+ if (i < 0) {
+ if (qh->UPPERdelaunay) { /* matches qh.upperdelaunay in qh_setfacetplane */
+ qh->lower_threshold[qh->hull_dim-1]= qh->ANGLEround * qh_ZEROdelaunay;
+ qh->GOODthreshold= True;
+ }else {
+ qh->upper_threshold[qh->hull_dim-1]= -qh->ANGLEround * qh_ZEROdelaunay;
+ if (!qh->GOODthreshold)
+ qh->SPLITthresholds= True; /* build upper-convex hull even if Qg */
+ /* qh_initqhull_globals errors if Qg without Pdk/etc. */
+ }
+ }
+ }
+ vertices= qh_initialvertices(qh, qh->hull_dim, maxpoints, qh->first_point, qh->num_points);
+ qh_initialhull(qh, vertices); /* initial qh->facet_list */
+ qh_partitionall(qh, vertices, qh->first_point, qh->num_points);
+ if (qh->PRINToptions1st || qh->TRACElevel || qh->IStracing) {
+ if (qh->TRACElevel || qh->IStracing)
+ qh_fprintf(qh, qh->ferr, 8103, "\nTrace level %d for %s | %s\n",
+ qh->IStracing ? qh->IStracing : qh->TRACElevel, qh->rbox_command, qh->qhull_command);
+ qh_fprintf(qh, qh->ferr, 8104, "Options selected for Qhull %s:\n%s\n", qh_version, qh->qhull_options);
+ }
+ qh_resetlists(qh, False, qh_RESETvisible /*qh.visible_list newvertex_list newfacet_list */);
+ qh->facet_next= qh->facet_list;
+ qh_furthestnext(qh /* qh->facet_list */);
+ if (qh->PREmerge) {
+ qh->cos_max= qh->premerge_cos;
+ qh->centrum_radius= qh->premerge_centrum;
+ }
+ if (qh->ONLYgood) {
+ if (qh->GOODvertex > 0 && qh->MERGING) {
+ qh_fprintf(qh, qh->ferr, 6151, "qhull input error: 'Qg QVn' (only good vertex) does not work with merging.\nUse 'QJ' to joggle the input or 'Q0' to turn off merging.\n");
+ qh_errexit(qh, qh_ERRinput, NULL, NULL);
+ }
+ if (!(qh->GOODthreshold || qh->GOODpoint
+ || (!qh->MERGEexact && !qh->PREmerge && qh->GOODvertexp))) {
+ qh_fprintf(qh, qh->ferr, 6152, "qhull input error: 'Qg' (ONLYgood) needs a good threshold('Pd0D0'), a\n\
+good point(QGn or QG-n), or a good vertex with 'QJ' or 'Q0' (QVn).\n");
+ qh_errexit(qh, qh_ERRinput, NULL, NULL);
+ }
+ if (qh->GOODvertex > 0 && !qh->MERGING /* matches qh_partitionall */
+ && !qh_isvertex(qh->GOODvertexp, vertices)) {
+ facet= qh_findbestnew(qh, qh->GOODvertexp, qh->facet_list,
+ &dist, !qh_ALL, &isoutside, &numpart);
+ zadd_(Zdistgood, numpart);
+ if (!isoutside) {
+ qh_fprintf(qh, qh->ferr, 6153, "qhull input error: point for QV%d is inside initial simplex. It can not be made a vertex.\n",
+ qh_pointid(qh, qh->GOODvertexp));
+ qh_errexit(qh, qh_ERRinput, NULL, NULL);
+ }
+ if (!qh_addpoint(qh, qh->GOODvertexp, facet, False)) {
+ qh_settempfree(qh, &vertices);
+ qh_settempfree(qh, &maxpoints);
+ return;
+ }
+ }
+ qh_findgood(qh, qh->facet_list, 0);
+ }
+ qh_settempfree(qh, &vertices);
+ qh_settempfree(qh, &maxpoints);
+ trace1((qh, qh->ferr, 1030, "qh_initbuild: initial hull created and points partitioned\n"));
+} /* initbuild */
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="initialhull">-</a>
+
+ qh_initialhull(qh, vertices )
+ constructs the initial hull as a DIM3 simplex of vertices
+
+ design:
+ creates a simplex (initializes lists)
+ determines orientation of simplex
+ sets hyperplanes for facets
+ doubles checks orientation (in case of axis-parallel facets with Gaussian elimination)
+ checks for flipped facets and qh.NARROWhull
+ checks the result
+*/
+void qh_initialhull(qhT *qh, setT *vertices) {
+ facetT *facet, *firstfacet, *neighbor, **neighborp;
+ realT dist, angle, minangle= REALmax;
+#ifndef qh_NOtrace
+ int k;
+#endif
+
+ qh_createsimplex(qh, vertices); /* qh->facet_list */
+ qh_resetlists(qh, False, qh_RESETvisible);
+ qh->facet_next= qh->facet_list; /* advance facet when processed */
+ qh->interior_point= qh_getcenter(qh, vertices);
+ firstfacet= qh->facet_list;
+ qh_setfacetplane(qh, firstfacet);
+ zinc_(Znumvisibility); /* needs to be in printsummary */
+ qh_distplane(qh, qh->interior_point, firstfacet, &dist);
+ if (dist > 0) {
+ FORALLfacets
+ facet->toporient ^= (unsigned char)True;
+ }
+ FORALLfacets
+ qh_setfacetplane(qh, facet);
+ FORALLfacets {
+ if (!qh_checkflipped(qh, facet, NULL, qh_ALL)) {/* due to axis-parallel facet */
+ trace1((qh, qh->ferr, 1031, "qh_initialhull: initial orientation incorrect. Correct all facets\n"));
+ facet->flipped= False;
+ FORALLfacets {
+ facet->toporient ^= (unsigned char)True;
+ qh_orientoutside(qh, facet);
+ }
+ break;
+ }
+ }
+ FORALLfacets {
+ if (!qh_checkflipped(qh, facet, NULL, !qh_ALL)) { /* can happen with 'R0.1' */
+ if (qh->DELAUNAY && ! qh->ATinfinity) {
+ if (qh->UPPERdelaunay)
+ qh_fprintf(qh, qh->ferr, 6240, "Qhull precision error: Initial simplex is cocircular or cospherical. Option 'Qs' searches all points. Can not compute the upper Delaunay triangulation or upper Voronoi diagram of cocircular/cospherical points.\n");
+ else
+ qh_fprintf(qh, qh->ferr, 6239, "Qhull precision error: Initial simplex is cocircular or cospherical. Use option 'Qz' for the Delaunay triangulation or Voronoi diagram of cocircular/cospherical points. Option 'Qz' adds a point \"at infinity\". Use option 'Qs' to search all points for the initial simplex.\n");
+ qh_errexit(qh, qh_ERRinput, NULL, NULL);
+ }
+ qh_precision(qh, "initial simplex is flat");
+ qh_fprintf(qh, qh->ferr, 6154, "Qhull precision error: Initial simplex is flat (facet %d is coplanar with the interior point)\n",
+ facet->id);
+ qh_errexit(qh, qh_ERRsingular, NULL, NULL); /* calls qh_printhelp_singular */
+ }
+ FOREACHneighbor_(facet) {
+ angle= qh_getangle(qh, facet->normal, neighbor->normal);
+ minimize_( minangle, angle);
+ }
+ }
+ if (minangle < qh_MAXnarrow && !qh->NOnarrow) {
+ realT diff= 1.0 + minangle;
+
+ qh->NARROWhull= True;
+ qh_option(qh, "_narrow-hull", NULL, &diff);
+ if (minangle < qh_WARNnarrow && !qh->RERUN && qh->PRINTprecision)
+ qh_printhelp_narrowhull(qh, qh->ferr, minangle);
+ }
+ zzval_(Zprocessed)= qh->hull_dim+1;
+ qh_checkpolygon(qh, qh->facet_list);
+ qh_checkconvex(qh, qh->facet_list, qh_DATAfault);
+#ifndef qh_NOtrace
+ if (qh->IStracing >= 1) {
+ qh_fprintf(qh, qh->ferr, 8105, "qh_initialhull: simplex constructed, interior point:");
+ for (k=0; k < qh->hull_dim; k++)
+ qh_fprintf(qh, qh->ferr, 8106, " %6.4g", qh->interior_point[k]);
+ qh_fprintf(qh, qh->ferr, 8107, "\n");
+ }
+#endif
+} /* initialhull */
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="initialvertices">-</a>
+
+ qh_initialvertices(qh, dim, maxpoints, points, numpoints )
+ determines a non-singular set of initial vertices
+ maxpoints may include duplicate points
+
+ returns:
+ temporary set of dim+1 vertices in descending order by vertex id
+ if qh.RANDOMoutside && !qh.ALLpoints
+ picks random points
+ if dim >= qh_INITIALmax,
+ uses min/max x and max points with non-zero determinants
+
+ notes:
+ unless qh.ALLpoints,
+ uses maxpoints as long as determinate is non-zero
+*/
+setT *qh_initialvertices(qhT *qh, int dim, setT *maxpoints, pointT *points, int numpoints) {
+ pointT *point, **pointp;
+ setT *vertices, *simplex, *tested;
+ realT randr;
+ int idx, point_i, point_n, k;
+ boolT nearzero= False;
+
+ vertices= qh_settemp(qh, dim + 1);
+ simplex= qh_settemp(qh, dim+1);
+ if (qh->ALLpoints)
+ qh_maxsimplex(qh, dim, NULL, points, numpoints, &simplex);
+ else if (qh->RANDOMoutside) {
+ while (qh_setsize(qh, simplex) != dim+1) {
+ randr= qh_RANDOMint;
+ randr= randr/(qh_RANDOMmax+1);
+ idx= (int)floor(qh->num_points * randr);
+ while (qh_setin(simplex, qh_point(qh, idx))) {
+ idx++; /* in case qh_RANDOMint always returns the same value */
+ idx= idx < qh->num_points ? idx : 0;
+ }
+ qh_setappend(qh, &simplex, qh_point(qh, idx));
+ }
+ }else if (qh->hull_dim >= qh_INITIALmax) {
+ tested= qh_settemp(qh, dim+1);
+ qh_setappend(qh, &simplex, SETfirst_(maxpoints)); /* max and min X coord */
+ qh_setappend(qh, &simplex, SETsecond_(maxpoints));
+ qh_maxsimplex(qh, fmin_(qh_INITIALsearch, dim), maxpoints, points, numpoints, &simplex);
+ k= qh_setsize(qh, simplex);
+ FOREACHpoint_i_(qh, maxpoints) {
+ if (point_i & 0x1) { /* first pick up max. coord. points */
+ if (!qh_setin(simplex, point) && !qh_setin(tested, point)){
+ qh_detsimplex(qh, point, simplex, k, &nearzero);
+ if (nearzero)
+ qh_setappend(qh, &tested, point);
+ else {
+ qh_setappend(qh, &simplex, point);
+ if (++k == dim) /* use search for last point */
+ break;
+ }
+ }
+ }
+ }
+ while (k != dim && (point= (pointT*)qh_setdellast(maxpoints))) {
+ if (!qh_setin(simplex, point) && !qh_setin(tested, point)){
+ qh_detsimplex(qh, point, simplex, k, &nearzero);
+ if (nearzero)
+ qh_setappend(qh, &tested, point);
+ else {
+ qh_setappend(qh, &simplex, point);
+ k++;
+ }
+ }
+ }
+ idx= 0;
+ while (k != dim && (point= qh_point(qh, idx++))) {
+ if (!qh_setin(simplex, point) && !qh_setin(tested, point)){
+ qh_detsimplex(qh, point, simplex, k, &nearzero);
+ if (!nearzero){
+ qh_setappend(qh, &simplex, point);
+ k++;
+ }
+ }
+ }
+ qh_settempfree(qh, &tested);
+ qh_maxsimplex(qh, dim, maxpoints, points, numpoints, &simplex);
+ }else
+ qh_maxsimplex(qh, dim, maxpoints, points, numpoints, &simplex);
+ FOREACHpoint_(simplex)
+ qh_setaddnth(qh, &vertices, 0, qh_newvertex(qh, point)); /* descending order */
+ qh_settempfree(qh, &simplex);
+ return vertices;
+} /* initialvertices */
+
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="isvertex">-</a>
+
+ qh_isvertex( point, vertices )
+ returns vertex if point is in vertex set, else returns NULL
+
+ notes:
+ for qh.GOODvertex
+*/
+vertexT *qh_isvertex(pointT *point, setT *vertices) {
+ vertexT *vertex, **vertexp;
+
+ FOREACHvertex_(vertices) {
+ if (vertex->point == point)
+ return vertex;
+ }
+ return NULL;
+} /* isvertex */
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="makenewfacets">-</a>
+
+ qh_makenewfacets(qh, point )
+ make new facets from point and qh.visible_list
+
+ returns:
+ qh.newfacet_list= list of new facets with hyperplanes and ->newfacet
+ qh.newvertex_list= list of vertices in new facets with ->newlist set
+
+ if (qh.ONLYgood)
+ newfacets reference horizon facets, but not vice versa
+ ridges reference non-simplicial horizon ridges, but not vice versa
+ does not change existing facets
+ else
+ sets qh.NEWfacets
+ new facets attached to horizon facets and ridges
+ for visible facets,
+ visible->r.replace is corresponding new facet
+
+ see also:
+ qh_makenewplanes() -- make hyperplanes for facets
+ qh_attachnewfacets() -- attachnewfacets if not done here(qh->ONLYgood)
+ qh_matchnewfacets() -- match up neighbors
+ qh_updatevertices() -- update vertex neighbors and delvertices
+ qh_deletevisible() -- delete visible facets
+ qh_checkpolygon() --check the result
+ qh_triangulate() -- triangulate a non-simplicial facet
+
+ design:
+ for each visible facet
+ make new facets to its horizon facets
+ update its f.replace
+ clear its neighbor set
+*/
+vertexT *qh_makenewfacets(qhT *qh, pointT *point /*visible_list*/) {
+ facetT *visible, *newfacet= NULL, *newfacet2= NULL, *neighbor, **neighborp;
+ vertexT *apex;
+ int numnew=0;
+
+ qh->newfacet_list= qh->facet_tail;
+ qh->newvertex_list= qh->vertex_tail;
+ apex= qh_newvertex(qh, point);
+ qh_appendvertex(qh, apex);
+ qh->visit_id++;
+ if (!qh->ONLYgood)
+ qh->NEWfacets= True;
+ FORALLvisible_facets {
+ FOREACHneighbor_(visible)
+ neighbor->seen= False;
+ if (visible->ridges) {
+ visible->visitid= qh->visit_id;
+ newfacet2= qh_makenew_nonsimplicial(qh, visible, apex, &numnew);
+ }
+ if (visible->simplicial)
+ newfacet= qh_makenew_simplicial(qh, visible, apex, &numnew);
+ if (!qh->ONLYgood) {
+ if (newfacet2) /* newfacet is null if all ridges defined */
+ newfacet= newfacet2;
+ if (newfacet)
+ visible->f.replace= newfacet;
+ else
+ zinc_(Zinsidevisible);
+ SETfirst_(visible->neighbors)= NULL;
+ }
+ }
+ trace1((qh, qh->ferr, 1032, "qh_makenewfacets: created %d new facets from point p%d to horizon\n",
+ numnew, qh_pointid(qh, point)));
+ if (qh->IStracing >= 4)
+ qh_printfacetlist(qh, qh->newfacet_list, NULL, qh_ALL);
+ return apex;
+} /* makenewfacets */
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="matchduplicates">-</a>
+
+ qh_matchduplicates(qh, atfacet, atskip, hashsize, hashcount )
+ match duplicate ridges in qh.hash_table for atfacet/atskip
+ duplicates marked with ->dupridge and qh_DUPLICATEridge
+
+ returns:
+ picks match with worst merge (min distance apart)
+ updates hashcount
+
+ see also:
+ qh_matchneighbor
+
+ notes:
+
+ design:
+ compute hash value for atfacet and atskip
+ repeat twice -- once to make best matches, once to match the rest
+ for each possible facet in qh.hash_table
+ if it is a matching facet and pass 2
+ make match
+ unless tricoplanar, mark match for merging (qh_MERGEridge)
+ [e.g., tricoplanar RBOX s 1000 t993602376 | QHULL C-1e-3 d Qbb FA Qt]
+ if it is a matching facet and pass 1
+ test if this is a better match
+ if pass 1,
+ make best match (it will not be merged)
+*/
+#ifndef qh_NOmerge
+void qh_matchduplicates(qhT *qh, facetT *atfacet, int atskip, int hashsize, int *hashcount) {
+ boolT same, ismatch;
+ int hash, scan;
+ facetT *facet, *newfacet, *maxmatch= NULL, *maxmatch2= NULL, *nextfacet;
+ int skip, newskip, nextskip= 0, maxskip= 0, maxskip2= 0, makematch;
+ realT maxdist= -REALmax, mindist, dist2, low, high;
+
+ hash= qh_gethash(qh, hashsize, atfacet->vertices, qh->hull_dim, 1,
+ SETelem_(atfacet->vertices, atskip));
+ trace2((qh, qh->ferr, 2046, "qh_matchduplicates: find duplicate matches for f%d skip %d hash %d hashcount %d\n",
+ atfacet->id, atskip, hash, *hashcount));
+ for (makematch= 0; makematch < 2; makematch++) {
+ qh->visit_id++;
+ for (newfacet= atfacet, newskip= atskip; newfacet; newfacet= nextfacet, newskip= nextskip) {
+ zinc_(Zhashlookup);
+ nextfacet= NULL;
+ newfacet->visitid= qh->visit_id;
+ for (scan= hash; (facet= SETelemt_(qh->hash_table, scan, facetT));
+ scan= (++scan >= hashsize ? 0 : scan)) {
+ if (!facet->dupridge || facet->visitid == qh->visit_id)
+ continue;
+ zinc_(Zhashtests);
+ if (qh_matchvertices(qh, 1, newfacet->vertices, newskip, facet->vertices, &skip, &same)) {
+ ismatch= (same == (boolT)(newfacet->toporient ^ facet->toporient));
+ if (SETelemt_(facet->neighbors, skip, facetT) != qh_DUPLICATEridge) {
+ if (!makematch) {
+ qh_fprintf(qh, qh->ferr, 6155, "qhull internal error (qh_matchduplicates): missing dupridge at f%d skip %d for new f%d skip %d hash %d\n",
+ facet->id, skip, newfacet->id, newskip, hash);
+ qh_errexit2(qh, qh_ERRqhull, facet, newfacet);
+ }
+ }else if (ismatch && makematch) {
+ if (SETelemt_(newfacet->neighbors, newskip, facetT) == qh_DUPLICATEridge) {
+ SETelem_(facet->neighbors, skip)= newfacet;
+ if (newfacet->tricoplanar)
+ SETelem_(newfacet->neighbors, newskip)= facet;
+ else
+ SETelem_(newfacet->neighbors, newskip)= qh_MERGEridge;
+ *hashcount -= 2; /* removed two unmatched facets */
+ trace4((qh, qh->ferr, 4059, "qh_matchduplicates: duplicate f%d skip %d matched with new f%d skip %d merge\n",
+ facet->id, skip, newfacet->id, newskip));
+ }
+ }else if (ismatch) {
+ mindist= qh_getdistance(qh, facet, newfacet, &low, &high);
+ dist2= qh_getdistance(qh, newfacet, facet, &low, &high);
+ minimize_(mindist, dist2);
+ if (mindist > maxdist) {
+ maxdist= mindist;
+ maxmatch= facet;
+ maxskip= skip;
+ maxmatch2= newfacet;
+ maxskip2= newskip;
+ }
+ trace3((qh, qh->ferr, 3018, "qh_matchduplicates: duplicate f%d skip %d new f%d skip %d at dist %2.2g, max is now f%d f%d\n",
+ facet->id, skip, newfacet->id, newskip, mindist,
+ maxmatch->id, maxmatch2->id));
+ }else { /* !ismatch */
+ nextfacet= facet;
+ nextskip= skip;
+ }
+ }
+ if (makematch && !facet
+ && SETelemt_(facet->neighbors, skip, facetT) == qh_DUPLICATEridge) {
+ qh_fprintf(qh, qh->ferr, 6156, "qhull internal error (qh_matchduplicates): no MERGEridge match for duplicate f%d skip %d at hash %d\n",
+ newfacet->id, newskip, hash);
+ qh_errexit(qh, qh_ERRqhull, newfacet, NULL);
+ }
+ }
+ } /* end of for each new facet at hash */
+ if (!makematch) {
+ if (!maxmatch) {
+ qh_fprintf(qh, qh->ferr, 6157, "qhull internal error (qh_matchduplicates): no maximum match at duplicate f%d skip %d at hash %d\n",
+ atfacet->id, atskip, hash);
+ qh_errexit(qh, qh_ERRqhull, atfacet, NULL);
+ }
+ SETelem_(maxmatch->neighbors, maxskip)= maxmatch2; /* maxmatch!=0 by QH6157 */
+ SETelem_(maxmatch2->neighbors, maxskip2)= maxmatch;
+ *hashcount -= 2; /* removed two unmatched facets */
+ zzinc_(Zmultiridge);
+ trace0((qh, qh->ferr, 25, "qh_matchduplicates: duplicate f%d skip %d matched with new f%d skip %d keep\n",
+ maxmatch->id, maxskip, maxmatch2->id, maxskip2));
+ qh_precision(qh, "ridge with multiple neighbors");
+ if (qh->IStracing >= 4)
+ qh_errprint(qh, "DUPLICATED/MATCH", maxmatch, maxmatch2, NULL, NULL);
+ }
+ }
+} /* matchduplicates */
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="nearcoplanar">-</a>
+
+ qh_nearcoplanar()
+ for all facets, remove near-inside points from facet->coplanarset</li>
+ coplanar points defined by innerplane from qh_outerinner()
+
+ returns:
+ if qh->KEEPcoplanar && !qh->KEEPinside
+ facet->coplanarset only contains coplanar points
+ if qh.JOGGLEmax
+ drops inner plane by another qh.JOGGLEmax diagonal since a
+ vertex could shift out while a coplanar point shifts in
+
+ notes:
+ used for qh.PREmerge and qh.JOGGLEmax
+ must agree with computation of qh.NEARcoplanar in qh_detroundoff(qh)
+ design:
+ if not keeping coplanar or inside points
+ free all coplanar sets
+ else if not keeping both coplanar and inside points
+ remove !coplanar or !inside points from coplanar sets
+*/
+void qh_nearcoplanar(qhT *qh /* qh.facet_list */) {
+ facetT *facet;
+ pointT *point, **pointp;
+ int numpart;
+ realT dist, innerplane;
+
+ if (!qh->KEEPcoplanar && !qh->KEEPinside) {
+ FORALLfacets {
+ if (facet->coplanarset)
+ qh_setfree(qh, &facet->coplanarset);
+ }
+ }else if (!qh->KEEPcoplanar || !qh->KEEPinside) {
+ qh_outerinner(qh, NULL, NULL, &innerplane);
+ if (qh->JOGGLEmax < REALmax/2)
+ innerplane -= qh->JOGGLEmax * sqrt((realT)qh->hull_dim);
+ numpart= 0;
+ FORALLfacets {
+ if (facet->coplanarset) {
+ FOREACHpoint_(facet->coplanarset) {
+ numpart++;
+ qh_distplane(qh, point, facet, &dist);
+ if (dist < innerplane) {
+ if (!qh->KEEPinside)
+ SETref_(point)= NULL;
+ }else if (!qh->KEEPcoplanar)
+ SETref_(point)= NULL;
+ }
+ qh_setcompact(qh, facet->coplanarset);
+ }
+ }
+ zzadd_(Zcheckpart, numpart);
+ }
+} /* nearcoplanar */
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="nearvertex">-</a>
+
+ qh_nearvertex(qh, facet, point, bestdist )
+ return nearest vertex in facet to point
+
+ returns:
+ vertex and its distance
+
+ notes:
+ if qh.DELAUNAY
+ distance is measured in the input set
+ searches neighboring tricoplanar facets (requires vertexneighbors)
+ Slow implementation. Recomputes vertex set for each point.
+ The vertex set could be stored in the qh.keepcentrum facet.
+*/
+vertexT *qh_nearvertex(qhT *qh, facetT *facet, pointT *point, realT *bestdistp) {
+ realT bestdist= REALmax, dist;
+ vertexT *bestvertex= NULL, *vertex, **vertexp, *apex;
+ coordT *center;
+ facetT *neighbor, **neighborp;
+ setT *vertices;
+ int dim= qh->hull_dim;
+
+ if (qh->DELAUNAY)
+ dim--;
+ if (facet->tricoplanar) {
+ if (!qh->VERTEXneighbors || !facet->center) {
+ qh_fprintf(qh, qh->ferr, 6158, "qhull internal error (qh_nearvertex): qh.VERTEXneighbors and facet->center required for tricoplanar facets\n");
+ qh_errexit(qh, qh_ERRqhull, facet, NULL);
+ }
+ vertices= qh_settemp(qh, qh->TEMPsize);
+ apex= SETfirstt_(facet->vertices, vertexT);
+ center= facet->center;
+ FOREACHneighbor_(apex) {
+ if (neighbor->center == center) {
+ FOREACHvertex_(neighbor->vertices)
+ qh_setappend(qh, &vertices, vertex);
+ }
+ }
+ }else
+ vertices= facet->vertices;
+ FOREACHvertex_(vertices) {
+ dist= qh_pointdist(vertex->point, point, -dim);
+ if (dist < bestdist) {
+ bestdist= dist;
+ bestvertex= vertex;
+ }
+ }
+ if (facet->tricoplanar)
+ qh_settempfree(qh, &vertices);
+ *bestdistp= sqrt(bestdist);
+ if (!bestvertex) {
+ qh_fprintf(qh, qh->ferr, 6261, "qhull internal error (qh_nearvertex): did not find bestvertex for f%d p%d\n", facet->id, qh_pointid(qh, point));
+ qh_errexit(qh, qh_ERRqhull, facet, NULL);
+ }
+ trace3((qh, qh->ferr, 3019, "qh_nearvertex: v%d dist %2.2g for f%d p%d\n",
+ bestvertex->id, *bestdistp, facet->id, qh_pointid(qh, point))); /* bestvertex!=0 by QH2161 */
+ return bestvertex;
+} /* nearvertex */
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="newhashtable">-</a>
+
+ qh_newhashtable(qh, newsize )
+ returns size of qh.hash_table of at least newsize slots
+
+ notes:
+ assumes qh.hash_table is NULL
+ qh_HASHfactor determines the number of extra slots
+ size is not divisible by 2, 3, or 5
+*/
+int qh_newhashtable(qhT *qh, int newsize) {
+ int size;
+
+ size= ((newsize+1)*qh_HASHfactor) | 0x1; /* odd number */
+ while (True) {
+ if (newsize<0 || size<0) {
+ qh_fprintf(qh, qh->qhmem.ferr, 6236, "qhull error (qh_newhashtable): negative request (%d) or size (%d). Did int overflow due to high-D?\n", newsize, size); /* WARN64 */
+ qh_errexit(qh, qhmem_ERRmem, NULL, NULL);
+ }
+ if ((size%3) && (size%5))
+ break;
+ size += 2;
+ /* loop terminates because there is an infinite number of primes */
+ }
+ qh->hash_table= qh_setnew(qh, size);
+ qh_setzero(qh, qh->hash_table, 0, size);
+ return size;
+} /* newhashtable */
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="newvertex">-</a>
+
+ qh_newvertex(qh, point )
+ returns a new vertex for point
+*/
+vertexT *qh_newvertex(qhT *qh, pointT *point) {
+ vertexT *vertex;
+
+ zinc_(Ztotvertices);
+ vertex= (vertexT *)qh_memalloc(qh, (int)sizeof(vertexT));
+ memset((char *) vertex, (size_t)0, sizeof(vertexT));
+ if (qh->vertex_id == UINT_MAX) {
+ qh_memfree(qh, vertex, (int)sizeof(vertexT));
+ qh_fprintf(qh, qh->ferr, 6159, "qhull error: more than 2^32 vertices. vertexT.id field overflows. Vertices would not be sorted correctly.\n");
+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+ }
+ if (qh->vertex_id == qh->tracevertex_id)
+ qh->tracevertex= vertex;
+ vertex->id= qh->vertex_id++;
+ vertex->point= point;
+ trace4((qh, qh->ferr, 4060, "qh_newvertex: vertex p%d(v%d) created\n", qh_pointid(qh, vertex->point),
+ vertex->id));
+ return(vertex);
+} /* newvertex */
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="nextridge3d">-</a>
+
+ qh_nextridge3d( atridge, facet, vertex )
+ return next ridge and vertex for a 3d facet
+ returns NULL on error
+ [for QhullFacet::nextRidge3d] Does not call qh_errexit nor access qhT.
+
+ notes:
+ in qh_ORIENTclock order
+ this is a O(n^2) implementation to trace all ridges
+ be sure to stop on any 2nd visit
+ same as QhullRidge::nextRidge3d
+ does not use qhT or qh_errexit [QhullFacet.cpp]
+
+ design:
+ for each ridge
+ exit if it is the ridge after atridge
+*/
+ridgeT *qh_nextridge3d(ridgeT *atridge, facetT *facet, vertexT **vertexp) {
+ vertexT *atvertex, *vertex, *othervertex;
+ ridgeT *ridge, **ridgep;
+
+ if ((atridge->top == facet) ^ qh_ORIENTclock)
+ atvertex= SETsecondt_(atridge->vertices, vertexT);
+ else
+ atvertex= SETfirstt_(atridge->vertices, vertexT);
+ FOREACHridge_(facet->ridges) {
+ if (ridge == atridge)
+ continue;
+ if ((ridge->top == facet) ^ qh_ORIENTclock) {
+ othervertex= SETsecondt_(ridge->vertices, vertexT);
+ vertex= SETfirstt_(ridge->vertices, vertexT);
+ }else {
+ vertex= SETsecondt_(ridge->vertices, vertexT);
+ othervertex= SETfirstt_(ridge->vertices, vertexT);
+ }
+ if (vertex == atvertex) {
+ if (vertexp)
+ *vertexp= othervertex;
+ return ridge;
+ }
+ }
+ return NULL;
+} /* nextridge3d */
+#else /* qh_NOmerge */
+void qh_matchduplicates(qhT *qh, facetT *atfacet, int atskip, int hashsize, int *hashcount) {
+}
+ridgeT *qh_nextridge3d(ridgeT *atridge, facetT *facet, vertexT **vertexp) {
+
+ return NULL;
+}
+#endif /* qh_NOmerge */
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="outcoplanar">-</a>
+
+ qh_outcoplanar()
+ move points from all facets' outsidesets to their coplanarsets
+
+ notes:
+ for post-processing under qh.NARROWhull
+
+ design:
+ for each facet
+ for each outside point for facet
+ partition point into coplanar set
+*/
+void qh_outcoplanar(qhT *qh /* facet_list */) {
+ pointT *point, **pointp;
+ facetT *facet;
+ realT dist;
+
+ trace1((qh, qh->ferr, 1033, "qh_outcoplanar: move outsideset to coplanarset for qh->NARROWhull\n"));
+ FORALLfacets {
+ FOREACHpoint_(facet->outsideset) {
+ qh->num_outside--;
+ if (qh->KEEPcoplanar || qh->KEEPnearinside) {
+ qh_distplane(qh, point, facet, &dist);
+ zinc_(Zpartition);
+ qh_partitioncoplanar(qh, point, facet, &dist);
+ }
+ }
+ qh_setfree(qh, &facet->outsideset);
+ }
+} /* outcoplanar */
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="point">-</a>
+
+ qh_point(qh, id )
+ return point for a point id, or NULL if unknown
+
+ alternative code:
+ return((pointT *)((unsigned long)qh.first_point
+ + (unsigned long)((id)*qh.normal_size)));
+*/
+pointT *qh_point(qhT *qh, int id) {
+
+ if (id < 0)
+ return NULL;
+ if (id < qh->num_points)
+ return qh->first_point + id * qh->hull_dim;
+ id -= qh->num_points;
+ if (id < qh_setsize(qh, qh->other_points))
+ return SETelemt_(qh->other_points, id, pointT);
+ return NULL;
+} /* point */
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="point_add">-</a>
+
+ qh_point_add(qh, set, point, elem )
+ stores elem at set[point.id]
+
+ returns:
+ access function for qh_pointfacet and qh_pointvertex
+
+ notes:
+ checks point.id
+*/
+void qh_point_add(qhT *qh, setT *set, pointT *point, void *elem) {
+ int id, size;
+
+ SETreturnsize_(set, size);
+ if ((id= qh_pointid(qh, point)) < 0)
+ qh_fprintf(qh, qh->ferr, 7067, "qhull internal warning (point_add): unknown point %p id %d\n",
+ point, id);
+ else if (id >= size) {
+ qh_fprintf(qh, qh->ferr, 6160, "qhull internal errror(point_add): point p%d is out of bounds(%d)\n",
+ id, size);
+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+ }else
+ SETelem_(set, id)= elem;
+} /* point_add */
+
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="pointfacet">-</a>
+
+ qh_pointfacet()
+ return temporary set of facet for each point
+ the set is indexed by point id
+
+ notes:
+ vertices assigned to one of the facets
+ coplanarset assigned to the facet
+ outside set assigned to the facet
+ NULL if no facet for point (inside)
+ includes qh.GOODpointp
+
+ access:
+ FOREACHfacet_i_(qh, facets) { ... }
+ SETelem_(facets, i)
+
+ design:
+ for each facet
+ add each vertex
+ add each coplanar point
+ add each outside point
+*/
+setT *qh_pointfacet(qhT *qh /*qh.facet_list*/) {
+ int numpoints= qh->num_points + qh_setsize(qh, qh->other_points);
+ setT *facets;
+ facetT *facet;
+ vertexT *vertex, **vertexp;
+ pointT *point, **pointp;
+
+ facets= qh_settemp(qh, numpoints);
+ qh_setzero(qh, facets, 0, numpoints);
+ qh->vertex_visit++;
+ FORALLfacets {
+ FOREACHvertex_(facet->vertices) {
+ if (vertex->visitid != qh->vertex_visit) {
+ vertex->visitid= qh->vertex_visit;
+ qh_point_add(qh, facets, vertex->point, facet);
+ }
+ }
+ FOREACHpoint_(facet->coplanarset)
+ qh_point_add(qh, facets, point, facet);
+ FOREACHpoint_(facet->outsideset)
+ qh_point_add(qh, facets, point, facet);
+ }
+ return facets;
+} /* pointfacet */
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="pointvertex">-</a>
+
+ qh_pointvertex(qh, )
+ return temporary set of vertices indexed by point id
+ entry is NULL if no vertex for a point
+ this will include qh.GOODpointp
+
+ access:
+ FOREACHvertex_i_(qh, vertices) { ... }
+ SETelem_(vertices, i)
+*/
+setT *qh_pointvertex(qhT *qh /*qh.facet_list*/) {
+ int numpoints= qh->num_points + qh_setsize(qh, qh->other_points);
+ setT *vertices;
+ vertexT *vertex;
+
+ vertices= qh_settemp(qh, numpoints);
+ qh_setzero(qh, vertices, 0, numpoints);
+ FORALLvertices
+ qh_point_add(qh, vertices, vertex->point, vertex);
+ return vertices;
+} /* pointvertex */
+
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="prependfacet">-</a>
+
+ qh_prependfacet(qh, facet, facetlist )
+ prepend facet to the start of a facetlist
+
+ returns:
+ increments qh.numfacets
+ updates facetlist, qh.facet_list, facet_next
+
+ notes:
+ be careful of prepending since it can lose a pointer.
+ e.g., can lose _next by deleting and then prepending before _next
+*/
+void qh_prependfacet(qhT *qh, facetT *facet, facetT **facetlist) {
+ facetT *prevfacet, *list;
+
+
+ trace4((qh, qh->ferr, 4061, "qh_prependfacet: prepend f%d before f%d\n",
+ facet->id, getid_(*facetlist)));
+ if (!*facetlist)
+ (*facetlist)= qh->facet_tail;
+ list= *facetlist;
+ prevfacet= list->previous;
+ facet->previous= prevfacet;
+ if (prevfacet)
+ prevfacet->next= facet;
+ list->previous= facet;
+ facet->next= *facetlist;
+ if (qh->facet_list == list) /* this may change *facetlist */
+ qh->facet_list= facet;
+ if (qh->facet_next == list)
+ qh->facet_next= facet;
+ *facetlist= facet;
+ qh->num_facets++;
+} /* prependfacet */
+
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="printhashtable">-</a>
+
+ qh_printhashtable(qh, fp )
+ print hash table to fp
+
+ notes:
+ not in I/O to avoid bringing io_r.c in
+
+ design:
+ for each hash entry
+ if defined
+ if unmatched or will merge (NULL, qh_MERGEridge, qh_DUPLICATEridge)
+ print entry and neighbors
+*/
+void qh_printhashtable(qhT *qh, FILE *fp) {
+ facetT *facet, *neighbor;
+ int id, facet_i, facet_n, neighbor_i= 0, neighbor_n= 0;
+ vertexT *vertex, **vertexp;
+
+ FOREACHfacet_i_(qh, qh->hash_table) {
+ if (facet) {
+ FOREACHneighbor_i_(qh, facet) {
+ if (!neighbor || neighbor == qh_MERGEridge || neighbor == qh_DUPLICATEridge)
+ break;
+ }
+ if (neighbor_i == neighbor_n)
+ continue;
+ qh_fprintf(qh, fp, 9283, "hash %d f%d ", facet_i, facet->id);
+ FOREACHvertex_(facet->vertices)
+ qh_fprintf(qh, fp, 9284, "v%d ", vertex->id);
+ qh_fprintf(qh, fp, 9285, "\n neighbors:");
+ FOREACHneighbor_i_(qh, facet) {
+ if (neighbor == qh_MERGEridge)
+ id= -3;
+ else if (neighbor == qh_DUPLICATEridge)
+ id= -2;
+ else
+ id= getid_(neighbor);
+ qh_fprintf(qh, fp, 9286, " %d", id);
+ }
+ qh_fprintf(qh, fp, 9287, "\n");
+ }
+ }
+} /* printhashtable */
+
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="printlists">-</a>
+
+ qh_printlists(qh, fp )
+ print out facet and vertex list for debugging (without 'f/v' tags)
+*/
+void qh_printlists(qhT *qh) {
+ facetT *facet;
+ vertexT *vertex;
+ int count= 0;
+
+ qh_fprintf(qh, qh->ferr, 8108, "qh_printlists: facets:");
+ FORALLfacets {
+ if (++count % 100 == 0)
+ qh_fprintf(qh, qh->ferr, 8109, "\n ");
+ qh_fprintf(qh, qh->ferr, 8110, " %d", facet->id);
+ }
+ qh_fprintf(qh, qh->ferr, 8111, "\n new facets %d visible facets %d next facet for qh_addpoint %d\n vertices(new %d):",
+ getid_(qh->newfacet_list), getid_(qh->visible_list), getid_(qh->facet_next),
+ getid_(qh->newvertex_list));
+ count = 0;
+ FORALLvertices {
+ if (++count % 100 == 0)
+ qh_fprintf(qh, qh->ferr, 8112, "\n ");
+ qh_fprintf(qh, qh->ferr, 8113, " %d", vertex->id);
+ }
+ qh_fprintf(qh, qh->ferr, 8114, "\n");
+} /* printlists */
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="resetlists">-</a>
+
+ qh_resetlists(qh, stats, qh_RESETvisible )
+ reset newvertex_list, newfacet_list, visible_list
+ if stats,
+ maintains statistics
+
+ returns:
+ visible_list is empty if qh_deletevisible was called
+*/
+void qh_resetlists(qhT *qh, boolT stats, boolT resetVisible /*qh.newvertex_list newfacet_list visible_list*/) {
+ vertexT *vertex;
+ facetT *newfacet, *visible;
+ int totnew=0, totver=0;
+
+ if (stats) {
+ FORALLvertex_(qh->newvertex_list)
+ totver++;
+ FORALLnew_facets
+ totnew++;
+ zadd_(Zvisvertextot, totver);
+ zmax_(Zvisvertexmax, totver);
+ zadd_(Znewfacettot, totnew);
+ zmax_(Znewfacetmax, totnew);
+ }
+ FORALLvertex_(qh->newvertex_list)
+ vertex->newlist= False;
+ qh->newvertex_list= NULL;
+ FORALLnew_facets
+ newfacet->newfacet= False;
+ qh->newfacet_list= NULL;
+ if (resetVisible) {
+ FORALLvisible_facets {
+ visible->f.replace= NULL;
+ visible->visible= False;
+ }
+ qh->num_visible= 0;
+ }
+ qh->visible_list= NULL; /* may still have visible facets via qh_triangulate */
+ qh->NEWfacets= False;
+} /* resetlists */
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="setvoronoi_all">-</a>
+
+ qh_setvoronoi_all(qh)
+ compute Voronoi centers for all facets
+ includes upperDelaunay facets if qh.UPPERdelaunay ('Qu')
+
+ returns:
+ facet->center is the Voronoi center
+
+ notes:
+ this is unused/untested code
+ please email bradb@shore.net if this works ok for you
+
+ use:
+ FORALLvertices {...} to locate the vertex for a point.
+ FOREACHneighbor_(vertex) {...} to visit the Voronoi centers for a Voronoi cell.
+*/
+void qh_setvoronoi_all(qhT *qh) {
+ facetT *facet;
+
+ qh_clearcenters(qh, qh_ASvoronoi);
+ qh_vertexneighbors(qh);
+
+ FORALLfacets {
+ if (!facet->normal || !facet->upperdelaunay || qh->UPPERdelaunay) {
+ if (!facet->center)
+ facet->center= qh_facetcenter(qh, facet->vertices);
+ }
+ }
+} /* setvoronoi_all */
+
+#ifndef qh_NOmerge
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="triangulate">-</a>
+
+ qh_triangulate()
+ triangulate non-simplicial facets on qh.facet_list,
+ if qh->VORONOI, sets Voronoi centers of non-simplicial facets
+ nop if hasTriangulation
+
+ returns:
+ all facets simplicial
+ each tricoplanar facet has ->f.triowner == owner of ->center,normal,etc.
+
+ notes:
+ call after qh_check_output since may switch to Voronoi centers
+ Output may overwrite ->f.triowner with ->f.area
+*/
+void qh_triangulate(qhT *qh /*qh.facet_list*/) {
+ facetT *facet, *nextfacet, *owner;
+ int onlygood= qh->ONLYgood;
+ facetT *neighbor, *visible= NULL, *facet1, *facet2, *new_facet_list= NULL;
+ facetT *orig_neighbor= NULL, *otherfacet;
+ vertexT *new_vertex_list= NULL;
+ mergeT *merge;
+ mergeType mergetype;
+ int neighbor_i, neighbor_n;
+
+ if (qh->hasTriangulation)
+ return;
+ trace1((qh, qh->ferr, 1034, "qh_triangulate: triangulate non-simplicial facets\n"));
+ if (qh->hull_dim == 2)
+ return;
+ if (qh->VORONOI) { /* otherwise lose Voronoi centers [could rebuild vertex set from tricoplanar] */
+ qh_clearcenters(qh, qh_ASvoronoi);
+ qh_vertexneighbors(qh);
+ }
+ qh->ONLYgood= False; /* for makenew_nonsimplicial */
+ qh->visit_id++;
+ qh->NEWfacets= True;
+ qh->degen_mergeset= qh_settemp(qh, qh->TEMPsize);
+ qh->newvertex_list= qh->vertex_tail;
+ for (facet= qh->facet_list; facet && facet->next; facet= nextfacet) { /* non-simplicial facets moved to end */
+ nextfacet= facet->next;
+ if (facet->visible || facet->simplicial)
+ continue;
+ /* triangulate all non-simplicial facets, otherwise merging does not work, e.g., RBOX c P-0.1 P+0.1 P+0.1 D3 | QHULL d Qt Tv */
+ if (!new_facet_list)
+ new_facet_list= facet; /* will be moved to end */
+ qh_triangulate_facet(qh, facet, &new_vertex_list);
+ }
+ trace2((qh, qh->ferr, 2047, "qh_triangulate: delete null facets from f%d -- apex same as second vertex\n", getid_(new_facet_list)));
+ for (facet= new_facet_list; facet && facet->next; facet= nextfacet) { /* null facets moved to end */
+ nextfacet= facet->next;
+ if (facet->visible)
+ continue;
+ if (facet->ridges) {
+ if (qh_setsize(qh, facet->ridges) > 0) {
+ qh_fprintf(qh, qh->ferr, 6161, "qhull error (qh_triangulate): ridges still defined for f%d\n", facet->id);
+ qh_errexit(qh, qh_ERRqhull, facet, NULL);
+ }
+ qh_setfree(qh, &facet->ridges);
+ }
+ if (SETfirst_(facet->vertices) == SETsecond_(facet->vertices)) {
+ zinc_(Ztrinull);
+ qh_triangulate_null(qh, facet);
+ }
+ }
+ trace2((qh, qh->ferr, 2048, "qh_triangulate: delete %d or more mirror facets -- same vertices and neighbors\n", qh_setsize(qh, qh->degen_mergeset)));
+ qh->visible_list= qh->facet_tail;
+ while ((merge= (mergeT*)qh_setdellast(qh->degen_mergeset))) {
+ facet1= merge->facet1;
+ facet2= merge->facet2;
+ mergetype= merge->type;
+ qh_memfree(qh, merge, (int)sizeof(mergeT));
+ if (mergetype == MRGmirror) {
+ zinc_(Ztrimirror);
+ qh_triangulate_mirror(qh, facet1, facet2);
+ }
+ }
+ qh_settempfree(qh, &qh->degen_mergeset);
+ trace2((qh, qh->ferr, 2049, "qh_triangulate: update neighbor lists for vertices from v%d\n", getid_(new_vertex_list)));
+ qh->newvertex_list= new_vertex_list; /* all vertices of new facets */
+ qh->visible_list= NULL;
+ qh_updatevertices(qh /*qh.newvertex_list, empty newfacet_list and visible_list*/);
+ qh_resetlists(qh, False, !qh_RESETvisible /*qh.newvertex_list, empty newfacet_list and visible_list*/);
+
+ trace2((qh, qh->ferr, 2050, "qh_triangulate: identify degenerate tricoplanar facets from f%d\n", getid_(new_facet_list)));
+ trace2((qh, qh->ferr, 2051, "qh_triangulate: and replace facet->f.triowner with tricoplanar facets that own center, normal, etc.\n"));
+ FORALLfacet_(new_facet_list) {
+ if (facet->tricoplanar && !facet->visible) {
+ FOREACHneighbor_i_(qh, facet) {
+ if (neighbor_i == 0) { /* first iteration */
+ if (neighbor->tricoplanar)
+ orig_neighbor= neighbor->f.triowner;
+ else
+ orig_neighbor= neighbor;
+ }else {
+ if (neighbor->tricoplanar)
+ otherfacet= neighbor->f.triowner;
+ else
+ otherfacet= neighbor;
+ if (orig_neighbor == otherfacet) {
+ zinc_(Ztridegen);
+ facet->degenerate= True;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ trace2((qh, qh->ferr, 2052, "qh_triangulate: delete visible facets -- non-simplicial, null, and mirrored facets\n"));
+ owner= NULL;
+ visible= NULL;
+ for (facet= new_facet_list; facet && facet->next; facet= nextfacet) { /* may delete facet */
+ nextfacet= facet->next;
+ if (facet->visible) {
+ if (facet->tricoplanar) { /* a null or mirrored facet */
+ qh_delfacet(qh, facet);
+ qh->num_visible--;
+ }else { /* a non-simplicial facet followed by its tricoplanars */
+ if (visible && !owner) {
+ /* RBOX 200 s D5 t1001471447 | QHULL Qt C-0.01 Qx Qc Tv Qt -- f4483 had 6 vertices/neighbors and 8 ridges */
+ trace2((qh, qh->ferr, 2053, "qh_triangulate: all tricoplanar facets degenerate for non-simplicial facet f%d\n",
+ visible->id));
+ qh_delfacet(qh, visible);
+ qh->num_visible--;
+ }
+ visible= facet;
+ owner= NULL;
+ }
+ }else if (facet->tricoplanar) {
+ if (facet->f.triowner != visible || visible==NULL) {
+ qh_fprintf(qh, qh->ferr, 6162, "qhull error (qh_triangulate): tricoplanar facet f%d not owned by its visible, non-simplicial facet f%d\n", facet->id, getid_(visible));
+ qh_errexit2(qh, qh_ERRqhull, facet, visible);
+ }
+ if (owner)
+ facet->f.triowner= owner;
+ else if (!facet->degenerate) {
+ owner= facet;
+ nextfacet= visible->next; /* rescan tricoplanar facets with owner, visible!=0 by QH6162 */
+ facet->keepcentrum= True; /* one facet owns ->normal, etc. */
+ facet->coplanarset= visible->coplanarset;
+ facet->outsideset= visible->outsideset;
+ visible->coplanarset= NULL;
+ visible->outsideset= NULL;
+ if (!qh->TRInormals) { /* center and normal copied to tricoplanar facets */
+ visible->center= NULL;
+ visible->normal= NULL;
+ }
+ qh_delfacet(qh, visible);
+ qh->num_visible--;
+ }
+ }
+ }
+ if (visible && !owner) {
+ trace2((qh, qh->ferr, 2054, "qh_triangulate: all tricoplanar facets degenerate for last non-simplicial facet f%d\n",
+ visible->id));
+ qh_delfacet(qh, visible);
+ qh->num_visible--;
+ }
+ qh->NEWfacets= False;
+ qh->ONLYgood= onlygood; /* restore value */
+ if (qh->CHECKfrequently)
+ qh_checkpolygon(qh, qh->facet_list);
+ qh->hasTriangulation= True;
+} /* triangulate */
+
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="triangulate_facet">-</a>
+
+ qh_triangulate_facet(qh, facetA, &firstVertex )
+ triangulate a non-simplicial facet
+ if qh.CENTERtype=qh_ASvoronoi, sets its Voronoi center
+ returns:
+ qh.newfacet_list == simplicial facets
+ facet->tricoplanar set and ->keepcentrum false
+ facet->degenerate set if duplicated apex
+ facet->f.trivisible set to facetA
+ facet->center copied from facetA (created if qh_ASvoronoi)
+ qh_eachvoronoi, qh_detvridge, qh_detvridge3 assume centers copied
+ facet->normal,offset,maxoutside copied from facetA
+
+ notes:
+ only called by qh_triangulate
+ qh_makenew_nonsimplicial uses neighbor->seen for the same
+ if qh.TRInormals, newfacet->normal will need qh_free
+ if qh.TRInormals and qh_AScentrum, newfacet->center will need qh_free
+ keepcentrum is also set on Zwidefacet in qh_mergefacet
+ freed by qh_clearcenters
+
+ see also:
+ qh_addpoint() -- add a point
+ qh_makenewfacets() -- construct a cone of facets for a new vertex
+
+ design:
+ if qh_ASvoronoi,
+ compute Voronoi center (facet->center)
+ select first vertex (highest ID to preserve ID ordering of ->vertices)
+ triangulate from vertex to ridges
+ copy facet->center, normal, offset
+ update vertex neighbors
+*/
+void qh_triangulate_facet(qhT *qh, facetT *facetA, vertexT **first_vertex) {
+ facetT *newfacet;
+ facetT *neighbor, **neighborp;
+ vertexT *apex;
+ int numnew=0;
+
+ trace3((qh, qh->ferr, 3020, "qh_triangulate_facet: triangulate facet f%d\n", facetA->id));
+
+ if (qh->IStracing >= 4)
+ qh_printfacet(qh, qh->ferr, facetA);
+ FOREACHneighbor_(facetA) {
+ neighbor->seen= False;
+ neighbor->coplanar= False;
+ }
+ if (qh->CENTERtype == qh_ASvoronoi && !facetA->center /* matches upperdelaunay in qh_setfacetplane() */
+ && fabs_(facetA->normal[qh->hull_dim -1]) >= qh->ANGLEround * qh_ZEROdelaunay) {
+ facetA->center= qh_facetcenter(qh, facetA->vertices);
+ }
+ qh_willdelete(qh, facetA, NULL);
+ qh->newfacet_list= qh->facet_tail;
+ facetA->visitid= qh->visit_id;
+ apex= SETfirstt_(facetA->vertices, vertexT);
+ qh_makenew_nonsimplicial(qh, facetA, apex, &numnew);
+ SETfirst_(facetA->neighbors)= NULL;
+ FORALLnew_facets {
+ newfacet->tricoplanar= True;
+ newfacet->f.trivisible= facetA;
+ newfacet->degenerate= False;
+ newfacet->upperdelaunay= facetA->upperdelaunay;
+ newfacet->good= facetA->good;
+ if (qh->TRInormals) { /* 'Q11' triangulate duplicates ->normal and ->center */
+ newfacet->keepcentrum= True;
+ if(facetA->normal){
+ newfacet->normal= qh_memalloc(qh, qh->normal_size);
+ memcpy((char *)newfacet->normal, facetA->normal, qh->normal_size);
+ }
+ if (qh->CENTERtype == qh_AScentrum)
+ newfacet->center= qh_getcentrum(qh, newfacet);
+ else if (qh->CENTERtype == qh_ASvoronoi && facetA->center){
+ newfacet->center= qh_memalloc(qh, qh->center_size);
+ memcpy((char *)newfacet->center, facetA->center, qh->center_size);
+ }
+ }else {
+ newfacet->keepcentrum= False;
+ /* one facet will have keepcentrum=True at end of qh_triangulate */
+ newfacet->normal= facetA->normal;
+ newfacet->center= facetA->center;
+ }
+ newfacet->offset= facetA->offset;
+#if qh_MAXoutside
+ newfacet->maxoutside= facetA->maxoutside;
+#endif
+ }
+ qh_matchnewfacets(qh /*qh.newfacet_list*/);
+ zinc_(Ztricoplanar);
+ zadd_(Ztricoplanartot, numnew);
+ zmax_(Ztricoplanarmax, numnew);
+ qh->visible_list= NULL;
+ if (!(*first_vertex))
+ (*first_vertex)= qh->newvertex_list;
+ qh->newvertex_list= NULL;
+ qh_updatevertices(qh /*qh.newfacet_list, qh.empty visible_list and qh.newvertex_list*/);
+ qh_resetlists(qh, False, !qh_RESETvisible /*qh.newfacet_list, qh.empty visible_list and qh.newvertex_list*/);
+} /* triangulate_facet */
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="triangulate_link">-</a>
+
+ qh_triangulate_link(qh, oldfacetA, facetA, oldfacetB, facetB)
+ relink facetA to facetB via oldfacets
+ returns:
+ adds mirror facets to qh->degen_mergeset (4-d and up only)
+ design:
+ if they are already neighbors, the opposing neighbors become MRGmirror facets
+*/
+void qh_triangulate_link(qhT *qh, facetT *oldfacetA, facetT *facetA, facetT *oldfacetB, facetT *facetB) {
+ int errmirror= False;
+
+ trace3((qh, qh->ferr, 3021, "qh_triangulate_link: relink old facets f%d and f%d between neighbors f%d and f%d\n",
+ oldfacetA->id, oldfacetB->id, facetA->id, facetB->id));
+ if (qh_setin(facetA->neighbors, facetB)) {
+ if (!qh_setin(facetB->neighbors, facetA))
+ errmirror= True;
+ else
+ qh_appendmergeset(qh, facetA, facetB, MRGmirror, NULL);
+ }else if (qh_setin(facetB->neighbors, facetA))
+ errmirror= True;
+ if (errmirror) {
+ qh_fprintf(qh, qh->ferr, 6163, "qhull error (qh_triangulate_link): mirror facets f%d and f%d do not match for old facets f%d and f%d\n",
+ facetA->id, facetB->id, oldfacetA->id, oldfacetB->id);
+ qh_errexit2(qh, qh_ERRqhull, facetA, facetB);
+ }
+ qh_setreplace(qh, facetB->neighbors, oldfacetB, facetA);
+ qh_setreplace(qh, facetA->neighbors, oldfacetA, facetB);
+} /* triangulate_link */
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="triangulate_mirror">-</a>
+
+ qh_triangulate_mirror(qh, facetA, facetB)
+ delete mirrored facets from qh_triangulate_null() and qh_triangulate_mirror
+ a mirrored facet shares the same vertices of a logical ridge
+ design:
+ since a null facet duplicates the first two vertices, the opposing neighbors absorb the null facet
+ if they are already neighbors, the opposing neighbors become MRGmirror facets
+*/
+void qh_triangulate_mirror(qhT *qh, facetT *facetA, facetT *facetB) {
+ facetT *neighbor, *neighborB;
+ int neighbor_i, neighbor_n;
+
+ trace3((qh, qh->ferr, 3022, "qh_triangulate_mirror: delete mirrored facets f%d and f%d\n",
+ facetA->id, facetB->id));
+ FOREACHneighbor_i_(qh, facetA) {
+ neighborB= SETelemt_(facetB->neighbors, neighbor_i, facetT);
+ if (neighbor == neighborB)
+ continue; /* occurs twice */
+ qh_triangulate_link(qh, facetA, neighbor, facetB, neighborB);
+ }
+ qh_willdelete(qh, facetA, NULL);
+ qh_willdelete(qh, facetB, NULL);
+} /* triangulate_mirror */
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="triangulate_null">-</a>
+
+ qh_triangulate_null(qh, facetA)
+ remove null facetA from qh_triangulate_facet()
+ a null facet has vertex #1 (apex) == vertex #2
+ returns:
+ adds facetA to ->visible for deletion after qh_updatevertices
+ qh->degen_mergeset contains mirror facets (4-d and up only)
+ design:
+ since a null facet duplicates the first two vertices, the opposing neighbors absorb the null facet
+ if they are already neighbors, the opposing neighbors become MRGmirror facets
+*/
+void qh_triangulate_null(qhT *qh, facetT *facetA) {
+ facetT *neighbor, *otherfacet;
+
+ trace3((qh, qh->ferr, 3023, "qh_triangulate_null: delete null facet f%d\n", facetA->id));
+ neighbor= SETfirstt_(facetA->neighbors, facetT);
+ otherfacet= SETsecondt_(facetA->neighbors, facetT);
+ qh_triangulate_link(qh, facetA, neighbor, facetA, otherfacet);
+ qh_willdelete(qh, facetA, NULL);
+} /* triangulate_null */
+
+#else /* qh_NOmerge */
+void qh_triangulate(qhT *qh) {
+}
+#endif /* qh_NOmerge */
+
+ /*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="vertexintersect">-</a>
+
+ qh_vertexintersect(qh, vertexsetA, vertexsetB )
+ intersects two vertex sets (inverse id ordered)
+ vertexsetA is a temporary set at the top of qh->qhmem.tempstack
+
+ returns:
+ replaces vertexsetA with the intersection
+
+ notes:
+ could overwrite vertexsetA if currently too slow
+*/
+void qh_vertexintersect(qhT *qh, setT **vertexsetA,setT *vertexsetB) {
+ setT *intersection;
+
+ intersection= qh_vertexintersect_new(qh, *vertexsetA, vertexsetB);
+ qh_settempfree(qh, vertexsetA);
+ *vertexsetA= intersection;
+ qh_settemppush(qh, intersection);
+} /* vertexintersect */
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="vertexintersect_new">-</a>
+
+ qh_vertexintersect_new(qh, )
+ intersects two vertex sets (inverse id ordered)
+
+ returns:
+ a new set
+*/
+setT *qh_vertexintersect_new(qhT *qh, setT *vertexsetA,setT *vertexsetB) {
+ setT *intersection= qh_setnew(qh, qh->hull_dim - 1);
+ vertexT **vertexA= SETaddr_(vertexsetA, vertexT);
+ vertexT **vertexB= SETaddr_(vertexsetB, vertexT);
+
+ while (*vertexA && *vertexB) {
+ if (*vertexA == *vertexB) {
+ qh_setappend(qh, &intersection, *vertexA);
+ vertexA++; vertexB++;
+ }else {
+ if ((*vertexA)->id > (*vertexB)->id)
+ vertexA++;
+ else
+ vertexB++;
+ }
+ }
+ return intersection;
+} /* vertexintersect_new */
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="vertexneighbors">-</a>
+
+ qh_vertexneighbors(qh)
+ for each vertex in qh.facet_list,
+ determine its neighboring facets
+
+ returns:
+ sets qh.VERTEXneighbors
+ nop if qh.VERTEXneighbors already set
+ qh_addpoint() will maintain them
+
+ notes:
+ assumes all vertex->neighbors are NULL
+
+ design:
+ for each facet
+ for each vertex
+ append facet to vertex->neighbors
+*/
+void qh_vertexneighbors(qhT *qh /*qh.facet_list*/) {
+ facetT *facet;
+ vertexT *vertex, **vertexp;
+
+ if (qh->VERTEXneighbors)
+ return;
+ trace1((qh, qh->ferr, 1035, "qh_vertexneighbors: determining neighboring facets for each vertex\n"));
+ qh->vertex_visit++;
+ FORALLfacets {
+ if (facet->visible)
+ continue;
+ FOREACHvertex_(facet->vertices) {
+ if (vertex->visitid != qh->vertex_visit) {
+ vertex->visitid= qh->vertex_visit;
+ vertex->neighbors= qh_setnew(qh, qh->hull_dim);
+ }
+ qh_setappend(qh, &vertex->neighbors, facet);
+ }
+ }
+ qh->VERTEXneighbors= True;
+} /* vertexneighbors */
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="vertexsubset">-</a>
+
+ qh_vertexsubset( vertexsetA, vertexsetB )
+ returns True if vertexsetA is a subset of vertexsetB
+ assumes vertexsets are sorted
+
+ note:
+ empty set is a subset of any other set
+*/
+boolT qh_vertexsubset(setT *vertexsetA, setT *vertexsetB) {
+ vertexT **vertexA= (vertexT **) SETaddr_(vertexsetA, vertexT);
+ vertexT **vertexB= (vertexT **) SETaddr_(vertexsetB, vertexT);
+
+ while (True) {
+ if (!*vertexA)
+ return True;
+ if (!*vertexB)
+ return False;
+ if ((*vertexA)->id > (*vertexB)->id)
+ return False;
+ if (*vertexA == *vertexB)
+ vertexA++;
+ vertexB++;
+ }
+ return False; /* avoid warnings */
+} /* vertexsubset */
diff --git a/xs/src/qhull/src/libqhull_r/poly_r.c b/xs/src/qhull/src/libqhull_r/poly_r.c
new file mode 100644
index 000000000..e5b479743
--- /dev/null
+++ b/xs/src/qhull/src/libqhull_r/poly_r.c
@@ -0,0 +1,1205 @@
+/*<html><pre> -<a href="qh-poly_r.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ poly_r.c
+ implements polygons and simplices
+
+ see qh-poly_r.htm, poly_r.h and libqhull_r.h
+
+ infrequent code is in poly2_r.c
+ (all but top 50 and their callers 12/3/95)
+
+ Copyright (c) 1993-2015 The Geometry Center.
+ $Id: //main/2015/qhull/src/libqhull_r/poly_r.c#3 $$Change: 2064 $
+ $DateTime: 2016/01/18 12:36:08 $$Author: bbarber $
+*/
+
+#include "qhull_ra.h"
+
+/*======== functions in alphabetical order ==========*/
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="appendfacet">-</a>
+
+ qh_appendfacet(qh, facet )
+ appends facet to end of qh.facet_list,
+
+ returns:
+ updates qh.newfacet_list, facet_next, facet_list
+ increments qh.numfacets
+
+ notes:
+ assumes qh.facet_list/facet_tail is defined (createsimplex)
+
+ see:
+ qh_removefacet()
+
+*/
+void qh_appendfacet(qhT *qh, facetT *facet) {
+ facetT *tail= qh->facet_tail;
+
+ if (tail == qh->newfacet_list)
+ qh->newfacet_list= facet;
+ if (tail == qh->facet_next)
+ qh->facet_next= facet;
+ facet->previous= tail->previous;
+ facet->next= tail;
+ if (tail->previous)
+ tail->previous->next= facet;
+ else
+ qh->facet_list= facet;
+ tail->previous= facet;
+ qh->num_facets++;
+ trace4((qh, qh->ferr, 4044, "qh_appendfacet: append f%d to facet_list\n", facet->id));
+} /* appendfacet */
+
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="appendvertex">-</a>
+
+ qh_appendvertex(qh, vertex )
+ appends vertex to end of qh.vertex_list,
+
+ returns:
+ sets vertex->newlist
+ updates qh.vertex_list, newvertex_list
+ increments qh.num_vertices
+
+ notes:
+ assumes qh.vertex_list/vertex_tail is defined (createsimplex)
+
+*/
+void qh_appendvertex(qhT *qh, vertexT *vertex) {
+ vertexT *tail= qh->vertex_tail;
+
+ if (tail == qh->newvertex_list)
+ qh->newvertex_list= vertex;
+ vertex->newlist= True;
+ vertex->previous= tail->previous;
+ vertex->next= tail;
+ if (tail->previous)
+ tail->previous->next= vertex;
+ else
+ qh->vertex_list= vertex;
+ tail->previous= vertex;
+ qh->num_vertices++;
+ trace4((qh, qh->ferr, 4045, "qh_appendvertex: append v%d to vertex_list\n", vertex->id));
+} /* appendvertex */
+
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="attachnewfacets">-</a>
+
+ qh_attachnewfacets(qh, )
+ attach horizon facets to new facets in qh.newfacet_list
+ newfacets have neighbor and ridge links to horizon but not vice versa
+ only needed for qh.ONLYgood
+
+ returns:
+ set qh.NEWfacets
+ horizon facets linked to new facets
+ ridges changed from visible facets to new facets
+ simplicial ridges deleted
+ qh.visible_list, no ridges valid
+ facet->f.replace is a newfacet (if any)
+
+ design:
+ delete interior ridges and neighbor sets by
+ for each visible, non-simplicial facet
+ for each ridge
+ if last visit or if neighbor is simplicial
+ if horizon neighbor
+ delete ridge for horizon's ridge set
+ delete ridge
+ erase neighbor set
+ attach horizon facets and new facets by
+ for all new facets
+ if corresponding horizon facet is simplicial
+ locate corresponding visible facet {may be more than one}
+ link visible facet to new facet
+ replace visible facet with new facet in horizon
+ else it's non-simplicial
+ for all visible neighbors of the horizon facet
+ link visible neighbor to new facet
+ delete visible neighbor from horizon facet
+ append new facet to horizon's neighbors
+ the first ridge of the new facet is the horizon ridge
+ link the new facet into the horizon ridge
+*/
+void qh_attachnewfacets(qhT *qh /* qh.visible_list, newfacet_list */) {
+ facetT *newfacet= NULL, *neighbor, **neighborp, *horizon, *visible;
+ ridgeT *ridge, **ridgep;
+
+ qh->NEWfacets= True;
+ trace3((qh, qh->ferr, 3012, "qh_attachnewfacets: delete interior ridges\n"));
+ qh->visit_id++;
+ FORALLvisible_facets {
+ visible->visitid= qh->visit_id;
+ if (visible->ridges) {
+ FOREACHridge_(visible->ridges) {
+ neighbor= otherfacet_(ridge, visible);
+ if (neighbor->visitid == qh->visit_id
+ || (!neighbor->visible && neighbor->simplicial)) {
+ if (!neighbor->visible) /* delete ridge for simplicial horizon */
+ qh_setdel(neighbor->ridges, ridge);
+ qh_setfree(qh, &(ridge->vertices)); /* delete on 2nd visit */
+ qh_memfree(qh, ridge, (int)sizeof(ridgeT));
+ }
+ }
+ SETfirst_(visible->ridges)= NULL;
+ }
+ SETfirst_(visible->neighbors)= NULL;
+ }
+ trace1((qh, qh->ferr, 1017, "qh_attachnewfacets: attach horizon facets to new facets\n"));
+ FORALLnew_facets {
+ horizon= SETfirstt_(newfacet->neighbors, facetT);
+ if (horizon->simplicial) {
+ visible= NULL;
+ FOREACHneighbor_(horizon) { /* may have more than one horizon ridge */
+ if (neighbor->visible) {
+ if (visible) {
+ if (qh_setequal_skip(newfacet->vertices, 0, horizon->vertices,
+ SETindex_(horizon->neighbors, neighbor))) {
+ visible= neighbor;
+ break;
+ }
+ }else
+ visible= neighbor;
+ }
+ }
+ if (visible) {
+ visible->f.replace= newfacet;
+ qh_setreplace(qh, horizon->neighbors, visible, newfacet);
+ }else {
+ qh_fprintf(qh, qh->ferr, 6102, "qhull internal error (qh_attachnewfacets): couldn't find visible facet for horizon f%d of newfacet f%d\n",
+ horizon->id, newfacet->id);
+ qh_errexit2(qh, qh_ERRqhull, horizon, newfacet);
+ }
+ }else { /* non-simplicial, with a ridge for newfacet */
+ FOREACHneighbor_(horizon) { /* may hold for many new facets */
+ if (neighbor->visible) {
+ neighbor->f.replace= newfacet;
+ qh_setdelnth(qh, horizon->neighbors,
+ SETindex_(horizon->neighbors, neighbor));
+ neighborp--; /* repeat */
+ }
+ }
+ qh_setappend(qh, &horizon->neighbors, newfacet);
+ ridge= SETfirstt_(newfacet->ridges, ridgeT);
+ if (ridge->top == horizon)
+ ridge->bottom= newfacet;
+ else
+ ridge->top= newfacet;
+ }
+ } /* newfacets */
+ if (qh->PRINTstatistics) {
+ FORALLvisible_facets {
+ if (!visible->f.replace)
+ zinc_(Zinsidevisible);
+ }
+ }
+} /* attachnewfacets */
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="checkflipped">-</a>
+
+ qh_checkflipped(qh, facet, dist, allerror )
+ checks facet orientation to interior point
+
+ if allerror set,
+ tests against qh.DISTround
+ else
+ tests against 0 since tested against DISTround before
+
+ returns:
+ False if it flipped orientation (sets facet->flipped)
+ distance if non-NULL
+*/
+boolT qh_checkflipped(qhT *qh, facetT *facet, realT *distp, boolT allerror) {
+ realT dist;
+
+ if (facet->flipped && !distp)
+ return False;
+ zzinc_(Zdistcheck);
+ qh_distplane(qh, qh->interior_point, facet, &dist);
+ if (distp)
+ *distp= dist;
+ if ((allerror && dist > -qh->DISTround)|| (!allerror && dist >= 0.0)) {
+ facet->flipped= True;
+ zzinc_(Zflippedfacets);
+ trace0((qh, qh->ferr, 19, "qh_checkflipped: facet f%d is flipped, distance= %6.12g during p%d\n",
+ facet->id, dist, qh->furthest_id));
+ qh_precision(qh, "flipped facet");
+ return False;
+ }
+ return True;
+} /* checkflipped */
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="delfacet">-</a>
+
+ qh_delfacet(qh, facet )
+ removes facet from facet_list and frees up its memory
+
+ notes:
+ assumes vertices and ridges already freed
+*/
+void qh_delfacet(qhT *qh, facetT *facet) {
+ void **freelistp; /* used if !qh_NOmem by qh_memfree_() */
+
+ trace4((qh, qh->ferr, 4046, "qh_delfacet: delete f%d\n", facet->id));
+ if (facet == qh->tracefacet)
+ qh->tracefacet= NULL;
+ if (facet == qh->GOODclosest)
+ qh->GOODclosest= NULL;
+ qh_removefacet(qh, facet);
+ if (!facet->tricoplanar || facet->keepcentrum) {
+ qh_memfree_(qh, facet->normal, qh->normal_size, freelistp);
+ if (qh->CENTERtype == qh_ASvoronoi) { /* braces for macro calls */
+ qh_memfree_(qh, facet->center, qh->center_size, freelistp);
+ }else /* AScentrum */ {
+ qh_memfree_(qh, facet->center, qh->normal_size, freelistp);
+ }
+ }
+ qh_setfree(qh, &(facet->neighbors));
+ if (facet->ridges)
+ qh_setfree(qh, &(facet->ridges));
+ qh_setfree(qh, &(facet->vertices));
+ if (facet->outsideset)
+ qh_setfree(qh, &(facet->outsideset));
+ if (facet->coplanarset)
+ qh_setfree(qh, &(facet->coplanarset));
+ qh_memfree_(qh, facet, (int)sizeof(facetT), freelistp);
+} /* delfacet */
+
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="deletevisible">-</a>
+
+ qh_deletevisible()
+ delete visible facets and vertices
+
+ returns:
+ deletes each facet and removes from facetlist
+ at exit, qh.visible_list empty (== qh.newfacet_list)
+
+ notes:
+ ridges already deleted
+ horizon facets do not reference facets on qh.visible_list
+ new facets in qh.newfacet_list
+ uses qh.visit_id;
+*/
+void qh_deletevisible(qhT *qh /*qh.visible_list*/) {
+ facetT *visible, *nextfacet;
+ vertexT *vertex, **vertexp;
+ int numvisible= 0, numdel= qh_setsize(qh, qh->del_vertices);
+
+ trace1((qh, qh->ferr, 1018, "qh_deletevisible: delete %d visible facets and %d vertices\n",
+ qh->num_visible, numdel));
+ for (visible= qh->visible_list; visible && visible->visible;
+ visible= nextfacet) { /* deleting current */
+ nextfacet= visible->next;
+ numvisible++;
+ qh_delfacet(qh, visible);
+ }
+ if (numvisible != qh->num_visible) {
+ qh_fprintf(qh, qh->ferr, 6103, "qhull internal error (qh_deletevisible): qh->num_visible %d is not number of visible facets %d\n",
+ qh->num_visible, numvisible);
+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+ }
+ qh->num_visible= 0;
+ zadd_(Zvisfacettot, numvisible);
+ zmax_(Zvisfacetmax, numvisible);
+ zzadd_(Zdelvertextot, numdel);
+ zmax_(Zdelvertexmax, numdel);
+ FOREACHvertex_(qh->del_vertices)
+ qh_delvertex(qh, vertex);
+ qh_settruncate(qh, qh->del_vertices, 0);
+} /* deletevisible */
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="facetintersect">-</a>
+
+ qh_facetintersect(qh, facetA, facetB, skipa, skipB, prepend )
+ return vertices for intersection of two simplicial facets
+ may include 1 prepended entry (if more, need to settemppush)
+
+ returns:
+ returns set of qh.hull_dim-1 + prepend vertices
+ returns skipped index for each test and checks for exactly one
+
+ notes:
+ does not need settemp since set in quick memory
+
+ see also:
+ qh_vertexintersect and qh_vertexintersect_new
+ use qh_setnew_delnthsorted to get nth ridge (no skip information)
+
+ design:
+ locate skipped vertex by scanning facet A's neighbors
+ locate skipped vertex by scanning facet B's neighbors
+ intersect the vertex sets
+*/
+setT *qh_facetintersect(qhT *qh, facetT *facetA, facetT *facetB,
+ int *skipA,int *skipB, int prepend) {
+ setT *intersect;
+ int dim= qh->hull_dim, i, j;
+ facetT **neighborsA, **neighborsB;
+
+ neighborsA= SETaddr_(facetA->neighbors, facetT);
+ neighborsB= SETaddr_(facetB->neighbors, facetT);
+ i= j= 0;
+ if (facetB == *neighborsA++)
+ *skipA= 0;
+ else if (facetB == *neighborsA++)
+ *skipA= 1;
+ else if (facetB == *neighborsA++)
+ *skipA= 2;
+ else {
+ for (i=3; i < dim; i++) {
+ if (facetB == *neighborsA++) {
+ *skipA= i;
+ break;
+ }
+ }
+ }
+ if (facetA == *neighborsB++)
+ *skipB= 0;
+ else if (facetA == *neighborsB++)
+ *skipB= 1;
+ else if (facetA == *neighborsB++)
+ *skipB= 2;
+ else {
+ for (j=3; j < dim; j++) {
+ if (facetA == *neighborsB++) {
+ *skipB= j;
+ break;
+ }
+ }
+ }
+ if (i >= dim || j >= dim) {
+ qh_fprintf(qh, qh->ferr, 6104, "qhull internal error (qh_facetintersect): f%d or f%d not in others neighbors\n",
+ facetA->id, facetB->id);
+ qh_errexit2(qh, qh_ERRqhull, facetA, facetB);
+ }
+ intersect= qh_setnew_delnthsorted(qh, facetA->vertices, qh->hull_dim, *skipA, prepend);
+ trace4((qh, qh->ferr, 4047, "qh_facetintersect: f%d skip %d matches f%d skip %d\n",
+ facetA->id, *skipA, facetB->id, *skipB));
+ return(intersect);
+} /* facetintersect */
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="gethash">-</a>
+
+ qh_gethash(qh, hashsize, set, size, firstindex, skipelem )
+ return hashvalue for a set with firstindex and skipelem
+
+ notes:
+ returned hash is in [0,hashsize)
+ assumes at least firstindex+1 elements
+ assumes skipelem is NULL, in set, or part of hash
+
+ hashes memory addresses which may change over different runs of the same data
+ using sum for hash does badly in high d
+*/
+int qh_gethash(qhT *qh, int hashsize, setT *set, int size, int firstindex, void *skipelem) {
+ void **elemp= SETelemaddr_(set, firstindex, void);
+ ptr_intT hash = 0, elem;
+ unsigned result;
+ int i;
+#ifdef _MSC_VER /* Microsoft Visual C++ -- warn about 64-bit issues */
+#pragma warning( push) /* WARN64 -- ptr_intT holds a 64-bit pointer */
+#pragma warning( disable : 4311) /* 'type cast': pointer truncation from 'void*' to 'ptr_intT' */
+#endif
+
+ switch (size-firstindex) {
+ case 1:
+ hash= (ptr_intT)(*elemp) - (ptr_intT) skipelem;
+ break;
+ case 2:
+ hash= (ptr_intT)(*elemp) + (ptr_intT)elemp[1] - (ptr_intT) skipelem;
+ break;
+ case 3:
+ hash= (ptr_intT)(*elemp) + (ptr_intT)elemp[1] + (ptr_intT)elemp[2]
+ - (ptr_intT) skipelem;
+ break;
+ case 4:
+ hash= (ptr_intT)(*elemp) + (ptr_intT)elemp[1] + (ptr_intT)elemp[2]
+ + (ptr_intT)elemp[3] - (ptr_intT) skipelem;
+ break;
+ case 5:
+ hash= (ptr_intT)(*elemp) + (ptr_intT)elemp[1] + (ptr_intT)elemp[2]
+ + (ptr_intT)elemp[3] + (ptr_intT)elemp[4] - (ptr_intT) skipelem;
+ break;
+ case 6:
+ hash= (ptr_intT)(*elemp) + (ptr_intT)elemp[1] + (ptr_intT)elemp[2]
+ + (ptr_intT)elemp[3] + (ptr_intT)elemp[4]+ (ptr_intT)elemp[5]
+ - (ptr_intT) skipelem;
+ break;
+ default:
+ hash= 0;
+ i= 3;
+ do { /* this is about 10% in 10-d */
+ if ((elem= (ptr_intT)*elemp++) != (ptr_intT)skipelem) {
+ hash ^= (elem << i) + (elem >> (32-i));
+ i += 3;
+ if (i >= 32)
+ i -= 32;
+ }
+ }while (*elemp);
+ break;
+ }
+ if (hashsize<0) {
+ qh_fprintf(qh, qh->ferr, 6202, "qhull internal error: negative hashsize %d passed to qh_gethash [poly.c]\n", hashsize);
+ qh_errexit2(qh, qh_ERRqhull, NULL, NULL);
+ }
+ result= (unsigned)hash;
+ result %= (unsigned)hashsize;
+ /* result= 0; for debugging */
+ return result;
+#ifdef _MSC_VER
+#pragma warning( pop)
+#endif
+} /* gethash */
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="makenewfacet">-</a>
+
+ qh_makenewfacet(qh, vertices, toporient, horizon )
+ creates a toporient? facet from vertices
+
+ returns:
+ returns newfacet
+ adds newfacet to qh.facet_list
+ newfacet->vertices= vertices
+ if horizon
+ newfacet->neighbor= horizon, but not vice versa
+ newvertex_list updated with vertices
+*/
+facetT *qh_makenewfacet(qhT *qh, setT *vertices, boolT toporient,facetT *horizon) {
+ facetT *newfacet;
+ vertexT *vertex, **vertexp;
+
+ FOREACHvertex_(vertices) {
+ if (!vertex->newlist) {
+ qh_removevertex(qh, vertex);
+ qh_appendvertex(qh, vertex);
+ }
+ }
+ newfacet= qh_newfacet(qh);
+ newfacet->vertices= vertices;
+ newfacet->toporient= (unsigned char)toporient;
+ if (horizon)
+ qh_setappend(qh, &(newfacet->neighbors), horizon);
+ qh_appendfacet(qh, newfacet);
+ return(newfacet);
+} /* makenewfacet */
+
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="makenewplanes">-</a>
+
+ qh_makenewplanes()
+ make new hyperplanes for facets on qh.newfacet_list
+
+ returns:
+ all facets have hyperplanes or are marked for merging
+ doesn't create hyperplane if horizon is coplanar (will merge)
+ updates qh.min_vertex if qh.JOGGLEmax
+
+ notes:
+ facet->f.samecycle is defined for facet->mergehorizon facets
+*/
+void qh_makenewplanes(qhT *qh /* qh.newfacet_list */) {
+ facetT *newfacet;
+
+ FORALLnew_facets {
+ if (!newfacet->mergehorizon)
+ qh_setfacetplane(qh, newfacet);
+ }
+ if (qh->JOGGLEmax < REALmax/2)
+ minimize_(qh->min_vertex, -wwval_(Wnewvertexmax));
+} /* makenewplanes */
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="makenew_nonsimplicial">-</a>
+
+ qh_makenew_nonsimplicial(qh, visible, apex, numnew )
+ make new facets for ridges of a visible facet
+
+ returns:
+ first newfacet, bumps numnew as needed
+ attaches new facets if !qh.ONLYgood
+ marks ridge neighbors for simplicial visible
+ if (qh.ONLYgood)
+ ridges on newfacet, horizon, and visible
+ else
+ ridge and neighbors between newfacet and horizon
+ visible facet's ridges are deleted
+
+ notes:
+ qh.visit_id if visible has already been processed
+ sets neighbor->seen for building f.samecycle
+ assumes all 'seen' flags initially false
+
+ design:
+ for each ridge of visible facet
+ get neighbor of visible facet
+ if neighbor was already processed
+ delete the ridge (will delete all visible facets later)
+ if neighbor is a horizon facet
+ create a new facet
+ if neighbor coplanar
+ adds newfacet to f.samecycle for later merging
+ else
+ updates neighbor's neighbor set
+ (checks for non-simplicial facet with multiple ridges to visible facet)
+ updates neighbor's ridge set
+ (checks for simplicial neighbor to non-simplicial visible facet)
+ (deletes ridge if neighbor is simplicial)
+
+*/
+#ifndef qh_NOmerge
+facetT *qh_makenew_nonsimplicial(qhT *qh, facetT *visible, vertexT *apex, int *numnew) {
+ void **freelistp; /* used if !qh_NOmem by qh_memfree_() */
+ ridgeT *ridge, **ridgep;
+ facetT *neighbor, *newfacet= NULL, *samecycle;
+ setT *vertices;
+ boolT toporient;
+ int ridgeid;
+
+ FOREACHridge_(visible->ridges) {
+ ridgeid= ridge->id;
+ neighbor= otherfacet_(ridge, visible);
+ if (neighbor->visible) {
+ if (!qh->ONLYgood) {
+ if (neighbor->visitid == qh->visit_id) {
+ qh_setfree(qh, &(ridge->vertices)); /* delete on 2nd visit */
+ qh_memfree_(qh, ridge, (int)sizeof(ridgeT), freelistp);
+ }
+ }
+ }else { /* neighbor is an horizon facet */
+ toporient= (ridge->top == visible);
+ vertices= qh_setnew(qh, qh->hull_dim); /* makes sure this is quick */
+ qh_setappend(qh, &vertices, apex);
+ qh_setappend_set(qh, &vertices, ridge->vertices);
+ newfacet= qh_makenewfacet(qh, vertices, toporient, neighbor);
+ (*numnew)++;
+ if (neighbor->coplanar) {
+ newfacet->mergehorizon= True;
+ if (!neighbor->seen) {
+ newfacet->f.samecycle= newfacet;
+ neighbor->f.newcycle= newfacet;
+ }else {
+ samecycle= neighbor->f.newcycle;
+ newfacet->f.samecycle= samecycle->f.samecycle;
+ samecycle->f.samecycle= newfacet;
+ }
+ }
+ if (qh->ONLYgood) {
+ if (!neighbor->simplicial)
+ qh_setappend(qh, &(newfacet->ridges), ridge);
+ }else { /* qh_attachnewfacets */
+ if (neighbor->seen) {
+ if (neighbor->simplicial) {
+ qh_fprintf(qh, qh->ferr, 6105, "qhull internal error (qh_makenew_nonsimplicial): simplicial f%d sharing two ridges with f%d\n",
+ neighbor->id, visible->id);
+ qh_errexit2(qh, qh_ERRqhull, neighbor, visible);
+ }
+ qh_setappend(qh, &(neighbor->neighbors), newfacet);
+ }else
+ qh_setreplace(qh, neighbor->neighbors, visible, newfacet);
+ if (neighbor->simplicial) {
+ qh_setdel(neighbor->ridges, ridge);
+ qh_setfree(qh, &(ridge->vertices));
+ qh_memfree(qh, ridge, (int)sizeof(ridgeT));
+ }else {
+ qh_setappend(qh, &(newfacet->ridges), ridge);
+ if (toporient)
+ ridge->top= newfacet;
+ else
+ ridge->bottom= newfacet;
+ }
+ trace4((qh, qh->ferr, 4048, "qh_makenew_nonsimplicial: created facet f%d from v%d and r%d of horizon f%d\n",
+ newfacet->id, apex->id, ridgeid, neighbor->id));
+ }
+ }
+ neighbor->seen= True;
+ } /* for each ridge */
+ if (!qh->ONLYgood)
+ SETfirst_(visible->ridges)= NULL;
+ return newfacet;
+} /* makenew_nonsimplicial */
+#else /* qh_NOmerge */
+facetT *qh_makenew_nonsimplicial(qhT *qh, facetT *visible, vertexT *apex, int *numnew) {
+ return NULL;
+}
+#endif /* qh_NOmerge */
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="makenew_simplicial">-</a>
+
+ qh_makenew_simplicial(qh, visible, apex, numnew )
+ make new facets for simplicial visible facet and apex
+
+ returns:
+ attaches new facets if (!qh.ONLYgood)
+ neighbors between newfacet and horizon
+
+ notes:
+ nop if neighbor->seen or neighbor->visible(see qh_makenew_nonsimplicial)
+
+ design:
+ locate neighboring horizon facet for visible facet
+ determine vertices and orientation
+ create new facet
+ if coplanar,
+ add new facet to f.samecycle
+ update horizon facet's neighbor list
+*/
+facetT *qh_makenew_simplicial(qhT *qh, facetT *visible, vertexT *apex, int *numnew) {
+ facetT *neighbor, **neighborp, *newfacet= NULL;
+ setT *vertices;
+ boolT flip, toporient;
+ int horizonskip= 0, visibleskip= 0;
+
+ FOREACHneighbor_(visible) {
+ if (!neighbor->seen && !neighbor->visible) {
+ vertices= qh_facetintersect(qh, neighbor,visible, &horizonskip, &visibleskip, 1);
+ SETfirst_(vertices)= apex;
+ flip= ((horizonskip & 0x1) ^ (visibleskip & 0x1));
+ if (neighbor->toporient)
+ toporient= horizonskip & 0x1;
+ else
+ toporient= (horizonskip & 0x1) ^ 0x1;
+ newfacet= qh_makenewfacet(qh, vertices, toporient, neighbor);
+ (*numnew)++;
+ if (neighbor->coplanar && (qh->PREmerge || qh->MERGEexact)) {
+#ifndef qh_NOmerge
+ newfacet->f.samecycle= newfacet;
+ newfacet->mergehorizon= True;
+#endif
+ }
+ if (!qh->ONLYgood)
+ SETelem_(neighbor->neighbors, horizonskip)= newfacet;
+ trace4((qh, qh->ferr, 4049, "qh_makenew_simplicial: create facet f%d top %d from v%d and horizon f%d skip %d top %d and visible f%d skip %d, flip? %d\n",
+ newfacet->id, toporient, apex->id, neighbor->id, horizonskip,
+ neighbor->toporient, visible->id, visibleskip, flip));
+ }
+ }
+ return newfacet;
+} /* makenew_simplicial */
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="matchneighbor">-</a>
+
+ qh_matchneighbor(qh, newfacet, newskip, hashsize, hashcount )
+ either match subridge of newfacet with neighbor or add to hash_table
+
+ returns:
+ duplicate ridges are unmatched and marked by qh_DUPLICATEridge
+
+ notes:
+ ridge is newfacet->vertices w/o newskip vertex
+ do not allocate memory (need to free hash_table cleanly)
+ uses linear hash chains
+
+ see also:
+ qh_matchduplicates
+
+ design:
+ for each possible matching facet in qh.hash_table
+ if vertices match
+ set ismatch, if facets have opposite orientation
+ if ismatch and matching facet doesn't have a match
+ match the facets by updating their neighbor sets
+ else
+ indicate a duplicate ridge
+ set facet hyperplane for later testing
+ add facet to hashtable
+ unless the other facet was already a duplicate ridge
+ mark both facets with a duplicate ridge
+ add other facet (if defined) to hash table
+*/
+void qh_matchneighbor(qhT *qh, facetT *newfacet, int newskip, int hashsize, int *hashcount) {
+ boolT newfound= False; /* True, if new facet is already in hash chain */
+ boolT same, ismatch;
+ int hash, scan;
+ facetT *facet, *matchfacet;
+ int skip, matchskip;
+
+ hash= qh_gethash(qh, hashsize, newfacet->vertices, qh->hull_dim, 1,
+ SETelem_(newfacet->vertices, newskip));
+ trace4((qh, qh->ferr, 4050, "qh_matchneighbor: newfacet f%d skip %d hash %d hashcount %d\n",
+ newfacet->id, newskip, hash, *hashcount));
+ zinc_(Zhashlookup);
+ for (scan= hash; (facet= SETelemt_(qh->hash_table, scan, facetT));
+ scan= (++scan >= hashsize ? 0 : scan)) {
+ if (facet == newfacet) {
+ newfound= True;
+ continue;
+ }
+ zinc_(Zhashtests);
+ if (qh_matchvertices(qh, 1, newfacet->vertices, newskip, facet->vertices, &skip, &same)) {
+ if (SETelem_(newfacet->vertices, newskip) ==
+ SETelem_(facet->vertices, skip)) {
+ qh_precision(qh, "two facets with the same vertices");
+ qh_fprintf(qh, qh->ferr, 6106, "qhull precision error: Vertex sets are the same for f%d and f%d. Can not force output.\n",
+ facet->id, newfacet->id);
+ qh_errexit2(qh, qh_ERRprec, facet, newfacet);
+ }
+ ismatch= (same == (boolT)((newfacet->toporient ^ facet->toporient)));
+ matchfacet= SETelemt_(facet->neighbors, skip, facetT);
+ if (ismatch && !matchfacet) {
+ SETelem_(facet->neighbors, skip)= newfacet;
+ SETelem_(newfacet->neighbors, newskip)= facet;
+ (*hashcount)--;
+ trace4((qh, qh->ferr, 4051, "qh_matchneighbor: f%d skip %d matched with new f%d skip %d\n",
+ facet->id, skip, newfacet->id, newskip));
+ return;
+ }
+ if (!qh->PREmerge && !qh->MERGEexact) {
+ qh_precision(qh, "a ridge with more than two neighbors");
+ qh_fprintf(qh, qh->ferr, 6107, "qhull precision error: facets f%d, f%d and f%d meet at a ridge with more than 2 neighbors. Can not continue.\n",
+ facet->id, newfacet->id, getid_(matchfacet));
+ qh_errexit2(qh, qh_ERRprec, facet, newfacet);
+ }
+ SETelem_(newfacet->neighbors, newskip)= qh_DUPLICATEridge;
+ newfacet->dupridge= True;
+ if (!newfacet->normal)
+ qh_setfacetplane(qh, newfacet);
+ qh_addhash(newfacet, qh->hash_table, hashsize, hash);
+ (*hashcount)++;
+ if (!facet->normal)
+ qh_setfacetplane(qh, facet);
+ if (matchfacet != qh_DUPLICATEridge) {
+ SETelem_(facet->neighbors, skip)= qh_DUPLICATEridge;
+ facet->dupridge= True;
+ if (!facet->normal)
+ qh_setfacetplane(qh, facet);
+ if (matchfacet) {
+ matchskip= qh_setindex(matchfacet->neighbors, facet);
+ if (matchskip<0) {
+ qh_fprintf(qh, qh->ferr, 6260, "qhull internal error (qh_matchneighbor): matchfacet f%d is in f%d neighbors but not vice versa. Can not continue.\n",
+ matchfacet->id, facet->id);
+ qh_errexit2(qh, qh_ERRqhull, matchfacet, facet);
+ }
+ SETelem_(matchfacet->neighbors, matchskip)= qh_DUPLICATEridge; /* matchskip>=0 by QH6260 */
+ matchfacet->dupridge= True;
+ if (!matchfacet->normal)
+ qh_setfacetplane(qh, matchfacet);
+ qh_addhash(matchfacet, qh->hash_table, hashsize, hash);
+ *hashcount += 2;
+ }
+ }
+ trace4((qh, qh->ferr, 4052, "qh_matchneighbor: new f%d skip %d duplicates ridge for f%d skip %d matching f%d ismatch %d at hash %d\n",
+ newfacet->id, newskip, facet->id, skip,
+ (matchfacet == qh_DUPLICATEridge ? -2 : getid_(matchfacet)),
+ ismatch, hash));
+ return; /* end of duplicate ridge */
+ }
+ }
+ if (!newfound)
+ SETelem_(qh->hash_table, scan)= newfacet; /* same as qh_addhash */
+ (*hashcount)++;
+ trace4((qh, qh->ferr, 4053, "qh_matchneighbor: no match for f%d skip %d at hash %d\n",
+ newfacet->id, newskip, hash));
+} /* matchneighbor */
+
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="matchnewfacets">-</a>
+
+ qh_matchnewfacets()
+ match newfacets in qh.newfacet_list to their newfacet neighbors
+
+ returns:
+ qh.newfacet_list with full neighbor sets
+ get vertices with nth neighbor by deleting nth vertex
+ if qh.PREmerge/MERGEexact or qh.FORCEoutput
+ sets facet->flippped if flipped normal (also prevents point partitioning)
+ if duplicate ridges and qh.PREmerge/MERGEexact
+ sets facet->dupridge
+ missing neighbor links identifies extra ridges to be merging (qh_MERGEridge)
+
+ notes:
+ newfacets already have neighbor[0] (horizon facet)
+ assumes qh.hash_table is NULL
+ vertex->neighbors has not been updated yet
+ do not allocate memory after qh.hash_table (need to free it cleanly)
+
+ design:
+ delete neighbor sets for all new facets
+ initialize a hash table
+ for all new facets
+ match facet with neighbors
+ if unmatched facets (due to duplicate ridges)
+ for each new facet with a duplicate ridge
+ match it with a facet
+ check for flipped facets
+*/
+void qh_matchnewfacets(qhT *qh /* qh.newfacet_list */) {
+ int numnew=0, hashcount=0, newskip;
+ facetT *newfacet, *neighbor;
+ int dim= qh->hull_dim, hashsize, neighbor_i, neighbor_n;
+ setT *neighbors;
+#ifndef qh_NOtrace
+ int facet_i, facet_n, numfree= 0;
+ facetT *facet;
+#endif
+
+ trace1((qh, qh->ferr, 1019, "qh_matchnewfacets: match neighbors for new facets.\n"));
+ FORALLnew_facets {
+ numnew++;
+ { /* inline qh_setzero(qh, newfacet->neighbors, 1, qh->hull_dim); */
+ neighbors= newfacet->neighbors;
+ neighbors->e[neighbors->maxsize].i= dim+1; /*may be overwritten*/
+ memset((char *)SETelemaddr_(neighbors, 1, void), 0, dim * SETelemsize);
+ }
+ }
+
+ qh_newhashtable(qh, numnew*(qh->hull_dim-1)); /* twice what is normally needed,
+ but every ridge could be DUPLICATEridge */
+ hashsize= qh_setsize(qh, qh->hash_table);
+ FORALLnew_facets {
+ for (newskip=1; newskip<qh->hull_dim; newskip++) /* furthest/horizon already matched */
+ /* hashsize>0 because hull_dim>1 and numnew>0 */
+ qh_matchneighbor(qh, newfacet, newskip, hashsize, &hashcount);
+#if 0 /* use the following to trap hashcount errors */
+ {
+ int count= 0, k;
+ facetT *facet, *neighbor;
+
+ count= 0;
+ FORALLfacet_(qh->newfacet_list) { /* newfacet already in use */
+ for (k=1; k < qh->hull_dim; k++) {
+ neighbor= SETelemt_(facet->neighbors, k, facetT);
+ if (!neighbor || neighbor == qh_DUPLICATEridge)
+ count++;
+ }
+ if (facet == newfacet)
+ break;
+ }
+ if (count != hashcount) {
+ qh_fprintf(qh, qh->ferr, 8088, "qh_matchnewfacets: after adding facet %d, hashcount %d != count %d\n",
+ newfacet->id, hashcount, count);
+ qh_errexit(qh, qh_ERRqhull, newfacet, NULL);
+ }
+ }
+#endif /* end of trap code */
+ }
+ if (hashcount) {
+ FORALLnew_facets {
+ if (newfacet->dupridge) {
+ FOREACHneighbor_i_(qh, newfacet) {
+ if (neighbor == qh_DUPLICATEridge) {
+ qh_matchduplicates(qh, newfacet, neighbor_i, hashsize, &hashcount);
+ /* this may report MERGEfacet */
+ }
+ }
+ }
+ }
+ }
+ if (hashcount) {
+ qh_fprintf(qh, qh->ferr, 6108, "qhull internal error (qh_matchnewfacets): %d neighbors did not match up\n",
+ hashcount);
+ qh_printhashtable(qh, qh->ferr);
+ qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+ }
+#ifndef qh_NOtrace
+ if (qh->IStracing >= 2) {
+ FOREACHfacet_i_(qh, qh->hash_table) {
+ if (!facet)
+ numfree++;
+ }
+ qh_fprintf(qh, qh->ferr, 8089, "qh_matchnewfacets: %d new facets, %d unused hash entries . hashsize %d\n",
+ numnew, numfree, qh_setsize(qh, qh->hash_table));
+ }
+#endif /* !qh_NOtrace */
+ qh_setfree(qh, &qh->hash_table);
+ if (qh->PREmerge || qh->MERGEexact) {
+ if (qh->IStracing >= 4)
+ qh_printfacetlist(qh, qh->newfacet_list, NULL, qh_ALL);
+ FORALLnew_facets {
+ if (newfacet->normal)
+ qh_checkflipped(qh, newfacet, NULL, qh_ALL);
+ }
+ }else if (qh->FORCEoutput)
+ qh_checkflipped_all(qh, qh->newfacet_list); /* prints warnings for flipped */
+} /* matchnewfacets */
+
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="matchvertices">-</a>
+
+ qh_matchvertices(qh, firstindex, verticesA, skipA, verticesB, skipB, same )
+ tests whether vertices match with a single skip
+ starts match at firstindex since all new facets have a common vertex
+
+ returns:
+ true if matched vertices
+ skip index for each set
+ sets same iff vertices have the same orientation
+
+ notes:
+ assumes skipA is in A and both sets are the same size
+
+ design:
+ set up pointers
+ scan both sets checking for a match
+ test orientation
+*/
+boolT qh_matchvertices(qhT *qh, int firstindex, setT *verticesA, int skipA,
+ setT *verticesB, int *skipB, boolT *same) {
+ vertexT **elemAp, **elemBp, **skipBp=NULL, **skipAp;
+
+ elemAp= SETelemaddr_(verticesA, firstindex, vertexT);
+ elemBp= SETelemaddr_(verticesB, firstindex, vertexT);
+ skipAp= SETelemaddr_(verticesA, skipA, vertexT);
+ do if (elemAp != skipAp) {
+ while (*elemAp != *elemBp++) {
+ if (skipBp)
+ return False;
+ skipBp= elemBp; /* one extra like FOREACH */
+ }
+ }while (*(++elemAp));
+ if (!skipBp)
+ skipBp= ++elemBp;
+ *skipB= SETindex_(verticesB, skipB); /* i.e., skipBp - verticesB */
+ *same= !((skipA & 0x1) ^ (*skipB & 0x1)); /* result is 0 or 1 */
+ trace4((qh, qh->ferr, 4054, "qh_matchvertices: matched by skip %d(v%d) and skip %d(v%d) same? %d\n",
+ skipA, (*skipAp)->id, *skipB, (*(skipBp-1))->id, *same));
+ return(True);
+} /* matchvertices */
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="newfacet">-</a>
+
+ qh_newfacet(qh)
+ return a new facet
+
+ returns:
+ all fields initialized or cleared (NULL)
+ preallocates neighbors set
+*/
+facetT *qh_newfacet(qhT *qh) {
+ facetT *facet;
+ void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */
+
+ qh_memalloc_(qh, (int)sizeof(facetT), freelistp, facet, facetT);
+ memset((char *)facet, (size_t)0, sizeof(facetT));
+ if (qh->facet_id == qh->tracefacet_id)
+ qh->tracefacet= facet;
+ facet->id= qh->facet_id++;
+ facet->neighbors= qh_setnew(qh, qh->hull_dim);
+#if !qh_COMPUTEfurthest
+ facet->furthestdist= 0.0;
+#endif
+#if qh_MAXoutside
+ if (qh->FORCEoutput && qh->APPROXhull)
+ facet->maxoutside= qh->MINoutside;
+ else
+ facet->maxoutside= qh->DISTround;
+#endif
+ facet->simplicial= True;
+ facet->good= True;
+ facet->newfacet= True;
+ trace4((qh, qh->ferr, 4055, "qh_newfacet: created facet f%d\n", facet->id));
+ return(facet);
+} /* newfacet */
+
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="newridge">-</a>
+
+ qh_newridge()
+ return a new ridge
+*/
+ridgeT *qh_newridge(qhT *qh) {
+ ridgeT *ridge;
+ void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */
+
+ qh_memalloc_(qh, (int)sizeof(ridgeT), freelistp, ridge, ridgeT);
+ memset((char *)ridge, (size_t)0, sizeof(ridgeT));
+ zinc_(Ztotridges);
+ if (qh->ridge_id == UINT_MAX) {
+ qh_fprintf(qh, qh->ferr, 7074, "\
+qhull warning: more than 2^32 ridges. Qhull results are OK. Since the ridge ID wraps around to 0, two ridges may have the same identifier.\n");
+ }
+ ridge->id= qh->ridge_id++;
+ trace4((qh, qh->ferr, 4056, "qh_newridge: created ridge r%d\n", ridge->id));
+ return(ridge);
+} /* newridge */
+
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="pointid">-</a>
+
+ qh_pointid(qh, point )
+ return id for a point,
+ returns qh_IDnone(-3) if null, qh_IDinterior(-2) if interior, or qh_IDunknown(-1) if not known
+
+ alternative code if point is in qh.first_point...
+ unsigned long id;
+ id= ((unsigned long)point - (unsigned long)qh.first_point)/qh.normal_size;
+
+ notes:
+ Valid points are non-negative
+ WARN64 -- id truncated to 32-bits, at most 2G points
+ NOerrors returned (QhullPoint::id)
+ if point not in point array
+ the code does a comparison of unrelated pointers.
+*/
+int qh_pointid(qhT *qh, pointT *point) {
+ ptr_intT offset, id;
+
+ if (!point || !qh)
+ return qh_IDnone;
+ else if (point == qh->interior_point)
+ return qh_IDinterior;
+ else if (point >= qh->first_point
+ && point < qh->first_point + qh->num_points * qh->hull_dim) {
+ offset= (ptr_intT)(point - qh->first_point);
+ id= offset / qh->hull_dim;
+ }else if ((id= qh_setindex(qh->other_points, point)) != -1)
+ id += qh->num_points;
+ else
+ return qh_IDunknown;
+ return (int)id;
+} /* pointid */
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="removefacet">-</a>
+
+ qh_removefacet(qh, facet )
+ unlinks facet from qh.facet_list,
+
+ returns:
+ updates qh.facet_list .newfacet_list .facet_next visible_list
+ decrements qh.num_facets
+
+ see:
+ qh_appendfacet
+*/
+void qh_removefacet(qhT *qh, facetT *facet) {
+ facetT *next= facet->next, *previous= facet->previous;
+
+ if (facet == qh->newfacet_list)
+ qh->newfacet_list= next;
+ if (facet == qh->facet_next)
+ qh->facet_next= next;
+ if (facet == qh->visible_list)
+ qh->visible_list= next;
+ if (previous) {
+ previous->next= next;
+ next->previous= previous;
+ }else { /* 1st facet in qh->facet_list */
+ qh->facet_list= next;
+ qh->facet_list->previous= NULL;
+ }
+ qh->num_facets--;
+ trace4((qh, qh->ferr, 4057, "qh_removefacet: remove f%d from facet_list\n", facet->id));
+} /* removefacet */
+
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="removevertex">-</a>
+
+ qh_removevertex(qh, vertex )
+ unlinks vertex from qh.vertex_list,
+
+ returns:
+ updates qh.vertex_list .newvertex_list
+ decrements qh.num_vertices
+*/
+void qh_removevertex(qhT *qh, vertexT *vertex) {
+ vertexT *next= vertex->next, *previous= vertex->previous;
+
+ if (vertex == qh->newvertex_list)
+ qh->newvertex_list= next;
+ if (previous) {
+ previous->next= next;
+ next->previous= previous;
+ }else { /* 1st vertex in qh->vertex_list */
+ qh->vertex_list= vertex->next;
+ qh->vertex_list->previous= NULL;
+ }
+ qh->num_vertices--;
+ trace4((qh, qh->ferr, 4058, "qh_removevertex: remove v%d from vertex_list\n", vertex->id));
+} /* removevertex */
+
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >-------------------------------</a><a name="updatevertices">-</a>
+
+ qh_updatevertices()
+ update vertex neighbors and delete interior vertices
+
+ returns:
+ if qh.VERTEXneighbors, updates neighbors for each vertex
+ if qh.newvertex_list,
+ removes visible neighbors from vertex neighbors
+ if qh.newfacet_list
+ adds new facets to vertex neighbors
+ if qh.visible_list
+ interior vertices added to qh.del_vertices for later partitioning
+
+ design:
+ if qh.VERTEXneighbors
+ deletes references to visible facets from vertex neighbors
+ appends new facets to the neighbor list for each vertex
+ checks all vertices of visible facets
+ removes visible facets from neighbor lists
+ marks unused vertices for deletion
+*/
+void qh_updatevertices(qhT *qh /*qh.newvertex_list, newfacet_list, visible_list*/) {
+ facetT *newfacet= NULL, *neighbor, **neighborp, *visible;
+ vertexT *vertex, **vertexp;
+
+ trace3((qh, qh->ferr, 3013, "qh_updatevertices: delete interior vertices and update vertex->neighbors\n"));
+ if (qh->VERTEXneighbors) {
+ FORALLvertex_(qh->newvertex_list) {
+ FOREACHneighbor_(vertex) {
+ if (neighbor->visible)
+ SETref_(neighbor)= NULL;
+ }
+ qh_setcompact(qh, vertex->neighbors);
+ }
+ FORALLnew_facets {
+ FOREACHvertex_(newfacet->vertices)
+ qh_setappend(qh, &vertex->neighbors, newfacet);
+ }
+ FORALLvisible_facets {
+ FOREACHvertex_(visible->vertices) {
+ if (!vertex->newlist && !vertex->deleted) {
+ FOREACHneighbor_(vertex) { /* this can happen under merging */
+ if (!neighbor->visible)
+ break;
+ }
+ if (neighbor)
+ qh_setdel(vertex->neighbors, visible);
+ else {
+ vertex->deleted= True;
+ qh_setappend(qh, &qh->del_vertices, vertex);
+ trace2((qh, qh->ferr, 2041, "qh_updatevertices: delete vertex p%d(v%d) in f%d\n",
+ qh_pointid(qh, vertex->point), vertex->id, visible->id));
+ }
+ }
+ }
+ }
+ }else { /* !VERTEXneighbors */
+ FORALLvisible_facets {
+ FOREACHvertex_(visible->vertices) {
+ if (!vertex->newlist && !vertex->deleted) {
+ vertex->deleted= True;
+ qh_setappend(qh, &qh->del_vertices, vertex);
+ trace2((qh, qh->ferr, 2042, "qh_updatevertices: delete vertex p%d(v%d) in f%d\n",
+ qh_pointid(qh, vertex->point), vertex->id, visible->id));
+ }
+ }
+ }
+ }
+} /* updatevertices */
+
+
+
diff --git a/xs/src/qhull/src/libqhull_r/poly_r.h b/xs/src/qhull/src/libqhull_r/poly_r.h
new file mode 100644
index 000000000..c71511bd6
--- /dev/null
+++ b/xs/src/qhull/src/libqhull_r/poly_r.h
@@ -0,0 +1,303 @@
+/*<html><pre> -<a href="qh-poly_r.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ poly_r.h
+ header file for poly_r.c and poly2_r.c
+
+ see qh-poly_r.htm, libqhull_r.h and poly_r.c
+
+ Copyright (c) 1993-2015 The Geometry Center.
+ $Id: //main/2015/qhull/src/libqhull_r/poly_r.h#5 $$Change: 2079 $
+ $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
+*/
+
+#ifndef qhDEFpoly
+#define qhDEFpoly 1
+
+#include "libqhull_r.h"
+
+/*=============== constants ========================== */
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >--------------------------------</a><a name="ALGORITHMfault">-</a>
+
+ ALGORITHMfault
+ use as argument to checkconvex() to report errors during buildhull
+*/
+#define qh_ALGORITHMfault 0
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >--------------------------------</a><a name="DATAfault">-</a>
+
+ DATAfault
+ use as argument to checkconvex() to report errors during initialhull
+*/
+#define qh_DATAfault 1
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >--------------------------------</a><a name="DUPLICATEridge">-</a>
+
+ DUPLICATEridge
+ special value for facet->neighbor to indicate a duplicate ridge
+
+ notes:
+ set by matchneighbor, used by matchmatch and mark_dupridge
+*/
+#define qh_DUPLICATEridge (facetT *)1L
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >--------------------------------</a><a name="MERGEridge">-</a>
+
+ MERGEridge flag in facet
+ special value for facet->neighbor to indicate a merged ridge
+
+ notes:
+ set by matchneighbor, used by matchmatch and mark_dupridge
+*/
+#define qh_MERGEridge (facetT *)2L
+
+
+/*============ -structures- ====================*/
+
+/*=========== -macros- =========================*/
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >--------------------------------</a><a name="FORALLfacet_">-</a>
+
+ FORALLfacet_( facetlist ) { ... }
+ assign 'facet' to each facet in facetlist
+
+ notes:
+ uses 'facetT *facet;'
+ assumes last facet is a sentinel
+
+ see:
+ FORALLfacets
+*/
+#define FORALLfacet_( facetlist ) if (facetlist ) for ( facet=( facetlist ); facet && facet->next; facet= facet->next )
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >--------------------------------</a><a name="FORALLnew_facets">-</a>
+
+ FORALLnew_facets { ... }
+ assign 'newfacet' to each facet in qh.newfacet_list
+
+ notes:
+ uses 'facetT *newfacet;'
+ at exit, newfacet==NULL
+*/
+#define FORALLnew_facets for ( newfacet=qh->newfacet_list;newfacet && newfacet->next;newfacet=newfacet->next )
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >--------------------------------</a><a name="FORALLvertex_">-</a>
+
+ FORALLvertex_( vertexlist ) { ... }
+ assign 'vertex' to each vertex in vertexlist
+
+ notes:
+ uses 'vertexT *vertex;'
+ at exit, vertex==NULL
+*/
+#define FORALLvertex_( vertexlist ) for (vertex=( vertexlist );vertex && vertex->next;vertex= vertex->next )
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >--------------------------------</a><a name="FORALLvisible_facets">-</a>
+
+ FORALLvisible_facets { ... }
+ assign 'visible' to each visible facet in qh.visible_list
+
+ notes:
+ uses 'vacetT *visible;'
+ at exit, visible==NULL
+*/
+#define FORALLvisible_facets for (visible=qh->visible_list; visible && visible->visible; visible= visible->next)
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >--------------------------------</a><a name="FORALLsame_">-</a>
+
+ FORALLsame_( newfacet ) { ... }
+ assign 'same' to each facet in newfacet->f.samecycle
+
+ notes:
+ uses 'facetT *same;'
+ stops when it returns to newfacet
+*/
+#define FORALLsame_(newfacet) for (same= newfacet->f.samecycle; same != newfacet; same= same->f.samecycle)
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >--------------------------------</a><a name="FORALLsame_cycle_">-</a>
+
+ FORALLsame_cycle_( newfacet ) { ... }
+ assign 'same' to each facet in newfacet->f.samecycle
+
+ notes:
+ uses 'facetT *same;'
+ at exit, same == NULL
+*/
+#define FORALLsame_cycle_(newfacet) \
+ for (same= newfacet->f.samecycle; \
+ same; same= (same == newfacet ? NULL : same->f.samecycle))
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >--------------------------------</a><a name="FOREACHneighborA_">-</a>
+
+ FOREACHneighborA_( facet ) { ... }
+ assign 'neighborA' to each neighbor in facet->neighbors
+
+ FOREACHneighborA_( vertex ) { ... }
+ assign 'neighborA' to each neighbor in vertex->neighbors
+
+ declare:
+ facetT *neighborA, **neighborAp;
+
+ see:
+ <a href="qset_r.h#FOREACHsetelement_">FOREACHsetelement_</a>
+*/
+#define FOREACHneighborA_(facet) FOREACHsetelement_(facetT, facet->neighbors, neighborA)
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >--------------------------------</a><a name="FOREACHvisible_">-</a>
+
+ FOREACHvisible_( facets ) { ... }
+ assign 'visible' to each facet in facets
+
+ notes:
+ uses 'facetT *facet, *facetp;'
+ see <a href="qset_r.h#FOREACHsetelement_">FOREACHsetelement_</a>
+*/
+#define FOREACHvisible_(facets) FOREACHsetelement_(facetT, facets, visible)
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >--------------------------------</a><a name="FOREACHnewfacet_">-</a>
+
+ FOREACHnewfacet_( facets ) { ... }
+ assign 'newfacet' to each facet in facets
+
+ notes:
+ uses 'facetT *newfacet, *newfacetp;'
+ see <a href="qset_r.h#FOREACHsetelement_">FOREACHsetelement_</a>
+*/
+#define FOREACHnewfacet_(facets) FOREACHsetelement_(facetT, facets, newfacet)
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >--------------------------------</a><a name="FOREACHvertexA_">-</a>
+
+ FOREACHvertexA_( vertices ) { ... }
+ assign 'vertexA' to each vertex in vertices
+
+ notes:
+ uses 'vertexT *vertexA, *vertexAp;'
+ see <a href="qset_r.h#FOREACHsetelement_">FOREACHsetelement_</a>
+*/
+#define FOREACHvertexA_(vertices) FOREACHsetelement_(vertexT, vertices, vertexA)
+
+/*-<a href="qh-poly_r.htm#TOC"
+ >--------------------------------</a><a name="FOREACHvertexreverse12_">-</a>
+
+ FOREACHvertexreverse12_( vertices ) { ... }
+ assign 'vertex' to each vertex in vertices
+ reverse order of first two vertices
+
+ notes:
+ uses 'vertexT *vertex, *vertexp;'
+ see <a href="qset_r.h#FOREACHsetelement_">FOREACHsetelement_</a>
+*/
+#define FOREACHvertexreverse12_(vertices) FOREACHsetelementreverse12_(vertexT, vertices, vertex)
+
+
+/*=============== prototypes poly_r.c in alphabetical order ================*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void qh_appendfacet(qhT *qh, facetT *facet);
+void qh_appendvertex(qhT *qh, vertexT *vertex);
+void qh_attachnewfacets(qhT *qh /* qh.visible_list, qh.newfacet_list */);
+boolT qh_checkflipped(qhT *qh, facetT *facet, realT *dist, boolT allerror);
+void qh_delfacet(qhT *qh, facetT *facet);
+void qh_deletevisible(qhT *qh /* qh.visible_list, qh.horizon_list */);
+setT *qh_facetintersect(qhT *qh, facetT *facetA, facetT *facetB, int *skipAp,int *skipBp, int extra);
+int qh_gethash(qhT *qh, int hashsize, setT *set, int size, int firstindex, void *skipelem);
+facetT *qh_makenewfacet(qhT *qh, setT *vertices, boolT toporient, facetT *facet);
+void qh_makenewplanes(qhT *qh /* qh.newfacet_list */);
+facetT *qh_makenew_nonsimplicial(qhT *qh, facetT *visible, vertexT *apex, int *numnew);
+facetT *qh_makenew_simplicial(qhT *qh, facetT *visible, vertexT *apex, int *numnew);
+void qh_matchneighbor(qhT *qh, facetT *newfacet, int newskip, int hashsize,
+ int *hashcount);
+void qh_matchnewfacets(qhT *qh);
+boolT qh_matchvertices(qhT *qh, int firstindex, setT *verticesA, int skipA,
+ setT *verticesB, int *skipB, boolT *same);
+facetT *qh_newfacet(qhT *qh);
+ridgeT *qh_newridge(qhT *qh);
+int qh_pointid(qhT *qh, pointT *point);
+void qh_removefacet(qhT *qh, facetT *facet);
+void qh_removevertex(qhT *qh, vertexT *vertex);
+void qh_updatevertices(qhT *qh);
+
+
+/*========== -prototypes poly2_r.c in alphabetical order ===========*/
+
+void qh_addhash(void *newelem, setT *hashtable, int hashsize, int hash);
+void qh_check_bestdist(qhT *qh);
+void qh_check_dupridge(qhT *qh, facetT *facet1, realT dist1, facetT *facet2, realT dist2);
+void qh_check_maxout(qhT *qh);
+void qh_check_output(qhT *qh);
+void qh_check_point(qhT *qh, pointT *point, facetT *facet, realT *maxoutside, realT *maxdist, facetT **errfacet1, facetT **errfacet2);
+void qh_check_points(qhT *qh);
+void qh_checkconvex(qhT *qh, facetT *facetlist, int fault);
+void qh_checkfacet(qhT *qh, facetT *facet, boolT newmerge, boolT *waserrorp);
+void qh_checkflipped_all(qhT *qh, facetT *facetlist);
+void qh_checkpolygon(qhT *qh, facetT *facetlist);
+void qh_checkvertex(qhT *qh, vertexT *vertex);
+void qh_clearcenters(qhT *qh, qh_CENTER type);
+void qh_createsimplex(qhT *qh, setT *vertices);
+void qh_delridge(qhT *qh, ridgeT *ridge);
+void qh_delvertex(qhT *qh, vertexT *vertex);
+setT *qh_facet3vertex(qhT *qh, facetT *facet);
+facetT *qh_findbestfacet(qhT *qh, pointT *point, boolT bestoutside,
+ realT *bestdist, boolT *isoutside);
+facetT *qh_findbestlower(qhT *qh, facetT *upperfacet, pointT *point, realT *bestdistp, int *numpart);
+facetT *qh_findfacet_all(qhT *qh, pointT *point, realT *bestdist, boolT *isoutside,
+ int *numpart);
+int qh_findgood(qhT *qh, facetT *facetlist, int goodhorizon);
+void qh_findgood_all(qhT *qh, facetT *facetlist);
+void qh_furthestnext(qhT *qh /* qh.facet_list */);
+void qh_furthestout(qhT *qh, facetT *facet);
+void qh_infiniteloop(qhT *qh, facetT *facet);
+void qh_initbuild(qhT *qh);
+void qh_initialhull(qhT *qh, setT *vertices);
+setT *qh_initialvertices(qhT *qh, int dim, setT *maxpoints, pointT *points, int numpoints);
+vertexT *qh_isvertex(pointT *point, setT *vertices);
+vertexT *qh_makenewfacets(qhT *qh, pointT *point /*horizon_list, visible_list*/);
+void qh_matchduplicates(qhT *qh, facetT *atfacet, int atskip, int hashsize, int *hashcount);
+void qh_nearcoplanar(qhT *qh /* qh.facet_list */);
+vertexT *qh_nearvertex(qhT *qh, facetT *facet, pointT *point, realT *bestdistp);
+int qh_newhashtable(qhT *qh, int newsize);
+vertexT *qh_newvertex(qhT *qh, pointT *point);
+ridgeT *qh_nextridge3d(ridgeT *atridge, facetT *facet, vertexT **vertexp);
+void qh_outcoplanar(qhT *qh /* qh.facet_list */);
+pointT *qh_point(qhT *qh, int id);
+void qh_point_add(qhT *qh, setT *set, pointT *point, void *elem);
+setT *qh_pointfacet(qhT *qh /*qh.facet_list*/);
+setT *qh_pointvertex(qhT *qh /*qh.facet_list*/);
+void qh_prependfacet(qhT *qh, facetT *facet, facetT **facetlist);
+void qh_printhashtable(qhT *qh, FILE *fp);
+void qh_printlists(qhT *qh);
+void qh_resetlists(qhT *qh, boolT stats, boolT resetVisible /*qh.newvertex_list qh.newfacet_list qh.visible_list*/);
+void qh_setvoronoi_all(qhT *qh);
+void qh_triangulate(qhT *qh /*qh.facet_list*/);
+void qh_triangulate_facet(qhT *qh, facetT *facetA, vertexT **first_vertex);
+void qh_triangulate_link(qhT *qh, facetT *oldfacetA, facetT *facetA, facetT *oldfacetB, facetT *facetB);
+void qh_triangulate_mirror(qhT *qh, facetT *facetA, facetT *facetB);
+void qh_triangulate_null(qhT *qh, facetT *facetA);
+void qh_vertexintersect(qhT *qh, setT **vertexsetA,setT *vertexsetB);
+setT *qh_vertexintersect_new(qhT *qh, setT *vertexsetA,setT *vertexsetB);
+void qh_vertexneighbors(qhT *qh /*qh.facet_list*/);
+boolT qh_vertexsubset(setT *vertexsetA, setT *vertexsetB);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* qhDEFpoly */
diff --git a/xs/src/qhull/src/libqhull_r/qh-geom_r.htm b/xs/src/qhull/src/libqhull_r/qh-geom_r.htm
new file mode 100644
index 000000000..eeefc0c75
--- /dev/null
+++ b/xs/src/qhull/src/libqhull_r/qh-geom_r.htm
@@ -0,0 +1,295 @@
+<!-- Do not edit with Front Page, it adds too many spaces -->
+<html>
+<head>
+<meta http-equiv="Content-Type"
+content="text/html; charset=iso-8859-1">
+<title>geom_r.c, geom2_r.c -- geometric and floating point routines</title>
+</head>
+
+<body>
+<!-- Navigation links -->
+<p><a name="TOP"><b>Up:</b></a> <a
+href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual</a>: Table of Contents <br>
+<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a>
+&#149; <a href="../../html/qh-quick.htm#options">Options</a>
+&#149; <a href="../../html/qh-opto.htm#output">Output</a>
+&#149; <a href="../../html/qh-optf.htm#format">Formats</a>
+&#149; <a href="../../html/qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="../../html/qh-optp.htm#print">Print</a>
+&#149; <a href="../../html/qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="../../html/qh-optc.htm#prec">Precision</a>
+&#149; <a href="../../html/qh-optt.htm#trace">Trace</a>
+&#149; <a href="index.htm">Functions</a><br>
+<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a><br>
+<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br>
+<b>To:</b> <a href="qh-geom_r.htm#TOC">Geom</a> &#149; <a href="qh-globa_r.htm">Global</a>
+&#149; <a href="qh-io_r.htm">Io</a> &#149; <a href="qh-mem_r.htm">Mem</a>
+&#149; <a href="qh-merge_r.htm">Merge</a> &#149; <a href="qh-poly_r.htm">Poly</a>
+&#149; <a href="qh-qhull_r.htm">Qhull</a> &#149; <a href="qh-set_r.htm">Set</a>
+&#149; <a href="qh-stat_r.htm">Stat</a> &#149; <a href="qh-user_r.htm">User</a>
+</p>
+
+<hr>
+<!-- Main text of document. -->
+
+<h2>geom_r.c, geom2_r.c, random_r.c -- geometric and floating point routines</h2>
+<blockquote>
+<p>Geometrically, a vertex is a point with <em>d</em> coordinates
+and a facet is a halfspace. A <em>halfspace</em> is defined by an
+oriented hyperplane through the facet's vertices. A <em>hyperplane</em>
+is defined by <em>d</em> normalized coefficients and an offset. A
+point is <em>above</em> a facet if its distance to the facet is
+positive.</p>
+
+<p>Qhull uses floating point coordinates for input points,
+vertices, halfspace equations, centrums, and an interior point.</p>
+
+<p>Qhull may be configured for single precision or double
+precision floating point arithmetic (see <a href="user_r.h#realT">realT</a>
+). </p>
+
+<p>Each floating point operation may incur round-off error (see
+<a href="qh-merge_r.htm#TOC">Merge</a>). The maximum error for distance
+computations is determined at initialization. The roundoff error
+in halfspace computation is accounted for by computing the
+distance from vertices to the halfspace. </p>
+</blockquote>
+<p><b>Copyright &copy; 1995-2015 C.B. Barber</b></p>
+<hr>
+<p><a href="#TOP">&#187;</a> <b>Geom</b>
+<a name="TOC">&#149;</a> <a href="qh-globa_r.htm#TOC">Global</a> &#149;
+<a href="qh-io_r.htm#TOC">Io</a> &#149; <a href="qh-mem_r.htm#TOC">Mem</a> &#149;
+<a href="qh-merge_r.htm#TOC">Merge</a> &#149; <a href="qh-poly_r.htm#TOC">Poly</a> &#149;
+<a href="qh-qhull_r.htm#TOC">Qhull</a> &#149; <a href="qh-set_r.htm#TOC">Set</a> &#149;
+<a href="qh-stat_r.htm#TOC">Stat</a> &#149; <a href="qh-user_r.htm#TOC">User</a> </p>
+
+<h3>Index to <a href="geom_r.c">geom_r.c</a>,
+<a href="geom2_r.c">geom2_r.c</a>, <a href="geom_r.h">geom_r.h</a>,
+<a href="random_r.c">random_r.c</a>, <a href="random_r.h">random_r.h</a>
+</h3>
+
+<ul>
+<li><a href="#gtype">geometric data types and constants</a> </li>
+<li><a href="#gmacro">mathematical macros</a>
+</li>
+<li><a href="#gmath">mathematical functions</a> </li>
+<li><a href="#gcomp">computational geometry functions</a> </li>
+<li><a href="#gpoint">point array functions</a> </li>
+<li><a href="#gfacet">geometric facet functions</a> </li>
+<li><a href="#ground">geometric roundoff functions</a></li>
+</ul>
+
+<h3><a href="qh-geom_r.htm#TOC">&#187;</a><a name="gtype">geometric data types
+and constants</a></h3>
+
+<ul>
+<li><a href="libqhull_r.h#coordT">coordT</a> coordinates and
+coefficients are stored as realT</li>
+<li><a href="libqhull_r.h#pointT">pointT</a> a point is an array
+of <tt>DIM3</tt> coordinates </li>
+</ul>
+
+<h3><a href="qh-geom_r.htm#TOC">&#187;</a><a name="gmacro">mathematical macros</a></h3>
+
+<ul>
+<li><a href="geom_r.h#fabs_">fabs_</a> returns the absolute
+value of a </li>
+<li><a href="geom_r.h#fmax_">fmax_</a> returns the maximum
+value of a and b </li>
+<li><a href="geom_r.h#fmin_">fmin_</a> returns the minimum
+value of a and b </li>
+<li><a href="geom_r.h#maximize_">maximize_</a> maximize a value
+</li>
+<li><a href="geom_r.h#minimize_">minimize_</a> minimize a value
+</li>
+<li><a href="geom_r.h#det2_">det2_</a> compute a 2-d
+determinate </li>
+<li><a href="geom_r.h#det3_">det3_</a> compute a 3-d
+determinate </li>
+<li><a href="geom_r.h#dX">dX, dY, dZ</a> compute the difference
+between two coordinates </li>
+</ul>
+
+<h3><a href="qh-geom_r.htm#TOC">&#187;</a><a name="gmath">mathematical functions</a></h3>
+
+<ul>
+<li><a href="geom_r.c#backnormal">qh_backnormal</a> solve for
+normal using back substitution </li>
+<li><a href="geom2_r.c#crossproduct">qh_crossproduct</a>
+compute the cross product of two 3-d vectors </li>
+<li><a href="geom2_r.c#determinant">qh_determinant</a> compute
+the determinant of a square matrix </li>
+<li><a href="geom_r.c#gausselim">qh_gausselim</a> Gaussian
+elimination with partial pivoting </li>
+<li><a href="geom2_r.c#gram_schmidt">qh_gram_schmidt</a>
+implements Gram-Schmidt orthogonalization by rows </li>
+<li><a href="geom2_r.c#maxabsval">qh_maxabsval</a> return max
+absolute value of a vector </li>
+<li><a href="geom2_r.c#minabsval">qh_minabsval</a> return min
+absolute value of a dim vector </li>
+<li><a href="geom2_r.c#mindiff">qh_mindiff</a> return index of
+min absolute difference of two vectors </li>
+<li><a href="geom_r.c#normalize">qh_normalize</a> normalize a
+vector </li>
+<li><a href="geom_r.c#normalize2">qh_normalize2</a> normalize a
+vector and report if too small </li>
+<li><a href="geom2_r.c#printmatrix">qh_printmatrix</a> print
+matrix given by row vectors </li>
+<li><a href="random_r.c#rand">qh_rand/srand</a> generate random
+numbers </li>
+<li><a href="random_r.c#randomfactor">qh_randomfactor</a> return
+a random factor near 1.0 </li>
+<li><a href="random_r.c#randommatrix">qh_randommatrix</a>
+generate a random dimXdim matrix in range (-1,1) </li>
+</ul>
+
+<h3><a href="qh-geom_r.htm#TOC">&#187;</a><a name="gcomp">computational geometry functions</a></h3>
+
+<ul>
+<li><a href="geom2_r.c#detsimplex">qh_detsimplex</a> compute
+determinate of a simplex of points </li>
+<li><a href="io_r.c#detvnorm">qh_detvnorm</a> determine normal for Voronoi ridge </li>
+<li><a href="geom2_r.c#distnorm">qh_distnorm</a> compute
+distance from point to hyperplane as defined by normal and offset</li>
+<li><a href="geom2_r.c#facetarea_simplex">qh_facetarea_simplex</a>
+return area of a simplex</li>
+<li><a href="geom_r.c#getangle">qh_getangle</a> return cosine
+of angle (i.e., dot product) </li>
+<li><a href="geom_r.c#getcenter">qh_getcenter</a> return
+arithmetic center for a set of vertices </li>
+<li><a href="geom2_r.c#pointdist">qh_pointdist</a> return
+distance between two points </li>
+<li><a href="geom2_r.c#rotatepoints">qh_rotatepoints</a> rotate
+numpoints points by a row matrix </li>
+<li><a href="geom2_r.c#sethalfspace">qh_sethalfspace</a> set
+coords to dual of halfspace relative to an interior point </li>
+<li><a href="geom_r.c#sethyperplane_det">qh_sethyperplane_det</a>
+return hyperplane for oriented simplex using determinates
+</li>
+<li><a href="geom_r.c#sethyperplane_gauss">qh_sethyperplane_gauss</a>
+return hyperplane for oriented simplex using Gaussian
+elimination </li>
+<li><a href="geom2_r.c#voronoi_center">qh_voronoi_center</a>
+return Voronoi center for a set of points </li>
+</ul>
+
+<h3><a href="qh-geom_r.htm#TOC">&#187;</a><a name="gpoint">point array functions</a></h3>
+<ul>
+<li><a href="geom2_r.c#copypoints">qh_copypoints</a> return
+malloc'd copy of points</li>
+<li><a href="geom2_r.c#joggleinput">qh_joggleinput</a> joggle
+input points by qh.JOGGLEmax </li>
+<li><a href="geom2_r.c#maxmin">qh_maxmin</a> return max/min
+points for each dimension</li>
+<li><a href="geom2_r.c#maxsimplex">qh_maxsimplex</a> determines
+maximum simplex for a set of points </li>
+<li><a href="geom2_r.c#printpoints">qh_printpoints</a> print ids for a
+set of points </li>
+<li><a href="geom2_r.c#projectinput">qh_projectinput</a> project
+input using qh DELAUNAY and qh low_bound/high_bound </li>
+<li><a href="geom2_r.c#projectpoints">qh_projectpoints</a>
+project points along one or more dimensions </li>
+<li><a href="geom2_r.c#rotateinput">qh_rotateinput</a> rotate
+input points using row matrix </li>
+<li><a href="geom2_r.c#scaleinput">qh_scaleinput</a> scale
+input points using qh low_bound/high_bound </li>
+<li><a href="geom2_r.c#scalelast">qh_scalelast</a> scale last
+coordinate to [0,m] for Delaunay triangulations </li>
+<li><a href="geom2_r.c#scalepoints">qh_scalepoints</a> scale
+points to new lowbound and highbound </li>
+<li><a href="geom2_r.c#setdelaunay">qh_setdelaunay</a> project
+points to paraboloid for Delaunay triangulation </li>
+<li><a href="geom2_r.c#sethalfspace_all">qh_sethalfspace_all</a>
+generate dual for halfspace intersection with interior
+point </li>
+</ul>
+
+<h3><a href="qh-geom_r.htm#TOC">&#187;</a><a name="gfacet">geometric facet functions</a></h3>
+<ul>
+<li><a href="geom_r.c#distplane">qh_distplane</a> return
+distance from point to facet </li>
+<li><a href="geom2_r.c#facetarea">qh_facetarea</a> return area
+of a facet </li>
+<li><a href="geom2_r.c#facetcenter">qh_facetcenter</a> return
+Voronoi center for a facet's vertices </li>
+<li><a href="geom_r.c#findbest">qh_findbest</a> find visible
+facet or best facet for a point </li>
+<li><a href="geom_r.c#findbesthorizon">qh_findbesthorizon</a>
+update best new facet with horizon facets</li>
+<li><a href="geom_r.c#findbestnew">qh_findbestnew</a> find best
+new facet for point </li>
+<li><a href="geom2_r.c#getarea">qh_getarea</a> get area of all
+facets in facetlist, collect statistics </li>
+<li><a href="geom_r.c#getcentrum">qh_getcentrum</a> return
+centrum for a facet </li>
+<li><a href="geom_r.c#getdistance">qh_getdistance</a> returns
+the max and min distance of a facet's vertices to a
+neighboring facet</li>
+<li><a href="geom2_r.c#findgooddist">qh_findgooddist</a> find
+best good facet visible for point from facet </li>
+<li><a href="geom2_r.c#inthresholds">qh_inthresholds</a> return
+True if facet normal within 'Pdn' and 'PDn'</li>
+<li><a href="geom2_r.c#orientoutside">qh_orientoutside</a>
+orient facet so that <tt>qh.interior_point</tt> is inside</li>
+<li><a href="geom_r.c#projectpoint">qh_projectpoint</a> project
+point onto a facet </li>
+<li><a href="geom_r.c#setfacetplane">qh_setfacetplane</a> sets
+the hyperplane for a facet </li>
+<li><a href="geom2_r.c#sharpnewfacets">qh_sharpnewfacets</a> true
+if new facets contains a sharp corner</li>
+</ul>
+
+<h3><a href="qh-geom_r.htm#TOC">&#187;</a><a name="ground">geometric roundoff functions</a></h3>
+<ul>
+<li><a href="geom2_r.c#detjoggle">qh_detjoggle</a> determine
+default joggle for points and distance roundoff error</li>
+<li><a href="geom2_r.c#detroundoff">qh_detroundoff</a>
+determine maximum roundoff error and other precision constants</li>
+<li><a href="geom2_r.c#distround">qh_distround</a> compute
+maximum roundoff error due to a distance computation to a
+normalized hyperplane</li>
+<li><a href="geom2_r.c#divzero">qh_divzero</a> divide by a
+number that is nearly zero </li>
+<li><a href="geom2_r.c#maxouter">qh_maxouter</a> return maximum outer
+plane</li>
+<li><a href="geom2_r.c#outerinner">qh_outerinner</a> return actual
+outer and inner planes
+</ul>
+
+<p><!-- Navigation links --> </p>
+<hr>
+<p><b>Up:</b>
+<a href="http://www.qhull.org">Home page for
+Qhull</a> <br>
+<b>Up:</b> <a href="index.htm#TOC">Qhull manual: Table of Contents</a> <br>
+<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a>
+&#149; <a href="../../html/qh-quick.htm#options">Options</a>
+&#149; <a href="../../html/qh-opto.htm#output">Output</a>
+&#149; <a href="../../html/qh-optf.htm#format">Formats</a>
+&#149; <a href="../../html/qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="../../html/qh-optp.htm#print">Print</a>
+&#149; <a href="../../html/qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="../../html/qh-optc.htm#prec">Precision</a>
+&#149; <a href="../../html/qh-optt.htm#trace">Trace</a>
+&#149; <a href="index.htm">Functions</a><br>
+<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a> <br>
+<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br>
+<b>To:</b> <a href="qh-geom_r.htm">Geom</a> &#149;
+<a href="qh-globa_r.htm">Global</a> &#149; <a href="qh-io_r.htm">Io</a>
+&#149; <a href="qh-mem_r.htm">Mem</a> &#149; <a href="qh-merge_r.htm">Merge</a>
+&#149; <a href="qh-poly_r.htm">Poly</a> &#149; <a href="qh-qhull_r.htm#TOC">Qhull</a>
+&#149; <a href="qh-set_r.htm">Set</a> &#149; <a href="qh-stat_r.htm">Stat</a>
+&#149; <a href="qh-user_r.htm">User</a><br>
+
+
+<p><!-- GC common information --> </p>
+<hr>
+<p><a href="http://www.geom.uiuc.edu/"><img
+src="../../html/qh--geom.gif" align="middle" width="40" height="40"></a><i>The
+Geometry Center Home Page </i></p>
+<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a>
+</a><br>
+Created: May 2, 1997 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p>
+</body>
+</html>
diff --git a/xs/src/qhull/src/libqhull_r/qh-globa_r.htm b/xs/src/qhull/src/libqhull_r/qh-globa_r.htm
new file mode 100644
index 000000000..45437a059
--- /dev/null
+++ b/xs/src/qhull/src/libqhull_r/qh-globa_r.htm
@@ -0,0 +1,163 @@
+<!-- Do not edit with Front Page, it adds too many spaces -->
+<html>
+<head>
+<meta http-equiv="Content-Type"
+content="text/html; charset=iso-8859-1">
+<title>global_r.c -- global variables and their functions</title>
+</head>
+
+<body>
+<!-- Navigation links -->
+<p><a name="TOP"><b>Up:</b></a> <a
+href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual</a>: Table of Contents <br>
+<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a>
+&#149; <a href="../../html/qh-quick.htm#options">Options</a>
+&#149; <a href="../../html/qh-opto.htm#output">Output</a>
+&#149; <a href="../../html/qh-optf.htm#format">Formats</a>
+&#149; <a href="../../html/qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="../../html/qh-optp.htm#print">Print</a>
+&#149; <a href="../../html/qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="../../html/qh-optc.htm#prec">Precision</a>
+&#149; <a href="../../html/qh-optt.htm#trace">Trace</a>
+&#149; <a href="index.htm">Functions</a><br>
+<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a><br>
+<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br>
+<b>To:</b> <a href="qh-geom_r.htm">Geom</a> &#149; <a href="qh-globa_r.htm#TOC">Global</a>
+&#149; <a href="qh-io_r.htm">Io</a> &#149; <a href="qh-mem_r.htm">Mem</a>
+&#149; <a href="qh-merge_r.htm">Merge</a> &#149; <a href="qh-poly_r.htm">Poly</a>
+&#149; <a href="qh-qhull_r.htm">Qhull</a> &#149; <a href="qh-set_r.htm">Set</a>
+&#149; <a href="qh-stat_r.htm">Stat</a> &#149; <a href="qh-user_r.htm">User</a>
+</p>
+
+<hr>
+<!-- Main text of document. -->
+
+<h2>global_r.c -- global variables and their functions</h2>
+<blockquote>
+<p>Qhull uses a data structure, <tt>qhT</tt>, to store
+globally defined constants, lists, sets, and variables. It is passed as the
+first argument to most functions.
+</p>
+</blockquote>
+<p><b>Copyright &copy; 1995-2015 C.B. Barber</b></p>
+<hr>
+<p><a href="#TOP">&#187;</a> <a href="qh-geom_r.htm#TOC">Geom</a>
+<a name="TOC">&#149;</a> <b>Global</b> &#149;
+<a href="qh-io_r.htm#TOC">Io</a> &#149; <a href="qh-mem_r.htm#TOC">Mem</a> &#149;
+<a href="qh-merge_r.htm#TOC">Merge</a> &#149; <a href="qh-poly_r.htm#TOC">Poly</a> &#149;
+<a href="qh-qhull_r.htm#TOC">Qhull</a> &#149; <a href="qh-set_r.htm#TOC">Set</a> &#149;
+<a href="qh-stat_r.htm#TOC">Stat</a> &#149; <a href="qh-user_r.htm#TOC">User</a> </p>
+
+<h3>Index to <a href="global_r.c">global_r.c</a> and
+<a href="libqhull_r.h">libqhull_r.h</a></h3>
+
+<ul>
+<li><a href="#ovar">Qhull's global variables</a> </li>
+<li><a href="#ofunc">Global variable and initialization
+routines</a> </li>
+</ul>
+
+<h3><a href="qh-globa_r.htm#TOC">&#187;</a><a name="ovar">Qhull's global
+variables</a></h3>
+
+<ul>
+<li><a href=global_r.c#qh_version>qh_version</a> version string
+<li><a href="libqhull_r.h#qh">qh</a> all global variables for
+qhull are in <tt>qh,qhmem</tt>, and <tt>qhstat</tt></li>
+<li><a href="libqhull_r.h#qh">QHULL_LIB_CHECK</a> Check for compatible library</li>
+<li><a href="libqhull_r.h#qh-const">qh constants</a> configuration
+flags and constants for Qhull </li>
+<li><a href="libqhull_r.h#qh-prec">qh precision constants</a>
+precision constants for Qhull </li>
+<li><a href="libqhull_r.h#qh-codetern">qh internal constants</a>
+internal constants for Qhull </li>
+<li><a href="libqhull_r.h#qh-lists">qh facet and vertex lists</a>
+lists of facets and vertices </li>
+<li><a href="libqhull_r.h#qh-var">qh global variables</a> minimum
+and maximum distances, next visit ids, several flags, and
+other global variables. </li>
+<li><a href="libqhull_r.h#qh-set">qh global sets</a> global sets
+for merging, hashing, input, etc. </li>
+<li><a href="libqhull_r.h#qh-buf">qh global buffers</a> buffers
+for matrix operations and input </li>
+<li><a href="libqhull_r.h#qh-static">qh static variables</a>
+static variables for individual functions </li>
+</ul>
+
+<h3><a href="qh-globa_r.htm#TOC">&#187;</a><a name="ofunc">Global variable and
+initialization routines</a></h3>
+
+<ul>
+<li><a href="global_r.c#appendprint">qh_appendprint</a> append
+output format to <tt>qh.PRINTout</tt> </li>
+<li><a href="global_r.c#freebuffers">qh_freebuffers</a> free
+global memory buffers </li>
+<li><a href="global_r.c#freeqhull">qh_freeqhull</a> free memory
+used by qhull </li>
+<li><a href="global_r.c#init_A">qh_init_A</a> called before
+error handling initialized </li>
+<li><a href="global_r.c#init_B">qh_init_B</a> called after
+points are defined </li>
+<li><a href="global_r.c#init_qhull_command">qh_init_qhull_command</a>
+build <tt>qh.qhull_command</tt> from <tt>argc/argv</tt></li>
+<li><a href="global_r.c#initflags">qh_initflags</a> set flags
+and constants from command line </li>
+<li><a href="global_r.c#initqhull_buffers">qh_initqhull_buffers</a>
+initialize global memory buffers </li>
+<li><a href="global_r.c#initqhull_globals">qh_initqhull_globals</a>
+initialize global variables </li>
+<li><a href="global_r.c#initqhull_mem">qh_initqhull_mem</a>
+initialize Qhull memory management </li>
+<li><a href="global_r.c#initqhull_start">qh_initqhull_start</a>
+allocate qh_qh and call qh_initqhull_start2()
+<li><a href="global_r.c#initqhull_start2">qh_initqhull_start2</a>
+initialize default values at Qhull startup </li>
+<li><a href="global_r.c#initthresholds">qh_initthresholds</a>
+initialize 'Pdn' and 'PDn' thresholds </li>
+<li><a href="global_r.c#lib_check">qh_lib_check</a> check for compatible Qhull library. Invoked by QHULL_LIB_CHECK at start of each program.</li>
+<li><a href="global_r.c#option">qh_option</a> append option
+description to <tt>qh.global_options</tt> </li>
+<li><a href="global_r.c#restore_qhull">qh_restore_qhull</a>
+restores a previously saved qhull </li>
+<li><a href="global_r.c#save_qhull">qh_save_qhull</a> saves
+qhull for a later qh_restore_qhull() </li>
+<li><a href="global_r.c#strtol">qh_strtol</a> duplicates
+strtod() and strtol() </li>
+<li><a href="global_r.c#zero">qh_zero</a> zeroes qhT before first use</li>
+</ul>
+
+<p><!-- Navigation links --> </p>
+<hr>
+<p><b>Up:</b>
+<a href="http://www.qhull.org">Home page for
+Qhull</a> <br>
+<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual: Table of Contents</a> <br>
+<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a>
+&#149; <a href="../../html/qh-quick.htm#options">Options</a>
+&#149; <a href="../../html/qh-opto.htm#output">Output</a>
+&#149; <a href="../../html/qh-optf.htm#format">Formats</a>
+&#149; <a href="../../html/qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="../../html/qh-optp.htm#print">Print</a>
+&#149; <a href="../../html/qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="../../html/qh-optc.htm#prec">Precision</a>
+&#149; <a href="../../html/qh-optt.htm#trace">Trace</a>
+&#149; <a href="index.htm">Functions</a><br>
+<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a> <br>
+<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br>
+<b>To:</b> <a href="qh-geom_r.htm">Geom</a> &#149;
+<a href="qh-globa_r.htm">Global</a> &#149; <a href="qh-io_r.htm">Io</a>
+&#149; <a href="qh-mem_r.htm">Mem</a> &#149; <a href="qh-merge_r.htm">Merge</a>
+&#149; <a href="qh-poly_r.htm">Poly</a> &#149; <a href="qh-qhull_r.htm#TOC">Qhull</a>
+&#149; <a href="qh-set_r.htm">Set</a> &#149; <a href="qh-stat_r.htm">Stat</a>
+&#149; <a href="qh-user_r.htm">User</a><br>
+<p><!-- GC common information --> </p>
+<hr>
+<p><a href="http://www.geom.uiuc.edu/"><img
+src="../../html/qh--geom.gif" align="middle" width="40" height="40"></a><i>The
+Geometry Center Home Page </i></p>
+<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a>
+</a><br>
+Created: May 2, 1997 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p>
+</body>
+</html>
diff --git a/xs/src/qhull/src/libqhull_r/qh-io_r.htm b/xs/src/qhull/src/libqhull_r/qh-io_r.htm
new file mode 100644
index 000000000..8a8a96300
--- /dev/null
+++ b/xs/src/qhull/src/libqhull_r/qh-io_r.htm
@@ -0,0 +1,305 @@
+<!-- Do not edit with Front Page, it adds too many spaces -->
+<html>
+<head>
+<meta http-equiv="Content-Type"
+content="text/html; charset=iso-8859-1">
+<title>io_r.c -- input and output operations</title>
+</head>
+
+<body>
+<!-- Navigation links -->
+<p><a name="TOP"><b>Up:</b></a> <a
+href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual</a>: Table of Contents <br>
+<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a>
+&#149; <a href="../../html/qh-quick.htm#options">Options</a>
+&#149; <a href="../../html/qh-opto.htm#output">Output</a>
+&#149; <a href="../../html/qh-optf.htm#format">Formats</a>
+&#149; <a href="../../html/qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="../../html/qh-optp.htm#print">Print</a>
+&#149; <a href="../../html/qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="../../html/qh-optc.htm#prec">Precision</a>
+&#149; <a href="../../html/qh-optt.htm#trace">Trace</a>
+&#149; <a href="index.htm">Functions</a><br>
+<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a><br>
+<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br>
+<b>To:</b> <a href="qh-geom_r.htm">Geom</a> &#149; <a href="qh-globa_r.htm">Global</a>
+&#149; <a href="qh-io_r.htm#TOC">Io</a> &#149; <a href="qh-mem_r.htm">Mem</a>
+&#149; <a href="qh-merge_r.htm">Merge</a> &#149; <a href="qh-poly_r.htm">Poly</a>
+&#149; <a href="qh-qhull_r.htm">Qhull</a> &#149; <a href="qh-set_r.htm">Set</a>
+&#149; <a href="qh-stat_r.htm">Stat</a> &#149; <a href="qh-user_r.htm">User</a>
+</p>
+<hr>
+
+<h2>io_r.c -- input and output operations</h2>
+<blockquote>
+
+<p>Qhull provides a wide range of input
+and output options. To organize the code, most output formats use
+the same driver: </p>
+
+<pre>
+ qh_printbegin( fp, format, facetlist, facets, printall );
+
+ FORALLfacet_( facetlist )
+ qh_printafacet( fp, format, facet, printall );
+
+ FOREACHfacet_( facets )
+ qh_printafacet( fp, format, facet, printall );
+
+ qh_printend( fp, format );
+</pre>
+
+<p>Note the 'printall' flag. It selects whether or not
+qh_skipfacet() is tested. </p>
+
+</blockquote>
+<p><b>Copyright &copy; 1995-2015 C.B. Barber</b></p>
+<hr>
+<p><a href="#TOP">&#187;</a> <a href="qh-geom_r.htm#TOC">Geom</a> <a name="TOC">&#149;</a>
+<a href="qh-globa_r.htm#TOC">Global</a> &#149; <b>Io</b> &#149;
+<a href="qh-mem_r.htm#TOC">Mem</a> &#149; <a href="qh-merge_r.htm#TOC">Merge</a> &#149;
+<a href="qh-poly_r.htm#TOC">Poly</a> &#149; <a href="qh-qhull_r.htm#TOC">Qhull</a> &#149;
+<a href="qh-set_r.htm#TOC">Set</a> &#149; <a href="qh-stat_r.htm#TOC">Stat</a> &#149;
+<a href="qh-user_r.htm#TOC">User</a> </p>
+
+<h3>Index to <a href="io_r.c">io_r.c</a> and <a href="io_r.h">io_r.h</a></h3>
+
+<ul>
+<li><a href="#iconst">io_r.h constants and types</a> </li>
+<li><a href="#ilevel">User level functions</a> </li>
+<li><a href="#iprint">Print functions for all output formats</a></li>
+<li><a href="#itext">Text output functions</a> </li>
+<li><a href="#iutil">Text utility functions</a></li>
+<li><a href="#igeom">Geomview output functions</a> </li>
+<li><a href="#iview">Geomview utility functions</a></li>
+</ul>
+
+<h3><a href="qh-io_r.htm#TOC">&#187;</a><a name="iconst">io_r.h constants and types</a></h3>
+
+<ul>
+<li><a href="io_r.h#qh_MAXfirst">qh_MAXfirst</a> maximum length
+of first two lines of stdin </li>
+<li><a href="io_r.h#qh_WHITESPACE">qh_WHITESPACE</a> possible
+values of white space </li>
+<li><a href="io_r.h#printvridgeT">printvridgeT</a> function to
+print results of qh_printvdiagram or qh_eachvoronoi</li>
+</ul>
+
+<h3><a href="qh-io_r.htm#TOC">&#187;</a><a name="ilevel">User level functions</a></h3>
+
+<ul>
+<li><a href="io_r.c#copyfilename">qh_copyfilename</a>
+copy filename identified by qh_skipfilename
+<li><a href="io_r.c#eachvoronoi_all">qh_eachvoronoi_all</a>
+visit each Voronoi ridge of the Voronoi diagram
+<li><a href="io_r.c#prepare_output">qh_prepare_output</a>
+prepare Qhull for output (called by qh_produce_output())
+<li><a href="io_r.c#printhelp_degenerate">qh_printhelp_degenerate</a>
+prints descriptive message for precision error </li>
+<li><a href="io_r.c#printhelp_singular">qh_printhelp_singular</a>
+print help message for singular data </li>
+<li><a href="libqhull_r.c#printsummary">qh_printsummary</a> print
+summary ('s')</li>
+<li><a href="io_r.c#produce_output">qh_produce_output</a>
+prints out the result of qhull()</li>
+<li><a href="io_r.c#produce_output">qh_produce_output2</a>
+prints out the result of qhull() without calling qh_prepare_output()</li>
+<li><a href="io_r.c#readfeasible">qh_readfeasible</a> read
+interior point from remainder and qh fin ('H')</li>
+<li><a href="io_r.c#readpoints">qh_readpoints</a> read input
+points </li>
+<li><a href="io_r.c#setfeasible">qh_setfeasible</a> set
+interior point from qh feasible_string ('Hn,n,n')</li>
+<li><a href="io_r.c#skipfilename">qh_skipfilename</a>
+skip filename in string
+</ul>
+
+<h3><a href="qh-io_r.htm#TOC">&#187;</a><a name="iprint">Print functions for all
+output formats</a></h3>
+
+<ul>
+<li><a href="io_r.c#countfacets">qh_countfacets</a> count good
+facets for printing and set visitid </li>
+<li><a href="io_r.c#markkeep">qh_markkeep</a> mark good facets
+that meet qh.KEEParea ('PAn'), qh.KEEPmerge ('PMn'), and qh.KEEPminArea ('PFn')</li>
+<li><a href="io_r.c#order_vertexneighbors">qh_order_vertexneighbors</a>
+order neighbors for a 3-d vertex by adjacency ('i', 'o')</li>
+<li><a href="io_r.c#printafacet">qh_printafacet</a> print facet
+in an output format </li>
+<li><a href="io_r.c#printbegin">qh_printbegin</a> print header
+for an output format </li>
+<li><a href="io_r.c#printend">qh_printend</a> print trailer for
+an output format </li>
+<li><a href="user_r.c#printfacetlist">qh_printfacetlist</a>
+print facets in a facetlist</li>
+<li><a href="io_r.c#printfacets">qh_printfacets</a> print
+facetlist and/or facet set in an output format </li>
+<li><a href="io_r.c#printneighborhood">qh_printneighborhood</a>
+print neighborhood of one or two facets ('Po')</li>
+<li><a href="io_r.c#produce_output">qh_produce_output</a>
+print the results of qh_qhull() </li>
+<li><a href="io_r.c#skipfacet">qh_skipfacet</a> True if not
+printing this facet ('Pdk:n', 'QVn', 'QGn')</li>
+<li><a href="io_r.c#facetvertices">qh_facetvertices</a> return
+vertices in a set of facets ('p')</li>
+</ul>
+
+<h3><a href="qh-io_r.htm#TOC">&#187;</a><a name="itext">Text output functions</a></h3>
+<ul>
+<li><a href="io_r.c#eachvoronoi">qh_eachvoronoi</a>
+print or visit each Voronoi ridge for an input site of the Voronoi diagram
+<li><a href="io_r.c#printextremes">qh_printextremes</a> print
+extreme points by point ID (vertices of convex hull) ('Fx')</li>
+<li><a href="io_r.c#printextremes_2d">qh_printextremes_2d</a> print
+2-d extreme points by point ID ('Fx')</li>
+<li><a href="io_r.c#printextremes_d">qh_printextremes_d</a> print
+extreme points of input sites for Delaunay triangulations ('Fx')</li>
+<li><a href="io_r.c#printfacet">qh_printfacet</a> print all
+fields of a facet ('f')</li>
+<li><a href="io_r.c#printfacet2math">qh_printfacet2math</a> print
+2-d Maple or Mathematica output for a facet ('FM' or 'm')</li>
+<li><a href="io_r.c#printfacet3math">qh_printfacet3math</a>
+print 3-d Maple or Mathematica facet ('FM' or 'm')</li>
+<li><a href="io_r.c#printfacet3vertex">qh_printfacet3vertex</a>
+print vertices for a 3-d facet ('i', 'o')</li>
+<li><a href="io_r.c#printfacetheader">qh_printfacetheader</a>
+prints header fields of a facet ('f')</li>
+<li><a href="io_r.c#printfacetNvertex_nonsimplicial">qh_printfacetNvertex_nonsimplicial</a>
+print vertices for an N-d non-simplicial facet ('i', 'Ft')</li>
+<li><a href="io_r.c#printfacetNvertex_simplicial">qh_printfacetNvertex_simplicial</a>
+print vertices for an N-d simplicial facet ('i', 'o', 'Ft')</li>
+<li><a href="io_r.c#printfacetridges">qh_printfacetridges</a>
+prints ridges of a facet ('f')</li>
+<li><a href="io_r.c#printpoints_out">qh_printpoints_out</a> prints
+vertices for facets by their point coordinates ('p')</li>
+<li><a href="io_r.c#printridge">qh_printridge</a> print all
+fields for a ridge ('f')</li>
+<li><a href="io_r.c#printvdiagram">qh_printvdiagram</a> print
+voronoi diagram as Voronoi vertices for each input pair</li>
+<li><a href="io_r.c#printvertex">qh_printvertex</a> print all
+fields for a vertex ('f')</li>
+<li><a href="io_r.c#printvertexlist">qh_printvertexlist</a>
+print vertices used by a list or set of facets ('f')</li>
+<li><a href="io_r.c#printvertices">qh_printvertices</a> print a
+set of vertices ('f')</li>
+<li><a href="io_r.c#printvneighbors">qh_printvneighbors</a>
+print vertex neighbors of vertices ('FN')</li>
+<li><a href="io_r.c#printvoronoi">qh_printvoronoi</a> print
+voronoi diagram in 'o' or 'G' format</li>
+</ul>
+
+<h3><a href="qh-io_r.htm#TOC">&#187;</a><a name="iutil">Text utility functions</a></h3>
+<ul>
+<li><a href="io_r.c#dfacet">dfacet</a> print facet by ID </li>
+<li><a href="io_r.c#dvertex">dvertex</a> print vertex by ID </li>
+<li><a href="io_r.c#compare_facetarea">qh_compare_facetarea</a>
+used by qsort() to order facets by area </li>
+<li><a href="io_r.c#compare_facetmerge">qh_compare_facetmerge</a>
+used by qsort() to order facets by number of merges </li>
+<li><a href="io_r.c#compare_facetvisit">qh_compare_facetvisit</a>
+used by qsort() to order facets by visit ID or ID </li>
+<li><a href="io_r.c#compare_vertexpoint">qh_compare_vertexpoint</a>
+used by qsort() to order vertices by point ID </li>
+<li><a href="io_r.c#detvnorm">qh_detvnorm</a> determine normal for Voronoi ridge </li>
+<li><a href="io_r.c#detvridge">qh_detvridge</a> determine Voronoi
+ridge for an input site
+<li><a href="io_r.c#detvridge3">qh_detvridge3</a> determine 3-d Voronoi
+ridge for an input site
+<li><a href="io_r.c#facet2point">qh_facet2point</a> return two
+projected temporary vertices for a 2-d facet ('m', 'G')</li>
+<li><a href="io_r.c#markvoronoi">qh_markvoronoi</a> mark Voronoi
+vertices for printing
+<li><a href="io_r.c#printcenter">qh_printcenter</a> print
+facet-&gt;center as centrum or Voronoi center ('Ft', 'v p', 'FC', 'f') </li>
+<li><a href="io_r.c#printpoint">qh_printpoint</a>, qh_printpointid, print
+coordinates of a point ('p', 'o', 'Fp', 'G', 'f')</li>
+<li><a href="io_r.c#printpoint3">qh_printpoint3</a> prints 2-d,
+3-d, or 4-d point as 3-d coordinates ('G')</li>
+<li><a href="io_r.c#printvdiagram2">qh_printvdiagram2</a> print
+voronoi diagram for each ridge of each vertex from qh_markvoronoi</li>
+<li><a href="io_r.c#printvnorm">qh_printvnorm</a> print
+separating plane of the Voronoi diagram for a pair of input sites</li>
+<li><a href="io_r.c#printvridge">qh_printvridge</a> print
+ridge of the Voronoi diagram for a pair of input sites</li>
+<li><a href="io_r.c#projectdim3">qh_projectdim3</a> project 2-d
+3-d or 4-d point to a 3-d point ('G')</li>
+</ul>
+
+<h3><a href="qh-io_r.htm#TOC">&#187;</a><a name="igeom">Geomview output functions</a></h3>
+<ul>
+<li><a href="io_r.c#printfacet2geom">qh_printfacet2geom</a>
+print facet as a 2-d VECT object </li>
+<li><a href="io_r.c#printfacet2geom_points">qh_printfacet2geom_points</a>
+print points as a 2-d VECT object with offset </li>
+<li><a href="io_r.c#printfacet3geom_nonsimplicial">qh_printfacet3geom_nonsimplicial</a>
+print Geomview OFF for a 3-d nonsimplicial facet. </li>
+<li><a href="io_r.c#printfacet3geom_points">qh_printfacet3geom_points</a>
+prints a 3-d facet as OFF Geomview object. </li>
+<li><a href="io_r.c#printfacet3geom_simplicial">qh_printfacet3geom_simplicial</a>
+print Geomview OFF for a 3-d simplicial facet. </li>
+<li><a href="io_r.c#printfacet4geom_nonsimplicial">qh_printfacet4geom_nonsimplicial</a>
+print Geomview 4OFF file for a 4d nonsimplicial facet </li>
+<li><a href="io_r.c#printfacet4geom_simplicial">qh_printfacet4geom_simplicial</a>
+print Geomview 4OFF file for a 4d simplicial facet </li>
+<li><a href="io_r.c#printhyperplaneintersection">qh_printhyperplaneintersection</a>
+print hyperplane intersection as OFF or 4OFF </li>
+<li><a href="io_r.c#printvoronoi">qh_printvoronoi</a> print
+voronoi diagram in 'o' or 'G' format</li>
+</ul>
+<h3><a href="qh-io_r.htm#TOC">&#187;</a><a name="iview">Geomview utility functions</a></h3>
+<ul>
+<li><a href="io_r.c#geomplanes">qh_geomplanes</a>
+ return outer and inner planes for Geomview</li>
+<li><a href="io_r.c#printcentrum">qh_printcentrum</a> print
+centrum for a facet in OOGL format </li>
+<li><a href="io_r.c#printend4geom">qh_printend4geom</a> helper
+function for qh_printbegin/printend </li>
+<li><a href="io_r.c#printhyperplaneintersection">qh_printhyperplaneintersection</a>
+print Geomview OFF or 4OFF for the intersection of two
+hyperplanes in 3-d or 4-d </li>
+<li><a href="io_r.c#printline3geom">qh_printline3geom</a> prints a
+line as a VECT </li>
+<li><a href="io_r.c#printpointvect">qh_printpointvect</a>
+prints a 2-d or 3-d point as 3-d VECT's </li>
+<li><a href="io_r.c#printpointvect2">qh_printpointvect2</a>
+prints a 2-d or 3-d point as 2 3-d VECT's </li>
+<li><a href="io_r.c#printspheres">qh_printspheres</a> prints 3-d
+vertices as OFF spheres </li>
+</ul>
+<p>
+<p><!-- Navigation links --> </p>
+<hr>
+<p><b>Up:</b>
+<a href="http://www.qhull.org">Home page for
+Qhull</a> <br>
+<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual: Table of Contents</a> <br>
+<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a>
+&#149; <a href="../../html/qh-quick.htm#options">Options</a>
+&#149; <a href="../../html/qh-opto.htm#output">Output</a>
+&#149; <a href="../../html/qh-optf.htm#format">Formats</a>
+&#149; <a href="../../html/qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="../../html/qh-optp.htm#print">Print</a>
+&#149; <a href="../../html/qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="../../html/qh-optc.htm#prec">Precision</a>
+&#149; <a href="../../html/qh-optt.htm#trace">Trace</a>
+&#149; <a href="index.htm">Functions</a><br>
+<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a> <br>
+<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br>
+<b>To:</b> <a href="qh-geom_r.htm">Geom</a> &#149;
+<a href="qh-globa_r.htm">Global</a> &#149; <a href="qh-io_r.htm">Io</a>
+&#149; <a href="qh-mem_r.htm">Mem</a> &#149; <a href="qh-merge_r.htm">Merge</a>
+&#149; <a href="qh-poly_r.htm">Poly</a> &#149; <a href="qh-qhull_r.htm#TOC">Qhull</a>
+&#149; <a href="qh-set_r.htm">Set</a> &#149; <a href="qh-stat_r.htm">Stat</a>
+&#149; <a href="qh-user_r.htm">User</a><br>
+</p>
+<p><!-- GC common information --> </p>
+<hr>
+<p><a href="http://www.geom.uiuc.edu/"><img
+src="../../html/qh--geom.gif" align="middle" width="40" height="40"></a><i>The
+Geometry Center Home Page </i></p>
+<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a>
+</a><br>
+Created: May 2, 1997 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p>
+</body>
+</html>
diff --git a/xs/src/qhull/src/libqhull_r/qh-mem_r.htm b/xs/src/qhull/src/libqhull_r/qh-mem_r.htm
new file mode 100644
index 000000000..db59119cb
--- /dev/null
+++ b/xs/src/qhull/src/libqhull_r/qh-mem_r.htm
@@ -0,0 +1,145 @@
+<!-- Do not edit with Front Page, it adds too many spaces -->
+<html>
+<head>
+<meta http-equiv="Content-Type"
+content="text/html; charset=iso-8859-1">
+<title>mem_r.c -- memory operations</title>
+</head>
+
+<body>
+<!-- Navigation links -->
+<p><a name="TOP"><b>Up:</b></a> <a
+href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual</a>: Table of Contents <br>
+<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a>
+&#149; <a href="../../html/qh-quick.htm#options">Options</a>
+&#149; <a href="../../html/qh-opto.htm#output">Output</a>
+&#149; <a href="../../html/qh-optf.htm#format">Formats</a>
+&#149; <a href="../../html/qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="../../html/qh-optp.htm#print">Print</a>
+&#149; <a href="../../html/qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="../../html/qh-optc.htm#prec">Precision</a>
+&#149; <a href="../../html/qh-optt.htm#trace">Trace</a>
+&#149; <a href="index.htm">Functions</a><br>
+<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a><br>
+<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br>
+<b>To:</b> <a href="qh-geom_r.htm">Geom</a> &#149; <a href="qh-globa_r.htm">Global</a>
+&#149; <a href="qh-io_r.htm">Io</a> &#149; <a href="qh-mem_r.htm#TOC">Mem</a>
+&#149; <a href="qh-merge_r.htm">Merge</a> &#149; <a href="qh-poly_r.htm">Poly</a>
+&#149; <a href="qh-qhull_r.htm">Qhull</a> &#149; <a href="qh-set_r.htm">Set</a>
+&#149; <a href="qh-stat_r.htm">Stat</a> &#149; <a href="qh-user_r.htm">User</a>
+</p>
+<hr>
+
+<h2>mem_r.c -- memory operations</h2>
+<blockquote>
+<p>Qhull uses quick-fit memory allocation. It maintains a
+set of free lists for a variety of small allocations. A
+small request returns a block from the best fitting free
+list. If the free list is empty, Qhull allocates a block
+from a reserved buffer. </p>
+<p>Use 'T5' to trace memory allocations.</p>
+
+</blockquote>
+<p><b>Copyright &copy; 1995-2015 C.B. Barber</b></p>
+<hr>
+<p><a href="#TOP">&#187;</a> <a href="qh-geom_r.htm#TOC">Geom</a>
+<a name="TOC">&#149;</a> <a href="qh-globa_r.htm#TOC">Global</a> &#149;
+<a href="qh-io_r.htm#TOC">Io</a> &#149; <b>Mem</b>
+&#149; <a href="qh-merge_r.htm#TOC">Merge</a> &#149; <a href="qh-poly_r.htm#TOC">Poly</a>
+&#149; <a href="qh-qhull_r.htm#TOC">Qhull</a> &#149; <a href="qh-set_r.htm#TOC">Set</a>
+&#149; <a href="qh-stat_r.htm#TOC">Stat</a> &#149; <a href="qh-user_r.htm#TOC">User</a>
+</p>
+<h3>Index to <a href="mem_r.c">mem_r.c</a> and
+<a href="mem_r.h">mem_r.h</a></h3>
+<ul>
+<li><a href="#etype">mem_r.h data types</a> </li>
+<li><a href="#emacro">mem_r.h macros</a> </li>
+<li><a href="#efunc">User level functions</a> </li>
+</ul>
+<h3><a href="qh-mem_r.htm#TOC">&#187;</a><a name="etype">mem_r.h data types and constants</a></h3>
+<ul>
+<li><a href="mem_r.h#ptr_intT">ptr_intT</a> for casting
+a void* to an integer-type </li>
+<li><a href="mem_r.h#qhmemT">qhmemT</a> global memory
+structure for mem_r.c </li>
+<li><a href="mem_r.h#NOmem">qh_NOmem</a> disable memory allocation</li>
+</ul>
+<h3><a href="qh-mem_r.htm#TOC">&#187;</a><a name="emacro">mem_r.h macros</a></h3>
+<ul>
+<li><a href="mem_r.h#memalloc_">qh_memalloc_</a>
+allocate memory</li>
+<li><a href="mem_r.h#memfree_">qh_memfree_</a> free
+memory</li>
+</ul>
+<h3><a href="qh-mem_r.htm#TOC">&#187;</a><a name="efunc">User level
+functions</a></h3>
+<ul>
+<li><a href="mem_r.c#memalloc">qh_memalloc</a> allocate
+memory </li>
+<li><a href="mem_r.c#memcheck">qh_memcheck</a>
+quick check of memory for internal consistency</li>
+<li><a href="mem_r.c#memfree">qh_memfree</a> free
+memory </li>
+<li><a href="mem_r.c#meminit">qh_meminit</a> initialize
+memory </li>
+<li><a href="mem_r.c#memstatistics">qh_memstatistics</a>
+print memory statistics </li>
+<li><a href="mem_r.c#meminit">qh_memtotlong</a> return total, allocated long memory</li>
+<li><a href="mem_r.c#NOmem">qh_NOmem</a> allocation routines with malloc() and free()
+</ul>
+
+<h3><a href="qh-mem_r.htm#TOC">&#187;</a><a name="m">Initialization and
+termination functions</a></h3>
+<ul>
+<li><a href="mem_r.c#intcompare">qh_intcompare</a> used by
+qsort and bsearch to compare two integers </li>
+<li><a href="mem_r.c#memfreeshort">qh_memfreeshort</a>
+frees up all short and qhmem memory allocations </li>
+<li><a href="mem_r.c#meminit">qh_meminit</a> initialize
+memory </li>
+<li><a href="mem_r.c#meminitbuffers">qh_meminitbuffers</a>
+initialize qhmem </li>
+<li><a href="mem_r.c#memsetup">qh_memsetup</a> set up
+memory after running memsize() </li>
+<li><a href="mem_r.c#memsize">qh_memsize</a> define a free
+list for this size </li>
+<li><a href="mem_r.c#memstatistics">qh_memstatistics</a>
+print out memory statistics </li>
+</ul>
+
+<p><!-- Navigation links --> </p>
+<hr>
+<p><b>Up:</b>
+<a href="http://www.qhull.org">Home page for
+Qhull</a> <br>
+<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual: Table of Contents</a> <br>
+<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a>
+&#149; <a href="../../html/qh-quick.htm#options">Options</a>
+&#149; <a href="../../html/qh-opto.htm#output">Output</a>
+&#149; <a href="../../html/qh-optf.htm#format">Formats</a>
+&#149; <a href="../../html/qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="../../html/qh-optp.htm#print">Print</a>
+&#149; <a href="../../html/qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="../../html/qh-optc.htm#prec">Precision</a>
+&#149; <a href="../../html/qh-optt.htm#trace">Trace</a>
+&#149; <a href="index.htm">Functions</a><br>
+<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a> <br>
+<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br>
+<b>To:</b> <a href="qh-geom_r.htm">Geom</a> &#149;
+<a href="qh-globa_r.htm">Global</a> &#149; <a href="qh-io_r.htm">Io</a>
+&#149; <a href="qh-mem_r.htm">Mem</a> &#149; <a href="qh-merge_r.htm">Merge</a>
+&#149; <a href="qh-poly_r.htm">Poly</a> &#149; <a href="qh-qhull_r.htm#TOC">Qhull</a>
+&#149; <a href="qh-set_r.htm">Set</a> &#149; <a href="qh-stat_r.htm">Stat</a>
+&#149; <a href="qh-user_r.htm">User</a><br>
+</p>
+<p><!-- GC common information --> </p>
+<hr>
+<p><a href="http://www.geom.uiuc.edu/"><img
+src="../../html/qh--geom.gif" align="middle" width="40" height="40"></a><i>The
+Geometry Center Home Page </i></p>
+<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a>
+</a><br>
+Created: May 2, 1997 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p>
+</body>
+</html>
diff --git a/xs/src/qhull/src/libqhull_r/qh-merge_r.htm b/xs/src/qhull/src/libqhull_r/qh-merge_r.htm
new file mode 100644
index 000000000..63e5135be
--- /dev/null
+++ b/xs/src/qhull/src/libqhull_r/qh-merge_r.htm
@@ -0,0 +1,366 @@
+<!-- Do not edit with Front Page, it adds too many spaces -->
+<html>
+<head>
+<meta http-equiv="Content-Type"
+content="text/html; charset=iso-8859-1">
+<title>merge_r.c -- facet merge operations</title>
+</head>
+
+<body>
+<!-- Navigation links -->
+<p><a name="TOP"><b>Up:</b></a> <a
+href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual</a>: Table of Contents <br>
+<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a>
+&#149; <a href="../../html/qh-quick.htm#options">Options</a>
+&#149; <a href="../../html/qh-opto.htm#output">Output</a>
+&#149; <a href="../../html/qh-optf.htm#format">Formats</a>
+&#149; <a href="../../html/qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="../../html/qh-optp.htm#print">Print</a>
+&#149; <a href="../../html/qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="../../html/qh-optc.htm#prec">Precision</a>
+&#149; <a href="../../html/qh-optt.htm#trace">Trace</a>
+&#149; <a href="index.htm">Functions</a><br>
+<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a><br>
+<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br>
+<b>To:</b> <a href="qh-geom_r.htm">Geom</a> &#149; <a href="qh-globa_r.htm">Global</a>
+&#149; <a href="qh-io_r.htm">Io</a> &#149; <a href="qh-mem_r.htm">Mem</a>
+&#149; <a href="qh-merge_r.htm#TOC">Merge</a> &#149; <a href="qh-poly_r.htm">Poly</a>
+&#149; <a href="qh-qhull_r.htm">Qhull</a> &#149; <a href="qh-set_r.htm">Set</a>
+&#149; <a href="qh-stat_r.htm">Stat</a> &#149; <a href="qh-user_r.htm">User</a>
+</p>
+<hr>
+
+<h2>merge_r.c -- facet merge operations</h2>
+<blockquote>
+<p>Qhull handles precision problems by merged facets or joggled input.
+Except for redundant vertices, it corrects a problem by
+merging two facets. When done, all facets are clearly
+convex. See <a href="../../html/qh-impre.htm">Imprecision in Qhull</a>
+for further information. </p>
+<p>Users may joggle the input ('<a href="../../html/qh-optq.htm#QJn">QJn</a>')
+instead of merging facets. </p>
+<p>Qhull detects and corrects the following problems: </p>
+<ul>
+<li><b>More than two facets meeting at a ridge. </b>When
+Qhull creates facets, it creates an even number
+of facets for each ridge. A convex hull always
+has two facets for each ridge. More than two
+facets may be created if non-adjacent facets
+share a vertex. This is called a <em>duplicate
+ridge</em>. In 2-d, a duplicate ridge would
+create a loop of facets. </li>
+</ul>
+<ul>
+<li><b>A facet contained in another facet. </b>Facet
+merging may leave all vertices of one facet as a
+subset of the vertices of another facet. This is
+called a <em>redundant facet</em>. </li>
+</ul>
+<ul>
+<li><b>A facet with fewer than three neighbors. </b>Facet
+merging may leave a facet with one or two
+neighbors. This is called a <em>degenerate facet</em>.
+</li>
+</ul>
+<ul>
+<li><b>A facet with flipped orientation. </b>A
+facet's hyperplane may define a halfspace that
+does not include the interior point.This is
+called a <em>flipped facet</em>. </li>
+</ul>
+<ul>
+<li><strong>A coplanar horizon facet.</strong> A
+newly processed point may be coplanar with an
+horizon facet. Qhull creates a new facet without
+a hyperplane. It links new facets for the same
+horizon facet together. This is called a <em>samecycle</em>.
+The new facet or samecycle is merged into the
+horizon facet. </li>
+</ul>
+<ul>
+<li><b>Concave facets. </b>A facet's centrum may be
+above a neighboring facet. If so, the facets meet
+at a concave angle. </li>
+</ul>
+<ul>
+<li><b>Coplanar facets. </b>A facet's centrum may be
+coplanar with a neighboring facet (i.e., it is
+neither clearly below nor clearly above the
+facet's hyperplane). Qhull removes coplanar
+facets in independent sets sorted by angle.</li>
+</ul>
+<ul>
+<li><b>Redundant vertex. </b>A vertex may have fewer
+than three neighboring facets. If so, it is
+redundant and may be renamed to an adjacent
+vertex without changing the topological
+structure.This is called a <em>redundant vertex</em>.
+</li>
+</ul>
+</blockquote>
+<p><b>Copyright &copy; 1995-2015 C.B. Barber</b></p>
+<hr>
+<p><a href="#TOP">&#187;</a> <a href="qh-geom_r.htm#TOC">Geom</a>
+<a name="TOC">&#149;</a> <a href="qh-globa_r.htm#TOC">Global</a>
+&#149; <a href="qh-io_r.htm#TOC">Io</a> &#149; <a href="qh-mem_r.htm#TOC">Mem</a>
+&#149; <b>Merge</b> &#149; <a href="qh-poly_r.htm#TOC">Poly</a>
+&#149; <a href="qh-qhull_r.htm#TOC">Qhull</a> &#149; <a href="qh-set_r.htm#TOC">Set</a>
+&#149; <a href="qh-stat_r.htm#TOC">Stat</a> &#149; <a href="qh-user_r.htm#TOC">User</a>
+</p>
+<h3>Index to <a href="merge_r.c">merge_r.c</a> and
+<a href="merge_r.h">merge_r.h</a></h3>
+<ul>
+<li><a href="#mtype">merge_r.h data types, macros, and
+global sets</a> </li>
+<li><a href="#mconst">merge_r.h constants</a> </li>
+</ul>
+<ul>
+<li><a href="#mtop">top-level merge functions</a> </li>
+<li><a href="#mset">functions for identifying merges</a></li>
+<li><a href="#mbest">functions for determining the
+best merge</a> </li>
+<li><a href="#mmerge">functions for merging facets</a>
+</li>
+<li><a href="#mcycle">functions for merging a cycle
+of facets</a> </li>
+<li><a href="#mrename">functions for renaming a
+vertex</a> </li>
+<li><a href="#mvertex">functions for identifying
+vertices for renaming</a> </li>
+<li><a href="#mcheck">functions for check and trace</a> </li>
+</ul>
+<h3><a href="qh-merge_r.htm#TOC">&#187;</a><a name="mtype">merge_r.h data
+types, macros, and global sets</a></h3>
+<ul>
+<li><a href="merge_r.h#mergeT">mergeT</a> structure to
+identify a merge of two facets</li>
+<li><a href="merge_r.h#FOREACHmerge_">FOREACHmerge_</a>
+assign 'merge' to each merge in merges </li>
+<li><a href="libqhull_r.h#qh-set">qh global sets</a>
+qh.facet_mergeset contains non-convex merges
+while qh.degen_mergeset contains degenerate and
+redundant facets</li>
+</ul>
+<h3><a href="qh-merge_r.htm#TOC">&#187;</a><a name="mconst">merge_r.h
+constants</a></h3>
+<ul>
+<li><a href="libqhull_r.h#qh-prec">qh precision constants</a>
+precision constants for Qhull </li>
+<li><a href="merge_r.h#MRG">MRG...</a> indicates the
+type of a merge (mergeT-&gt;type)</li>
+<li><a href="merge_r.h#qh_ANGLEredundant">qh_ANGLEredundant</a>
+indicates redundant merge in mergeT-&gt;angle </li>
+<li><a href="merge_r.h#qh_ANGLEdegen">qh_ANGLEdegen</a>
+indicates degenerate facet in mergeT-&gt;angle </li>
+<li><a href="merge_r.h#qh_ANGLEconcave">qh_ANGLEconcave</a>
+offset to indicate concave facets in
+mergeT-&gt;angle </li>
+<li><a href="merge_r.h#qh_MERGEapex">qh_MERGEapex</a>
+flag for qh_mergefacet() to indicate an apex
+merge </li>
+</ul>
+<h3><a href="qh-merge_r.htm#TOC">&#187;</a><a name="mtop">top-level merge
+functions</a></h3>
+<ul>
+<li><a href="merge_r.c#all_merges">qh_all_merges</a>
+merge all non-convex facets </li>
+<li><a href="merge_r.c#checkzero">qh_checkzero</a>
+check that facets are clearly convex </li>
+<li><a href="merge_r.c#flippedmerges">qh_flippedmerges</a>
+merge flipped facets into best neighbor </li>
+<li><a href="merge_r.c#forcedmerges">qh_forcedmerges</a>
+merge all duplicate ridges </li>
+<li><a href="merge_r.c#merge_degenredundant">qh_merge_degenredundant</a>
+merge degenerate and redundant facets </li>
+<li><a href="merge_r.c#merge_nonconvex">qh_merge_nonconvex</a>
+merge a non-convex ridge </li>
+<li><a href="merge_r.c#premerge">qh_premerge</a>
+pre-merge non-convex facets </li>
+<li><a href="merge_r.c#postmerge">qh_postmerge</a>
+post-merge nonconvex facets as defined by
+maxcentrum/maxangle </li>
+</ul>
+
+<h3><a href="qh-merge_r.htm#TOC">&#187;</a><a name="mset">functions for
+identifying merges</a></h3>
+<ul>
+<li><a href="merge_r.c#appendmergeset">qh_appendmergeset</a>
+appends an entry to qh.facet_mergeset</li>
+<li><a href="merge_r.c#compareangle">qh_compareangle</a>
+used by qsort() to order merges </li>
+<li><a href="merge_r.c#comparemerge">qh_comparemerge</a>
+used by qsort() to order merges </li>
+<li><a href="merge_r.c#degen_redundant_facet">qh_degen_redundant_facet</a>
+check for a degenerate and redundant facet</li>
+<li><a href="merge_r.c#degen_redundant_neighbors">qh_degen_redundant_neighbors</a>
+append degenerate and redundant neighbors to
+qh.degen_mergeset </li>
+<li><a href="merge_r.c#getmergeset_initial">qh_getmergeset_initial</a>
+build initial qh.facet_mergeset </li>
+<li><a href="merge_r.c#getmergeset">qh_getmergeset</a>
+update qh.facet_mergeset </li>
+<li><a href="merge_r.c#mark_dupridges">qh_mark_dupridges</a>
+add duplicated ridges to qh.facet_mergeset</li>
+<li><a href="merge_r.c#maydropneighbor">qh_maydropneighbor</a>
+drop neighbor relationship if no ridge between
+facet and neighbor </li>
+<li><a href="merge_r.c#test_appendmerge">qh_test_appendmerge</a>
+test a pair of facets for convexity and append to
+qh.facet_mergeset if non-convex </li>
+<li><a href="merge_r.c#test_vneighbors">qh_test_vneighbors</a>
+test vertex neighbors for convexity </li>
+</ul>
+
+<h3><a href="qh-merge_r.htm#TOC">&#187;</a><a name="mbest">functions for
+determining the best merge</a></h3>
+<ul>
+<li><a href="merge_r.c#findbest_test">qh_findbest_test</a>
+test neighbor for best merge </li>
+<li><a href="merge_r.c#findbestneighbor">qh_findbestneighbor</a>
+finds best neighbor of a facet for merging (i.e.,
+closest hyperplane) </li>
+</ul>
+
+<h3><a href="qh-merge_r.htm#TOC">&#187;</a><a name="mmerge">functions for
+merging facets</a></h3>
+<ul>
+<li><a href="merge_r.c#copynonconvex">qh_copynonconvex</a>
+copy non-convex flag to another ridge for the
+same neighbor </li>
+<li><a href="merge_r.c#makeridges">qh_makeridges</a>
+creates explicit ridges between simplicial facets
+</li>
+<li><a href="merge_r.c#mergefacet">qh_mergefacet</a>
+merges one facet into another facet</li>
+<li><a href="merge_r.c#mergeneighbors">qh_mergeneighbors</a>
+merges the neighbors of two facets </li>
+<li><a href="merge_r.c#mergeridges">qh_mergeridges</a>
+merges the ridge sets of two facets </li>
+<li><a href="merge_r.c#mergesimplex">qh_mergesimplex</a>
+merge a simplicial facet into another simplicial
+facet </li>
+<li><a href="merge_r.c#mergevertex_del">qh_mergevertex_del</a>
+delete a vertex due to merging one facet into
+another facet </li>
+<li><a href="merge_r.c#mergevertex_neighbors">qh_mergevertex_neighbors</a>
+merge the vertex neighbors of two facets </li>
+<li><a href="merge_r.c#mergevertices">qh_mergevertices</a>
+merge the vertex sets of two facets </li>
+<li><a href="merge_r.c#newvertices">qh_newvertices</a>
+register all vertices as new vertices </li>
+<li><a href="merge_r.c#updatetested">qh_updatetested</a>
+clear tested flags and centrums involved in a
+merge </li>
+<li><a href="merge_r.c#willdelete">qh_willdelete</a>
+moves facet to qh.visible_list; sets replacement
+or NULL </li>
+</ul>
+
+<h3><a href="qh-merge_r.htm#TOC">&#187;</a><a name="mcycle">functions for
+merging a cycle of facets</a></h3>
+<p>If a point is coplanar with an horizon facet, the
+corresponding new facets are linked together (a <em>samecycle</em>)
+for merging.</p>
+<ul>
+<li><a href="merge_r.c#basevertices">qh_basevertices</a>
+return temporary set of base vertices for a
+samecycle </li>
+<li><a href="merge_r.c#mergecycle">qh_mergecycle</a>
+merge a samecycle into a horizon facet </li>
+<li><a href="merge_r.c#mergecycle_all">qh_mergecycle_all</a>
+merge all samecycles into horizon facets</li>
+<li><a href="merge_r.c#mergecycle_facets">qh_mergecycle_facets</a>
+finish merge of samecycle </li>
+<li><a href="merge_r.c#mergecycle_neighbors">qh_mergecycle_neighbors</a>
+merge neighbor sets for samecycle </li>
+<li><a href="merge_r.c#mergecycle_ridges">qh_mergecycle_ridges</a>
+merge ridge sets for samecycle </li>
+<li><a href="merge_r.c#mergecycle_vneighbors">qh_mergecycle_vneighbors</a>
+merge vertex neighbor sets for samecycle </li>
+</ul>
+<h3><a href="qh-merge_r.htm#TOC">&#187;</a><a name="mrename">functions
+for renaming a vertex</a></h3>
+<ul>
+<li><a href="merge_r.c#comparevisit">qh_comparevisit</a>
+used by qsort() to order vertices by visitid</li>
+<li><a href="merge_r.c#reducevertices">qh_reducevertices</a>
+reduce vertex sets </li>
+<li><a href="merge_r.c#redundant_vertex">qh_redundant_vertex</a>
+returns true if detect and rename redundant
+vertex </li>
+<li><a href="merge_r.c#rename_sharedvertex">qh_rename_sharedvertex</a>
+detect and rename a shared vertex </li>
+<li><a href="merge_r.c#renameridgevertex">qh_renameridgevertex</a>
+rename oldvertex to newvertex in a ridge </li>
+<li><a href="merge_r.c#renamevertex">qh_renamevertex</a>
+rename oldvertex to newvertex in ridges </li>
+<li><a href="merge_r.c#remove_extravertices">qh_remove_extravertices</a>
+remove extra vertices in non-simplicial facets </li>
+</ul>
+
+<h3><a href="qh-merge_r.htm#TOC">&#187;</a><a name="mvertex">functions
+for identifying vertices for renaming</a></h3>
+<ul>
+<li><a href="merge_r.c#find_newvertex">qh_find_newvertex</a>
+locate new vertex for renaming old vertex </li>
+<li><a href="merge_r.c#hashridge">qh_hashridge</a> add
+ridge to hashtable </li>
+<li><a href="merge_r.c#hashridge_find">qh_hashridge_find</a>
+returns matching ridge in hashtable</li>
+<li><a href="merge_r.c#neighbor_intersections">qh_neighbor_intersections</a>
+return intersection of vertex sets for
+neighboring facets </li>
+<li><a href="merge_r.c#vertexridges">qh_vertexridges</a>
+return temporary set of ridges adjacent to a
+vertex </li>
+<li><a href="merge_r.c#vertexridges_facet">qh_vertexridges_facet</a>
+add adjacent ridges for a vertex in facet </li>
+</ul>
+
+<h3><a href="qh-merge_r.htm#TOC">&#187;</a><a name="mcheck">functions for check and
+trace</a></h3>
+<ul>
+<li><a href="merge_r.c#checkconnect">qh_checkconnect</a>
+check that new facets are connected </li>
+<li><a href="merge_r.c#tracemerge">qh_tracemerge</a>
+print trace message after merge </li>
+<li><a href="merge_r.c#tracemerging">qh_tracemerging</a>
+print trace message during post-merging </li>
+</ul>
+
+<p><!-- Navigation links --> </p>
+<hr>
+<p><b>Up:</b>
+<a href="http://www.qhull.org">Home page for
+Qhull</a> <br>
+<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual: Table of Contents</a> <br>
+<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a>
+&#149; <a href="../../html/qh-quick.htm#options">Options</a>
+&#149; <a href="../../html/qh-opto.htm#output">Output</a>
+&#149; <a href="../../html/qh-optf.htm#format">Formats</a>
+&#149; <a href="../../html/qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="../../html/qh-optp.htm#print">Print</a>
+&#149; <a href="../../html/qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="../../html/qh-optc.htm#prec">Precision</a>
+&#149; <a href="../../html/qh-optt.htm#trace">Trace</a>
+&#149; <a href="index.htm">Functions</a><br>
+<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a> <br>
+<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br>
+<b>To:</b> <a href="qh-geom_r.htm">Geom</a> &#149;
+<a href="qh-globa_r.htm">Global</a> &#149; <a href="qh-io_r.htm">Io</a>
+&#149; <a href="qh-mem_r.htm">Mem</a> &#149; <a href="qh-merge_r.htm">Merge</a>
+&#149; <a href="qh-poly_r.htm">Poly</a> &#149; <a href="qh-qhull_r.htm#TOC">Qhull</a>
+&#149; <a href="qh-set_r.htm">Set</a> &#149; <a href="qh-stat_r.htm">Stat</a>
+&#149; <a href="qh-user_r.htm">User</a><br>
+</p>
+<p><!-- GC common information --> </p>
+<hr>
+<p><a href="http://www.geom.uiuc.edu/"><img
+src="../../html/qh--geom.gif" align="middle" width="40" height="40"></a><i>The
+Geometry Center Home Page </i></p>
+<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a>
+</a><br>
+Created: May 2, 1997 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p>
+</body>
+</html>
diff --git a/xs/src/qhull/src/libqhull_r/qh-poly_r.htm b/xs/src/qhull/src/libqhull_r/qh-poly_r.htm
new file mode 100644
index 000000000..c5b6f2f83
--- /dev/null
+++ b/xs/src/qhull/src/libqhull_r/qh-poly_r.htm
@@ -0,0 +1,485 @@
+<!-- Do not edit with Front Page, it adds too many spaces -->
+<html>
+<head>
+<meta http-equiv="Content-Type"
+content="text/html; charset=iso-8859-1">
+<title>poly_r.c, poly2_r.c -- polyhedron operations</title>
+</head>
+
+<body>
+<!-- Navigation links -->
+<p><a name="TOP"><b>Up:</b></a> <a
+href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual</a>: Table of Contents <br>
+<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a>
+&#149; <a href="../../html/qh-quick.htm#options">Options</a>
+&#149; <a href="../../html/qh-opto.htm#output">Output</a>
+&#149; <a href="../../html/qh-optf.htm#format">Formats</a>
+&#149; <a href="../../html/qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="../../html/qh-optp.htm#print">Print</a>
+&#149; <a href="../../html/qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="../../html/qh-optc.htm#prec">Precision</a>
+&#149; <a href="../../html/qh-optt.htm#trace">Trace</a>
+&#149; <a href="index.htm">Functions</a><br>
+<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a><br>
+<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br>
+<b>To:</b> <a href="qh-geom_r.htm">Geom</a> &#149; <a href="qh-globa_r.htm">Global</a>
+&#149; <a href="qh-io_r.htm">Io</a> &#149; <a href="qh-mem_r.htm">Mem</a>
+&#149; <a href="qh-merge_r.htm">Merge</a> &#149; <a href="qh-poly_r.htm#TOC">Poly</a>
+&#149; <a href="qh-qhull_r.htm">Qhull</a> &#149; <a href="qh-set_r.htm">Set</a>
+&#149; <a href="qh-stat_r.htm">Stat</a> &#149; <a href="qh-user_r.htm">User</a>
+</p>
+<hr>
+
+<h2>poly_r.c, poly2_r.c -- polyhedron operations</h2>
+<blockquote>
+
+<p>Qhull uses dimension-free terminology. Qhull builds a
+polyhedron in dimension <em>d. </em>A <em>polyhedron</em> is a
+simplicial complex of faces with geometric information for the
+top and bottom-level faces. A (<em>d-1</em>)-face is a <em>facet</em>,
+a (<em>d-2</em>)-face is a <em>ridge</em>, and a <em>0</em>-face
+is a <em>vertex</em>. For example in 3-d, a facet is a polygon
+and a ridge is an edge. A facet is built from a ridge (the <em>base</em>)
+and a vertex (the <em>apex</em>). See
+<a href="../../html/index.htm#structure">Qhull's data structures</a>.</p>
+
+<p>Qhull's primary data structure is a polyhedron. A
+polyhedron is a list of facets. Each facet has a set of
+neighboring facets and a set of vertices. Each facet has a
+hyperplane. For example, a tetrahedron has four facets.
+If its vertices are <em>a, b, c, d</em>, and its facets
+are <em>1, 2, 3, 4,</em> the tetrahedron is </p>
+<blockquote>
+<ul>
+<li>facet 1 <ul>
+ <li>vertices: b c d </li>
+ <li>neighbors: 2 3 4 </li>
+</ul>
+</li>
+<li>facet 2 <ul>
+ <li>vertices: a c d </li>
+ <li>neighbors: 1 3 4 </li>
+</ul>
+</li>
+<li>facet 3 <ul>
+ <li>vertices: a b d </li>
+ <li>neighbors: 1 2 4 </li>
+</ul>
+</li>
+<li>facet 4 <ul>
+ <li>vertices: a b c </li>
+ <li>neighbors: 1 2 3 </li>
+</ul>
+</li>
+</ul>
+</blockquote>
+<p>A facet may be simplicial or non-simplicial. In 3-d, a
+<i>simplicial facet</i> has three vertices and three
+neighbors. A <i>nonsimplicial facet</i> has more than
+three vertices and more than three neighbors. A
+nonsimplicial facet has a set of ridges and a centrum. </p>
+<p>
+A simplicial facet has an orientation. An <i>orientation</i>
+is either <i>top</i> or <i>bottom</i>.
+The flag, <tt>facet-&gt;toporient,</tt>
+defines the orientation of the facet's vertices. For example in 3-d,
+'top' is left-handed orientation (i.e., the vertex order follows the direction
+of the left-hand fingers when the thumb is pointing away from the center).
+Except for axis-parallel facets in 5-d and higher, topological orientation
+determines the geometric orientation of the facet's hyperplane.
+
+<p>A nonsimplicial facet is due to merging two or more
+facets. The facet's ridge set determine a simplicial
+decomposition of the facet. Each ridge is a 1-face (i.e.,
+it has two vertices and two neighboring facets). The
+orientation of a ridge is determined by the order of the
+neighboring facets. The flag, <tt>facet-&gt;toporient,</tt>is
+ignored. </p>
+<p>A nonsimplicial facet has a centrum for testing
+convexity. A <i>centrum</i> is a point on the facet's
+hyperplane that is near the center of the facet. Except
+for large facets, it is the arithmetic average of the
+facet's vertices. </p>
+<p>A nonsimplicial facet is an approximation that is
+defined by offsets from the facet's hyperplane. When
+Qhull finishes, the <i>outer plane</i> is above all
+points while the <i>inner plane</i> is below the facet's
+vertices. This guarantees that any exact convex hull
+passes between the inner and outer planes. The outer
+plane is defined by <tt>facet-&gt;maxoutside</tt> while
+the inner plane is computed from the facet's vertices.</p>
+
+<p>Qhull 3.1 includes triangulation of non-simplicial facets
+('<a href="../../html/qh-optq.htm#Qt">Qt</a>').
+These facets,
+called <i>tricoplanar</i>, share the same normal. centrum, and Voronoi center.
+One facet (keepcentrum) owns these data structures.
+While tricoplanar facets are more accurate than the simplicial facets from
+joggled input, they
+may have zero area or flipped orientation.
+
+</blockquote>
+<p><b>Copyright &copy; 1995-2015 C.B. Barber</b></p>
+<hr>
+<p><a href="#TOP">&#187;</a> <a href="qh-geom_r.htm#TOC">Geom</a>
+<a name="TOC">&#149;</a> <a href="qh-globa_r.htm#TOC">Global</a>
+&#149; <a href="qh-io_r.htm#TOC">Io</a> &#149; <a href="qh-mem_r.htm#TOC">Mem</a>
+&#149; <a href="qh-merge_r.htm#TOC">Merge</a> &#149; <b>Poly</b>
+&#149; <a href="qh-qhull_r.htm#TOC">Qhull</a> &#149; <a href="qh-set_r.htm#TOC">Set</a>
+&#149; <a href="qh-stat_r.htm#TOC">Stat</a> &#149; <a href="qh-user_r.htm#TOC">User</a>
+</p>
+<h3>Index to <a href="poly_r.c">poly_r.c</a>,
+<a href="poly2_r.c">poly2_r.c</a>, <a href="poly_r.h">poly_r.h</a>,
+and <a href="libqhull_r.h">libqhull_r.h</a></h3>
+<ul>
+<li><a href="#ptype">Data types and global
+lists for polyhedrons</a> </li>
+<li><a href="#pconst">poly_r.h constants</a> </li>
+<li><a href="#pgall">Global FORALL macros</a> </li>
+<li><a href="#pall">FORALL macros</a> </li>
+<li><a href="#peach">FOREACH macros</a> </li>
+<li><a href="#pieach">Indexed FOREACH macros</a> </li>
+<li><a href="#pmacro">Other macros for polyhedrons</a><p>&nbsp;</li>
+<li><a href="#plist">Facetlist functions</a> </li>
+<li><a href="#pfacet">Facet functions</a> </li>
+<li><a href="#pvertex">Vertex, ridge, and point
+functions</a> </li>
+<li><a href="#phash">Hashtable functions</a> </li>
+<li><a href="#pnew">Allocation and deallocation
+functions</a> </li>
+<li><a href="#pcheck">Check functions</a> </li>
+</ul>
+<h3><a href="qh-poly_r.htm#TOC">&#187;</a><a name="ptype">Data
+types and global lists for polyhedrons</a></h3>
+<ul>
+<li><a href="libqhull_r.h#facetT">facetT</a> defines a
+facet </li>
+<li><a href="libqhull_r.h#ridgeT">ridgeT</a> defines a
+ridge </li>
+<li><a href="libqhull_r.h#vertexT">vertexT</a> defines a
+vertex </li>
+<li><a href="libqhull_r.h#qh-lists">qh facet and vertex
+lists</a> lists of facets and vertices </li>
+<li><a href="libqhull_r.h#qh-set">qh global sets</a>
+global sets for merging, hashing, input, etc. </li>
+</ul>
+<h3><a href="qh-poly_r.htm#TOC">&#187;</a><a name="pconst">poly_r.h constants</a></h3>
+<ul>
+<li><a href="poly_r.h#ALGORITHMfault">ALGORITHMfault</a>
+flag to not report errors in qh_checkconvex() </li>
+<li><a href="poly_r.h#DATAfault">DATAfault</a> flag to
+report errors in qh_checkconvex() </li>
+<li><a href="poly_r.h#DUPLICATEridge">DUPLICATEridge</a>
+special value for facet-&gt;neighbor to indicate
+a duplicate ridge </li>
+<li><a href="poly_r.h#MERGEridge">MERGEridge</a>
+special value for facet-&gt;neighbor to indicate
+a merged ridge </li>
+</ul>
+<h3><a href="qh-poly_r.htm#TOC">&#187;</a><a name="pgall">Global FORALL
+macros</a></h3>
+<ul>
+<li><a href="libqhull_r.h#FORALLfacets">FORALLfacets</a>
+assign 'facet' to each facet in qh.facet_list </li>
+<li><a href="poly_r.h#FORALLnew_facets">FORALLnew_facets</a>
+assign 'facet' to each facet in qh.newfacet_list </li>
+<li><a href="poly_r.h#FORALLvisible_facets">FORALLvisible_facets</a>
+assign 'visible' to each visible facet in
+qh.visible_list </li>
+<li><a href="libqhull_r.h#FORALLpoints">FORALLpoints</a>
+assign 'point' to each point in qh.first_point,
+qh.num_points </li>
+<li><a href="libqhull_r.h#FORALLvertices">FORALLvertices</a>
+assign 'vertex' to each vertex in qh.vertex_list </li>
+</ul>
+<h3><a href="qh-poly_r.htm#TOC">&#187;</a><a name="pall">FORALL macros</a></h3>
+<ul>
+<li><a href="poly_r.h#FORALLfacet_">FORALLfacet_</a>
+assign 'facet' to each facet in facetlist </li>
+<li><a href="libqhull_r.h#FORALLpoint_">FORALLpoint_</a>
+assign 'point' to each point in points array</li>
+<li><a href="poly_r.h#FORALLsame_">FORALLsame_</a>
+assign 'same' to each facet in samecycle</li>
+<li><a href="poly_r.h#FORALLsame_cycle_">FORALLsame_cycle_</a>
+assign 'same' to each facet in samecycle</li>
+<li><a href="poly_r.h#FORALLvertex_">FORALLvertex_</a>
+assign 'vertex' to each vertex in vertexlist </li>
+</ul>
+<h3><a href="qh-poly_r.htm#TOC">&#187;</a><a name="peach">FOREACH macros</a></h3>
+<ul>
+<li><a href="libqhull_r.h#FOREACHfacet_">FOREACHfacet_</a>
+assign 'facet' to each facet in facets </li>
+<li><a href="libqhull_r.h#FOREACHneighbor_">FOREACHneighbor_</a>
+assign 'neighbor' to each facet in
+facet-&gt;neighbors or vertex-&gt;neighbors</li>
+<li><a href="poly_r.h#FOREACHnewfacet_">FOREACHnewfacet_</a>
+assign 'newfacet' to each facet in facet set </li>
+<li><a href="libqhull_r.h#FOREACHpoint_">FOREACHpoint_</a>
+assign 'point' to each point in points set </li>
+<li><a href="libqhull_r.h#FOREACHridge_">FOREACHridge_</a>
+assign 'ridge' to each ridge in ridge set </li>
+<li><a href="libqhull_r.h#FOREACHvertex_">FOREACHvertex_</a>
+assign 'vertex' to each vertex in vertex set </li>
+<li><a href="poly_r.h#FOREACHvertexA_">FOREACHvertexA_</a>
+assign 'vertexA' to each vertex in vertex set</li>
+<li><a href="poly_r.h#FOREACHvisible_">FOREACHvisible_</a>
+assign 'visible' to each facet in facet set </li>
+</ul>
+<h3><a href="qh-poly_r.htm#TOC">&#187;</a><a name="pieach">Indexed
+FOREACH macros</a></h3>
+<ul>
+<li><a href="libqhull_r.h#FOREACHfacet_i_">FOREACHfacet_i_</a>
+assign 'facet' and 'facet_i' to each facet in
+facet set </li>
+<li><a href="libqhull_r.h#FOREACHneighbor_i_">FOREACHneighbor_i_</a>
+assign 'neighbor' and 'neighbor_i' to each facet
+in facet-&gt;neighbors or vertex-&gt;neighbors</li>
+<li><a href="libqhull_r.h#FOREACHpoint_i_">FOREACHpoint_i_</a>
+assign 'point' and 'point_i' to each point in
+points set </li>
+<li><a href="libqhull_r.h#FOREACHridge_i_">FOREACHridge_i_</a>
+assign 'ridge' and 'ridge_i' to each ridge in
+ridges set </li>
+<li><a href="libqhull_r.h#FOREACHvertex_i_">FOREACHvertex_i_</a>
+assign 'vertex' and 'vertex_i' to each vertex in
+vertices set </li>
+<li><a href="poly_r.h#FOREACHvertexreverse12_">FOREACHvertexreverse12_</a>
+assign 'vertex' to each vertex in vertex set;
+reverse the order of first two vertices </li>
+</ul>
+<h3><a href="qh-poly_r.htm#TOC">&#187;</a><a name="pmacro">Other macros for polyhedrons</a></h3>
+<ul>
+<li><a href="libqhull_r.h#getid_">getid_</a> return ID for
+a facet, ridge, or vertex </li>
+<li><a href="libqhull_r.h#otherfacet_">otherfacet_</a>
+return neighboring facet for a ridge in a facet </li>
+</ul>
+<h3><a href="qh-poly_r.htm#TOC">&#187;</a><a name="plist">Facetlist
+functions</a></h3>
+<ul>
+<li><a href="poly_r.c#appendfacet">qh_appendfacet</a>
+appends facet to end of qh.facet_list</li>
+<li><a href="poly_r.c#attachnewfacets">qh_attachnewfacets</a>
+attach new facets in qh.newfacet_list to the
+horizon </li>
+<li><a href="poly2_r.c#findgood">qh_findgood</a>
+identify good facets for qh.PRINTgood </li>
+<li><a href="poly2_r.c#findgood_all">qh_findgood_all</a>
+identify more good facets for qh.PRINTgood </li>
+<li><a href="poly2_r.c#furthestnext">qh_furthestnext</a>
+move facet with furthest of furthest points to
+facet_next </li>
+<li><a href="poly2_r.c#initialhull">qh_initialhull</a>
+construct the initial hull as a simplex of
+vertices </li>
+<li><a href="poly2_r.c#nearcoplanar">qh_nearcoplanar</a>
+ remove near-inside points from coplanar sets</li>
+<li><a href="poly2_r.c#prependfacet">qh_prependfacet</a>
+prepends facet to start of facetlist </li>
+<li><a href="user_r.c#printfacetlist">qh_printfacetlist</a>
+print facets in a facetlist</li>
+<li><a href="poly2_r.c#printlists">qh_printlists</a>
+print out facet list for debugging </li>
+<li><a href="poly_r.c#removefacet">qh_removefacet</a>
+unlinks facet from qh.facet_list</li>
+<li><a href="poly2_r.c#resetlists">qh_resetlists</a>
+reset qh.newvertex_list, qh.newfacet_list, and
+qh.visible_list </li>
+</ul>
+<h3><a href="qh-poly_r.htm#TOC">&#187;</a><a name="pfacet">Facet
+functions</a></h3>
+<ul>
+<li><a href="poly2_r.c#createsimplex">qh_createsimplex</a>
+create a simplex of facets from a set of vertices
+</li>
+<li><a href="poly2_r.c#findbestlower">qh_findbestlower</a> find best
+non-upper, non-flipped facet for point at upperfacet</li>
+<li><a href="poly2_r.c#furthestout">qh_furthestout</a>
+make furthest outside point the last point of a
+facet's outside set </li>
+<li><a href="poly_r.c#makenew_nonsimplicial">qh_makenew_nonsimplicial</a>
+make new facets from ridges of visible facets </li>
+<li><a href="poly_r.c#makenew_simplicial">qh_makenew_simplicial</a>
+make new facets for horizon neighbors </li>
+<li><a href="poly_r.c#makenewfacet">qh_makenewfacet</a>
+create a facet from vertices and apex </li>
+<li><a href="poly2_r.c#makenewfacets">qh_makenewfacets</a>
+make new facets from vertex, horizon facets, and
+visible facets </li>
+<li><a href="poly_r.c#makenewplanes">qh_makenewplanes</a>
+make new hyperplanes for facets </li>
+<li><a href="poly2_r.c#outcoplanar">qh_outcoplanar</a>
+move points from outside set to coplanar set </li>
+<li><a href="poly2_r.c#setvoronoi_all">qh_setvoronoi_all</a>
+compute Voronoi centers for all facets </li>
+<li><a href="poly2_r.c#triangulate">qh_triangulate</a>
+triangulate non-simplicial facets</li>
+<li><a href="poly2_r.c#triangulate_facet">qh_triangulate_facet</a>
+triangulate a non-simplicial facet</li>
+<li><a href="poly2_r.c#triangulate_link">qh_triangulate_link</a>
+link facets together from qh_triangulate</li>
+<li><a href="poly2_r.c#triangulate_mirror">qh_triangulate_mirror</a>
+delete mirrored facets from qh_triangulate</li>
+<li><a href="poly2_r.c#triangulate_null">qh_triangulate_null</a>
+delete null facet from qh_triangulate</li>
+</ul>
+<h3><a href="qh-poly_r.htm#TOC">&#187;</a><a name="pvertex">Vertex,
+ridge, and point functions</a></h3>
+<ul>
+<li><a href="poly_r.c#appendvertex">qh_appendvertex</a>
+append vertex to end of qh.vertex_list, </li>
+<li><a href="io_r.c#detvridge">qh_detvridge</a> determine Voronoi
+ridge for an input site
+<li><a href="io_r.c#detvridge3">qh_detvridge3</a> determine 3-d Voronoi
+ridge for an input site
+<li><a href="poly2_r.c#facet3vertex">qh_facet3vertex</a>
+return an oriented vertex set for a 3-d facet </li>
+<li><a href="poly_r.c#facetintersect">qh_facetintersect</a>
+return intersection of simplicial facets </li>
+<li><a href="poly2_r.c#initialvertices">qh_initialvertices</a>
+return non-singular set of initial vertices </li>
+<li><a href="poly2_r.c#isvertex">qh_isvertex</a> true
+if point is in a vertex set </li>
+<li><a href="poly2_r.c#nearvertex">qh_nearvertex</a>
+return nearest vertex to point </li>
+<li><a href="poly2_r.c#nextridge3d">qh_nextridge3d</a>
+iterate over each ridge and vertex for a 3-d
+facet </li>
+<li><a href="poly2_r.c#point">qh_point</a> return point
+for a point ID </li>
+<li><a href="poly2_r.c#pointfacet">qh_pointfacet</a>
+return temporary set of facets indexed by point
+ID </li>
+<li><a href="poly_r.c#pointid">qh_pointid</a> return ID
+for a point</li>
+<li><a href="poly2_r.c#pointvertex">qh_pointvertex</a>
+return temporary set of vertices indexed by point
+ID </li>
+<li><a href="poly_r.c#removevertex">qh_removevertex</a>
+unlink vertex from qh.vertex_list, </li>
+<li><a href="poly_r.c#updatevertices">qh_updatevertices</a>
+update vertex neighbors and delete interior
+vertices </li>
+<li><a href="poly2_r.c#vertexintersect">qh_vertexintersect</a>
+intersect two vertex sets </li>
+<li><a href="poly2_r.c#vertexintersect_new">qh_vertexintersect_new</a>
+return intersection of two vertex sets </li>
+<li><a href="poly2_r.c#vertexneighbors">qh_vertexneighbors</a>
+for each vertex in hull, determine facet
+neighbors </li>
+<li><a href="poly2_r.c#vertexsubset">qh_vertexsubset</a>
+returns True if vertexsetA is a subset of
+vertexsetB </li>
+</ul>
+<h3><a href="qh-poly_r.htm#TOC">&#187;</a><a name="phash">Hashtable functions</a></h3>
+<ul>
+<li><a href="poly2_r.c#addhash">qh_addhash</a> add hash
+element to linear hash table</li>
+<li><a href="poly_r.c#gethash">qh_gethash</a> return
+hash value for a set</li>
+<li><a href="poly2_r.c#matchduplicates">qh_matchduplicates</a>
+match duplicate ridges in hash table </li>
+<li><a href="poly_r.c#matchneighbor">qh_matchneighbor</a>
+try to match subridge of new facet with a
+neighbor </li>
+<li><a href="poly_r.c#matchnewfacets">qh_matchnewfacets</a>
+match new facets with their new facet neighbors </li>
+<li><a href="poly_r.c#matchvertices">qh_matchvertices</a>
+tests whether a facet and hash entry match at a
+ridge </li>
+<li><a href="poly2_r.c#newhashtable">qh_newhashtable</a>
+allocate a new qh_r.hash_table </li>
+<li><a href="poly2_r.c#printhashtable">qh_printhashtable</a>
+print hash table </li>
+</ul>
+<h3><a href="qh-poly_r.htm#TOC">&#187;</a><a name="pnew">Allocation and
+deallocation functions</a></h3>
+<ul>
+<li><a href="poly2_r.c#clearcenters">qh_clearcenters</a>
+clear old data from facet-&gt;center </li>
+<li><a href="poly_r.c#deletevisible">qh_deletevisible</a>
+delete visible facets and vertices </li>
+<li><a href="poly_r.c#delfacet">qh_delfacet</a> free up
+the memory occupied by a facet </li>
+<li><a href="poly2_r.c#delridge">qh_delridge</a> delete
+ridge</li>
+<li><a href="poly2_r.c#delvertex">qh_delvertex</a>
+delete vertex </li>
+<li><a href="poly_r.c#newfacet">qh_newfacet</a> create
+and allocate space for a facet </li>
+<li><a href="poly_r.c#newridge">qh_newridge</a> create
+and allocate space for a ridge </li>
+<li><a href="poly2_r.c#newvertex">qh_newvertex</a>
+create and allocate space for a vertex </li>
+</ul>
+<h3><a href="qh-poly_r.htm#TOC">&#187;</a><a name="pcheck">Check
+functions</a></h3>
+<ul>
+<li><a href="poly2_r.c#check_bestdist">qh_check_bestdist</a>
+check that points are not outside of facets </li>
+<li><a href="poly2_r.c#check_dupridge">qh_check_dupridge</a>
+check duplicate ridge between facet1 and facet2 for wide merge </li>
+<li><a href="poly2_r.c#check_maxout">qh_check_maxout</a>
+updates qh.max_outside and checks all points
+against bestfacet </li>
+<li><a href="poly2_r.c#check_output">qh_check_output</a>
+check topological and geometric output</li>
+<li><a href="poly2_r.c#check_point">qh_check_point</a>
+check that point is not outside of facet </li>
+<li><a href="poly2_r.c#check_points">qh_check_points</a>
+check that all points are inside all facets </li>
+<li><a href="poly2_r.c#checkconvex">qh_checkconvex</a>
+check that each ridge in facetlist is convex </li>
+<li><a href="poly2_r.c#checkfacet">qh_checkfacet</a>
+check for consistency errors in facet </li>
+<li><a href="poly_r.c#checkflipped">qh_checkflipped</a>
+check facet orientation to the interior point </li>
+<li><a href="poly2_r.c#checkflipped_all">qh_checkflipped_all</a>
+check facet orientation for a facet list </li>
+<li><a href="poly2_r.c#checkpolygon">qh_checkpolygon</a>
+check topological structure </li>
+<li><a href="poly2_r.c#checkvertex">qh_checkvertex</a>
+check vertex for consistency </li>
+<li><a href="poly2_r.c#infiniteloop">qh_infiniteloop</a>
+report error for a loop of facets </li>
+<li><a href="poly2_r.c#printlists">qh_printlists</a>
+print out facet list for debugging </li>
+</ul>
+
+
+<p><!-- Navigation links --> </p>
+<hr>
+<p><b>Up:</b>
+<a href="http://www.qhull.org">Home page for
+Qhull</a> <br>
+<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual: Table of Contents</a> <br>
+<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a>
+&#149; <a href="../../html/qh-quick.htm#options">Options</a>
+&#149; <a href="../../html/qh-opto.htm#output">Output</a>
+&#149; <a href="../../html/qh-optf.htm#format">Formats</a>
+&#149; <a href="../../html/qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="../../html/qh-optp.htm#print">Print</a>
+&#149; <a href="../../html/qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="../../html/qh-optc.htm#prec">Precision</a>
+&#149; <a href="../../html/qh-optt.htm#trace">Trace</a>
+&#149; <a href="index.htm">Functions</a><br>
+<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a> <br>
+<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br>
+<b>To:</b> <a href="qh-geom_r.htm">Geom</a> &#149;
+<a href="qh-globa_r.htm">Global</a> &#149; <a href="qh-io_r.htm">Io</a>
+&#149; <a href="qh-mem_r.htm">Mem</a> &#149; <a href="qh-merge_r.htm">Merge</a>
+&#149; <a href="qh-poly_r.htm">Poly</a> &#149; <a href="qh-qhull_r.htm#TOC">Qhull</a>
+&#149; <a href="qh-set_r.htm">Set</a> &#149; <a href="qh-stat_r.htm">Stat</a>
+&#149; <a href="qh-user_r.htm">User</a><br>
+</p>
+<p><!-- GC common information --> </p>
+<hr>
+<p><a href="http://www.geom.uiuc.edu/"><img
+src="../../html/qh--geom.gif" align="middle" width="40" height="40"></a><i>The
+Geometry Center Home Page </i></p>
+<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a>
+</a><br>
+Created: May 2, 1997 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p>
+</body>
+</html>
diff --git a/xs/src/qhull/src/libqhull_r/qh-qhull_r.htm b/xs/src/qhull/src/libqhull_r/qh-qhull_r.htm
new file mode 100644
index 000000000..25d5e4972
--- /dev/null
+++ b/xs/src/qhull/src/libqhull_r/qh-qhull_r.htm
@@ -0,0 +1,279 @@
+<!-- Do not edit with Front Page, it adds too many spaces -->
+<html>
+<head>
+<meta http-equiv="Content-Type"
+content="text/html; charset=iso-8859-1">
+<title>libqhull_r.c -- top-level functions and basic data types</title>
+</head>
+
+<body>
+<!-- Navigation links -->
+<p><a name="TOP"><b>Up:</b></a> <a
+href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual</a>: Table of Contents <br>
+<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a>
+&#149; <a href="../../html/qh-quick.htm#options">Options</a>
+&#149; <a href="../../html/qh-opto.htm#output">Output</a>
+&#149; <a href="../../html/qh-optf.htm#format">Formats</a>
+&#149; <a href="../../html/qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="../../html/qh-optp.htm#print">Print</a>
+&#149; <a href="../../html/qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="../../html/qh-optc.htm#prec">Precision</a>
+&#149; <a href="../../html/qh-optt.htm#trace">Trace</a>
+&#149; <a href="index.htm">Functions</a><br>
+<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a><br>
+<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br>
+<b>To:</b> <a href="qh-geom_r.htm">Geom</a> &#149; <a href="qh-globa_r.htm">Global</a>
+&#149; <a href="qh-io_r.htm">Io</a> &#149; <a href="qh-mem_r.htm">Mem</a>
+&#149; <a href="qh-merge_r.htm">Merge</a> &#149; <a href="qh-poly_r.htm">Poly</a>
+&#149; <a href="qh-qhull_r.htm#TOC">Qhull</a> &#149; <a href="qh-set_r.htm">Set</a>
+&#149; <a href="qh-stat_r.htm">Stat</a> &#149; <a href="qh-user_r.htm">User</a>
+</p>
+<hr>
+
+<h2>libqhull_r.c -- top-level functions and basic data types</h2>
+<blockquote>
+<p>Qhull implements the Quickhull algorithm for computing
+the convex hull. The Quickhull algorithm combines two
+well-known algorithms: the 2-d quickhull algorithm and
+the n-d beneath-beyond algorithm. See
+<a href="../../html/index.htm#description">Description of Qhull</a>. </p>
+<p>This section provides an index to the top-level
+functions and base data types. The top-level header file, <tt>libqhull_r.h</tt>,
+contains prototypes for these functions.</p>
+</blockquote>
+<p><b>Copyright &copy; 1995-2015 C.B. Barber</b></p>
+<hr>
+<p><a href="#TOP">&#187;</a> <a href="qh-geom_r.htm#TOC">Geom</a>
+<a name="TOC">&#149;</a> <a href="qh-globa_r.htm#TOC">Global</a>
+&#149; <a href="qh-io_r.htm#TOC">Io</a> &#149; <a href="qh-mem_r.htm#TOC">Mem</a>
+&#149; <a href="qh-merge_r.htm#TOC">Merge</a> &#149; <a href="qh-poly_r.htm#TOC">Poly</a>
+&#149; <b>Qhull</b> &#149; <a href="qh-set_r.htm#TOC">Set</a>
+&#149; <a href="qh-stat_r.htm#TOC">Stat</a> &#149; <a href="qh-user_r.htm#TOC">User</a>
+</p>
+<h3>Index to <a href="libqhull_r.c">libqhull_r.c</a>,
+<a href="libqhull_r.h">libqhull_r.h</a>, and
+<a href="../qhull/unix_r.c">unix_r.c</a></h3>
+<ul>
+<li><a href="#qtype">libqhull_r.h and unix_r.c data types and
+constants</a> </li>
+<li><a href="#qmacro">libqhull_r.h other macros</a> </li>
+<li><a href="#qfunc">Quickhull routines in call order</a> </li>
+<li><a href="#qinit">Top-level routines for initializing and terminating Qhull</a></li>
+<li><a href="#qin">Top-level routines for reading and modifying the input</a></li>
+<li><a href="#qcall">Top-level routines for calling Qhull</a></li>
+<li><a href="#qout">Top-level routines for returning results</a></li>
+<li><a href="#qtest">Top-level routines for testing and debugging</a></li>
+</ul>
+
+<h3><a href="qh-qhull_r.htm#TOC">&#187;</a><a name="qtype">libqhull_r.h and unix_r.c
+data types and constants</a></h3>
+<ul>
+<li><a href="libqhull_r.h#flagT">flagT</a> Boolean flag as
+a bit </li>
+<li><a href="libqhull_r.h#boolT">boolT</a> boolean value,
+either True or False </li>
+<li><a href="libqhull_r.h#CENTERtype">CENTERtype</a> to
+distinguish facet-&gt;center </li>
+<li><a href="libqhull_r.h#qh_PRINT">qh_PRINT</a> output
+formats for printing (qh.PRINTout) </li>
+<li><a href="libqhull_r.h#qh_ALL">qh_ALL</a> argument flag
+for selecting everything </li>
+<li><a href="libqhull_r.h#qh_ERR">qh_ERR</a> Qhull exit
+codes for indicating errors </li>
+<li><a href="libqhull_r.h#qh_FILEstderr">qh_FILEstderr</a> Fake stderr
+to distinguish error output from normal output [C++ only]</li>
+<li><a href="../qhull/unix_r.c#prompt">qh_prompt</a> version and long prompt for Qhull</li>
+<li><a href="../qhull/unix_r.c#prompt2">qh_prompt2</a> synopsis for Qhull</li>
+<li><a href="../qhull/unix_r.c#prompt3">qh_prompt3</a> concise prompt for Qhull</li>
+<li><a href="global_r.c#qh_version">qh_version</a> version stamp</li>
+</ul>
+
+<h3><a href="qh-qhull_r.htm#TOC">&#187;</a><a name="qmacro">libqhull_r.h other
+macros</a></h3>
+<ul>
+<li><a href="qhull_ra.h#traceN">traceN</a> print trace
+message if <em>qh.IStracing &gt;= N</em>. </li>
+<li><a href="qhull_ra.h#QHULL_UNUSED">QHULL_UNUSED</a> declare an
+ unused variable to avoid warnings. </li>
+</ul>
+
+<h3><a href="qh-qhull_r.htm#TOC">&#187;</a><a name="qfunc">Quickhull
+routines in call order</a></h3>
+<ul>
+<li><a href="../qhull/unix_r.c#main">main</a> processes the
+command line, calls qhull() to do the work, and
+exits </li>
+<li><a href="libqhull_r.c#qhull">qh_qhull</a> construct
+the convex hull of a set of points </li>
+<li><a href="libqhull_r.c#build_withrestart">qh_build_withrestart</a>
+allow restarts while calling qh_buildhull</li>
+<li><a href="poly2_r.c#initbuild">qh_initbuild</a>
+initialize hull and outside sets with point array</li>
+<li><a href="libqhull_r.c#partitionall">qh_partitionall</a>
+partition all points into outside sets </li>
+<li><a href="libqhull_r.c#buildhull">qh_buildhull</a>
+construct a convex hull by adding points one at a
+time </li>
+<li><a href="libqhull_r.c#nextfurthest">qh_nextfurthest</a>
+return next furthest point for processing </li>
+<li><a href="libqhull_r.c#buildtracing">qh_buildtracing</a>
+trace an iteration of buildhull </li>
+<li><a href="libqhull_r.c#addpoint">qh_addpoint</a> add a
+point to the convex hull </li>
+<li><a href="libqhull_r.c#findhorizon">qh_findhorizon</a>
+find the horizon and visible facets for a point </li>
+<li><a href="libqhull_r.c#partitionvisible">qh_partitionvisible</a>
+partition points from facets in qh.visible_list
+to facets in qh.newfacet_list </li>
+<li><a href="libqhull_r.c#partitionpoint">qh_partitionpoint</a>
+partition a point as inside, coplanar with, or
+outside a facet </li>
+<li><a href="libqhull_r.c#partitioncoplanar">qh_partitioncoplanar</a>
+partition coplanar point into a facet </li>
+<li><a href="libqhull_r.c#precision">qh_precision</a> restart on precision errors if not merging and if 'QJn'</li>
+</ul>
+
+<h3><a href="qh-qhull_r.htm#TOC">&#187;</a><a name="qinit">Top-level routines for initializing and terminating Qhull (in other modules)</a></h3>
+<ul>
+<li><a href="global_r.c#freebuild">qh_freebuild</a>
+free memory used by qh_initbuild and qh_buildhull
+</li>
+<li><a href="global_r.c#checkflags">qh_checkflags</a>
+check flags for multiple frontends to qhull
+<li><a href="global_r.c#freeqhull">qh_freeqhull</a>
+free memory used by qhull </li>
+<li><a href="global_r.c#init_A">qh_init_A</a> called
+before error handling initialized </li>
+<li><a href="global_r.c#init_B">qh_init_B</a> called
+after points are defined </li>
+<li><a href="global_r.c#initflags">qh_initflags</a> set
+flags and constants from command line </li>
+<li><a href="rboxlib_r.c#rboxpoints">qh_rboxpoints</a>
+generate points for qhull </li>
+<li><a href="global_r.c#restore_qhull">qh_restore_qhull</a>
+restores a saved qhull </li>
+<li><a href="global_r.c#save_qhull">qh_save_qhull</a>
+saves qhull for later restoring </li>
+<li><a href="user_r.c#user_memsizes">qh_user_memsizes</a>
+define additional quick allocation sizes
+</li>
+</ul>
+
+<h3><a href="qh-qhull_r.htm#TOC">&#187;</a><a name="qin">Top-level routines for reading and modifying the input (in other modules)</a></h3>
+<ul>
+<li><a href="geom2_r.c#gram_schmidt">qh_gram_schmidt</a>
+implements Gram-Schmidt orthogonalization by rows </li>
+<li><a href="geom2_r.c#projectinput">qh_projectinput</a>
+project input along one or more dimensions +
+Delaunay projection </li>
+<li><a href="geom2_r.c#randommatrix">qh_randommatrix</a>
+generate a random dimXdim matrix in range (-1,1) </li>
+<li><a href="io_r.c#readpoints">qh_readpoints</a> read
+points from input </li>
+<li><a href="geom2_r.c#rotateinput">qh_rotateinput</a> rotate
+input points using row matrix </li>
+<li><a href="geom2_r.c#scaleinput">qh_scaleinput</a> scale
+input points using qh low_bound/high_bound </li>
+<li><a href="geom2_r.c#setdelaunay">qh_setdelaunay</a> project
+points to paraboloid for Delaunay triangulation </li>
+<li><a href="geom2_r.c#sethalfspace_all">qh_sethalfspace_all</a>
+generate dual for halfspace intersection with interior
+point </li>
+</ul>
+
+<h3><a href="qh-qhull_r.htm#TOC">&#187;</a><a name="qcall">Top-level routines for calling Qhull (in other modules)</a></h3>
+<ul>
+<li><a href="libqhull_r.c#addpoint">qh_addpoint</a> add
+point to convex hull </li>
+<li><a href="poly2_r.c#findbestfacet">qh_findbestfacet</a>
+find facet that is furthest below a point </li>
+<li><a href="poly2_r.c#findfacet_all">qh_findfacet_all</a>
+exhaustive search for facet below a point </li>
+<li><a href="libqhull_r.c#qhull">qh_qhull</a> construct
+the convex hull of a set of points </li>
+</ul>
+
+<h3><a href="qh-qhull_r.htm#TOC">&#187;</a><a name="qout">Top-level routines for returning results (in other modules)</a></h3>
+<ul>
+<li><a href="stat_r.c#collectstatistics">qh_collectstatistics</a>
+collect statistics for qh.facet_list </li>
+<li><a href="poly2_r.c#nearvertex">qh_nearvertex</a>
+return nearest vertex to point </li>
+<li><a href="poly2_r.c#point">qh_point</a> return point
+for a point ID </li>
+<li><a href="poly2_r.c#pointfacet">qh_pointfacet</a>
+return temporary set of facets indexed by point
+ID </li>
+<li><a href="poly_r.c#pointid">qh_pointid</a> return ID
+for a point</li>
+<li><a href="poly2_r.c#pointvertex">qh_pointvertex</a>
+return vertices (if any) for all points</li>
+<li><a href="stat_r.c#printallstatistics">qh_printallstatistics</a>
+print all statistics </li>
+<li><a href="io_r.c#printneighborhood">qh_printneighborhood</a>
+print neighborhood of one or two facets </li>
+<li><a href="libqhull_r.c#printsummary">qh_printsummary</a>
+print summary </li>
+<li><a href="io_r.c#produce_output">qh_produce_output</a>
+print the results of qh_qhull() </li>
+<li><a href="poly2_r.c#setvoronoi_all">qh_setvoronoi_all</a>
+compute Voronoi centers for all facets </li>
+</ul>
+
+<h3><a href="qh-qhull_r.htm#TOC">&#187;</a><a name="qtest">Top-level routines for testing and debugging (in other modules)</a></h3>
+<ul>
+<li><a href="io_r.c#dfacet">dfacet</a> print facet by
+ID from debugger </li>
+<li><a href="io_r.c#dvertex">dvertex</a> print vertex
+by ID from debugger </li>
+<li><a href="poly2_r.c#check_output">qh_check_output</a>
+check output </li>
+<li><a href="poly2_r.c#check_points">qh_check_points</a>
+verify that all points are inside the convex hull
+</li>
+<li><a href="user_r.c#errexit">qh_errexit</a> report
+error with a facet and a ridge</li>
+<li><a href="libqhull_r.c#errexit2">qh_errexit2</a> report
+error with two facets </li>
+<li><a href="user_r.c#errprint">qh_errprint</a> print
+erroneous facets, ridge, and vertex </li>
+<li><a href="user_r.c#printfacetlist">qh_printfacetlist</a>
+print all fields for a list of facets </li>
+</ul>
+
+<p><!-- Navigation links --> </p>
+<hr>
+<p><b>Up:</b>
+<a href="http://www.qhull.org">Home page for
+Qhull</a> <br>
+<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual: Table of Contents</a> <br>
+<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a>
+&#149; <a href="../../html/qh-quick.htm#options">Options</a>
+&#149; <a href="../../html/qh-opto.htm#output">Output</a>
+&#149; <a href="../../html/qh-optf.htm#format">Formats</a>
+&#149; <a href="../../html/qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="../../html/qh-optp.htm#print">Print</a>
+&#149; <a href="../../html/qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="../../html/qh-optc.htm#prec">Precision</a>
+&#149; <a href="../../html/qh-optt.htm#trace">Trace</a>
+&#149; <a href="index.htm">Functions</a><br>
+<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a> <br>
+<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br>
+<b>To:</b> <a href="qh-geom_r.htm">Geom</a> &#149;
+<a href="qh-globa_r.htm">Global</a> &#149; <a href="qh-io_r.htm">Io</a>
+&#149; <a href="qh-mem_r.htm">Mem</a> &#149; <a href="qh-merge_r.htm">Merge</a>
+&#149; <a href="qh-poly_r.htm">Poly</a> &#149; <a href="qh-qhull_r.htm#TOC">Qhull</a>
+&#149; <a href="qh-set_r.htm">Set</a> &#149; <a href="qh-stat_r.htm">Stat</a>
+&#149; <a href="qh-user_r.htm">User</a><br>
+</p>
+<p><!-- GC common information --> </p>
+<hr>
+<p><a href="http://www.geom.uiuc.edu/"><img
+src="../../html/qh--geom.gif" align="middle" width="40" height="40"></a><i>The
+Geometry Center Home Page </i></p>
+<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a>
+</a><br>
+Created: May 2, 1997 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p>
+</body>
+</html>
diff --git a/xs/src/qhull/src/libqhull_r/qh-set_r.htm b/xs/src/qhull/src/libqhull_r/qh-set_r.htm
new file mode 100644
index 000000000..cf8ab63af
--- /dev/null
+++ b/xs/src/qhull/src/libqhull_r/qh-set_r.htm
@@ -0,0 +1,308 @@
+<!-- Do not edit with Front Page, it adds too many spaces -->
+<html>
+<head>
+<meta http-equiv="Content-Type"
+content="text/html; charset=iso-8859-1">
+<title>qset_r.c -- set data type and operations</title>
+</head>
+
+<body>
+<!-- Navigation links -->
+<p><a name="TOP"><b>Up:</b></a> <a
+href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual</a>: Table of Contents <br>
+<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a>
+&#149; <a href="../../html/qh-quick.htm#options">Options</a>
+&#149; <a href="../../html/qh-opto.htm#output">Output</a>
+&#149; <a href="../../html/qh-optf.htm#format">Formats</a>
+&#149; <a href="../../html/qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="../../html/qh-optp.htm#print">Print</a>
+&#149; <a href="../../html/qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="../../html/qh-optc.htm#prec">Precision</a>
+&#149; <a href="../../html/qh-optt.htm#trace">Trace</a>
+&#149; <a href="index.htm">Functions</a><br>
+<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a><br>
+<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br>
+<b>To:</b> <a href="qh-geom_r.htm">Geom</a> &#149; <a href="qh-globa_r.htm">Global</a>
+&#149; <a href="qh-io_r.htm">Io</a> &#149; <a href="qh-mem_r.htm">Mem</a>
+&#149; <a href="qh-merge_r.htm">Merge</a> &#149; <a href="qh-poly_r.htm">Poly</a>
+&#149; <a href="qh-qhull_r.htm">Qhull</a> &#149; <a href="qh-set_r.htm#TOC">Set</a>
+&#149; <a href="qh-stat_r.htm">Stat</a> &#149; <a href="qh-user_r.htm">User</a>
+</p>
+<hr>
+
+<h2>qset_r.c -- set data type and operations</h2>
+<blockquote>
+<p>Qhull's data structures are constructed from sets. The
+functions and macros in qset_r.c construct, iterate, and
+modify these sets. They are the most frequently called
+functions in Qhull. For this reason, efficiency is the
+primary concern. </p>
+<p>In Qhull, a <i>set</i> is represented by an unordered
+array of pointers with a maximum size and a NULL
+terminator (<a href="qset_r.h#setT">setT</a>).
+Most sets correspond to mathematical sets
+(i.e., the pointers are unique). Some sets are sorted to
+enforce uniqueness. Some sets are ordered. For example,
+the order of vertices in a ridge determine the ridge's
+orientation. If you reverse the order of adjacent
+vertices, the orientation reverses. Some sets are not
+mathematical sets. They may be indexed as an array and
+they may include NULL pointers. </p>
+<p>The most common operation on a set is to iterate its
+members. This is done with a 'FOREACH...' macro. Each set
+has a custom macro. For example, 'FOREACHvertex_'
+iterates over a set of vertices. Each vertex is assigned
+to the variable 'vertex' from the pointer 'vertexp'. </p>
+<p>Most sets are constructed by appending elements to the
+set. The last element of a set is either NULL or the
+index of the terminating NULL for a partially full set.
+If a set is full, appending an element copies the set to
+a larger array. </p>
+
+</blockquote>
+<p><b>Copyright &copy; 1995-2015 C.B. Barber</b></p>
+<hr>
+<p><a href="#TOP">&#187;</a> <a href="qh-geom_r.htm#TOC">Geom</a>
+<a name="TOC">&#149;</a> <a href="qh-globa_r.htm#TOC">Global</a> &#149;
+<a href="qh-io_r.htm#TOC">Io</a> &#149; <a href="qh-mem_r.htm#TOC">Mem</a> &#149;
+<a href="qh-merge_r.htm#TOC">Merge</a> &#149; <a href="qh-poly_r.htm#TOC">Poly</a>
+&#149; <a href="qh-qhull_r.htm#TOC">Qhull</a> &#149; <b>Set</b>
+&#149; <a href="qh-stat_r.htm#TOC">Stat</a> &#149; <a href="qh-user_r.htm#TOC">User</a>
+</p>
+<h3>Index to <a href="qset_r.c">qset_r.c</a> and
+<a href="qset_r.h">qset_r.h</a></h3>
+<ul>
+<li><a href="#stype">Data types and constants</a> </li>
+<li><a href="#seach">FOREACH macros</a> </li>
+<li><a href="#saccess">access and size macros</a> </li>
+<li><a href="#sint">internal macros</a> </li>
+<li><a href="#saddr">address macros</a><p>&nbsp;</li>
+
+<li><a href="#snew">Allocation and deallocation functions</a> </li>
+<li><a href="#spred">Access and predicate functions</a>
+</li>
+<li><a href="#sadd">Add functions</a> </li>
+<li><a href="#scheck">Check and print functions</a></li>
+<li><a href="#scopy">Copy, compact, and zero functions</a></li>
+<li><a href="#sdel">Delete functions</a> </li>
+<li><a href="#stemp">Temporary set functions</a> </li>
+</ul>
+<h3><a href="qh-set_r.htm#TOC">&#187;</a><a name="stype">Data types and
+constants</a></h3>
+<ul>
+<li><a href="qset_r.h#SETelemsize">SETelemsize</a> size
+of a set element in bytes </li>
+<li><a href="qset_r.h#setT">setT</a> a set with a
+maximum size and a current size</li>
+<li><a href="libqhull_r.h#qh-set">qh global sets</a>
+global sets for temporary sets, etc. </li>
+</ul>
+<h3><a href="qh-set_r.htm#TOC">&#187;</a><a name="seach">FOREACH macros</a></h3>
+<ul>
+<li><a href="qset_r.h#FOREACHelem_">FOREACHelem_</a>
+assign 'elem' to each element in a set </li>
+<li><a href="qset_r.h#FOREACHset_">FOREACHset_</a>
+assign 'set' to each set in a set of sets </li>
+<li><a href="qset_r.h#FOREACHsetelement_">FOREACHsetelement_</a>
+define a FOREACH iterator </li>
+<li><a href="qset_r.h#FOREACHsetelement_i_">FOREACHsetelement_i_</a>
+define an indexed FOREACH iterator </li>
+<li><a href="qset_r.h#FOREACHsetelementreverse_">FOREACHsetelementreverse_</a>
+define a reversed FOREACH iterator </li>
+<li><a href="qset_r.h#FOREACHsetelementreverse12_">FOREACHsetelementreverse12_</a>
+define a FOREACH iterator with e[1] and e[0]
+reversed </li>
+</ul>
+<h3><a href="qh-set_r.htm#TOC">&#187;</a><a name="saccess">Access and
+size macros</a></h3>
+<ul>
+<li><a href="qset_r.h#SETelem_">SETelem_</a> return the
+n'th element of set </li>
+<li><a href="qset_r.h#SETelemt_">SETelemt_</a> return
+the n'th element of set as a type</li>
+<li><a href="qset_r.h#SETempty_">SETempty_</a> return
+true (1) if set is empty </li>
+<li><a href="qset_r.h#SETfirst_">SETfirst_</a> return
+first element of set </li>
+<li><a href="qset_r.h#SETfirstt_">SETfirstt_</a> return
+first element of set as a type</li>
+<li><a href="qset_r.h#SETindex_">SETindex_</a> return
+index of elem in set </li>
+<li><a href="qset_r.h#SETreturnsize_">SETreturnsize_</a>
+return size of a set (normally use <a href="qset_r.c#setsize">qh_setsize</a>) </li>
+<li><a href="qset_r.h#SETsecond_">SETsecond_</a> return
+second element of set </li>
+<li><a href="qset_r.h#SETsecondt_">SETsecondt_</a>
+return second element of set as a type</li>
+<li><a href="qset_r.h#SETtruncate_">SETtruncate_</a>
+truncate set to size, i.e., qh_settruncate()</li>
+</ul>
+<h3><a href="qh-set_r.htm#TOC">&#187;</a><a name="sint">Internal macros</a></h3>
+<ul>
+<li><a href="qset_r.c#SETsizeaddr_">SETsizeaddr_</a>
+return pointer to end element of a set (indicates
+current size) </li>
+</ul>
+
+<h3><a href="qh-set_r.htm#TOC">&#187;</a><a name="saddr">address macros</a></h3>
+<ul>
+<li><a href="qset_r.h#SETaddr_">SETaddr_</a> return
+address of a set's elements </li>
+<li><a href="qset_r.h#SETelemaddr_">SETelemaddr_</a>
+return address of the n'th element of a set </li>
+<li><a href="qset_r.h#SETref_">SETref_</a> l_r.h.s. for
+modifying the current element in a FOREACH
+iteration </li>
+</ul>
+
+<h3><a href="qh-set_r.htm#TOC">&#187;</a><a name="snew">Allocation and
+deallocation functions</a></h3>
+<ul>
+<li><a href="qset_r.c#setfree">qh_setfree</a> free the
+space occupied by a set </li>
+<li><a href="qset_r.c#setfree2">qh_setfree2</a> free a
+set and its elements </li>
+<li><a href="qset_r.c#setfreelong">qh_setfreelong</a>
+free a set only if it is in long memory </li>
+<li><a href="qset_r.c#setnew">qh_setnew</a> create a new
+set </li>
+</ul>
+
+<h3><a href="qh-set_r.htm#TOC">&#187;</a><a name="spred">Access and
+predicate functions </a></h3>
+<ul>
+<li><a href="qset_r.c#setendpointer">qh_setendpointer</a> return
+pointer to NULL terminator of a set</li>
+<li><a href="qset_r.c#setequal">qh_setequal</a> return 1
+if two sorted sets are equal </li>
+<li><a href="qset_r.c#setequal_except">qh_setequal_except</a>
+return 1 if two sorted sets are equal except for
+an element </li>
+<li><a href="qset_r.c#setequal_skip">qh_setequal_skip</a>
+return 1 if two sorted sets are equal except for
+a pair of skipped elements </li>
+<li><a href="qset_r.c#setequal_skip">qh_setequal_skip</a>
+return 1 if two sorted sets are equal except for
+a pair of skipped elements </li>
+<li><a href="qset_r.c#setin">qh_setin</a> return 1 if an
+element is in a set </li>
+<li><a href="qset_r.c#setindex">qh_setindex</a> return
+the index of an element in a set </li>
+<li><a href="qset_r.c#setlast">qh_setlast</a> return
+last element of a set</li>
+<li><a href="qset_r.c#setsize">qh_setsize</a> returns
+the size of a set </li>
+</ul>
+
+<h3><a href="qh-set_r.htm#TOC">&#187;</a><a name="sadd">Add functions</a></h3>
+<ul>
+<li><a href="qset_r.c#setaddnth">qh_setaddnth</a> add a
+element as n'th element of sorted or unsorted set
+</li>
+<li><a href="qset_r.c#setaddsorted">qh_setaddsorted</a>
+add an element to a sorted set </li>
+<li><a href="qset_r.c#setappend">qh_setappend</a> append
+an element to a set </li>
+<li><a href="qset_r.c#setappend_set">qh_setappend_set</a>
+append a set of elements to a set </li>
+<li><a href="qset_r.c#setappend2ndlast">qh_setappend2ndlast</a>
+add an element as the next to the last element in
+a set </li>
+<li><a href="qset_r.c#setlarger">qh_setlarger</a> return
+a larger set with the same elements</li>
+<li><a href="qset_r.c#setreplace">qh_setreplace</a>
+replace one element with another in a set</li>
+<li><a href="qset_r.c#setunique">qh_setunique</a> add an
+element if it is not already in a set </li>
+</ul>
+
+<h3><a href="qh-set_r.htm#TOC">&#187;</a><a name="scheck">Check and print functions</a></h3>
+<ul>
+<li><a href="qset_r.c#setcheck">qh_setcheck</a> check a
+set for validity </li>
+<li><a href="qset_r.c#setprint">qh_setprint</a> print a
+set's elements to fp </li>
+</ul>
+
+<h3><a href="qh-set_r.htm#TOC">&#187;</a><a name="scopy">Copy, compact, and zero functions</a></h3>
+<ul>
+<li><a href="qset_r.c#setcompact">qh_setcompact</a>
+compact NULLs from an unsorted set </li>
+<li><a href="qset_r.c#setcopy">qh_setcopy</a> make a
+copy of a sorted or unsorted set </li>
+<li><a href="qset_r.c#setduplicate">qh_setduplicate</a>
+duplicate a set and its elements </li>
+<li><a href="qset_r.c#settruncate">qh_settruncate</a>
+truncate a set to size elements </li>
+<li><a href="qset_r.c#setzero">qh_setzero</a> zero the
+remainder of a set </li>
+</ul>
+
+<h3><a href="qh-set_r.htm#TOC">&#187;</a><a name="sdel">Delete functions</a></h3>
+<ul>
+<li><a href="qset_r.c#setdel">qh_setdel</a> delete an
+element from an unsorted set. </li>
+<li><a href="qset_r.c#setdellast">qh_setdellast</a>
+delete and return last element from a set</li>
+<li><a href="qset_r.c#setdelnth">qh_setdelnth</a> delete
+and return nth element from an unsorted set </li>
+<li><a href="qset_r.c#setdelnthsorted">qh_setdelnthsorted</a>
+delete and return nth element from a sorted set </li>
+<li><a href="qset_r.c#setdelsorted">qh_setdelsorted</a>
+delete an element from a sorted set </li>
+<li><a href="qset_r.c#setnew_delnthsorted">qh_setnew_delnthsorted</a>
+create a sorted set not containing the nth
+element </li>
+</ul>
+
+<h3><a href="qh-set_r.htm#TOC">&#187;</a><a name="stemp">Temporary set functions</a></h3>
+<ul>
+<li><a href="qset_r.c#settemp">qh_settemp</a> return a
+temporary set and append it qhmem.tempstack</li>
+<li><a href="qset_r.c#settempfree">qh_settempfree</a>
+free and pop a set from qhmem.tempstack</li>
+<li><a href="qset_r.c#settempfree_all">qh_settempfree_all</a>
+free all sets in qhmem.tempstack </li>
+<li><a href="qset_r.c#settemppop">qh_settemppop</a> pop
+a set from qhmem.tempstack (makes it permanent) </li>
+<li><a href="qset_r.c#settemppush">qh_settemppush</a>
+push a set unto qhmem.tempstack (makes it
+temporary) </li>
+</ul>
+
+<p><!-- Navigation links --> </p>
+<hr>
+<p><b>Up:</b>
+<a href="http://www.qhull.org">Home page for
+Qhull</a> <br>
+<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual: Table of Contents</a> <br>
+<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a>
+&#149; <a href="../../html/qh-quick.htm#options">Options</a>
+&#149; <a href="../../html/qh-opto.htm#output">Output</a>
+&#149; <a href="../../html/qh-optf.htm#format">Formats</a>
+&#149; <a href="../../html/qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="../../html/qh-optp.htm#print">Print</a>
+&#149; <a href="../../html/qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="../../html/qh-optc.htm#prec">Precision</a>
+&#149; <a href="../../html/qh-optt.htm#trace">Trace</a>
+&#149; <a href="index.htm">Functions</a><br>
+<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a> <br>
+<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br>
+<b>To:</b> <a href="qh-geom_r.htm">Geom</a> &#149;
+<a href="qh-globa_r.htm">Global</a> &#149; <a href="qh-io_r.htm">Io</a>
+&#149; <a href="qh-mem_r.htm">Mem</a> &#149; <a href="qh-merge_r.htm">Merge</a>
+&#149; <a href="qh-poly_r.htm">Poly</a> &#149; <a href="qh-qhull_r.htm#TOC">Qhull</a>
+&#149; <a href="qh-set_r.htm">Set</a> &#149; <a href="qh-stat_r.htm">Stat</a>
+&#149; <a href="qh-user_r.htm">User</a><br>
+</p>
+<p><!-- GC common information --> </p>
+<hr>
+<p><a href="http://www.geom.uiuc.edu/"><img
+src="../../html/qh--geom.gif" align="middle" width="40" height="40"></a><i>The
+Geometry Center Home Page </i></p>
+<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a>
+</a><br>
+Created: May 2, 1997 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p>
+</body>
+</html>
diff --git a/xs/src/qhull/src/libqhull_r/qh-stat_r.htm b/xs/src/qhull/src/libqhull_r/qh-stat_r.htm
new file mode 100644
index 000000000..ea9d7fc56
--- /dev/null
+++ b/xs/src/qhull/src/libqhull_r/qh-stat_r.htm
@@ -0,0 +1,161 @@
+<!-- Do not edit with Front Page, it adds too many spaces -->
+<html>
+<head>
+<meta http-equiv="Content-Type"
+content="text/html; charset=iso-8859-1">
+<title>stat_r.c -- statistical operations</title>
+</head>
+
+<body>
+<!-- Navigation links -->
+<p><a name="TOP"><b>Up:</b></a> <a
+href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual</a>: Table of Contents <br>
+<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a>
+&#149; <a href="../../html/qh-quick.htm#options">Options</a>
+&#149; <a href="../../html/qh-opto.htm#output">Output</a>
+&#149; <a href="../../html/qh-optf.htm#format">Formats</a>
+&#149; <a href="../../html/qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="../../html/qh-optp.htm#print">Print</a>
+&#149; <a href="../../html/qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="../../html/qh-optc.htm#prec">Precision</a>
+&#149; <a href="../../html/qh-optt.htm#trace">Trace</a>
+&#149; <a href="index.htm">Functions</a><br>
+<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a><br>
+<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br>
+<b>To:</b> <a href="qh-geom_r.htm">Geom</a> &#149; <a href="qh-globa_r.htm">Global</a>
+&#149; <a href="qh-io_r.htm">Io</a> &#149; <a href="qh-mem_r.htm">Mem</a>
+&#149; <a href="qh-merge_r.htm">Merge</a> &#149; <a href="qh-poly_r.htm">Poly</a>
+&#149; <a href="qh-qhull_r.htm">Qhull</a> &#149; <a href="qh-set_r.htm">Set</a>
+&#149; <a href="qh-stat_r.htm#TOC">Stat</a> &#149; <a href="qh-user_r.htm">User</a>
+</p>
+<hr>
+
+<h2>stat_r.c -- statistical operations</h2>
+<blockquote>
+<p>Qhull records many statistics. These functions and
+macros make it inexpensive to add a statistic.
+<p>As with Qhull's global variables, the statistics data structure is
+accessed by a macro, 'qhstat'. If qh_QHpointer is defined, the macro
+is 'qh_qhstat->', otherwise the macro is 'qh_qhstat.'.
+Statistics
+may be turned off in user_r.h. If so, all but the 'zz'
+statistics are ignored.</p>
+</blockquote>
+<p><b>Copyright &copy; 1995-2015 C.B. Barber</b></p>
+<hr>
+<p><a href="#TOP">&#187;</a> <a href="qh-geom_r.htm#TOC">Geom</a>
+<a name="TOC">&#149;</a> <a href="qh-globa_r.htm#TOC">Global</a>
+&#149; <a href="qh-io_r.htm#TOC">Io</a> &#149; <a href="qh-mem_r.htm#TOC">Mem</a>
+&#149; <a href="qh-merge_r.htm#TOC">Merge</a> &#149; <a href="qh-poly_r.htm#TOC">Poly</a>
+&#149; <a href="qh-qhull_r.htm#TOC">Qhull</a> &#149; <a href="qh-set_r.htm#TOC">Set</a>
+&#149; <b>Stat</b> &#149; <a href="qh-user_r.htm#TOC">User</a>
+</p>
+<h3>Index to <a href="stat_r.c">stat_r.c</a> and
+<a href="stat_r.h">stat_r.h</a></h3>
+<ul>
+<li><a href="#ttype">stat_r.h types</a> </li>
+<li><a href="#tconst">stat_r.h constants</a> </li>
+<li><a href="#tmacro">stat_r.h macros</a> </li>
+<li><a href="#tfunc">stat_r.c functions</a> </li>
+</ul>
+
+<h3><a href="qh-stat_r.htm#TOC">&#187;</a><a name="ttype">stat_r.h types</a></h3>
+<ul>
+<li><a href="stat_r.h#intrealT">intrealT</a> union of
+integer and real</li>
+<li><a href="stat_r.h#qhstat">qhstat</a> global data
+structure for statistics </li>
+</ul>
+<h3><a href="qh-stat_r.htm#TOC">&#187;</a><a name="tconst">stat_r.h
+constants</a></h3>
+<ul>
+<li><a href="stat_r.h#KEEPstatistics">qh_KEEPstatistics</a> 0 turns off most statistics</li>
+<li><a href="stat_r.h#statistics">Z..., W...</a> integer (Z) and real (W) statistics
+</li>
+<li><a href="stat_r.h#ZZstat">ZZstat</a> Z.../W... statistics that
+remain defined if qh_KEEPstatistics=0
+</li>
+<li><a href="stat_r.h#ztype">ztype</a> zdoc, zinc, etc.
+for definining statistics </li>
+</ul>
+<h3><a href="qh-stat_r.htm#TOC">&#187;</a><a name="tmacro">stat_r.h macros</a></h3>
+<ul>
+<li><a href="stat_r.h#MAYdebugx">MAYdebugx</a> called
+frequently for error trapping </li>
+<li><a href="stat_r.h#zadd_">zadd_/wadd_</a> add value
+to an integer or real statistic </li>
+<li><a href="stat_r.h#zdef_">zdef_</a> define a
+statistic </li>
+<li><a href="stat_r.h#zinc_">zinc_</a> increment an
+integer statistic </li>
+<li><a href="stat_r.h#zmax_">zmax_/wmax_</a> update
+integer or real maximum statistic </li>
+<li><a href="stat_r.h#zmin_">zmin_/wmin_</a> update
+integer or real minimum statistic </li>
+<li><a href="stat_r.h#zval_">zval_/wval_</a> set or
+return value of a statistic </li>
+</ul>
+
+<h3><a href="qh-stat_r.htm#TOC">&#187;</a><a name="tfunc">stat_r.c
+functions</a></h3>
+<ul>
+<li><a href="stat_r.c#allstatA">qh_allstatA</a> define
+statistics in groups of 20 </li>
+<li><a href="stat_r.c#allstatistics">qh_allstatistics</a>
+reset printed flag for all statistics </li>
+<li><a href="stat_r.c#collectstatistics">qh_collectstatistics</a>
+collect statistics for qh.facet_list </li>
+<li><a href="stat_r.c#initstatistics">qh_initstatistics</a>
+allocate and initialize statistics </li>
+<li><a href="stat_r.c#newstats">qh_newstats</a> returns
+True if statistics for zdoc </li>
+<li><a href="stat_r.c#nostatistic">qh_nostatistic</a>
+true if no statistic to print </li>
+<li><a href="stat_r.c#printallstatistics">qh_printallstatistics</a>
+print all statistics </li>
+<li><a href="stat_r.c#printstatistics">qh_printstatistics</a>
+print statistics to a file </li>
+<li><a href="stat_r.c#printstatlevel">qh_printstatlevel</a>
+print level information for a statistic </li>
+<li><a href="stat_r.c#printstats">qh_printstats</a>
+print statistics for a zdoc group </li>
+<li><a href="stat_r.c#stddev">qh_stddev</a> compute the
+standard deviation and average from statistics </li>
+</ul>
+
+<p><!-- Navigation links --> </p>
+<hr>
+<p><b>Up:</b>
+<a href="http://www.qhull.org">Home page for
+Qhull</a> <br>
+<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual: Table of Contents</a> <br>
+<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a>
+&#149; <a href="../../html/qh-quick.htm#options">Options</a>
+&#149; <a href="../../html/qh-opto.htm#output">Output</a>
+&#149; <a href="../../html/qh-optf.htm#format">Formats</a>
+&#149; <a href="../../html/qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="../../html/qh-optp.htm#print">Print</a>
+&#149; <a href="../../html/qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="../../html/qh-optc.htm#prec">Precision</a>
+&#149; <a href="../../html/qh-optt.htm#trace">Trace</a>
+&#149; <a href="index.htm">Functions</a><br>
+<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a> <br>
+<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br>
+<b>To:</b> <a href="qh-geom_r.htm">Geom</a> &#149;
+<a href="qh-globa_r.htm">Global</a> &#149; <a href="qh-io_r.htm">Io</a>
+&#149; <a href="qh-mem_r.htm">Mem</a> &#149; <a href="qh-merge_r.htm">Merge</a>
+&#149; <a href="qh-poly_r.htm">Poly</a> &#149; <a href="qh-qhull_r.htm#TOC">Qhull</a>
+&#149; <a href="qh-set_r.htm">Set</a> &#149; <a href="qh-stat_r.htm">Stat</a>
+&#149; <a href="qh-user_r.htm">User</a><br>
+</p>
+<p><!-- GC common information --> </p>
+<hr>
+<p><a href="http://www.geom.uiuc.edu/"><img
+src="../../html/qh--geom.gif" align="middle" width="40" height="40"></a><i>The
+Geometry Center Home Page </i></p>
+<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a>
+</a><br>
+Created: May 2, 1997 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p>
+</body>
+</html>
diff --git a/xs/src/qhull/src/libqhull_r/qh-user_r.htm b/xs/src/qhull/src/libqhull_r/qh-user_r.htm
new file mode 100644
index 000000000..909fec656
--- /dev/null
+++ b/xs/src/qhull/src/libqhull_r/qh-user_r.htm
@@ -0,0 +1,271 @@
+<!-- Do not edit with Front Page, it adds too many spaces -->
+<html>
+<head>
+<meta http-equiv="Content-Type"
+content="text/html; charset=iso-8859-1">
+<title>user_r.c -- user-definable operations</title>
+</head>
+
+<body>
+<!-- Navigation links -->
+<p><a name="TOP"><b>Up:</b></a> <a
+href="http://www.qhull.org">Home page</a> for Qhull<br>
+<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual</a>: Table of Contents <br>
+<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a>
+&#149; <a href="../../html/qh-quick.htm#options">Options</a>
+&#149; <a href="../../html/qh-opto.htm#output">Output</a>
+&#149; <a href="../../html/qh-optf.htm#format">Formats</a>
+&#149; <a href="../../html/qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="../../html/qh-optp.htm#print">Print</a>
+&#149; <a href="../../html/qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="../../html/qh-optc.htm#prec">Precision</a>
+&#149; <a href="../../html/qh-optt.htm#trace">Trace</a>
+&#149; <a href="index.htm">Functions</a><br>
+<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a><br>
+<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br>
+<b>To:</b> <a href="qh-geom_r.htm">Geom</a> &#149; <a href="qh-globa_r.htm">Global</a>
+&#149; <a href="qh-io_r.htm">Io</a> &#149; <a href="qh-mem_r.htm">Mem</a>
+&#149; <a href="qh-merge_r.htm">Merge</a> &#149; <a href="qh-poly_r.htm">Poly</a>
+&#149; <a href="qh-qhull_r.htm">Qhull</a> &#149; <a href="qh-set_r.htm">Set</a>
+&#149; <a href="qh-stat_r.htm">Stat</a> &#149; <a href="qh-user_r.htm#TOC">User</a>
+</p>
+<hr>
+<h2>user_r.c -- user-definable operations</h2>
+<blockquote>
+<p>This section contains functions and constants that the
+user may want to change. </p>
+
+</blockquote>
+<p><b>Copyright &copy; 1995-2015 C.B. Barber</b></p>
+<hr>
+<p><a href="#TOP">&#187;</a> <a href="qh-geom_r.htm#TOC">Geom</a>
+<a name="TOC">&#149;</a> <a href="qh-globa_r.htm#TOC">Global</a>
+&#149; <a href="qh-io_r.htm#TOC">Io</a> &#149; <a href="qh-mem_r.htm#TOC">Mem</a>
+&#149; <a href="qh-merge_r.htm#TOC">Merge</a> &#149; <a href="qh-poly_r.htm#TOC">Poly</a>
+&#149; <a href="qh-qhull_r.htm#TOC">Qhull</a> &#149; <a href="qh-set_r.htm#TOC">Set</a>
+&#149; <a href="qh-stat_r.htm#TOC">Stat</a> &#149; <b>User</b>
+</p>
+<h3>Index to <a href="user_r.c">user_r.c</a>, <a href="usermem_r.c">usermem_r.c</a>, <a href="userprintf_r.c">userprintf_r.c</a>, <a href="userprintf_rbox_r.c">userprintf_rbox_r.c</a> and
+<a href="user_r.h">user_r.h</a></h3>
+<ul>
+<li><a href="#qulllib">qhull library constants</a></li>
+<li><a href="#utype">user_r.h data types and
+configuration macros</a> </li>
+<li><a href="#ujoggle">joggle constants</a></li>
+<li><a href="#uperform">performance related constants</a></li>
+<li><a href="#umemory">memory constants</a></li>
+<li><a href="#ucond">conditional compilation</a></li>
+<li><a href="#umerge">merge constants</a> </li>
+<li><a href="#ufunc">user_r.c functions</a> </li>
+<li><a href="#u2func">usermem_r.c functions</a> </li>
+<li><a href="#u3func">userprintf_r.c functions</a> </li>
+</ul>
+
+<h3><a href="qh-user_r.htm#TOC">&#187;</a><a name="qulllib">Qhull library constants</a></h3>
+<ul>
+<li><a href="user_r.h#filenamelen">FILENAMElen</a> -- max length of TI or TO filename </li>
+<li><a href="user_r.h#msgcode">msgcode</a> -- unique message codes for qh_fprintf </li>
+<li><a href="user_r.h#qh_OPTIONline">qh_OPTIONline</a> -- max length of option line ('FO')</li>
+</ul>
+
+
+<h3><a href="qh-user_r.htm#TOC">&#187;</a><a name="utype">user_r.h data
+types and configuration macros</a></h3>
+<ul>
+<li><a href="user_r.h#realT">realT, qh_REAL...</a> size
+of floating point numbers </li>
+<li><a href="user_r.h#countT">countT, COUNTmax</a> size
+of counts and identifiers, typically 'int' or 'long long' </li>
+<li><a href="user_r.h#CPUclock">qh_CPUclock</a> clock()
+function for reporting the total time spent by
+Qhull </li>
+<li><a href="user_r.h#RANDOM">qh_RANDOM...</a> random
+number generator </li>
+</ul>
+
+<h3><a href="qh-user_r.htm#TOC">&#187;</a><a name="udef">definition constants</a></h3>
+<ul>
+<li><a href="user_r.h#DEFAULTbox">qh_DEFAULTbox</a>
+define default box size for rbox, 'Qbb', and 'QbB' (Geomview expects 0.5) </li>
+<li><a href="user_r.h#INFINITE">qh_INFINITE</a> on
+output, indicates Voronoi center at infinity </li>
+<li><a href="user_r.h#ORIENTclock">qh_ORIENTclock</a>
+define convention for orienting facets</li>
+<li><a href="user_r.h#ZEROdelaunay">qh_ZEROdelaunay</a>
+define facets that are ignored in Delaunay triangulations</li>
+</ul>
+
+<h3><a href="qh-user_r.htm#TOC">&#187;</a><a name="ujoggle">joggle constants</a></h3>
+<ul>
+<li><a href="user_r.h#JOGGLEagain">qh_JOGGLEagain</a>
+how often to retry before using qh_JOGGLEmaxincrease
+again </li>
+<li><a href="user_r.h#JOGGLEdefault">qh_JOGGLEdefault</a>
+default value for qh.JOGGLEmax for 'QP' </li>
+<li><a href="user_r.h#JOGGLEincrease">qh_JOGGLEincrease</a>
+factor to increase qh.JOGGLEmax on retrys for
+'QPn' </li>
+<li><a href="user_r.h#JOGGLEmaxincrease">qh_JOGGLEmaxincrease</a> max
+for increasing qh.JOGGLEmax relative to
+qh.MAXwidth </li>
+<li><a href="user_r.h#JOGGLEretry">qh_JOGGLEmaxretry</a>
+report error if this many retries </li>
+<li><a href="user_r.h#JOGGLEretry">qh_JOGGLEretry</a>
+how often to retry before using qh_JOGGLEmax </li>
+</ul>
+
+<h3><a href="qh-user_r.htm#TOC">&#187;</a><a name="uperform">performance
+related constants</a></h3>
+<ul>
+<li><a href="user_r.h#HASHfactor">qh_HASHfactor</a>
+total/used hash slots </li>
+<li><a href="user_r.h#INITIALmax">qh_INITIALmax</a> if
+dim &gt;= qh_INITIALmax, use min/max coordinate
+points for initial simplex </li>
+<li><a href="user_r.h#INITIALsearch">qh_INITIALsearch</a>
+if qh.INITIALmax, search points up to this
+dimension </li>
+<li><a href="user_r.h#NOtrace">qh_NOtrace</a> disallow
+tracing </li>
+<li><a href="user_r.h#VERIFYdirect">qh_VERIFYdirect</a>
+'Tv' verifies all <em>points X facets</em> if op
+count is smaller </li>
+</ul>
+
+<h3><a href="qh-user_r.htm#TOC">&#187;</a><a name="umemory">memory constants</a></h3>
+<ul>
+<li><a href="user_r.h#MEMalign">qh_MEMalign</a> memory
+alignment for qh_meminitbuffers() in global_r.c </li>
+<li><a href="user_r.h#MEMbufsize">qh_MEMbufsize</a>
+size of additional memory buffers </li>
+<li><a href="user_r.h#MEMinitbuf">qh_MEMinitbuf</a>
+size of initial memory buffer </li>
+</ul>
+
+<h3><a href="qh-user_r.htm#TOC">&#187;</a><a name="ucond">conditional compilation</a></h3>
+<ul>
+<li><a href="user_r.h#compiler">compiler</a> defined symbols,
+e.g., _STDC_ and _cplusplus
+
+<li><a href="user_r.h#COMPUTEfurthest">qh_COMPUTEfurthest</a>
+ compute furthest distance to an outside point instead of storing it with the facet
+<li><a href="user_r.h#KEEPstatistics">qh_KEEPstatistics</a>
+ enable statistic gathering and reporting with option 'Ts'
+<li><a href="user_r.h#MAXoutside">qh_MAXoutside</a>
+record outer plane for each facet
+<li><a href="user_r.h#NOmerge">qh_NOmerge</a>
+disable facet merging
+<li><a href="user_r.h#NOtrace">qh_NOtrace</a>
+disable tracing with option 'T4'
+<li><a href="user_r.h#QHpointer">qh_QHpointer</a>
+access global data with pointer or static structure
+<li><a href="user_r.h#QUICKhelp">qh_QUICKhelp</a>
+use abbreviated help messages, e.g., for degenerate inputs
+</ul>
+
+<h3><a href="qh-user_r.htm#TOC">&#187;</a><a name="umerge">merge
+constants</a></h3>
+<ul>
+<li><a href="user_r.h#BESTcentrum">qh_BESTcentrum</a>
+when does qh_findbestneighbor() test centrums? </li>
+<li><a href="user_r.h#BESTnonconvex">qh_BESTnonconvex</a>
+when does qh_findbestneighbor() test nonconvex
+ridges only? </li>
+<li><a href="user_r.h#COPLANARratio">qh_COPLANARratio</a>
+what is qh.MINvisible? </li>
+<li><a href="user_r.h#DIMreduceBuild">qh_DIMreduceBuild</a>
+max dimension for vertex reduction </li>
+<li><a href="user_r.h#DIMmergeVertex">qh_DIMmergeVertex</a>
+max dimension for vertex merging </li>
+<li><a href="user_r.h#DISToutside">qh_DISToutside</a>
+when is a point clearly outside of a facet for qh_findbestnew and qh_partitionall</li>
+<li><a href="user_r.h#MAXnarrow">qh_MAXnarrow</a> max.
+cosine for qh.NARROWhull </li>
+<li><a href="user_r.h#MAXnewcentrum">qh_MAXnewcentrum</a>
+when does qh_reducevertices_centrum() reset the
+centrum? </li>
+<li><a href="user_r.h#MAXnewmerges">qh_MAXnewmerges</a>
+when does qh_merge_nonconvex() call
+qh_reducevertices_centrums? </li>
+<li><a href="user_r.h#RATIOnearinside">qh_RATIOnearinside</a>
+ratio for retaining inside points for
+qh_check_maxout() </li>
+<li><a href="user_r.h#SEARCHdist">qh_SEARCHdist</a>
+when is facet coplanar with the best facet for qh_findbesthorizon</li>
+<li><a href="user_r.h#USEfindbestnew">qh_USEfindbestnew</a>
+when to use qh_findbestnew for qh_partitionpoint()</li>
+<li><a href="user_r.h#WARNnarrow">qh_WARNnarrow</a>
+max. cosine to warn about qh.NARROWhull </li>
+<li><a href="user_r.h#WIDEcoplanar">qh_WIDEcoplanar</a>
+what is a wide facet? </li>
+<li><a href="user_r.h#WIDEduplicate">qh_WIDEduplicate</a>
+what is a wide ratio on merging duplicate ridges? </li>
+</ul>
+
+<h3><a href="qh-user_r.htm#TOC">&#187;</a><a name="ufunc">user_r.c
+functions</a></h3>
+<ul>
+<li><a href="user_r.c#qhull_template">Qhull template</a> for calling qh_new_qhull from your program</li>
+<li><a href="user_r.c#errexit">qh_errexit</a> report
+error and exit qhull()</li>
+<li><a href="user_r.c#errprint">qh_errprint</a> print
+information about facets and ridges </li>
+<li><a href="user_r.c#new_qhull">qh_new_qhull</a> call qhull on an array
+of points</li>
+<li><a href="user_r.c#printfacetlist">qh_printfacetlist</a>
+print all fields of all facets </li>
+</ul>
+
+<h3><a href="qh-user_r.htm#TOC">&#187;</a><a name="u2func">usermem_r.c
+functions</a></h3>
+<ul>
+<li><a href="usermem_r.c#qh_exit">qh_exit</a> exit program, same as exit(). May be redefined as throw "QH10003.." by libqhullcpp/usermem_r-cpp.cpp</li>
+<li><a href="usermem_r.c#qh_fprintf_stderr">qh_fprintf_stderr</a> print to stderr when qh->ferr is not defined.</li>
+<li><a href="usermem_r.c#qh_free">qh_free</a> free memory, same as free().</li>
+<li><a href="usermem_r.c#qh_malloc">qh_malloc</a> allocate memory, same as malloc()</li>
+</ul>
+
+<h3><a href="qh-user_r.htm#TOC">&#187;</a><a name="u3func">userprintf_r.c
+ and userprintf_rbox,c functions</a></h3>
+<ul>
+<li><a href="userprintf_r.c#qh_fprintf">qh_fprintf</a> print
+information from Qhull, sames as fprintf(). </li>
+<li><a href="userprintf_rbox_r.c#qh_fprintf_rbox">qh_fprintf_rbox</a> print
+information from Rbox, sames as fprintf(). </li>
+</ul>
+
+<p><!-- Navigation links --> </p>
+<hr>
+<p><b>Up:</b>
+<a href="http://www.qhull.org">Home page for
+Qhull</a> <br>
+<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual: Table of Contents</a> <br>
+<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a>
+&#149; <a href="../../html/qh-quick.htm#options">Options</a>
+&#149; <a href="../../html/qh-opto.htm#output">Output</a>
+&#149; <a href="../../html/qh-optf.htm#format">Formats</a>
+&#149; <a href="../../html/qh-optg.htm#geomview">Geomview</a>
+&#149; <a href="../../html/qh-optp.htm#print">Print</a>
+&#149; <a href="../../html/qh-optq.htm#qhull">Qhull</a>
+&#149; <a href="../../html/qh-optc.htm#prec">Precision</a>
+&#149; <a href="../../html/qh-optt.htm#trace">Trace</a>
+&#149; <a href="index.htm">Functions</a><br>
+<b>Up:</b> <a href="../../html/qh-code.htm#TOC">Qhull code: Table of Contents</a> <br>
+<b>To:</b> <a href="index.htm">Qhull functions</a>, macros, and data structures<br>
+<b>To:</b> <a href="qh-geom_r.htm">Geom</a> &#149;
+<a href="qh-globa_r.htm">Global</a> &#149; <a href="qh-io_r.htm">Io</a>
+&#149; <a href="qh-mem_r.htm">Mem</a> &#149; <a href="qh-merge_r.htm">Merge</a>
+&#149; <a href="qh-poly_r.htm">Poly</a> &#149; <a href="qh-qhull_r.htm#TOC">Qhull</a>
+&#149; <a href="qh-set_r.htm">Set</a> &#149; <a href="qh-stat_r.htm">Stat</a>
+&#149; <a href="qh-user_r.htm">User</a><br>
+</p>
+<p><!-- GC common information --> </p>
+<hr>
+<p><a href="http://www.geom.uiuc.edu/"><img
+src="../../html/qh--geom.gif" align="middle" width="40" height="40"></a><i>The
+Geometry Center Home Page </i></p>
+<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a>
+</a><br>
+Created: May 2, 1997 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p>
+</body>
+</html>
diff --git a/xs/src/qhull/src/libqhull_r/qhull_r-exports.def b/xs/src/qhull/src/libqhull_r/qhull_r-exports.def
new file mode 100644
index 000000000..325d57c3b
--- /dev/null
+++ b/xs/src/qhull/src/libqhull_r/qhull_r-exports.def
@@ -0,0 +1,404 @@
+; qhull_r-exports.def -- msvc module-definition file
+;
+; Generated from depends.exe by cut-and-paste of exported symbols by mingw gcc
+; [jan'14] 391 symbols
+; Same as ../libqhullp/qhull-exports.def without DATA items (reentrant)
+;
+; $Id: //main/2015/qhull/src/libqhull_r/qhull_r-exports.def#3 $$Change: 2047 $
+; $DateTime: 2016/01/04 22:03:18 $$Author: bbarber $
+;
+; Define qhull_VERSION in CMakeLists.txt, Makefile, qhull-exports.def, qhull_p-exports.def, qhull_r-exports.def, and qhull-warn.pri
+VERSION 7.0
+EXPORTS
+qh_addhash
+qh_addpoint
+qh_all_merges
+qh_allstatA
+qh_allstatB
+qh_allstatC
+qh_allstatD
+qh_allstatE
+qh_allstatE2
+qh_allstatF
+qh_allstatG
+qh_allstatH
+qh_allstatI
+qh_allstatistics
+qh_appendfacet
+qh_appendmergeset
+qh_appendprint
+qh_appendvertex
+qh_argv_to_command
+qh_argv_to_command_size
+qh_attachnewfacets
+qh_backnormal
+qh_basevertices
+qh_build_withrestart
+qh_buildhull
+qh_buildtracing
+qh_check_bestdist
+qh_check_dupridge
+qh_check_maxout
+qh_check_output
+qh_check_point
+qh_check_points
+qh_checkconnect
+qh_checkconvex
+qh_checkfacet
+qh_checkflags
+qh_checkflipped
+qh_checkflipped_all
+qh_checkpolygon
+qh_checkvertex
+qh_checkzero
+qh_clear_outputflags
+qh_clearcenters
+qh_clock
+qh_collectstatistics
+qh_compare_facetarea
+qh_compare_facetmerge
+qh_compare_facetvisit
+qh_compareangle
+qh_comparemerge
+qh_comparevisit
+qh_copyfilename
+qh_copynonconvex
+qh_copypoints
+qh_countfacets
+qh_createsimplex
+qh_crossproduct
+qh_degen_redundant_facet
+qh_degen_redundant_neighbors
+qh_deletevisible
+qh_delfacet
+qh_delridge
+qh_delvertex
+qh_determinant
+qh_detjoggle
+qh_detroundoff
+qh_detsimplex
+qh_detvnorm
+qh_detvridge
+qh_detvridge3
+qh_dfacet
+qh_distnorm
+qh_distplane
+qh_distround
+qh_divzero
+qh_dvertex
+qh_eachvoronoi
+qh_eachvoronoi_all
+qh_errexit
+qh_errexit2
+qh_errexit_rbox
+qh_errprint
+qh_exit
+qh_facet2point
+qh_facet3vertex
+qh_facetarea
+qh_facetarea_simplex
+qh_facetcenter
+qh_facetintersect
+qh_facetvertices
+qh_find_newvertex
+qh_findbest
+qh_findbest_test
+qh_findbestfacet
+qh_findbesthorizon
+qh_findbestlower
+qh_findbestneighbor
+qh_findbestnew
+qh_findfacet_all
+qh_findgood
+qh_findgood_all
+qh_findgooddist
+qh_findhorizon
+qh_flippedmerges
+qh_forcedmerges
+qh_fprintf
+qh_fprintf_rbox
+qh_fprintf_stderr
+qh_free
+qh_freebuffers
+qh_freebuild
+qh_freeqhull
+qh_furthestnext
+qh_furthestout
+qh_gausselim
+qh_geomplanes
+qh_getangle
+qh_getarea
+qh_getcenter
+qh_getcentrum
+qh_getdistance
+qh_gethash
+qh_getmergeset
+qh_getmergeset_initial
+qh_gram_schmidt
+qh_hashridge
+qh_hashridge_find
+qh_infiniteloop
+qh_init_A
+qh_init_B
+qh_init_qhull_command
+qh_initbuild
+qh_initflags
+qh_initialhull
+qh_initialvertices
+qh_initqhull_buffers
+qh_initqhull_globals
+qh_initqhull_mem
+qh_initqhull_outputflags
+qh_initqhull_start
+qh_initqhull_start2
+qh_initstatistics
+qh_initthresholds
+qh_inthresholds
+qh_isvertex
+qh_joggleinput
+qh_lib_check
+qh_makenew_nonsimplicial
+qh_makenew_simplicial
+qh_makenewfacet
+qh_makenewfacets
+qh_makenewplanes
+qh_makeridges
+qh_malloc
+qh_mark_dupridges
+qh_markkeep
+qh_markvoronoi
+qh_matchduplicates
+qh_matchneighbor
+qh_matchnewfacets
+qh_matchvertices
+qh_maxabsval
+qh_maxmin
+qh_maxouter
+qh_maxsimplex
+qh_maydropneighbor
+qh_memalloc
+qh_memfree
+qh_memfreeshort
+qh_meminit
+qh_meminitbuffers
+qh_memsetup
+qh_memsize
+qh_memstatistics
+qh_memtotal
+qh_merge_degenredundant
+qh_merge_nonconvex
+qh_mergecycle
+qh_mergecycle_all
+qh_mergecycle_facets
+qh_mergecycle_neighbors
+qh_mergecycle_ridges
+qh_mergecycle_vneighbors
+qh_mergefacet
+qh_mergefacet2d
+qh_mergeneighbors
+qh_mergeridges
+qh_mergesimplex
+qh_mergevertex_del
+qh_mergevertex_neighbors
+qh_mergevertices
+qh_minabsval
+qh_mindiff
+qh_nearcoplanar
+qh_nearvertex
+qh_neighbor_intersections
+qh_new_qhull
+qh_newfacet
+qh_newhashtable
+qh_newridge
+qh_newstats
+qh_newvertex
+qh_newvertices
+qh_nextfurthest
+qh_nextridge3d
+qh_normalize
+qh_normalize2
+qh_nostatistic
+qh_option
+qh_order_vertexneighbors
+qh_orientoutside
+qh_out1
+qh_out2n
+qh_out3n
+qh_outcoplanar
+qh_outerinner
+qh_partitionall
+qh_partitioncoplanar
+qh_partitionpoint
+qh_partitionvisible
+qh_point
+qh_point_add
+qh_pointdist
+qh_pointfacet
+qh_pointid
+qh_pointvertex
+qh_postmerge
+qh_precision
+qh_premerge
+qh_prepare_output
+qh_prependfacet
+qh_printafacet
+qh_printallstatistics
+qh_printbegin
+qh_printcenter
+qh_printcentrum
+qh_printend
+qh_printend4geom
+qh_printextremes
+qh_printextremes_2d
+qh_printextremes_d
+qh_printfacet
+qh_printfacet2geom
+qh_printfacet2geom_points
+qh_printfacet2math
+qh_printfacet3geom_nonsimplicial
+qh_printfacet3geom_points
+qh_printfacet3geom_simplicial
+qh_printfacet3math
+qh_printfacet3vertex
+qh_printfacet4geom_nonsimplicial
+qh_printfacet4geom_simplicial
+qh_printfacetNvertex_nonsimplicial
+qh_printfacetNvertex_simplicial
+qh_printfacetheader
+qh_printfacetlist
+qh_printfacetridges
+qh_printfacets
+qh_printhashtable
+qh_printhelp_degenerate
+qh_printhelp_narrowhull
+qh_printhelp_singular
+qh_printhyperplaneintersection
+qh_printline3geom
+qh_printlists
+qh_printmatrix
+qh_printneighborhood
+qh_printpoint
+qh_printpoint3
+qh_printpointid
+qh_printpoints
+qh_printpoints_out
+qh_printpointvect
+qh_printpointvect2
+qh_printridge
+qh_printspheres
+qh_printstatistics
+qh_printstatlevel
+qh_printstats
+qh_printsummary
+qh_printvdiagram
+qh_printvdiagram2
+qh_printvertex
+qh_printvertexlist
+qh_printvertices
+qh_printvneighbors
+qh_printvnorm
+qh_printvoronoi
+qh_printvridge
+qh_produce_output
+qh_produce_output2
+qh_projectdim3
+qh_projectinput
+qh_projectpoint
+qh_projectpoints
+qh_qhull
+qh_rand
+qh_randomfactor
+qh_randommatrix
+qh_rboxpoints
+qh_readfeasible
+qh_readpoints
+qh_reducevertices
+qh_redundant_vertex
+qh_remove_extravertices
+qh_removefacet
+qh_removevertex
+qh_rename_sharedvertex
+qh_renameridgevertex
+qh_renamevertex
+qh_resetlists
+qh_rotateinput
+qh_rotatepoints
+qh_roundi
+qh_scaleinput
+qh_scalelast
+qh_scalepoints
+qh_setaddnth
+qh_setaddsorted
+qh_setappend
+qh_setappend2ndlast
+qh_setappend_set
+qh_setcheck
+qh_setcompact
+qh_setcopy
+qh_setdel
+qh_setdelaunay
+qh_setdellast
+qh_setdelnth
+qh_setdelnthsorted
+qh_setdelsorted
+qh_setduplicate
+qh_setequal
+qh_setequal_except
+qh_setequal_skip
+qh_setfacetplane
+qh_setfeasible
+qh_setfree
+qh_setfree2
+qh_setfreelong
+qh_sethalfspace
+qh_sethalfspace_all
+qh_sethyperplane_det
+qh_sethyperplane_gauss
+qh_setin
+qh_setindex
+qh_setlarger
+qh_setlast
+qh_setnew
+qh_setnew_delnthsorted
+qh_setprint
+qh_setreplace
+qh_setsize
+qh_settemp
+qh_settempfree
+qh_settempfree_all
+qh_settemppop
+qh_settemppush
+qh_settruncate
+qh_setunique
+qh_setvoronoi_all
+qh_setzero
+qh_sharpnewfacets
+qh_skipfacet
+qh_skipfilename
+qh_srand
+qh_stddev
+qh_strtod
+qh_strtol
+qh_test_appendmerge
+qh_test_vneighbors
+qh_tracemerge
+qh_tracemerging
+qh_triangulate
+qh_triangulate_facet
+qh_triangulate_link
+qh_triangulate_mirror
+qh_triangulate_null
+qh_updatetested
+qh_updatevertices
+qh_user_memsizes
+qh_version
+qh_version2
+qh_vertexintersect
+qh_vertexintersect_new
+qh_vertexneighbors
+qh_vertexridges
+qh_vertexridges_facet
+qh_vertexsubset
+qh_voronoi_center
+qh_willdelete
+qh_zero
diff --git a/xs/src/qhull/src/libqhull_r/qhull_ra.h b/xs/src/qhull/src/libqhull_r/qhull_ra.h
new file mode 100644
index 000000000..5c5bd8779
--- /dev/null
+++ b/xs/src/qhull/src/libqhull_r/qhull_ra.h
@@ -0,0 +1,158 @@
+/*<html><pre> -<a href="qh-qhull_r.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ qhull_ra.h
+ all header files for compiling qhull with reentrant code
+ included before C++ headers for user_r.h:QHULL_CRTDBG
+
+ see qh-qhull.htm
+
+ see libqhull_r.h for user-level definitions
+
+ see user_r.h for user-definable constants
+
+ defines internal functions for libqhull_r.c global_r.c
+
+ Copyright (c) 1993-2015 The Geometry Center.
+ $Id: //main/2015/qhull/src/libqhull_r/qhull_ra.h#6 $$Change: 2079 $
+ $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
+
+ Notes: grep for ((" and (" to catch fprintf("lkasdjf");
+ full parens around (x?y:z)
+ use '#include "libqhull_r/qhull_ra.h"' to avoid name clashes
+*/
+
+#ifndef qhDEFqhulla
+#define qhDEFqhulla 1
+
+#include "libqhull_r.h" /* Includes user_r.h and data types */
+
+#include "stat_r.h"
+#include "random_r.h"
+#include "mem_r.h"
+#include "qset_r.h"
+#include "geom_r.h"
+#include "merge_r.h"
+#include "poly_r.h"
+#include "io_r.h"
+
+#include <setjmp.h>
+#include <string.h>
+#include <math.h>
+#include <float.h> /* some compilers will not need float.h */
+#include <limits.h>
+#include <time.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+/*** uncomment here and qset_r.c
+ if string.h does not define memcpy()
+#include <memory.h>
+*/
+
+#if qh_CLOCKtype == 2 /* defined in user_r.h from libqhull_r.h */
+#include <sys/types.h>
+#include <sys/times.h>
+#include <unistd.h>
+#endif
+
+#ifdef _MSC_VER /* Microsoft Visual C++ -- warning level 4 */
+#pragma warning( disable : 4100) /* unreferenced formal parameter */
+#pragma warning( disable : 4127) /* conditional expression is constant */
+#pragma warning( disable : 4706) /* assignment within conditional function */
+#pragma warning( disable : 4996) /* function was declared deprecated(strcpy, localtime, etc.) */
+#endif
+
+/* ======= -macros- =========== */
+
+/*-<a href="qh-qhull_r.htm#TOC"
+ >--------------------------------</a><a name="traceN">-</a>
+
+ traceN((qh, qh->ferr, 0Nnnn, "format\n", vars));
+ calls qh_fprintf if qh.IStracing >= N
+
+ Add debugging traps to the end of qh_fprintf
+
+ notes:
+ removing tracing reduces code size but doesn't change execution speed
+*/
+#ifndef qh_NOtrace
+#define trace0(args) {if (qh->IStracing) qh_fprintf args;}
+#define trace1(args) {if (qh->IStracing >= 1) qh_fprintf args;}
+#define trace2(args) {if (qh->IStracing >= 2) qh_fprintf args;}
+#define trace3(args) {if (qh->IStracing >= 3) qh_fprintf args;}
+#define trace4(args) {if (qh->IStracing >= 4) qh_fprintf args;}
+#define trace5(args) {if (qh->IStracing >= 5) qh_fprintf args;}
+#else /* qh_NOtrace */
+#define trace0(args) {}
+#define trace1(args) {}
+#define trace2(args) {}
+#define trace3(args) {}
+#define trace4(args) {}
+#define trace5(args) {}
+#endif /* qh_NOtrace */
+
+/*-<a href="qh-qhull_r.htm#TOC"
+ >--------------------------------</a><a name="QHULL_UNUSED">-</a>
+
+ Define an unused variable to avoid compiler warnings
+
+ Derived from Qt's corelib/global/qglobal.h
+
+*/
+
+#if defined(__cplusplus) && defined(__INTEL_COMPILER) && !defined(QHULL_OS_WIN)
+template <typename T>
+inline void qhullUnused(T &x) { (void)x; }
+# define QHULL_UNUSED(x) qhullUnused(x);
+#else
+# define QHULL_UNUSED(x) (void)x;
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/***** -libqhull_r.c prototypes (alphabetical after qhull) ********************/
+
+void qh_qhull(qhT *qh);
+boolT qh_addpoint(qhT *qh, pointT *furthest, facetT *facet, boolT checkdist);
+void qh_buildhull(qhT *qh);
+void qh_buildtracing(qhT *qh, pointT *furthest, facetT *facet);
+void qh_build_withrestart(qhT *qh);
+void qh_errexit2(qhT *qh, int exitcode, facetT *facet, facetT *otherfacet);
+void qh_findhorizon(qhT *qh, pointT *point, facetT *facet, int *goodvisible,int *goodhorizon);
+pointT *qh_nextfurthest(qhT *qh, facetT **visible);
+void qh_partitionall(qhT *qh, setT *vertices, pointT *points,int npoints);
+void qh_partitioncoplanar(qhT *qh, pointT *point, facetT *facet, realT *dist);
+void qh_partitionpoint(qhT *qh, pointT *point, facetT *facet);
+void qh_partitionvisible(qhT *qh, boolT allpoints, int *numpoints);
+void qh_precision(qhT *qh, const char *reason);
+void qh_printsummary(qhT *qh, FILE *fp);
+
+/***** -global_r.c internal prototypes (alphabetical) ***********************/
+
+void qh_appendprint(qhT *qh, qh_PRINT format);
+void qh_freebuild(qhT *qh, boolT allmem);
+void qh_freebuffers(qhT *qh);
+void qh_initbuffers(qhT *qh, coordT *points, int numpoints, int dim, boolT ismalloc);
+
+/***** -stat_r.c internal prototypes (alphabetical) ***********************/
+
+void qh_allstatA(qhT *qh);
+void qh_allstatB(qhT *qh);
+void qh_allstatC(qhT *qh);
+void qh_allstatD(qhT *qh);
+void qh_allstatE(qhT *qh);
+void qh_allstatE2(qhT *qh);
+void qh_allstatF(qhT *qh);
+void qh_allstatG(qhT *qh);
+void qh_allstatH(qhT *qh);
+void qh_freebuffers(qhT *qh);
+void qh_initbuffers(qhT *qh, coordT *points, int numpoints, int dim, boolT ismalloc);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* qhDEFqhulla */
diff --git a/xs/src/qhull/src/libqhull_r/qset_r.c b/xs/src/qhull/src/libqhull_r/qset_r.c
new file mode 100644
index 000000000..15cd3c0e2
--- /dev/null
+++ b/xs/src/qhull/src/libqhull_r/qset_r.c
@@ -0,0 +1,1340 @@
+/*<html><pre> -<a href="qh-set_r.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ qset_r.c
+ implements set manipulations needed for quickhull
+
+ see qh-set_r.htm and qset_r.h
+
+ Be careful of strict aliasing (two pointers of different types
+ that reference the same location). The last slot of a set is
+ either the actual size of the set plus 1, or the NULL terminator
+ of the set (i.e., setelemT).
+
+ Copyright (c) 1993-2015 The Geometry Center.
+ $Id: //main/2015/qhull/src/libqhull_r/qset_r.c#3 $$Change: 2062 $
+ $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $
+*/
+
+#include "libqhull_r.h" /* for qhT and QHULL_CRTDBG */
+#include "qset_r.h"
+#include "mem_r.h"
+#include <stdio.h>
+#include <string.h>
+/*** uncomment here and qhull_ra.h
+ if string.h does not define memcpy()
+#include <memory.h>
+*/
+
+#ifndef qhDEFlibqhull
+typedef struct ridgeT ridgeT;
+typedef struct facetT facetT;
+void qh_errexit(qhT *qh, int exitcode, facetT *, ridgeT *);
+void qh_fprintf(qhT *qh, FILE *fp, int msgcode, const char *fmt, ... );
+# ifdef _MSC_VER /* Microsoft Visual C++ -- warning level 4 */
+# pragma warning( disable : 4127) /* conditional expression is constant */
+# pragma warning( disable : 4706) /* assignment within conditional function */
+# endif
+#endif
+
+/*=============== internal macros ===========================*/
+
+/*============ functions in alphabetical order ===================*/
+
+/*-<a href="qh-set_r.htm#TOC"
+ >--------------------------------<a name="setaddnth">-</a>
+
+ qh_setaddnth(qh, setp, nth, newelem)
+ adds newelem as n'th element of sorted or unsorted *setp
+
+ notes:
+ *setp and newelem must be defined
+ *setp may be a temp set
+ nth=0 is first element
+ errors if nth is out of bounds
+
+ design:
+ expand *setp if empty or full
+ move tail of *setp up one
+ insert newelem
+*/
+void qh_setaddnth(qhT *qh, setT **setp, int nth, void *newelem) {
+ int oldsize, i;
+ setelemT *sizep; /* avoid strict aliasing */
+ setelemT *oldp, *newp;
+
+ if (!*setp || (sizep= SETsizeaddr_(*setp))->i==0) {
+ qh_setlarger(qh, setp);
+ sizep= SETsizeaddr_(*setp);
+ }
+ oldsize= sizep->i - 1;
+ if (nth < 0 || nth > oldsize) {
+ qh_fprintf(qh, qh->qhmem.ferr, 6171, "qhull internal error (qh_setaddnth): nth %d is out-of-bounds for set:\n", nth);
+ qh_setprint(qh, qh->qhmem.ferr, "", *setp);
+ qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);
+ }
+ sizep->i++;
+ oldp= (setelemT *)SETelemaddr_(*setp, oldsize, void); /* NULL */
+ newp= oldp+1;
+ for (i=oldsize-nth+1; i--; ) /* move at least NULL */
+ (newp--)->p= (oldp--)->p; /* may overwrite *sizep */
+ newp->p= newelem;
+} /* setaddnth */
+
+
+/*-<a href="qh-set_r.htm#TOC"
+ >--------------------------------<a name="setaddsorted">-</a>
+
+ setaddsorted( setp, newelem )
+ adds an newelem into sorted *setp
+
+ notes:
+ *setp and newelem must be defined
+ *setp may be a temp set
+ nop if newelem already in set
+
+ design:
+ find newelem's position in *setp
+ insert newelem
+*/
+void qh_setaddsorted(qhT *qh, setT **setp, void *newelem) {
+ int newindex=0;
+ void *elem, **elemp;
+
+ FOREACHelem_(*setp) { /* could use binary search instead */
+ if (elem < newelem)
+ newindex++;
+ else if (elem == newelem)
+ return;
+ else
+ break;
+ }
+ qh_setaddnth(qh, setp, newindex, newelem);
+} /* setaddsorted */
+
+
+/*-<a href="qh-set_r.htm#TOC"
+ >-------------------------------<a name="setappend">-</a>
+
+ qh_setappend(qh, setp, newelem)
+ append newelem to *setp
+
+ notes:
+ *setp may be a temp set
+ *setp and newelem may be NULL
+
+ design:
+ expand *setp if empty or full
+ append newelem to *setp
+
+*/
+void qh_setappend(qhT *qh, setT **setp, void *newelem) {
+ setelemT *sizep; /* Avoid strict aliasing. Writing to *endp may overwrite *sizep */
+ setelemT *endp;
+ int count;
+
+ if (!newelem)
+ return;
+ if (!*setp || (sizep= SETsizeaddr_(*setp))->i==0) {
+ qh_setlarger(qh, setp);
+ sizep= SETsizeaddr_(*setp);
+ }
+ count= (sizep->i)++ - 1;
+ endp= (setelemT *)SETelemaddr_(*setp, count, void);
+ (endp++)->p= newelem;
+ endp->p= NULL;
+} /* setappend */
+
+/*-<a href="qh-set_r.htm#TOC"
+ >-------------------------------<a name="setappend_set">-</a>
+
+ qh_setappend_set(qh, setp, setA)
+ appends setA to *setp
+
+ notes:
+ *setp can not be a temp set
+ *setp and setA may be NULL
+
+ design:
+ setup for copy
+ expand *setp if it is too small
+ append all elements of setA to *setp
+*/
+void qh_setappend_set(qhT *qh, setT **setp, setT *setA) {
+ int sizeA, size;
+ setT *oldset;
+ setelemT *sizep;
+
+ if (!setA)
+ return;
+ SETreturnsize_(setA, sizeA);
+ if (!*setp)
+ *setp= qh_setnew(qh, sizeA);
+ sizep= SETsizeaddr_(*setp);
+ if (!(size= sizep->i))
+ size= (*setp)->maxsize;
+ else
+ size--;
+ if (size + sizeA > (*setp)->maxsize) {
+ oldset= *setp;
+ *setp= qh_setcopy(qh, oldset, sizeA);
+ qh_setfree(qh, &oldset);
+ sizep= SETsizeaddr_(*setp);
+ }
+ if (sizeA > 0) {
+ sizep->i= size+sizeA+1; /* memcpy may overwrite */
+ memcpy((char *)&((*setp)->e[size].p), (char *)&(setA->e[0].p), (size_t)(sizeA+1) * SETelemsize);
+ }
+} /* setappend_set */
+
+
+/*-<a href="qh-set_r.htm#TOC"
+ >-------------------------------<a name="setappend2ndlast">-</a>
+
+ qh_setappend2ndlast(qh, setp, newelem )
+ makes newelem the next to the last element in *setp
+
+ notes:
+ *setp must have at least one element
+ newelem must be defined
+ *setp may be a temp set
+
+ design:
+ expand *setp if empty or full
+ move last element of *setp up one
+ insert newelem
+*/
+void qh_setappend2ndlast(qhT *qh, setT **setp, void *newelem) {
+ setelemT *sizep; /* Avoid strict aliasing. Writing to *endp may overwrite *sizep */
+ setelemT *endp, *lastp;
+ int count;
+
+ if (!*setp || (sizep= SETsizeaddr_(*setp))->i==0) {
+ qh_setlarger(qh, setp);
+ sizep= SETsizeaddr_(*setp);
+ }
+ count= (sizep->i)++ - 1;
+ endp= (setelemT *)SETelemaddr_(*setp, count, void); /* NULL */
+ lastp= endp-1;
+ *(endp++)= *lastp;
+ endp->p= NULL; /* may overwrite *sizep */
+ lastp->p= newelem;
+} /* setappend2ndlast */
+
+/*-<a href="qh-set_r.htm#TOC"
+ >-------------------------------<a name="setcheck">-</a>
+
+ qh_setcheck(qh, set, typename, id )
+ check set for validity
+ report errors with typename and id
+
+ design:
+ checks that maxsize, actual size, and NULL terminator agree
+*/
+void qh_setcheck(qhT *qh, setT *set, const char *tname, unsigned id) {
+ int maxsize, size;
+ int waserr= 0;
+
+ if (!set)
+ return;
+ SETreturnsize_(set, size);
+ maxsize= set->maxsize;
+ if (size > maxsize || !maxsize) {
+ qh_fprintf(qh, qh->qhmem.ferr, 6172, "qhull internal error (qh_setcheck): actual size %d of %s%d is greater than max size %d\n",
+ size, tname, id, maxsize);
+ waserr= 1;
+ }else if (set->e[size].p) {
+ qh_fprintf(qh, qh->qhmem.ferr, 6173, "qhull internal error (qh_setcheck): %s%d(size %d max %d) is not null terminated.\n",
+ tname, id, size-1, maxsize);
+ waserr= 1;
+ }
+ if (waserr) {
+ qh_setprint(qh, qh->qhmem.ferr, "ERRONEOUS", set);
+ qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);
+ }
+} /* setcheck */
+
+
+/*-<a href="qh-set_r.htm#TOC"
+ >-------------------------------<a name="setcompact">-</a>
+
+ qh_setcompact(qh, set )
+ remove internal NULLs from an unsorted set
+
+ returns:
+ updated set
+
+ notes:
+ set may be NULL
+ it would be faster to swap tail of set into holes, like qh_setdel
+
+ design:
+ setup pointers into set
+ skip NULLs while copying elements to start of set
+ update the actual size
+*/
+void qh_setcompact(qhT *qh, setT *set) {
+ int size;
+ void **destp, **elemp, **endp, **firstp;
+
+ if (!set)
+ return;
+ SETreturnsize_(set, size);
+ destp= elemp= firstp= SETaddr_(set, void);
+ endp= destp + size;
+ while (1) {
+ if (!(*destp++ = *elemp++)) {
+ destp--;
+ if (elemp > endp)
+ break;
+ }
+ }
+ qh_settruncate(qh, set, (int)(destp-firstp)); /* WARN64 */
+} /* setcompact */
+
+
+/*-<a href="qh-set_r.htm#TOC"
+ >-------------------------------<a name="setcopy">-</a>
+
+ qh_setcopy(qh, set, extra )
+ make a copy of a sorted or unsorted set with extra slots
+
+ returns:
+ new set
+
+ design:
+ create a newset with extra slots
+ copy the elements to the newset
+
+*/
+setT *qh_setcopy(qhT *qh, setT *set, int extra) {
+ setT *newset;
+ int size;
+
+ if (extra < 0)
+ extra= 0;
+ SETreturnsize_(set, size);
+ newset= qh_setnew(qh, size+extra);
+ SETsizeaddr_(newset)->i= size+1; /* memcpy may overwrite */
+ memcpy((char *)&(newset->e[0].p), (char *)&(set->e[0].p), (size_t)(size+1) * SETelemsize);
+ return(newset);
+} /* setcopy */
+
+
+/*-<a href="qh-set_r.htm#TOC"
+ >-------------------------------<a name="setdel">-</a>
+
+ qh_setdel(set, oldelem )
+ delete oldelem from an unsorted set
+
+ returns:
+ returns oldelem if found
+ returns NULL otherwise
+
+ notes:
+ set may be NULL
+ oldelem must not be NULL;
+ only deletes one copy of oldelem in set
+
+ design:
+ locate oldelem
+ update actual size if it was full
+ move the last element to the oldelem's location
+*/
+void *qh_setdel(setT *set, void *oldelem) {
+ setelemT *sizep;
+ setelemT *elemp;
+ setelemT *lastp;
+
+ if (!set)
+ return NULL;
+ elemp= (setelemT *)SETaddr_(set, void);
+ while (elemp->p != oldelem && elemp->p)
+ elemp++;
+ if (elemp->p) {
+ sizep= SETsizeaddr_(set);
+ if (!(sizep->i)--) /* if was a full set */
+ sizep->i= set->maxsize; /* *sizep= (maxsize-1)+ 1 */
+ lastp= (setelemT *)SETelemaddr_(set, sizep->i-1, void);
+ elemp->p= lastp->p; /* may overwrite itself */
+ lastp->p= NULL;
+ return oldelem;
+ }
+ return NULL;
+} /* setdel */
+
+
+/*-<a href="qh-set_r.htm#TOC"
+ >-------------------------------<a name="setdellast">-</a>
+
+ qh_setdellast(set)
+ return last element of set or NULL
+
+ notes:
+ deletes element from set
+ set may be NULL
+
+ design:
+ return NULL if empty
+ if full set
+ delete last element and set actual size
+ else
+ delete last element and update actual size
+*/
+void *qh_setdellast(setT *set) {
+ int setsize; /* actually, actual_size + 1 */
+ int maxsize;
+ setelemT *sizep;
+ void *returnvalue;
+
+ if (!set || !(set->e[0].p))
+ return NULL;
+ sizep= SETsizeaddr_(set);
+ if ((setsize= sizep->i)) {
+ returnvalue= set->e[setsize - 2].p;
+ set->e[setsize - 2].p= NULL;
+ sizep->i--;
+ }else {
+ maxsize= set->maxsize;
+ returnvalue= set->e[maxsize - 1].p;
+ set->e[maxsize - 1].p= NULL;
+ sizep->i= maxsize;
+ }
+ return returnvalue;
+} /* setdellast */
+
+
+/*-<a href="qh-set_r.htm#TOC"
+ >-------------------------------<a name="setdelnth">-</a>
+
+ qh_setdelnth(qh, set, nth )
+ deletes nth element from unsorted set
+ 0 is first element
+
+ returns:
+ returns the element (needs type conversion)
+
+ notes:
+ errors if nth invalid
+
+ design:
+ setup points and check nth
+ delete nth element and overwrite with last element
+*/
+void *qh_setdelnth(qhT *qh, setT *set, int nth) {
+ void *elem;
+ setelemT *sizep;
+ setelemT *elemp, *lastp;
+
+ sizep= SETsizeaddr_(set);
+ if ((sizep->i--)==0) /* if was a full set */
+ sizep->i= set->maxsize; /* *sizep= (maxsize-1)+ 1 */
+ if (nth < 0 || nth >= sizep->i) {
+ qh_fprintf(qh, qh->qhmem.ferr, 6174, "qhull internal error (qh_setdelnth): nth %d is out-of-bounds for set:\n", nth);
+ qh_setprint(qh, qh->qhmem.ferr, "", set);
+ qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);
+ }
+ elemp= (setelemT *)SETelemaddr_(set, nth, void); /* nth valid by QH6174 */
+ lastp= (setelemT *)SETelemaddr_(set, sizep->i-1, void);
+ elem= elemp->p;
+ elemp->p= lastp->p; /* may overwrite itself */
+ lastp->p= NULL;
+ return elem;
+} /* setdelnth */
+
+/*-<a href="qh-set_r.htm#TOC"
+ >-------------------------------<a name="setdelnthsorted">-</a>
+
+ qh_setdelnthsorted(qh, set, nth )
+ deletes nth element from sorted set
+
+ returns:
+ returns the element (use type conversion)
+
+ notes:
+ errors if nth invalid
+
+ see also:
+ setnew_delnthsorted
+
+ design:
+ setup points and check nth
+ copy remaining elements down one
+ update actual size
+*/
+void *qh_setdelnthsorted(qhT *qh, setT *set, int nth) {
+ void *elem;
+ setelemT *sizep;
+ setelemT *newp, *oldp;
+
+ sizep= SETsizeaddr_(set);
+ if (nth < 0 || (sizep->i && nth >= sizep->i-1) || nth >= set->maxsize) {
+ qh_fprintf(qh, qh->qhmem.ferr, 6175, "qhull internal error (qh_setdelnthsorted): nth %d is out-of-bounds for set:\n", nth);
+ qh_setprint(qh, qh->qhmem.ferr, "", set);
+ qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);
+ }
+ newp= (setelemT *)SETelemaddr_(set, nth, void);
+ elem= newp->p;
+ oldp= newp+1;
+ while (((newp++)->p= (oldp++)->p))
+ ; /* copy remaining elements and NULL */
+ if ((sizep->i--)==0) /* if was a full set */
+ sizep->i= set->maxsize; /* *sizep= (max size-1)+ 1 */
+ return elem;
+} /* setdelnthsorted */
+
+
+/*-<a href="qh-set_r.htm#TOC"
+ >-------------------------------<a name="setdelsorted">-</a>
+
+ qh_setdelsorted(set, oldelem )
+ deletes oldelem from sorted set
+
+ returns:
+ returns oldelem if it was deleted
+
+ notes:
+ set may be NULL
+
+ design:
+ locate oldelem in set
+ copy remaining elements down one
+ update actual size
+*/
+void *qh_setdelsorted(setT *set, void *oldelem) {
+ setelemT *sizep;
+ setelemT *newp, *oldp;
+
+ if (!set)
+ return NULL;
+ newp= (setelemT *)SETaddr_(set, void);
+ while(newp->p != oldelem && newp->p)
+ newp++;
+ if (newp->p) {
+ oldp= newp+1;
+ while (((newp++)->p= (oldp++)->p))
+ ; /* copy remaining elements */
+ sizep= SETsizeaddr_(set);
+ if ((sizep->i--)==0) /* if was a full set */
+ sizep->i= set->maxsize; /* *sizep= (max size-1)+ 1 */
+ return oldelem;
+ }
+ return NULL;
+} /* setdelsorted */
+
+
+/*-<a href="qh-set_r.htm#TOC"
+ >-------------------------------<a name="setduplicate">-</a>
+
+ qh_setduplicate(qh, set, elemsize )
+ duplicate a set of elemsize elements
+
+ notes:
+ use setcopy if retaining old elements
+
+ design:
+ create a new set
+ for each elem of the old set
+ create a newelem
+ append newelem to newset
+*/
+setT *qh_setduplicate(qhT *qh, setT *set, int elemsize) {
+ void *elem, **elemp, *newElem;
+ setT *newSet;
+ int size;
+
+ if (!(size= qh_setsize(qh, set)))
+ return NULL;
+ newSet= qh_setnew(qh, size);
+ FOREACHelem_(set) {
+ newElem= qh_memalloc(qh, elemsize);
+ memcpy(newElem, elem, (size_t)elemsize);
+ qh_setappend(qh, &newSet, newElem);
+ }
+ return newSet;
+} /* setduplicate */
+
+
+/*-<a href="qh-set_r.htm#TOC"
+ >-------------------------------<a name="setendpointer">-</a>
+
+ qh_setendpointer( set )
+ Returns pointer to NULL terminator of a set's elements
+ set can not be NULL
+
+*/
+void **qh_setendpointer(setT *set) {
+
+ setelemT *sizep= SETsizeaddr_(set);
+ int n= sizep->i;
+ return (n ? &set->e[n-1].p : &sizep->p);
+} /* qh_setendpointer */
+
+/*-<a href="qh-set_r.htm#TOC"
+ >-------------------------------<a name="setequal">-</a>
+
+ qh_setequal( setA, setB )
+ returns 1 if two sorted sets are equal, otherwise returns 0
+
+ notes:
+ either set may be NULL
+
+ design:
+ check size of each set
+ setup pointers
+ compare elements of each set
+*/
+int qh_setequal(setT *setA, setT *setB) {
+ void **elemAp, **elemBp;
+ int sizeA= 0, sizeB= 0;
+
+ if (setA) {
+ SETreturnsize_(setA, sizeA);
+ }
+ if (setB) {
+ SETreturnsize_(setB, sizeB);
+ }
+ if (sizeA != sizeB)
+ return 0;
+ if (!sizeA)
+ return 1;
+ elemAp= SETaddr_(setA, void);
+ elemBp= SETaddr_(setB, void);
+ if (!memcmp((char *)elemAp, (char *)elemBp, sizeA*SETelemsize))
+ return 1;
+ return 0;
+} /* setequal */
+
+
+/*-<a href="qh-set_r.htm#TOC"
+ >-------------------------------<a name="setequal_except">-</a>
+
+ qh_setequal_except( setA, skipelemA, setB, skipelemB )
+ returns 1 if sorted setA and setB are equal except for skipelemA & B
+
+ returns:
+ false if either skipelemA or skipelemB are missing
+
+ notes:
+ neither set may be NULL
+
+ if skipelemB is NULL,
+ can skip any one element of setB
+
+ design:
+ setup pointers
+ search for skipelemA, skipelemB, and mismatches
+ check results
+*/
+int qh_setequal_except(setT *setA, void *skipelemA, setT *setB, void *skipelemB) {
+ void **elemA, **elemB;
+ int skip=0;
+
+ elemA= SETaddr_(setA, void);
+ elemB= SETaddr_(setB, void);
+ while (1) {
+ if (*elemA == skipelemA) {
+ skip++;
+ elemA++;
+ }
+ if (skipelemB) {
+ if (*elemB == skipelemB) {
+ skip++;
+ elemB++;
+ }
+ }else if (*elemA != *elemB) {
+ skip++;
+ if (!(skipelemB= *elemB++))
+ return 0;
+ }
+ if (!*elemA)
+ break;
+ if (*elemA++ != *elemB++)
+ return 0;
+ }
+ if (skip != 2 || *elemB)
+ return 0;
+ return 1;
+} /* setequal_except */
+
+
+/*-<a href="qh-set_r.htm#TOC"
+ >-------------------------------<a name="setequal_skip">-</a>
+
+ qh_setequal_skip( setA, skipA, setB, skipB )
+ returns 1 if sorted setA and setB are equal except for elements skipA & B
+
+ returns:
+ false if different size
+
+ notes:
+ neither set may be NULL
+
+ design:
+ setup pointers
+ search for mismatches while skipping skipA and skipB
+*/
+int qh_setequal_skip(setT *setA, int skipA, setT *setB, int skipB) {
+ void **elemA, **elemB, **skipAp, **skipBp;
+
+ elemA= SETaddr_(setA, void);
+ elemB= SETaddr_(setB, void);
+ skipAp= SETelemaddr_(setA, skipA, void);
+ skipBp= SETelemaddr_(setB, skipB, void);
+ while (1) {
+ if (elemA == skipAp)
+ elemA++;
+ if (elemB == skipBp)
+ elemB++;
+ if (!*elemA)
+ break;
+ if (*elemA++ != *elemB++)
+ return 0;
+ }
+ if (*elemB)
+ return 0;
+ return 1;
+} /* setequal_skip */
+
+
+/*-<a href="qh-set_r.htm#TOC"
+ >-------------------------------<a name="setfree">-</a>
+
+ qh_setfree(qh, setp )
+ frees the space occupied by a sorted or unsorted set
+
+ returns:
+ sets setp to NULL
+
+ notes:
+ set may be NULL
+
+ design:
+ free array
+ free set
+*/
+void qh_setfree(qhT *qh, setT **setp) {
+ int size;
+ void **freelistp; /* used if !qh_NOmem by qh_memfree_() */
+
+ if (*setp) {
+ size= sizeof(setT) + ((*setp)->maxsize)*SETelemsize;
+ if (size <= qh->qhmem.LASTsize) {
+ qh_memfree_(qh, *setp, size, freelistp);
+ }else
+ qh_memfree(qh, *setp, size);
+ *setp= NULL;
+ }
+} /* setfree */
+
+
+/*-<a href="qh-set_r.htm#TOC"
+ >-------------------------------<a name="setfree2">-</a>
+
+ qh_setfree2(qh, setp, elemsize )
+ frees the space occupied by a set and its elements
+
+ notes:
+ set may be NULL
+
+ design:
+ free each element
+ free set
+*/
+void qh_setfree2(qhT *qh, setT **setp, int elemsize) {
+ void *elem, **elemp;
+
+ FOREACHelem_(*setp)
+ qh_memfree(qh, elem, elemsize);
+ qh_setfree(qh, setp);
+} /* setfree2 */
+
+
+
+/*-<a href="qh-set_r.htm#TOC"
+ >-------------------------------<a name="setfreelong">-</a>
+
+ qh_setfreelong(qh, setp )
+ frees a set only if it's in long memory
+
+ returns:
+ sets setp to NULL if it is freed
+
+ notes:
+ set may be NULL
+
+ design:
+ if set is large
+ free it
+*/
+void qh_setfreelong(qhT *qh, setT **setp) {
+ int size;
+
+ if (*setp) {
+ size= sizeof(setT) + ((*setp)->maxsize)*SETelemsize;
+ if (size > qh->qhmem.LASTsize) {
+ qh_memfree(qh, *setp, size);
+ *setp= NULL;
+ }
+ }
+} /* setfreelong */
+
+
+/*-<a href="qh-set_r.htm#TOC"
+ >-------------------------------<a name="setin">-</a>
+
+ qh_setin(set, setelem )
+ returns 1 if setelem is in a set, 0 otherwise
+
+ notes:
+ set may be NULL or unsorted
+
+ design:
+ scans set for setelem
+*/
+int qh_setin(setT *set, void *setelem) {
+ void *elem, **elemp;
+
+ FOREACHelem_(set) {
+ if (elem == setelem)
+ return 1;
+ }
+ return 0;
+} /* setin */
+
+
+/*-<a href="qh-set_r.htm#TOC"
+ >-------------------------------<a name="setindex">-</a>
+
+ qh_setindex(set, atelem )
+ returns the index of atelem in set.
+ returns -1, if not in set or maxsize wrong
+
+ notes:
+ set may be NULL and may contain nulls.
+ NOerrors returned (qh_pointid, QhullPoint::id)
+
+ design:
+ checks maxsize
+ scans set for atelem
+*/
+int qh_setindex(setT *set, void *atelem) {
+ void **elem;
+ int size, i;
+
+ if (!set)
+ return -1;
+ SETreturnsize_(set, size);
+ if (size > set->maxsize)
+ return -1;
+ elem= SETaddr_(set, void);
+ for (i=0; i < size; i++) {
+ if (*elem++ == atelem)
+ return i;
+ }
+ return -1;
+} /* setindex */
+
+
+/*-<a href="qh-set_r.htm#TOC"
+ >-------------------------------<a name="setlarger">-</a>
+
+ qh_setlarger(qh, oldsetp )
+ returns a larger set that contains all elements of *oldsetp
+
+ notes:
+ the set is at least twice as large
+ if temp set, updates qh->qhmem.tempstack
+
+ design:
+ creates a new set
+ copies the old set to the new set
+ updates pointers in tempstack
+ deletes the old set
+*/
+void qh_setlarger(qhT *qh, setT **oldsetp) {
+ int size= 1;
+ setT *newset, *set, **setp, *oldset;
+ setelemT *sizep;
+ setelemT *newp, *oldp;
+
+ if (*oldsetp) {
+ oldset= *oldsetp;
+ SETreturnsize_(oldset, size);
+ qh->qhmem.cntlarger++;
+ qh->qhmem.totlarger += size+1;
+ newset= qh_setnew(qh, 2 * size);
+ oldp= (setelemT *)SETaddr_(oldset, void);
+ newp= (setelemT *)SETaddr_(newset, void);
+ memcpy((char *)newp, (char *)oldp, (size_t)(size+1) * SETelemsize);
+ sizep= SETsizeaddr_(newset);
+ sizep->i= size+1;
+ FOREACHset_((setT *)qh->qhmem.tempstack) {
+ if (set == oldset)
+ *(setp-1)= newset;
+ }
+ qh_setfree(qh, oldsetp);
+ }else
+ newset= qh_setnew(qh, 3);
+ *oldsetp= newset;
+} /* setlarger */
+
+
+/*-<a href="qh-set_r.htm#TOC"
+ >-------------------------------<a name="setlast">-</a>
+
+ qh_setlast( set )
+ return last element of set or NULL (use type conversion)
+
+ notes:
+ set may be NULL
+
+ design:
+ return last element
+*/
+void *qh_setlast(setT *set) {
+ int size;
+
+ if (set) {
+ size= SETsizeaddr_(set)->i;
+ if (!size)
+ return SETelem_(set, set->maxsize - 1);
+ else if (size > 1)
+ return SETelem_(set, size - 2);
+ }
+ return NULL;
+} /* setlast */
+
+
+/*-<a href="qh-set_r.htm#TOC"
+ >-------------------------------<a name="setnew">-</a>
+
+ qh_setnew(qh, setsize )
+ creates and allocates space for a set
+
+ notes:
+ setsize means the number of elements (!including the NULL terminator)
+ use qh_settemp/qh_setfreetemp if set is temporary
+
+ design:
+ allocate memory for set
+ roundup memory if small set
+ initialize as empty set
+*/
+setT *qh_setnew(qhT *qh, int setsize) {
+ setT *set;
+ int sizereceived; /* used if !qh_NOmem */
+ int size;
+ void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */
+
+ if (!setsize)
+ setsize++;
+ size= sizeof(setT) + setsize * SETelemsize;
+ if (size>0 && size <= qh->qhmem.LASTsize) {
+ qh_memalloc_(qh, size, freelistp, set, setT);
+#ifndef qh_NOmem
+ sizereceived= qh->qhmem.sizetable[ qh->qhmem.indextable[size]];
+ if (sizereceived > size)
+ setsize += (sizereceived - size)/SETelemsize;
+#endif
+ }else
+ set= (setT*)qh_memalloc(qh, size);
+ set->maxsize= setsize;
+ set->e[setsize].i= 1;
+ set->e[0].p= NULL;
+ return(set);
+} /* setnew */
+
+
+/*-<a href="qh-set_r.htm#TOC"
+ >-------------------------------<a name="setnew_delnthsorted">-</a>
+
+ qh_setnew_delnthsorted(qh, set, size, nth, prepend )
+ creates a sorted set not containing nth element
+ if prepend, the first prepend elements are undefined
+
+ notes:
+ set must be defined
+ checks nth
+ see also: setdelnthsorted
+
+ design:
+ create new set
+ setup pointers and allocate room for prepend'ed entries
+ append head of old set to new set
+ append tail of old set to new set
+*/
+setT *qh_setnew_delnthsorted(qhT *qh, setT *set, int size, int nth, int prepend) {
+ setT *newset;
+ void **oldp, **newp;
+ int tailsize= size - nth -1, newsize;
+
+ if (tailsize < 0) {
+ qh_fprintf(qh, qh->qhmem.ferr, 6176, "qhull internal error (qh_setnew_delnthsorted): nth %d is out-of-bounds for set:\n", nth);
+ qh_setprint(qh, qh->qhmem.ferr, "", set);
+ qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);
+ }
+ newsize= size-1 + prepend;
+ newset= qh_setnew(qh, newsize);
+ newset->e[newset->maxsize].i= newsize+1; /* may be overwritten */
+ oldp= SETaddr_(set, void);
+ newp= SETaddr_(newset, void) + prepend;
+ switch (nth) {
+ case 0:
+ break;
+ case 1:
+ *(newp++)= *oldp++;
+ break;
+ case 2:
+ *(newp++)= *oldp++;
+ *(newp++)= *oldp++;
+ break;
+ case 3:
+ *(newp++)= *oldp++;
+ *(newp++)= *oldp++;
+ *(newp++)= *oldp++;
+ break;
+ case 4:
+ *(newp++)= *oldp++;
+ *(newp++)= *oldp++;
+ *(newp++)= *oldp++;
+ *(newp++)= *oldp++;
+ break;
+ default:
+ memcpy((char *)newp, (char *)oldp, (size_t)nth * SETelemsize);
+ newp += nth;
+ oldp += nth;
+ break;
+ }
+ oldp++;
+ switch (tailsize) {
+ case 0:
+ break;
+ case 1:
+ *(newp++)= *oldp++;
+ break;
+ case 2:
+ *(newp++)= *oldp++;
+ *(newp++)= *oldp++;
+ break;
+ case 3:
+ *(newp++)= *oldp++;
+ *(newp++)= *oldp++;
+ *(newp++)= *oldp++;
+ break;
+ case 4:
+ *(newp++)= *oldp++;
+ *(newp++)= *oldp++;
+ *(newp++)= *oldp++;
+ *(newp++)= *oldp++;
+ break;
+ default:
+ memcpy((char *)newp, (char *)oldp, (size_t)tailsize * SETelemsize);
+ newp += tailsize;
+ }
+ *newp= NULL;
+ return(newset);
+} /* setnew_delnthsorted */
+
+
+/*-<a href="qh-set_r.htm#TOC"
+ >-------------------------------<a name="setprint">-</a>
+
+ qh_setprint(qh, fp, string, set )
+ print set elements to fp with identifying string
+
+ notes:
+ never errors
+*/
+void qh_setprint(qhT *qh, FILE *fp, const char* string, setT *set) {
+ int size, k;
+
+ if (!set)
+ qh_fprintf(qh, fp, 9346, "%s set is null\n", string);
+ else {
+ SETreturnsize_(set, size);
+ qh_fprintf(qh, fp, 9347, "%s set=%p maxsize=%d size=%d elems=",
+ string, set, set->maxsize, size);
+ if (size > set->maxsize)
+ size= set->maxsize+1;
+ for (k=0; k < size; k++)
+ qh_fprintf(qh, fp, 9348, " %p", set->e[k].p);
+ qh_fprintf(qh, fp, 9349, "\n");
+ }
+} /* setprint */
+
+/*-<a href="qh-set_r.htm#TOC"
+ >-------------------------------<a name="setreplace">-</a>
+
+ qh_setreplace(qh, set, oldelem, newelem )
+ replaces oldelem in set with newelem
+
+ notes:
+ errors if oldelem not in the set
+ newelem may be NULL, but it turns the set into an indexed set (no FOREACH)
+
+ design:
+ find oldelem
+ replace with newelem
+*/
+void qh_setreplace(qhT *qh, setT *set, void *oldelem, void *newelem) {
+ void **elemp;
+
+ elemp= SETaddr_(set, void);
+ while (*elemp != oldelem && *elemp)
+ elemp++;
+ if (*elemp)
+ *elemp= newelem;
+ else {
+ qh_fprintf(qh, qh->qhmem.ferr, 6177, "qhull internal error (qh_setreplace): elem %p not found in set\n",
+ oldelem);
+ qh_setprint(qh, qh->qhmem.ferr, "", set);
+ qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);
+ }
+} /* setreplace */
+
+
+/*-<a href="qh-set_r.htm#TOC"
+ >-------------------------------<a name="setsize">-</a>
+
+ qh_setsize(qh, set )
+ returns the size of a set
+
+ notes:
+ errors if set's maxsize is incorrect
+ same as SETreturnsize_(set)
+ same code for qh_setsize [qset_r.c] and QhullSetBase::count
+
+ design:
+ determine actual size of set from maxsize
+*/
+int qh_setsize(qhT *qh, setT *set) {
+ int size;
+ setelemT *sizep;
+
+ if (!set)
+ return(0);
+ sizep= SETsizeaddr_(set);
+ if ((size= sizep->i)) {
+ size--;
+ if (size > set->maxsize) {
+ qh_fprintf(qh, qh->qhmem.ferr, 6178, "qhull internal error (qh_setsize): current set size %d is greater than maximum size %d\n",
+ size, set->maxsize);
+ qh_setprint(qh, qh->qhmem.ferr, "set: ", set);
+ qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);
+ }
+ }else
+ size= set->maxsize;
+ return size;
+} /* setsize */
+
+/*-<a href="qh-set_r.htm#TOC"
+ >-------------------------------<a name="settemp">-</a>
+
+ qh_settemp(qh, setsize )
+ return a stacked, temporary set of upto setsize elements
+
+ notes:
+ use settempfree or settempfree_all to release from qh->qhmem.tempstack
+ see also qh_setnew
+
+ design:
+ allocate set
+ append to qh->qhmem.tempstack
+
+*/
+setT *qh_settemp(qhT *qh, int setsize) {
+ setT *newset;
+
+ newset= qh_setnew(qh, setsize);
+ qh_setappend(qh, &qh->qhmem.tempstack, newset);
+ if (qh->qhmem.IStracing >= 5)
+ qh_fprintf(qh, qh->qhmem.ferr, 8123, "qh_settemp: temp set %p of %d elements, depth %d\n",
+ newset, newset->maxsize, qh_setsize(qh, qh->qhmem.tempstack));
+ return newset;
+} /* settemp */
+
+/*-<a href="qh-set_r.htm#TOC"
+ >-------------------------------<a name="settempfree">-</a>
+
+ qh_settempfree(qh, set )
+ free temporary set at top of qh->qhmem.tempstack
+
+ notes:
+ nop if set is NULL
+ errors if set not from previous qh_settemp
+
+ to locate errors:
+ use 'T2' to find source and then find mis-matching qh_settemp
+
+ design:
+ check top of qh->qhmem.tempstack
+ free it
+*/
+void qh_settempfree(qhT *qh, setT **set) {
+ setT *stackedset;
+
+ if (!*set)
+ return;
+ stackedset= qh_settemppop(qh);
+ if (stackedset != *set) {
+ qh_settemppush(qh, stackedset);
+ qh_fprintf(qh, qh->qhmem.ferr, 6179, "qhull internal error (qh_settempfree): set %p(size %d) was not last temporary allocated(depth %d, set %p, size %d)\n",
+ *set, qh_setsize(qh, *set), qh_setsize(qh, qh->qhmem.tempstack)+1,
+ stackedset, qh_setsize(qh, stackedset));
+ qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);
+ }
+ qh_setfree(qh, set);
+} /* settempfree */
+
+/*-<a href="qh-set_r.htm#TOC"
+ >-------------------------------<a name="settempfree_all">-</a>
+
+ qh_settempfree_all(qh)
+ free all temporary sets in qh->qhmem.tempstack
+
+ design:
+ for each set in tempstack
+ free set
+ free qh->qhmem.tempstack
+*/
+void qh_settempfree_all(qhT *qh) {
+ setT *set, **setp;
+
+ FOREACHset_(qh->qhmem.tempstack)
+ qh_setfree(qh, &set);
+ qh_setfree(qh, &qh->qhmem.tempstack);
+} /* settempfree_all */
+
+/*-<a href="qh-set_r.htm#TOC"
+ >-------------------------------<a name="settemppop">-</a>
+
+ qh_settemppop(qh)
+ pop and return temporary set from qh->qhmem.tempstack
+
+ notes:
+ the returned set is permanent
+
+ design:
+ pop and check top of qh->qhmem.tempstack
+*/
+setT *qh_settemppop(qhT *qh) {
+ setT *stackedset;
+
+ stackedset= (setT*)qh_setdellast(qh->qhmem.tempstack);
+ if (!stackedset) {
+ qh_fprintf(qh, qh->qhmem.ferr, 6180, "qhull internal error (qh_settemppop): pop from empty temporary stack\n");
+ qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);
+ }
+ if (qh->qhmem.IStracing >= 5)
+ qh_fprintf(qh, qh->qhmem.ferr, 8124, "qh_settemppop: depth %d temp set %p of %d elements\n",
+ qh_setsize(qh, qh->qhmem.tempstack)+1, stackedset, qh_setsize(qh, stackedset));
+ return stackedset;
+} /* settemppop */
+
+/*-<a href="qh-set_r.htm#TOC"
+ >-------------------------------<a name="settemppush">-</a>
+
+ qh_settemppush(qh, set )
+ push temporary set unto qh->qhmem.tempstack (makes it temporary)
+
+ notes:
+ duplicates settemp() for tracing
+
+ design:
+ append set to tempstack
+*/
+void qh_settemppush(qhT *qh, setT *set) {
+ if (!set) {
+ qh_fprintf(qh, qh->qhmem.ferr, 6267, "qhull error (qh_settemppush): can not push a NULL temp\n");
+ qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);
+ }
+ qh_setappend(qh, &qh->qhmem.tempstack, set);
+ if (qh->qhmem.IStracing >= 5)
+ qh_fprintf(qh, qh->qhmem.ferr, 8125, "qh_settemppush: depth %d temp set %p of %d elements\n",
+ qh_setsize(qh, qh->qhmem.tempstack), set, qh_setsize(qh, set));
+} /* settemppush */
+
+
+/*-<a href="qh-set_r.htm#TOC"
+ >-------------------------------<a name="settruncate">-</a>
+
+ qh_settruncate(qh, set, size )
+ truncate set to size elements
+
+ notes:
+ set must be defined
+
+ see:
+ SETtruncate_
+
+ design:
+ check size
+ update actual size of set
+*/
+void qh_settruncate(qhT *qh, setT *set, int size) {
+
+ if (size < 0 || size > set->maxsize) {
+ qh_fprintf(qh, qh->qhmem.ferr, 6181, "qhull internal error (qh_settruncate): size %d out of bounds for set:\n", size);
+ qh_setprint(qh, qh->qhmem.ferr, "", set);
+ qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);
+ }
+ set->e[set->maxsize].i= size+1; /* maybe overwritten */
+ set->e[size].p= NULL;
+} /* settruncate */
+
+/*-<a href="qh-set_r.htm#TOC"
+ >-------------------------------<a name="setunique">-</a>
+
+ qh_setunique(qh, set, elem )
+ add elem to unsorted set unless it is already in set
+
+ notes:
+ returns 1 if it is appended
+
+ design:
+ if elem not in set
+ append elem to set
+*/
+int qh_setunique(qhT *qh, setT **set, void *elem) {
+
+ if (!qh_setin(*set, elem)) {
+ qh_setappend(qh, set, elem);
+ return 1;
+ }
+ return 0;
+} /* setunique */
+
+/*-<a href="qh-set_r.htm#TOC"
+ >-------------------------------<a name="setzero">-</a>
+
+ qh_setzero(qh, set, index, size )
+ zero elements from index on
+ set actual size of set to size
+
+ notes:
+ set must be defined
+ the set becomes an indexed set (can not use FOREACH...)
+
+ see also:
+ qh_settruncate
+
+ design:
+ check index and size
+ update actual size
+ zero elements starting at e[index]
+*/
+void qh_setzero(qhT *qh, setT *set, int idx, int size) {
+ int count;
+
+ if (idx < 0 || idx >= size || size > set->maxsize) {
+ qh_fprintf(qh, qh->qhmem.ferr, 6182, "qhull internal error (qh_setzero): index %d or size %d out of bounds for set:\n", idx, size);
+ qh_setprint(qh, qh->qhmem.ferr, "", set);
+ qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);
+ }
+ set->e[set->maxsize].i= size+1; /* may be overwritten */
+ count= size - idx + 1; /* +1 for NULL terminator */
+ memset((char *)SETelemaddr_(set, idx, void), 0, (size_t)count * SETelemsize);
+} /* setzero */
+
+
diff --git a/xs/src/qhull/src/libqhull_r/qset_r.h b/xs/src/qhull/src/libqhull_r/qset_r.h
new file mode 100644
index 000000000..7ba199d6f
--- /dev/null
+++ b/xs/src/qhull/src/libqhull_r/qset_r.h
@@ -0,0 +1,502 @@
+/*<html><pre> -<a href="qh-set_r.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ qset_r.h
+ header file for qset_r.c that implements set
+
+ see qh-set_r.htm and qset_r.c
+
+ only uses mem_r.c, malloc/free
+
+ for error handling, writes message and calls
+ qh_errexit(qhT *qh, qhmem_ERRqhull, NULL, NULL);
+
+ set operations satisfy the following properties:
+ - sets have a max size, the actual size (if different) is stored at the end
+ - every set is NULL terminated
+ - sets may be sorted or unsorted, the caller must distinguish this
+
+ Copyright (c) 1993-2015 The Geometry Center.
+ $Id: //main/2015/qhull/src/libqhull_r/qset_r.h#4 $$Change: 2079 $
+ $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
+*/
+
+#ifndef qhDEFset
+#define qhDEFset 1
+
+#include <stdio.h>
+
+/*================= -structures- ===============*/
+
+#ifndef DEFsetT
+#define DEFsetT 1
+typedef struct setT setT; /* a set is a sorted or unsorted array of pointers */
+#endif
+
+#ifndef DEFqhT
+#define DEFqhT 1
+typedef struct qhT qhT; /* defined in libqhull_r.h */
+#endif
+
+/* [jan'15] Decided not to use countT. Most sets are small. The code uses signed tests */
+
+/*-<a href="qh-set_r.htm#TOC"
+>----------------------------------------</a><a name="setT">-</a>
+
+setT
+ a set or list of pointers with maximum size and actual size.
+
+variations:
+ unsorted, unique -- a list of unique pointers with NULL terminator
+ user guarantees uniqueness
+ sorted -- a sorted list of unique pointers with NULL terminator
+ qset_r.c guarantees uniqueness
+ unsorted -- a list of pointers terminated with NULL
+ indexed -- an array of pointers with NULL elements
+
+structure for set of n elements:
+
+ --------------
+ | maxsize
+ --------------
+ | e[0] - a pointer, may be NULL for indexed sets
+ --------------
+ | e[1]
+
+ --------------
+ | ...
+ --------------
+ | e[n-1]
+ --------------
+ | e[n] = NULL
+ --------------
+ | ...
+ --------------
+ | e[maxsize] - n+1 or NULL (determines actual size of set)
+ --------------
+
+*/
+
+/*-- setelemT -- internal type to allow both pointers and indices
+*/
+typedef union setelemT setelemT;
+union setelemT {
+ void *p;
+ int i; /* integer used for e[maxSize] */
+};
+
+struct setT {
+ int maxsize; /* maximum number of elements (except NULL) */
+ setelemT e[1]; /* array of pointers, tail is NULL */
+ /* last slot (unless NULL) is actual size+1
+ e[maxsize]==NULL or e[e[maxsize]-1]==NULL */
+ /* this may generate a warning since e[] contains
+ maxsize elements */
+};
+
+/*=========== -constants- =========================*/
+
+/*-<a href="qh-set_r.htm#TOC"
+ >-----------------------------------</a><a name="SETelemsize">-</a>
+
+ SETelemsize
+ size of a set element in bytes
+*/
+#define SETelemsize ((int)sizeof(setelemT))
+
+
+/*=========== -macros- =========================*/
+
+/*-<a href="qh-set_r.htm#TOC"
+ >-----------------------------------</a><a name="FOREACHsetelement_">-</a>
+
+ FOREACHsetelement_(type, set, variable)
+ define FOREACH iterator
+
+ declare:
+ assumes *variable and **variablep are declared
+ no space in "variable)" [DEC Alpha cc compiler]
+
+ each iteration:
+ variable is set element
+ variablep is one beyond variable.
+
+ to repeat an element:
+ variablep--; / *repeat* /
+
+ at exit:
+ variable is NULL at end of loop
+
+ example:
+ #define FOREACHfacet_( facets ) FOREACHsetelement_( facetT, facets, facet )
+
+ notes:
+ use FOREACHsetelement_i_() if need index or include NULLs
+
+ WARNING:
+ nested loops can't use the same variable (define another FOREACH)
+
+ needs braces if nested inside another FOREACH
+ this includes intervening blocks, e.g. FOREACH...{ if () FOREACH...} )
+*/
+#define FOREACHsetelement_(type, set, variable) \
+ if (((variable= NULL), set)) for (\
+ variable##p= (type **)&((set)->e[0].p); \
+ (variable= *variable##p++);)
+
+/*-<a href="qh-set_r.htm#TOC"
+ >----------------------------------------</a><a name="FOREACHsetelement_i_">-</a>
+
+ FOREACHsetelement_i_(qh, type, set, variable)
+ define indexed FOREACH iterator
+
+ declare:
+ type *variable, variable_n, variable_i;
+
+ each iteration:
+ variable is set element, may be NULL
+ variable_i is index, variable_n is qh_setsize()
+
+ to repeat an element:
+ variable_i--; variable_n-- repeats for deleted element
+
+ at exit:
+ variable==NULL and variable_i==variable_n
+
+ example:
+ #define FOREACHfacet_i_( qh, facets ) FOREACHsetelement_i_( qh, facetT, facets, facet )
+
+ WARNING:
+ nested loops can't use the same variable (define another FOREACH)
+
+ needs braces if nested inside another FOREACH
+ this includes intervening blocks, e.g. FOREACH...{ if () FOREACH...} )
+*/
+#define FOREACHsetelement_i_(qh, type, set, variable) \
+ if (((variable= NULL), set)) for (\
+ variable##_i= 0, variable= (type *)((set)->e[0].p), \
+ variable##_n= qh_setsize(qh, set);\
+ variable##_i < variable##_n;\
+ variable= (type *)((set)->e[++variable##_i].p) )
+
+/*-<a href="qh-set_r.htm#TOC"
+ >--------------------------------------</a><a name="FOREACHsetelementreverse_">-</a>
+
+ FOREACHsetelementreverse_(qh, type, set, variable)-
+ define FOREACH iterator in reverse order
+
+ declare:
+ assumes *variable and **variablep are declared
+ also declare 'int variabletemp'
+
+ each iteration:
+ variable is set element
+
+ to repeat an element:
+ variabletemp++; / *repeat* /
+
+ at exit:
+ variable is NULL
+
+ example:
+ #define FOREACHvertexreverse_( vertices ) FOREACHsetelementreverse_( vertexT, vertices, vertex )
+
+ notes:
+ use FOREACHsetelementreverse12_() to reverse first two elements
+ WARNING: needs braces if nested inside another FOREACH
+*/
+#define FOREACHsetelementreverse_(qh, type, set, variable) \
+ if (((variable= NULL), set)) for (\
+ variable##temp= qh_setsize(qh, set)-1, variable= qh_setlast(qh, set);\
+ variable; variable= \
+ ((--variable##temp >= 0) ? SETelemt_(set, variable##temp, type) : NULL))
+
+/*-<a href="qh-set_r.htm#TOC"
+ >-----------------------------------</a><a name="FOREACHsetelementreverse12_">-</a>
+
+ FOREACHsetelementreverse12_(type, set, variable)-
+ define FOREACH iterator with e[1] and e[0] reversed
+
+ declare:
+ assumes *variable and **variablep are declared
+
+ each iteration:
+ variable is set element
+ variablep is one after variable.
+
+ to repeat an element:
+ variablep--; / *repeat* /
+
+ at exit:
+ variable is NULL at end of loop
+
+ example
+ #define FOREACHvertexreverse12_( vertices ) FOREACHsetelementreverse12_( vertexT, vertices, vertex )
+
+ notes:
+ WARNING: needs braces if nested inside another FOREACH
+*/
+#define FOREACHsetelementreverse12_(type, set, variable) \
+ if (((variable= NULL), set)) for (\
+ variable##p= (type **)&((set)->e[1].p); \
+ (variable= *variable##p); \
+ variable##p == ((type **)&((set)->e[0].p))?variable##p += 2: \
+ (variable##p == ((type **)&((set)->e[1].p))?variable##p--:variable##p++))
+
+/*-<a href="qh-set_r.htm#TOC"
+ >-----------------------------------</a><a name="FOREACHelem_">-</a>
+
+ FOREACHelem_( set )-
+ iterate elements in a set
+
+ declare:
+ void *elem, *elemp;
+
+ each iteration:
+ elem is set element
+ elemp is one beyond
+
+ to repeat an element:
+ elemp--; / *repeat* /
+
+ at exit:
+ elem == NULL at end of loop
+
+ example:
+ FOREACHelem_(set) {
+
+ notes:
+ WARNING: needs braces if nested inside another FOREACH
+*/
+#define FOREACHelem_(set) FOREACHsetelement_(void, set, elem)
+
+/*-<a href="qh-set_r.htm#TOC"
+ >-----------------------------------</a><a name="FOREACHset_">-</a>
+
+ FOREACHset_( set )-
+ iterate a set of sets
+
+ declare:
+ setT *set, **setp;
+
+ each iteration:
+ set is set element
+ setp is one beyond
+
+ to repeat an element:
+ setp--; / *repeat* /
+
+ at exit:
+ set == NULL at end of loop
+
+ example
+ FOREACHset_(sets) {
+
+ notes:
+ WARNING: needs braces if nested inside another FOREACH
+*/
+#define FOREACHset_(sets) FOREACHsetelement_(setT, sets, set)
+
+/*-<a href="qh-set_r.htm#TOC"
+ >-----------------------------------------</a><a name="SETindex_">-</a>
+
+ SETindex_( set, elem )
+ return index of elem in set
+
+ notes:
+ for use with FOREACH iteration
+ WARN64 -- Maximum set size is 2G
+
+ example:
+ i= SETindex_(ridges, ridge)
+*/
+#define SETindex_(set, elem) ((int)((void **)elem##p - (void **)&(set)->e[1].p))
+
+/*-<a href="qh-set_r.htm#TOC"
+ >---------------------------------------</a><a name="SETref_">-</a>
+
+ SETref_( elem )
+ l.h.s. for modifying the current element in a FOREACH iteration
+
+ example:
+ SETref_(ridge)= anotherridge;
+*/
+#define SETref_(elem) (elem##p[-1])
+
+/*-<a href="qh-set_r.htm#TOC"
+ >---------------------------------------</a><a name="SETelem_">-</a>
+
+ SETelem_(set, n)
+ return the n'th element of set
+
+ notes:
+ assumes that n is valid [0..size] and that set is defined
+ use SETelemt_() for type cast
+*/
+#define SETelem_(set, n) ((set)->e[n].p)
+
+/*-<a href="qh-set_r.htm#TOC"
+ >---------------------------------------</a><a name="SETelemt_">-</a>
+
+ SETelemt_(set, n, type)
+ return the n'th element of set as a type
+
+ notes:
+ assumes that n is valid [0..size] and that set is defined
+*/
+#define SETelemt_(set, n, type) ((type*)((set)->e[n].p))
+
+/*-<a href="qh-set_r.htm#TOC"
+ >---------------------------------------</a><a name="SETelemaddr_">-</a>
+
+ SETelemaddr_(set, n, type)
+ return address of the n'th element of a set
+
+ notes:
+ assumes that n is valid [0..size] and set is defined
+*/
+#define SETelemaddr_(set, n, type) ((type **)(&((set)->e[n].p)))
+
+/*-<a href="qh-set_r.htm#TOC"
+ >---------------------------------------</a><a name="SETfirst_">-</a>
+
+ SETfirst_(set)
+ return first element of set
+
+*/
+#define SETfirst_(set) ((set)->e[0].p)
+
+/*-<a href="qh-set_r.htm#TOC"
+ >---------------------------------------</a><a name="SETfirstt_">-</a>
+
+ SETfirstt_(set, type)
+ return first element of set as a type
+
+*/
+#define SETfirstt_(set, type) ((type*)((set)->e[0].p))
+
+/*-<a href="qh-set_r.htm#TOC"
+ >---------------------------------------</a><a name="SETsecond_">-</a>
+
+ SETsecond_(set)
+ return second element of set
+
+*/
+#define SETsecond_(set) ((set)->e[1].p)
+
+/*-<a href="qh-set_r.htm#TOC"
+ >---------------------------------------</a><a name="SETsecondt_">-</a>
+
+ SETsecondt_(set, type)
+ return second element of set as a type
+*/
+#define SETsecondt_(set, type) ((type*)((set)->e[1].p))
+
+/*-<a href="qh-set_r.htm#TOC"
+ >---------------------------------------</a><a name="SETaddr_">-</a>
+
+ SETaddr_(set, type)
+ return address of set's elements
+*/
+#define SETaddr_(set,type) ((type **)(&((set)->e[0].p)))
+
+/*-<a href="qh-set_r.htm#TOC"
+ >---------------------------------------</a><a name="SETreturnsize_">-</a>
+
+ SETreturnsize_(set, size)
+ return size of a set
+
+ notes:
+ set must be defined
+ use qh_setsize(qhT *qh, set) unless speed is critical
+*/
+#define SETreturnsize_(set, size) (((size)= ((set)->e[(set)->maxsize].i))?(--(size)):((size)= (set)->maxsize))
+
+/*-<a href="qh-set_r.htm#TOC"
+ >---------------------------------------</a><a name="SETempty_">-</a>
+
+ SETempty_(set)
+ return true(1) if set is empty
+
+ notes:
+ set may be NULL
+*/
+#define SETempty_(set) (!set || (SETfirst_(set) ? 0 : 1))
+
+/*-<a href="qh-set_r.htm#TOC"
+ >-------------------------------<a name="SETsizeaddr_">-</a>
+
+ SETsizeaddr_(set)
+ return pointer to 'actual size+1' of set (set CANNOT be NULL!!)
+ Its type is setelemT* for strict aliasing
+ All SETelemaddr_ must be cast to setelemT
+
+
+ notes:
+ *SETsizeaddr==NULL or e[*SETsizeaddr-1].p==NULL
+*/
+#define SETsizeaddr_(set) (&((set)->e[(set)->maxsize]))
+
+/*-<a href="qh-set_r.htm#TOC"
+ >---------------------------------------</a><a name="SETtruncate_">-</a>
+
+ SETtruncate_(set, size)
+ truncate set to size
+
+ see:
+ qh_settruncate()
+
+*/
+#define SETtruncate_(set, size) {set->e[set->maxsize].i= size+1; /* maybe overwritten */ \
+ set->e[size].p= NULL;}
+
+/*======= prototypes in alphabetical order ============*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void qh_setaddsorted(qhT *qh, setT **setp, void *elem);
+void qh_setaddnth(qhT *qh, setT **setp, int nth, void *newelem);
+void qh_setappend(qhT *qh, setT **setp, void *elem);
+void qh_setappend_set(qhT *qh, setT **setp, setT *setA);
+void qh_setappend2ndlast(qhT *qh, setT **setp, void *elem);
+void qh_setcheck(qhT *qh, setT *set, const char *tname, unsigned id);
+void qh_setcompact(qhT *qh, setT *set);
+setT *qh_setcopy(qhT *qh, setT *set, int extra);
+void *qh_setdel(setT *set, void *elem);
+void *qh_setdellast(setT *set);
+void *qh_setdelnth(qhT *qh, setT *set, int nth);
+void *qh_setdelnthsorted(qhT *qh, setT *set, int nth);
+void *qh_setdelsorted(setT *set, void *newelem);
+setT *qh_setduplicate(qhT *qh, setT *set, int elemsize);
+void **qh_setendpointer(setT *set);
+int qh_setequal(setT *setA, setT *setB);
+int qh_setequal_except(setT *setA, void *skipelemA, setT *setB, void *skipelemB);
+int qh_setequal_skip(setT *setA, int skipA, setT *setB, int skipB);
+void qh_setfree(qhT *qh, setT **set);
+void qh_setfree2(qhT *qh, setT **setp, int elemsize);
+void qh_setfreelong(qhT *qh, setT **set);
+int qh_setin(setT *set, void *setelem);
+int qh_setindex(setT *set, void *setelem);
+void qh_setlarger(qhT *qh, setT **setp);
+void *qh_setlast(setT *set);
+setT *qh_setnew(qhT *qh, int size);
+setT *qh_setnew_delnthsorted(qhT *qh, setT *set, int size, int nth, int prepend);
+void qh_setprint(qhT *qh, FILE *fp, const char* string, setT *set);
+void qh_setreplace(qhT *qh, setT *set, void *oldelem, void *newelem);
+int qh_setsize(qhT *qh, setT *set);
+setT *qh_settemp(qhT *qh, int setsize);
+void qh_settempfree(qhT *qh, setT **set);
+void qh_settempfree_all(qhT *qh);
+setT *qh_settemppop(qhT *qh);
+void qh_settemppush(qhT *qh, setT *set);
+void qh_settruncate(qhT *qh, setT *set, int size);
+int qh_setunique(qhT *qh, setT **set, void *elem);
+void qh_setzero(qhT *qh, setT *set, int idx, int size);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* qhDEFset */
diff --git a/xs/src/qhull/src/libqhull_r/random_r.c b/xs/src/qhull/src/libqhull_r/random_r.c
new file mode 100644
index 000000000..1fefb51c3
--- /dev/null
+++ b/xs/src/qhull/src/libqhull_r/random_r.c
@@ -0,0 +1,247 @@
+/*<html><pre> -<a href="index_r.htm#TOC"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ random_r.c and utilities
+ Park & Miller's minimimal standard random number generator
+ argc/argv conversion
+
+ Used by rbox. Do not use 'qh'
+*/
+
+#include "libqhull_r.h"
+#include "random_r.h"
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef _MSC_VER /* Microsoft Visual C++ -- warning level 4 */
+#pragma warning( disable : 4706) /* assignment within conditional function */
+#pragma warning( disable : 4996) /* function was declared deprecated(strcpy, localtime, etc.) */
+#endif
+
+/*-<a href="qh-globa_r.htm#TOC"
+ >-------------------------------</a><a name="argv_to_command">-</a>
+
+ qh_argv_to_command(argc, argv, command, max_size )
+
+ build command from argc/argv
+ max_size is at least
+
+ returns:
+ a space-delimited string of options (just as typed)
+ returns false if max_size is too short
+
+ notes:
+ silently removes
+ makes option string easy to input and output
+ matches qh_argv_to_command_size()
+
+ argc may be 0
+*/
+int qh_argv_to_command(int argc, char *argv[], char* command, int max_size) {
+ int i, remaining;
+ char *s;
+ *command= '\0'; /* max_size > 0 */
+
+ if (argc) {
+ if ((s= strrchr( argv[0], '\\')) /* get filename w/o .exe extension */
+ || (s= strrchr( argv[0], '/')))
+ s++;
+ else
+ s= argv[0];
+ if ((int)strlen(s) < max_size) /* WARN64 */
+ strcpy(command, s);
+ else
+ goto error_argv;
+ if ((s= strstr(command, ".EXE"))
+ || (s= strstr(command, ".exe")))
+ *s= '\0';
+ }
+ for (i=1; i < argc; i++) {
+ s= argv[i];
+ remaining= max_size - (int)strlen(command) - (int)strlen(s) - 2; /* WARN64 */
+ if (!*s || strchr(s, ' ')) {
+ char *t= command + strlen(command);
+ remaining -= 2;
+ if (remaining < 0) {
+ goto error_argv;
+ }
+ *t++= ' ';
+ *t++= '"';
+ while (*s) {
+ if (*s == '"') {
+ if (--remaining < 0)
+ goto error_argv;
+ *t++= '\\';
+ }
+ *t++= *s++;
+ }
+ *t++= '"';
+ *t= '\0';
+ }else if (remaining < 0) {
+ goto error_argv;
+ }else
+ strcat(command, " ");
+ strcat(command, s);
+ }
+ return 1;
+
+error_argv:
+ return 0;
+} /* argv_to_command */
+
+/*-<a href="qh-globa_r.htm#TOC"
+>-------------------------------</a><a name="argv_to_command_size">-</a>
+
+qh_argv_to_command_size(argc, argv )
+
+ return size to allocate for qh_argv_to_command()
+
+notes:
+ argc may be 0
+ actual size is usually shorter
+*/
+int qh_argv_to_command_size(int argc, char *argv[]) {
+ unsigned int count= 1; /* null-terminator if argc==0 */
+ int i;
+ char *s;
+
+ for (i=0; i<argc; i++){
+ count += (int)strlen(argv[i]) + 1; /* WARN64 */
+ if (i>0 && strchr(argv[i], ' ')) {
+ count += 2; /* quote delimiters */
+ for (s=argv[i]; *s; s++) {
+ if (*s == '"') {
+ count++;
+ }
+ }
+ }
+ }
+ return count;
+} /* argv_to_command_size */
+
+/*-<a href="qh-geom_r.htm#TOC"
+ >-------------------------------</a><a name="rand">-</a>
+
+ qh_rand()
+ qh_srand(qh, seed )
+ generate pseudo-random number between 1 and 2^31 -2
+
+ notes:
+ For qhull and rbox, called from qh_RANDOMint(),etc. [user.h]
+
+ From Park & Miller's minimal standard random number generator
+ Communications of the ACM, 31:1192-1201, 1988.
+ Does not use 0 or 2^31 -1
+ this is silently enforced by qh_srand()
+ Can make 'Rn' much faster by moving qh_rand to qh_distplane
+*/
+
+/* Global variables and constants */
+
+#define qh_rand_a 16807
+#define qh_rand_m 2147483647
+#define qh_rand_q 127773 /* m div a */
+#define qh_rand_r 2836 /* m mod a */
+
+int qh_rand(qhT *qh) {
+ int lo, hi, test;
+ int seed = qh->last_random;
+
+ hi = seed / qh_rand_q; /* seed div q */
+ lo = seed % qh_rand_q; /* seed mod q */
+ test = qh_rand_a * lo - qh_rand_r * hi;
+ if (test > 0)
+ seed= test;
+ else
+ seed= test + qh_rand_m;
+ qh->last_random= seed;
+ /* seed = seed < qh_RANDOMmax/2 ? 0 : qh_RANDOMmax; for testing */
+ /* seed = qh_RANDOMmax; for testing */
+ return seed;
+} /* rand */
+
+void qh_srand(qhT *qh, int seed) {
+ if (seed < 1)
+ qh->last_random= 1;
+ else if (seed >= qh_rand_m)
+ qh->last_random= qh_rand_m - 1;
+ else
+ qh->last_random= seed;
+} /* qh_srand */
+
+/*-<a href="qh-geom_r.htm#TOC"
+>-------------------------------</a><a name="randomfactor">-</a>
+
+qh_randomfactor(qh, scale, offset )
+ return a random factor r * scale + offset
+
+notes:
+ qh.RANDOMa/b are defined in global_r.c
+ qh_RANDOMint requires 'qh'
+*/
+realT qh_randomfactor(qhT *qh, realT scale, realT offset) {
+ realT randr;
+
+ randr= qh_RANDOMint;
+ return randr * scale + offset;
+} /* randomfactor */
+
+/*-<a href="qh-geom_r.htm#TOC"
+>-------------------------------</a><a name="randommatrix">-</a>
+
+qh_randommatrix(qh, buffer, dim, rows )
+ generate a random dim X dim matrix in range [-1,1]
+ assumes buffer is [dim+1, dim]
+
+ returns:
+ sets buffer to random numbers
+ sets rows to rows of buffer
+ sets row[dim] as scratch row
+
+ notes:
+ qh_RANDOMint requires 'qh'
+*/
+void qh_randommatrix(qhT *qh, realT *buffer, int dim, realT **rows) {
+ int i, k;
+ realT **rowi, *coord, realr;
+
+ coord= buffer;
+ rowi= rows;
+ for (i=0; i < dim; i++) {
+ *(rowi++)= coord;
+ for (k=0; k < dim; k++) {
+ realr= qh_RANDOMint;
+ *(coord++)= 2.0 * realr/(qh_RANDOMmax+1) - 1.0;
+ }
+ }
+ *rowi= coord;
+} /* randommatrix */
+
+/*-<a href="qh-globa_r.htm#TOC"
+ >-------------------------------</a><a name="strtol">-</a>
+
+ qh_strtol( s, endp) qh_strtod( s, endp)
+ internal versions of strtol() and strtod()
+ does not skip trailing spaces
+ notes:
+ some implementations of strtol()/strtod() skip trailing spaces
+*/
+double qh_strtod(const char *s, char **endp) {
+ double result;
+
+ result= strtod(s, endp);
+ if (s < (*endp) && (*endp)[-1] == ' ')
+ (*endp)--;
+ return result;
+} /* strtod */
+
+int qh_strtol(const char *s, char **endp) {
+ int result;
+
+ result= (int) strtol(s, endp, 10); /* WARN64 */
+ if (s< (*endp) && (*endp)[-1] == ' ')
+ (*endp)--;
+ return result;
+} /* strtol */
diff --git a/xs/src/qhull/src/libqhull_r/random_r.h b/xs/src/qhull/src/libqhull_r/random_r.h
new file mode 100644
index 000000000..dc884b33c
--- /dev/null
+++ b/xs/src/qhull/src/libqhull_r/random_r.h
@@ -0,0 +1,41 @@
+/*<html><pre> -<a href="qh-geom_r.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ random.h
+ header file for random and utility routines
+
+ see qh-geom_r.htm and random_r.c
+
+ Copyright (c) 1993-2015 The Geometry Center.
+ $Id: //main/2015/qhull/src/libqhull_r/random_r.h#4 $$Change: 2079 $
+ $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
+*/
+
+#ifndef qhDEFrandom
+#define qhDEFrandom 1
+
+#include "libqhull_r.h"
+
+/*============= prototypes in alphabetical order ======= */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int qh_argv_to_command(int argc, char *argv[], char* command, int max_size);
+int qh_argv_to_command_size(int argc, char *argv[]);
+int qh_rand(qhT *qh);
+void qh_srand(qhT *qh, int seed);
+realT qh_randomfactor(qhT *qh, realT scale, realT offset);
+void qh_randommatrix(qhT *qh, realT *buffer, int dim, realT **row);
+int qh_strtol(const char *s, char **endp);
+double qh_strtod(const char *s, char **endp);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* qhDEFrandom */
+
+
+
diff --git a/xs/src/qhull/src/libqhull_r/rboxlib_r.c b/xs/src/qhull/src/libqhull_r/rboxlib_r.c
new file mode 100644
index 000000000..6c0c7e35e
--- /dev/null
+++ b/xs/src/qhull/src/libqhull_r/rboxlib_r.c
@@ -0,0 +1,842 @@
+/*<html><pre> -<a href="index_r.htm#TOC"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ rboxlib_r.c
+ Generate input points
+
+ notes:
+ For documentation, see prompt[] of rbox_r.c
+ 50 points generated for 'rbox D4'
+
+ WARNING:
+ incorrect range if qh_RANDOMmax is defined wrong (user_r.h)
+*/
+
+#include "libqhull_r.h" /* First for user_r.h */
+#include "random_r.h"
+
+#include <ctype.h>
+#include <limits.h>
+#include <math.h>
+#include <setjmp.h>
+#include <string.h>
+#include <time.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef _MSC_VER /* Microsoft Visual C++ */
+#pragma warning( disable : 4706) /* assignment within conditional expression. */
+#pragma warning( disable : 4996) /* this function (strncat,sprintf,strcpy) or variable may be unsafe. */
+#endif
+
+#define MAXdim 200
+#define PI 3.1415926535897932384
+
+/* ------------------------------ prototypes ----------------*/
+int qh_roundi(qhT *qh, double a);
+void qh_out1(qhT *qh, double a);
+void qh_out2n(qhT *qh, double a, double b);
+void qh_out3n(qhT *qh, double a, double b, double c);
+void qh_outcoord(qhT *qh, int iscdd, double *coord, int dim);
+void qh_outcoincident(qhT *qh, int coincidentpoints, double radius, int iscdd, double *coord, int dim);
+
+void qh_fprintf_rbox(qhT *qh, FILE *fp, int msgcode, const char *fmt, ... );
+void qh_free(void *mem);
+void *qh_malloc(size_t size);
+int qh_rand(qhT *qh);
+void qh_srand(qhT *qh, int seed);
+
+/*-<a href="qh-qhull_r.htm#TOC"
+ >-------------------------------</a><a name="rboxpoints">-</a>
+
+ qh_rboxpoints(qh, rbox_command )
+ Generate points to qh->fout according to rbox options
+ Report errors on qh->ferr
+
+ returns:
+ 0 (qh_ERRnone) on success
+ 1 (qh_ERRinput) on input error
+ 4 (qh_ERRmem) on memory error
+ 5 (qh_ERRqhull) on internal error
+
+ notes:
+ To avoid using stdio, redefine qh_malloc, qh_free, and qh_fprintf_rbox (user_r.c)
+
+ design:
+ Straight line code (consider defining a struct and functions):
+
+ Parse arguments into variables
+ Determine the number of points
+ Generate the points
+*/
+int qh_rboxpoints(qhT *qh, char* rbox_command) {
+ int i,j,k;
+ int gendim;
+ int coincidentcount=0, coincidenttotal=0, coincidentpoints=0;
+ int cubesize, diamondsize, seed=0, count, apex;
+ int dim=3 , numpoints= 0, totpoints, addpoints=0;
+ int issphere=0, isaxis=0, iscdd= 0, islens= 0, isregular=0, iswidth=0, addcube=0;
+ int isgap=0, isspiral=0, NOcommand= 0, adddiamond=0;
+ int israndom=0, istime=0;
+ int isbox=0, issimplex=0, issimplex2=0, ismesh=0;
+ double width=0.0, gap=0.0, radius=0.0, coincidentradius=0.0;
+ double coord[MAXdim], offset, meshm=3.0, meshn=4.0, meshr=5.0;
+ double *coordp, *simplex= NULL, *simplexp;
+ int nthroot, mult[MAXdim];
+ double norm, factor, randr, rangap, lensangle= 0, lensbase= 1;
+ double anglediff, angle, x, y, cube= 0.0, diamond= 0.0;
+ double box= qh_DEFAULTbox; /* scale all numbers before output */
+ double randmax= qh_RANDOMmax;
+ char command[200], seedbuf[200];
+ char *s= command, *t, *first_point= NULL;
+ time_t timedata;
+ int exitcode;
+
+ exitcode= setjmp(qh->rbox_errexit);
+ if (exitcode) {
+ /* same code for error exit and normal return, qh->NOerrexit is set */
+ if (simplex)
+ qh_free(simplex);
+ return exitcode;
+ }
+
+ *command= '\0';
+ strncat(command, rbox_command, sizeof(command)-strlen(command)-1);
+
+ while (*s && !isspace(*s)) /* skip program name */
+ s++;
+ while (*s) {
+ while (*s && isspace(*s))
+ s++;
+ if (*s == '-')
+ s++;
+ if (!*s)
+ break;
+ if (isdigit(*s)) {
+ numpoints= qh_strtol(s, &s);
+ continue;
+ }
+ /* ============= read flags =============== */
+ switch (*s++) {
+ case 'c':
+ addcube= 1;
+ t= s;
+ while (isspace(*t))
+ t++;
+ if (*t == 'G')
+ cube= qh_strtod(++t, &s);
+ break;
+ case 'd':
+ adddiamond= 1;
+ t= s;
+ while (isspace(*t))
+ t++;
+ if (*t == 'G')
+ diamond= qh_strtod(++t, &s);
+ break;
+ case 'h':
+ iscdd= 1;
+ break;
+ case 'l':
+ isspiral= 1;
+ break;
+ case 'n':
+ NOcommand= 1;
+ break;
+ case 'r':
+ isregular= 1;
+ break;
+ case 's':
+ issphere= 1;
+ break;
+ case 't':
+ istime= 1;
+ if (isdigit(*s)) {
+ seed= qh_strtol(s, &s);
+ israndom= 0;
+ }else
+ israndom= 1;
+ break;
+ case 'x':
+ issimplex= 1;
+ break;
+ case 'y':
+ issimplex2= 1;
+ break;
+ case 'z':
+ qh->rbox_isinteger= 1;
+ break;
+ case 'B':
+ box= qh_strtod(s, &s);
+ isbox= 1;
+ break;
+ case 'C':
+ if (*s)
+ coincidentpoints= qh_strtol(s, &s);
+ if (*s == ',') {
+ ++s;
+ coincidentradius= qh_strtod(s, &s);
+ }
+ if (*s == ',') {
+ ++s;
+ coincidenttotal= qh_strtol(s, &s);
+ }
+ if (*s && !isspace(*s)) {
+ qh_fprintf_rbox(qh, qh->ferr, 7080, "rbox error: arguments for 'Cn,r,m' are not 'int', 'float', and 'int'. Remaining string is '%s'\n", s);
+ qh_errexit_rbox(qh, qh_ERRinput);
+ }
+ if (coincidentpoints==0){
+ qh_fprintf_rbox(qh, qh->ferr, 6268, "rbox error: missing arguments for 'Cn,r,m' where n is the number of coincident points, r is the radius (default 0.0), and m is the number of points\n");
+ qh_errexit_rbox(qh, qh_ERRinput);
+ }
+ if (coincidentpoints<0 || coincidenttotal<0 || coincidentradius<0.0){
+ qh_fprintf_rbox(qh, qh->ferr, 6269, "rbox error: negative arguments for 'Cn,m,r' where n (%d) is the number of coincident points, m (%d) is the number of points, and r (%.2g) is the radius (default 0.0)\n", coincidentpoints, coincidenttotal, coincidentradius);
+ qh_errexit_rbox(qh, qh_ERRinput);
+ }
+ break;
+ case 'D':
+ dim= qh_strtol(s, &s);
+ if (dim < 1
+ || dim > MAXdim) {
+ qh_fprintf_rbox(qh, qh->ferr, 6189, "rbox error: dimension, D%d, out of bounds (>=%d or <=0)", dim, MAXdim);
+ qh_errexit_rbox(qh, qh_ERRinput);
+ }
+ break;
+ case 'G':
+ if (isdigit(*s))
+ gap= qh_strtod(s, &s);
+ else
+ gap= 0.5;
+ isgap= 1;
+ break;
+ case 'L':
+ if (isdigit(*s))
+ radius= qh_strtod(s, &s);
+ else
+ radius= 10;
+ islens= 1;
+ break;
+ case 'M':
+ ismesh= 1;
+ if (*s)
+ meshn= qh_strtod(s, &s);
+ if (*s == ',') {
+ ++s;
+ meshm= qh_strtod(s, &s);
+ }else
+ meshm= 0.0;
+ if (*s == ',') {
+ ++s;
+ meshr= qh_strtod(s, &s);
+ }else
+ meshr= sqrt(meshn*meshn + meshm*meshm);
+ if (*s && !isspace(*s)) {
+ qh_fprintf_rbox(qh, qh->ferr, 7069, "rbox warning: assuming 'M3,4,5' since mesh args are not integers or reals\n");
+ meshn= 3.0, meshm=4.0, meshr=5.0;
+ }
+ break;
+ case 'O':
+ qh->rbox_out_offset= qh_strtod(s, &s);
+ break;
+ case 'P':
+ if (!first_point)
+ first_point= s-1;
+ addpoints++;
+ while (*s && !isspace(*s)) /* read points later */
+ s++;
+ break;
+ case 'W':
+ width= qh_strtod(s, &s);
+ iswidth= 1;
+ break;
+ case 'Z':
+ if (isdigit(*s))
+ radius= qh_strtod(s, &s);
+ else
+ radius= 1.0;
+ isaxis= 1;
+ break;
+ default:
+ qh_fprintf_rbox(qh, qh->ferr, 7070, "rbox error: unknown flag at %s.\nExecute 'rbox' without arguments for documentation.\n", s);
+ qh_errexit_rbox(qh, qh_ERRinput);
+ }
+ if (*s && !isspace(*s)) {
+ qh_fprintf_rbox(qh, qh->ferr, 7071, "rbox error: missing space between flags at %s.\n", s);
+ qh_errexit_rbox(qh, qh_ERRinput);
+ }
+ }
+
+ /* ============= defaults, constants, and sizes =============== */
+ if (qh->rbox_isinteger && !isbox)
+ box= qh_DEFAULTzbox;
+ if (addcube) {
+ cubesize= (int)floor(ldexp(1.0,dim)+0.5);
+ if (cube == 0.0)
+ cube= box;
+ }else
+ cubesize= 0;
+ if (adddiamond) {
+ diamondsize= 2*dim;
+ if (diamond == 0.0)
+ diamond= box;
+ }else
+ diamondsize= 0;
+ if (islens) {
+ if (isaxis) {
+ qh_fprintf_rbox(qh, qh->ferr, 6190, "rbox error: can not combine 'Ln' with 'Zn'\n");
+ qh_errexit_rbox(qh, qh_ERRinput);
+ }
+ if (radius <= 1.0) {
+ qh_fprintf_rbox(qh, qh->ferr, 6191, "rbox error: lens radius %.2g should be greater than 1.0\n",
+ radius);
+ qh_errexit_rbox(qh, qh_ERRinput);
+ }
+ lensangle= asin(1.0/radius);
+ lensbase= radius * cos(lensangle);
+ }
+
+ if (!numpoints) {
+ if (issimplex2)
+ ; /* ok */
+ else if (isregular + issimplex + islens + issphere + isaxis + isspiral + iswidth + ismesh) {
+ qh_fprintf_rbox(qh, qh->ferr, 6192, "rbox error: missing count\n");
+ qh_errexit_rbox(qh, qh_ERRinput);
+ }else if (adddiamond + addcube + addpoints)
+ ; /* ok */
+ else {
+ numpoints= 50; /* ./rbox D4 is the test case */
+ issphere= 1;
+ }
+ }
+ if ((issimplex + islens + isspiral + ismesh > 1)
+ || (issimplex + issphere + isspiral + ismesh > 1)) {
+ qh_fprintf_rbox(qh, qh->ferr, 6193, "rbox error: can only specify one of 'l', 's', 'x', 'Ln', or 'Mn,m,r' ('Ln s' is ok).\n");
+ qh_errexit_rbox(qh, qh_ERRinput);
+ }
+ if (coincidentpoints>0 && (numpoints == 0 || coincidenttotal > numpoints)) {
+ qh_fprintf_rbox(qh, qh->ferr, 6270, "rbox error: 'Cn,r,m' requested n coincident points for each of m points. Either there is no points or m (%d) is greater than the number of points (%d).\n", coincidenttotal, numpoints);
+ qh_errexit_rbox(qh, qh_ERRinput);
+ }
+ if (coincidenttotal == 0)
+ coincidenttotal= numpoints;
+
+ /* ============= print header with total points =============== */
+ if (issimplex || ismesh)
+ totpoints= numpoints;
+ else if (issimplex2)
+ totpoints= numpoints+dim+1;
+ else if (isregular) {
+ totpoints= numpoints;
+ if (dim == 2) {
+ if (islens)
+ totpoints += numpoints - 2;
+ }else if (dim == 3) {
+ if (islens)
+ totpoints += 2 * numpoints;
+ else if (isgap)
+ totpoints += 1 + numpoints;
+ else
+ totpoints += 2;
+ }
+ }else
+ totpoints= numpoints + isaxis;
+ totpoints += cubesize + diamondsize + addpoints;
+ totpoints += coincidentpoints*coincidenttotal;
+
+ /* ============= seed randoms =============== */
+ if (istime == 0) {
+ for (s=command; *s; s++) {
+ if (issimplex2 && *s == 'y') /* make 'y' same seed as 'x' */
+ i= 'x';
+ else
+ i= *s;
+ seed= 11*seed + i;
+ }
+ }else if (israndom) {
+ seed= (int)time(&timedata);
+ sprintf(seedbuf, " t%d", seed); /* appends an extra t, not worth removing */
+ strncat(command, seedbuf, sizeof(command)-strlen(command)-1);
+ t= strstr(command, " t ");
+ if (t)
+ strcpy(t+1, t+3); /* remove " t " */
+ } /* else, seed explicitly set to n */
+ qh_RANDOMseed_(qh, seed);
+
+ /* ============= print header =============== */
+
+ if (iscdd)
+ qh_fprintf_rbox(qh, qh->fout, 9391, "%s\nbegin\n %d %d %s\n",
+ NOcommand ? "" : command,
+ totpoints, dim+1,
+ qh->rbox_isinteger ? "integer" : "real");
+ else if (NOcommand)
+ qh_fprintf_rbox(qh, qh->fout, 9392, "%d\n%d\n", dim, totpoints);
+ else
+ /* qh_fprintf_rbox special cases 9393 to append 'command' to the RboxPoints.comment() */
+ qh_fprintf_rbox(qh, qh->fout, 9393, "%d %s\n%d\n", dim, command, totpoints);
+
+ /* ============= explicit points =============== */
+ if ((s= first_point)) {
+ while (s && *s) { /* 'P' */
+ count= 0;
+ if (iscdd)
+ qh_out1(qh, 1.0);
+ while (*++s) {
+ qh_out1(qh, qh_strtod(s, &s));
+ count++;
+ if (isspace(*s) || !*s)
+ break;
+ if (*s != ',') {
+ qh_fprintf_rbox(qh, qh->ferr, 6194, "rbox error: missing comma after coordinate in %s\n\n", s);
+ qh_errexit_rbox(qh, qh_ERRinput);
+ }
+ }
+ if (count < dim) {
+ for (k=dim-count; k--; )
+ qh_out1(qh, 0.0);
+ }else if (count > dim) {
+ qh_fprintf_rbox(qh, qh->ferr, 6195, "rbox error: %d coordinates instead of %d coordinates in %s\n\n",
+ count, dim, s);
+ qh_errexit_rbox(qh, qh_ERRinput);
+ }
+ qh_fprintf_rbox(qh, qh->fout, 9394, "\n");
+ while ((s= strchr(s, 'P'))) {
+ if (isspace(s[-1]))
+ break;
+ }
+ }
+ }
+
+ /* ============= simplex distribution =============== */
+ if (issimplex+issimplex2) {
+ if (!(simplex= (double*)qh_malloc( dim * (dim+1) * sizeof(double)))) {
+ qh_fprintf_rbox(qh, qh->ferr, 6196, "rbox error: insufficient memory for simplex\n");
+ qh_errexit_rbox(qh, qh_ERRmem); /* qh_ERRmem */
+ }
+ simplexp= simplex;
+ if (isregular) {
+ for (i=0; i<dim; i++) {
+ for (k=0; k<dim; k++)
+ *(simplexp++)= i==k ? 1.0 : 0.0;
+ }
+ for (k=0; k<dim; k++)
+ *(simplexp++)= -1.0;
+ }else {
+ for (i=0; i<dim+1; i++) {
+ for (k=0; k<dim; k++) {
+ randr= qh_RANDOMint;
+ *(simplexp++)= 2.0 * randr/randmax - 1.0;
+ }
+ }
+ }
+ if (issimplex2) {
+ simplexp= simplex;
+ for (i=0; i<dim+1; i++) {
+ if (iscdd)
+ qh_out1(qh, 1.0);
+ for (k=0; k<dim; k++)
+ qh_out1(qh, *(simplexp++) * box);
+ qh_fprintf_rbox(qh, qh->fout, 9395, "\n");
+ }
+ }
+ for (j=0; j<numpoints; j++) {
+ if (iswidth)
+ apex= qh_RANDOMint % (dim+1);
+ else
+ apex= -1;
+ for (k=0; k<dim; k++)
+ coord[k]= 0.0;
+ norm= 0.0;
+ for (i=0; i<dim+1; i++) {
+ randr= qh_RANDOMint;
+ factor= randr/randmax;
+ if (i == apex)
+ factor *= width;
+ norm += factor;
+ for (k=0; k<dim; k++) {
+ simplexp= simplex + i*dim + k;
+ coord[k] += factor * (*simplexp);
+ }
+ }
+ for (k=0; k<dim; k++)
+ coord[k] *= box/norm;
+ qh_outcoord(qh, iscdd, coord, dim);
+ if(coincidentcount++ < coincidenttotal)
+ qh_outcoincident(qh, coincidentpoints, coincidentradius, iscdd, coord, dim);
+ }
+ isregular= 0; /* continue with isbox */
+ numpoints= 0;
+ }
+
+ /* ============= mesh distribution =============== */
+ if (ismesh) {
+ nthroot= (int)(pow((double)numpoints, 1.0/dim) + 0.99999);
+ for (k=dim; k--; )
+ mult[k]= 0;
+ for (i=0; i < numpoints; i++) {
+ coordp= coord;
+ for (k=0; k < dim; k++) {
+ if (k == 0)
+ *(coordp++)= mult[0] * meshn + mult[1] * (-meshm);
+ else if (k == 1)
+ *(coordp++)= mult[0] * meshm + mult[1] * meshn;
+ else
+ *(coordp++)= mult[k] * meshr;
+ }
+ qh_outcoord(qh, iscdd, coord, dim);
+ if(coincidentcount++ < coincidenttotal)
+ qh_outcoincident(qh, coincidentpoints, coincidentradius, iscdd, coord, dim);
+ for (k=0; k < dim; k++) {
+ if (++mult[k] < nthroot)
+ break;
+ mult[k]= 0;
+ }
+ }
+ }
+ /* ============= regular points for 's' =============== */
+ else if (isregular && !islens) {
+ if (dim != 2 && dim != 3) {
+ qh_free(simplex);
+ qh_fprintf_rbox(qh, qh->ferr, 6197, "rbox error: regular points can be used only in 2-d and 3-d\n\n");
+ qh_errexit_rbox(qh, qh_ERRinput);
+ }
+ if (!isaxis || radius == 0.0) {
+ isaxis= 1;
+ radius= 1.0;
+ }
+ if (dim == 3) {
+ if (iscdd)
+ qh_out1(qh, 1.0);
+ qh_out3n(qh, 0.0, 0.0, -box);
+ if (!isgap) {
+ if (iscdd)
+ qh_out1(qh, 1.0);
+ qh_out3n(qh, 0.0, 0.0, box);
+ }
+ }
+ angle= 0.0;
+ anglediff= 2.0 * PI/numpoints;
+ for (i=0; i < numpoints; i++) {
+ angle += anglediff;
+ x= radius * cos(angle);
+ y= radius * sin(angle);
+ if (dim == 2) {
+ if (iscdd)
+ qh_out1(qh, 1.0);
+ qh_out2n(qh, x*box, y*box);
+ }else {
+ norm= sqrt(1.0 + x*x + y*y);
+ if (iscdd)
+ qh_out1(qh, 1.0);
+ qh_out3n(qh, box*x/norm, box*y/norm, box/norm);
+ if (isgap) {
+ x *= 1-gap;
+ y *= 1-gap;
+ norm= sqrt(1.0 + x*x + y*y);
+ if (iscdd)
+ qh_out1(qh, 1.0);
+ qh_out3n(qh, box*x/norm, box*y/norm, box/norm);
+ }
+ }
+ }
+ }
+ /* ============= regular points for 'r Ln D2' =============== */
+ else if (isregular && islens && dim == 2) {
+ double cos_0;
+
+ angle= lensangle;
+ anglediff= 2 * lensangle/(numpoints - 1);
+ cos_0= cos(lensangle);
+ for (i=0; i < numpoints; i++, angle -= anglediff) {
+ x= radius * sin(angle);
+ y= radius * (cos(angle) - cos_0);
+ if (iscdd)
+ qh_out1(qh, 1.0);
+ qh_out2n(qh, x*box, y*box);
+ if (i != 0 && i != numpoints - 1) {
+ if (iscdd)
+ qh_out1(qh, 1.0);
+ qh_out2n(qh, x*box, -y*box);
+ }
+ }
+ }
+ /* ============= regular points for 'r Ln D3' =============== */
+ else if (isregular && islens && dim != 2) {
+ if (dim != 3) {
+ qh_free(simplex);
+ qh_fprintf_rbox(qh, qh->ferr, 6198, "rbox error: regular points can be used only in 2-d and 3-d\n\n");
+ qh_errexit_rbox(qh, qh_ERRinput);
+ }
+ angle= 0.0;
+ anglediff= 2* PI/numpoints;
+ if (!isgap) {
+ isgap= 1;
+ gap= 0.5;
+ }
+ offset= sqrt(radius * radius - (1-gap)*(1-gap)) - lensbase;
+ for (i=0; i < numpoints; i++, angle += anglediff) {
+ x= cos(angle);
+ y= sin(angle);
+ if (iscdd)
+ qh_out1(qh, 1.0);
+ qh_out3n(qh, box*x, box*y, 0.0);
+ x *= 1-gap;
+ y *= 1-gap;
+ if (iscdd)
+ qh_out1(qh, 1.0);
+ qh_out3n(qh, box*x, box*y, box * offset);
+ if (iscdd)
+ qh_out1(qh, 1.0);
+ qh_out3n(qh, box*x, box*y, -box * offset);
+ }
+ }
+ /* ============= apex of 'Zn' distribution + gendim =============== */
+ else {
+ if (isaxis) {
+ gendim= dim-1;
+ if (iscdd)
+ qh_out1(qh, 1.0);
+ for (j=0; j < gendim; j++)
+ qh_out1(qh, 0.0);
+ qh_out1(qh, -box);
+ qh_fprintf_rbox(qh, qh->fout, 9398, "\n");
+ }else if (islens)
+ gendim= dim-1;
+ else
+ gendim= dim;
+ /* ============= generate random point in unit cube =============== */
+ for (i=0; i < numpoints; i++) {
+ norm= 0.0;
+ for (j=0; j < gendim; j++) {
+ randr= qh_RANDOMint;
+ coord[j]= 2.0 * randr/randmax - 1.0;
+ norm += coord[j] * coord[j];
+ }
+ norm= sqrt(norm);
+ /* ============= dim-1 point of 'Zn' distribution ========== */
+ if (isaxis) {
+ if (!isgap) {
+ isgap= 1;
+ gap= 1.0;
+ }
+ randr= qh_RANDOMint;
+ rangap= 1.0 - gap * randr/randmax;
+ factor= radius * rangap / norm;
+ for (j=0; j<gendim; j++)
+ coord[j]= factor * coord[j];
+ /* ============= dim-1 point of 'Ln s' distribution =========== */
+ }else if (islens && issphere) {
+ if (!isgap) {
+ isgap= 1;
+ gap= 1.0;
+ }
+ randr= qh_RANDOMint;
+ rangap= 1.0 - gap * randr/randmax;
+ factor= rangap / norm;
+ for (j=0; j<gendim; j++)
+ coord[j]= factor * coord[j];
+ /* ============= dim-1 point of 'Ln' distribution ========== */
+ }else if (islens && !issphere) {
+ if (!isgap) {
+ isgap= 1;
+ gap= 1.0;
+ }
+ j= qh_RANDOMint % gendim;
+ if (coord[j] < 0)
+ coord[j]= -1.0 - coord[j] * gap;
+ else
+ coord[j]= 1.0 - coord[j] * gap;
+ /* ============= point of 'l' distribution =============== */
+ }else if (isspiral) {
+ if (dim != 3) {
+ qh_free(simplex);
+ qh_fprintf_rbox(qh, qh->ferr, 6199, "rbox error: spiral distribution is available only in 3d\n\n");
+ qh_errexit_rbox(qh, qh_ERRinput);
+ }
+ coord[0]= cos(2*PI*i/(numpoints - 1));
+ coord[1]= sin(2*PI*i/(numpoints - 1));
+ coord[2]= 2.0*(double)i/(double)(numpoints-1) - 1.0;
+ /* ============= point of 's' distribution =============== */
+ }else if (issphere) {
+ factor= 1.0/norm;
+ if (iswidth) {
+ randr= qh_RANDOMint;
+ factor *= 1.0 - width * randr/randmax;
+ }
+ for (j=0; j<dim; j++)
+ coord[j]= factor * coord[j];
+ }
+ /* ============= project 'Zn s' point in to sphere =============== */
+ if (isaxis && issphere) {
+ coord[dim-1]= 1.0;
+ norm= 1.0;
+ for (j=0; j<gendim; j++)
+ norm += coord[j] * coord[j];
+ norm= sqrt(norm);
+ for (j=0; j<dim; j++)
+ coord[j]= coord[j] / norm;
+ if (iswidth) {
+ randr= qh_RANDOMint;
+ coord[dim-1] *= 1 - width * randr/randmax;
+ }
+ /* ============= project 'Zn' point onto cube =============== */
+ }else if (isaxis && !issphere) { /* not very interesting */
+ randr= qh_RANDOMint;
+ coord[dim-1]= 2.0 * randr/randmax - 1.0;
+ /* ============= project 'Ln' point out to sphere =============== */
+ }else if (islens) {
+ coord[dim-1]= lensbase;
+ for (j=0, norm= 0; j<dim; j++)
+ norm += coord[j] * coord[j];
+ norm= sqrt(norm);
+ for (j=0; j<dim; j++)
+ coord[j]= coord[j] * radius/ norm;
+ coord[dim-1] -= lensbase;
+ if (iswidth) {
+ randr= qh_RANDOMint;
+ coord[dim-1] *= 1 - width * randr/randmax;
+ }
+ if (qh_RANDOMint > randmax/2)
+ coord[dim-1]= -coord[dim-1];
+ /* ============= project 'Wn' point toward boundary =============== */
+ }else if (iswidth && !issphere) {
+ j= qh_RANDOMint % gendim;
+ if (coord[j] < 0)
+ coord[j]= -1.0 - coord[j] * width;
+ else
+ coord[j]= 1.0 - coord[j] * width;
+ }
+ /* ============= scale point to box =============== */
+ for (k=0; k<dim; k++)
+ coord[k]= coord[k] * box;
+
+ /* ============= write output =============== */
+ qh_outcoord(qh, iscdd, coord, dim);
+ if(coincidentcount++ < coincidenttotal)
+ qh_outcoincident(qh, coincidentpoints, coincidentradius, iscdd, coord, dim);
+ }
+ }
+
+ /* ============= write cube vertices =============== */
+ if (addcube) {
+ for (j=0; j<cubesize; j++) {
+ if (iscdd)
+ qh_out1(qh, 1.0);
+ for (k=dim-1; k>=0; k--) {
+ if (j & ( 1 << k))
+ qh_out1(qh, cube);
+ else
+ qh_out1(qh, -cube);
+ }
+ qh_fprintf_rbox(qh, qh->fout, 9400, "\n");
+ }
+ }
+
+ /* ============= write diamond vertices =============== */
+ if (adddiamond) {
+ for (j=0; j<diamondsize; j++) {
+ if (iscdd)
+ qh_out1(qh, 1.0);
+ for (k=dim-1; k>=0; k--) {
+ if (j/2 != k)
+ qh_out1(qh, 0.0);
+ else if (j & 0x1)
+ qh_out1(qh, diamond);
+ else
+ qh_out1(qh, -diamond);
+ }
+ qh_fprintf_rbox(qh, qh->fout, 9401, "\n");
+ }
+ }
+
+ if (iscdd)
+ qh_fprintf_rbox(qh, qh->fout, 9402, "end\nhull\n");
+
+ /* same code for error exit and normal return */
+ qh_free(simplex);
+ return qh_ERRnone;
+} /* rboxpoints */
+
+/*------------------------------------------------
+outxxx - output functions for qh_rboxpoints
+*/
+int qh_roundi(qhT *qh, double a) {
+ if (a < 0.0) {
+ if (a - 0.5 < INT_MIN) {
+ qh_fprintf_rbox(qh, qh->ferr, 6200, "rbox input error: negative coordinate %2.2g is too large. Reduce 'Bn'\n", a);
+ qh_errexit_rbox(qh, qh_ERRinput);
+ }
+ return (int)(a - 0.5);
+ }else {
+ if (a + 0.5 > INT_MAX) {
+ qh_fprintf_rbox(qh, qh->ferr, 6201, "rbox input error: coordinate %2.2g is too large. Reduce 'Bn'\n", a);
+ qh_errexit_rbox(qh, qh_ERRinput);
+ }
+ return (int)(a + 0.5);
+ }
+} /* qh_roundi */
+
+void qh_out1(qhT *qh, double a) {
+
+ if (qh->rbox_isinteger)
+ qh_fprintf_rbox(qh, qh->fout, 9403, "%d ", qh_roundi(qh, a+qh->rbox_out_offset));
+ else
+ qh_fprintf_rbox(qh, qh->fout, 9404, qh_REAL_1, a+qh->rbox_out_offset);
+} /* qh_out1 */
+
+void qh_out2n(qhT *qh, double a, double b) {
+
+ if (qh->rbox_isinteger)
+ qh_fprintf_rbox(qh, qh->fout, 9405, "%d %d\n", qh_roundi(qh, a+qh->rbox_out_offset), qh_roundi(qh, b+qh->rbox_out_offset));
+ else
+ qh_fprintf_rbox(qh, qh->fout, 9406, qh_REAL_2n, a+qh->rbox_out_offset, b+qh->rbox_out_offset);
+} /* qh_out2n */
+
+void qh_out3n(qhT *qh, double a, double b, double c) {
+
+ if (qh->rbox_isinteger)
+ qh_fprintf_rbox(qh, qh->fout, 9407, "%d %d %d\n", qh_roundi(qh, a+qh->rbox_out_offset), qh_roundi(qh, b+qh->rbox_out_offset), qh_roundi(qh, c+qh->rbox_out_offset));
+ else
+ qh_fprintf_rbox(qh, qh->fout, 9408, qh_REAL_3n, a+qh->rbox_out_offset, b+qh->rbox_out_offset, c+qh->rbox_out_offset);
+} /* qh_out3n */
+
+void qh_outcoord(qhT *qh, int iscdd, double *coord, int dim) {
+ double *p= coord;
+ int k;
+
+ if (iscdd)
+ qh_out1(qh, 1.0);
+ for (k=0; k < dim; k++)
+ qh_out1(qh, *(p++));
+ qh_fprintf_rbox(qh, qh->fout, 9396, "\n");
+} /* qh_outcoord */
+
+void qh_outcoincident(qhT *qh, int coincidentpoints, double radius, int iscdd, double *coord, int dim) {
+ double *p;
+ double randr, delta;
+ int i,k;
+ double randmax= qh_RANDOMmax;
+
+ for (i= 0; i<coincidentpoints; i++) {
+ p= coord;
+ if (iscdd)
+ qh_out1(qh, 1.0);
+ for (k=0; k < dim; k++) {
+ randr= qh_RANDOMint;
+ delta= 2.0 * randr/randmax - 1.0; /* -1..+1 */
+ delta *= radius;
+ qh_out1(qh, *(p++) + delta);
+ }
+ qh_fprintf_rbox(qh, qh->fout, 9410, "\n");
+ }
+} /* qh_outcoincident */
+
+/*------------------------------------------------
+ Only called from qh_rboxpoints or qh_fprintf_rbox
+ qh_fprintf_rbox is only called from qh_rboxpoints
+*/
+void qh_errexit_rbox(qhT *qh, int exitcode)
+{
+ longjmp(qh->rbox_errexit, exitcode);
+} /* qh_errexit_rbox */
+
diff --git a/xs/src/qhull/src/libqhull_r/stat_r.c b/xs/src/qhull/src/libqhull_r/stat_r.c
new file mode 100644
index 000000000..0f3ff0d3d
--- /dev/null
+++ b/xs/src/qhull/src/libqhull_r/stat_r.c
@@ -0,0 +1,682 @@
+/*<html><pre> -<a href="qh-stat_r.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ stat_r.c
+ contains all statistics that are collected for qhull
+
+ see qh-stat_r.htm and stat_r.h
+
+ Copyright (c) 1993-2015 The Geometry Center.
+ $Id: //main/2015/qhull/src/libqhull_r/stat_r.c#5 $$Change: 2062 $
+ $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $
+*/
+
+#include "qhull_ra.h"
+
+/*========== functions in alphabetic order ================*/
+
+/*-<a href="qh-stat_r.htm#TOC"
+ >-------------------------------</a><a name="allstatA">-</a>
+
+ qh_allstatA()
+ define statistics in groups of 20
+
+ notes:
+ (otherwise, 'gcc -O2' uses too much memory)
+ uses qhstat.next
+*/
+void qh_allstatA(qhT *qh) {
+
+ /* zdef_(type,name,doc,average) */
+ zzdef_(zdoc, Zdoc2, "precision statistics", -1);
+ zdef_(zinc, Znewvertex, NULL, -1);
+ zdef_(wadd, Wnewvertex, "ave. distance of a new vertex to a facet(!0s)", Znewvertex);
+ zzdef_(wmax, Wnewvertexmax, "max. distance of a new vertex to a facet", -1);
+ zdef_(wmax, Wvertexmax, "max. distance of an output vertex to a facet", -1);
+ zdef_(wmin, Wvertexmin, "min. distance of an output vertex to a facet", -1);
+ zdef_(wmin, Wmindenom, "min. denominator in hyperplane computation", -1);
+
+ qh->qhstat.precision= qh->qhstat.next; /* call qh_precision for each of these */
+ zzdef_(zdoc, Zdoc3, "precision problems (corrected unless 'Q0' or an error)", -1);
+ zzdef_(zinc, Zcoplanarridges, "coplanar half ridges in output", -1);
+ zzdef_(zinc, Zconcaveridges, "concave half ridges in output", -1);
+ zzdef_(zinc, Zflippedfacets, "flipped facets", -1);
+ zzdef_(zinc, Zcoplanarhorizon, "coplanar horizon facets for new vertices", -1);
+ zzdef_(zinc, Zcoplanarpart, "coplanar points during partitioning", -1);
+ zzdef_(zinc, Zminnorm, "degenerate hyperplanes recomputed with gaussian elimination", -1);
+ zzdef_(zinc, Znearlysingular, "nearly singular or axis-parallel hyperplanes", -1);
+ zzdef_(zinc, Zback0, "zero divisors during back substitute", -1);
+ zzdef_(zinc, Zgauss0, "zero divisors during gaussian elimination", -1);
+ zzdef_(zinc, Zmultiridge, "ridges with multiple neighbors", -1);
+}
+void qh_allstatB(qhT *qh) {
+ zzdef_(zdoc, Zdoc1, "summary information", -1);
+ zdef_(zinc, Zvertices, "number of vertices in output", -1);
+ zdef_(zinc, Znumfacets, "number of facets in output", -1);
+ zdef_(zinc, Znonsimplicial, "number of non-simplicial facets in output", -1);
+ zdef_(zinc, Znowsimplicial, "number of simplicial facets that were merged", -1);
+ zdef_(zinc, Znumridges, "number of ridges in output", -1);
+ zdef_(zadd, Znumridges, "average number of ridges per facet", Znumfacets);
+ zdef_(zmax, Zmaxridges, "maximum number of ridges", -1);
+ zdef_(zadd, Znumneighbors, "average number of neighbors per facet", Znumfacets);
+ zdef_(zmax, Zmaxneighbors, "maximum number of neighbors", -1);
+ zdef_(zadd, Znumvertices, "average number of vertices per facet", Znumfacets);
+ zdef_(zmax, Zmaxvertices, "maximum number of vertices", -1);
+ zdef_(zadd, Znumvneighbors, "average number of neighbors per vertex", Zvertices);
+ zdef_(zmax, Zmaxvneighbors, "maximum number of neighbors", -1);
+ zdef_(wadd, Wcpu, "cpu seconds for qhull after input", -1);
+ zdef_(zinc, Ztotvertices, "vertices created altogether", -1);
+ zzdef_(zinc, Zsetplane, "facets created altogether", -1);
+ zdef_(zinc, Ztotridges, "ridges created altogether", -1);
+ zdef_(zinc, Zpostfacets, "facets before post merge", -1);
+ zdef_(zadd, Znummergetot, "average merges per facet(at most 511)", Znumfacets);
+ zdef_(zmax, Znummergemax, " maximum merges for a facet(at most 511)", -1);
+ zdef_(zinc, Zangle, NULL, -1);
+ zdef_(wadd, Wangle, "average angle(cosine) of facet normals for all ridges", Zangle);
+ zdef_(wmax, Wanglemax, " maximum angle(cosine) of facet normals across a ridge", -1);
+ zdef_(wmin, Wanglemin, " minimum angle(cosine) of facet normals across a ridge", -1);
+ zdef_(wadd, Wareatot, "total area of facets", -1);
+ zdef_(wmax, Wareamax, " maximum facet area", -1);
+ zdef_(wmin, Wareamin, " minimum facet area", -1);
+}
+void qh_allstatC(qhT *qh) {
+ zdef_(zdoc, Zdoc9, "build hull statistics", -1);
+ zzdef_(zinc, Zprocessed, "points processed", -1);
+ zzdef_(zinc, Zretry, "retries due to precision problems", -1);
+ zdef_(wmax, Wretrymax, " max. random joggle", -1);
+ zdef_(zmax, Zmaxvertex, "max. vertices at any one time", -1);
+ zdef_(zinc, Ztotvisible, "ave. visible facets per iteration", Zprocessed);
+ zdef_(zinc, Zinsidevisible, " ave. visible facets without an horizon neighbor", Zprocessed);
+ zdef_(zadd, Zvisfacettot, " ave. facets deleted per iteration", Zprocessed);
+ zdef_(zmax, Zvisfacetmax, " maximum", -1);
+ zdef_(zadd, Zvisvertextot, "ave. visible vertices per iteration", Zprocessed);
+ zdef_(zmax, Zvisvertexmax, " maximum", -1);
+ zdef_(zinc, Ztothorizon, "ave. horizon facets per iteration", Zprocessed);
+ zdef_(zadd, Znewfacettot, "ave. new or merged facets per iteration", Zprocessed);
+ zdef_(zmax, Znewfacetmax, " maximum(includes initial simplex)", -1);
+ zdef_(wadd, Wnewbalance, "average new facet balance", Zprocessed);
+ zdef_(wadd, Wnewbalance2, " standard deviation", -1);
+ zdef_(wadd, Wpbalance, "average partition balance", Zpbalance);
+ zdef_(wadd, Wpbalance2, " standard deviation", -1);
+ zdef_(zinc, Zpbalance, " number of trials", -1);
+ zdef_(zinc, Zsearchpoints, "searches of all points for initial simplex", -1);
+ zdef_(zinc, Zdetsimplex, "determinants computed(area & initial hull)", -1);
+ zdef_(zinc, Znoarea, "determinants not computed because vertex too low", -1);
+ zdef_(zinc, Znotmax, "points ignored(!above max_outside)", -1);
+ zdef_(zinc, Znotgood, "points ignored(!above a good facet)", -1);
+ zdef_(zinc, Znotgoodnew, "points ignored(didn't create a good new facet)", -1);
+ zdef_(zinc, Zgoodfacet, "good facets found", -1);
+ zzdef_(zinc, Znumvisibility, "distance tests for facet visibility", -1);
+ zdef_(zinc, Zdistvertex, "distance tests to report minimum vertex", -1);
+ zzdef_(zinc, Ztotcheck, "points checked for facets' outer planes", -1);
+ zzdef_(zinc, Zcheckpart, " ave. distance tests per check", Ztotcheck);
+}
+void qh_allstatD(qhT *qh) {
+ zdef_(zinc, Zvisit, "resets of visit_id", -1);
+ zdef_(zinc, Zvvisit, " resets of vertex_visit", -1);
+ zdef_(zmax, Zvisit2max, " max visit_id/2", -1);
+ zdef_(zmax, Zvvisit2max, " max vertex_visit/2", -1);
+
+ zdef_(zdoc, Zdoc4, "partitioning statistics(see previous for outer planes)", -1);
+ zzdef_(zadd, Zdelvertextot, "total vertices deleted", -1);
+ zdef_(zmax, Zdelvertexmax, " maximum vertices deleted per iteration", -1);
+ zdef_(zinc, Zfindbest, "calls to findbest", -1);
+ zdef_(zadd, Zfindbesttot, " ave. facets tested", Zfindbest);
+ zdef_(zmax, Zfindbestmax, " max. facets tested", -1);
+ zdef_(zadd, Zfindcoplanar, " ave. coplanar search", Zfindbest);
+ zdef_(zinc, Zfindnew, "calls to findbestnew", -1);
+ zdef_(zadd, Zfindnewtot, " ave. facets tested", Zfindnew);
+ zdef_(zmax, Zfindnewmax, " max. facets tested", -1);
+ zdef_(zinc, Zfindnewjump, " ave. clearly better", Zfindnew);
+ zdef_(zinc, Zfindnewsharp, " calls due to qh_sharpnewfacets", -1);
+ zdef_(zinc, Zfindhorizon, "calls to findhorizon", -1);
+ zdef_(zadd, Zfindhorizontot, " ave. facets tested", Zfindhorizon);
+ zdef_(zmax, Zfindhorizonmax, " max. facets tested", -1);
+ zdef_(zinc, Zfindjump, " ave. clearly better", Zfindhorizon);
+ zdef_(zinc, Zparthorizon, " horizon facets better than bestfacet", -1);
+ zdef_(zinc, Zpartangle, "angle tests for repartitioned coplanar points", -1);
+ zdef_(zinc, Zpartflip, " repartitioned coplanar points for flipped orientation", -1);
+}
+void qh_allstatE(qhT *qh) {
+ zdef_(zinc, Zpartinside, "inside points", -1);
+ zdef_(zinc, Zpartnear, " inside points kept with a facet", -1);
+ zdef_(zinc, Zcoplanarinside, " inside points that were coplanar with a facet", -1);
+ zdef_(zinc, Zbestlower, "calls to findbestlower", -1);
+ zdef_(zinc, Zbestlowerv, " with search of vertex neighbors", -1);
+ zdef_(zinc, Zbestlowerall, " with rare search of all facets", -1);
+ zdef_(zmax, Zbestloweralln, " facets per search of all facets", -1);
+ zdef_(wadd, Wmaxout, "difference in max_outside at final check", -1);
+ zzdef_(zinc, Zpartitionall, "distance tests for initial partition", -1);
+ zdef_(zinc, Ztotpartition, "partitions of a point", -1);
+ zzdef_(zinc, Zpartition, "distance tests for partitioning", -1);
+ zzdef_(zinc, Zdistcheck, "distance tests for checking flipped facets", -1);
+ zzdef_(zinc, Zdistconvex, "distance tests for checking convexity", -1);
+ zdef_(zinc, Zdistgood, "distance tests for checking good point", -1);
+ zdef_(zinc, Zdistio, "distance tests for output", -1);
+ zdef_(zinc, Zdiststat, "distance tests for statistics", -1);
+ zdef_(zinc, Zdistplane, "total number of distance tests", -1);
+ zdef_(zinc, Ztotpartcoplanar, "partitions of coplanar points or deleted vertices", -1);
+ zzdef_(zinc, Zpartcoplanar, " distance tests for these partitions", -1);
+ zdef_(zinc, Zcomputefurthest, "distance tests for computing furthest", -1);
+}
+void qh_allstatE2(qhT *qh) {
+ zdef_(zdoc, Zdoc5, "statistics for matching ridges", -1);
+ zdef_(zinc, Zhashlookup, "total lookups for matching ridges of new facets", -1);
+ zdef_(zinc, Zhashtests, "average number of tests to match a ridge", Zhashlookup);
+ zdef_(zinc, Zhashridge, "total lookups of subridges(duplicates and boundary)", -1);
+ zdef_(zinc, Zhashridgetest, "average number of tests per subridge", Zhashridge);
+ zdef_(zinc, Zdupsame, "duplicated ridges in same merge cycle", -1);
+ zdef_(zinc, Zdupflip, "duplicated ridges with flipped facets", -1);
+
+ zdef_(zdoc, Zdoc6, "statistics for determining merges", -1);
+ zdef_(zinc, Zangletests, "angles computed for ridge convexity", -1);
+ zdef_(zinc, Zbestcentrum, "best merges used centrum instead of vertices",-1);
+ zzdef_(zinc, Zbestdist, "distance tests for best merge", -1);
+ zzdef_(zinc, Zcentrumtests, "distance tests for centrum convexity", -1);
+ zzdef_(zinc, Zdistzero, "distance tests for checking simplicial convexity", -1);
+ zdef_(zinc, Zcoplanarangle, "coplanar angles in getmergeset", -1);
+ zdef_(zinc, Zcoplanarcentrum, "coplanar centrums in getmergeset", -1);
+ zdef_(zinc, Zconcaveridge, "concave ridges in getmergeset", -1);
+}
+void qh_allstatF(qhT *qh) {
+ zdef_(zdoc, Zdoc7, "statistics for merging", -1);
+ zdef_(zinc, Zpremergetot, "merge iterations", -1);
+ zdef_(zadd, Zmergeinittot, "ave. initial non-convex ridges per iteration", Zpremergetot);
+ zdef_(zadd, Zmergeinitmax, " maximum", -1);
+ zdef_(zadd, Zmergesettot, " ave. additional non-convex ridges per iteration", Zpremergetot);
+ zdef_(zadd, Zmergesetmax, " maximum additional in one pass", -1);
+ zdef_(zadd, Zmergeinittot2, "initial non-convex ridges for post merging", -1);
+ zdef_(zadd, Zmergesettot2, " additional non-convex ridges", -1);
+ zdef_(wmax, Wmaxoutside, "max distance of vertex or coplanar point above facet(w/roundoff)", -1);
+ zdef_(wmin, Wminvertex, "max distance of merged vertex below facet(or roundoff)", -1);
+ zdef_(zinc, Zwidefacet, "centrums frozen due to a wide merge", -1);
+ zdef_(zinc, Zwidevertices, "centrums frozen due to extra vertices", -1);
+ zzdef_(zinc, Ztotmerge, "total number of facets or cycles of facets merged", -1);
+ zdef_(zinc, Zmergesimplex, "merged a simplex", -1);
+ zdef_(zinc, Zonehorizon, "simplices merged into coplanar horizon", -1);
+ zzdef_(zinc, Zcyclehorizon, "cycles of facets merged into coplanar horizon", -1);
+ zzdef_(zadd, Zcyclefacettot, " ave. facets per cycle", Zcyclehorizon);
+ zdef_(zmax, Zcyclefacetmax, " max. facets", -1);
+ zdef_(zinc, Zmergeintohorizon, "new facets merged into horizon", -1);
+ zdef_(zinc, Zmergenew, "new facets merged", -1);
+ zdef_(zinc, Zmergehorizon, "horizon facets merged into new facets", -1);
+ zdef_(zinc, Zmergevertex, "vertices deleted by merging", -1);
+ zdef_(zinc, Zcyclevertex, "vertices deleted by merging into coplanar horizon", -1);
+ zdef_(zinc, Zdegenvertex, "vertices deleted by degenerate facet", -1);
+ zdef_(zinc, Zmergeflipdup, "merges due to flipped facets in duplicated ridge", -1);
+ zdef_(zinc, Zneighbor, "merges due to redundant neighbors", -1);
+ zdef_(zadd, Ztestvneighbor, "non-convex vertex neighbors", -1);
+}
+void qh_allstatG(qhT *qh) {
+ zdef_(zinc, Zacoplanar, "merges due to angle coplanar facets", -1);
+ zdef_(wadd, Wacoplanartot, " average merge distance", Zacoplanar);
+ zdef_(wmax, Wacoplanarmax, " maximum merge distance", -1);
+ zdef_(zinc, Zcoplanar, "merges due to coplanar facets", -1);
+ zdef_(wadd, Wcoplanartot, " average merge distance", Zcoplanar);
+ zdef_(wmax, Wcoplanarmax, " maximum merge distance", -1);
+ zdef_(zinc, Zconcave, "merges due to concave facets", -1);
+ zdef_(wadd, Wconcavetot, " average merge distance", Zconcave);
+ zdef_(wmax, Wconcavemax, " maximum merge distance", -1);
+ zdef_(zinc, Zavoidold, "coplanar/concave merges due to avoiding old merge", -1);
+ zdef_(wadd, Wavoidoldtot, " average merge distance", Zavoidold);
+ zdef_(wmax, Wavoidoldmax, " maximum merge distance", -1);
+ zdef_(zinc, Zdegen, "merges due to degenerate facets", -1);
+ zdef_(wadd, Wdegentot, " average merge distance", Zdegen);
+ zdef_(wmax, Wdegenmax, " maximum merge distance", -1);
+ zdef_(zinc, Zflipped, "merges due to removing flipped facets", -1);
+ zdef_(wadd, Wflippedtot, " average merge distance", Zflipped);
+ zdef_(wmax, Wflippedmax, " maximum merge distance", -1);
+ zdef_(zinc, Zduplicate, "merges due to duplicated ridges", -1);
+ zdef_(wadd, Wduplicatetot, " average merge distance", Zduplicate);
+ zdef_(wmax, Wduplicatemax, " maximum merge distance", -1);
+}
+void qh_allstatH(qhT *qh) {
+ zdef_(zdoc, Zdoc8, "renamed vertex statistics", -1);
+ zdef_(zinc, Zrenameshare, "renamed vertices shared by two facets", -1);
+ zdef_(zinc, Zrenamepinch, "renamed vertices in a pinched facet", -1);
+ zdef_(zinc, Zrenameall, "renamed vertices shared by multiple facets", -1);
+ zdef_(zinc, Zfindfail, "rename failures due to duplicated ridges", -1);
+ zdef_(zinc, Zdupridge, " duplicate ridges detected", -1);
+ zdef_(zinc, Zdelridge, "deleted ridges due to renamed vertices", -1);
+ zdef_(zinc, Zdropneighbor, "dropped neighbors due to renamed vertices", -1);
+ zdef_(zinc, Zdropdegen, "degenerate facets due to dropped neighbors", -1);
+ zdef_(zinc, Zdelfacetdup, " facets deleted because of no neighbors", -1);
+ zdef_(zinc, Zremvertex, "vertices removed from facets due to no ridges", -1);
+ zdef_(zinc, Zremvertexdel, " deleted", -1);
+ zdef_(zinc, Zintersectnum, "vertex intersections for locating redundant vertices", -1);
+ zdef_(zinc, Zintersectfail, "intersections failed to find a redundant vertex", -1);
+ zdef_(zinc, Zintersect, "intersections found redundant vertices", -1);
+ zdef_(zadd, Zintersecttot, " ave. number found per vertex", Zintersect);
+ zdef_(zmax, Zintersectmax, " max. found for a vertex", -1);
+ zdef_(zinc, Zvertexridge, NULL, -1);
+ zdef_(zadd, Zvertexridgetot, " ave. number of ridges per tested vertex", Zvertexridge);
+ zdef_(zmax, Zvertexridgemax, " max. number of ridges per tested vertex", -1);
+
+ zdef_(zdoc, Zdoc10, "memory usage statistics(in bytes)", -1);
+ zdef_(zadd, Zmemfacets, "for facets and their normals, neighbor and vertex sets", -1);
+ zdef_(zadd, Zmemvertices, "for vertices and their neighbor sets", -1);
+ zdef_(zadd, Zmempoints, "for input points, outside and coplanar sets, and qhT",-1);
+ zdef_(zadd, Zmemridges, "for ridges and their vertex sets", -1);
+} /* allstat */
+
+void qh_allstatI(qhT *qh) {
+ qh->qhstat.vridges= qh->qhstat.next;
+ zzdef_(zdoc, Zdoc11, "Voronoi ridge statistics", -1);
+ zzdef_(zinc, Zridge, "non-simplicial Voronoi vertices for all ridges", -1);
+ zzdef_(wadd, Wridge, " ave. distance to ridge", Zridge);
+ zzdef_(wmax, Wridgemax, " max. distance to ridge", -1);
+ zzdef_(zinc, Zridgemid, "bounded ridges", -1);
+ zzdef_(wadd, Wridgemid, " ave. distance of midpoint to ridge", Zridgemid);
+ zzdef_(wmax, Wridgemidmax, " max. distance of midpoint to ridge", -1);
+ zzdef_(zinc, Zridgeok, "bounded ridges with ok normal", -1);
+ zzdef_(wadd, Wridgeok, " ave. angle to ridge", Zridgeok);
+ zzdef_(wmax, Wridgeokmax, " max. angle to ridge", -1);
+ zzdef_(zinc, Zridge0, "bounded ridges with near-zero normal", -1);
+ zzdef_(wadd, Wridge0, " ave. angle to ridge", Zridge0);
+ zzdef_(wmax, Wridge0max, " max. angle to ridge", -1);
+
+ zdef_(zdoc, Zdoc12, "Triangulation statistics(Qt)", -1);
+ zdef_(zinc, Ztricoplanar, "non-simplicial facets triangulated", -1);
+ zdef_(zadd, Ztricoplanartot, " ave. new facets created(may be deleted)", Ztricoplanar);
+ zdef_(zmax, Ztricoplanarmax, " max. new facets created", -1);
+ zdef_(zinc, Ztrinull, "null new facets deleted(duplicated vertex)", -1);
+ zdef_(zinc, Ztrimirror, "mirrored pairs of new facets deleted(same vertices)", -1);
+ zdef_(zinc, Ztridegen, "degenerate new facets in output(same ridge)", -1);
+} /* allstat */
+
+/*-<a href="qh-stat_r.htm#TOC"
+ >-------------------------------</a><a name="allstatistics">-</a>
+
+ qh_allstatistics()
+ reset printed flag for all statistics
+*/
+void qh_allstatistics(qhT *qh) {
+ int i;
+
+ for(i=ZEND; i--; )
+ qh->qhstat.printed[i]= False;
+} /* allstatistics */
+
+#if qh_KEEPstatistics
+/*-<a href="qh-stat_r.htm#TOC"
+ >-------------------------------</a><a name="collectstatistics">-</a>
+
+ qh_collectstatistics()
+ collect statistics for qh.facet_list
+
+*/
+void qh_collectstatistics(qhT *qh) {
+ facetT *facet, *neighbor, **neighborp;
+ vertexT *vertex, **vertexp;
+ realT dotproduct, dist;
+ int sizneighbors, sizridges, sizvertices, i;
+
+ qh->old_randomdist= qh->RANDOMdist;
+ qh->RANDOMdist= False;
+ zval_(Zmempoints)= qh->num_points * qh->normal_size + sizeof(qhT);
+ zval_(Zmemfacets)= 0;
+ zval_(Zmemridges)= 0;
+ zval_(Zmemvertices)= 0;
+ zval_(Zangle)= 0;
+ wval_(Wangle)= 0.0;
+ zval_(Znumridges)= 0;
+ zval_(Znumfacets)= 0;
+ zval_(Znumneighbors)= 0;
+ zval_(Znumvertices)= 0;
+ zval_(Znumvneighbors)= 0;
+ zval_(Znummergetot)= 0;
+ zval_(Znummergemax)= 0;
+ zval_(Zvertices)= qh->num_vertices - qh_setsize(qh, qh->del_vertices);
+ if (qh->MERGING || qh->APPROXhull || qh->JOGGLEmax < REALmax/2)
+ wmax_(Wmaxoutside, qh->max_outside);
+ if (qh->MERGING)
+ wmin_(Wminvertex, qh->min_vertex);
+ FORALLfacets
+ facet->seen= False;
+ if (qh->DELAUNAY) {
+ FORALLfacets {
+ if (facet->upperdelaunay != qh->UPPERdelaunay)
+ facet->seen= True; /* remove from angle statistics */
+ }
+ }
+ FORALLfacets {
+ if (facet->visible && qh->NEWfacets)
+ continue;
+ sizvertices= qh_setsize(qh, facet->vertices);
+ sizneighbors= qh_setsize(qh, facet->neighbors);
+ sizridges= qh_setsize(qh, facet->ridges);
+ zinc_(Znumfacets);
+ zadd_(Znumvertices, sizvertices);
+ zmax_(Zmaxvertices, sizvertices);
+ zadd_(Znumneighbors, sizneighbors);
+ zmax_(Zmaxneighbors, sizneighbors);
+ zadd_(Znummergetot, facet->nummerge);
+ i= facet->nummerge; /* avoid warnings */
+ zmax_(Znummergemax, i);
+ if (!facet->simplicial) {
+ if (sizvertices == qh->hull_dim) {
+ zinc_(Znowsimplicial);
+ }else {
+ zinc_(Znonsimplicial);
+ }
+ }
+ if (sizridges) {
+ zadd_(Znumridges, sizridges);
+ zmax_(Zmaxridges, sizridges);
+ }
+ zadd_(Zmemfacets, sizeof(facetT) + qh->normal_size + 2*sizeof(setT)
+ + SETelemsize * (sizneighbors + sizvertices));
+ if (facet->ridges) {
+ zadd_(Zmemridges,
+ sizeof(setT) + SETelemsize * sizridges + sizridges *
+ (sizeof(ridgeT) + sizeof(setT) + SETelemsize * (qh->hull_dim-1))/2);
+ }
+ if (facet->outsideset)
+ zadd_(Zmempoints, sizeof(setT) + SETelemsize * qh_setsize(qh, facet->outsideset));
+ if (facet->coplanarset)
+ zadd_(Zmempoints, sizeof(setT) + SETelemsize * qh_setsize(qh, facet->coplanarset));
+ if (facet->seen) /* Delaunay upper envelope */
+ continue;
+ facet->seen= True;
+ FOREACHneighbor_(facet) {
+ if (neighbor == qh_DUPLICATEridge || neighbor == qh_MERGEridge
+ || neighbor->seen || !facet->normal || !neighbor->normal)
+ continue;
+ dotproduct= qh_getangle(qh, facet->normal, neighbor->normal);
+ zinc_(Zangle);
+ wadd_(Wangle, dotproduct);
+ wmax_(Wanglemax, dotproduct)
+ wmin_(Wanglemin, dotproduct)
+ }
+ if (facet->normal) {
+ FOREACHvertex_(facet->vertices) {
+ zinc_(Zdiststat);
+ qh_distplane(qh, vertex->point, facet, &dist);
+ wmax_(Wvertexmax, dist);
+ wmin_(Wvertexmin, dist);
+ }
+ }
+ }
+ FORALLvertices {
+ if (vertex->deleted)
+ continue;
+ zadd_(Zmemvertices, sizeof(vertexT));
+ if (vertex->neighbors) {
+ sizneighbors= qh_setsize(qh, vertex->neighbors);
+ zadd_(Znumvneighbors, sizneighbors);
+ zmax_(Zmaxvneighbors, sizneighbors);
+ zadd_(Zmemvertices, sizeof(vertexT) + SETelemsize * sizneighbors);
+ }
+ }
+ qh->RANDOMdist= qh->old_randomdist;
+} /* collectstatistics */
+#endif /* qh_KEEPstatistics */
+
+/*-<a href="qh-stat_r.htm#TOC"
+ >-------------------------------</a><a name="initstatistics">-</a>
+
+ qh_initstatistics(qh)
+ initialize statistics
+
+ notes:
+ NOerrors -- qh_initstatistics can not use qh_errexit(), qh_fprintf, or qh.ferr
+ On first call, only qhmem.ferr is defined. qh_memalloc is not setup.
+ Also invoked by QhullQh().
+*/
+void qh_initstatistics(qhT *qh) {
+ int i;
+ realT realx;
+ int intx;
+
+ qh->qhstat.next= 0;
+ qh_allstatA(qh);
+ qh_allstatB(qh);
+ qh_allstatC(qh);
+ qh_allstatD(qh);
+ qh_allstatE(qh);
+ qh_allstatE2(qh);
+ qh_allstatF(qh);
+ qh_allstatG(qh);
+ qh_allstatH(qh);
+ qh_allstatI(qh);
+ if (qh->qhstat.next > (int)sizeof(qh->qhstat.id)) {
+ qh_fprintf(qh, qh->qhmem.ferr, 6184, "qhull error (qh_initstatistics): increase size of qhstat.id[].\n\
+ qhstat.next %d should be <= sizeof(qh->qhstat.id) %d\n", qh->qhstat.next, (int)sizeof(qh->qhstat.id));
+#if 0 /* for locating error, Znumridges should be duplicated */
+ for(i=0; i < ZEND; i++) {
+ int j;
+ for(j=i+1; j < ZEND; j++) {
+ if (qh->qhstat.id[i] == qh->qhstat.id[j]) {
+ qh_fprintf(qh, qh->qhmem.ferr, 6185, "qhull error (qh_initstatistics): duplicated statistic %d at indices %d and %d\n",
+ qh->qhstat.id[i], i, j);
+ }
+ }
+ }
+#endif
+ qh_exit(qh_ERRqhull); /* can not use qh_errexit() */
+ }
+ qh->qhstat.init[zinc].i= 0;
+ qh->qhstat.init[zadd].i= 0;
+ qh->qhstat.init[zmin].i= INT_MAX;
+ qh->qhstat.init[zmax].i= INT_MIN;
+ qh->qhstat.init[wadd].r= 0;
+ qh->qhstat.init[wmin].r= REALmax;
+ qh->qhstat.init[wmax].r= -REALmax;
+ for(i=0; i < ZEND; i++) {
+ if (qh->qhstat.type[i] > ZTYPEreal) {
+ realx= qh->qhstat.init[(unsigned char)(qh->qhstat.type[i])].r;
+ qh->qhstat.stats[i].r= realx;
+ }else if (qh->qhstat.type[i] != zdoc) {
+ intx= qh->qhstat.init[(unsigned char)(qh->qhstat.type[i])].i;
+ qh->qhstat.stats[i].i= intx;
+ }
+ }
+} /* initstatistics */
+
+/*-<a href="qh-stat_r.htm#TOC"
+ >-------------------------------</a><a name="newstats">-</a>
+
+ qh_newstats(qh, )
+ returns True if statistics for zdoc
+
+ returns:
+ next zdoc
+*/
+boolT qh_newstats(qhT *qh, int idx, int *nextindex) {
+ boolT isnew= False;
+ int start, i;
+
+ if (qh->qhstat.type[qh->qhstat.id[idx]] == zdoc)
+ start= idx+1;
+ else
+ start= idx;
+ for(i= start; i < qh->qhstat.next && qh->qhstat.type[qh->qhstat.id[i]] != zdoc; i++) {
+ if (!qh_nostatistic(qh, qh->qhstat.id[i]) && !qh->qhstat.printed[qh->qhstat.id[i]])
+ isnew= True;
+ }
+ *nextindex= i;
+ return isnew;
+} /* newstats */
+
+/*-<a href="qh-stat_r.htm#TOC"
+ >-------------------------------</a><a name="nostatistic">-</a>
+
+ qh_nostatistic(qh, index )
+ true if no statistic to print
+*/
+boolT qh_nostatistic(qhT *qh, int i) {
+
+ if ((qh->qhstat.type[i] > ZTYPEreal
+ &&qh->qhstat.stats[i].r == qh->qhstat.init[(unsigned char)(qh->qhstat.type[i])].r)
+ || (qh->qhstat.type[i] < ZTYPEreal
+ &&qh->qhstat.stats[i].i == qh->qhstat.init[(unsigned char)(qh->qhstat.type[i])].i))
+ return True;
+ return False;
+} /* nostatistic */
+
+#if qh_KEEPstatistics
+/*-<a href="qh-stat_r.htm#TOC"
+ >-------------------------------</a><a name="printallstatistics">-</a>
+
+ qh_printallstatistics(qh, fp, string )
+ print all statistics with header 'string'
+*/
+void qh_printallstatistics(qhT *qh, FILE *fp, const char *string) {
+
+ qh_allstatistics(qh);
+ qh_collectstatistics(qh);
+ qh_printstatistics(qh, fp, string);
+ qh_memstatistics(qh, fp);
+}
+
+
+/*-<a href="qh-stat_r.htm#TOC"
+ >-------------------------------</a><a name="printstatistics">-</a>
+
+ qh_printstatistics(qh, fp, string )
+ print statistics to a file with header 'string'
+ skips statistics with qhstat.printed[] (reset with qh_allstatistics)
+
+ see:
+ qh_printallstatistics()
+*/
+void qh_printstatistics(qhT *qh, FILE *fp, const char *string) {
+ int i, k;
+ realT ave;
+
+ if (qh->num_points != qh->num_vertices) {
+ wval_(Wpbalance)= 0;
+ wval_(Wpbalance2)= 0;
+ }else
+ wval_(Wpbalance2)= qh_stddev(zval_(Zpbalance), wval_(Wpbalance),
+ wval_(Wpbalance2), &ave);
+ wval_(Wnewbalance2)= qh_stddev(zval_(Zprocessed), wval_(Wnewbalance),
+ wval_(Wnewbalance2), &ave);
+ qh_fprintf(qh, fp, 9350, "\n\
+%s\n\
+ qhull invoked by: %s | %s\n%s with options:\n%s\n", string, qh->rbox_command,
+ qh->qhull_command, qh_version, qh->qhull_options);
+ qh_fprintf(qh, fp, 9351, "\nprecision constants:\n\
+ %6.2g max. abs. coordinate in the (transformed) input('Qbd:n')\n\
+ %6.2g max. roundoff error for distance computation('En')\n\
+ %6.2g max. roundoff error for angle computations\n\
+ %6.2g min. distance for outside points ('Wn')\n\
+ %6.2g min. distance for visible facets ('Vn')\n\
+ %6.2g max. distance for coplanar facets ('Un')\n\
+ %6.2g max. facet width for recomputing centrum and area\n\
+",
+ qh->MAXabs_coord, qh->DISTround, qh->ANGLEround, qh->MINoutside,
+ qh->MINvisible, qh->MAXcoplanar, qh->WIDEfacet);
+ if (qh->KEEPnearinside)
+ qh_fprintf(qh, fp, 9352, "\
+ %6.2g max. distance for near-inside points\n", qh->NEARinside);
+ if (qh->premerge_cos < REALmax/2) qh_fprintf(qh, fp, 9353, "\
+ %6.2g max. cosine for pre-merge angle\n", qh->premerge_cos);
+ if (qh->PREmerge) qh_fprintf(qh, fp, 9354, "\
+ %6.2g radius of pre-merge centrum\n", qh->premerge_centrum);
+ if (qh->postmerge_cos < REALmax/2) qh_fprintf(qh, fp, 9355, "\
+ %6.2g max. cosine for post-merge angle\n", qh->postmerge_cos);
+ if (qh->POSTmerge) qh_fprintf(qh, fp, 9356, "\
+ %6.2g radius of post-merge centrum\n", qh->postmerge_centrum);
+ qh_fprintf(qh, fp, 9357, "\
+ %6.2g max. distance for merging two simplicial facets\n\
+ %6.2g max. roundoff error for arithmetic operations\n\
+ %6.2g min. denominator for divisions\n\
+ zero diagonal for Gauss: ", qh->ONEmerge, REALepsilon, qh->MINdenom);
+ for(k=0; k < qh->hull_dim; k++)
+ qh_fprintf(qh, fp, 9358, "%6.2e ", qh->NEARzero[k]);
+ qh_fprintf(qh, fp, 9359, "\n\n");
+ for(i=0 ; i < qh->qhstat.next; )
+ qh_printstats(qh, fp, i, &i);
+} /* printstatistics */
+#endif /* qh_KEEPstatistics */
+
+/*-<a href="qh-stat_r.htm#TOC"
+ >-------------------------------</a><a name="printstatlevel">-</a>
+
+ qh_printstatlevel(qh, fp, id )
+ print level information for a statistic
+
+ notes:
+ nop if id >= ZEND, printed, or same as initial value
+*/
+void qh_printstatlevel(qhT *qh, FILE *fp, int id) {
+#define NULLfield " "
+
+ if (id >= ZEND || qh->qhstat.printed[id])
+ return;
+ if (qh->qhstat.type[id] == zdoc) {
+ qh_fprintf(qh, fp, 9360, "%s\n", qh->qhstat.doc[id]);
+ return;
+ }
+ if (qh_nostatistic(qh, id) || !qh->qhstat.doc[id])
+ return;
+ qh->qhstat.printed[id]= True;
+ if (qh->qhstat.count[id] != -1
+ && qh->qhstat.stats[(unsigned char)(qh->qhstat.count[id])].i == 0)
+ qh_fprintf(qh, fp, 9361, " *0 cnt*");
+ else if (qh->qhstat.type[id] >= ZTYPEreal && qh->qhstat.count[id] == -1)
+ qh_fprintf(qh, fp, 9362, "%7.2g", qh->qhstat.stats[id].r);
+ else if (qh->qhstat.type[id] >= ZTYPEreal && qh->qhstat.count[id] != -1)
+ qh_fprintf(qh, fp, 9363, "%7.2g", qh->qhstat.stats[id].r/ qh->qhstat.stats[(unsigned char)(qh->qhstat.count[id])].i);
+ else if (qh->qhstat.type[id] < ZTYPEreal && qh->qhstat.count[id] == -1)
+ qh_fprintf(qh, fp, 9364, "%7d", qh->qhstat.stats[id].i);
+ else if (qh->qhstat.type[id] < ZTYPEreal && qh->qhstat.count[id] != -1)
+ qh_fprintf(qh, fp, 9365, "%7.3g", (realT) qh->qhstat.stats[id].i / qh->qhstat.stats[(unsigned char)(qh->qhstat.count[id])].i);
+ qh_fprintf(qh, fp, 9366, " %s\n", qh->qhstat.doc[id]);
+} /* printstatlevel */
+
+
+/*-<a href="qh-stat_r.htm#TOC"
+ >-------------------------------</a><a name="printstats">-</a>
+
+ qh_printstats(qh, fp, index, nextindex )
+ print statistics for a zdoc group
+
+ returns:
+ next zdoc if non-null
+*/
+void qh_printstats(qhT *qh, FILE *fp, int idx, int *nextindex) {
+ int j, nexti;
+
+ if (qh_newstats(qh, idx, &nexti)) {
+ qh_fprintf(qh, fp, 9367, "\n");
+ for (j=idx; j<nexti; j++)
+ qh_printstatlevel(qh, fp, qh->qhstat.id[j]);
+ }
+ if (nextindex)
+ *nextindex= nexti;
+} /* printstats */
+
+#if qh_KEEPstatistics
+
+/*-<a href="qh-stat_r.htm#TOC"
+ >-------------------------------</a><a name="stddev">-</a>
+
+ qh_stddev(num, tot, tot2, ave )
+ compute the standard deviation and average from statistics
+
+ tot2 is the sum of the squares
+ notes:
+ computes r.m.s.:
+ (x-ave)^2
+ == x^2 - 2x tot/num + (tot/num)^2
+ == tot2 - 2 tot tot/num + tot tot/num
+ == tot2 - tot ave
+*/
+realT qh_stddev(int num, realT tot, realT tot2, realT *ave) {
+ realT stddev;
+
+ *ave= tot/num;
+ stddev= sqrt(tot2/num - *ave * *ave);
+ return stddev;
+} /* stddev */
+
+#endif /* qh_KEEPstatistics */
+
+#if !qh_KEEPstatistics
+void qh_collectstatistics(qhT *qh) {}
+void qh_printallstatistics(qhT *qh, FILE *fp, char *string) {};
+void qh_printstatistics(qhT *qh, FILE *fp, char *string) {}
+#endif
+
diff --git a/xs/src/qhull/src/libqhull_r/stat_r.h b/xs/src/qhull/src/libqhull_r/stat_r.h
new file mode 100644
index 000000000..75e7d1057
--- /dev/null
+++ b/xs/src/qhull/src/libqhull_r/stat_r.h
@@ -0,0 +1,533 @@
+/*<html><pre> -<a href="qh-stat_r.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ stat_r.h
+ contains all statistics that are collected for qhull
+
+ see qh-stat_r.htm and stat_r.c
+
+ Copyright (c) 1993-2015 The Geometry Center.
+ $Id: //main/2015/qhull/src/libqhull_r/stat_r.h#5 $$Change: 2079 $
+ $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
+
+ recompile qhull if you change this file
+
+ Integer statistics are Z* while real statistics are W*.
+
+ define MAYdebugx to call a routine at every statistic event
+
+*/
+
+#ifndef qhDEFstat
+#define qhDEFstat 1
+
+/* Depends on realT. Do not include libqhull_r to avoid circular dependency */
+
+#ifndef DEFqhT
+#define DEFqhT 1
+typedef struct qhT qhT; /* Defined by libqhull_r.h */
+#endif
+
+#ifndef DEFqhstatT
+#define DEFqhstatT 1
+typedef struct qhstatT qhstatT; /* Defined here */
+#endif
+
+/*-<a href="qh-stat_r.htm#TOC"
+ >-------------------------------</a><a name="KEEPstatistics">-</a>
+
+ qh_KEEPstatistics
+ 0 turns off statistic gathering (except zzdef/zzinc/zzadd/zzval/wwval)
+*/
+#ifndef qh_KEEPstatistics
+#define qh_KEEPstatistics 1
+#endif
+
+/*-<a href="qh-stat_r.htm#TOC"
+ >-------------------------------</a><a name="statistics">-</a>
+
+ Zxxx for integers, Wxxx for reals
+
+ notes:
+ be sure that all statistics are defined in stat_r.c
+ otherwise initialization may core dump
+ can pick up all statistics by:
+ grep '[zw].*_[(][ZW]' *.c >z.x
+ remove trailers with query">-</a>
+ remove leaders with query-replace-regexp [ ^I]+ (
+*/
+#if qh_KEEPstatistics
+enum qh_statistics { /* alphabetical after Z/W */
+ Zacoplanar,
+ Wacoplanarmax,
+ Wacoplanartot,
+ Zangle,
+ Wangle,
+ Wanglemax,
+ Wanglemin,
+ Zangletests,
+ Wareatot,
+ Wareamax,
+ Wareamin,
+ Zavoidold,
+ Wavoidoldmax,
+ Wavoidoldtot,
+ Zback0,
+ Zbestcentrum,
+ Zbestdist,
+ Zbestlower,
+ Zbestlowerall,
+ Zbestloweralln,
+ Zbestlowerv,
+ Zcentrumtests,
+ Zcheckpart,
+ Zcomputefurthest,
+ Zconcave,
+ Wconcavemax,
+ Wconcavetot,
+ Zconcaveridges,
+ Zconcaveridge,
+ Zcoplanar,
+ Wcoplanarmax,
+ Wcoplanartot,
+ Zcoplanarangle,
+ Zcoplanarcentrum,
+ Zcoplanarhorizon,
+ Zcoplanarinside,
+ Zcoplanarpart,
+ Zcoplanarridges,
+ Wcpu,
+ Zcyclefacetmax,
+ Zcyclefacettot,
+ Zcyclehorizon,
+ Zcyclevertex,
+ Zdegen,
+ Wdegenmax,
+ Wdegentot,
+ Zdegenvertex,
+ Zdelfacetdup,
+ Zdelridge,
+ Zdelvertextot,
+ Zdelvertexmax,
+ Zdetsimplex,
+ Zdistcheck,
+ Zdistconvex,
+ Zdistgood,
+ Zdistio,
+ Zdistplane,
+ Zdiststat,
+ Zdistvertex,
+ Zdistzero,
+ Zdoc1,
+ Zdoc2,
+ Zdoc3,
+ Zdoc4,
+ Zdoc5,
+ Zdoc6,
+ Zdoc7,
+ Zdoc8,
+ Zdoc9,
+ Zdoc10,
+ Zdoc11,
+ Zdoc12,
+ Zdropdegen,
+ Zdropneighbor,
+ Zdupflip,
+ Zduplicate,
+ Wduplicatemax,
+ Wduplicatetot,
+ Zdupridge,
+ Zdupsame,
+ Zflipped,
+ Wflippedmax,
+ Wflippedtot,
+ Zflippedfacets,
+ Zfindbest,
+ Zfindbestmax,
+ Zfindbesttot,
+ Zfindcoplanar,
+ Zfindfail,
+ Zfindhorizon,
+ Zfindhorizonmax,
+ Zfindhorizontot,
+ Zfindjump,
+ Zfindnew,
+ Zfindnewmax,
+ Zfindnewtot,
+ Zfindnewjump,
+ Zfindnewsharp,
+ Zgauss0,
+ Zgoodfacet,
+ Zhashlookup,
+ Zhashridge,
+ Zhashridgetest,
+ Zhashtests,
+ Zinsidevisible,
+ Zintersect,
+ Zintersectfail,
+ Zintersectmax,
+ Zintersectnum,
+ Zintersecttot,
+ Zmaxneighbors,
+ Wmaxout,
+ Wmaxoutside,
+ Zmaxridges,
+ Zmaxvertex,
+ Zmaxvertices,
+ Zmaxvneighbors,
+ Zmemfacets,
+ Zmempoints,
+ Zmemridges,
+ Zmemvertices,
+ Zmergeflipdup,
+ Zmergehorizon,
+ Zmergeinittot,
+ Zmergeinitmax,
+ Zmergeinittot2,
+ Zmergeintohorizon,
+ Zmergenew,
+ Zmergesettot,
+ Zmergesetmax,
+ Zmergesettot2,
+ Zmergesimplex,
+ Zmergevertex,
+ Wmindenom,
+ Wminvertex,
+ Zminnorm,
+ Zmultiridge,
+ Znearlysingular,
+ Zneighbor,
+ Wnewbalance,
+ Wnewbalance2,
+ Znewfacettot,
+ Znewfacetmax,
+ Znewvertex,
+ Wnewvertex,
+ Wnewvertexmax,
+ Znoarea,
+ Znonsimplicial,
+ Znowsimplicial,
+ Znotgood,
+ Znotgoodnew,
+ Znotmax,
+ Znumfacets,
+ Znummergemax,
+ Znummergetot,
+ Znumneighbors,
+ Znumridges,
+ Znumvertices,
+ Znumvisibility,
+ Znumvneighbors,
+ Zonehorizon,
+ Zpartangle,
+ Zpartcoplanar,
+ Zpartflip,
+ Zparthorizon,
+ Zpartinside,
+ Zpartition,
+ Zpartitionall,
+ Zpartnear,
+ Zpbalance,
+ Wpbalance,
+ Wpbalance2,
+ Zpostfacets,
+ Zpremergetot,
+ Zprocessed,
+ Zremvertex,
+ Zremvertexdel,
+ Zrenameall,
+ Zrenamepinch,
+ Zrenameshare,
+ Zretry,
+ Wretrymax,
+ Zridge,
+ Wridge,
+ Wridgemax,
+ Zridge0,
+ Wridge0,
+ Wridge0max,
+ Zridgemid,
+ Wridgemid,
+ Wridgemidmax,
+ Zridgeok,
+ Wridgeok,
+ Wridgeokmax,
+ Zsearchpoints,
+ Zsetplane,
+ Ztestvneighbor,
+ Ztotcheck,
+ Ztothorizon,
+ Ztotmerge,
+ Ztotpartcoplanar,
+ Ztotpartition,
+ Ztotridges,
+ Ztotvertices,
+ Ztotvisible,
+ Ztricoplanar,
+ Ztricoplanarmax,
+ Ztricoplanartot,
+ Ztridegen,
+ Ztrimirror,
+ Ztrinull,
+ Wvertexmax,
+ Wvertexmin,
+ Zvertexridge,
+ Zvertexridgetot,
+ Zvertexridgemax,
+ Zvertices,
+ Zvisfacettot,
+ Zvisfacetmax,
+ Zvisit,
+ Zvisit2max,
+ Zvisvertextot,
+ Zvisvertexmax,
+ Zvvisit,
+ Zvvisit2max,
+ Zwidefacet,
+ Zwidevertices,
+ ZEND};
+
+/*-<a href="qh-stat_r.htm#TOC"
+ >-------------------------------</a><a name="ZZstat">-</a>
+
+ Zxxx/Wxxx statistics that remain defined if qh_KEEPstatistics=0
+
+ notes:
+ be sure to use zzdef, zzinc, etc. with these statistics (no double checking!)
+*/
+#else
+enum qh_statistics { /* for zzdef etc. macros */
+ Zback0,
+ Zbestdist,
+ Zcentrumtests,
+ Zcheckpart,
+ Zconcaveridges,
+ Zcoplanarhorizon,
+ Zcoplanarpart,
+ Zcoplanarridges,
+ Zcyclefacettot,
+ Zcyclehorizon,
+ Zdelvertextot,
+ Zdistcheck,
+ Zdistconvex,
+ Zdistzero,
+ Zdoc1,
+ Zdoc2,
+ Zdoc3,
+ Zdoc11,
+ Zflippedfacets,
+ Zgauss0,
+ Zminnorm,
+ Zmultiridge,
+ Znearlysingular,
+ Wnewvertexmax,
+ Znumvisibility,
+ Zpartcoplanar,
+ Zpartition,
+ Zpartitionall,
+ Zprocessed,
+ Zretry,
+ Zridge,
+ Wridge,
+ Wridgemax,
+ Zridge0,
+ Wridge0,
+ Wridge0max,
+ Zridgemid,
+ Wridgemid,
+ Wridgemidmax,
+ Zridgeok,
+ Wridgeok,
+ Wridgeokmax,
+ Zsetplane,
+ Ztotcheck,
+ Ztotmerge,
+ ZEND};
+#endif
+
+/*-<a href="qh-stat_r.htm#TOC"
+ >-------------------------------</a><a name="ztype">-</a>
+
+ ztype
+ the type of a statistic sets its initial value.
+
+ notes:
+ The type should be the same as the macro for collecting the statistic
+*/
+enum ztypes {zdoc,zinc,zadd,zmax,zmin,ZTYPEreal,wadd,wmax,wmin,ZTYPEend};
+
+/*========== macros and constants =============*/
+
+/*-<a href="qh-stat_r.htm#TOC"
+ >--------------------------------</a><a name="MAYdebugx">-</a>
+
+ MAYdebugx
+ define as maydebug() to be called frequently for error trapping
+*/
+#define MAYdebugx
+
+/*-<a href="qh-stat_r.htm#TOC"
+ >--------------------------------</a><a name="zdef_">-</a>
+
+ zzdef_, zdef_( type, name, doc, -1)
+ define a statistic (assumes 'qhstat.next= 0;')
+
+ zdef_( type, name, doc, count)
+ define an averaged statistic
+ printed as name/count
+*/
+#define zzdef_(stype,name,string,cnt) qh->qhstat.id[qh->qhstat.next++]=name; \
+ qh->qhstat.doc[name]= string; qh->qhstat.count[name]= cnt; qh->qhstat.type[name]= stype
+#if qh_KEEPstatistics
+#define zdef_(stype,name,string,cnt) qh->qhstat.id[qh->qhstat.next++]=name; \
+ qh->qhstat.doc[name]= string; qh->qhstat.count[name]= cnt; qh->qhstat.type[name]= stype
+#else
+#define zdef_(type,name,doc,count)
+#endif
+
+/*-<a href="qh-stat_r.htm#TOC"
+ >--------------------------------</a><a name="zinc_">-</a>
+
+ zzinc_( name ), zinc_( name)
+ increment an integer statistic
+*/
+#define zzinc_(id) {MAYdebugx; qh->qhstat.stats[id].i++;}
+#if qh_KEEPstatistics
+#define zinc_(id) {MAYdebugx; qh->qhstat.stats[id].i++;}
+#else
+#define zinc_(id) {}
+#endif
+
+/*-<a href="qh-stat_r.htm#TOC"
+ >--------------------------------</a><a name="zadd_">-</a>
+
+ zzadd_( name, value ), zadd_( name, value ), wadd_( name, value )
+ add value to an integer or real statistic
+*/
+#define zzadd_(id, val) {MAYdebugx; qh->qhstat.stats[id].i += (val);}
+#define wwadd_(id, val) {MAYdebugx; qh->qhstat.stats[id].r += (val);}
+#if qh_KEEPstatistics
+#define zadd_(id, val) {MAYdebugx; qh->qhstat.stats[id].i += (val);}
+#define wadd_(id, val) {MAYdebugx; qh->qhstat.stats[id].r += (val);}
+#else
+#define zadd_(id, val) {}
+#define wadd_(id, val) {}
+#endif
+
+/*-<a href="qh-stat_r.htm#TOC"
+ >--------------------------------</a><a name="zval_">-</a>
+
+ zzval_( name ), zval_( name ), wwval_( name )
+ set or return value of a statistic
+*/
+#define zzval_(id) ((qh->qhstat.stats[id]).i)
+#define wwval_(id) ((qh->qhstat.stats[id]).r)
+#if qh_KEEPstatistics
+#define zval_(id) ((qh->qhstat.stats[id]).i)
+#define wval_(id) ((qh->qhstat.stats[id]).r)
+#else
+#define zval_(id) qh->qhstat.tempi
+#define wval_(id) qh->qhstat.tempr
+#endif
+
+/*-<a href="qh-stat_r.htm#TOC"
+ >--------------------------------</a><a name="zmax_">-</a>
+
+ zmax_( id, val ), wmax_( id, value )
+ maximize id with val
+*/
+#define wwmax_(id, val) {MAYdebugx; maximize_(qh->qhstat.stats[id].r,(val));}
+#if qh_KEEPstatistics
+#define zmax_(id, val) {MAYdebugx; maximize_(qh->qhstat.stats[id].i,(val));}
+#define wmax_(id, val) {MAYdebugx; maximize_(qh->qhstat.stats[id].r,(val));}
+#else
+#define zmax_(id, val) {}
+#define wmax_(id, val) {}
+#endif
+
+/*-<a href="qh-stat_r.htm#TOC"
+ >--------------------------------</a><a name="zmin_">-</a>
+
+ zmin_( id, val ), wmin_( id, value )
+ minimize id with val
+*/
+#if qh_KEEPstatistics
+#define zmin_(id, val) {MAYdebugx; minimize_(qh->qhstat.stats[id].i,(val));}
+#define wmin_(id, val) {MAYdebugx; minimize_(qh->qhstat.stats[id].r,(val));}
+#else
+#define zmin_(id, val) {}
+#define wmin_(id, val) {}
+#endif
+
+/*================== stat_r.h types ==============*/
+
+
+/*-<a href="qh-stat_r.htm#TOC"
+ >--------------------------------</a><a name="intrealT">-</a>
+
+ intrealT
+ union of integer and real, used for statistics
+*/
+typedef union intrealT intrealT; /* union of int and realT */
+union intrealT {
+ int i;
+ realT r;
+};
+
+/*-<a href="qh-stat_r.htm#TOC"
+ >--------------------------------</a><a name="qhstat">-</a>
+
+ qhstat
+ Data structure for statistics, similar to qh and qhrbox
+
+ Allocated as part of qhT (libqhull_r.h)
+*/
+
+struct qhstatT {
+ intrealT stats[ZEND]; /* integer and real statistics */
+ unsigned char id[ZEND+10]; /* id's in print order */
+ const char *doc[ZEND]; /* array of documentation strings */
+ short int count[ZEND]; /* -1 if none, else index of count to use */
+ char type[ZEND]; /* type, see ztypes above */
+ char printed[ZEND]; /* true, if statistic has been printed */
+ intrealT init[ZTYPEend]; /* initial values by types, set initstatistics */
+
+ int next; /* next index for zdef_ */
+ int precision; /* index for precision problems */
+ int vridges; /* index for Voronoi ridges */
+ int tempi;
+ realT tempr;
+};
+
+/*========== function prototypes ===========*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void qh_allstatA(qhT *qh);
+void qh_allstatB(qhT *qh);
+void qh_allstatC(qhT *qh);
+void qh_allstatD(qhT *qh);
+void qh_allstatE(qhT *qh);
+void qh_allstatE2(qhT *qh);
+void qh_allstatF(qhT *qh);
+void qh_allstatG(qhT *qh);
+void qh_allstatH(qhT *qh);
+void qh_allstatI(qhT *qh);
+void qh_allstatistics(qhT *qh);
+void qh_collectstatistics(qhT *qh);
+void qh_initstatistics(qhT *qh);
+boolT qh_newstats(qhT *qh, int idx, int *nextindex);
+boolT qh_nostatistic(qhT *qh, int i);
+void qh_printallstatistics(qhT *qh, FILE *fp, const char *string);
+void qh_printstatistics(qhT *qh, FILE *fp, const char *string);
+void qh_printstatlevel(qhT *qh, FILE *fp, int id);
+void qh_printstats(qhT *qh, FILE *fp, int idx, int *nextindex);
+realT qh_stddev(int num, realT tot, realT tot2, realT *ave);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* qhDEFstat */
diff --git a/xs/src/qhull/src/libqhull_r/user_r.c b/xs/src/qhull/src/libqhull_r/user_r.c
new file mode 100644
index 000000000..bf7ed1d8d
--- /dev/null
+++ b/xs/src/qhull/src/libqhull_r/user_r.c
@@ -0,0 +1,527 @@
+/*<html><pre> -<a href="qh-user_r.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ user.c
+ user redefinable functions
+
+ see user2_r.c for qh_fprintf, qh_malloc, qh_free
+
+ see README.txt see COPYING.txt for copyright information.
+
+ see libqhull_r.h for data structures, macros, and user-callable functions.
+
+ see user_eg.c, user_eg2.c, and unix.c for examples.
+
+ see user.h for user-definable constants
+
+ use qh_NOmem in mem_r.h to turn off memory management
+ use qh_NOmerge in user.h to turn off facet merging
+ set qh_KEEPstatistics in user.h to 0 to turn off statistics
+
+ This is unsupported software. You're welcome to make changes,
+ but you're on your own if something goes wrong. Use 'Tc' to
+ check frequently. Usually qhull will report an error if
+ a data structure becomes inconsistent. If so, it also reports
+ the last point added to the hull, e.g., 102. You can then trace
+ the execution of qhull with "T4P102".
+
+ Please report any errors that you fix to qhull@qhull.org
+
+ Qhull-template is a template for calling qhull from within your application
+
+ if you recompile and load this module, then user.o will not be loaded
+ from qhull.a
+
+ you can add additional quick allocation sizes in qh_user_memsizes
+
+ if the other functions here are redefined to not use qh_print...,
+ then io.o will not be loaded from qhull.a. See user_eg_r.c for an
+ example. We recommend keeping io.o for the extra debugging
+ information it supplies.
+*/
+
+#include "qhull_ra.h"
+
+#include <stdarg.h>
+
+/*-<a href="qh-user_r.htm#TOC"
+ >-------------------------------</a><a name="qhull_template">-</a>
+
+ Qhull-template
+ Template for calling qhull from inside your program
+
+ returns:
+ exit code(see qh_ERR... in libqhull_r.h)
+ all memory freed
+
+ notes:
+ This can be called any number of times.
+
+*/
+#if 0
+{
+ int dim; /* dimension of points */
+ int numpoints; /* number of points */
+ coordT *points; /* array of coordinates for each point */
+ boolT ismalloc; /* True if qhull should free points in qh_freeqhull() or reallocation */
+ char flags[]= "qhull Tv"; /* option flags for qhull, see qh_opt.htm */
+ FILE *outfile= stdout; /* output from qh_produce_output(qh)
+ use NULL to skip qh_produce_output(qh) */
+ FILE *errfile= stderr; /* error messages from qhull code */
+ int exitcode; /* 0 if no error from qhull */
+ facetT *facet; /* set by FORALLfacets */
+ int curlong, totlong; /* memory remaining after qh_memfreeshort */
+
+ qhT qh_qh; /* Qhull's data structure. First argument of most calls */
+ qhT *qh= &qh_qh; /* Alternatively -- qhT *qh= (qhT*)malloc(sizeof(qhT)) */
+
+ QHULL_LIB_CHECK /* Check for compatible library */
+
+ qh_zero(qh, errfile);
+
+ /* initialize dim, numpoints, points[], ismalloc here */
+ exitcode= qh_new_qhull(qh, dim, numpoints, points, ismalloc,
+ flags, outfile, errfile);
+ if (!exitcode) { /* if no error */
+ /* 'qh->facet_list' contains the convex hull */
+ FORALLfacets {
+ /* ... your code ... */
+ }
+ }
+ qh_freeqhull(qh, !qh_ALL);
+ qh_memfreeshort(qh, &curlong, &totlong);
+ if (curlong || totlong)
+ qh_fprintf(qh, errfile, 7068, "qhull internal warning (main): did not free %d bytes of long memory(%d pieces)\n", totlong, curlong);
+}
+#endif
+
+/*-<a href="qh-user_r.htm#TOC"
+ >-------------------------------</a><a name="new_qhull">-</a>
+
+ qh_new_qhull(qh, dim, numpoints, points, ismalloc, qhull_cmd, outfile, errfile )
+ Run qhull and return results in qh.
+ Returns exitcode (0 if no errors).
+ Before first call, either call qh_zero(qh, errfile), or set qh to all zero.
+
+ notes:
+ do not modify points until finished with results.
+ The qhull data structure contains pointers into the points array.
+ do not call qhull functions before qh_new_qhull().
+ The qhull data structure is not initialized until qh_new_qhull().
+ do not call qh_init_A (global_r.c)
+
+ Default errfile is stderr, outfile may be null
+ qhull_cmd must start with "qhull "
+ projects points to a new point array for Delaunay triangulations ('d' and 'v')
+ transforms points into a new point array for halfspace intersection ('H')
+
+ see:
+ Qhull-template at the beginning of this file.
+ An example of using qh_new_qhull is user_eg_r.c
+*/
+int qh_new_qhull(qhT *qh, int dim, int numpoints, coordT *points, boolT ismalloc,
+ char *qhull_cmd, FILE *outfile, FILE *errfile) {
+ /* gcc may issue a "might be clobbered" warning for dim, points, and ismalloc [-Wclobbered].
+ These parameters are not referenced after a longjmp() and hence not clobbered.
+ See http://stackoverflow.com/questions/7721854/what-sense-do-these-clobbered-variable-warnings-make */
+ int exitcode, hulldim;
+ boolT new_ismalloc;
+ coordT *new_points;
+
+ if(!errfile){
+ errfile= stderr;
+ }
+ if (!qh->qhmem.ferr) {
+ qh_meminit(qh, errfile);
+ } else {
+ qh_memcheck(qh);
+ }
+ if (strncmp(qhull_cmd, "qhull ", (size_t)6)) {
+ qh_fprintf(qh, errfile, 6186, "qhull error (qh_new_qhull): start qhull_cmd argument with \"qhull \"\n");
+ return qh_ERRinput;
+ }
+ qh_initqhull_start(qh, NULL, outfile, errfile);
+ trace1((qh, qh->ferr, 1044, "qh_new_qhull: build new Qhull for %d %d-d points with %s\n", numpoints, dim, qhull_cmd));
+ exitcode = setjmp(qh->errexit);
+ if (!exitcode)
+ {
+ qh->NOerrexit = False;
+ qh_initflags(qh, qhull_cmd);
+ if (qh->DELAUNAY)
+ qh->PROJECTdelaunay= True;
+ if (qh->HALFspace) {
+ /* points is an array of halfspaces,
+ the last coordinate of each halfspace is its offset */
+ hulldim= dim-1;
+ qh_setfeasible(qh, hulldim);
+ new_points= qh_sethalfspace_all(qh, dim, numpoints, points, qh->feasible_point);
+ new_ismalloc= True;
+ if (ismalloc)
+ qh_free(points);
+ }else {
+ hulldim= dim;
+ new_points= points;
+ new_ismalloc= ismalloc;
+ }
+ qh_init_B(qh, new_points, numpoints, hulldim, new_ismalloc);
+ qh_qhull(qh);
+ qh_check_output(qh);
+ if (outfile) {
+ qh_produce_output(qh);
+ }else {
+ qh_prepare_output(qh);
+ }
+ if (qh->VERIFYoutput && !qh->STOPpoint && !qh->STOPcone)
+ qh_check_points(qh);
+ }
+ qh->NOerrexit = True;
+ return exitcode;
+} /* new_qhull */
+
+/*-<a href="qh-user_r.htm#TOC"
+ >-------------------------------</a><a name="errexit">-</a>
+
+ qh_errexit(qh, exitcode, facet, ridge )
+ report and exit from an error
+ report facet and ridge if non-NULL
+ reports useful information such as last point processed
+ set qh.FORCEoutput to print neighborhood of facet
+
+ see:
+ qh_errexit2() in libqhull_r.c for printing 2 facets
+
+ design:
+ check for error within error processing
+ compute qh.hulltime
+ print facet and ridge (if any)
+ report commandString, options, qh.furthest_id
+ print summary and statistics (including precision statistics)
+ if qh_ERRsingular
+ print help text for singular data set
+ exit program via long jump (if defined) or exit()
+*/
+void qh_errexit(qhT *qh, int exitcode, facetT *facet, ridgeT *ridge) {
+
+ if (qh->ERREXITcalled) {
+ qh_fprintf(qh, qh->ferr, 8126, "\nqhull error while processing previous error. Exit program\n");
+ qh_exit(qh_ERRqhull);
+ }
+ qh->ERREXITcalled= True;
+ if (!qh->QHULLfinished)
+ qh->hulltime= qh_CPUclock - qh->hulltime;
+ qh_errprint(qh, "ERRONEOUS", facet, NULL, ridge, NULL);
+ qh_fprintf(qh, qh->ferr, 8127, "\nWhile executing: %s | %s\n", qh->rbox_command, qh->qhull_command);
+ qh_fprintf(qh, qh->ferr, 8128, "Options selected for Qhull %s:\n%s\n", qh_version, qh->qhull_options);
+ if (qh->furthest_id >= 0) {
+ qh_fprintf(qh, qh->ferr, 8129, "Last point added to hull was p%d.", qh->furthest_id);
+ if (zzval_(Ztotmerge))
+ qh_fprintf(qh, qh->ferr, 8130, " Last merge was #%d.", zzval_(Ztotmerge));
+ if (qh->QHULLfinished)
+ qh_fprintf(qh, qh->ferr, 8131, "\nQhull has finished constructing the hull.");
+ else if (qh->POSTmerging)
+ qh_fprintf(qh, qh->ferr, 8132, "\nQhull has started post-merging.");
+ qh_fprintf(qh, qh->ferr, 8133, "\n");
+ }
+ if (qh->FORCEoutput && (qh->QHULLfinished || (!facet && !ridge)))
+ qh_produce_output(qh);
+ else if (exitcode != qh_ERRinput) {
+ if (exitcode != qh_ERRsingular && zzval_(Zsetplane) > qh->hull_dim+1) {
+ qh_fprintf(qh, qh->ferr, 8134, "\nAt error exit:\n");
+ qh_printsummary(qh, qh->ferr);
+ if (qh->PRINTstatistics) {
+ qh_collectstatistics(qh);
+ qh_printstatistics(qh, qh->ferr, "at error exit");
+ qh_memstatistics(qh, qh->ferr);
+ }
+ }
+ if (qh->PRINTprecision)
+ qh_printstats(qh, qh->ferr, qh->qhstat.precision, NULL);
+ }
+ if (!exitcode)
+ exitcode= qh_ERRqhull;
+ else if (exitcode == qh_ERRsingular)
+ qh_printhelp_singular(qh, qh->ferr);
+ else if (exitcode == qh_ERRprec && !qh->PREmerge)
+ qh_printhelp_degenerate(qh, qh->ferr);
+ if (qh->NOerrexit) {
+ qh_fprintf(qh, qh->ferr, 6187, "qhull error while ending program, or qh->NOerrexit not cleared after setjmp(). Exit program with error.\n");
+ qh_exit(qh_ERRqhull);
+ }
+ qh->ERREXITcalled= False;
+ qh->NOerrexit= True;
+ qh->ALLOWrestart= False; /* longjmp will undo qh_build_withrestart */
+ longjmp(qh->errexit, exitcode);
+} /* errexit */
+
+
+/*-<a href="qh-user_r.htm#TOC"
+ >-------------------------------</a><a name="errprint">-</a>
+
+ qh_errprint(qh, fp, string, atfacet, otherfacet, atridge, atvertex )
+ prints out the information of facets and ridges to fp
+ also prints neighbors and geomview output
+
+ notes:
+ except for string, any parameter may be NULL
+*/
+void qh_errprint(qhT *qh, const char *string, facetT *atfacet, facetT *otherfacet, ridgeT *atridge, vertexT *atvertex) {
+ int i;
+
+ if (atfacet) {
+ qh_fprintf(qh, qh->ferr, 8135, "%s FACET:\n", string);
+ qh_printfacet(qh, qh->ferr, atfacet);
+ }
+ if (otherfacet) {
+ qh_fprintf(qh, qh->ferr, 8136, "%s OTHER FACET:\n", string);
+ qh_printfacet(qh, qh->ferr, otherfacet);
+ }
+ if (atridge) {
+ qh_fprintf(qh, qh->ferr, 8137, "%s RIDGE:\n", string);
+ qh_printridge(qh, qh->ferr, atridge);
+ if (atridge->top && atridge->top != atfacet && atridge->top != otherfacet)
+ qh_printfacet(qh, qh->ferr, atridge->top);
+ if (atridge->bottom
+ && atridge->bottom != atfacet && atridge->bottom != otherfacet)
+ qh_printfacet(qh, qh->ferr, atridge->bottom);
+ if (!atfacet)
+ atfacet= atridge->top;
+ if (!otherfacet)
+ otherfacet= otherfacet_(atridge, atfacet);
+ }
+ if (atvertex) {
+ qh_fprintf(qh, qh->ferr, 8138, "%s VERTEX:\n", string);
+ qh_printvertex(qh, qh->ferr, atvertex);
+ }
+ if (qh->fout && qh->FORCEoutput && atfacet && !qh->QHULLfinished && !qh->IStracing) {
+ qh_fprintf(qh, qh->ferr, 8139, "ERRONEOUS and NEIGHBORING FACETS to output\n");
+ for (i=0; i < qh_PRINTEND; i++) /* use fout for geomview output */
+ qh_printneighborhood(qh, qh->fout, qh->PRINTout[i], atfacet, otherfacet,
+ !qh_ALL);
+ }
+} /* errprint */
+
+
+/*-<a href="qh-user_r.htm#TOC"
+ >-------------------------------</a><a name="printfacetlist">-</a>
+
+ qh_printfacetlist(qh, fp, facetlist, facets, printall )
+ print all fields for a facet list and/or set of facets to fp
+ if !printall,
+ only prints good facets
+
+ notes:
+ also prints all vertices
+*/
+void qh_printfacetlist(qhT *qh, facetT *facetlist, setT *facets, boolT printall) {
+ facetT *facet, **facetp;
+
+ qh_printbegin(qh, qh->ferr, qh_PRINTfacets, facetlist, facets, printall);
+ FORALLfacet_(facetlist)
+ qh_printafacet(qh, qh->ferr, qh_PRINTfacets, facet, printall);
+ FOREACHfacet_(facets)
+ qh_printafacet(qh, qh->ferr, qh_PRINTfacets, facet, printall);
+ qh_printend(qh, qh->ferr, qh_PRINTfacets, facetlist, facets, printall);
+} /* printfacetlist */
+
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="printhelp_degenerate">-</a>
+
+ qh_printhelp_degenerate(qh, fp )
+ prints descriptive message for precision error
+
+ notes:
+ no message if qh_QUICKhelp
+*/
+void qh_printhelp_degenerate(qhT *qh, FILE *fp) {
+
+ if (qh->MERGEexact || qh->PREmerge || qh->JOGGLEmax < REALmax/2)
+ qh_fprintf(qh, fp, 9368, "\n\
+A Qhull error has occurred. Qhull should have corrected the above\n\
+precision error. Please send the input and all of the output to\n\
+qhull_bug@qhull.org\n");
+ else if (!qh_QUICKhelp) {
+ qh_fprintf(qh, fp, 9369, "\n\
+Precision problems were detected during construction of the convex hull.\n\
+This occurs because convex hull algorithms assume that calculations are\n\
+exact, but floating-point arithmetic has roundoff errors.\n\
+\n\
+To correct for precision problems, do not use 'Q0'. By default, Qhull\n\
+selects 'C-0' or 'Qx' and merges non-convex facets. With option 'QJ',\n\
+Qhull joggles the input to prevent precision problems. See \"Imprecision\n\
+in Qhull\" (qh-impre.htm).\n\
+\n\
+If you use 'Q0', the output may include\n\
+coplanar ridges, concave ridges, and flipped facets. In 4-d and higher,\n\
+Qhull may produce a ridge with four neighbors or two facets with the same \n\
+vertices. Qhull reports these events when they occur. It stops when a\n\
+concave ridge, flipped facet, or duplicate facet occurs.\n");
+#if REALfloat
+ qh_fprintf(qh, fp, 9370, "\
+\n\
+Qhull is currently using single precision arithmetic. The following\n\
+will probably remove the precision problems:\n\
+ - recompile qhull for realT precision(#define REALfloat 0 in user.h).\n");
+#endif
+ if (qh->DELAUNAY && !qh->SCALElast && qh->MAXabs_coord > 1e4)
+ qh_fprintf(qh, fp, 9371, "\
+\n\
+When computing the Delaunay triangulation of coordinates > 1.0,\n\
+ - use 'Qbb' to scale the last coordinate to [0,m] (max previous coordinate)\n");
+ if (qh->DELAUNAY && !qh->ATinfinity)
+ qh_fprintf(qh, fp, 9372, "\
+When computing the Delaunay triangulation:\n\
+ - use 'Qz' to add a point at-infinity. This reduces precision problems.\n");
+
+ qh_fprintf(qh, fp, 9373, "\
+\n\
+If you need triangular output:\n\
+ - use option 'Qt' to triangulate the output\n\
+ - use option 'QJ' to joggle the input points and remove precision errors\n\
+ - use option 'Ft'. It triangulates non-simplicial facets with added points.\n\
+\n\
+If you must use 'Q0',\n\
+try one or more of the following options. They can not guarantee an output.\n\
+ - use 'QbB' to scale the input to a cube.\n\
+ - use 'Po' to produce output and prevent partitioning for flipped facets\n\
+ - use 'V0' to set min. distance to visible facet as 0 instead of roundoff\n\
+ - use 'En' to specify a maximum roundoff error less than %2.2g.\n\
+ - options 'Qf', 'Qbb', and 'QR0' may also help\n",
+ qh->DISTround);
+ qh_fprintf(qh, fp, 9374, "\
+\n\
+To guarantee simplicial output:\n\
+ - use option 'Qt' to triangulate the output\n\
+ - use option 'QJ' to joggle the input points and remove precision errors\n\
+ - use option 'Ft' to triangulate the output by adding points\n\
+ - use exact arithmetic (see \"Imprecision in Qhull\", qh-impre.htm)\n\
+");
+ }
+} /* printhelp_degenerate */
+
+
+/*-<a href="qh-globa_r.htm#TOC"
+ >-------------------------------</a><a name="printhelp_narrowhull">-</a>
+
+ qh_printhelp_narrowhull(qh, minangle )
+ Warn about a narrow hull
+
+ notes:
+ Alternatively, reduce qh_WARNnarrow in user.h
+
+*/
+void qh_printhelp_narrowhull(qhT *qh, FILE *fp, realT minangle) {
+
+ qh_fprintf(qh, fp, 9375, "qhull precision warning: \n\
+The initial hull is narrow (cosine of min. angle is %.16f).\n\
+Is the input lower dimensional (e.g., on a plane in 3-d)? Qhull may\n\
+produce a wide facet. Options 'QbB' (scale to unit box) or 'Qbb' (scale\n\
+last coordinate) may remove this warning. Use 'Pp' to skip this warning.\n\
+See 'Limitations' in qh-impre.htm.\n",
+ -minangle); /* convert from angle between normals to angle between facets */
+} /* printhelp_narrowhull */
+
+/*-<a href="qh-io_r.htm#TOC"
+ >-------------------------------</a><a name="printhelp_singular">-</a>
+
+ qh_printhelp_singular(qh, fp )
+ prints descriptive message for singular input
+*/
+void qh_printhelp_singular(qhT *qh, FILE *fp) {
+ facetT *facet;
+ vertexT *vertex, **vertexp;
+ realT min, max, *coord, dist;
+ int i,k;
+
+ qh_fprintf(qh, fp, 9376, "\n\
+The input to qhull appears to be less than %d dimensional, or a\n\
+computation has overflowed.\n\n\
+Qhull could not construct a clearly convex simplex from points:\n",
+ qh->hull_dim);
+ qh_printvertexlist(qh, fp, "", qh->facet_list, NULL, qh_ALL);
+ if (!qh_QUICKhelp)
+ qh_fprintf(qh, fp, 9377, "\n\
+The center point is coplanar with a facet, or a vertex is coplanar\n\
+with a neighboring facet. The maximum round off error for\n\
+computing distances is %2.2g. The center point, facets and distances\n\
+to the center point are as follows:\n\n", qh->DISTround);
+ qh_printpointid(qh, fp, "center point", qh->hull_dim, qh->interior_point, qh_IDunknown);
+ qh_fprintf(qh, fp, 9378, "\n");
+ FORALLfacets {
+ qh_fprintf(qh, fp, 9379, "facet");
+ FOREACHvertex_(facet->vertices)
+ qh_fprintf(qh, fp, 9380, " p%d", qh_pointid(qh, vertex->point));
+ zinc_(Zdistio);
+ qh_distplane(qh, qh->interior_point, facet, &dist);
+ qh_fprintf(qh, fp, 9381, " distance= %4.2g\n", dist);
+ }
+ if (!qh_QUICKhelp) {
+ if (qh->HALFspace)
+ qh_fprintf(qh, fp, 9382, "\n\
+These points are the dual of the given halfspaces. They indicate that\n\
+the intersection is degenerate.\n");
+ qh_fprintf(qh, fp, 9383,"\n\
+These points either have a maximum or minimum x-coordinate, or\n\
+they maximize the determinant for k coordinates. Trial points\n\
+are first selected from points that maximize a coordinate.\n");
+ if (qh->hull_dim >= qh_INITIALmax)
+ qh_fprintf(qh, fp, 9384, "\n\
+Because of the high dimension, the min x-coordinate and max-coordinate\n\
+points are used if the determinant is non-zero. Option 'Qs' will\n\
+do a better, though much slower, job. Instead of 'Qs', you can change\n\
+the points by randomly rotating the input with 'QR0'.\n");
+ }
+ qh_fprintf(qh, fp, 9385, "\nThe min and max coordinates for each dimension are:\n");
+ for (k=0; k < qh->hull_dim; k++) {
+ min= REALmax;
+ max= -REALmin;
+ for (i=qh->num_points, coord= qh->first_point+k; i--; coord += qh->hull_dim) {
+ maximize_(max, *coord);
+ minimize_(min, *coord);
+ }
+ qh_fprintf(qh, fp, 9386, " %d: %8.4g %8.4g difference= %4.4g\n", k, min, max, max-min);
+ }
+ if (!qh_QUICKhelp) {
+ qh_fprintf(qh, fp, 9387, "\n\
+If the input should be full dimensional, you have several options that\n\
+may determine an initial simplex:\n\
+ - use 'QJ' to joggle the input and make it full dimensional\n\
+ - use 'QbB' to scale the points to the unit cube\n\
+ - use 'QR0' to randomly rotate the input for different maximum points\n\
+ - use 'Qs' to search all points for the initial simplex\n\
+ - use 'En' to specify a maximum roundoff error less than %2.2g.\n\
+ - trace execution with 'T3' to see the determinant for each point.\n",
+ qh->DISTround);
+#if REALfloat
+ qh_fprintf(qh, fp, 9388, "\
+ - recompile qhull for realT precision(#define REALfloat 0 in libqhull_r.h).\n");
+#endif
+ qh_fprintf(qh, fp, 9389, "\n\
+If the input is lower dimensional:\n\
+ - use 'QJ' to joggle the input and make it full dimensional\n\
+ - use 'Qbk:0Bk:0' to delete coordinate k from the input. You should\n\
+ pick the coordinate with the least range. The hull will have the\n\
+ correct topology.\n\
+ - determine the flat containing the points, rotate the points\n\
+ into a coordinate plane, and delete the other coordinates.\n\
+ - add one or more points to make the input full dimensional.\n\
+");
+ }
+} /* printhelp_singular */
+
+/*-<a href="qh-globa_r.htm#TOC"
+ >-------------------------------</a><a name="user_memsizes">-</a>
+
+ qh_user_memsizes(qh)
+ allocate up to 10 additional, quick allocation sizes
+
+ notes:
+ increase maximum number of allocations in qh_initqhull_mem()
+*/
+void qh_user_memsizes(qhT *qh) {
+
+ QHULL_UNUSED(qh)
+ /* qh_memsize(qh, size); */
+} /* user_memsizes */
+
+
diff --git a/xs/src/qhull/src/libqhull_r/user_r.h b/xs/src/qhull/src/libqhull_r/user_r.h
new file mode 100644
index 000000000..7cca65a80
--- /dev/null
+++ b/xs/src/qhull/src/libqhull_r/user_r.h
@@ -0,0 +1,882 @@
+/*<html><pre> -<a href="qh-user_r.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ user.h
+ user redefinable constants
+
+ for each source file, user_r.h is included first
+
+ see qh-user_r.htm. see COPYING for copyright information.
+
+ See user_r.c for sample code.
+
+ before reading any code, review libqhull_r.h for data structure definitions
+
+Sections:
+ ============= qhull library constants ======================
+ ============= data types and configuration macros ==========
+ ============= performance related constants ================
+ ============= memory constants =============================
+ ============= joggle constants =============================
+ ============= conditional compilation ======================
+ ============= -merge constants- ============================
+
+Code flags --
+ NOerrors -- the code does not call qh_errexit()
+ WARN64 -- the code may be incompatible with 64-bit pointers
+
+*/
+
+#include <time.h>
+
+#ifndef qhDEFuser
+#define qhDEFuser 1
+
+/* Derived from Qt's corelib/global/qglobal.h */
+#if !defined(SAG_COM) && !defined(__CYGWIN__) && (defined(WIN64) || defined(_WIN64) || defined(__WIN64__) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__))
+# define QHULL_OS_WIN
+#elif defined(__MWERKS__) && defined(__INTEL__) /* Metrowerks discontinued before the release of Intel Macs */
+# define QHULL_OS_WIN
+#endif
+
+/*============================================================*/
+/*============= qhull library constants ======================*/
+/*============================================================*/
+
+/*-<a href="qh-user_r.htm#TOC"
+ >--------------------------------</a><a name="filenamelen">-</a>
+
+ FILENAMElen -- max length for TI and TO filenames
+
+*/
+
+#define qh_FILENAMElen 500
+
+/*-<a href="qh-user_r.htm#TOC"
+ >--------------------------------</a><a name="msgcode">-</a>
+
+ msgcode -- Unique message codes for qh_fprintf
+
+ If add new messages, assign these values and increment in user.h and user_r.h
+ See QhullError.h for 10000 errors.
+
+ def counters = [27, 1048, 2059, 3026, 4068, 5003,
+ 6273, 7081, 8147, 9411, 10000, 11029]
+
+ See: qh_ERR* [libqhull_r.h]
+*/
+
+#define MSG_TRACE0 0
+#define MSG_TRACE1 1000
+#define MSG_TRACE2 2000
+#define MSG_TRACE3 3000
+#define MSG_TRACE4 4000
+#define MSG_TRACE5 5000
+#define MSG_ERROR 6000 /* errors written to qh.ferr */
+#define MSG_WARNING 7000
+#define MSG_STDERR 8000 /* log messages Written to qh.ferr */
+#define MSG_OUTPUT 9000
+#define MSG_QHULL_ERROR 10000 /* errors thrown by QhullError.cpp (QHULLlastError is in QhullError.h) */
+#define MSG_FIXUP 11000 /* FIXUP QH11... */
+#define MSG_MAXLEN 3000 /* qh_printhelp_degenerate() in user.c */
+
+
+/*-<a href="qh-user_r.htm#TOC"
+ >--------------------------------</a><a name="qh_OPTIONline">-</a>
+
+ qh_OPTIONline -- max length of an option line 'FO'
+*/
+#define qh_OPTIONline 80
+
+/*============================================================*/
+/*============= data types and configuration macros ==========*/
+/*============================================================*/
+
+/*-<a href="qh-user_r.htm#TOC"
+ >--------------------------------</a><a name="realT">-</a>
+
+ realT
+ set the size of floating point numbers
+
+ qh_REALdigits
+ maximimum number of significant digits
+
+ qh_REAL_1, qh_REAL_2n, qh_REAL_3n
+ format strings for printf
+
+ qh_REALmax, qh_REALmin
+ maximum and minimum (near zero) values
+
+ qh_REALepsilon
+ machine roundoff. Maximum roundoff error for addition and multiplication.
+
+ notes:
+ Select whether to store floating point numbers in single precision (float)
+ or double precision (double).
+
+ Use 'float' to save about 8% in time and 25% in space. This is particularly
+ helpful if high-d where convex hulls are space limited. Using 'float' also
+ reduces the printed size of Qhull's output since numbers have 8 digits of
+ precision.
+
+ Use 'double' when greater arithmetic precision is needed. This is needed
+ for Delaunay triangulations and Voronoi diagrams when you are not merging
+ facets.
+
+ If 'double' gives insufficient precision, your data probably includes
+ degeneracies. If so you should use facet merging (done by default)
+ or exact arithmetic (see imprecision section of manual, qh-impre.htm).
+ You may also use option 'Po' to force output despite precision errors.
+
+ You may use 'long double', but many format statements need to be changed
+ and you may need a 'long double' square root routine. S. Grundmann
+ (sg@eeiwzb.et.tu-dresden.de) has done this. He reports that the code runs
+ much slower with little gain in precision.
+
+ WARNING: on some machines, int f(){realT a= REALmax;return (a == REALmax);}
+ returns False. Use (a > REALmax/2) instead of (a == REALmax).
+
+ REALfloat = 1 all numbers are 'float' type
+ = 0 all numbers are 'double' type
+*/
+#define REALfloat 1
+
+#if (REALfloat == 1)
+#define realT float
+#define REALmax FLT_MAX
+#define REALmin FLT_MIN
+#define REALepsilon FLT_EPSILON
+#define qh_REALdigits 8 /* maximum number of significant digits */
+#define qh_REAL_1 "%6.8g "
+#define qh_REAL_2n "%6.8g %6.8g\n"
+#define qh_REAL_3n "%6.8g %6.8g %6.8g\n"
+
+#elif (REALfloat == 0)
+#define realT double
+#define REALmax DBL_MAX
+#define REALmin DBL_MIN
+#define REALepsilon DBL_EPSILON
+#define qh_REALdigits 16 /* maximum number of significant digits */
+#define qh_REAL_1 "%6.16g "
+#define qh_REAL_2n "%6.16g %6.16g\n"
+#define qh_REAL_3n "%6.16g %6.16g %6.16g\n"
+
+#else
+#error unknown float option
+#endif
+
+/*-<a href="qh-user_r.htm#TOC"
+ >--------------------------------</a><a name="countT">-</a>
+
+ countT
+ The type for counts and identifiers (e.g., the number of points, vertex identifiers)
+ Currently used by C++ code-only. Decided against using it for setT because most sets are small.
+
+ Defined as 'int' for C-code compatibility and QH11026
+
+ FIXUP QH11026 countT may be defined as a unsigned value, but several code issues need to be solved first. See countT in Changes.txt
+*/
+
+#ifndef DEFcountT
+#define DEFcountT 1
+typedef int countT;
+#endif
+#define COUNTmax 0x7fffffff
+
+
+/*-<a href="qh-user_r.htm#TOC"
+ >--------------------------------</a><a name="CPUclock">-</a>
+
+ qh_CPUclock
+ define the clock() function for reporting the total time spent by Qhull
+ returns CPU ticks as a 'long int'
+ qh_CPUclock is only used for reporting the total time spent by Qhull
+
+ qh_SECticks
+ the number of clock ticks per second
+
+ notes:
+ looks for CLOCKS_PER_SEC, CLOCKS_PER_SECOND, or assumes microseconds
+ to define a custom clock, set qh_CLOCKtype to 0
+
+ if your system does not use clock() to return CPU ticks, replace
+ qh_CPUclock with the corresponding function. It is converted
+ to 'unsigned long' to prevent wrap-around during long runs. By default,
+ <time.h> defines clock_t as 'long'
+
+ Set qh_CLOCKtype to
+
+ 1 for CLOCKS_PER_SEC, CLOCKS_PER_SECOND, or microsecond
+ Note: may fail if more than 1 hour elapsed time
+
+ 2 use qh_clock() with POSIX times() (see global_r.c)
+*/
+#define qh_CLOCKtype 1 /* change to the desired number */
+
+#if (qh_CLOCKtype == 1)
+
+#if defined(CLOCKS_PER_SECOND)
+#define qh_CPUclock ((unsigned long)clock()) /* return CPU clock */
+#define qh_SECticks CLOCKS_PER_SECOND
+
+#elif defined(CLOCKS_PER_SEC)
+#define qh_CPUclock ((unsigned long)clock()) /* return CPU clock */
+#define qh_SECticks CLOCKS_PER_SEC
+
+#elif defined(CLK_TCK)
+#define qh_CPUclock ((unsigned long)clock()) /* return CPU clock */
+#define qh_SECticks CLK_TCK
+
+#else
+#define qh_CPUclock ((unsigned long)clock()) /* return CPU clock */
+#define qh_SECticks 1E6
+#endif
+
+#elif (qh_CLOCKtype == 2)
+#define qh_CPUclock qh_clock() /* return CPU clock */
+#define qh_SECticks 100
+
+#else /* qh_CLOCKtype == ? */
+#error unknown clock option
+#endif
+
+/*-<a href="qh-user_r.htm#TOC"
+ >--------------------------------</a><a name="RANDOM">-</a>
+
+ qh_RANDOMtype, qh_RANDOMmax, qh_RANDOMseed
+ define random number generator
+
+ qh_RANDOMint generates a random integer between 0 and qh_RANDOMmax.
+ qh_RANDOMseed sets the random number seed for qh_RANDOMint
+
+ Set qh_RANDOMtype (default 5) to:
+ 1 for random() with 31 bits (UCB)
+ 2 for rand() with RAND_MAX or 15 bits (system 5)
+ 3 for rand() with 31 bits (Sun)
+ 4 for lrand48() with 31 bits (Solaris)
+ 5 for qh_rand(qh) with 31 bits (included with Qhull, requires 'qh')
+
+ notes:
+ Random numbers are used by rbox to generate point sets. Random
+ numbers are used by Qhull to rotate the input ('QRn' option),
+ simulate a randomized algorithm ('Qr' option), and to simulate
+ roundoff errors ('Rn' option).
+
+ Random number generators differ between systems. Most systems provide
+ rand() but the period varies. The period of rand() is not critical
+ since qhull does not normally use random numbers.
+
+ The default generator is Park & Miller's minimal standard random
+ number generator [CACM 31:1195 '88]. It is included with Qhull.
+
+ If qh_RANDOMmax is wrong, qhull will report a warning and Geomview
+ output will likely be invisible.
+*/
+#define qh_RANDOMtype 5 /* *** change to the desired number *** */
+
+#if (qh_RANDOMtype == 1)
+#define qh_RANDOMmax ((realT)0x7fffffffUL) /* 31 bits, random()/MAX */
+#define qh_RANDOMint random()
+#define qh_RANDOMseed_(qh, seed) srandom(seed);
+
+#elif (qh_RANDOMtype == 2)
+#ifdef RAND_MAX
+#define qh_RANDOMmax ((realT)RAND_MAX)
+#else
+#define qh_RANDOMmax ((realT)32767) /* 15 bits (System 5) */
+#endif
+#define qh_RANDOMint rand()
+#define qh_RANDOMseed_(qh, seed) srand((unsigned)seed);
+
+#elif (qh_RANDOMtype == 3)
+#define qh_RANDOMmax ((realT)0x7fffffffUL) /* 31 bits, Sun */
+#define qh_RANDOMint rand()
+#define qh_RANDOMseed_(qh, seed) srand((unsigned)seed);
+
+#elif (qh_RANDOMtype == 4)
+#define qh_RANDOMmax ((realT)0x7fffffffUL) /* 31 bits, lrand38()/MAX */
+#define qh_RANDOMint lrand48()
+#define qh_RANDOMseed_(qh, seed) srand48(seed);
+
+#elif (qh_RANDOMtype == 5) /* 'qh' is an implicit parameter */
+#define qh_RANDOMmax ((realT)2147483646UL) /* 31 bits, qh_rand/MAX */
+#define qh_RANDOMint qh_rand(qh)
+#define qh_RANDOMseed_(qh, seed) qh_srand(qh, seed);
+/* unlike rand(), never returns 0 */
+
+#else
+#error: unknown random option
+#endif
+
+/*-<a href="qh-user_r.htm#TOC"
+ >--------------------------------</a><a name="ORIENTclock">-</a>
+
+ qh_ORIENTclock
+ 0 for inward pointing normals by Geomview convention
+*/
+#define qh_ORIENTclock 0
+
+
+/*============================================================*/
+/*============= joggle constants =============================*/
+/*============================================================*/
+
+/*-<a href="qh-user_r.htm#TOC"
+>--------------------------------</a><a name="JOGGLEdefault">-</a>
+
+qh_JOGGLEdefault
+default qh.JOGGLEmax is qh.DISTround * qh_JOGGLEdefault
+
+notes:
+rbox s r 100 | qhull QJ1e-15 QR0 generates 90% faults at distround 7e-16
+rbox s r 100 | qhull QJ1e-14 QR0 generates 70% faults
+rbox s r 100 | qhull QJ1e-13 QR0 generates 35% faults
+rbox s r 100 | qhull QJ1e-12 QR0 generates 8% faults
+rbox s r 100 | qhull QJ1e-11 QR0 generates 1% faults
+rbox s r 100 | qhull QJ1e-10 QR0 generates 0% faults
+rbox 1000 W0 | qhull QJ1e-12 QR0 generates 86% faults
+rbox 1000 W0 | qhull QJ1e-11 QR0 generates 20% faults
+rbox 1000 W0 | qhull QJ1e-10 QR0 generates 2% faults
+the later have about 20 points per facet, each of which may interfere
+
+pick a value large enough to avoid retries on most inputs
+*/
+#define qh_JOGGLEdefault 30000.0
+
+/*-<a href="qh-user_r.htm#TOC"
+>--------------------------------</a><a name="JOGGLEincrease">-</a>
+
+qh_JOGGLEincrease
+factor to increase qh.JOGGLEmax on qh_JOGGLEretry or qh_JOGGLEagain
+*/
+#define qh_JOGGLEincrease 10.0
+
+/*-<a href="qh-user_r.htm#TOC"
+>--------------------------------</a><a name="JOGGLEretry">-</a>
+
+qh_JOGGLEretry
+if ZZretry = qh_JOGGLEretry, increase qh.JOGGLEmax
+
+notes:
+try twice at the original value in case of bad luck the first time
+*/
+#define qh_JOGGLEretry 2
+
+/*-<a href="qh-user_r.htm#TOC"
+>--------------------------------</a><a name="JOGGLEagain">-</a>
+
+qh_JOGGLEagain
+every following qh_JOGGLEagain, increase qh.JOGGLEmax
+
+notes:
+1 is OK since it's already failed qh_JOGGLEretry times
+*/
+#define qh_JOGGLEagain 1
+
+/*-<a href="qh-user_r.htm#TOC"
+>--------------------------------</a><a name="JOGGLEmaxincrease">-</a>
+
+qh_JOGGLEmaxincrease
+maximum qh.JOGGLEmax due to qh_JOGGLEincrease
+relative to qh.MAXwidth
+
+notes:
+qh.joggleinput will retry at this value until qh_JOGGLEmaxretry
+*/
+#define qh_JOGGLEmaxincrease 1e-2
+
+/*-<a href="qh-user_r.htm#TOC"
+>--------------------------------</a><a name="JOGGLEmaxretry">-</a>
+
+qh_JOGGLEmaxretry
+stop after qh_JOGGLEmaxretry attempts
+*/
+#define qh_JOGGLEmaxretry 100
+
+/*============================================================*/
+/*============= performance related constants ================*/
+/*============================================================*/
+
+/*-<a href="qh-user_r.htm#TOC"
+ >--------------------------------</a><a name="HASHfactor">-</a>
+
+ qh_HASHfactor
+ total hash slots / used hash slots. Must be at least 1.1.
+
+ notes:
+ =2 for at worst 50% occupancy for qh.hash_table and normally 25% occupancy
+*/
+#define qh_HASHfactor 2
+
+/*-<a href="qh-user_r.htm#TOC"
+ >--------------------------------</a><a name="VERIFYdirect">-</a>
+
+ qh_VERIFYdirect
+ with 'Tv' verify all points against all facets if op count is smaller
+
+ notes:
+ if greater, calls qh_check_bestdist() instead
+*/
+#define qh_VERIFYdirect 1000000
+
+/*-<a href="qh-user_r.htm#TOC"
+ >--------------------------------</a><a name="INITIALsearch">-</a>
+
+ qh_INITIALsearch
+ if qh_INITIALmax, search points up to this dimension
+*/
+#define qh_INITIALsearch 6
+
+/*-<a href="qh-user_r.htm#TOC"
+ >--------------------------------</a><a name="INITIALmax">-</a>
+
+ qh_INITIALmax
+ if dim >= qh_INITIALmax, use min/max coordinate points for initial simplex
+
+ notes:
+ from points with non-zero determinants
+ use option 'Qs' to override (much slower)
+*/
+#define qh_INITIALmax 8
+
+/*============================================================*/
+/*============= memory constants =============================*/
+/*============================================================*/
+
+/*-<a href="qh-user_r.htm#TOC"
+ >--------------------------------</a><a name="MEMalign">-</a>
+
+ qh_MEMalign
+ memory alignment for qh_meminitbuffers() in global_r.c
+
+ notes:
+ to avoid bus errors, memory allocation must consider alignment requirements.
+ malloc() automatically takes care of alignment. Since mem_r.c manages
+ its own memory, we need to explicitly specify alignment in
+ qh_meminitbuffers().
+
+ A safe choice is sizeof(double). sizeof(float) may be used if doubles
+ do not occur in data structures and pointers are the same size. Be careful
+ of machines (e.g., DEC Alpha) with large pointers.
+
+ If using gcc, best alignment is [fmax_() is defined in geom_r.h]
+ #define qh_MEMalign fmax_(__alignof__(realT),__alignof__(void *))
+*/
+#define qh_MEMalign ((int)(fmax_(sizeof(realT), sizeof(void *))))
+
+/*-<a href="qh-user_r.htm#TOC"
+ >--------------------------------</a><a name="MEMbufsize">-</a>
+
+ qh_MEMbufsize
+ size of additional memory buffers
+
+ notes:
+ used for qh_meminitbuffers() in global_r.c
+*/
+#define qh_MEMbufsize 0x10000 /* allocate 64K memory buffers */
+
+/*-<a href="qh-user_r.htm#TOC"
+ >--------------------------------</a><a name="MEMinitbuf">-</a>
+
+ qh_MEMinitbuf
+ size of initial memory buffer
+
+ notes:
+ use for qh_meminitbuffers() in global_r.c
+*/
+#define qh_MEMinitbuf 0x20000 /* initially allocate 128K buffer */
+
+/*-<a href="qh-user_r.htm#TOC"
+ >--------------------------------</a><a name="INFINITE">-</a>
+
+ qh_INFINITE
+ on output, indicates Voronoi center at infinity
+*/
+#define qh_INFINITE -10.101
+
+/*-<a href="qh-user_r.htm#TOC"
+ >--------------------------------</a><a name="DEFAULTbox">-</a>
+
+ qh_DEFAULTbox
+ default box size (Geomview expects 0.5)
+
+ qh_DEFAULTbox
+ default box size for integer coorindate (rbox only)
+*/
+#define qh_DEFAULTbox 0.5
+#define qh_DEFAULTzbox 1e6
+
+/*============================================================*/
+/*============= conditional compilation ======================*/
+/*============================================================*/
+
+/*-<a href="qh-user_r.htm#TOC"
+ >--------------------------------</a><a name="compiler">-</a>
+
+ __cplusplus
+ defined by C++ compilers
+
+ __MSC_VER
+ defined by Microsoft Visual C++
+
+ __MWERKS__ && __INTEL__
+ defined by Metrowerks when compiling for Windows (not Intel-based Macintosh)
+
+ __MWERKS__ && __POWERPC__
+ defined by Metrowerks when compiling for PowerPC-based Macintosh
+
+ __STDC__
+ defined for strict ANSI C
+*/
+
+/*-<a href="qh-user_r.htm#TOC"
+ >--------------------------------</a><a name="COMPUTEfurthest">-</a>
+
+ qh_COMPUTEfurthest
+ compute furthest distance to an outside point instead of storing it with the facet
+ =1 to compute furthest
+
+ notes:
+ computing furthest saves memory but costs time
+ about 40% more distance tests for partitioning
+ removes facet->furthestdist
+*/
+#define qh_COMPUTEfurthest 0
+
+/*-<a href="qh-user_r.htm#TOC"
+ >--------------------------------</a><a name="KEEPstatistics">-</a>
+
+ qh_KEEPstatistics
+ =0 removes most of statistic gathering and reporting
+
+ notes:
+ if 0, code size is reduced by about 4%.
+*/
+#define qh_KEEPstatistics 1
+
+/*-<a href="qh-user_r.htm#TOC"
+ >--------------------------------</a><a name="MAXoutside">-</a>
+
+ qh_MAXoutside
+ record outer plane for each facet
+ =1 to record facet->maxoutside
+
+ notes:
+ this takes a realT per facet and slightly slows down qhull
+ it produces better outer planes for geomview output
+*/
+#define qh_MAXoutside 1
+
+/*-<a href="qh-user_r.htm#TOC"
+ >--------------------------------</a><a name="NOmerge">-</a>
+
+ qh_NOmerge
+ disables facet merging if defined
+
+ notes:
+ This saves about 10% space.
+
+ Unless 'Q0'
+ qh_NOmerge sets 'QJ' to avoid precision errors
+
+ #define qh_NOmerge
+
+ see:
+ <a href="mem_r.h#NOmem">qh_NOmem</a> in mem_r.c
+
+ see user_r.c/user_eg.c for removing io_r.o
+*/
+
+/*-<a href="qh-user_r.htm#TOC"
+ >--------------------------------</a><a name="NOtrace">-</a>
+
+ qh_NOtrace
+ no tracing if defined
+
+ notes:
+ This saves about 5% space.
+
+ #define qh_NOtrace
+*/
+
+#if 0 /* sample code */
+ exitcode= qh_new_qhull(qhT *qh, dim, numpoints, points, ismalloc,
+ flags, outfile, errfile);
+ qh_freeqhull(qhT *qh, !qh_ALL); /* frees long memory used by second call */
+ qh_memfreeshort(qhT *qh, &curlong, &totlong); /* frees short memory and memory allocator */
+#endif
+
+/*-<a href="qh-user_r.htm#TOC"
+ >--------------------------------</a><a name="QUICKhelp">-</a>
+
+ qh_QUICKhelp
+ =1 to use abbreviated help messages, e.g., for degenerate inputs
+*/
+#define qh_QUICKhelp 0
+
+/*============================================================*/
+/*============= -merge constants- ============================*/
+/*============================================================*/
+/*
+ These constants effect facet merging. You probably will not need
+ to modify them. They effect the performance of facet merging.
+*/
+
+/*-<a href="qh-user_r.htm#TOC"
+ >--------------------------------</a><a name="DIMmergeVertex">-</a>
+
+ qh_DIMmergeVertex
+ max dimension for vertex merging (it is not effective in high-d)
+*/
+#define qh_DIMmergeVertex 6
+
+/*-<a href="qh-user_r.htm#TOC"
+ >--------------------------------</a><a name="DIMreduceBuild">-</a>
+
+ qh_DIMreduceBuild
+ max dimension for vertex reduction during build (slow in high-d)
+*/
+#define qh_DIMreduceBuild 5
+
+/*-<a href="qh-user_r.htm#TOC"
+ >--------------------------------</a><a name="BESTcentrum">-</a>
+
+ qh_BESTcentrum
+ if > 2*dim+n vertices, qh_findbestneighbor() tests centrums (faster)
+ else, qh_findbestneighbor() tests all vertices (much better merges)
+
+ qh_BESTcentrum2
+ if qh_BESTcentrum2 * DIM3 + BESTcentrum < #vertices tests centrums
+*/
+#define qh_BESTcentrum 20
+#define qh_BESTcentrum2 2
+
+/*-<a href="qh-user_r.htm#TOC"
+ >--------------------------------</a><a name="BESTnonconvex">-</a>
+
+ qh_BESTnonconvex
+ if > dim+n neighbors, qh_findbestneighbor() tests nonconvex ridges.
+
+ notes:
+ It is needed because qh_findbestneighbor is slow for large facets
+*/
+#define qh_BESTnonconvex 15
+
+/*-<a href="qh-user_r.htm#TOC"
+ >--------------------------------</a><a name="MAXnewmerges">-</a>
+
+ qh_MAXnewmerges
+ if >n newmerges, qh_merge_nonconvex() calls qh_reducevertices_centrums.
+
+ notes:
+ It is needed because postmerge can merge many facets at once
+*/
+#define qh_MAXnewmerges 2
+
+/*-<a href="qh-user_r.htm#TOC"
+ >--------------------------------</a><a name="MAXnewcentrum">-</a>
+
+ qh_MAXnewcentrum
+ if <= dim+n vertices (n approximates the number of merges),
+ reset the centrum in qh_updatetested() and qh_mergecycle_facets()
+
+ notes:
+ needed to reduce cost and because centrums may move too much if
+ many vertices in high-d
+*/
+#define qh_MAXnewcentrum 5
+
+/*-<a href="qh-user_r.htm#TOC"
+ >--------------------------------</a><a name="COPLANARratio">-</a>
+
+ qh_COPLANARratio
+ for 3-d+ merging, qh.MINvisible is n*premerge_centrum
+
+ notes:
+ for non-merging, it's DISTround
+*/
+#define qh_COPLANARratio 3
+
+/*-<a href="qh-user_r.htm#TOC"
+ >--------------------------------</a><a name="DISToutside">-</a>
+
+ qh_DISToutside
+ When is a point clearly outside of a facet?
+ Stops search in qh_findbestnew or qh_partitionall
+ qh_findbest uses qh.MINoutside since since it is only called if no merges.
+
+ notes:
+ 'Qf' always searches for best facet
+ if !qh.MERGING, same as qh.MINoutside.
+ if qh_USEfindbestnew, increase value since neighboring facets may be ill-behaved
+ [Note: Zdelvertextot occurs normally with interior points]
+ RBOX 1000 s Z1 G1e-13 t1001188774 | QHULL Tv
+ When there is a sharp edge, need to move points to a
+ clearly good facet; otherwise may be lost in another partitioning.
+ if too big then O(n^2) behavior for partitioning in cone
+ if very small then important points not processed
+ Needed in qh_partitionall for
+ RBOX 1000 s Z1 G1e-13 t1001032651 | QHULL Tv
+ Needed in qh_findbestnew for many instances of
+ RBOX 1000 s Z1 G1e-13 t | QHULL Tv
+
+ See:
+ qh_DISToutside -- when is a point clearly outside of a facet
+ qh_SEARCHdist -- when is facet coplanar with the best facet?
+ qh_USEfindbestnew -- when to use qh_findbestnew for qh_partitionpoint()
+*/
+#define qh_DISToutside ((qh_USEfindbestnew ? 2 : 1) * \
+ fmax_((qh->MERGING ? 2 : 1)*qh->MINoutside, qh->max_outside))
+
+/*-<a href="qh-user_r.htm#TOC"
+ >--------------------------------</a><a name="RATIOnearinside">-</a>
+
+ qh_RATIOnearinside
+ ratio of qh.NEARinside to qh.ONEmerge for retaining inside points for
+ qh_check_maxout().
+
+ notes:
+ This is overkill since do not know the correct value.
+ It effects whether 'Qc' reports all coplanar points
+ Not used for 'd' since non-extreme points are coplanar
+*/
+#define qh_RATIOnearinside 5
+
+/*-<a href="qh-user_r.htm#TOC"
+ >--------------------------------</a><a name="SEARCHdist">-</a>
+
+ qh_SEARCHdist
+ When is a facet coplanar with the best facet?
+ qh_findbesthorizon: all coplanar facets of the best facet need to be searched.
+
+ See:
+ qh_DISToutside -- when is a point clearly outside of a facet
+ qh_SEARCHdist -- when is facet coplanar with the best facet?
+ qh_USEfindbestnew -- when to use qh_findbestnew for qh_partitionpoint()
+*/
+#define qh_SEARCHdist ((qh_USEfindbestnew ? 2 : 1) * \
+ (qh->max_outside + 2 * qh->DISTround + fmax_( qh->MINvisible, qh->MAXcoplanar)));
+
+/*-<a href="qh-user_r.htm#TOC"
+ >--------------------------------</a><a name="USEfindbestnew">-</a>
+
+ qh_USEfindbestnew
+ Always use qh_findbestnew for qh_partitionpoint, otherwise use
+ qh_findbestnew if merged new facet or sharpnewfacets.
+
+ See:
+ qh_DISToutside -- when is a point clearly outside of a facet
+ qh_SEARCHdist -- when is facet coplanar with the best facet?
+ qh_USEfindbestnew -- when to use qh_findbestnew for qh_partitionpoint()
+*/
+#define qh_USEfindbestnew (zzval_(Ztotmerge) > 50)
+
+/*-<a href="qh-user_r.htm#TOC"
+ >--------------------------------</a><a name="WIDEcoplanar">-</a>
+
+ qh_WIDEcoplanar
+ n*MAXcoplanar or n*MINvisible for a WIDEfacet
+
+ if vertex is further than qh.WIDEfacet from the hyperplane
+ then its ridges are not counted in computing the area, and
+ the facet's centrum is frozen.
+
+ notes:
+ qh.WIDEfacet= max(qh.MAXoutside,qh_WIDEcoplanar*qh.MAXcoplanar,
+ qh_WIDEcoplanar * qh.MINvisible);
+*/
+#define qh_WIDEcoplanar 6
+
+/*-<a href="qh-user_r.htm#TOC"
+ >--------------------------------</a><a name="WIDEduplicate">-</a>
+
+ qh_WIDEduplicate
+ Merge ratio for errexit from qh_forcedmerges due to duplicate ridge
+ Override with option Q12 no-wide-duplicate
+
+ Notes:
+ Merging a duplicate ridge can lead to very wide facets.
+ A future release of qhull will avoid duplicate ridges by removing duplicate sub-ridges from the horizon
+*/
+#define qh_WIDEduplicate 100
+
+/*-<a href="qh-user_r.htm#TOC"
+ >--------------------------------</a><a name="MAXnarrow">-</a>
+
+ qh_MAXnarrow
+ max. cosine in initial hull that sets qh.NARROWhull
+
+ notes:
+ If qh.NARROWhull, the initial partition does not make
+ coplanar points. If narrow, a coplanar point can be
+ coplanar to two facets of opposite orientations and
+ distant from the exact convex hull.
+
+ Conservative estimate. Don't actually see problems until it is -1.0
+*/
+#define qh_MAXnarrow -0.99999999
+
+/*-<a href="qh-user_r.htm#TOC"
+ >--------------------------------</a><a name="WARNnarrow">-</a>
+
+ qh_WARNnarrow
+ max. cosine in initial hull to warn about qh.NARROWhull
+
+ notes:
+ this is a conservative estimate.
+ Don't actually see problems until it is -1.0. See qh-impre.htm
+*/
+#define qh_WARNnarrow -0.999999999999999
+
+/*-<a href="qh-user_r.htm#TOC"
+ >--------------------------------</a><a name="ZEROdelaunay">-</a>
+
+ qh_ZEROdelaunay
+ a zero Delaunay facet occurs for input sites coplanar with their convex hull
+ the last normal coefficient of a zero Delaunay facet is within
+ qh_ZEROdelaunay * qh.ANGLEround of 0
+
+ notes:
+ qh_ZEROdelaunay does not allow for joggled input ('QJ').
+
+ You can avoid zero Delaunay facets by surrounding the input with a box.
+
+ Use option 'PDk:-n' to explicitly define zero Delaunay facets
+ k= dimension of input sites (e.g., 3 for 3-d Delaunay triangulation)
+ n= the cutoff for zero Delaunay facets (e.g., 'PD3:-1e-12')
+*/
+#define qh_ZEROdelaunay 2
+
+/*============================================================*/
+/*============= Microsoft DevStudio ==========================*/
+/*============================================================*/
+
+/*
+ Finding Memory Leaks Using the CRT Library
+ https://msdn.microsoft.com/en-us/library/x98tx3cf(v=vs.100).aspx
+
+ Reports enabled in qh_lib_check for Debug window and stderr
+
+ From 2005=>msvcr80d, 2010=>msvcr100d, 2012=>msvcr110d
+
+ Watch: {,,msvcr80d.dll}_crtBreakAlloc Value from {n} in the leak report
+ _CrtSetBreakAlloc(689); // qh_lib_check() [global_r.c]
+
+ Examples
+ http://free-cad.sourceforge.net/SrcDocu/d2/d7f/MemDebug_8cpp_source.html
+ https://github.com/illlust/Game/blob/master/library/MemoryLeak.cpp
+*/
+#if 0 /* off (0) by default for QHULL_CRTDBG */
+#define QHULL_CRTDBG
+#endif
+
+#if defined(_MSC_VER) && defined(_DEBUG) && defined(QHULL_CRTDBG)
+#define _CRTDBG_MAP_ALLOC
+#include <stdlib.h>
+#include <crtdbg.h>
+#endif
+
+#endif /* qh_DEFuser */
+
+
+
diff --git a/xs/src/qhull/src/libqhull_r/usermem_r.c b/xs/src/qhull/src/libqhull_r/usermem_r.c
new file mode 100644
index 000000000..3297b0318
--- /dev/null
+++ b/xs/src/qhull/src/libqhull_r/usermem_r.c
@@ -0,0 +1,94 @@
+/*<html><pre> -<a href="qh-user_r.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ usermem_r.c
+ qh_exit(), qh_free(), and qh_malloc()
+
+ See README.txt.
+
+ If you redefine one of these functions you must redefine all of them.
+ If you recompile and load this file, then usermem.o will not be loaded
+ from qhull.a or qhull.lib
+
+ See libqhull_r.h for data structures, macros, and user-callable functions.
+ See user_r.c for qhull-related, redefinable functions
+ see user_r.h for user-definable constants
+ See userprintf_r.c for qh_fprintf and userprintf_rbox_r.c for qh_fprintf_rbox
+
+ Please report any errors that you fix to qhull@qhull.org
+*/
+
+#include "libqhull_r.h"
+
+#include <stdarg.h>
+#include <stdlib.h>
+
+/*-<a href="qh-user_r.htm#TOC"
+ >-------------------------------</a><a name="qh_exit">-</a>
+
+ qh_exit( exitcode )
+ exit program
+
+ notes:
+ qh_exit() is called when qh_errexit() and longjmp() are not available.
+
+ This is the only use of exit() in Qhull
+ To replace qh_exit with 'throw', see libqhullcpp/usermem_r-cpp.cpp
+*/
+void qh_exit(int exitcode) {
+ exit(exitcode);
+} /* exit */
+
+/*-<a href="qh-user_r.htm#TOC"
+ >-------------------------------</a><a name="qh_fprintf_stderr">-</a>
+
+ qh_fprintf_stderr( msgcode, format, list of args )
+ fprintf to stderr with msgcode (non-zero)
+
+ notes:
+ qh_fprintf_stderr() is called when qh->ferr is not defined, usually due to an initialization error
+
+ It is typically followed by qh_errexit().
+
+ Redefine this function to avoid using stderr
+
+ Use qh_fprintf [userprintf_r.c] for normal printing
+*/
+void qh_fprintf_stderr(int msgcode, const char *fmt, ... ) {
+ va_list args;
+
+ va_start(args, fmt);
+ if(msgcode)
+ fprintf(stderr, "QH%.4d ", msgcode);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+} /* fprintf_stderr */
+
+/*-<a href="qh-user_r.htm#TOC"
+>-------------------------------</a><a name="qh_free">-</a>
+
+ qh_free(qhT *qh, mem )
+ free memory
+
+ notes:
+ same as free()
+ No calls to qh_errexit()
+*/
+void qh_free(void *mem) {
+ free(mem);
+} /* free */
+
+/*-<a href="qh-user_r.htm#TOC"
+ >-------------------------------</a><a name="qh_malloc">-</a>
+
+ qh_malloc( mem )
+ allocate memory
+
+ notes:
+ same as malloc()
+*/
+void *qh_malloc(size_t size) {
+ return malloc(size);
+} /* malloc */
+
+
diff --git a/xs/src/qhull/src/libqhull_r/userprintf_r.c b/xs/src/qhull/src/libqhull_r/userprintf_r.c
new file mode 100644
index 000000000..6004491a1
--- /dev/null
+++ b/xs/src/qhull/src/libqhull_r/userprintf_r.c
@@ -0,0 +1,65 @@
+/*<html><pre> -<a href="qh-user_r.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ userprintf_r.c
+ qh_fprintf()
+
+ see README.txt see COPYING.txt for copyright information.
+
+ If you recompile and load this file, then userprintf_r.o will not be loaded
+ from qhull.a or qhull.lib
+
+ See libqhull_r.h for data structures, macros, and user-callable functions.
+ See user_r.c for qhull-related, redefinable functions
+ see user_r.h for user-definable constants
+ See usermem_r.c for qh_exit(), qh_free(), and qh_malloc()
+ see Qhull.cpp and RboxPoints.cpp for examples.
+
+ Please report any errors that you fix to qhull@qhull.org
+*/
+
+#include "libqhull_r.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/*-<a href="qh-user_r.htm#TOC"
+ >-------------------------------</a><a name="qh_fprintf">-</a>
+
+ qh_fprintf(qh, fp, msgcode, format, list of args )
+ print arguments to *fp according to format
+ Use qh_fprintf_rbox() for rboxlib_r.c
+
+ notes:
+ same as fprintf()
+ fgets() is not trapped like fprintf()
+ exit qh_fprintf via qh_errexit()
+ may be called for errors in qh_initstatistics and qh_meminit
+*/
+
+void qh_fprintf(qhT *qh, FILE *fp, int msgcode, const char *fmt, ... ) {
+ va_list args;
+
+ if (!fp) {
+ if(!qh){
+ qh_fprintf_stderr(6241, "userprintf_r.c: fp and qh not defined for qh_fprintf '%s'", fmt);
+ qh_exit(qhmem_ERRqhull); /* can not use qh_errexit() */
+ }
+ /* could use qh->qhmem.ferr, but probably better to be cautious */
+ qh_fprintf_stderr(6232, "Qhull internal error (userprintf_r.c): fp is 0. Wrong qh_fprintf called.\n");
+ qh_errexit(qh, 6232, NULL, NULL);
+ }
+ va_start(args, fmt);
+ if (qh && qh->ANNOTATEoutput) {
+ fprintf(fp, "[QH%.4d]", msgcode);
+ }else if (msgcode >= MSG_ERROR && msgcode < MSG_STDERR ) {
+ fprintf(fp, "QH%.4d ", msgcode);
+ }
+ vfprintf(fp, fmt, args);
+ va_end(args);
+
+ /* Place debugging traps here. Use with option 'Tn' */
+
+} /* qh_fprintf */
+
diff --git a/xs/src/qhull/src/libqhull_r/userprintf_rbox_r.c b/xs/src/qhull/src/libqhull_r/userprintf_rbox_r.c
new file mode 100644
index 000000000..1e721a22a
--- /dev/null
+++ b/xs/src/qhull/src/libqhull_r/userprintf_rbox_r.c
@@ -0,0 +1,53 @@
+/*<html><pre> -<a href="qh-user_r.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ userprintf_rbox_r.c
+ qh_fprintf_rbox()
+
+ see README.txt see COPYING.txt for copyright information.
+
+ If you recompile and load this file, then userprintf_rbox_r.o will not be loaded
+ from qhull.a or qhull.lib
+
+ See libqhull_r.h for data structures, macros, and user-callable functions.
+ See user_r.c for qhull-related, redefinable functions
+ see user_r.h for user-definable constants
+ See usermem_r.c for qh_exit(), qh_free(), and qh_malloc()
+ see Qhull.cpp and RboxPoints.cpp for examples.
+
+ Please report any errors that you fix to qhull@qhull.org
+*/
+
+#include "libqhull_r.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/*-<a href="qh-user_r.htm#TOC"
+ >-------------------------------</a><a name="qh_fprintf_rbox">-</a>
+
+ qh_fprintf_rbox(qh, fp, msgcode, format, list of args )
+ print arguments to *fp according to format
+ Use qh_fprintf_rbox() for rboxlib_r.c
+
+ notes:
+ same as fprintf()
+ fgets() is not trapped like fprintf()
+ exit qh_fprintf_rbox via qh_errexit_rbox()
+*/
+
+void qh_fprintf_rbox(qhT *qh, FILE *fp, int msgcode, const char *fmt, ... ) {
+ va_list args;
+
+ if (!fp) {
+ qh_fprintf_stderr(6231, "Qhull internal error (userprintf_rbox_r.c): fp is 0. Wrong qh_fprintf_rbox called.\n");
+ qh_errexit_rbox(qh, 6231);
+ }
+ if (msgcode >= MSG_ERROR && msgcode < MSG_STDERR)
+ fprintf(fp, "QH%.4d ", msgcode);
+ va_start(args, fmt);
+ vfprintf(fp, fmt, args);
+ va_end(args);
+} /* qh_fprintf_rbox */
+
diff --git a/xs/src/qhull/src/libqhullcpp/Coordinates.cpp b/xs/src/qhull/src/libqhullcpp/Coordinates.cpp
new file mode 100644
index 000000000..806b438ab
--- /dev/null
+++ b/xs/src/qhull/src/libqhullcpp/Coordinates.cpp
@@ -0,0 +1,198 @@
+/****************************************************************************
+**
+** Copyright (c) 2009-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/libqhullcpp/Coordinates.cpp#4 $$Change: 2066 $
+** $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
+**
+****************************************************************************/
+
+#include "libqhullcpp/Coordinates.h"
+
+#include "libqhullcpp/functionObjects.h"
+#include "libqhullcpp/QhullError.h"
+
+#include <iostream>
+#include <iterator>
+#include <algorithm>
+
+#ifdef _MSC_VER // Microsoft Visual C++ -- warning level 4
+#endif
+
+namespace orgQhull {
+
+#//! Coordinates -- vector of coordT (normally double)
+
+#//!\name Constructor
+
+#//!\name Element access
+
+// Inefficient without result-value-optimization or implicitly shared object
+Coordinates Coordinates::
+mid(countT idx, countT length) const
+{
+ countT newLength= length;
+ if(length<0 || idx+length > count()){
+ newLength= count()-idx;
+ }
+ Coordinates result;
+ if(newLength>0){
+ std::copy(begin()+idx, begin()+(idx+newLength), std::back_inserter(result));
+ }
+ return result;
+}//mid
+
+coordT Coordinates::
+value(countT idx, const coordT &defaultValue) const
+{
+ return ((idx < 0 || idx >= count()) ? defaultValue : (*this)[idx]);
+}//value
+
+#//!\name GetSet
+
+Coordinates Coordinates::
+operator+(const Coordinates &other) const
+{
+ Coordinates result(*this);
+ std::copy(other.begin(), other.end(), std::back_inserter(result));
+ return result;
+}//operator+
+
+Coordinates & Coordinates::
+operator+=(const Coordinates &other)
+{
+ if(&other==this){
+ Coordinates clone(other);
+ std::copy(clone.begin(), clone.end(), std::back_inserter(*this));
+ }else{
+ std::copy(other.begin(), other.end(), std::back_inserter(*this));
+ }
+ return *this;
+}//operator+=
+
+#//!\name Read-write
+
+void Coordinates::
+append(int pointDimension, coordT *c)
+{
+ if(c){
+ coordT *p= c;
+ for(int i= 0; i<pointDimension; ++i){
+ coordinate_array.push_back(*p++);
+ }
+ }
+}//append dim coordT
+
+coordT Coordinates::
+takeAt(countT idx)
+{
+ coordT c= at(idx);
+ erase(begin()+idx);
+ return c;
+}//takeAt
+
+coordT Coordinates::
+takeLast()
+{
+ coordT c= last();
+ removeLast();
+ return c;
+}//takeLast
+
+void Coordinates::
+swap(countT idx, countT other)
+{
+ coordT c= at(idx);
+ at(idx)= at(other);
+ at(other)= c;
+}//swap
+
+#//!\name Search
+
+bool Coordinates::
+contains(const coordT &t) const
+{
+ CoordinatesIterator i(*this);
+ return i.findNext(t);
+}//contains
+
+countT Coordinates::
+count(const coordT &t) const
+{
+ CoordinatesIterator i(*this);
+ countT result= 0;
+ while(i.findNext(t)){
+ ++result;
+ }
+ return result;
+}//count
+
+countT Coordinates::
+indexOf(const coordT &t, countT from) const
+{
+ if(from<0){
+ from += count();
+ if(from<0){
+ from= 0;
+ }
+ }
+ if(from<count()){
+ const_iterator i= begin()+from;
+ while(i!=constEnd()){
+ if(*i==t){
+ return (static_cast<countT>(i-begin())); // WARN64 coordinate index
+ }
+ ++i;
+ }
+ }
+ return -1;
+}//indexOf
+
+countT Coordinates::
+lastIndexOf(const coordT &t, countT from) const
+{
+ if(from<0){
+ from += count();
+ }else if(from>=count()){
+ from= count()-1;
+ }
+ if(from>=0){
+ const_iterator i= begin()+from+1;
+ while(i-- != constBegin()){
+ if(*i==t){
+ return (static_cast<countT>(i-begin())); // WARN64 coordinate index
+ }
+ }
+ }
+ return -1;
+}//lastIndexOf
+
+void Coordinates::
+removeAll(const coordT &t)
+{
+ MutableCoordinatesIterator i(*this);
+ while(i.findNext(t)){
+ i.remove();
+ }
+}//removeAll
+
+}//namespace orgQhull
+
+#//!\name Global functions
+
+using std::endl;
+using std::istream;
+using std::ostream;
+using std::string;
+using std::ws;
+using orgQhull::Coordinates;
+
+ostream &
+operator<<(ostream &os, const Coordinates &cs)
+{
+ Coordinates::const_iterator c= cs.begin();
+ for(countT i=cs.count(); i--; ){
+ os << *c++ << " ";
+ }
+ return os;
+}//operator<<
+
diff --git a/xs/src/qhull/src/libqhullcpp/Coordinates.h b/xs/src/qhull/src/libqhullcpp/Coordinates.h
new file mode 100644
index 000000000..df8bd1138
--- /dev/null
+++ b/xs/src/qhull/src/libqhullcpp/Coordinates.h
@@ -0,0 +1,303 @@
+/****************************************************************************
+**
+** Copyright (c) 2009-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/libqhullcpp/Coordinates.h#6 $$Change: 2079 $
+** $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
+**
+****************************************************************************/
+
+#ifndef QHCOORDINATES_H
+#define QHCOORDINATES_H
+
+#include "libqhull_r/qhull_ra.h"
+#include "libqhullcpp/QhullError.h"
+#include "libqhullcpp/QhullIterator.h"
+
+#include <cstddef> // ptrdiff_t, size_t
+#include <ostream>
+// Requires STL vector class. Can use with another vector class such as QList.
+#include <vector>
+
+namespace orgQhull {
+
+#//!\name Defined here
+ //! An std::vector of point coordinates independent of dimension
+ //! Used by PointCoordinates for RboxPoints and by Qhull for feasiblePoint
+ //! A QhullPoint refers to previously allocated coordinates
+ class Coordinates;
+ class MutableCoordinatesIterator;
+
+class Coordinates {
+
+private:
+#//!\name Fields
+ std::vector<coordT> coordinate_array;
+
+public:
+#//!\name Subtypes
+
+ class const_iterator;
+ class iterator;
+ typedef iterator Iterator;
+ typedef const_iterator ConstIterator;
+
+ typedef coordT value_type;
+ typedef const value_type *const_pointer;
+ typedef const value_type & const_reference;
+ typedef value_type * pointer;
+ typedef value_type & reference;
+ typedef ptrdiff_t difference_type;
+ typedef countT size_type;
+
+#//!\name Construct
+ Coordinates() {};
+ explicit Coordinates(const std::vector<coordT> &other) : coordinate_array(other) {}
+ Coordinates(const Coordinates &other) : coordinate_array(other.coordinate_array) {}
+ Coordinates & operator=(const Coordinates &other) { coordinate_array= other.coordinate_array; return *this; }
+ Coordinates & operator=(const std::vector<coordT> &other) { coordinate_array= other; return *this; }
+ ~Coordinates() {}
+
+#//!\name Conversion
+
+#ifndef QHULL_NO_STL
+ std::vector<coordT> toStdVector() const { return coordinate_array; }
+#endif //QHULL_NO_STL
+#ifdef QHULL_USES_QT
+ QList<coordT> toQList() const;
+#endif //QHULL_USES_QT
+
+#//!\name GetSet
+ countT count() const { return static_cast<countT>(size()); }
+ coordT * data() { return isEmpty() ? 0 : &at(0); }
+ const coordT * data() const { return const_cast<const pointT*>(isEmpty() ? 0 : &at(0)); }
+ bool isEmpty() const { return coordinate_array.empty(); }
+ bool operator==(const Coordinates &other) const { return coordinate_array==other.coordinate_array; }
+ bool operator!=(const Coordinates &other) const { return coordinate_array!=other.coordinate_array; }
+ size_t size() const { return coordinate_array.size(); }
+
+#//!\name Element access
+ coordT & at(countT idx) { return coordinate_array.at(idx); }
+ const coordT & at(countT idx) const { return coordinate_array.at(idx); }
+ coordT & back() { return coordinate_array.back(); }
+ const coordT & back() const { return coordinate_array.back(); }
+ coordT & first() { return front(); }
+ const coordT & first() const { return front(); }
+ coordT & front() { return coordinate_array.front(); }
+ const coordT & front() const { return coordinate_array.front(); }
+ coordT & last() { return back(); }
+ const coordT & last() const { return back(); }
+ Coordinates mid(countT idx, countT length= -1) const; //!<\todo countT -1 indicates
+ coordT & operator[](countT idx) { return coordinate_array.operator[](idx); }
+ const coordT & operator[](countT idx) const { return coordinate_array.operator[](idx); }
+ coordT value(countT idx, const coordT &defaultValue) const;
+
+#//!\name Iterator
+ iterator begin() { return iterator(coordinate_array.begin()); }
+ const_iterator begin() const { return const_iterator(coordinate_array.begin()); }
+ const_iterator constBegin() const { return begin(); }
+ const_iterator constEnd() const { return end(); }
+ iterator end() { return iterator(coordinate_array.end()); }
+ const_iterator end() const { return const_iterator(coordinate_array.end()); }
+
+#//!\name GetSet
+ Coordinates operator+(const Coordinates &other) const;
+
+#//!\name Modify
+ void append(int pointDimension, coordT *c);
+ void append(const coordT &c) { push_back(c); }
+ void clear() { coordinate_array.clear(); }
+ iterator erase(iterator idx) { return iterator(coordinate_array.erase(idx.base())); }
+ iterator erase(iterator beginIterator, iterator endIterator) { return iterator(coordinate_array.erase(beginIterator.base(), endIterator.base())); }
+ void insert(countT before, const coordT &c) { insert(begin()+before, c); }
+ iterator insert(iterator before, const coordT &c) { return iterator(coordinate_array.insert(before.base(), c)); }
+ void move(countT from, countT to) { insert(to, takeAt(from)); }
+ Coordinates & operator+=(const Coordinates &other);
+ Coordinates & operator+=(const coordT &c) { append(c); return *this; }
+ Coordinates & operator<<(const Coordinates &other) { return *this += other; }
+ Coordinates & operator<<(const coordT &c) { return *this += c; }
+ void pop_back() { coordinate_array.pop_back(); }
+ void pop_front() { removeFirst(); }
+ void prepend(const coordT &c) { insert(begin(), c); }
+ void push_back(const coordT &c) { coordinate_array.push_back(c); }
+ void push_front(const coordT &c) { insert(begin(), c); }
+ //removeAll below
+ void removeAt(countT idx) { erase(begin()+idx); }
+ void removeFirst() { erase(begin()); }
+ void removeLast() { erase(--end()); }
+ void replace(countT idx, const coordT &c) { (*this)[idx]= c; }
+ void reserve(countT i) { coordinate_array.reserve(i); }
+ void swap(countT idx, countT other);
+ coordT takeAt(countT idx);
+ coordT takeFirst() { return takeAt(0); }
+ coordT takeLast();
+
+#//!\name Search
+ bool contains(const coordT &t) const;
+ countT count(const coordT &t) const;
+ countT indexOf(const coordT &t, countT from = 0) const;
+ countT lastIndexOf(const coordT &t, countT from = -1) const;
+ void removeAll(const coordT &t);
+
+#//!\name Coordinates::iterator -- from QhullPoints, forwarding to coordinate_array
+ // before const_iterator for conversion with comparison operators
+ // Reviewed corelib/tools/qlist.h and corelib/tools/qvector.h w/o QT_STRICT_ITERATORS
+ class iterator {
+
+ private:
+ std::vector<coordT>::iterator i;
+ friend class const_iterator;
+
+ public:
+ typedef std::random_access_iterator_tag iterator_category;
+ typedef coordT value_type;
+ typedef value_type *pointer;
+ typedef value_type &reference;
+ typedef ptrdiff_t difference_type;
+
+ iterator() {}
+ iterator(const iterator &other) { i= other.i; }
+ explicit iterator(const std::vector<coordT>::iterator &vi) { i= vi; }
+ iterator & operator=(const iterator &other) { i= other.i; return *this; }
+ std::vector<coordT>::iterator &base() { return i; }
+ coordT & operator*() const { return *i; }
+ // No operator->() when the base type is double
+ coordT & operator[](countT idx) const { return i[idx]; }
+
+ bool operator==(const iterator &other) const { return i==other.i; }
+ bool operator!=(const iterator &other) const { return i!=other.i; }
+ bool operator<(const iterator &other) const { return i<other.i; }
+ bool operator<=(const iterator &other) const { return i<=other.i; }
+ bool operator>(const iterator &other) const { return i>other.i; }
+ bool operator>=(const iterator &other) const { return i>=other.i; }
+ // reinterpret_cast to break circular dependency
+ bool operator==(const Coordinates::const_iterator &other) const { return *this==reinterpret_cast<const iterator &>(other); }
+ bool operator!=(const Coordinates::const_iterator &other) const { return *this!=reinterpret_cast<const iterator &>(other); }
+ bool operator<(const Coordinates::const_iterator &other) const { return *this<reinterpret_cast<const iterator &>(other); }
+ bool operator<=(const Coordinates::const_iterator &other) const { return *this<=reinterpret_cast<const iterator &>(other); }
+ bool operator>(const Coordinates::const_iterator &other) const { return *this>reinterpret_cast<const iterator &>(other); }
+ bool operator>=(const Coordinates::const_iterator &other) const { return *this>=reinterpret_cast<const iterator &>(other); }
+
+ iterator & operator++() { ++i; return *this; }
+ iterator operator++(int) { return iterator(i++); }
+ iterator & operator--() { --i; return *this; }
+ iterator operator--(int) { return iterator(i--); }
+ iterator & operator+=(countT idx) { i += idx; return *this; }
+ iterator & operator-=(countT idx) { i -= idx; return *this; }
+ iterator operator+(countT idx) const { return iterator(i+idx); }
+ iterator operator-(countT idx) const { return iterator(i-idx); }
+ difference_type operator-(iterator other) const { return i-other.i; }
+ };//Coordinates::iterator
+
+#//!\name Coordinates::const_iterator
+ class const_iterator {
+
+ private:
+ std::vector<coordT>::const_iterator i;
+
+ public:
+ typedef std::random_access_iterator_tag iterator_category;
+ typedef coordT value_type;
+ typedef const value_type *pointer;
+ typedef const value_type &reference;
+ typedef ptrdiff_t difference_type;
+
+ const_iterator() {}
+ const_iterator(const const_iterator &other) { i= other.i; }
+ const_iterator(const iterator &o) : i(o.i) {}
+ explicit const_iterator(const std::vector<coordT>::const_iterator &vi) { i= vi; }
+ const_iterator &operator=(const const_iterator &other) { i= other.i; return *this; }
+ const coordT & operator*() const { return *i; }
+ // No operator->() when the base type is double
+ const coordT & operator[](countT idx) const { return i[idx]; }
+
+ bool operator==(const const_iterator &other) const { return i==other.i; }
+ bool operator!=(const const_iterator &other) const { return i!=other.i; }
+ bool operator<(const const_iterator &other) const { return i<other.i; }
+ bool operator<=(const const_iterator &other) const { return i<=other.i; }
+ bool operator>(const const_iterator &other) const { return i>other.i; }
+ bool operator>=(const const_iterator &other) const { return i>=other.i; }
+
+ const_iterator & operator++() { ++i; return *this; }
+ const_iterator operator++(int) { return const_iterator(i++); }
+ const_iterator & operator--() { --i; return *this; }
+ const_iterator operator--(int) { return const_iterator(i--); }
+ const_iterator & operator+=(countT idx) { i += idx; return *this; }
+ const_iterator & operator-=(countT idx) { i -= idx; return *this; }
+ const_iterator operator+(countT idx) const { return const_iterator(i+idx); }
+ const_iterator operator-(countT idx) const { return const_iterator(i-idx); }
+ difference_type operator-(const_iterator other) const { return i-other.i; }
+ };//Coordinates::const_iterator
+
+};//Coordinates
+
+//class CoordinatesIterator
+//QHULL_DECLARE_SEQUENTIAL_ITERATOR(Coordinates, coordT)
+
+class CoordinatesIterator
+{
+ typedef Coordinates::const_iterator const_iterator;
+
+private:
+ const Coordinates * c;
+ const_iterator i;
+
+public:
+ CoordinatesIterator(const Coordinates &container): c(&container), i(c->constBegin()) {}
+ CoordinatesIterator &operator=(const Coordinates &container) { c= &container; i= c->constBegin(); return *this; }
+ ~CoordinatesIterator() {}
+
+ bool findNext(const coordT &t) { while (i != c->constEnd()) if(*i++ == t){ return true;} return false; }
+ bool findPrevious(const coordT &t) { while (i != c->constBegin())if (*(--i) == t){ return true;} return false; }
+ bool hasNext() const { return i != c->constEnd(); }
+ bool hasPrevious() const { return i != c->constBegin(); }
+ const coordT & next() { return *i++; }
+ const coordT & previous() { return *--i; }
+ const coordT & peekNext() const { return *i; }
+ const coordT & peekPrevious() const { const_iterator p= i; return *--p; }
+ void toFront() { i= c->constBegin(); }
+ void toBack() { i= c->constEnd(); }
+};//CoordinatesIterator
+
+//class MutableCoordinatesIterator
+//QHULL_DECLARE_MUTABLE_SEQUENTIAL_ITERATOR(Coordinates, coordT)
+class MutableCoordinatesIterator
+{
+ typedef Coordinates::iterator iterator;
+ typedef Coordinates::const_iterator const_iterator;
+
+private:
+ Coordinates * c;
+ iterator i;
+ iterator n;
+ bool item_exists() const { return const_iterator(n) != c->constEnd(); }
+
+public:
+ MutableCoordinatesIterator(Coordinates &container) : c(&container) { i= c->begin(); n= c->end(); }
+ MutableCoordinatesIterator &operator=(Coordinates &container) { c= &container; i= c->begin(); n= c->end(); return *this; }
+ ~MutableCoordinatesIterator() {}
+
+ bool findNext(const coordT &t) { while(c->constEnd()!=const_iterator(n= i)){ if(*i++==t){ return true;}} return false; }
+ bool findPrevious(const coordT &t) { while(c->constBegin()!=const_iterator(i)){ if(*(n= --i)== t){ return true;}} n= c->end(); return false; }
+ bool hasNext() const { return (c->constEnd()!=const_iterator(i)); }
+ bool hasPrevious() const { return (c->constBegin()!=const_iterator(i)); }
+ void insert(const coordT &t) { n= i= c->insert(i, t); ++i; }
+ coordT & next() { n= i++; return *n; }
+ coordT & peekNext() const { return *i; }
+ coordT & peekPrevious() const { iterator p= i; return *--p; }
+ coordT & previous() { n= --i; return *n; }
+ void remove() { if(c->constEnd()!=const_iterator(n)){ i= c->erase(n); n= c->end();} }
+ void setValue(const coordT &t) const { if(c->constEnd()!=const_iterator(n)){ *n= t;} }
+ void toFront() { i= c->begin(); n= c->end(); }
+ void toBack() { i= c->end(); n= i; }
+ coordT & value() { QHULL_ASSERT(item_exists()); return *n; }
+ const coordT & value() const { QHULL_ASSERT(item_exists()); return *n; }
+};//MutableCoordinatesIterator
+
+
+}//namespace orgQhull
+
+#//!\name Global
+
+std::ostream &operator<<(std::ostream &os, const orgQhull::Coordinates &c);
+
+#endif // QHCOORDINATES_H
diff --git a/xs/src/qhull/src/libqhullcpp/PointCoordinates.cpp b/xs/src/qhull/src/libqhullcpp/PointCoordinates.cpp
new file mode 100644
index 000000000..a5b71e901
--- /dev/null
+++ b/xs/src/qhull/src/libqhullcpp/PointCoordinates.cpp
@@ -0,0 +1,348 @@
+/****************************************************************************
+**
+** Copyright (c) 2009-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/libqhullcpp/PointCoordinates.cpp#3 $$Change: 2066 $
+** $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
+**
+****************************************************************************/
+
+#include "libqhullcpp/PointCoordinates.h"
+
+#include "libqhullcpp/QhullError.h"
+#include "libqhullcpp/QhullPoint.h"
+
+#include <iterator>
+#include <iostream>
+
+using std::istream;
+using std::string;
+using std::ws;
+
+#ifdef _MSC_VER // Microsoft Visual C++ -- warning level 4
+#pragma warning( disable : 4996) // function was declared deprecated(strcpy, localtime, etc.)
+#endif
+
+namespace orgQhull {
+
+#//! PointCoordinates -- vector of PointCoordinates
+
+#//!\name Constructors
+
+PointCoordinates::
+PointCoordinates()
+: QhullPoints()
+, point_coordinates()
+, describe_points()
+{
+}
+
+PointCoordinates::
+PointCoordinates(const std::string &aComment)
+: QhullPoints()
+, point_coordinates()
+, describe_points(aComment)
+{
+}
+
+PointCoordinates::
+PointCoordinates(int pointDimension, const std::string &aComment)
+: QhullPoints()
+, point_coordinates()
+, describe_points(aComment)
+{
+ setDimension(pointDimension);
+}
+
+//! Qhull and QhullQh constructors are the same
+PointCoordinates::
+PointCoordinates(const Qhull &q)
+: QhullPoints(q)
+, point_coordinates()
+, describe_points()
+{
+}
+
+PointCoordinates::
+PointCoordinates(const Qhull &q, const std::string &aComment)
+: QhullPoints(q)
+, point_coordinates()
+, describe_points(aComment)
+{
+}
+
+PointCoordinates::
+PointCoordinates(const Qhull &q, int pointDimension, const std::string &aComment)
+: QhullPoints(q)
+, point_coordinates()
+, describe_points(aComment)
+{
+ setDimension(pointDimension);
+}
+
+PointCoordinates::
+PointCoordinates(const Qhull &q, int pointDimension, const std::string &aComment, countT coordinatesCount, const coordT *c)
+: QhullPoints(q)
+, point_coordinates()
+, describe_points(aComment)
+{
+ setDimension(pointDimension);
+ append(coordinatesCount, c);
+}
+
+PointCoordinates::
+PointCoordinates(QhullQh *qqh)
+: QhullPoints(qqh)
+, point_coordinates()
+, describe_points()
+{
+}
+
+PointCoordinates::
+PointCoordinates(QhullQh *qqh, const std::string &aComment)
+: QhullPoints(qqh)
+, point_coordinates()
+, describe_points(aComment)
+{
+}
+
+PointCoordinates::
+PointCoordinates(QhullQh *qqh, int pointDimension, const std::string &aComment)
+: QhullPoints(qqh)
+, point_coordinates()
+, describe_points(aComment)
+{
+ setDimension(pointDimension);
+}
+
+PointCoordinates::
+PointCoordinates(QhullQh *qqh, int pointDimension, const std::string &aComment, countT coordinatesCount, const coordT *c)
+: QhullPoints(qqh)
+, point_coordinates()
+, describe_points(aComment)
+{
+ setDimension(pointDimension);
+ append(coordinatesCount, c);
+}
+
+PointCoordinates::
+PointCoordinates(const PointCoordinates &other)
+: QhullPoints(other)
+, point_coordinates(other.point_coordinates)
+, describe_points(other.describe_points)
+{
+ makeValid(); // Update point_first and point_end
+}
+
+PointCoordinates & PointCoordinates::
+operator=(const PointCoordinates &other)
+{
+ QhullPoints::operator=(other);
+ point_coordinates= other.point_coordinates;
+ describe_points= other.describe_points;
+ makeValid(); // Update point_first and point_end
+ return *this;
+}//operator=
+
+PointCoordinates::
+~PointCoordinates()
+{ }
+
+#//!\name GetSet
+
+void PointCoordinates::
+checkValid() const
+{
+ if(getCoordinates().data()!=data()
+ || getCoordinates().count()!=coordinateCount()){
+ throw QhullError(10060, "Qhull error: first point (%x) is not PointCoordinates.data() or count (%d) is not PointCoordinates.count (%d)", coordinateCount(), getCoordinates().count(), 0.0, data());
+ }
+}//checkValid
+
+void PointCoordinates::
+setDimension(int i)
+{
+ if(i<0){
+ throw QhullError(10062, "Qhull error: can not set PointCoordinates dimension to %d", i);
+ }
+ int currentDimension=QhullPoints::dimension();
+ if(currentDimension!=0 && i!=currentDimension){
+ throw QhullError(10063, "Qhull error: can not change PointCoordinates dimension (from %d to %d)", currentDimension, i);
+ }
+ QhullPoints::setDimension(i);
+}//setDimension
+
+#//!\name Foreach
+
+Coordinates::ConstIterator PointCoordinates::
+beginCoordinates(countT pointIndex) const
+{
+ return point_coordinates.begin()+indexOffset(pointIndex);
+}
+
+Coordinates::Iterator PointCoordinates::
+beginCoordinates(countT pointIndex)
+{
+ return point_coordinates.begin()+indexOffset(pointIndex);
+}
+
+#//!\name Methods
+
+void PointCoordinates::
+append(countT coordinatesCount, const coordT *c)
+{
+ if(coordinatesCount<=0){
+ return;
+ }
+ if(includesCoordinates(c)){
+ throw QhullError(10065, "Qhull error: can not append a subset of PointCoordinates to itself. The coordinates for point %d may move.", indexOf(c, QhullError::NOthrow));
+ }
+ reserveCoordinates(coordinatesCount);
+ std::copy(c, c+coordinatesCount, std::back_inserter(point_coordinates));
+ makeValid();
+}//append coordT
+
+void PointCoordinates::
+append(const PointCoordinates &other)
+{
+ setDimension(other.dimension());
+ append(other.coordinateCount(), other.data());
+}//append PointCoordinates
+
+void PointCoordinates::
+append(const QhullPoint &p)
+{
+ setDimension(p.dimension());
+ append(p.dimension(), p.coordinates());
+}//append QhullPoint
+
+void PointCoordinates::
+appendComment(const std::string &s){
+ if(char c= s[0] && describe_points.empty()){
+ if(c=='-' || isdigit(c)){
+ throw QhullError(10028, "Qhull argument error: comments can not start with a number or minus, %s", 0, 0, 0.0, s.c_str());
+ }
+ }
+ describe_points += s;
+}//appendComment
+
+//! Read PointCoordinates from istream. First two numbers are dimension and count. A non-digit starts a rboxCommand.
+//! Overwrites describe_points. See qh_readpoints [io.c]
+void PointCoordinates::
+appendPoints(istream &in)
+{
+ int inDimension;
+ countT inCount;
+ in >> ws >> inDimension >> ws;
+ if(!in.good()){
+ in.clear();
+ string remainder;
+ getline(in, remainder);
+ throw QhullError(10005, "Qhull error: input did not start with dimension or count -- %s", 0, 0, 0, remainder.c_str());
+ }
+ char c= (char)in.peek();
+ if(c!='-' && !isdigit(c)){ // Comments start with a non-digit
+ getline(in, describe_points);
+ in >> ws;
+ }
+ in >> inCount >> ws;
+ if(!in.good()){
+ in.clear();
+ string remainder;
+ getline(in, remainder);
+ throw QhullError(10009, "Qhull error: input did not start with dimension and count -- %d %s", inDimension, 0, 0, remainder.c_str());
+ }
+ c= (char)in.peek();
+ if(c!='-' && !isdigit(c)){ // Comments start with a non-digit
+ getline(in, describe_points);
+ in >> ws;
+ }
+ if(inCount<inDimension){ // Count may precede dimension
+ std::swap(inCount, inDimension);
+ }
+ setDimension(inDimension);
+ reserveCoordinates(inCount*inDimension);
+ countT coordinatesCount= 0;
+ while(!in.eof()){
+ realT p;
+ in >> p >> ws;
+ if(in.fail()){
+ in.clear();
+ string remainder;
+ getline(in, remainder);
+ throw QhullError(10008, "Qhull error: failed to read coordinate %d of point %d\n %s", coordinatesCount % inDimension, coordinatesCount/inDimension, 0, remainder.c_str());
+ }else{
+ point_coordinates.push_back(p);
+ coordinatesCount++;
+ }
+ }
+ if(coordinatesCount != inCount*inDimension){
+ if(coordinatesCount%inDimension==0){
+ throw QhullError(10006, "Qhull error: expected %d %d-d PointCoordinates but read %i PointCoordinates", int(inCount), inDimension, 0.0, int(coordinatesCount/inDimension));
+ }else{
+ throw QhullError(10012, "Qhull error: expected %d %d-d PointCoordinates but read %i PointCoordinates plus %f extra coordinates", inCount, inDimension, float(coordinatesCount%inDimension), coordinatesCount/inDimension);
+ }
+ }
+ makeValid();
+}//appendPoints istream
+
+PointCoordinates PointCoordinates::
+operator+(const PointCoordinates &other) const
+{
+ PointCoordinates pc= *this;
+ pc << other;
+ return pc;
+}//operator+
+
+void PointCoordinates::
+reserveCoordinates(countT newCoordinates)
+{
+ // vector::reserve is not const
+ point_coordinates.reserve((countT)point_coordinates.size()+newCoordinates); // WARN64
+ makeValid();
+}//reserveCoordinates
+
+#//!\name Helpers
+
+countT PointCoordinates::
+indexOffset(countT i) const {
+ countT n= i*dimension();
+ countT coordinatesCount= point_coordinates.count();
+ if(i<0 || n>coordinatesCount){
+ throw QhullError(10061, "Qhull error: point_coordinates is too short (%d) for point %d", coordinatesCount, i);
+ }
+ return n;
+}
+
+}//namespace orgQhull
+
+#//!\name Global functions
+
+using std::endl;
+using std::ostream;
+
+using orgQhull::Coordinates;
+using orgQhull::PointCoordinates;
+
+ostream&
+operator<<(ostream &os, const PointCoordinates &p)
+{
+ p.checkValid();
+ countT count= p.count();
+ int dimension= p.dimension();
+ string comment= p.comment();
+ if(comment.empty()){
+ os << dimension << endl;
+ }else{
+ os << dimension << " " << comment << endl;
+ }
+ os << count << endl;
+ Coordinates::ConstIterator c= p.beginCoordinates();
+ for(countT i=0; i<count; i++){
+ for(int j=0; j<dimension; j++){
+ os << *c++ << " ";
+ }
+ os << endl;
+ }
+ return os;
+}//operator<<
+
diff --git a/xs/src/qhull/src/libqhullcpp/PointCoordinates.h b/xs/src/qhull/src/libqhullcpp/PointCoordinates.h
new file mode 100644
index 000000000..4c63cdfca
--- /dev/null
+++ b/xs/src/qhull/src/libqhullcpp/PointCoordinates.h
@@ -0,0 +1,161 @@
+/****************************************************************************
+**
+** Copyright (c) 2009-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/libqhullcpp/PointCoordinates.h#4 $$Change: 2079 $
+** $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
+**
+****************************************************************************/
+
+#ifndef QHPOINTCOORDINATES_H
+#define QHPOINTCOORDINATES_H
+
+#include "libqhull_r/qhull_ra.h"
+#include "libqhullcpp/QhullPoints.h"
+#include "libqhullcpp/Coordinates.h"
+
+#include <ostream>
+#include <string>
+
+#ifndef QHULL_NO_STL
+#include <vector>
+#endif
+
+namespace orgQhull {
+
+#//!\name Defined here
+ //! QhullPoints with Coordinates and description
+ //! Inherited by RboxPoints
+ class PointCoordinates;
+
+class PointCoordinates : public QhullPoints {
+
+private:
+#//!\name Fields
+ Coordinates point_coordinates; //! std::vector of point coordinates
+ //! may have extraCoordinates()
+ std::string describe_points; //! Comment describing PointCoordinates
+
+public:
+#//!\name Construct
+ //! QhullPoint, PointCoordinates, and QhullPoints have similar constructors
+ //! If Qhull/QhullQh is not initialized, then dimension()==0 PointCoordinates();
+ PointCoordinates();
+ explicit PointCoordinates(const std::string &aComment);
+ PointCoordinates(int pointDimension, const std::string &aComment);
+ //! Qhull/QhullQh used for dimension() and QhullPoint equality
+ explicit PointCoordinates(const Qhull &q);
+ PointCoordinates(const Qhull &q, const std::string &aComment);
+ PointCoordinates(const Qhull &q, int pointDimension, const std::string &aComment);
+ PointCoordinates(const Qhull &q, int pointDimension, const std::string &aComment, countT coordinatesCount, const coordT *c); // may be invalid
+ //! Use append() and appendPoints() for Coordinates and vector<coordT>
+ explicit PointCoordinates(QhullQh *qqh);
+ PointCoordinates(QhullQh *qqh, const std::string &aComment);
+ PointCoordinates(QhullQh *qqh, int pointDimension, const std::string &aComment);
+ PointCoordinates(QhullQh *qqh, int pointDimension, const std::string &aComment, countT coordinatesCount, const coordT *c); // may be invalid
+ //! Use append() and appendPoints() for Coordinates and vector<coordT>
+ PointCoordinates(const PointCoordinates &other);
+ PointCoordinates & operator=(const PointCoordinates &other);
+ ~PointCoordinates();
+
+#//!\name Convert
+ //! QhullPoints coordinates, constData, data, count, size
+#ifndef QHULL_NO_STL
+ void append(const std::vector<coordT> &otherCoordinates) { if(!otherCoordinates.empty()){ append((int)otherCoordinates.size(), &otherCoordinates[0]); } }
+ std::vector<coordT> toStdVector() const { return point_coordinates.toStdVector(); }
+#endif //QHULL_NO_STL
+#ifdef QHULL_USES_QT
+ void append(const QList<coordT> &pointCoordinates) { if(!pointCoordinates.isEmpty()){ append(pointCoordinates.count(), &pointCoordinates[0]); } }
+ QList<coordT> toQList() const { return point_coordinates.toQList(); }
+#endif //QHULL_USES_QT
+
+#//!\name GetSet
+ //! See QhullPoints for coordinates, coordinateCount, dimension, empty, isEmpty, ==, !=
+ void checkValid() const;
+ std::string comment() const { return describe_points; }
+ void makeValid() { defineAs(point_coordinates.count(), point_coordinates.data()); }
+ const Coordinates & getCoordinates() const { return point_coordinates; }
+ void setComment(const std::string &s) { describe_points= s; }
+ void setDimension(int i);
+
+private:
+ //! disable QhullPoints.defineAs()
+ void defineAs(countT coordinatesCount, coordT *c) { QhullPoints::defineAs(coordinatesCount, c); }
+public:
+
+#//!\name ElementAccess
+ //! See QhullPoints for at, back, first, front, last, mid, [], value
+
+#//!\name Foreach
+ //! See QhullPoints for begin, constBegin, end
+ Coordinates::ConstIterator beginCoordinates() const { return point_coordinates.begin(); }
+ Coordinates::Iterator beginCoordinates() { return point_coordinates.begin(); }
+ Coordinates::ConstIterator beginCoordinates(countT pointIndex) const;
+ Coordinates::Iterator beginCoordinates(countT pointIndex);
+ Coordinates::ConstIterator endCoordinates() const { return point_coordinates.end(); }
+ Coordinates::Iterator endCoordinates() { return point_coordinates.end(); }
+
+#//!\name Search
+ //! See QhullPoints for contains, count, indexOf, lastIndexOf
+
+#//!\name GetSet
+ PointCoordinates operator+(const PointCoordinates &other) const;
+
+#//!\name Modify
+ //FIXUP QH11001: Add clear() and other modify operators from Coordinates.h. Include QhullPoint::operator=()
+ void append(countT coordinatesCount, const coordT *c); //! Dimension previously defined
+ void append(const coordT &c) { append(1, &c); } //! Dimension previously defined
+ void append(const QhullPoint &p);
+ //! See convert for std::vector and QList
+ void append(const Coordinates &c) { append(c.count(), c.data()); }
+ void append(const PointCoordinates &other);
+ void appendComment(const std::string &s);
+ void appendPoints(std::istream &in);
+ PointCoordinates & operator+=(const PointCoordinates &other) { append(other); return *this; }
+ PointCoordinates & operator+=(const coordT &c) { append(c); return *this; }
+ PointCoordinates & operator+=(const QhullPoint &p) { append(p); return *this; }
+ PointCoordinates & operator<<(const PointCoordinates &other) { return *this += other; }
+ PointCoordinates & operator<<(const coordT &c) { return *this += c; }
+ PointCoordinates & operator<<(const QhullPoint &p) { return *this += p; }
+ // reserve() is non-const
+ void reserveCoordinates(countT newCoordinates);
+
+#//!\name Helpers
+private:
+ int indexOffset(int i) const;
+
+};//PointCoordinates
+
+// No references to QhullPoint. Prevents use of QHULL_DECLARE_SEQUENTIAL_ITERATOR(PointCoordinates, QhullPoint)
+class PointCoordinatesIterator
+{
+ typedef PointCoordinates::const_iterator const_iterator;
+
+private:
+ const PointCoordinates *c;
+ const_iterator i;
+
+public:
+ PointCoordinatesIterator(const PointCoordinates &container) : c(&container), i(c->constBegin()) {}
+ PointCoordinatesIterator &operator=(const PointCoordinates &container) { c = &container; i = c->constBegin(); return *this; }
+
+ void toFront() { i = c->constBegin(); }
+ void toBack() { i = c->constEnd(); }
+ bool hasNext() const { return i != c->constEnd(); }
+ const QhullPoint next() { return *i++; }
+ const QhullPoint peekNext() const { return *i; }
+ bool hasPrevious() const { return i != c->constBegin(); }
+ const QhullPoint previous() { return *--i; }
+ const QhullPoint peekPrevious() const { const_iterator p = i; return *--p; }
+ bool findNext(const QhullPoint &t) { while(i != c->constEnd()){ if (*i++ == t) return true;} return false; }
+ bool findPrevious(const QhullPoint &t) { while(i != c->constBegin()){ if (*(--i) == t) return true;} return false; }
+};//CoordinatesIterator
+
+// FIXUP QH11002: Add MutablePointCoordinatesIterator after adding modify operators
+\
+}//namespace orgQhull
+
+#//!\name Global
+
+std::ostream & operator<<(std::ostream &os, const orgQhull::PointCoordinates &p);
+
+#endif // QHPOINTCOORDINATES_H
diff --git a/xs/src/qhull/src/libqhullcpp/Qhull.cpp b/xs/src/qhull/src/libqhullcpp/Qhull.cpp
new file mode 100644
index 000000000..7124a15cd
--- /dev/null
+++ b/xs/src/qhull/src/libqhullcpp/Qhull.cpp
@@ -0,0 +1,352 @@
+/****************************************************************************
+**
+** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/libqhullcpp/Qhull.cpp#4 $$Change: 2078 $
+** $DateTime: 2016/02/07 16:53:56 $$Author: bbarber $
+**
+****************************************************************************/
+
+#//! Qhull -- invoke qhull from C++
+#//! Compile libqhull_r and Qhull together due to use of setjmp/longjmp()
+
+#include "libqhullcpp/Qhull.h"
+
+#include "libqhullcpp/QhullError.h"
+#include "libqhullcpp/RboxPoints.h"
+#include "libqhullcpp/QhullQh.h"
+#include "libqhullcpp/QhullFacet.h"
+#include "libqhullcpp/QhullFacetList.h"
+
+#include <iostream>
+
+using std::cerr;
+using std::string;
+using std::vector;
+using std::ostream;
+
+#ifdef _MSC_VER // Microsoft Visual C++ -- warning level 4
+#pragma warning( disable : 4611) // interaction between '_setjmp' and C++ object destruction is non-portable
+#pragma warning( disable : 4996) // function was declared deprecated(strcpy, localtime, etc.)
+#endif
+
+namespace orgQhull {
+
+#//!\name Global variables
+
+const char s_unsupported_options[]=" Fd TI ";
+const char 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 ";
+
+#//!\name Constructor, destructor, etc.
+Qhull::
+Qhull()
+: qh_qh(0)
+, origin_point()
+, run_called(false)
+, feasible_point()
+{
+ allocateQhullQh();
+}//Qhull
+
+//! Invokes Qhull on rboxPoints
+//! Same as runQhull()
+//! For rbox commands, see http://www.qhull.org/html/rbox.htm or html/rbox.htm
+//! For qhull commands, see http://www.qhull.org/html/qhull.htm or html/qhull.htm
+Qhull::
+Qhull(const RboxPoints &rboxPoints, const char *qhullCommand2)
+: qh_qh(0)
+, origin_point()
+, run_called(false)
+, feasible_point()
+{
+ allocateQhullQh();
+ runQhull(rboxPoints, qhullCommand2);
+}//Qhull rbox
+
+//! Invokes Qhull on a set of input points
+//! Same as runQhull()
+//! For qhull commands, see http://www.qhull.org/html/qhull.htm or html/qhull.htm
+Qhull::
+Qhull(const char *inputComment2, int pointDimension, int pointCount, const realT *pointCoordinates, const char *qhullCommand2)
+: qh_qh(0)
+, origin_point()
+, run_called(false)
+, feasible_point()
+{
+ allocateQhullQh();
+ runQhull(inputComment2, pointDimension, pointCount, pointCoordinates, qhullCommand2);
+}//Qhull points
+
+void Qhull::
+allocateQhullQh()
+{
+ QHULL_LIB_CHECK /* Check for compatible library */
+
+ qh_qh= new QhullQh;
+ void *p= qh_qh;
+ void *p2= static_cast<qhT *>(qh_qh);
+ char *s= static_cast<char *>(p);
+ char *s2= static_cast<char *>(p2);
+ if(s!=s2){
+ throw QhullError(10074, "Qhull error: QhullQh at a different address than base type QhT (%d bytes). Please report compiler to qhull.org", int(s2-s));
+ }
+}//allocateQhullQh
+
+Qhull::
+~Qhull() throw()
+{
+ // Except for cerr, does not throw errors
+ if(qh_qh->hasQhullMessage()){
+ cerr<< "\nQhull output at end\n"; //FIXUP QH11005: where should error and log messages go on ~Qhull?
+ cerr<< qh_qh->qhullMessage();
+ qh_qh->clearQhullMessage();
+ }
+ delete qh_qh;
+ qh_qh= 0;
+}//~Qhull
+
+#//!\name GetSet
+
+void Qhull::
+checkIfQhullInitialized()
+{
+ if(!initialized()){ // qh_initqhull_buffers() not called
+ throw QhullError(10023, "Qhull error: checkIfQhullInitialized failed. Call runQhull() first.");
+ }
+}//checkIfQhullInitialized
+
+//! Return feasiblePoint for halfspace intersection
+//! If called before runQhull(), then it returns the value from setFeasiblePoint. qh.feasible_string overrides this value if it is defined.
+Coordinates Qhull::
+feasiblePoint() const
+{
+ Coordinates result;
+ if(qh_qh->feasible_point){
+ result.append(qh_qh->hull_dim, qh_qh->feasible_point);
+ }else{
+ result= feasible_point;
+ }
+ return result;
+}//feasiblePoint
+
+//! Return origin point for qh.input_dim
+QhullPoint Qhull::
+inputOrigin()
+{
+ QhullPoint result= origin();
+ result.setDimension(qh_qh->input_dim);
+ return result;
+}//inputOrigin
+
+#//!\name GetValue
+
+double Qhull::
+area(){
+ checkIfQhullInitialized();
+ if(!qh_qh->hasAreaVolume){
+ QH_TRY_(qh_qh){ // no object creation -- destructors skipped on longjmp()
+ qh_getarea(qh_qh, qh_qh->facet_list);
+ }
+ qh_qh->NOerrexit= true;
+ qh_qh->maybeThrowQhullMessage(QH_TRY_status);
+ }
+ return qh_qh->totarea;
+}//area
+
+double Qhull::
+volume(){
+ checkIfQhullInitialized();
+ if(!qh_qh->hasAreaVolume){
+ QH_TRY_(qh_qh){ // no object creation -- destructors skipped on longjmp()
+ qh_getarea(qh_qh, qh_qh->facet_list);
+ }
+ qh_qh->NOerrexit= true;
+ qh_qh->maybeThrowQhullMessage(QH_TRY_status);
+ }
+ return qh_qh->totvol;
+}//volume
+
+#//!\name Foreach
+
+//! Define QhullVertex::neighborFacets().
+//! Automatically called if merging facets or computing the Voronoi diagram.
+//! Noop if called multiple times.
+void Qhull::
+defineVertexNeighborFacets(){
+ checkIfQhullInitialized();
+ if(!qh_qh->hasAreaVolume){
+ QH_TRY_(qh_qh){ // no object creation -- destructors skipped on longjmp()
+ qh_vertexneighbors(qh_qh);
+ }
+ qh_qh->NOerrexit= true;
+ qh_qh->maybeThrowQhullMessage(QH_TRY_status);
+ }
+}//defineVertexNeighborFacets
+
+QhullFacetList Qhull::
+facetList() const{
+ return QhullFacetList(beginFacet(), endFacet());
+}//facetList
+
+QhullPoints Qhull::
+points() const
+{
+ return QhullPoints(qh_qh, qh_qh->hull_dim, qh_qh->num_points*qh_qh->hull_dim, qh_qh->first_point);
+}//points
+
+QhullPointSet Qhull::
+otherPoints() const
+{
+ return QhullPointSet(qh_qh, qh_qh->other_points);
+}//otherPoints
+
+//! Return vertices of the convex hull.
+QhullVertexList Qhull::
+vertexList() const{
+ return QhullVertexList(beginVertex(), endVertex());
+}//vertexList
+
+#//!\name Methods
+
+void Qhull::
+outputQhull()
+{
+ checkIfQhullInitialized();
+ QH_TRY_(qh_qh){ // no object creation -- destructors skipped on longjmp()
+ qh_produce_output2(qh_qh);
+ }
+ qh_qh->NOerrexit= true;
+ qh_qh->maybeThrowQhullMessage(QH_TRY_status);
+}//outputQhull
+
+void Qhull::
+outputQhull(const char *outputflags)
+{
+ checkIfQhullInitialized();
+ string cmd(" "); // qh_checkflags skips first word
+ cmd += outputflags;
+ char *command= const_cast<char*>(cmd.c_str());
+ QH_TRY_(qh_qh){ // no object creation -- destructors skipped on longjmp()
+ qh_clear_outputflags(qh_qh);
+ char *s = qh_qh->qhull_command + strlen(qh_qh->qhull_command) + 1; //space
+ strncat(qh_qh->qhull_command, command, sizeof(qh_qh->qhull_command)-strlen(qh_qh->qhull_command)-1);
+ qh_checkflags(qh_qh, command, const_cast<char *>(s_not_output_options));
+ qh_initflags(qh_qh, s);
+ qh_initqhull_outputflags(qh_qh);
+ if(qh_qh->KEEPminArea < REALmax/2
+ || (0 != qh_qh->KEEParea + qh_qh->KEEPmerge + qh_qh->GOODvertex
+ + qh_qh->GOODthreshold + qh_qh->GOODpoint + qh_qh->SPLITthresholds)){
+ facetT *facet;
+ qh_qh->ONLYgood= False;
+ FORALLfacet_(qh_qh->facet_list) {
+ facet->good= True;
+ }
+ qh_prepare_output(qh_qh);
+ }
+ qh_produce_output2(qh_qh);
+ if(qh_qh->VERIFYoutput && !qh_qh->STOPpoint && !qh_qh->STOPcone){
+ qh_check_points(qh_qh);
+ }
+ }
+ qh_qh->NOerrexit= true;
+ qh_qh->maybeThrowQhullMessage(QH_TRY_status);
+}//outputQhull
+
+//! For qhull commands, see http://www.qhull.org/html/qhull.htm or html/qhull.htm
+void Qhull::
+runQhull(const RboxPoints &rboxPoints, const char *qhullCommand2)
+{
+ runQhull(rboxPoints.comment().c_str(), rboxPoints.dimension(), rboxPoints.count(), &*rboxPoints.coordinates(), qhullCommand2);
+}//runQhull, RboxPoints
+
+//! pointCoordinates is a array of points, input sites ('d' or 'v'), or halfspaces with offset last ('H')
+//! Derived from qh_new_qhull [user.c]
+//! For rbox commands, see http://www.qhull.org/html/rbox.htm or html/rbox.htm
+//! For qhull commands, see http://www.qhull.org/html/qhull.htm or html/qhull.htm
+void Qhull::
+runQhull(const char *inputComment, int pointDimension, int pointCount, const realT *pointCoordinates, const char *qhullCommand)
+{
+ /* gcc may issue a "might be clobbered" warning for pointDimension and pointCoordinates [-Wclobbered].
+ These parameters are not referenced after a longjmp() and hence not clobbered.
+ See http://stackoverflow.com/questions/7721854/what-sense-do-these-clobbered-variable-warnings-make */
+ if(run_called){
+ throw QhullError(10027, "Qhull error: runQhull called twice. Only one call allowed.");
+ }
+ run_called= true;
+ string s("qhull ");
+ s += qhullCommand;
+ char *command= const_cast<char*>(s.c_str());
+ /************* Expansion of QH_TRY_ for debugging
+ int QH_TRY_status;
+ if(qh_qh->NOerrexit){
+ qh_qh->NOerrexit= False;
+ QH_TRY_status= setjmp(qh_qh->errexit);
+ }else{
+ QH_TRY_status= QH_TRY_ERROR;
+ }
+ if(!QH_TRY_status){
+ *************/
+ QH_TRY_(qh_qh){ // no object creation -- destructors are skipped on longjmp()
+ qh_checkflags(qh_qh, command, const_cast<char *>(s_unsupported_options));
+ qh_initflags(qh_qh, command);
+ *qh_qh->rbox_command= '\0';
+ strncat( qh_qh->rbox_command, inputComment, sizeof(qh_qh->rbox_command)-1);
+ if(qh_qh->DELAUNAY){
+ qh_qh->PROJECTdelaunay= True; // qh_init_B() calls qh_projectinput()
+ }
+ pointT *newPoints= const_cast<pointT*>(pointCoordinates);
+ int newDimension= pointDimension;
+ int newIsMalloc= False;
+ if(qh_qh->HALFspace){
+ --newDimension;
+ initializeFeasiblePoint(newDimension);
+ newPoints= qh_sethalfspace_all(qh_qh, pointDimension, pointCount, newPoints, qh_qh->feasible_point);
+ newIsMalloc= True;
+ }
+ qh_init_B(qh_qh, newPoints, pointCount, newDimension, newIsMalloc);
+ qh_qhull(qh_qh);
+ qh_check_output(qh_qh);
+ qh_prepare_output(qh_qh);
+ if(qh_qh->VERIFYoutput && !qh_qh->STOPpoint && !qh_qh->STOPcone){
+ qh_check_points(qh_qh);
+ }
+ }
+ qh_qh->NOerrexit= true;
+ for(int k= qh_qh->hull_dim; k--; ){ // Do not move into QH_TRY block. It may throw an error
+ origin_point << 0.0;
+ }
+ qh_qh->maybeThrowQhullMessage(QH_TRY_status);
+}//runQhull
+
+#//!\name Helpers -- be careful of allocating C++ objects due to setjmp/longjmp() error handling by qh_... routines
+
+//! initialize qh.feasible_point for half-space intersection
+//! Sets from qh.feasible_string if available, otherwise from Qhull::feasible_point
+//! called only once from runQhull(), otherwise it leaks memory (the same as qh_setFeasible)
+void Qhull::
+initializeFeasiblePoint(int hulldim)
+{
+ if(qh_qh->feasible_string){
+ qh_setfeasible(qh_qh, hulldim);
+ }else{
+ if(feasible_point.isEmpty()){
+ qh_fprintf(qh_qh, qh_qh->ferr, 6209, "qhull error: missing feasible point for halfspace intersection. Use option 'Hn,n' or Qhull::setFeasiblePoint before runQhull()\n");
+ qh_errexit(qh_qh, qh_ERRmem, NULL, NULL);
+ }
+ if(feasible_point.size()!=(size_t)hulldim){
+ qh_fprintf(qh_qh, qh_qh->ferr, 6210, "qhull error: dimension of feasiblePoint should be %d. It is %u", hulldim, feasible_point.size());
+ qh_errexit(qh_qh, qh_ERRmem, NULL, NULL);
+ }
+ if (!(qh_qh->feasible_point= (coordT*)qh_malloc(hulldim * sizeof(coordT)))) {
+ qh_fprintf(qh_qh, qh_qh->ferr, 6202, "qhull error: insufficient memory for feasible point\n");
+ qh_errexit(qh_qh, qh_ERRmem, NULL, NULL);
+ }
+ coordT *t= qh_qh->feasible_point;
+ // No qh_... routines after here -- longjmp() ignores destructor
+ for(Coordinates::ConstIterator p=feasible_point.begin(); p<feasible_point.end(); p++){
+ *t++= *p;
+ }
+ }
+}//initializeFeasiblePoint
+
+}//namespace orgQhull
+
diff --git a/xs/src/qhull/src/libqhullcpp/Qhull.h b/xs/src/qhull/src/libqhullcpp/Qhull.h
new file mode 100644
index 000000000..e3ad9d8e1
--- /dev/null
+++ b/xs/src/qhull/src/libqhullcpp/Qhull.h
@@ -0,0 +1,132 @@
+/****************************************************************************
+**
+** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/libqhullcpp/Qhull.h#3 $$Change: 2066 $
+** $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
+**
+****************************************************************************/
+
+#ifndef QHULLCPP_H
+#define QHULLCPP_H
+
+#include "libqhullcpp/QhullPoint.h"
+#include "libqhullcpp/QhullVertex.h"
+#include "libqhullcpp/QhullFacet.h"
+
+namespace orgQhull {
+
+/***
+ Compile qhullcpp and libqhull with the same compiler. setjmp() and longjmp() must be the same.
+
+ #define QHULL_NO_STL
+ Do not supply conversions to STL
+ Coordinates.h requires <vector>. It could be rewritten for another vector class such as QList
+ #define QHULL_USES_QT
+ Supply conversions to QT
+ qhulltest requires QT. It is defined in RoadTest.h
+
+ #define QHULL_ASSERT
+ Defined by QhullError.h
+ It invokes assert()
+*/
+
+#//!\name Used here
+ class QhullFacetList;
+ class QhullPoints;
+ class QhullQh;
+ class RboxPoints;
+
+#//!\name Defined here
+ class Qhull;
+
+//! Interface to Qhull from C++
+class Qhull {
+
+private:
+#//!\name Members and friends
+ QhullQh * qh_qh; //! qhT for this instance
+ Coordinates origin_point; //! origin for qh_qh->hull_dim. Set by runQhull()
+ bool run_called; //! True at start of runQhull. Errors if call again.
+ Coordinates feasible_point; //! feasible point for half-space intersection (alternative to qh.feasible_string for qh.feasible_point)
+
+public:
+#//!\name Constructors
+ Qhull(); //!< call runQhull() next
+ Qhull(const RboxPoints &rboxPoints, const char *qhullCommand2);
+ Qhull(const char *inputComment2, int pointDimension, int pointCount, const realT *pointCoordinates, const char *qhullCommand2);
+ ~Qhull() throw();
+private: //! Disable copy constructor and assignment. Qhull owns QhullQh.
+ Qhull(const Qhull &);
+ Qhull & operator=(const Qhull &);
+
+private:
+ void allocateQhullQh();
+
+public:
+
+#//!\name GetSet
+ void checkIfQhullInitialized();
+ int dimension() const { return qh_qh->input_dim; } //!< Dimension of input and result
+ void disableOutputStream() { qh_qh->disableOutputStream(); }
+ void enableOutputStream() { qh_qh->enableOutputStream(); }
+ countT facetCount() const { return qh_qh->num_facets; }
+ Coordinates feasiblePoint() const;
+ int hullDimension() const { return qh_qh->hull_dim; } //!< Dimension of the computed hull
+ bool hasOutputStream() const { return qh_qh->hasOutputStream(); }
+ bool initialized() const { return (qh_qh->hull_dim>0); }
+ const char * inputComment() const { return qh_qh->rbox_command; }
+ QhullPoint inputOrigin();
+ //! non-const due to QhullPoint
+ QhullPoint origin() { QHULL_ASSERT(initialized()); return QhullPoint(qh_qh, origin_point.data()); }
+ QhullQh * qh() const { return qh_qh; };
+ const char * qhullCommand() const { return qh_qh->qhull_command; }
+ const char * rboxCommand() const { return qh_qh->rbox_command; }
+ int rotateRandom() const { return qh_qh->ROTATErandom; } //!< Return QRn for repeating QR0 runs
+ void setFeasiblePoint(const Coordinates &c) { feasible_point= c; } //!< Sets qh.feasible_point via initializeFeasiblePoint
+ countT vertexCount() const { return qh_qh->num_vertices; }
+
+#//!\name Delegated to QhullQh
+ double angleEpsilon() const { return qh_qh->angleEpsilon(); } //!< Epsilon for hyperplane angle equality
+ void appendQhullMessage(const std::string &s) { qh_qh->appendQhullMessage(s); }
+ void clearQhullMessage() { qh_qh->clearQhullMessage(); }
+ double distanceEpsilon() const { return qh_qh->distanceEpsilon(); } //!< Epsilon for distance to hyperplane
+ double factorEpsilon() const { return qh_qh->factorEpsilon(); } //!< Factor for angleEpsilon and distanceEpsilon
+ std::string qhullMessage() const { return qh_qh->qhullMessage(); }
+ bool hasQhullMessage() const { return qh_qh->hasQhullMessage(); }
+ int qhullStatus() const { return qh_qh->qhullStatus(); }
+ void setErrorStream(std::ostream *os) { qh_qh->setErrorStream(os); }
+ void setFactorEpsilon(double a) { qh_qh->setFactorEpsilon(a); }
+ void setOutputStream(std::ostream *os) { qh_qh->setOutputStream(os); }
+
+#//!\name ForEach
+ QhullFacet beginFacet() const { return QhullFacet(qh_qh, qh_qh->facet_list); }
+ QhullVertex beginVertex() const { return QhullVertex(qh_qh, qh_qh->vertex_list); }
+ void defineVertexNeighborFacets(); //!< Automatically called if merging facets or Voronoi diagram
+ QhullFacet endFacet() const { return QhullFacet(qh_qh, qh_qh->facet_tail); }
+ QhullVertex endVertex() const { return QhullVertex(qh_qh, qh_qh->vertex_tail); }
+ QhullFacetList facetList() const;
+ QhullFacet firstFacet() const { return beginFacet(); }
+ QhullVertex firstVertex() const { return beginVertex(); }
+ QhullPoints points() const;
+ QhullPointSet otherPoints() const;
+ //! Same as points().coordinates()
+ coordT * pointCoordinateBegin() const { return qh_qh->first_point; }
+ coordT * pointCoordinateEnd() const { return qh_qh->first_point + qh_qh->num_points*qh_qh->hull_dim; }
+ QhullVertexList vertexList() const;
+
+#//!\name Methods
+ double area();
+ void outputQhull();
+ void outputQhull(const char * outputflags);
+ void runQhull(const RboxPoints &rboxPoints, const char *qhullCommand2);
+ void runQhull(const char *inputComment2, int pointDimension, int pointCount, const realT *pointCoordinates, const char *qhullCommand2);
+ double volume();
+
+#//!\name Helpers
+private:
+ void initializeFeasiblePoint(int hulldim);
+};//Qhull
+
+}//namespace orgQhull
+
+#endif // QHULLCPP_H
diff --git a/xs/src/qhull/src/libqhullcpp/QhullError.h b/xs/src/qhull/src/libqhullcpp/QhullError.h
new file mode 100644
index 000000000..08d50aa0f
--- /dev/null
+++ b/xs/src/qhull/src/libqhullcpp/QhullError.h
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/libqhullcpp/QhullError.h#2 $$Change: 2066 $
+** $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
+**
+****************************************************************************/
+
+#ifndef QHULLERROR_H
+#define QHULLERROR_H
+
+#include "libqhullcpp/RoadError.h"
+// No dependencies on libqhull
+
+#ifndef QHULL_ASSERT
+#define QHULL_ASSERT assert
+#include <assert.h>
+#endif
+
+namespace orgQhull {
+
+#//!\name Defined here
+ //! QhullError -- std::exception class for Qhull
+ class QhullError;
+
+class QhullError : public RoadError {
+
+public:
+#//!\name Constants
+ enum {
+ QHULLfirstError= 10000, //MSG_QHULL_ERROR in Qhull's user.h
+ QHULLlastError= 10078,
+ NOthrow= 1 //! For flag to indexOf()
+ };
+
+#//!\name Constructors
+ // default constructors
+ QhullError() : RoadError() {};
+ QhullError(const QhullError &other) : RoadError(other) {}
+ QhullError(int code, const std::string &message) : RoadError(code, message) {};
+ QhullError(int code, const char *fmt) : RoadError(code, fmt) {};
+ QhullError(int code, const char *fmt, int d) : RoadError(code, fmt, d) {};
+ QhullError(int code, const char *fmt, int d, int d2) : RoadError(code, fmt, d, d2) {};
+ QhullError(int code, const char *fmt, int d, int d2, float f) : RoadError(code, fmt, d, d2, f) {};
+ QhullError(int code, const char *fmt, int d, int d2, float f, const char *s) : RoadError(code, fmt, d, d2, f, s) {};
+ QhullError(int code, const char *fmt, int d, int d2, float f, const void *x) : RoadError(code, fmt, d, d2, f, x) {};
+ QhullError(int code, const char *fmt, int d, int d2, float f, int i) : RoadError(code, fmt, d, d2, f, i) {};
+ QhullError(int code, const char *fmt, int d, int d2, float f, long long i) : RoadError(code, fmt, d, d2, f, i) {};
+ QhullError(int code, const char *fmt, int d, int d2, float f, double e) : RoadError(code, fmt, d, d2, f, e) {};
+ QhullError &operator=(const QhullError &other) { this->RoadError::operator=(other); return *this; }
+ ~QhullError() throw() {}
+
+};//class QhullError
+
+
+}//namespace orgQhull
+
+#//!\name Global
+
+inline std::ostream &operator<<(std::ostream &os, const orgQhull::QhullError &e) { return os << e.what(); }
+
+#endif // QHULLERROR_H
diff --git a/xs/src/qhull/src/libqhullcpp/QhullFacet.cpp b/xs/src/qhull/src/libqhullcpp/QhullFacet.cpp
new file mode 100644
index 000000000..40d3828a4
--- /dev/null
+++ b/xs/src/qhull/src/libqhullcpp/QhullFacet.cpp
@@ -0,0 +1,519 @@
+/****************************************************************************
+**
+** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/libqhullcpp/QhullFacet.cpp#3 $$Change: 2066 $
+** $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
+**
+****************************************************************************/
+
+#//! QhullFacet -- Qhull's facet structure, facetT, as a C++ class
+
+#include "libqhullcpp/QhullFacet.h"
+
+#include "libqhullcpp/QhullError.h"
+#include "libqhullcpp/Qhull.h"
+#include "libqhullcpp/QhullSet.h"
+#include "libqhullcpp/QhullPoint.h"
+#include "libqhullcpp/QhullPointSet.h"
+#include "libqhullcpp/QhullRidge.h"
+#include "libqhullcpp/QhullFacetSet.h"
+#include "libqhullcpp/QhullVertex.h"
+
+#include <ostream>
+
+using std::endl;
+using std::ostream;
+
+#ifdef _MSC_VER // Microsoft Visual C++ -- warning level 4
+#pragma warning( disable : 4611) // interaction between '_setjmp' and C++ object destruction is non-portable
+#pragma warning( disable : 4996) // function was declared deprecated(strcpy, localtime, etc.)
+#endif
+
+namespace orgQhull {
+
+#//!\name Class objects
+facetT QhullFacet::
+s_empty_facet= {0,0,0,0,{0},
+ 0,0,0,0,0,
+ 0,0,0,0,0,
+ 0,0,0,0,0,
+ 0,0,0,0,0,
+ 0,0,0,0,0,
+ 0,0,0,0,0,
+ 0,0,0,0};
+
+#//!\name Constructors
+
+QhullFacet::
+QhullFacet(const Qhull &q)
+: qh_facet(&s_empty_facet)
+, qh_qh(q.qh())
+{
+}
+
+QhullFacet::
+QhullFacet(const Qhull &q, facetT *f)
+: qh_facet(f ? f : &s_empty_facet)
+, qh_qh(q.qh())
+{
+}
+
+#//!\name GetSet
+
+//! Return voronoi center or facet centrum. Derived from qh_printcenter [io_r.c]
+//! if printFormat=qh_PRINTtriangles and qh.DELAUNAY, returns centrum of a Delaunay facet
+//! Sets center if needed
+//! Code duplicated for PrintCenter and getCenter
+//! Returns QhullPoint() if none or qh_INFINITE
+QhullPoint QhullFacet::
+getCenter(qh_PRINT printFormat)
+{
+ if(!qh_qh){
+ // returns QhullPoint()
+ }else if(qh_qh->CENTERtype==qh_ASvoronoi){
+ if(!qh_facet->normal || !qh_facet->upperdelaunay || !qh_qh->ATinfinity){
+ if(!qh_facet->center){
+ QH_TRY_(qh_qh){ // no object creation -- destructors skipped on longjmp()
+ qh_facet->center= qh_facetcenter(qh_qh, qh_facet->vertices);
+ }
+ qh_qh->NOerrexit= true;
+ qh_qh->maybeThrowQhullMessage(QH_TRY_status);
+ }
+ return QhullPoint(qh_qh, qh_qh->hull_dim-1, qh_facet->center);
+ }
+ }else if(qh_qh->CENTERtype==qh_AScentrum){
+ volatile int numCoords= qh_qh->hull_dim;
+ if(printFormat==qh_PRINTtriangles && qh_qh->DELAUNAY){
+ numCoords--;
+ }
+ if(!qh_facet->center){
+ QH_TRY_(qh_qh){ // no object creation -- destructors skipped on longjmp()
+ qh_facet->center= qh_getcentrum(qh_qh, getFacetT());
+ }
+ qh_qh->NOerrexit= true;
+ qh_qh->maybeThrowQhullMessage(QH_TRY_status);
+ }
+ return QhullPoint(qh_qh, numCoords, qh_facet->center);
+ }
+ return QhullPoint();
+ }//getCenter
+
+//! Return innerplane clearly below the vertices
+//! from io_r.c[qh_PRINTinner]
+QhullHyperplane QhullFacet::
+innerplane() const{
+ QhullHyperplane h;
+ if(qh_qh){
+ realT inner;
+ // Does not error, TRY_QHULL_ not needed
+ qh_outerinner(qh_qh, const_cast<facetT *>(getFacetT()), NULL, &inner);
+ h= hyperplane();
+ h.setOffset(h.offset()-inner); //inner is negative
+ }
+ return h;
+}//innerplane
+
+//! Return outerplane clearly above all points
+//! from io_r.c[qh_PRINTouter]
+QhullHyperplane QhullFacet::
+outerplane() const{
+ QhullHyperplane h;
+ if(qh_qh){
+ realT outer;
+ // Does not error, TRY_QHULL_ not needed
+ qh_outerinner(qh_qh, const_cast<facetT *>(getFacetT()), &outer, NULL);
+ h= hyperplane();
+ h.setOffset(h.offset()-outer); //outer is positive
+ }
+ return h;
+}//outerplane
+
+//! Set by qh_triangulate for option 'Qt'.
+//! Errors if tricoplanar and facetArea() or qh_getarea() called first.
+QhullFacet QhullFacet::
+tricoplanarOwner() const
+{
+ if(qh_facet->tricoplanar){
+ if(qh_facet->isarea){
+ throw QhullError(10018, "Qhull error: facetArea() or qh_getarea() previously called. triCoplanarOwner() is not available.");
+ }
+ return QhullFacet(qh_qh, qh_facet->f.triowner);
+ }
+ return QhullFacet(qh_qh);
+}//tricoplanarOwner
+
+QhullPoint QhullFacet::
+voronoiVertex()
+{
+ if(qh_qh && qh_qh->CENTERtype!=qh_ASvoronoi){
+ throw QhullError(10052, "Error: QhullFacet.voronoiVertex() requires option 'v' (qh_ASvoronoi)");
+ }
+ return getCenter();
+}//voronoiVertex
+
+#//!\name Value
+
+//! Disables tricoplanarOwner()
+double QhullFacet::
+facetArea()
+{
+ if(qh_qh && !qh_facet->isarea){
+ QH_TRY_(qh_qh){ // no object creation -- destructors skipped on longjmp()
+ qh_facet->f.area= qh_facetarea(qh_qh, qh_facet);
+ qh_facet->isarea= True;
+ }
+ qh_qh->NOerrexit= true;
+ qh_qh->maybeThrowQhullMessage(QH_TRY_status);
+ }
+ return qh_facet->f.area;
+}//facetArea
+
+#//!\name Foreach
+
+QhullPointSet QhullFacet::
+coplanarPoints() const
+{
+ return QhullPointSet(qh_qh, qh_facet->coplanarset);
+}//coplanarPoints
+
+QhullFacetSet QhullFacet::
+neighborFacets() const
+{
+ return QhullFacetSet(qh_qh, qh_facet->neighbors);
+}//neighborFacets
+
+QhullPointSet QhullFacet::
+outsidePoints() const
+{
+ return QhullPointSet(qh_qh, qh_facet->outsideset);
+}//outsidePoints
+
+QhullRidgeSet QhullFacet::
+ridges() const
+{
+ return QhullRidgeSet(qh_qh, qh_facet->ridges);
+}//ridges
+
+QhullVertexSet QhullFacet::
+vertices() const
+{
+ return QhullVertexSet(qh_qh, qh_facet->vertices);
+}//vertices
+
+}//namespace orgQhull
+
+#//!\name operator<<
+
+using std::ostream;
+
+using orgQhull::QhullFacet;
+using orgQhull::QhullFacetSet;
+using orgQhull::QhullPoint;
+using orgQhull::QhullPointSet;
+using orgQhull::QhullRidge;
+using orgQhull::QhullRidgeSet;
+using orgQhull::QhullSetBase;
+using orgQhull::QhullVertexSet;
+
+ostream &
+operator<<(ostream &os, const QhullFacet::PrintFacet &pr)
+{
+ os << pr.message;
+ QhullFacet f= *pr.facet;
+ if(f.getFacetT()==0){ // Special values from set iterator
+ os << " NULLfacet" << endl;
+ return os;
+ }
+ if(f.getFacetT()==qh_MERGEridge){
+ os << " MERGEridge" << endl;
+ return os;
+ }
+ if(f.getFacetT()==qh_DUPLICATEridge){
+ os << " DUPLICATEridge" << endl;
+ return os;
+ }
+ os << f.printHeader();
+ if(!f.ridges().isEmpty()){
+ os << f.printRidges();
+ }
+ return os;
+}//operator<< PrintFacet
+
+//! Print Voronoi center or facet centrum to stream. Same as qh_printcenter [_r.]
+//! Code duplicated for PrintCenter and getCenter
+//! Sets center if needed
+ostream &
+operator<<(ostream &os, const QhullFacet::PrintCenter &pr)
+{
+ facetT *f= pr.facet->getFacetT();
+ if(pr.facet->qh()->CENTERtype!=qh_ASvoronoi && pr.facet->qh()->CENTERtype!=qh_AScentrum){
+ return os;
+ }
+ if (pr.message){
+ os << pr.message;
+ }
+ int numCoords;
+ if(pr.facet->qh()->CENTERtype==qh_ASvoronoi){
+ numCoords= pr.facet->qh()->hull_dim-1;
+ if(!f->normal || !f->upperdelaunay || !pr.facet->qh()->ATinfinity){
+ if(!f->center){
+ f->center= qh_facetcenter(pr.facet->qh(), f->vertices);
+ }
+ for(int k=0; k<numCoords; k++){
+ os << f->center[k] << " "; // FIXUP QH11010 qh_REAL_1
+ }
+ }else{
+ for(int k=0; k<numCoords; k++){
+ os << qh_INFINITE << " "; // FIXUP QH11010 qh_REAL_1
+ }
+ }
+ }else{ // qh CENTERtype==qh_AScentrum
+ numCoords= pr.facet->qh()->hull_dim;
+ if(pr.print_format==qh_PRINTtriangles && pr.facet->qh()->DELAUNAY){
+ numCoords--;
+ }
+ if(!f->center){
+ f->center= qh_getcentrum(pr.facet->qh(), f);
+ }
+ for(int k=0; k<numCoords; k++){
+ os << f->center[k] << " "; // FIXUP QH11010 qh_REAL_1
+ }
+ }
+ if(pr.print_format==qh_PRINTgeom && numCoords==2){
+ os << " 0";
+ }
+ os << endl;
+ return os;
+}//operator<< PrintCenter
+
+//! Print flags for facet to stream. Space prefix. From qh_printfacetheader [io_r.c]
+ostream &
+operator<<(ostream &os, const QhullFacet::PrintFlags &p)
+{
+ const facetT *f= p.facet->getFacetT();
+ if(p.message){
+ os << p.message;
+ }
+
+ os << (p.facet->isTopOrient() ? " top" : " bottom");
+ if(p.facet->isSimplicial()){
+ os << " simplicial";
+ }
+ if(p.facet->isTriCoplanar()){
+ os << " tricoplanar";
+ }
+ if(p.facet->isUpperDelaunay()){
+ os << " upperDelaunay";
+ }
+ if(f->visible){
+ os << " visible";
+ }
+ if(f->newfacet){
+ os << " new";
+ }
+ if(f->tested){
+ os << " tested";
+ }
+ if(!f->good){
+ os << " notG";
+ }
+ if(f->seen){
+ os << " seen";
+ }
+ if(f->coplanar){
+ os << " coplanar";
+ }
+ if(f->mergehorizon){
+ os << " mergehorizon";
+ }
+ if(f->keepcentrum){
+ os << " keepcentrum";
+ }
+ if(f->dupridge){
+ os << " dupridge";
+ }
+ if(f->mergeridge && !f->mergeridge2){
+ os << " mergeridge1";
+ }
+ if(f->mergeridge2){
+ os << " mergeridge2";
+ }
+ if(f->newmerge){
+ os << " newmerge";
+ }
+ if(f->flipped){
+ os << " flipped";
+ }
+ if(f->notfurthest){
+ os << " notfurthest";
+ }
+ if(f->degenerate){
+ os << " degenerate";
+ }
+ if(f->redundant){
+ os << " redundant";
+ }
+ os << endl;
+ return os;
+}//operator<< PrintFlags
+
+//! Print header for facet to stream. Space prefix. From qh_printfacetheader [io_r.c]
+ostream &
+operator<<(ostream &os, const QhullFacet::PrintHeader &pr)
+{
+ QhullFacet facet= *pr.facet;
+ facetT *f= facet.getFacetT();
+ os << "- f" << facet.id() << endl;
+ os << facet.printFlags(" - flags:");
+ if(f->isarea){
+ os << " - area: " << f->f.area << endl; //FIXUP QH11010 2.2g
+ }else if(pr.facet->qh()->NEWfacets && f->visible && f->f.replace){
+ os << " - replacement: f" << f->f.replace->id << endl;
+ }else if(f->newfacet){
+ if(f->f.samecycle && f->f.samecycle != f){
+ os << " - shares same visible/horizon as f" << f->f.samecycle->id << endl;
+ }
+ }else if(f->tricoplanar /* !isarea */){
+ if(f->f.triowner){
+ os << " - owner of normal & centrum is facet f" << f->f.triowner->id << endl;
+ }
+ }else if(f->f.newcycle){
+ os << " - was horizon to f" << f->f.newcycle->id << endl;
+ }
+ if(f->nummerge){
+ os << " - merges: " << f->nummerge << endl;
+ }
+ os << facet.hyperplane().print(" - normal: ", "\n - offset: "); // FIXUP QH11010 %10.7g
+ if(pr.facet->qh()->CENTERtype==qh_ASvoronoi || f->center){
+ os << facet.printCenter(qh_PRINTfacets, " - center: ");
+ }
+#if qh_MAXoutside
+ if(f->maxoutside > pr.facet->qh()->DISTround){
+ os << " - maxoutside: " << f->maxoutside << endl; //FIXUP QH11010 %10.7g
+ }
+#endif
+ QhullPointSet ps= facet.outsidePoints();
+ if(!ps.isEmpty()){
+ QhullPoint furthest= ps.last();
+ if (ps.size() < 6) {
+ os << " - outside set(furthest p" << furthest.id() << "):" << endl;
+ for(QhullPointSet::iterator i=ps.begin(); i!=ps.end(); ++i){
+ QhullPoint p= *i;
+ os << p.print(" ");
+ }
+ }else if(ps.size()<21){
+ os << ps.print(" - outside set:");
+ }else{
+ os << " - outside set: " << ps.size() << " points.";
+ os << furthest.print(" Furthest");
+ }
+#if !qh_COMPUTEfurthest
+ os << " - furthest distance= " << f->furthestdist << endl; //FIXUP QH11010 %2.2g
+#endif
+ }
+ QhullPointSet cs= facet.coplanarPoints();
+ if(!cs.isEmpty()){
+ QhullPoint furthest= cs.last();
+ if (cs.size() < 6) {
+ os << " - coplanar set(furthest p" << furthest.id() << "):" << endl;
+ for(QhullPointSet::iterator i=cs.begin(); i!=cs.end(); ++i){
+ QhullPoint p= *i;
+ os << p.print(" ");
+ }
+ }else if(cs.size()<21){
+ os << cs.print(" - coplanar set:");
+ }else{
+ os << " - coplanar set: " << cs.size() << " points.";
+ os << furthest.print(" Furthest");
+ }
+ // FIXUP QH11027 Can/should zinc_(Zdistio) be called from C++ interface
+ double d= facet.distance(furthest);
+ os << " furthest distance= " << d << endl; //FIXUP QH11010 %2.2g
+ }
+ QhullVertexSet vs= facet.vertices();
+ if(!vs.isEmpty()){
+ os << vs.print(" - vertices:");
+ }
+ QhullFacetSet fs= facet.neighborFacets();
+ fs.selectAll();
+ if(!fs.isEmpty()){
+ os << fs.printIdentifiers(" - neighboring facets:");
+ }
+ return os;
+}//operator<< PrintHeader
+
+
+//! Print ridges of facet to stream. Same as qh_printfacetridges [io_r.c]
+ostream &
+operator<<(ostream &os, const QhullFacet::PrintRidges &pr)
+{
+ const QhullFacet facet= *pr.facet;
+ facetT *f= facet.getFacetT();
+ QhullRidgeSet rs= facet.ridges();
+ if(!rs.isEmpty()){
+ if(f->visible && pr.facet->qh()->NEWfacets){
+ os << " - ridges(ids may be garbage):";
+ for(QhullRidgeSet::iterator i=rs.begin(); i!=rs.end(); ++i){
+ QhullRidge r= *i;
+ os << " r" << r.id();
+ }
+ os << endl;
+ }else{
+ os << " - ridges:" << endl;
+ }
+
+ // Keep track of printed ridges
+ for(QhullRidgeSet::iterator i=rs.begin(); i!=rs.end(); ++i){
+ QhullRidge r= *i;
+ r.getRidgeT()->seen= false;
+ }
+ int ridgeCount= 0;
+ if(facet.dimension()==3){
+ for(QhullRidge r= rs.first(); !r.getRidgeT()->seen; r= r.nextRidge3d(facet)){
+ r.getRidgeT()->seen= true;
+ os << r.print("");
+ ++ridgeCount;
+ if(!r.hasNextRidge3d(facet)){
+ break;
+ }
+ }
+ }else {
+ QhullFacetSet ns(facet.neighborFacets());
+ for(QhullFacetSet::iterator i=ns.begin(); i!=ns.end(); ++i){
+ QhullFacet neighbor= *i;
+ QhullRidgeSet nrs(neighbor.ridges());
+ for(QhullRidgeSet::iterator j=nrs.begin(); j!=nrs.end(); ++j){
+ QhullRidge r= *j;
+ if(r.otherFacet(neighbor)==facet){
+ r.getRidgeT()->seen= true;
+ os << r.print("");
+ ridgeCount++;
+ }
+ }
+ }
+ }
+ if(ridgeCount!=rs.count()){
+ os << " - all ridges:";
+ for(QhullRidgeSet::iterator i=rs.begin(); i!=rs.end(); ++i){
+ QhullRidge r= *i;
+ os << " r" << r.id();
+ }
+ os << endl;
+ }
+ for(QhullRidgeSet::iterator i=rs.begin(); i!=rs.end(); ++i){
+ QhullRidge r= *i;
+ if(!r.getRidgeT()->seen){
+ os << r.print("");
+ }
+ }
+ }
+ return os;
+}//operator<< PrintRidges
+
+// "No conversion" error if defined inline
+ostream &
+operator<<(ostream &os, QhullFacet &f)
+{
+ os << f.print("");
+ return os;
+}//<< QhullFacet
diff --git a/xs/src/qhull/src/libqhullcpp/QhullFacet.h b/xs/src/qhull/src/libqhullcpp/QhullFacet.h
new file mode 100644
index 000000000..ae4f008fd
--- /dev/null
+++ b/xs/src/qhull/src/libqhullcpp/QhullFacet.h
@@ -0,0 +1,151 @@
+/****************************************************************************
+**
+** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/libqhullcpp/QhullFacet.h#4 $$Change: 2079 $
+** $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
+**
+****************************************************************************/
+
+#ifndef QHULLFACET_H
+#define QHULLFACET_H
+
+#include "libqhull_r/qhull_ra.h"
+#include "libqhullcpp/QhullHyperplane.h"
+#include "libqhullcpp/QhullPoint.h"
+#include "libqhullcpp/QhullSet.h"
+#include "libqhullcpp/QhullPointSet.h"
+
+#include <ostream>
+
+namespace orgQhull {
+
+#//!\name Used here
+ class Coordinates;
+ class Qhull;
+ class QhullFacetSet;
+ class QhullRidge;
+ class QhullVertex;
+ class QhullVertexSet;
+
+#//!\name Defined here
+ class QhullFacet;
+ typedef QhullSet<QhullRidge> QhullRidgeSet;
+
+//! A QhullFacet is the C++ equivalent to Qhull's facetT*
+class QhullFacet {
+
+#//!\name Defined here
+public:
+ typedef facetT * base_type; // for QhullVertexSet
+
+private:
+#//!\name Fields -- no additions (QhullFacetSet of facetT*)
+ facetT * qh_facet; //!< Corresponding facetT, may be 0 for corner cases (e.g., *facetSet.end()==0) and tricoplanarOwner()
+ QhullQh * qh_qh; //!< QhullQh/qhT for facetT, may be 0
+
+#//!\name Class objects
+ static facetT s_empty_facet; // needed for shallow copy
+
+public:
+#//!\name Constructors
+ QhullFacet() : qh_facet(&s_empty_facet), qh_qh(0) {}
+ explicit QhullFacet(const Qhull &q);
+ QhullFacet(const Qhull &q, facetT *f);
+ explicit QhullFacet(QhullQh *qqh) : qh_facet(&s_empty_facet), qh_qh(qqh) {}
+ QhullFacet(QhullQh *qqh, facetT *f) : qh_facet(f ? f : &s_empty_facet), qh_qh(qqh) {}
+ // Creates an alias. Does not copy QhullFacet. Needed for return by value and parameter passing
+ QhullFacet(const QhullFacet &other) : qh_facet(other.qh_facet ? other.qh_facet : &s_empty_facet), qh_qh(other.qh_qh) {}
+ // Creates an alias. Does not copy QhullFacet. Needed for vector<QhullFacet>
+ QhullFacet & operator=(const QhullFacet &other) { qh_facet= other.qh_facet ? other.qh_facet : &s_empty_facet; qh_qh= other.qh_qh; return *this; }
+ ~QhullFacet() {}
+
+
+#//!\name GetSet
+ int dimension() const { return (qh_qh ? qh_qh->hull_dim : 0); }
+ QhullPoint getCenter() { return getCenter(qh_PRINTpoints); }
+ QhullPoint getCenter(qh_PRINT printFormat);
+ facetT * getBaseT() const { return getFacetT(); } //!< For QhullSet<QhullFacet>
+ // Do not define facetT(). It conflicts with return type facetT*
+ facetT * getFacetT() const { return qh_facet; }
+ QhullHyperplane hyperplane() const { return QhullHyperplane(qh_qh, dimension(), qh_facet->normal, qh_facet->offset); }
+ countT id() const { return (qh_facet ? qh_facet->id : (int)qh_IDunknown); }
+ QhullHyperplane innerplane() const;
+ bool isValid() const { return qh_qh && qh_facet && qh_facet != &s_empty_facet; }
+ bool isGood() const { return qh_facet && qh_facet->good; }
+ bool isSimplicial() const { return qh_facet && qh_facet->simplicial; }
+ bool isTopOrient() const { return qh_facet && qh_facet->toporient; }
+ bool isTriCoplanar() const { return qh_facet && qh_facet->tricoplanar; }
+ bool isUpperDelaunay() const { return qh_facet && qh_facet->upperdelaunay; }
+ QhullFacet next() const { return QhullFacet(qh_qh, qh_facet->next); }
+ bool operator==(const QhullFacet &other) const { return qh_facet==other.qh_facet; }
+ bool operator!=(const QhullFacet &other) const { return !operator==(other); }
+ QhullHyperplane outerplane() const;
+ QhullFacet previous() const { return QhullFacet(qh_qh, qh_facet->previous); }
+ QhullQh * qh() const { return qh_qh; }
+ QhullFacet tricoplanarOwner() const;
+ QhullPoint voronoiVertex();
+
+#//!\name value
+ //! Undefined if c.size() != dimension()
+ double distance(const Coordinates &c) const { return distance(c.data()); }
+ double distance(const pointT *p) const { return distance(QhullPoint(qh_qh, const_cast<coordT *>(p))); }
+ double distance(const QhullPoint &p) const { return hyperplane().distance(p); }
+ double facetArea();
+
+#//!\name foreach
+ // Can not inline. Otherwise circular reference
+ QhullPointSet coplanarPoints() const;
+ QhullFacetSet neighborFacets() const;
+ QhullPointSet outsidePoints() const;
+ QhullRidgeSet ridges() const;
+ QhullVertexSet vertices() const;
+
+#//!\name IO
+ struct PrintCenter{
+ QhullFacet * facet; // non-const due to facet.center()
+ const char * message;
+ qh_PRINT print_format;
+ PrintCenter(QhullFacet &f, qh_PRINT printFormat, const char * s) : facet(&f), message(s), print_format(printFormat){}
+ };//PrintCenter
+ PrintCenter printCenter(qh_PRINT printFormat, const char *message) { return PrintCenter(*this, printFormat, message); }
+
+ struct PrintFacet{
+ QhullFacet * facet; // non-const due to f->center()
+ const char * message;
+ explicit PrintFacet(QhullFacet &f, const char * s) : facet(&f), message(s) {}
+ };//PrintFacet
+ PrintFacet print(const char *message) { return PrintFacet(*this, message); }
+
+ struct PrintFlags{
+ const QhullFacet *facet;
+ const char * message;
+ PrintFlags(const QhullFacet &f, const char *s) : facet(&f), message(s) {}
+ };//PrintFlags
+ PrintFlags printFlags(const char *message) const { return PrintFlags(*this, message); }
+
+ struct PrintHeader{
+ QhullFacet * facet; // non-const due to f->center()
+ PrintHeader(QhullFacet &f) : facet(&f) {}
+ };//PrintHeader
+ PrintHeader printHeader() { return PrintHeader(*this); }
+
+ struct PrintRidges{
+ const QhullFacet *facet;
+ PrintRidges(QhullFacet &f) : facet(&f) {}
+ };//PrintRidges
+ PrintRidges printRidges() { return PrintRidges(*this); }
+
+};//class QhullFacet
+
+}//namespace orgQhull
+
+#//!\name Global
+
+std::ostream &operator<<(std::ostream &os, const orgQhull::QhullFacet::PrintFacet &pr);
+std::ostream &operator<<(std::ostream &os, const orgQhull::QhullFacet::PrintCenter &pr);
+std::ostream &operator<<(std::ostream &os, const orgQhull::QhullFacet::PrintFlags &pr);
+std::ostream &operator<<(std::ostream &os, const orgQhull::QhullFacet::PrintHeader &pr);
+std::ostream &operator<<(std::ostream &os, const orgQhull::QhullFacet::PrintRidges &pr);
+std::ostream &operator<<(std::ostream &os, orgQhull::QhullFacet &f); // non-const due to qh_getcenter()
+
+#endif // QHULLFACET_H
diff --git a/xs/src/qhull/src/libqhullcpp/QhullFacetList.cpp b/xs/src/qhull/src/libqhullcpp/QhullFacetList.cpp
new file mode 100644
index 000000000..9e6ddfe9e
--- /dev/null
+++ b/xs/src/qhull/src/libqhullcpp/QhullFacetList.cpp
@@ -0,0 +1,174 @@
+/****************************************************************************
+**
+** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/libqhullcpp/QhullFacetList.cpp#3 $$Change: 2066 $
+** $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
+**
+****************************************************************************/
+
+#//! QhullFacetList -- Qhull's linked facets, as a C++ class
+
+#include "libqhullcpp/QhullFacetList.h"
+
+#include "libqhullcpp/QhullFacet.h"
+#include "libqhullcpp/QhullPoint.h"
+#include "libqhullcpp/QhullRidge.h"
+#include "libqhullcpp/QhullVertex.h"
+
+using std::string;
+using std::vector;
+
+#ifdef _MSC_VER // Microsoft Visual C++ -- warning level 4
+#pragma warning( disable : 4611) // interaction between '_setjmp' and C++ object destruction is non-portable
+#pragma warning( disable : 4996) // function was declared deprecated(strcpy, localtime, etc.)
+#endif
+
+namespace orgQhull {
+
+#//!\name Constructors
+
+QhullFacetList::
+QhullFacetList(const Qhull &q, facetT *b, facetT *e )
+: QhullLinkedList<QhullFacet>(QhullFacet(q, b), QhullFacet(q, e))
+, select_all(false)
+{
+}
+
+#//!\name Conversions
+
+// See qt_qhull.cpp for QList conversions
+
+#ifndef QHULL_NO_STL
+std::vector<QhullFacet> QhullFacetList::
+toStdVector() const
+{
+ QhullLinkedListIterator<QhullFacet> i(*this);
+ std::vector<QhullFacet> vs;
+ while(i.hasNext()){
+ QhullFacet f= i.next();
+ if(isSelectAll() || f.isGood()){
+ vs.push_back(f);
+ }
+ }
+ return vs;
+}//toStdVector
+#endif //QHULL_NO_STL
+
+#ifndef QHULL_NO_STL
+//! Same as PrintVertices
+std::vector<QhullVertex> QhullFacetList::
+vertices_toStdVector() const
+{
+ std::vector<QhullVertex> vs;
+ QhullVertexSet qvs(qh(), first().getFacetT(), 0, isSelectAll());
+
+ for(QhullVertexSet::iterator i=qvs.begin(); i!=qvs.end(); ++i){
+ vs.push_back(*i);
+ }
+ return vs;
+}//vertices_toStdVector
+#endif //QHULL_NO_STL
+
+#//!\name GetSet
+
+bool QhullFacetList::
+contains(const QhullFacet &facet) const
+{
+ if(isSelectAll()){
+ return QhullLinkedList<QhullFacet>::contains(facet);
+ }
+ for(QhullFacetList::const_iterator i=begin(); i != end(); ++i){
+ QhullFacet f= *i;
+ if(f==facet && f.isGood()){
+ return true;
+ }
+ }
+ return false;
+}//contains
+
+int QhullFacetList::
+count() const
+{
+ if(isSelectAll()){
+ return QhullLinkedList<QhullFacet>::count();
+ }
+ int counter= 0;
+ for(QhullFacetList::const_iterator i=begin(); i != end(); ++i){
+ if((*i).isGood()){
+ counter++;
+ }
+ }
+ return counter;
+}//count
+
+int QhullFacetList::
+count(const QhullFacet &facet) const
+{
+ if(isSelectAll()){
+ return QhullLinkedList<QhullFacet>::count(facet);
+ }
+ int counter= 0;
+ for(QhullFacetList::const_iterator i=begin(); i != end(); ++i){
+ QhullFacet f= *i;
+ if(f==facet && f.isGood()){
+ counter++;
+ }
+ }
+ return counter;
+}//count
+
+}//namespace orgQhull
+
+#//!\name Global functions
+
+using std::endl;
+using std::ostream;
+using orgQhull::QhullFacet;
+using orgQhull::QhullFacetList;
+using orgQhull::QhullVertex;
+using orgQhull::QhullVertexSet;
+
+ostream &
+operator<<(ostream &os, const QhullFacetList::PrintFacetList &pr)
+{
+ os << pr.print_message;
+ QhullFacetList fs= *pr.facet_list;
+ os << "Vertices for " << fs.count() << " facets" << endl;
+ os << fs.printVertices();
+ os << fs.printFacets();
+ return os;
+}//operator<<
+
+//! Print facet list to stream. From qh_printafacet [io_r.c]
+ostream &
+operator<<(ostream &os, const QhullFacetList::PrintFacets &pr)
+{
+ for(QhullFacetList::const_iterator i= pr.facet_list->begin(); i != pr.facet_list->end(); ++i){
+ QhullFacet f= *i;
+ if(pr.facet_list->isSelectAll() || f.isGood()){
+ os << f.print("");
+ }
+ }
+ return os;
+}//printFacets
+
+//! Print vertices of good faces in facet list to stream. From qh_printvertexlist [io_r.c]
+//! Same as vertices_toStdVector
+ostream &
+operator<<(ostream &os, const QhullFacetList::PrintVertices &pr)
+{
+ QhullVertexSet vs(pr.facet_list->qh(), pr.facet_list->first().getFacetT(), NULL, pr.facet_list->isSelectAll());
+ for(QhullVertexSet::iterator i=vs.begin(); i!=vs.end(); ++i){
+ QhullVertex v= *i;
+ os << v.print("");
+ }
+ return os;
+}//printVertices
+
+std::ostream &
+operator<<(ostream &os, const QhullFacetList &fs)
+{
+ os << fs.printFacets();
+ return os;
+}//QhullFacetList
+
diff --git a/xs/src/qhull/src/libqhullcpp/QhullFacetList.h b/xs/src/qhull/src/libqhullcpp/QhullFacetList.h
new file mode 100644
index 000000000..e61e56840
--- /dev/null
+++ b/xs/src/qhull/src/libqhullcpp/QhullFacetList.h
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/libqhullcpp/QhullFacetList.h#2 $$Change: 2066 $
+** $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
+**
+****************************************************************************/
+
+#ifndef QHULLFACETLIST_H
+#define QHULLFACETLIST_H
+
+#include "libqhullcpp/QhullLinkedList.h"
+#include "libqhullcpp/QhullFacet.h"
+
+#include <ostream>
+
+#ifndef QHULL_NO_STL
+#include <vector>
+#endif
+
+namespace orgQhull {
+
+#//!\name Used here
+ class Qhull;
+ class QhullFacet;
+ class QhullQh;
+
+#//!\name Defined here
+ //! QhullFacetList -- List of QhullFacet/facetT, as a C++ class.
+ //!\see QhullFacetSet.h
+ class QhullFacetList;
+ //! QhullFacetListIterator -- if(f.isGood()){ ... }
+ typedef QhullLinkedListIterator<QhullFacet> QhullFacetListIterator;
+
+class QhullFacetList : public QhullLinkedList<QhullFacet> {
+
+#//!\name Fields
+private:
+ bool select_all; //! True if include bad facets. Default is false.
+
+#//!\name Constructors
+public:
+ QhullFacetList(const Qhull &q, facetT *b, facetT *e);
+ QhullFacetList(QhullQh *qqh, facetT *b, facetT *e);
+ QhullFacetList(QhullFacet b, QhullFacet e) : QhullLinkedList<QhullFacet>(b, e), select_all(false) {}
+ //Copy constructor copies pointer but not contents. Needed for return by value and parameter passing.
+ QhullFacetList(const QhullFacetList &other) : QhullLinkedList<QhullFacet>(*other.begin(), *other.end()), select_all(other.select_all) {}
+ QhullFacetList & operator=(const QhullFacetList &other) { QhullLinkedList<QhullFacet>::operator =(other); select_all= other.select_all; return *this; }
+ ~QhullFacetList() {}
+
+private: //!Disable default constructor. See QhullLinkedList
+ QhullFacetList();
+public:
+
+#//!\name Conversion
+#ifndef QHULL_NO_STL
+ std::vector<QhullFacet> toStdVector() const;
+ std::vector<QhullVertex> vertices_toStdVector() const;
+#endif //QHULL_NO_STL
+#ifdef QHULL_USES_QT
+ QList<QhullFacet> toQList() const;
+ QList<QhullVertex> vertices_toQList() const;
+#endif //QHULL_USES_QT
+
+#//!\name GetSet
+ //! Filtered by facet.isGood(). May be 0 when !isEmpty().
+ countT count() const;
+ bool contains(const QhullFacet &f) const;
+ countT count(const QhullFacet &f) const;
+ bool isSelectAll() const { return select_all; }
+ QhullQh * qh() const { return first().qh(); }
+ void selectAll() { select_all= true; }
+ void selectGood() { select_all= false; }
+ //!< operator==() does not depend on isGood()
+
+#//!\name IO
+ struct PrintFacetList{
+ const QhullFacetList *facet_list;
+ const char * print_message; //!< non-null message
+ PrintFacetList(const QhullFacetList &fl, const char *message) : facet_list(&fl), print_message(message) {}
+ };//PrintFacetList
+ PrintFacetList print(const char *message) const { return PrintFacetList(*this, message); }
+
+ struct PrintFacets{
+ const QhullFacetList *facet_list;
+ PrintFacets(const QhullFacetList &fl) : facet_list(&fl) {}
+ };//PrintFacets
+ PrintFacets printFacets() const { return PrintFacets(*this); }
+
+ struct PrintVertices{
+ const QhullFacetList *facet_list;
+ PrintVertices(const QhullFacetList &fl) : facet_list(&fl) {}
+ };//PrintVertices
+ PrintVertices printVertices() const { return PrintVertices(*this); }
+};//class QhullFacetList
+
+}//namespace orgQhull
+
+#//!\name == Global namespace =========================================
+
+std::ostream &operator<<(std::ostream &os, const orgQhull::QhullFacetList::PrintFacetList &p);
+std::ostream &operator<<(std::ostream &os, const orgQhull::QhullFacetList::PrintFacets &p);
+std::ostream &operator<<(std::ostream &os, const orgQhull::QhullFacetList::PrintVertices &p);
+std::ostream &operator<<(std::ostream &os, const orgQhull::QhullFacetList &fs);
+
+#endif // QHULLFACETLIST_H
diff --git a/xs/src/qhull/src/libqhullcpp/QhullFacetSet.cpp b/xs/src/qhull/src/libqhullcpp/QhullFacetSet.cpp
new file mode 100644
index 000000000..d30c21e26
--- /dev/null
+++ b/xs/src/qhull/src/libqhullcpp/QhullFacetSet.cpp
@@ -0,0 +1,147 @@
+/****************************************************************************
+**
+** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/libqhullcpp/QhullFacetSet.cpp#2 $$Change: 2066 $
+** $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
+**
+****************************************************************************/
+
+#//! QhullFacetSet -- Qhull's linked facets, as a C++ class
+
+#include "libqhullcpp/QhullFacetSet.h"
+
+#include "libqhullcpp/QhullFacet.h"
+#include "libqhullcpp/QhullPoint.h"
+#include "libqhullcpp/QhullRidge.h"
+#include "libqhullcpp/QhullVertex.h"
+
+#ifndef QHULL_NO_STL
+using std::vector;
+#endif
+
+#ifdef _MSC_VER // Microsoft Visual C++ -- warning level 4
+#pragma warning( disable : 4611) // interaction between '_setjmp' and C++ object destruction is non-portable
+#pragma warning( disable : 4996) // function was declared deprecated(strcpy, localtime, etc.)
+#endif
+
+namespace orgQhull {
+
+#//!\name Conversions
+
+// See qt-qhull.cpp for QList conversions
+
+#ifndef QHULL_NO_STL
+std::vector<QhullFacet> QhullFacetSet::
+toStdVector() const
+{
+ QhullSetIterator<QhullFacet> i(*this);
+ std::vector<QhullFacet> vs;
+ while(i.hasNext()){
+ QhullFacet f= i.next();
+ if(isSelectAll() || f.isGood()){
+ vs.push_back(f);
+ }
+ }
+ return vs;
+}//toStdVector
+#endif //QHULL_NO_STL
+
+#//!\name GetSet
+
+bool QhullFacetSet::
+contains(const QhullFacet &facet) const
+{
+ if(isSelectAll()){
+ return QhullSet<QhullFacet>::contains(facet);
+ }
+ for(QhullFacetSet::const_iterator i=begin(); i != end(); ++i){
+ QhullFacet f= *i;
+ if(f==facet && f.isGood()){
+ return true;
+ }
+ }
+ return false;
+}//contains
+
+int QhullFacetSet::
+count() const
+{
+ if(isSelectAll()){
+ return QhullSet<QhullFacet>::count();
+ }
+ int counter= 0;
+ for(QhullFacetSet::const_iterator i=begin(); i != end(); ++i){
+ QhullFacet f= *i;
+ if(f.isGood()){
+ counter++;
+ }
+ }
+ return counter;
+}//count
+
+int QhullFacetSet::
+count(const QhullFacet &facet) const
+{
+ if(isSelectAll()){
+ return QhullSet<QhullFacet>::count(facet);
+ }
+ int counter= 0;
+ for(QhullFacetSet::const_iterator i=begin(); i != end(); ++i){
+ QhullFacet f= *i;
+ if(f==facet && f.isGood()){
+ counter++;
+ }
+ }
+ return counter;
+}//count
+
+}//namespace orgQhull
+
+#//!\name Global functions
+
+using std::endl;
+using std::ostream;
+using orgQhull::QhullFacet;
+using orgQhull::QhullFacetSet;
+
+ostream &
+operator<<(ostream &os, const QhullFacetSet &fs)
+{
+ os << fs.print("");
+ return os;
+}//<<QhullFacetSet
+
+ostream &
+
+operator<<(ostream &os, const QhullFacetSet::PrintFacetSet &pr)
+{
+ os << pr.print_message;
+ QhullFacetSet fs= *pr.facet_set;
+ for(QhullFacetSet::iterator i=fs.begin(); i != fs.end(); ++i){
+ QhullFacet f= *i;
+ if(fs.isSelectAll() || f.isGood()){
+ os << f;
+ }
+ }
+ return os;
+}//<< QhullFacetSet::PrintFacetSet
+
+//! Print facet identifiers to stream. Space prefix. From qh_printfacetheader [io_r.c]
+ostream &
+operator<<(ostream &os, const QhullFacetSet::PrintIdentifiers &p)
+{
+ os << p.print_message;
+ for(QhullFacetSet::const_iterator i=p.facet_set->begin(); i!=p.facet_set->end(); ++i){
+ const QhullFacet f= *i;
+ if(f.getFacetT()==qh_MERGEridge){
+ os << " MERGE";
+ }else if(f.getFacetT()==qh_DUPLICATEridge){
+ os << " DUP";
+ }else if(p.facet_set->isSelectAll() || f.isGood()){
+ os << " f" << f.id();
+ }
+ }
+ os << endl;
+ return os;
+}//<<QhullFacetSet::PrintIdentifiers
+
diff --git a/xs/src/qhull/src/libqhullcpp/QhullFacetSet.h b/xs/src/qhull/src/libqhullcpp/QhullFacetSet.h
new file mode 100644
index 000000000..ee3208559
--- /dev/null
+++ b/xs/src/qhull/src/libqhullcpp/QhullFacetSet.h
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/libqhullcpp/QhullFacetSet.h#2 $$Change: 2066 $
+** $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
+**
+****************************************************************************/
+
+#ifndef QHULLFACETSET_H
+#define QHULLFACETSET_H
+
+#include "libqhullcpp/QhullSet.h"
+#include "libqhullcpp/QhullFacet.h"
+
+#include <ostream>
+
+namespace orgQhull {
+
+#//!\name Used here
+ class Qhull;
+
+#//!\name Defined here
+ //! QhullFacetSet -- a set of Qhull facets, as a C++ class. See QhullFacetList.h
+ class QhullFacetSet;
+ typedef QhullSetIterator<QhullFacet> QhullFacetSetIterator;
+
+class QhullFacetSet : public QhullSet<QhullFacet> {
+
+#//!\name Defined here
+public:
+ typedef facetT * base_type; // for QhullVertexSet
+
+private:
+#//!\name Fields
+ bool select_all; //! True if include bad facets. Default is false.
+
+public:
+#//!\name Constructor
+ //Conversion from setT* is not type-safe. Implicit conversion for void* to T
+ QhullFacetSet(const Qhull &q, setT *s) : QhullSet<QhullFacet>(q, s), select_all(false) {}
+ QhullFacetSet(QhullQh *qqh, setT *s) : QhullSet<QhullFacet>(qqh, s), select_all(false) {}
+ //!Copy constructor copies pointers but not contents. Needed for return by value and parameter passing.
+ QhullFacetSet(const QhullFacetSet &other) : QhullSet<QhullFacet>(other), select_all(other.select_all) {}
+ //!Assignment copies pointers but not contents.
+ QhullFacetSet & operator=(const QhullFacetSet &other) { QhullSet<QhullFacet>::operator=(other); select_all= other.select_all; return *this; }
+
+private:
+ //!Disable default constructor. See QhullSetBase
+ QhullFacetSet();
+public:
+
+#//!\name Conversion
+#ifndef QHULL_NO_STL
+ std::vector<QhullFacet> toStdVector() const;
+#endif //QHULL_NO_STL
+#ifdef QHULL_USES_QT
+ QList<QhullFacet> toQList() const;
+#endif //QHULL_USES_QT
+
+#//!\name GetSet
+ //! Filtered by facet.isGood(). May be 0 when !isEmpty().
+ countT count() const;
+ bool contains(const QhullFacet &f) const;
+ countT count(const QhullFacet &f) const;
+ bool isSelectAll() const { return select_all; }
+ //! operator==() does not depend on isGood()
+ void selectAll() { select_all= true; }
+ void selectGood() { select_all= false; }
+
+#//!\name IO
+ // Not same as QhullFacetList#IO. A QhullFacetSet is a component of a QhullFacetList.
+
+ struct PrintFacetSet{
+ const QhullFacetSet *facet_set;
+ const char * print_message; //!< non-null message
+ PrintFacetSet(const char *message, const QhullFacetSet *s) : facet_set(s), print_message(message) {}
+ };//PrintFacetSet
+ const PrintFacetSet print(const char *message) const { return PrintFacetSet(message, this); }
+
+ struct PrintIdentifiers{
+ const QhullFacetSet *facet_set;
+ const char * print_message; //!< non-null message
+ PrintIdentifiers(const char *message, const QhullFacetSet *s) : facet_set(s), print_message(message) {}
+ };//PrintIdentifiers
+ PrintIdentifiers printIdentifiers(const char *message) const { return PrintIdentifiers(message, this); }
+
+};//class QhullFacetSet
+
+}//namespace orgQhull
+
+#//!\name == Global namespace =========================================
+
+std::ostream &operator<<(std::ostream &os, const orgQhull::QhullFacetSet &fs);
+std::ostream &operator<<(std::ostream &os, const orgQhull::QhullFacetSet::PrintFacetSet &pr);
+std::ostream &operator<<(std::ostream &os, const orgQhull::QhullFacetSet::PrintIdentifiers &p);
+
+#endif // QHULLFACETSET_H
diff --git a/xs/src/qhull/src/libqhullcpp/QhullHyperplane.cpp b/xs/src/qhull/src/libqhullcpp/QhullHyperplane.cpp
new file mode 100644
index 000000000..ed5cc4bae
--- /dev/null
+++ b/xs/src/qhull/src/libqhullcpp/QhullHyperplane.cpp
@@ -0,0 +1,187 @@
+/****************************************************************************
+**
+** Copyright (c) 2009-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/libqhullcpp/QhullHyperplane.cpp#3 $$Change: 2066 $
+** $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
+**
+****************************************************************************/
+
+#include "libqhullcpp/QhullHyperplane.h"
+
+#include "libqhullcpp/Qhull.h"
+#include "libqhullcpp/QhullPoint.h"
+
+#include <iostream>
+
+
+#ifdef _MSC_VER // Microsoft Visual C++ -- warning level 4
+#endif
+
+namespace orgQhull {
+
+#//!\name Constructors
+
+QhullHyperplane::
+QhullHyperplane(const Qhull &q)
+: hyperplane_coordinates(0)
+, qh_qh(q.qh())
+, hyperplane_offset(0.0)
+, hyperplane_dimension(0)
+{
+}
+
+QhullHyperplane::
+QhullHyperplane(const Qhull &q, int hyperplaneDimension, coordT *c, coordT hyperplaneOffset)
+: hyperplane_coordinates(c)
+, qh_qh(q.qh())
+, hyperplane_offset(hyperplaneOffset)
+, hyperplane_dimension(hyperplaneDimension)
+{
+}
+
+#//!\name Conversions
+
+// See qt-qhull.cpp for QList conversions
+
+#ifndef QHULL_NO_STL
+std::vector<coordT> QhullHyperplane::
+toStdVector() const
+{
+ QhullHyperplaneIterator i(*this);
+ std::vector<coordT> fs;
+ while(i.hasNext()){
+ fs.push_back(i.next());
+ }
+ fs.push_back(hyperplane_offset);
+ return fs;
+}//toStdVector
+#endif //QHULL_NO_STL
+
+#//!\name GetSet
+
+//! Return true if equal
+//! If qh_qh defined, tests qh.distanceEpsilon and qh.angleEpsilon
+//! otherwise, tests equal coordinates and offset
+bool QhullHyperplane::
+operator==(const QhullHyperplane &other) const
+{
+ if(hyperplane_dimension!=other.hyperplane_dimension || !hyperplane_coordinates || !other.hyperplane_coordinates){
+ return false;
+ }
+ double d= fabs(hyperplane_offset-other.hyperplane_offset);
+ if(d > (qh_qh ? qh_qh->distanceEpsilon() : 0.0)){
+ return false;
+ }
+ double angle= hyperplaneAngle(other);
+
+ double a= fabs(angle-1.0);
+ if(a > (qh_qh ? qh_qh->angleEpsilon() : 0.0)){
+ return false;
+ }
+ return true;
+}//operator==
+
+#//!\name Methods
+
+//! Return distance from point to hyperplane.
+//! If greater than zero, the point is above the facet (i.e., outside).
+// qh_distplane [geom_r.c], QhullFacet::distance, and QhullHyperplane::distance are copies
+// Does not support RANDOMdist or logging
+double QhullHyperplane::
+distance(const QhullPoint &p) const
+{
+ const coordT *point= p.coordinates();
+ int dim= p.dimension();
+ QHULL_ASSERT(dim==dimension());
+ const coordT *normal= coordinates();
+ double dist;
+
+ switch (dim){
+ case 2:
+ dist= offset() + point[0] * normal[0] + point[1] * normal[1];
+ break;
+ case 3:
+ dist= offset() + point[0] * normal[0] + point[1] * normal[1] + point[2] * normal[2];
+ break;
+ case 4:
+ dist= offset()+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3];
+ break;
+ case 5:
+ dist= offset()+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3]+point[4]*normal[4];
+ break;
+ case 6:
+ dist= offset()+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3]+point[4]*normal[4]+point[5]*normal[5];
+ break;
+ case 7:
+ dist= offset()+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3]+point[4]*normal[4]+point[5]*normal[5]+point[6]*normal[6];
+ break;
+ case 8:
+ dist= offset()+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3]+point[4]*normal[4]+point[5]*normal[5]+point[6]*normal[6]+point[7]*normal[7];
+ break;
+ default:
+ dist= offset();
+ for (int k=dim; k--; )
+ dist += *point++ * *normal++;
+ break;
+ }
+ return dist;
+}//distance
+
+double QhullHyperplane::
+hyperplaneAngle(const QhullHyperplane &other) const
+{
+ volatile realT result= 0.0;
+ QH_TRY_(qh_qh){ // no object creation -- destructors skipped on longjmp()
+ result= qh_getangle(qh_qh, hyperplane_coordinates, other.hyperplane_coordinates);
+ }
+ qh_qh->NOerrexit= true;
+ qh_qh->maybeThrowQhullMessage(QH_TRY_status);
+ return result;
+}//hyperplaneAngle
+
+double QhullHyperplane::
+norm() const {
+ double d= 0.0;
+ const coordT *c= coordinates();
+ for (int k=dimension(); k--; ){
+ d += *c * *c;
+ ++c;
+ }
+ return sqrt(d);
+}//norm
+
+}//namespace orgQhull
+
+#//!\name Global functions
+
+using std::ostream;
+using orgQhull::QhullHyperplane;
+
+#//!\name GetSet<<
+
+ostream &
+operator<<(ostream &os, const QhullHyperplane &p)
+{
+ os << p.print("");
+ return os;
+}
+
+ostream &
+operator<<(ostream &os, const QhullHyperplane::PrintHyperplane &pr)
+{
+ os << pr.print_message;
+ QhullHyperplane p= *pr.hyperplane;
+ const realT *c= p.coordinates();
+ for(int k=p.dimension(); k--; ){
+ realT r= *c++;
+ if(pr.print_message){
+ os << " " << r; // FIXUP QH11010 %8.4g
+ }else{
+ os << " " << r; // FIXUP QH11010 qh_REAL_1
+ }
+ }
+ os << pr.hyperplane_offset_message << " " << p.offset();
+ os << std::endl;
+ return os;
+}//PrintHyperplane
+
diff --git a/xs/src/qhull/src/libqhullcpp/QhullHyperplane.h b/xs/src/qhull/src/libqhullcpp/QhullHyperplane.h
new file mode 100644
index 000000000..2868ce5c9
--- /dev/null
+++ b/xs/src/qhull/src/libqhullcpp/QhullHyperplane.h
@@ -0,0 +1,123 @@
+/****************************************************************************
+**
+** Copyright (c) 2009-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/libqhullcpp/QhullHyperplane.h#4 $$Change: 2079 $
+** $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
+**
+****************************************************************************/
+
+#ifndef QHHYPERPLANE_H
+#define QHHYPERPLANE_H
+
+#include "libqhull_r/qhull_ra.h"
+#include "libqhullcpp/QhullError.h"
+#include "libqhullcpp/QhullIterator.h"
+#include "libqhullcpp/QhullQh.h"
+
+#include <ostream>
+
+namespace orgQhull {
+
+#//!\name Used here
+ class Qhull;
+ class QhullPoint;
+
+#//!\name Defined here
+ //! QhullHyperplane as an offset, dimension, and pointer to coordinates
+ class QhullHyperplane;
+ //! Java-style iterator for QhullHyperplane coordinates
+ class QhullHyperplaneIterator;
+
+class QhullHyperplane { // Similar to QhullPoint
+public:
+#//!\name Subtypes
+ typedef const coordT * iterator;
+ typedef const coordT * const_iterator;
+ typedef QhullHyperplane::iterator Iterator;
+ typedef QhullHyperplane::const_iterator ConstIterator;
+
+private:
+#//!\name Fields
+ coordT * hyperplane_coordinates; //!< Normal to hyperplane. facetT.normal is normalized to 1.0
+ QhullQh * qh_qh; //!< qhT for distanceEpsilon() in operator==
+ coordT hyperplane_offset; //!< Distance from hyperplane to origin
+ int hyperplane_dimension; //!< Dimension of hyperplane
+
+#//!\name Construct
+public:
+ QhullHyperplane() : hyperplane_coordinates(0), qh_qh(0), hyperplane_offset(0.0), hyperplane_dimension(0) {}
+ explicit QhullHyperplane(const Qhull &q);
+ QhullHyperplane(const Qhull &q, int hyperplaneDimension, coordT *c, coordT hyperplaneOffset);
+ explicit QhullHyperplane(QhullQh *qqh) : hyperplane_coordinates(0), qh_qh(qqh), hyperplane_offset(0.0), hyperplane_dimension(0) {}
+ QhullHyperplane(QhullQh *qqh, int hyperplaneDimension, coordT *c, coordT hyperplaneOffset) : hyperplane_coordinates(c), qh_qh(qqh), hyperplane_offset(hyperplaneOffset), hyperplane_dimension(hyperplaneDimension) {}
+ // Creates an alias. Does not copy the hyperplane's coordinates. Needed for return by value and parameter passing.
+ QhullHyperplane(const QhullHyperplane &other) : hyperplane_coordinates(other.hyperplane_coordinates), qh_qh(other.qh_qh), hyperplane_offset(other.hyperplane_offset), hyperplane_dimension(other.hyperplane_dimension) {}
+ // Creates an alias. Does not copy the hyperplane's coordinates. Needed for vector<QhullHyperplane>
+ QhullHyperplane & operator=(const QhullHyperplane &other) { hyperplane_coordinates= other.hyperplane_coordinates; qh_qh= other.qh_qh; hyperplane_offset= other.hyperplane_offset; hyperplane_dimension= other.hyperplane_dimension; return *this; }
+ ~QhullHyperplane() {}
+
+#//!\name Conversions --
+//! Includes offset at end
+#ifndef QHULL_NO_STL
+ std::vector<coordT> toStdVector() const;
+#endif //QHULL_NO_STL
+#ifdef QHULL_USES_QT
+ QList<coordT> toQList() const;
+#endif //QHULL_USES_QT
+
+#//!\name GetSet
+public:
+ const coordT * coordinates() const { return hyperplane_coordinates; }
+ coordT * coordinates() { return hyperplane_coordinates; }
+ void defineAs(int hyperplaneDimension, coordT *c, coordT hyperplaneOffset) { QHULL_ASSERT(hyperplaneDimension>=0); hyperplane_coordinates= c; hyperplane_dimension= hyperplaneDimension; hyperplane_offset= hyperplaneOffset; }
+ //! Creates an alias to other using the same qh_qh
+ void defineAs(QhullHyperplane &other) { hyperplane_coordinates= other.coordinates(); hyperplane_dimension= other.dimension(); hyperplane_offset= other.offset(); }
+ int dimension() const { return hyperplane_dimension; }
+ bool isValid() const { return hyperplane_coordinates!=0 && hyperplane_dimension>0; }
+ coordT offset() const { return hyperplane_offset; }
+ bool operator==(const QhullHyperplane &other) const;
+ bool operator!=(const QhullHyperplane &other) const { return !operator==(other); }
+ const coordT & operator[](int idx) const { QHULL_ASSERT(idx>=0 && idx<hyperplane_dimension); return *(hyperplane_coordinates+idx); }
+ coordT & operator[](int idx) { QHULL_ASSERT(idx>=0 && idx<hyperplane_dimension); return *(hyperplane_coordinates+idx); }
+ void setCoordinates(coordT *c) { hyperplane_coordinates= c; }
+ void setDimension(int hyperplaneDimension) { hyperplane_dimension= hyperplaneDimension; }
+ void setOffset(coordT hyperplaneOffset) { hyperplane_offset= hyperplaneOffset; }
+
+#//!\name iterator
+ iterator begin() { return hyperplane_coordinates; }
+ const_iterator begin() const { return hyperplane_coordinates; }
+ const_iterator constBegin() const { return hyperplane_coordinates; }
+ const_iterator constEnd() const { return hyperplane_coordinates+hyperplane_dimension; }
+ int count() { return hyperplane_dimension; }
+ iterator end() { return hyperplane_coordinates+hyperplane_dimension; }
+ const_iterator end() const { return hyperplane_coordinates+hyperplane_dimension; }
+ size_t size() { return (size_t)hyperplane_dimension; }
+
+#//!\name Methods
+ double distance(const QhullPoint &p) const;
+ double hyperplaneAngle(const QhullHyperplane &other) const;
+ double norm() const;
+
+#//!\name IO
+ struct PrintHyperplane{
+ const QhullHyperplane *hyperplane;
+ const char * print_message; //!< non-null message
+ const char * hyperplane_offset_message; //!< non-null message
+ PrintHyperplane(const char *message, const char *offsetMessage, const QhullHyperplane &p) : hyperplane(&p), print_message(message), hyperplane_offset_message(offsetMessage) {}
+ };//PrintHyperplane
+ PrintHyperplane print(const char *message) const { return PrintHyperplane(message, "", *this); }
+ PrintHyperplane print(const char *message, const char *offsetMessage) const { return PrintHyperplane(message, offsetMessage, *this); }
+
+};//QhullHyperplane
+
+QHULL_DECLARE_SEQUENTIAL_ITERATOR(QhullHyperplane, coordT)
+
+}//namespace orgQhull
+
+#//!\name Global
+
+std::ostream &operator<<(std::ostream &os, const orgQhull::QhullHyperplane::PrintHyperplane &pr);
+std::ostream &operator<<(std::ostream &os, const orgQhull::QhullHyperplane &p);
+
+#endif // QHHYPERPLANE_H
+
diff --git a/xs/src/qhull/src/libqhullcpp/QhullIterator.h b/xs/src/qhull/src/libqhullcpp/QhullIterator.h
new file mode 100644
index 000000000..ec60496bf
--- /dev/null
+++ b/xs/src/qhull/src/libqhullcpp/QhullIterator.h
@@ -0,0 +1,173 @@
+/****************************************************************************
+**
+** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/libqhullcpp/QhullIterator.h#4 $$Change: 2079 $
+** $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
+**
+****************************************************************************/
+
+#ifndef QHULLITERATOR_H
+#define QHULLITERATOR_H
+
+#include "libqhull_r/qhull_ra.h"
+
+#include <assert.h>
+#include <iterator>
+#include <string>
+#include <vector>
+
+namespace orgQhull {
+
+#//!\name Defined here
+ //! Only QHULL_DECLARE_SEQUENTIAL_ITERATOR is used in libqhullcpp. The others need further development
+ //! QHULL_DECLARE_SEQUENTIAL_ITERATOR(C) -- Declare a Java-style iterator
+ //! QHULL_DECLARE_MUTABLE_SEQUENTIAL_ITERATOR(C) -- Declare a mutable Java-style iterator
+ //! QHULL_DECLARE_SET_ITERATOR(C) -- Declare a set iterator
+ //! QHULL_DECLARE_MUTABLE_SET_ITERATOR(C) -- Declare a mutable set iterator
+ //! Derived from Qt/core/tools/qiterator.h and qset_r.h/FOREACHsetelement_()
+
+// Stores C* as done in Mutable... Assumes the container is not deleted.
+// C::const_iterator is an STL-style iterator that returns T&
+#define QHULL_DECLARE_SEQUENTIAL_ITERATOR(C, T) \
+ \
+ class C##Iterator \
+ { \
+ typedef C::const_iterator const_iterator; \
+ const C *c; \
+ const_iterator i; \
+ public: \
+ inline C##Iterator(const C &container) \
+ : c(&container), i(c->constBegin()) {} \
+ inline C##Iterator &operator=(const C &container) \
+ { c = &container; i = c->constBegin(); return *this; } \
+ inline void toFront() { i = c->constBegin(); } \
+ inline void toBack() { i = c->constEnd(); } \
+ inline bool hasNext() const { return i != c->constEnd(); } \
+ inline const T &next() { return *i++; } \
+ inline const T &peekNext() const { return *i; } \
+ inline bool hasPrevious() const { return i != c->constBegin(); } \
+ inline const T &previous() { return *--i; } \
+ inline const T &peekPrevious() const { const_iterator p = i; return *--p; } \
+ inline bool findNext(const T &t) \
+ { while (i != c->constEnd()) if (*i++ == t) return true; return false; } \
+ inline bool findPrevious(const T &t) \
+ { while (i != c->constBegin()) if (*(--i) == t) return true; \
+ return false; } \
+ };//C##Iterator
+
+// Remove setShareable() from Q_DECLARE_MUTABLE_SEQUENTIAL_ITERATOR
+// Uses QHULL_ASSERT (assert.h)
+// Duplicated in MutablePointIterator without insert or remove
+// Not used in libqhullcpp. See Coordinates.h
+#define QHULL_DECLARE_MUTABLE_SEQUENTIAL_ITERATOR(C, T) \
+ class Mutable##C##Iterator \
+ { \
+ typedef C::iterator iterator; \
+ typedef C::const_iterator const_iterator; \
+ C *c; \
+ iterator i, n; \
+ inline bool item_exists() const { return const_iterator(n) != c->constEnd(); } \
+ public: \
+ inline Mutable##C##Iterator(C &container) \
+ : c(&container) \
+ { i = c->begin(); n = c->end(); } \
+ inline ~Mutable##C##Iterator() \
+ {} \
+ inline Mutable##C##Iterator &operator=(C &container) \
+ { c = &container; \
+ i = c->begin(); n = c->end(); return *this; } \
+ inline void toFront() { i = c->begin(); n = c->end(); } \
+ inline void toBack() { i = c->end(); n = i; } \
+ inline bool hasNext() const { return c->constEnd() != const_iterator(i); } \
+ inline T &next() { n = i++; return *n; } \
+ inline T &peekNext() const { return *i; } \
+ inline bool hasPrevious() const { return c->constBegin() != const_iterator(i); } \
+ inline T &previous() { n = --i; return *n; } \
+ inline T &peekPrevious() const { iterator p = i; return *--p; } \
+ inline void remove() \
+ { if (c->constEnd() != const_iterator(n)) { i = c->erase(n); n = c->end(); } } \
+ inline void setValue(const T &t) const { if (c->constEnd() != const_iterator(n)) *n = t; } \
+ inline T &value() { QHULL_ASSERT(item_exists()); return *n; } \
+ inline const T &value() const { QHULL_ASSERT(item_exists()); return *n; } \
+ inline void insert(const T &t) { n = i = c->insert(i, t); ++i; } \
+ inline bool findNext(const T &t) \
+ { while (c->constEnd() != const_iterator(n = i)) if (*i++ == t) return true; return false; } \
+ inline bool findPrevious(const T &t) \
+ { while (c->constBegin() != const_iterator(i)) if (*(n = --i) == t) return true; \
+ n = c->end(); return false; } \
+ };//Mutable##C##Iterator
+
+// Not used in libqhullcpp.
+#define QHULL_DECLARE_SET_ITERATOR(C) \
+\
+ template <class T> \
+ class Qhull##C##Iterator \
+ { \
+ typedef typename Qhull##C<T>::const_iterator const_iterator; \
+ Qhull##C<T> c; \
+ const_iterator i; \
+ public: \
+ inline Qhull##C##Iterator(const Qhull##C<T> &container) \
+ : c(container), i(c.constBegin()) {} \
+ inline Qhull##C##Iterator &operator=(const Qhull##C<T> &container) \
+ { c = container; i = c.constBegin(); return *this; } \
+ inline void toFront() { i = c.constBegin(); } \
+ inline void toBack() { i = c.constEnd(); } \
+ inline bool hasNext() const { return i != c.constEnd(); } \
+ inline const T &next() { return *i++; } \
+ inline const T &peekNext() const { return *i; } \
+ inline bool hasPrevious() const { return i != c.constBegin(); } \
+ inline const T &previous() { return *--i; } \
+ inline const T &peekPrevious() const { const_iterator p = i; return *--p; } \
+ inline bool findNext(const T &t) \
+ { while (i != c.constEnd()) if (*i++ == t) return true; return false; } \
+ inline bool findPrevious(const T &t) \
+ { while (i != c.constBegin()) if (*(--i) == t) return true; \
+ return false; } \
+ };//Qhull##C##Iterator
+
+// Not used in libqhullcpp.
+#define QHULL_DECLARE_MUTABLE_SET_ITERATOR(C) \
+\
+template <class T> \
+class QhullMutable##C##Iterator \
+{ \
+ typedef typename Qhull##C::iterator iterator; \
+ typedef typename Qhull##C::const_iterator const_iterator; \
+ Qhull##C *c; \
+ iterator i, n; \
+ inline bool item_exists() const { return const_iterator(n) != c->constEnd(); } \
+public: \
+ inline Mutable##C##Iterator(Qhull##C &container) \
+ : c(&container) \
+ { c->setSharable(false); i = c->begin(); n = c->end(); } \
+ inline ~Mutable##C##Iterator() \
+ { c->setSharable(true); } \
+ inline Mutable##C##Iterator &operator=(Qhull##C &container) \
+ { c->setSharable(true); c = &container; c->setSharable(false); \
+ i = c->begin(); n = c->end(); return *this; } \
+ inline void toFront() { i = c->begin(); n = c->end(); } \
+ inline void toBack() { i = c->end(); n = i; } \
+ inline bool hasNext() const { return c->constEnd() != const_iterator(i); } \
+ inline T &next() { n = i++; return *n; } \
+ inline T &peekNext() const { return *i; } \
+ inline bool hasPrevious() const { return c->constBegin() != const_iterator(i); } \
+ inline T &previous() { n = --i; return *n; } \
+ inline T &peekPrevious() const { iterator p = i; return *--p; } \
+ inline void remove() \
+ { if (c->constEnd() != const_iterator(n)) { i = c->erase(n); n = c->end(); } } \
+ inline void setValue(const T &t) const { if (c->constEnd() != const_iterator(n)) *n = t; } \
+ inline T &value() { Q_ASSERT(item_exists()); return *n; } \
+ inline const T &value() const { Q_ASSERT(item_exists()); return *n; } \
+ inline void insert(const T &t) { n = i = c->insert(i, t); ++i; } \
+ inline bool findNext(const T &t) \
+ { while (c->constEnd() != const_iterator(n = i)) if (*i++ == t) return true; return false; } \
+ inline bool findPrevious(const T &t) \
+ { while (c->constBegin() != const_iterator(i)) if (*(n = --i) == t) return true; \
+ n = c->end(); return false; } \
+};//QhullMutable##C##Iterator
+
+}//namespace orgQhull
+
+#endif // QHULLITERATOR_H
+
diff --git a/xs/src/qhull/src/libqhullcpp/QhullLinkedList.h b/xs/src/qhull/src/libqhullcpp/QhullLinkedList.h
new file mode 100644
index 000000000..d4caf52c1
--- /dev/null
+++ b/xs/src/qhull/src/libqhullcpp/QhullLinkedList.h
@@ -0,0 +1,388 @@
+/****************************************************************************
+**
+** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/libqhullcpp/QhullLinkedList.h#7 $$Change: 2079 $
+** $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
+**
+****************************************************************************/
+
+#ifndef QHULLLINKEDLIST_H
+#define QHULLLINKEDLIST_H
+
+#include "libqhull_r/qhull_ra.h"
+#include "libqhullcpp/QhullError.h"
+
+#include <cstddef> // ptrdiff_t, size_t
+
+#ifdef QHULL_USES_QT
+#include <QtCore/QList>
+#endif
+
+#ifndef QHULL_NO_STL
+#include <algorithm>
+#include <iterator>
+#include <vector>
+#endif
+
+namespace orgQhull {
+
+#//!\name Defined here
+ //! QhullLinkedList<T> -- A linked list modeled on QLinkedList.
+ //! T is an opaque type with T(B *b), b=t.getBaseT(), t=t.next(), and t=t.prev(). The end node is a sentinel.
+ //! QhullQh/qhT owns the contents.
+ //! QhullLinkedList does not define erase(), clear(), removeFirst(), removeLast(), pop_back(), pop_front(), fromStdList()
+ //! Derived from Qt/core/tools/qlinkedlist.h and libqhull_r.h/FORALLfacets_()
+ //! QhullLinkedList<T>::const_iterator -- STL-style iterator
+ //! QhullLinkedList<T>::iterator -- STL-style iterator
+ //! QhullLinkedListIterator<T> -- Java-style iterator
+ //! Derived from Qt/core/tools/qiterator.h
+ //! Works with Qt's foreach keyword [Qt/src/corelib/global/qglobal.h]
+
+template <typename T>
+class QhullLinkedList
+{
+#//!\name Defined here
+public:
+ class const_iterator;
+ class iterator;
+ typedef const_iterator ConstIterator;
+ typedef iterator Iterator;
+ typedef ptrdiff_t difference_type;
+ typedef countT size_type;
+ typedef T value_type;
+ typedef const value_type *const_pointer;
+ typedef const value_type &const_reference;
+ typedef value_type *pointer;
+ typedef value_type &reference;
+
+#//!\name Fields
+private:
+ T begin_node;
+ T end_node; //! Sentinel node at end of list
+
+#//!\name Constructors
+public:
+ QhullLinkedList<T>(T b, T e) : begin_node(b), end_node(e) {}
+ //! Copy constructor copies begin_node and end_node, but not the list elements. Needed for return by value and parameter passing.
+ QhullLinkedList<T>(const QhullLinkedList<T> &other) : begin_node(other.begin_node), end_node(other.end_node) {}
+ //! Copy assignment copies begin_node and end_node, but not the list elements.
+ QhullLinkedList<T> & operator=(const QhullLinkedList<T> &other) { begin_node= other.begin_node; end_node= other.end_node; return *this; }
+ ~QhullLinkedList<T>() {}
+
+private:
+ //!disabled since a sentinel must be allocated as the private type
+ QhullLinkedList<T>() {}
+
+public:
+
+#//!\name Conversions
+#ifndef QHULL_NO_STL
+ std::vector<T> toStdVector() const;
+#endif
+#ifdef QHULL_USES_QT
+ QList<T> toQList() const;
+#endif
+
+#//!\name GetSet
+ countT count() const;
+ //count(t) under #//!\name Search
+ bool isEmpty() const { return (begin_node==end_node); }
+ bool operator==(const QhullLinkedList<T> &o) const;
+ bool operator!=(const QhullLinkedList<T> &o) const { return !operator==(o); }
+ size_t size() const { return count(); }
+
+#//!\name Element access
+ //! For back() and last(), return T instead of T& (T is computed)
+ const T back() const { return last(); }
+ T back() { return last(); }
+ const T & first() const { QHULL_ASSERT(!isEmpty()); return begin_node; }
+ T & first() { QHULL_ASSERT(!isEmpty()); return begin_node; }
+ const T & front() const { return first(); }
+ T & front() { return first(); }
+ const T last() const { QHULL_ASSERT(!isEmpty()); return *--end(); }
+ T last() { QHULL_ASSERT(!isEmpty()); return *--end(); }
+
+#//!\name Modify -- Allocation of opaque types not implemented.
+
+#//!\name Search
+ bool contains(const T &t) const;
+ countT count(const T &t) const;
+
+#//!\name Iterator
+ iterator begin() { return begin_node; }
+ const_iterator begin() const { return begin_node; }
+ const_iterator constBegin() const { return begin_node; }
+ const_iterator constEnd() const { return end_node; }
+ iterator end() { return end_node; }
+ const_iterator end() const { return end_node; }
+
+ class iterator {
+
+ private:
+ T i;
+ friend class const_iterator;
+
+ public:
+ typedef std::bidirectional_iterator_tag iterator_category;
+ typedef T value_type;
+ typedef value_type *pointer;
+ typedef value_type &reference;
+ typedef ptrdiff_t difference_type;
+
+ iterator() : i() {}
+ iterator(const T &t) : i(t) {} //!< Automatic conversion to iterator
+ iterator(const iterator &o) : i(o.i) {}
+ iterator & operator=(const iterator &o) { i= o.i; return *this; }
+
+ const T & operator*() const { return i; }
+ T & operator*() { return i; }
+ // Do not define operator[]
+ const T * operator->() const { return &i; }
+ T * operator->() { return &i; }
+ bool operator==(const iterator &o) const { return i == o.i; }
+ bool operator!=(const iterator &o) const { return !operator==(o); }
+ bool operator==(const const_iterator &o) const { return i==reinterpret_cast<const iterator &>(o).i; }
+ bool operator!=(const const_iterator &o) const { return !operator==(o); }
+ iterator & operator++() { i= i.next(); return *this; }
+ iterator operator++(int) { iterator o= i; i= i.next(); return o; }
+ iterator & operator--() { i= i.previous(); return *this; }
+ iterator operator--(int) { iterator o= i; i= i.previous(); return o; }
+ iterator operator+(int j) const;
+ iterator operator-(int j) const { return operator+(-j); }
+ iterator & operator+=(int j) { return (*this= *this + j); }
+ iterator & operator-=(int j) { return (*this= *this - j); }
+ };//QhullLinkedList::iterator
+
+ class const_iterator {
+
+ private:
+ T i;
+
+ public:
+ typedef std::bidirectional_iterator_tag iterator_category;
+ typedef T value_type;
+ typedef const value_type *pointer;
+ typedef const value_type &reference;
+ typedef ptrdiff_t difference_type;
+
+ const_iterator() : i() {}
+ const_iterator(const T &t) : i(t) {} //!< Automatic conversion to const_iterator
+ const_iterator(const iterator &o) : i(o.i) {}
+ const_iterator(const const_iterator &o) : i(o.i) {}
+ const_iterator &operator=(const const_iterator &o) { i= o.i; return *this; }
+
+ const T & operator*() const { return i; }
+ const T * operator->() const { return i; }
+ bool operator==(const const_iterator &o) const { return i == o.i; }
+ bool operator!=(const const_iterator &o) const { return !operator==(o); }
+ // No comparisons or iterator diff
+ const_iterator &operator++() { i= i.next(); return *this; }
+ const_iterator operator++(int) { const_iterator o= i; i= i.next(); return o; }
+ const_iterator &operator--() { i= i.previous(); return *this; }
+ const_iterator operator--(int) { const_iterator o= i; i= i.previous(); return o; }
+ const_iterator operator+(int j) const;
+ const_iterator operator-(int j) const { return operator+(-j); }
+ const_iterator &operator+=(int j) { return (*this= *this + j); }
+ const_iterator &operator-=(int j) { return (*this= *this - j); }
+ };//QhullLinkedList::const_iterator
+
+};//QhullLinkedList
+
+template <typename T>
+class QhullLinkedListIterator // FIXUP QH11016 define QhullMutableLinkedListIterator
+{
+ typedef typename QhullLinkedList<T>::const_iterator const_iterator;
+ const QhullLinkedList<T> *c;
+ const_iterator i;
+
+public:
+ QhullLinkedListIterator(const QhullLinkedList<T> &container) : c(&container), i(c->constBegin()) {}
+ QhullLinkedListIterator & operator=(const QhullLinkedList<T> &container) { c= &container; i= c->constBegin(); return *this; }
+ bool findNext(const T &t);
+ bool findPrevious(const T &t);
+ bool hasNext() const { return i != c->constEnd(); }
+ bool hasPrevious() const { return i != c->constBegin(); }
+ T next() { return *i++; }
+ T peekNext() const { return *i; }
+ T peekPrevious() const { const_iterator p= i; return *--p; }
+ T previous() { return *--i; }
+ void toFront() { i= c->constBegin(); }
+ void toBack() { i= c->constEnd(); }
+};//QhullLinkedListIterator
+
+#//!\name == Definitions =========================================
+
+#//!\name Conversion
+
+#ifndef QHULL_NO_STL
+template <typename T>
+std::vector<T> QhullLinkedList<T>::
+toStdVector() const
+{
+ std::vector<T> tmp;
+ std::copy(constBegin(), constEnd(), std::back_inserter(tmp));
+ return tmp;
+}//toStdVector
+#endif
+
+#ifdef QHULL_USES_QT
+template <typename T>
+QList<T> QhullLinkedList<T>::
+toQList() const
+{
+ QhullLinkedListIterator<T> i(*this);
+ QList<T> ls;
+ while(i.hasNext()){
+ ls.append(i.next());
+ }
+ return ls;
+}//toQList
+#endif
+
+#//!\name GetSet
+
+template <typename T>
+countT QhullLinkedList<T>::
+count() const
+{
+ const_iterator i= begin_node;
+ countT c= 0;
+ while(i != end_node){
+ c++;
+ i++;
+ }
+ return c;
+}//count
+
+#//!\name Search
+
+template <typename T>
+bool QhullLinkedList<T>::
+contains(const T &t) const
+{
+ const_iterator i= begin_node;
+ while(i != end_node){
+ if(i==t){
+ return true;
+ }
+ i++;
+ }
+ return false;
+}//contains
+
+template <typename T>
+countT QhullLinkedList<T>::
+count(const T &t) const
+{
+ const_iterator i= begin_node;
+ countT c= 0;
+ while(i != end_node){
+ if(i==t){
+ c++;
+ }
+ i++;
+ }
+ return c;
+}//count
+
+template <typename T>
+bool QhullLinkedList<T>::
+operator==(const QhullLinkedList<T> &l) const
+{
+ if(begin_node==l.begin_node){
+ return (end_node==l.end_node);
+ }
+ T i= begin_node;
+ T il= l.begin_node;
+ while(i != end_node){
+ if(i != il){
+ return false;
+ }
+ i= static_cast<T>(i.next());
+ il= static_cast<T>(il.next());
+ }
+ if(il != l.end_node){
+ return false;
+ }
+ return true;
+}//operator==
+
+#//!\name Iterator
+
+template <typename T>
+typename QhullLinkedList<T>::iterator QhullLinkedList<T>::iterator::
+operator+(int j) const
+{
+ T n= i;
+ if(j>0){
+ while(j--){
+ n= n.next();
+ }
+ }else{
+ while(j++){
+ n= n.previous();
+ }
+ }
+ return iterator(n);
+}//operator+
+
+template <typename T>
+typename QhullLinkedList<T>::const_iterator QhullLinkedList<T>::const_iterator::
+operator+(int j) const
+{
+ T n= i;
+ if(j>0){
+ while(j--){
+ n= n.next();
+ }
+ }else{
+ while(j++){
+ n= n.previous();
+ }
+ }
+ return const_iterator(n);
+}//operator+
+
+#//!\name QhullLinkedListIterator
+
+template <typename T>
+bool QhullLinkedListIterator<T>::
+findNext(const T &t)
+{
+ while(i != c->constEnd()){
+ if (*i++ == t){
+ return true;
+ }
+ }
+ return false;
+}//findNext
+
+template <typename T>
+bool QhullLinkedListIterator<T>::
+findPrevious(const T &t)
+{
+ while(i!=c->constBegin()){
+ if(*(--i)==t){
+ return true;
+ }
+ }
+ return false;
+}//findNext
+
+}//namespace orgQhull
+
+#//!\name Global
+
+template <typename T>
+std::ostream &
+operator<<(std::ostream &os, const orgQhull::QhullLinkedList<T> &qs)
+{
+ typename orgQhull::QhullLinkedList<T>::const_iterator i;
+ for(i= qs.begin(); i != qs.end(); ++i){
+ os << *i;
+ }
+ return os;
+}//operator<<
+
+#endif // QHULLLINKEDLIST_H
+
diff --git a/xs/src/qhull/src/libqhullcpp/QhullPoint.cpp b/xs/src/qhull/src/libqhullcpp/QhullPoint.cpp
new file mode 100644
index 000000000..f5e912460
--- /dev/null
+++ b/xs/src/qhull/src/libqhullcpp/QhullPoint.cpp
@@ -0,0 +1,203 @@
+/****************************************************************************
+**
+** Copyright (c) 2009-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/libqhullcpp/QhullPoint.cpp#3 $$Change: 2066 $
+** $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
+**
+****************************************************************************/
+
+#include "libqhullcpp/QhullPoint.h"
+
+#include "libqhullcpp/QhullError.h"
+#include "libqhullcpp/Qhull.h"
+
+#include <iostream>
+#include <algorithm>
+
+#ifdef _MSC_VER // Microsoft Visual C++ -- warning level 4
+#endif
+
+namespace orgQhull {
+
+#//!\name Constructors
+
+
+QhullPoint::
+QhullPoint(const Qhull &q)
+: point_coordinates(0)
+, qh_qh(q.qh())
+, point_dimension(q.hullDimension())
+{
+}//QhullPoint
+
+QhullPoint::
+QhullPoint(const Qhull &q, coordT *c)
+: point_coordinates(c)
+, qh_qh(q.qh())
+, point_dimension(q.hullDimension())
+{
+ QHULL_ASSERT(q.hullDimension()>0);
+}//QhullPoint dim, coordT
+
+QhullPoint::
+QhullPoint(const Qhull &q, int pointDimension, coordT *c)
+: point_coordinates(c)
+, qh_qh(q.qh())
+, point_dimension(pointDimension)
+{
+}//QhullPoint dim, coordT
+
+//! QhullPoint of Coordinates with point_dimension==c.count()
+QhullPoint::
+QhullPoint(const Qhull &q, Coordinates &c)
+: point_coordinates(c.data())
+, qh_qh(q.qh())
+, point_dimension(c.count())
+{
+}//QhullPoint Coordinates
+
+#//!\name Conversions
+
+// See qt-qhull.cpp for QList conversion
+
+#ifndef QHULL_NO_STL
+std::vector<coordT> QhullPoint::
+toStdVector() const
+{
+ QhullPointIterator i(*this);
+ std::vector<coordT> vs;
+ while(i.hasNext()){
+ vs.push_back(i.next());
+ }
+ return vs;
+}//toStdVector
+#endif //QHULL_NO_STL
+
+#//!\name GetSet
+
+//! QhullPoint is equal if it has the same address and dimension
+//! If !qh_qh, returns true if dimension and coordinates are equal
+//! If qh_qh, returns true if the distance between points is less than qh_qh->distanceEpsilon()
+//!\todo Compares distance with distance-to-hyperplane (distanceEpsilon). Is that correct?
+bool QhullPoint::
+operator==(const QhullPoint &other) const
+{
+ if(point_dimension!=other.point_dimension){
+ return false;
+ }
+ const coordT *c= point_coordinates;
+ const coordT *c2= other.point_coordinates;
+ if(c==c2){
+ return true;
+ }
+ if(!c || !c2){
+ return false;
+ }
+ if(!qh_qh || qh_qh->hull_dim==0){
+ for(int k= point_dimension; k--; ){
+ if(*c++ != *c2++){
+ return false;
+ }
+ }
+ return true;
+ }
+ double dist2= 0.0;
+ for(int k= point_dimension; k--; ){
+ double diff= *c++ - *c2++;
+ dist2 += diff*diff;
+ }
+ dist2= sqrt(dist2);
+ return (dist2 < qh_qh->distanceEpsilon());
+}//operator==
+
+#//!\name Methods
+
+//! Return distance between two points.
+double QhullPoint::
+distance(const QhullPoint &p) const
+{
+ const coordT *c= point_coordinates;
+ const coordT *c2= p.point_coordinates;
+ int dim= point_dimension;
+ if(dim!=p.point_dimension){
+ throw QhullError(10075, "QhullPoint error: Expecting dimension %d for distance(). Got %d", dim, p.point_dimension);
+ }
+ if(!c || !c2){
+ throw QhullError(10076, "QhullPoint error: Cannot compute distance() for undefined point");
+ }
+ double dist;
+
+ switch(dim){
+ case 2:
+ dist= (c[0]-c2[0])*(c[0]-c2[0]) + (c[1]-c2[1])*(c[1]-c2[1]);
+ break;
+ case 3:
+ dist= (c[0]-c2[0])*(c[0]-c2[0]) + (c[1]-c2[1])*(c[1]-c2[1]) + (c[2]-c2[2])*(c[2]-c2[2]);
+ break;
+ case 4:
+ dist= (c[0]-c2[0])*(c[0]-c2[0]) + (c[1]-c2[1])*(c[1]-c2[1]) + (c[2]-c2[2])*(c[2]-c2[2]) + (c[3]-c2[3])*(c[3]-c2[3]);
+ break;
+ case 5:
+ dist= (c[0]-c2[0])*(c[0]-c2[0]) + (c[1]-c2[1])*(c[1]-c2[1]) + (c[2]-c2[2])*(c[2]-c2[2]) + (c[3]-c2[3])*(c[3]-c2[3]) + (c[4]-c2[4])*(c[4]-c2[4]);
+ break;
+ case 6:
+ dist= (c[0]-c2[0])*(c[0]-c2[0]) + (c[1]-c2[1])*(c[1]-c2[1]) + (c[2]-c2[2])*(c[2]-c2[2]) + (c[3]-c2[3])*(c[3]-c2[3]) + (c[4]-c2[4])*(c[4]-c2[4]) + (c[5]-c2[5])*(c[5]-c2[5]);
+ break;
+ case 7:
+ dist= (c[0]-c2[0])*(c[0]-c2[0]) + (c[1]-c2[1])*(c[1]-c2[1]) + (c[2]-c2[2])*(c[2]-c2[2]) + (c[3]-c2[3])*(c[3]-c2[3]) + (c[4]-c2[4])*(c[4]-c2[4]) + (c[5]-c2[5])*(c[5]-c2[5]) + (c[6]-c2[6])*(c[6]-c2[6]);
+ break;
+ case 8:
+ dist= (c[0]-c2[0])*(c[0]-c2[0]) + (c[1]-c2[1])*(c[1]-c2[1]) + (c[2]-c2[2])*(c[2]-c2[2]) + (c[3]-c2[3])*(c[3]-c2[3]) + (c[4]-c2[4])*(c[4]-c2[4]) + (c[5]-c2[5])*(c[5]-c2[5]) + (c[6]-c2[6])*(c[6]-c2[6]) + (c[7]-c2[7])*(c[7]-c2[7]);
+ break;
+ default:
+ dist= 0.0;
+ for(int k=dim; k--; ){
+ dist += (*c - *c2) * (*c - *c2);
+ ++c;
+ ++c2;
+ }
+ break;
+ }
+ return sqrt(dist);
+}//distance
+
+}//namespace orgQhull
+
+#//!\name Global functions
+
+using std::ostream;
+using orgQhull::QhullPoint;
+
+//! Same as qh_printpointid [io.c]
+ostream &
+operator<<(ostream &os, const QhullPoint::PrintPoint &pr)
+{
+ QhullPoint p= *pr.point;
+ countT i= p.id();
+ if(pr.point_message){
+ if(*pr.point_message){
+ os << pr.point_message << " ";
+ }
+ if(pr.with_identifier && (i!=qh_IDunknown) && (i!=qh_IDnone)){
+ os << "p" << i << ": ";
+ }
+ }
+ const realT *c= p.coordinates();
+ for(int k=p.dimension(); k--; ){
+ realT r= *c++;
+ if(pr.point_message){
+ os << " " << r; // FIXUP QH11010 %8.4g
+ }else{
+ os << " " << r; // FIXUP QH11010 qh_REAL_1
+ }
+ }
+ os << std::endl;
+ return os;
+}//printPoint
+
+ostream &
+operator<<(ostream &os, const QhullPoint &p)
+{
+ os << p.print("");
+ return os;
+}//operator<<
diff --git a/xs/src/qhull/src/libqhullcpp/QhullPoint.h b/xs/src/qhull/src/libqhullcpp/QhullPoint.h
new file mode 100644
index 000000000..17f94ab36
--- /dev/null
+++ b/xs/src/qhull/src/libqhullcpp/QhullPoint.h
@@ -0,0 +1,136 @@
+/****************************************************************************
+**
+** Copyright (c) 2009-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/libqhullcpp/QhullPoint.h#4 $$Change: 2079 $
+** $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
+**
+****************************************************************************/
+
+#ifndef QHPOINT_H
+#define QHPOINT_H
+
+#include "libqhull_r/qhull_ra.h"
+#include "libqhullcpp/QhullError.h"
+#include "libqhullcpp/QhullIterator.h"
+#include "libqhullcpp/QhullQh.h"
+#include "libqhullcpp/Coordinates.h"
+
+#include <ostream>
+
+namespace orgQhull {
+
+#//!\name Defined here
+ class QhullPoint; //!< QhullPoint as a pointer and dimension to shared memory
+ class QhullPointIterator; //!< Java-style iterator for QhullPoint coordinates
+
+#//!\name Used here
+ class Qhull;
+
+//! A QhullPoint is a dimension and an array of coordinates.
+//! With Qhull/QhullQh, a QhullPoint has an identifier. Point equality is relative to qh.distanceEpsilon
+class QhullPoint {
+
+#//!\name Iterators
+public:
+ typedef coordT * base_type; // for QhullPointSet
+ typedef const coordT * iterator;
+ typedef const coordT * const_iterator;
+ typedef QhullPoint::iterator Iterator;
+ typedef QhullPoint::const_iterator ConstIterator;
+
+#//!\name Fields
+protected: // For QhullPoints::iterator, QhullPoints::const_iterator
+ coordT * point_coordinates; //!< Pointer to first coordinate, 0 if undefined
+ QhullQh * qh_qh; //!< qhT for this instance of Qhull. 0 if undefined.
+ //!< operator==() returns true if points within sqrt(qh_qh->distanceEpsilon())
+ //!< If !qh_qh, id() is -3, and operator==() requires equal coordinates
+ int point_dimension; //!< Default dimension is qh_qh->hull_dim
+public:
+
+#//!\name Constructors
+ //! QhullPoint, PointCoordinates, and QhullPoints have similar constructors
+ //! If Qhull/QhullQh is not initialized, then QhullPoint.dimension() is zero unless explicitly set
+ //! Cannot define QhullPoints(int pointDimension) since it is ambiguous with QhullPoints(QhullQh *qqh)
+ QhullPoint() : point_coordinates(0), qh_qh(0), point_dimension(0) {}
+ QhullPoint(int pointDimension, coordT *c) : point_coordinates(c), qh_qh(0), point_dimension(pointDimension) { QHULL_ASSERT(pointDimension>0); }
+ explicit QhullPoint(const Qhull &q);
+ QhullPoint(const Qhull &q, coordT *c);
+ QhullPoint(const Qhull &q, Coordinates &c);
+ QhullPoint(const Qhull &q, int pointDimension, coordT *c);
+ explicit QhullPoint(QhullQh *qqh) : point_coordinates(0), qh_qh(qqh), point_dimension(qqh->hull_dim) {}
+ QhullPoint(QhullQh *qqh, coordT *c) : point_coordinates(c), qh_qh(qqh), point_dimension(qqh->hull_dim) { QHULL_ASSERT(qqh->hull_dim>0); }
+ QhullPoint(QhullQh *qqh, Coordinates &c) : point_coordinates(c.data()), qh_qh(qqh), point_dimension(c.count()) {}
+ QhullPoint(QhullQh *qqh, int pointDimension, coordT *c) : point_coordinates(c), qh_qh(qqh), point_dimension(pointDimension) {}
+ //! Creates an alias. Does not make a deep copy of the point. Needed for return by value and parameter passing.
+ QhullPoint(const QhullPoint &other) : point_coordinates(other.point_coordinates), qh_qh(other.qh_qh), point_dimension(other.point_dimension) {}
+ //! Creates an alias. Does not make a deep copy of the point. Needed for vector<QhullPoint>
+ QhullPoint & operator=(const QhullPoint &other) { point_coordinates= other.point_coordinates; qh_qh= other.qh_qh; point_dimension= other.point_dimension; return *this; }
+ ~QhullPoint() {}
+
+
+#//!\name Conversions
+
+#ifndef QHULL_NO_STL
+ std::vector<coordT> toStdVector() const;
+#endif //QHULL_NO_STL
+#ifdef QHULL_USES_QT
+ QList<coordT> toQList() const;
+#endif //QHULL_USES_QT
+
+#//!\name GetSet
+public:
+ const coordT * coordinates() const { return point_coordinates; } //!< 0 if undefined
+ coordT * coordinates() { return point_coordinates; } //!< 0 if undefined
+ void defineAs(coordT *c) { QHULL_ASSERT(point_dimension>0); point_coordinates= c; }
+ void defineAs(int pointDimension, coordT *c) { QHULL_ASSERT(pointDimension>=0); point_coordinates= c; point_dimension= pointDimension; }
+ void defineAs(QhullPoint &other) { point_coordinates= other.point_coordinates; qh_qh= other.qh_qh; point_dimension= other.point_dimension; }
+ int dimension() const { return point_dimension; }
+ coordT * getBaseT() const { return point_coordinates; } // for QhullPointSet
+ countT id() const { return qh_pointid(qh_qh, point_coordinates); } // NOerrors
+ bool isValid() const { return (point_coordinates!=0 && point_dimension>0); };
+ bool operator==(const QhullPoint &other) const;
+ bool operator!=(const QhullPoint &other) const { return ! operator==(other); }
+ const coordT & operator[](int idx) const { QHULL_ASSERT(point_coordinates!=0 && idx>=0 && idx<point_dimension); return *(point_coordinates+idx); } //!< 0 to hull_dim-1
+ coordT & operator[](int idx) { QHULL_ASSERT(point_coordinates!=0 && idx>=0 && idx<point_dimension); return *(point_coordinates+idx); } //!< 0 to hull_dim-1
+ QhullQh * qh() { return qh_qh; }
+ void setCoordinates(coordT *c) { point_coordinates= c; }
+ void setDimension(int pointDimension) { point_dimension= pointDimension; }
+
+#//!\name foreach
+ iterator begin() { return point_coordinates; }
+ const_iterator begin() const { return point_coordinates; }
+ const_iterator constBegin() const { return point_coordinates; }
+ const_iterator constEnd() const { return (point_coordinates ? point_coordinates+point_dimension : 0); }
+ int count() { return (point_coordinates ? point_dimension : 0); }
+ iterator end() { return (point_coordinates ? point_coordinates+point_dimension : 0); }
+ const_iterator end() const { return (point_coordinates ? point_coordinates+point_dimension : 0); }
+ size_t size() { return (size_t)(point_coordinates ? point_dimension : 0); }
+
+#//!\name Methods
+ void advancePoint(countT idx) { if(point_coordinates) { point_coordinates += idx*point_dimension; } }
+ double distance(const QhullPoint &p) const;
+
+#//!\name IO
+
+ struct PrintPoint{
+ const QhullPoint *point;
+ const char * point_message;
+ bool with_identifier;
+ PrintPoint(const char *message, bool withIdentifier, const QhullPoint &p) : point(&p), point_message(message), with_identifier(withIdentifier) {}
+ };//PrintPoint
+ PrintPoint print(const char *message) const { return PrintPoint(message, false, *this); }
+ PrintPoint printWithIdentifier(const char *message) const { return PrintPoint(message, true, *this); }
+
+};//QhullPoint
+
+QHULL_DECLARE_SEQUENTIAL_ITERATOR(QhullPoint, coordT)
+
+}//namespace orgQhull
+
+#//!\name Global
+
+std::ostream &operator<<(std::ostream &os, const orgQhull::QhullPoint::PrintPoint &pr);
+std::ostream &operator<<(std::ostream &os, const orgQhull::QhullPoint &p);
+
+#endif // QHPOINT_H
+
diff --git a/xs/src/qhull/src/libqhullcpp/QhullPointSet.cpp b/xs/src/qhull/src/libqhullcpp/QhullPointSet.cpp
new file mode 100644
index 000000000..561fd6c76
--- /dev/null
+++ b/xs/src/qhull/src/libqhullcpp/QhullPointSet.cpp
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** Copyright (c) 2009-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/libqhullcpp/QhullPointSet.cpp#2 $$Change: 2066 $
+** $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
+**
+****************************************************************************/
+
+#include "libqhullcpp/QhullPointSet.h"
+
+#include <iostream>
+#include <algorithm>
+
+#ifdef _MSC_VER // Microsoft Visual C++ -- warning level 4
+#endif
+
+namespace orgQhull {
+
+// Implemented via QhullSet.h
+
+}//namespace orgQhull
+
+#//!\name Global functions
+
+using std::endl;
+using std::ostream;
+using orgQhull::QhullPoint;
+using orgQhull::QhullPointSet;
+using orgQhull::QhullPointSetIterator;
+
+ostream &
+operator<<(ostream &os, const QhullPointSet::PrintIdentifiers &pr)
+{
+ os << pr.print_message;
+ const QhullPointSet s= *pr.point_set;
+ QhullPointSetIterator i(s);
+ while(i.hasNext()){
+ if(i.hasPrevious()){
+ os << " ";
+ }
+ const QhullPoint point= i.next();
+ countT id= point.id();
+ os << "p" << id;
+
+ }
+ os << endl;
+ return os;
+}//PrintIdentifiers
+
+ostream &
+operator<<(ostream &os, const QhullPointSet::PrintPointSet &pr)
+{
+ os << pr.print_message;
+ const QhullPointSet s= *pr.point_set;
+ for(QhullPointSet::const_iterator i=s.begin(); i != s.end(); ++i){
+ const QhullPoint point= *i;
+ os << point;
+ }
+ return os;
+}//printPointSet
+
+
diff --git a/xs/src/qhull/src/libqhullcpp/QhullPointSet.h b/xs/src/qhull/src/libqhullcpp/QhullPointSet.h
new file mode 100644
index 000000000..8562e170e
--- /dev/null
+++ b/xs/src/qhull/src/libqhullcpp/QhullPointSet.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (c) 2009-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/libqhullcpp/QhullPointSet.h#4 $$Change: 2079 $
+** $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
+**
+****************************************************************************/
+
+#ifndef QHULLPOINTSET_H
+#define QHULLPOINTSET_H
+
+#include "libqhull_r/qhull_ra.h"
+#include "libqhullcpp/QhullSet.h"
+#include "libqhullcpp/QhullPoint.h"
+
+#include <ostream>
+
+namespace orgQhull {
+
+#//!\name Used here
+ class Qhull;
+ class QhullPoint;
+
+#//!\name Defined here
+ //! QhullPointSet -- a set of coordinate pointers with input dimension
+ // with const_iterator and iterator
+ class QhullPointSet;
+
+class QhullPointSet : public QhullSet<QhullPoint> {
+
+private:
+#//!\name Fields
+ // no fields
+public:
+
+#//!\name Construct
+ QhullPointSet(const Qhull &q, setT *s) : QhullSet<QhullPoint>(q, s) {}
+ //Conversion from setT* is not type-safe. Implicit conversion for void* to T
+ QhullPointSet(QhullQh *qqh, setT *s) : QhullSet<QhullPoint>(qqh, s) {}
+ //Copy constructor copies pointer but not contents. Needed for return by value and parameter passing.
+ QhullPointSet(const QhullPointSet &other) : QhullSet<QhullPoint>(other) {}
+ //!Assignment copies pointers but not contents.
+ QhullPointSet & operator=(const QhullPointSet &other) { QhullSet<QhullPoint>::operator=(other); return *this; }
+ ~QhullPointSet() {}
+
+ //!Default constructor disabled.
+private:
+ QhullPointSet();
+public:
+
+#//!\name IO
+ struct PrintIdentifiers{
+ const QhullPointSet *point_set;
+ const char * print_message; //!< non-null message
+ PrintIdentifiers(const char *message, const QhullPointSet *s) : point_set(s), print_message(message) {}
+ };//PrintIdentifiers
+ PrintIdentifiers printIdentifiers(const char *message) const { return PrintIdentifiers(message, this); }
+
+ struct PrintPointSet{
+ const QhullPointSet *point_set;
+ const char * print_message; //!< non-null message
+ PrintPointSet(const char *message, const QhullPointSet &s) : point_set(&s), print_message(message) {}
+ };//PrintPointSet
+ PrintPointSet print(const char *message) const { return PrintPointSet(message, *this); }
+
+};//QhullPointSet
+
+typedef QhullSetIterator<QhullPoint> QhullPointSetIterator;
+
+}//namespace orgQhull
+
+#//!\name Global
+
+std::ostream &operator<<(std::ostream &os, const orgQhull::QhullPointSet::PrintIdentifiers &pr);
+std::ostream &operator<<(std::ostream &os, const orgQhull::QhullPointSet::PrintPointSet &pr);
+
+#endif // QHULLPOINTSET_H
diff --git a/xs/src/qhull/src/libqhullcpp/QhullPoints.cpp b/xs/src/qhull/src/libqhullcpp/QhullPoints.cpp
new file mode 100644
index 000000000..2320b5007
--- /dev/null
+++ b/xs/src/qhull/src/libqhullcpp/QhullPoints.cpp
@@ -0,0 +1,320 @@
+/****************************************************************************
+**
+** Copyright (c) 2009-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/libqhullcpp/QhullPoints.cpp#3 $$Change: 2066 $
+** $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
+**
+****************************************************************************/
+
+#include "libqhullcpp/QhullPoints.h"
+
+#include "libqhullcpp/Qhull.h"
+
+#include <iostream>
+
+#ifndef QHULL_NO_STL
+#include <vector>
+#endif
+
+#ifdef _MSC_VER // Microsoft Visual C++ -- warning level 4
+#endif
+
+namespace orgQhull {
+
+#//!\name Constructors
+
+QhullPoints::
+QhullPoints(const Qhull &q)
+: point_first(0)
+, point_end(0)
+, qh_qh(q.qh())
+, point_dimension(q.hullDimension())
+{
+}//QhullPoints Qhull
+
+QhullPoints::
+QhullPoints(const Qhull &q, countT coordinateCount2, coordT *c)
+: point_first(c)
+, point_end(c+coordinateCount2)
+, qh_qh(q.qh())
+, point_dimension(q.hullDimension())
+{
+ QHULL_ASSERT(q.hullDimension());
+ QHULL_ASSERT(coordinateCount2>=0);
+}//QhullPoints Qhull dim
+
+QhullPoints::
+QhullPoints(const Qhull &q, int pointDimension, countT coordinateCount2, coordT *c)
+: point_first(c)
+, point_end(c+coordinateCount2)
+, qh_qh(q.qh())
+, point_dimension(pointDimension)
+{
+ QHULL_ASSERT(pointDimension>=0);
+ QHULL_ASSERT(coordinateCount2>=0);
+}//QhullPoints Qhull dim coordT
+
+QhullPoints::
+QhullPoints(QhullQh *qqh, int pointDimension, countT coordinateCount2, coordT *c)
+: point_first(c)
+, point_end(c+coordinateCount2)
+, qh_qh(qqh)
+, point_dimension(pointDimension)
+{
+ QHULL_ASSERT(pointDimension>=0);
+ QHULL_ASSERT(coordinateCount2>=0);
+}//QhullPoints QhullQh dim coordT
+
+#//!\name Conversions
+// See qt-qhull.cpp for QList conversion
+
+#ifndef QHULL_NO_STL
+std::vector<QhullPoint> QhullPoints::
+toStdVector() const
+{
+ QhullPointsIterator i(*this);
+ std::vector<QhullPoint> vs;
+ while(i.hasNext()){
+ vs.push_back(i.next());
+ }
+ return vs;
+}//toStdVector
+#endif //QHULL_NO_STL
+
+#//!\name GetSet
+
+countT QhullPoints::
+extraCoordinatesCount() const
+{
+ if(point_dimension>0){
+ return (countT)((point_end-point_first)%(size_t)point_dimension);
+ }
+ return 0;
+}//extraCoordinatesCount
+
+//! QhullPoints is equal if the same address, or if the coordinates are identical
+//! Use QhullPoint.operator==() for DISTround equality
+bool QhullPoints::
+operator==(const QhullPoints &other) const
+{
+ if((point_end-point_first) != (other.point_end-other.point_first)){
+ return false;
+ }
+ if(point_dimension!=other.point_dimension){
+ return false;
+ }
+ if(point_first==other.point_first){
+ return true;
+ }
+ if(!qh_qh || qh_qh->hull_dim==0){
+ const coordT *c= point_first;
+ const coordT *c2= other.point_first;
+ while(c<point_end){
+ if(*c++!=*c2++){
+ return false;
+ }
+ }
+ }else{
+ const_iterator i= begin();
+ const_iterator i2= other.begin();
+ while(i<end()){
+ if(*i++!=*i2++){
+ return false;
+ }
+ }
+ }
+ return true;
+}//operator==
+
+//! Reset QhullPoints to QhullQh and its hullDimension()
+//! Does not free up old qh_qh
+void QhullPoints::
+resetQhullQh(QhullQh *qqh)
+{
+ qh_qh= qqh;
+ point_dimension= (qqh ? qqh->hull_dim : 0);
+ point_first= 0;
+ point_end= 0;
+}//resetQhullQh
+
+QhullPoint QhullPoints::
+value(countT idx) const
+{
+ QhullPoint p(qh_qh);
+ if(idx>=0 && idx<count()){
+ p.defineAs(point_dimension, point_first+idx*point_dimension);
+ }
+ return p;
+}//value
+
+QhullPoint QhullPoints::
+value(countT idx, QhullPoint &defaultValue) const
+{
+ QhullPoint p(qh_qh);
+ if(idx>=0 && idx<count()){
+ p.defineAs(point_dimension, point_first+idx*point_dimension);
+ }else{
+ p.defineAs(defaultValue);
+ }
+ return p;
+}//value
+
+#//!\name Methods
+
+bool QhullPoints::
+contains(const QhullPoint &t) const
+{
+ const_iterator i= begin();
+ while(i != end()){
+ if(*i==t){
+ return true;
+ }
+ i++;
+ }
+ return false;
+}//contains
+
+countT QhullPoints::
+count(const QhullPoint &t) const
+{
+ countT n= 0;
+ const_iterator i= begin();
+ while(i != end()){
+ if(*i==t){
+ ++n;
+ }
+ i++;
+ }
+ return n;
+}//count
+
+countT QhullPoints::
+indexOf(const coordT *pointCoordinates) const
+{
+ if(!includesCoordinates(pointCoordinates) || point_dimension==0){
+ return -1;
+ }
+ size_t offset= pointCoordinates-point_first;
+ countT idx= (countT)(offset/(size_t)point_dimension);
+ countT extra= (countT)(offset%(size_t)point_dimension);
+ if(extra!=0){
+ throw QhullError(10066, "Qhull error: coordinates %x are not at point boundary (extra %d at index %d)", extra, idx, 0.0, pointCoordinates);
+ }
+ return idx;
+}//indexOf coordT
+
+countT QhullPoints::
+indexOf(const coordT *pointCoordinates, int noThrow) const
+{
+ size_t extra= 0;
+ if(noThrow){
+ if(!includesCoordinates(pointCoordinates) || point_dimension==0){
+ return -1;
+ }
+ extra= (pointCoordinates-point_first)%(size_t)point_dimension;
+ }
+ return indexOf(pointCoordinates-extra);
+}//indexOf coordT noThrow
+
+countT QhullPoints::
+indexOf(const QhullPoint &t) const
+{
+ countT j=0;
+ const_iterator i= begin();
+ while(i!=end()){
+ if(*i==t){
+ return j;
+ }
+ ++i;
+ ++j;
+ }
+ return -1;
+}//indexOf
+
+countT QhullPoints::
+lastIndexOf(const QhullPoint &t) const
+{
+ countT j= count();
+ const_iterator i= end();
+ while(i != begin()){
+ --i;
+ --j;
+ if(*i==t){
+ return j;
+ }
+ }
+ return -1;
+}//lastIndexOf
+
+QhullPoints QhullPoints::
+mid(countT idx, countT length) const
+{
+ countT n= count();
+ if(idx<0 || idx>=n){
+ n= 0;
+ }else if(length<0 || idx+length>=n){
+ n -= idx;
+ }else{
+ n -= idx+length;
+ }
+ return QhullPoints(qh_qh, point_dimension, n*point_dimension, point_first+idx*point_dimension);
+}//mid
+
+#//!\name QhullPointsIterator
+
+bool QhullPointsIterator::
+findNext(const QhullPoint &p)
+{
+ while(i!=ps->constEnd()){
+ if(*i++ == p){
+ return true;
+ }
+ }
+ return false;
+}//findNext
+
+bool QhullPointsIterator::
+findPrevious(const QhullPoint &p)
+{
+ while(i!=ps->constBegin()){
+ if(*--i == p){
+ return true;
+ }
+ }
+ return false;
+}//findPrevious
+
+}//namespace orgQhull
+
+#//!\name Global functions
+
+using std::ostream;
+using orgQhull::QhullPoint;
+using orgQhull::QhullPoints;
+using orgQhull::QhullPointsIterator;
+
+ostream &
+operator<<(ostream &os, const QhullPoints &p)
+{
+ QhullPointsIterator i(p);
+ while(i.hasNext()){
+ os << i.next();
+ }
+ return os;
+}//operator<<QhullPoints
+
+ostream &
+operator<<(ostream &os, const QhullPoints::PrintPoints &pr)
+{
+ os << pr.point_message;
+ QhullPoints ps= *pr.points;
+ for(QhullPoints::iterator i=ps.begin(); i!=ps.end(); ++i){
+ QhullPoint p= *i;
+ if(pr.with_identifier){
+ os << p.printWithIdentifier("");
+ }else{
+ os << p.print("");
+ }
+ }
+ return os;
+}//<<PrintPoints
diff --git a/xs/src/qhull/src/libqhullcpp/QhullPoints.h b/xs/src/qhull/src/libqhullcpp/QhullPoints.h
new file mode 100644
index 000000000..ce169a159
--- /dev/null
+++ b/xs/src/qhull/src/libqhullcpp/QhullPoints.h
@@ -0,0 +1,266 @@
+/****************************************************************************
+**
+** Copyright (c) 2009-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/libqhullcpp/QhullPoints.h#5 $$Change: 2079 $
+** $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
+**
+****************************************************************************/
+
+#ifndef QHULLPOINTS_H
+#define QHULLPOINTS_H
+
+#include "libqhull_r/qhull_ra.h"
+#include "libqhullcpp/QhullPoint.h"
+
+#include <cstddef> // ptrdiff_t, size_t
+#include <ostream>
+
+namespace orgQhull {
+
+#//!\name Defined here
+ class QhullPoints; //!< One or more points Coordinate pointers with dimension and iterators
+ class QhullPointsIterator; //!< Java-style iterator
+
+//! QhullPoints are an array of QhullPoint as pointers into an array of coordinates.
+//! For Qhull/QhullQh, QhullPoints must use hull_dim. Can change QhullPoint to input_dim if needed for Delaunay input site
+class QhullPoints {
+
+private:
+#//!\name Fields
+ coordT * point_first; //!< First coordinate of an array of points of point_dimension
+ coordT * point_end; //!< End of point coordinates (end>=first). Trailing coordinates ignored
+ QhullQh * qh_qh; //!< Maybe initialized NULL to allow ownership by RboxPoints
+ //!< qh_qh used for QhullPoint() and qh_qh->hull_dim in constructor
+ int point_dimension; //!< Dimension, >=0
+
+public:
+#//!\name Subtypes
+ class const_iterator;
+ class iterator;
+ typedef QhullPoints::const_iterator ConstIterator;
+ typedef QhullPoints::iterator Iterator;
+
+#//!\name Construct
+ //! QhullPoint, PointCoordinates, and QhullPoints have similar constructors
+ //! If Qhull/QhullQh is not initialized, then QhullPoints.dimension() is zero unless explicitly set
+ //! Cannot define QhullPoints(int pointDimension) since it is ambiguous with QhullPoints(QhullQh *qqh)
+ QhullPoints() : point_first(0), point_end(0), qh_qh(0), point_dimension(0) { }
+ QhullPoints(int pointDimension, countT coordinateCount2, coordT *c) : point_first(c), point_end(c+coordinateCount2), qh_qh(0), point_dimension(pointDimension) { QHULL_ASSERT(pointDimension>=0); }
+ explicit QhullPoints(const Qhull &q);
+ QhullPoints(const Qhull &q, countT coordinateCount2, coordT *c);
+ QhullPoints(const Qhull &q, int pointDimension, countT coordinateCount2, coordT *c);
+ explicit QhullPoints(QhullQh *qqh) : point_first(0), point_end(0), qh_qh(qqh), point_dimension(qqh ? qqh->hull_dim : 0) { }
+ QhullPoints(QhullQh *qqh, countT coordinateCount2, coordT *c) : point_first(c), point_end(c+coordinateCount2), qh_qh(qqh), point_dimension(qqh ? qqh->hull_dim : 0) { QHULL_ASSERT(qqh && qqh->hull_dim>0); }
+ QhullPoints(QhullQh *qqh, int pointDimension, countT coordinateCount2, coordT *c);
+ //! Copy constructor copies pointers but not contents. Needed for return by value and parameter passing.
+ QhullPoints(const QhullPoints &other) : point_first(other.point_first), point_end(other.point_end), qh_qh(other.qh_qh), point_dimension(other.point_dimension) {}
+ QhullPoints & operator=(const QhullPoints &other) { point_first= other.point_first; point_end= other.point_end; qh_qh= other.qh_qh; point_dimension= other.point_dimension; return *this; }
+ ~QhullPoints() {}
+
+public:
+
+#//!\name Conversion
+
+#ifndef QHULL_NO_STL
+ std::vector<QhullPoint> toStdVector() const;
+#endif //QHULL_NO_STL
+#ifdef QHULL_USES_QT
+ QList<QhullPoint> toQList() const;
+#endif //QHULL_USES_QT
+
+#//!\name GetSet
+ // Constructs QhullPoint. Cannot return reference.
+ const QhullPoint at(countT idx) const { /* point_first==0 caught by point_end assert */ coordT *p= point_first+idx*point_dimension; QHULL_ASSERT(p<point_end); return QhullPoint(qh_qh, point_dimension, p); }
+ // Constructs QhullPoint. Cannot return reference.
+ const QhullPoint back() const { return last(); }
+ QhullPoint back() { return last(); }
+ ConstIterator begin() const { return ConstIterator(*this); }
+ Iterator begin() { return Iterator(*this); }
+ ConstIterator constBegin() const { return ConstIterator(*this); }
+ const coordT * constData() const { return point_first; }
+ ConstIterator constEnd() const { return ConstIterator(qh_qh, point_dimension, point_end); }
+ coordT * coordinates() const { return point_first; }
+ countT coordinateCount() const { return (countT)(point_end-point_first); } // WARN64
+ countT count() const { return (countT)size(); } // WARN64
+ const coordT * data() const { return point_first; }
+ coordT * data() { return point_first; }
+ void defineAs(int pointDimension, countT coordinatesCount, coordT *c) { QHULL_ASSERT(pointDimension>=0 && coordinatesCount>=0 && c!=0); point_first= c; point_end= c+coordinatesCount; point_dimension= pointDimension; }
+ void defineAs(countT coordinatesCount, coordT *c) { QHULL_ASSERT((point_dimension>0 && coordinatesCount>=0 && c!=0) || (c==0 && coordinatesCount==0)); point_first= c; point_end= c+coordinatesCount; }
+ void defineAs(const QhullPoints &other) { point_first= other.point_first; point_end= other.point_end; qh_qh= other.qh_qh; point_dimension= other.point_dimension; }
+ int dimension() const { return point_dimension; }
+ ConstIterator end() const { return ConstIterator(qh_qh, point_dimension, point_end); }
+ Iterator end() { return Iterator(qh_qh, point_dimension, point_end); }
+ coordT * extraCoordinates() const { return extraCoordinatesCount() ? (point_end-extraCoordinatesCount()) : 0; }
+ countT extraCoordinatesCount() const; // WARN64
+ // Constructs QhullPoint. Cannot return reference.
+ const QhullPoint first() const { return QhullPoint(qh_qh, point_dimension, point_first); }
+ QhullPoint first() { return QhullPoint(qh_qh, point_dimension, point_first); }
+ // Constructs QhullPoint. Cannot return reference.
+ const QhullPoint front() const { return first(); }
+ QhullPoint front() { return first(); }
+ bool includesCoordinates(const coordT *c) const { return c>=point_first && c<point_end; }
+ bool isEmpty() const { return (point_end==point_first || point_dimension==0); }
+ // Constructs QhullPoint. Cannot return reference.
+ const QhullPoint last() const { QHULL_ASSERT(point_first!=0); return QhullPoint(qh_qh, point_dimension, point_end - point_dimension); }
+ QhullPoint last() { QHULL_ASSERT(point_first!=0); return QhullPoint(qh_qh, point_dimension, point_end - point_dimension); }
+ bool operator==(const QhullPoints &other) const;
+ bool operator!=(const QhullPoints &other) const { return ! operator==(other); }
+ QhullPoint operator[](countT idx) const { return at(idx); }
+ QhullQh * qh() const { return qh_qh; }
+ void resetQhullQh(QhullQh *qqh);
+ void setDimension(int d) { point_dimension= d; }
+ size_t size() const { return point_dimension ? (point_end-point_first)/point_dimension : 0; }
+ QhullPoint value(countT idx) const;
+ QhullPoint value(countT idx, QhullPoint &defaultValue) const;
+
+#//!\name Methods
+ bool contains(const QhullPoint &t) const;
+ countT count(const QhullPoint &t) const;
+ countT indexOf(const coordT *pointCoordinates) const;
+ countT indexOf(const coordT *pointCoordinates, int noThrow) const;
+ countT indexOf(const QhullPoint &t) const;
+ countT lastIndexOf(const QhullPoint &t) const;
+ //! Returns a subset of the points, not a copy
+ QhullPoints mid(countT idx, countT length= -1) const;
+
+#//!\name QhullPoints::iterator
+ // Modeled on qlist.h w/o QT_STRICT_ITERATORS
+ // before const_iterator for conversion with comparison operators
+ // See: QhullSet.h
+ class iterator : public QhullPoint {
+
+ public:
+ typedef std::random_access_iterator_tag iterator_category;
+ typedef QhullPoint value_type;
+ typedef value_type * pointer;
+ typedef value_type & reference;
+ typedef ptrdiff_t difference_type;
+
+ explicit iterator(const QhullPoints &ps) : QhullPoint(ps.qh(), ps.dimension(), ps.coordinates()) {}
+ iterator(const int pointDimension, coordT *c): QhullPoint(pointDimension, c) {}
+ iterator(const Qhull &q, coordT *c): QhullPoint(q, c) {}
+ iterator(const Qhull &q, int pointDimension, coordT *c): QhullPoint(q, pointDimension, c) {}
+ iterator(QhullQh *qqh, coordT *c): QhullPoint(qqh, c) {}
+ iterator(QhullQh *qqh, int pointDimension, coordT *c): QhullPoint(qqh, pointDimension, c) {}
+ iterator(const iterator &other): QhullPoint(*other) {}
+ iterator & operator=(const iterator &other) { defineAs( const_cast<iterator &>(other)); return *this; }
+
+ // Need 'const QhullPoint' to maintain const
+ const QhullPoint & operator*() const { return *this; }
+ QhullPoint & operator*() { return *this; }
+ const QhullPoint * operator->() const { return this; }
+ QhullPoint * operator->() { return this; }
+ // value instead of reference since advancePoint() modifies self
+ QhullPoint operator[](countT idx) const { QhullPoint result= *this; result.advancePoint(idx); return result; }
+ bool operator==(const iterator &o) const { QHULL_ASSERT(qh_qh==o.qh_qh); return (point_coordinates==o.point_coordinates && point_dimension==o.point_dimension); }
+ bool operator!=(const iterator &o) const { return !operator==(o); }
+ bool operator<(const iterator &o) const { QHULL_ASSERT(qh_qh==o.qh_qh); return point_coordinates < o.point_coordinates; }
+ bool operator<=(const iterator &o) const { QHULL_ASSERT(qh_qh==o.qh_qh); return point_coordinates <= o.point_coordinates; }
+ bool operator>(const iterator &o) const { QHULL_ASSERT(qh_qh==o.qh_qh); return point_coordinates > o.point_coordinates; }
+ bool operator>=(const iterator &o) const { QHULL_ASSERT(qh_qh==o.qh_qh); return point_coordinates >= o.point_coordinates; }
+ // reinterpret_cast to break circular dependency
+ bool operator==(const QhullPoints::const_iterator &o) const { QHULL_ASSERT(qh_qh==reinterpret_cast<const iterator &>(o).qh_qh); return (point_coordinates==reinterpret_cast<const iterator &>(o).point_coordinates && point_dimension==reinterpret_cast<const iterator &>(o).point_dimension); }
+ bool operator!=(const QhullPoints::const_iterator &o) const { return !operator==(reinterpret_cast<const iterator &>(o)); }
+ bool operator<(const QhullPoints::const_iterator &o) const { QHULL_ASSERT(qh_qh==reinterpret_cast<const iterator &>(o).qh_qh); return point_coordinates < reinterpret_cast<const iterator &>(o).point_coordinates; }
+ bool operator<=(const QhullPoints::const_iterator &o) const { QHULL_ASSERT(qh_qh==reinterpret_cast<const iterator &>(o).qh_qh); return point_coordinates <= reinterpret_cast<const iterator &>(o).point_coordinates; }
+ bool operator>(const QhullPoints::const_iterator &o) const { QHULL_ASSERT(qh_qh==reinterpret_cast<const iterator &>(o).qh_qh); return point_coordinates > reinterpret_cast<const iterator &>(o).point_coordinates; }
+ bool operator>=(const QhullPoints::const_iterator &o) const { QHULL_ASSERT(qh_qh==reinterpret_cast<const iterator &>(o).qh_qh); return point_coordinates >= reinterpret_cast<const iterator &>(o).point_coordinates; }
+ iterator & operator++() { advancePoint(1); return *this; }
+ iterator operator++(int) { iterator n= *this; operator++(); return iterator(n); }
+ iterator & operator--() { advancePoint(-1); return *this; }
+ iterator operator--(int) { iterator n= *this; operator--(); return iterator(n); }
+ iterator & operator+=(countT idx) { advancePoint(idx); return *this; }
+ iterator & operator-=(countT idx) { advancePoint(-idx); return *this; }
+ iterator operator+(countT idx) const { iterator n= *this; n.advancePoint(idx); return n; }
+ iterator operator-(countT idx) const { iterator n= *this; n.advancePoint(-idx); return n; }
+ difference_type operator-(iterator o) const { QHULL_ASSERT(qh_qh==o.qh_qh && point_dimension==o.point_dimension); return (point_dimension ? (point_coordinates-o.point_coordinates)/point_dimension : 0); }
+ };//QhullPoints::iterator
+
+#//!\name QhullPoints::const_iterator
+ //!\todo FIXUP QH11018 const_iterator same as iterator. SHould have a common definition
+ class const_iterator : public QhullPoint {
+
+ public:
+ typedef std::random_access_iterator_tag iterator_category;
+ typedef QhullPoint value_type;
+ typedef const value_type * pointer;
+ typedef const value_type & reference;
+ typedef ptrdiff_t difference_type;
+
+ const_iterator(const QhullPoints::iterator &o) : QhullPoint(*o) {}
+ explicit const_iterator(const QhullPoints &ps) : QhullPoint(ps.qh(), ps.dimension(), ps.coordinates()) {}
+ const_iterator(const int pointDimension, coordT *c): QhullPoint(pointDimension, c) {}
+ const_iterator(const Qhull &q, coordT *c): QhullPoint(q, c) {}
+ const_iterator(const Qhull &q, int pointDimension, coordT *c): QhullPoint(q, pointDimension, c) {}
+ const_iterator(QhullQh *qqh, coordT *c): QhullPoint(qqh, c) {}
+ const_iterator(QhullQh *qqh, int pointDimension, coordT *c): QhullPoint(qqh, pointDimension, c) {}
+ const_iterator(const const_iterator &o) : QhullPoint(*o) {}
+ const_iterator &operator=(const const_iterator &o) { defineAs(const_cast<const_iterator &>(o)); return *this; }
+
+ // value/non-const since advancePoint(1), etc. modifies self
+ const QhullPoint & operator*() const { return *this; }
+ const QhullPoint * operator->() const { return this; }
+ // value instead of reference since advancePoint() modifies self
+ const QhullPoint operator[](countT idx) const { QhullPoint n= *this; n.advancePoint(idx); return n; }
+ bool operator==(const const_iterator &o) const { QHULL_ASSERT(qh_qh==o.qh_qh); return (point_coordinates==o.point_coordinates && point_dimension==o.point_dimension); }
+ bool operator!=(const const_iterator &o) const { return ! operator==(o); }
+ bool operator<(const const_iterator &o) const { QHULL_ASSERT(qh_qh==o.qh_qh); return point_coordinates < o.point_coordinates; }
+ bool operator<=(const const_iterator &o) const { QHULL_ASSERT(qh_qh==o.qh_qh); return point_coordinates <= o.point_coordinates; }
+ bool operator>(const const_iterator &o) const { QHULL_ASSERT(qh_qh==o.qh_qh); return point_coordinates > o.point_coordinates; }
+ bool operator>=(const const_iterator &o) const { QHULL_ASSERT(qh_qh==o.qh_qh); return point_coordinates >= o.point_coordinates; }
+ const_iterator &operator++() { advancePoint(1); return *this; }
+ const_iterator operator++(int) { const_iterator n= *this; operator++(); return const_iterator(n); }
+ const_iterator &operator--() { advancePoint(-1); return *this; }
+ const_iterator operator--(int) { const_iterator n= *this; operator--(); return const_iterator(n); }
+ const_iterator &operator+=(countT idx) { advancePoint(idx); return *this; }
+ const_iterator &operator-=(countT idx) { advancePoint(-idx); return *this; }
+ const_iterator operator+(countT idx) const { const_iterator n= *this; n.advancePoint(idx); return n; }
+ const_iterator operator-(countT idx) const { const_iterator n= *this; n.advancePoint(-idx); return n; }
+ difference_type operator-(const_iterator o) const { QHULL_ASSERT(qh_qh==o.qh_qh && point_dimension==o.point_dimension); return (point_dimension ? (point_coordinates-o.point_coordinates)/point_dimension : 0); }
+ };//QhullPoints::const_iterator
+
+#//!\name IO
+ struct PrintPoints{
+ const QhullPoints *points;
+ const char * point_message;
+ bool with_identifier;
+ PrintPoints(const char *message, bool withIdentifier, const QhullPoints &ps) : points(&ps), point_message(message), with_identifier(withIdentifier) {}
+ };//PrintPoints
+ PrintPoints print(const char *message) const { return PrintPoints(message, false, *this); }
+ PrintPoints printWithIdentifier(const char *message) const { return PrintPoints(message, true, *this); }
+};//QhullPoints
+
+// Instead of QHULL_DECLARE_SEQUENTIAL_ITERATOR because next(),etc would return a reference to a temporary
+class QhullPointsIterator
+{
+ typedef QhullPoints::const_iterator const_iterator;
+
+#//!\name Fields
+private:
+ const QhullPoints *ps;
+ const_iterator i;
+
+public:
+ QhullPointsIterator(const QhullPoints &other) : ps(&other), i(ps->constBegin()) {}
+ QhullPointsIterator &operator=(const QhullPoints &other) { ps = &other; i = ps->constBegin(); return *this; }
+
+ bool findNext(const QhullPoint &t);
+ bool findPrevious(const QhullPoint &t);
+ bool hasNext() const { return i != ps->constEnd(); }
+ bool hasPrevious() const { return i != ps->constBegin(); }
+ QhullPoint next() { return *i++; }
+ QhullPoint peekNext() const { return *i; }
+ QhullPoint peekPrevious() const { const_iterator p = i; return *--p; }
+ QhullPoint previous() { return *--i; }
+ void toBack() { i = ps->constEnd(); }
+ void toFront() { i = ps->constBegin(); }
+};//QhullPointsIterator
+
+}//namespace orgQhull
+
+#//!\name Global
+
+std::ostream & operator<<(std::ostream &os, const orgQhull::QhullPoints &p);
+std::ostream & operator<<(std::ostream &os, const orgQhull::QhullPoints::PrintPoints &pr);
+
+#endif // QHULLPOINTS_H
diff --git a/xs/src/qhull/src/libqhullcpp/QhullQh.cpp b/xs/src/qhull/src/libqhullcpp/QhullQh.cpp
new file mode 100644
index 000000000..363533700
--- /dev/null
+++ b/xs/src/qhull/src/libqhullcpp/QhullQh.cpp
@@ -0,0 +1,237 @@
+/****************************************************************************
+**
+** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/libqhullcpp/QhullQh.cpp#5 $$Change: 2066 $
+** $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
+**
+****************************************************************************/
+
+#//! QhullQh -- Qhull's global data structure, qhT, as a C++ class
+
+
+#include "libqhullcpp/QhullQh.h"
+
+#include "libqhullcpp/QhullError.h"
+#include "libqhullcpp/QhullStat.h"
+
+#include <sstream>
+#include <iostream>
+
+#include <stdarg.h>
+
+using std::cerr;
+using std::string;
+using std::vector;
+using std::ostream;
+
+#ifdef _MSC_VER // Microsoft Visual C++ -- warning level 4
+#pragma warning( disable : 4611) // interaction between '_setjmp' and C++ object destruction is non-portable
+#pragma warning( disable : 4996) // function was declared deprecated(strcpy, localtime, etc.)
+#endif
+
+namespace orgQhull {
+
+#//!\name Global variables
+const double QhullQh::
+default_factor_epsilon= 1.0;
+
+#//!\name Constructor, destructor, etc.
+
+//! Derived from qh_new_qhull[user.c]
+QhullQh::
+QhullQh()
+: qhull_status(qh_ERRnone)
+, qhull_message()
+, error_stream(0)
+, output_stream(0)
+, factor_epsilon(QhullQh::default_factor_epsilon)
+, use_output_stream(false)
+{
+ // NOerrors: TRY_QHULL_ not needed since these routines do not call qh_errexit()
+ qh_meminit(this, NULL);
+ qh_initstatistics(this);
+ qh_initqhull_start2(this, NULL, NULL, qh_FILEstderr); // Initialize qhT
+ this->ISqhullQh= True;
+}//QhullQh
+
+QhullQh::
+~QhullQh()
+{
+ checkAndFreeQhullMemory();
+}//~QhullQh
+
+#//!\name Methods
+
+//! Check memory for internal consistency
+//! Free global memory used by qh_initbuild and qh_buildhull
+//! Zero the qhT data structure, except for memory (qhmemT) and statistics (qhstatT)
+//! Check and free short memory (e.g., facetT)
+//! Zero the qhmemT data structure
+void QhullQh::
+checkAndFreeQhullMemory()
+{
+#ifdef qh_NOmem
+ qh_freeqhull(this, qh_ALL);
+#else
+ qh_memcheck(this);
+ qh_freeqhull(this, !qh_ALL);
+ countT curlong;
+ countT totlong;
+ qh_memfreeshort(this, &curlong, &totlong);
+ if (curlong || totlong)
+ throw QhullError(10026, "Qhull error: qhull did not free %d bytes of long memory (%d pieces).", totlong, curlong);
+#endif
+}//checkAndFreeQhullMemory
+
+#//!\name Messaging
+
+void QhullQh::
+appendQhullMessage(const string &s)
+{
+ if(output_stream && use_output_stream && this->USEstdout){
+ *output_stream << s;
+ }else if(error_stream){
+ *error_stream << s;
+ }else{
+ qhull_message += s;
+ }
+}//appendQhullMessage
+
+//! clearQhullMessage does not throw errors (~Qhull)
+void QhullQh::
+clearQhullMessage()
+{
+ qhull_status= qh_ERRnone;
+ qhull_message.clear();
+ RoadError::clearGlobalLog();
+}//clearQhullMessage
+
+//! hasQhullMessage does not throw errors (~Qhull)
+bool QhullQh::
+hasQhullMessage() const
+{
+ return (!qhull_message.empty() || qhull_status!=qh_ERRnone);
+ //FIXUP QH11006 -- inconsistent usage with Rbox. hasRboxMessage just tests rbox_status. No appendRboxMessage()
+}
+
+void QhullQh::
+maybeThrowQhullMessage(int exitCode)
+{
+ if(!NOerrexit){
+ if(qhull_message.size()>0){
+ qhull_message.append("\n");
+ }
+ if(exitCode || qhull_status==qh_ERRnone){
+ qhull_status= 10073;
+ }else{
+ qhull_message.append("QH10073: ");
+ }
+ qhull_message.append("Cannot call maybeThrowQhullMessage() from QH_TRY_(). Or missing 'qh->NOerrexit=true;' after QH_TRY_(){...}.");
+ }
+ if(qhull_status==qh_ERRnone){
+ qhull_status= exitCode;
+ }
+ if(qhull_status!=qh_ERRnone){
+ QhullError e(qhull_status, qhull_message);
+ clearQhullMessage();
+ throw e; // FIXUP QH11007: copy constructor is expensive if logging
+ }
+}//maybeThrowQhullMessage
+
+void QhullQh::
+maybeThrowQhullMessage(int exitCode, int noThrow) throw()
+{
+ QHULL_UNUSED(noThrow);
+
+ if(qhull_status==qh_ERRnone){
+ qhull_status= exitCode;
+ }
+ if(qhull_status!=qh_ERRnone){
+ QhullError e(qhull_status, qhull_message);
+ e.logErrorLastResort();
+ }
+}//maybeThrowQhullMessage
+
+//! qhullMessage does not throw errors (~Qhull)
+std::string QhullQh::
+qhullMessage() const
+{
+ if(qhull_message.empty() && qhull_status!=qh_ERRnone){
+ return "qhull: no message for error. Check cerr or error stream\n";
+ }else{
+ return qhull_message;
+ }
+}//qhullMessage
+
+int QhullQh::
+qhullStatus() const
+{
+ return qhull_status;
+}//qhullStatus
+
+void QhullQh::
+setErrorStream(ostream *os)
+{
+ error_stream= os;
+}//setErrorStream
+
+//! Updates use_output_stream
+void QhullQh::
+setOutputStream(ostream *os)
+{
+ output_stream= os;
+ use_output_stream= (os!=0);
+}//setOutputStream
+
+}//namespace orgQhull
+
+/*-<a href="qh_qh-user.htm#TOC"
+ >-------------------------------</a><a name="qh_fprintf">-</a>
+
+ qh_fprintf(qhT *qh, fp, msgcode, format, list of args )
+ replaces qh_fprintf() in userprintf_r.c
+
+notes:
+ only called from libqhull
+ same as fprintf() and RboxPoints.qh_fprintf_rbox()
+ fgets() is not trapped like fprintf()
+ Do not throw errors from here. Use qh_errexit;
+*/
+extern "C"
+void qh_fprintf(qhT *qh, FILE *fp, int msgcode, const char *fmt, ... ) {
+ va_list args;
+
+ using namespace orgQhull;
+
+ if(!qh->ISqhullQh){
+ qh_fprintf_stderr(10025, "Qhull error: qh_fprintf called from a Qhull instance without QhullQh defined\n");
+ qh_exit(10025);
+ }
+ QhullQh *qhullQh= static_cast<QhullQh *>(qh);
+ va_start(args, fmt);
+ if(msgcode<MSG_OUTPUT || fp == qh_FILEstderr){
+ if(msgcode>=MSG_ERROR && msgcode<MSG_WARNING){
+ if(qhullQh->qhull_status<MSG_ERROR || qhullQh->qhull_status>=MSG_WARNING){
+ qhullQh->qhull_status= msgcode;
+ }
+ }
+ char newMessage[MSG_MAXLEN];
+ // RoadError will add the message tag
+ vsnprintf(newMessage, sizeof(newMessage), fmt, args);
+ qhullQh->appendQhullMessage(newMessage);
+ va_end(args);
+ return;
+ }
+ if(qhullQh->output_stream && qhullQh->use_output_stream){
+ char newMessage[MSG_MAXLEN];
+ vsnprintf(newMessage, sizeof(newMessage), fmt, args);
+ *qhullQh->output_stream << newMessage;
+ va_end(args);
+ return;
+ }
+ // FIXUP QH11008: how do users trap messages and handle input? A callback?
+ char newMessage[MSG_MAXLEN];
+ vsnprintf(newMessage, sizeof(newMessage), fmt, args);
+ qhullQh->appendQhullMessage(newMessage);
+ va_end(args);
+} /* qh_fprintf */
diff --git a/xs/src/qhull/src/libqhullcpp/QhullQh.h b/xs/src/qhull/src/libqhullcpp/QhullQh.h
new file mode 100644
index 000000000..c3b277ff0
--- /dev/null
+++ b/xs/src/qhull/src/libqhullcpp/QhullQh.h
@@ -0,0 +1,110 @@
+/****************************************************************************
+**
+** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/libqhullcpp/QhullQh.h#2 $$Change: 2079 $
+** $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
+**
+****************************************************************************/
+
+#ifndef QHULLQH_H
+#define QHULLQH_H
+
+#include "libqhull_r/qhull_ra.h"
+
+#include <string>
+
+#ifdef _MSC_VER // Microsoft Visual C++ -- warning level 4
+#pragma warning( disable : 4611) /* interaction between '_setjmp' and C++ object destruction is non-portable */
+/* setjmp should not be implemented with 'catch' */
+#endif
+
+//! Use QH_TRY_ or QH_TRY_NOTHROW_ to call a libqhull_r routine that may invoke qh_errexit()
+//! QH_TRY_(qh){...} qh->NOerrexit=true;
+//! No object creation -- longjmp() skips object destructors
+//! To test for error when done -- qh->maybeThrowQhullMessage(QH_TRY_status);
+//! Use the same compiler for QH_TRY_, libqhullcpp, and libqhull_r. setjmp() is not portable between compilers.
+
+#define QH_TRY_ERROR 10071
+
+#define QH_TRY_(qh) \
+ int QH_TRY_status; \
+ if(qh->NOerrexit){ \
+ qh->NOerrexit= False; \
+ QH_TRY_status= setjmp(qh->errexit); \
+ }else{ \
+ throw QhullError(QH_TRY_ERROR, "Cannot invoke QH_TRY_() from inside a QH_TRY_. Or missing 'qh->NOerrexit=true' after previously called QH_TRY_(qh){...}"); \
+ } \
+ if(!QH_TRY_status)
+
+#define QH_TRY_NO_THROW_(qh) \
+ int QH_TRY_status; \
+ if(qh->NOerrexit){ \
+ qh->NOerrexit= False; \
+ QH_TRY_status= setjmp(qh->errexit); \
+ }else{ \
+ QH_TRY_status= QH_TRY_ERROR; \
+ } \
+ if(!QH_TRY_status)
+
+namespace orgQhull {
+
+#//!\name Defined here
+ //! QhullQh -- Qhull's global data structure, qhT, as a C++ class
+ class QhullQh;
+
+//! POD type equivalent to qhT. No virtual members
+class QhullQh : public qhT {
+
+#//!\name Constants
+
+#//!\name Fields
+private:
+ int qhull_status; //!< qh_ERRnone if valid
+ std::string qhull_message; //!< Returned messages from libqhull_r
+ std::ostream * error_stream; //!< overrides errorMessage, use appendQhullMessage()
+ std::ostream * output_stream; //!< send output to stream
+ double factor_epsilon; //!< Factor to increase ANGLEround and DISTround for hyperplane equality
+ bool use_output_stream; //!< True if using output_stream
+
+ friend void ::qh_fprintf(qhT *qh, FILE *fp, int msgcode, const char *fmt, ... );
+
+ static const double default_factor_epsilon; //!< Default factor_epsilon is 1.0, never updated
+
+#//!\name Constructors
+public:
+ QhullQh();
+ ~QhullQh();
+private:
+ //!disable copy constructor and assignment
+ QhullQh(const QhullQh &);
+ QhullQh & operator=(const QhullQh &);
+public:
+
+#//!\name GetSet
+ double factorEpsilon() const { return factor_epsilon; }
+ void setFactorEpsilon(double a) { factor_epsilon= a; }
+ void disableOutputStream() { use_output_stream= false; }
+ void enableOutputStream() { use_output_stream= true; }
+
+#//!\name Messaging
+ void appendQhullMessage(const std::string &s);
+ void clearQhullMessage();
+ std::string qhullMessage() const;
+ bool hasOutputStream() const { return use_output_stream; }
+ bool hasQhullMessage() const;
+ void maybeThrowQhullMessage(int exitCode);
+ void maybeThrowQhullMessage(int exitCode, int noThrow) throw();
+ int qhullStatus() const;
+ void setErrorStream(std::ostream *os);
+ void setOutputStream(std::ostream *os);
+
+#//!\name Methods
+ double angleEpsilon() const { return this->ANGLEround*factor_epsilon; } //!< Epsilon for hyperplane angle equality
+ void checkAndFreeQhullMemory();
+ double distanceEpsilon() const { return this->DISTround*factor_epsilon; } //!< Epsilon for distance to hyperplane
+
+};//class QhullQh
+
+}//namespace orgQhull
+
+#endif // QHULLQH_H
diff --git a/xs/src/qhull/src/libqhullcpp/QhullRidge.cpp b/xs/src/qhull/src/libqhullcpp/QhullRidge.cpp
new file mode 100644
index 000000000..7a0181280
--- /dev/null
+++ b/xs/src/qhull/src/libqhullcpp/QhullRidge.cpp
@@ -0,0 +1,124 @@
+/****************************************************************************
+**
+** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/libqhullcpp/QhullRidge.cpp#3 $$Change: 2066 $
+** $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
+**
+****************************************************************************/
+
+#//! QhullRidge -- Qhull's ridge structure, ridgeT, as a C++ class
+
+#include "libqhullcpp/QhullRidge.h"
+
+#include "libqhullcpp/QhullSets.h"
+#include "libqhullcpp/QhullVertex.h"
+#include "libqhullcpp/Qhull.h"
+
+#ifdef _MSC_VER // Microsoft Visual C++ -- warning level 4
+#pragma warning( disable : 4611) // interaction between '_setjmp' and C++ object destruction is non-portable
+#pragma warning( disable : 4996) // function was declared deprecated(strcpy, localtime, etc.)
+#endif
+
+namespace orgQhull {
+
+#//!\name Class objects
+ridgeT QhullRidge::
+s_empty_ridge= {0,0,0,0,0,
+ 0,0};
+
+#//!\name Constructors
+
+QhullRidge::QhullRidge(const Qhull &q)
+: qh_ridge(&s_empty_ridge)
+, qh_qh(q.qh())
+{
+}//Default
+
+QhullRidge::QhullRidge(const Qhull &q, ridgeT *r)
+: qh_ridge(r ? r : &s_empty_ridge)
+, qh_qh(q.qh())
+{
+}//ridgeT
+
+#//!\name foreach
+
+//! Return True if nextRidge3d
+//! Simplicial facets may have incomplete ridgeSets
+//! Does not use qh_errexit()
+bool QhullRidge::
+hasNextRidge3d(const QhullFacet &f) const
+{
+ if(!qh_qh){
+ return false;
+ }
+ vertexT *v= 0;
+ // Does not call qh_errexit(), TRY_QHULL_ not needed
+ ridgeT *ridge= qh_nextridge3d(getRidgeT(), f.getFacetT(), &v);
+ return (ridge!=0);
+}//hasNextRidge3d
+
+//! Return next ridge and optional vertex for a 3d facet and ridge
+//! Does not use qh_errexit()
+QhullRidge QhullRidge::
+nextRidge3d(const QhullFacet &f, QhullVertex *nextVertex) const
+{
+ vertexT *v= 0;
+ ridgeT *ridge= 0;
+ if(qh_qh){
+ // Does not call qh_errexit(), TRY_QHULL_ not needed
+ ridge= qh_nextridge3d(getRidgeT(), f.getFacetT(), &v);
+ if(!ridge){
+ throw QhullError(10030, "Qhull error nextRidge3d: missing next ridge for facet %d ridge %d. Does facet contain ridge?", f.id(), id());
+ }
+ }
+ if(nextVertex!=0){
+ *nextVertex= QhullVertex(qh_qh, v);
+ }
+ return QhullRidge(qh_qh, ridge);
+}//nextRidge3d
+
+}//namespace orgQhull
+
+#//!\name Global functions
+
+using std::endl;
+using std::ostream;
+using orgQhull::QhullRidge;
+using orgQhull::QhullVertex;
+
+ostream &
+operator<<(ostream &os, const QhullRidge &r)
+{
+ os << r.print("");
+ return os;
+}//<< QhullRidge
+
+//! Duplicate of qh_printridge [io_r.c]
+ostream &
+operator<<(ostream &os, const QhullRidge::PrintRidge &pr)
+{
+ if(*pr.print_message){
+ os << pr.print_message << " ";
+ }else{
+ os << " - ";
+ }
+ QhullRidge r= *pr.ridge;
+ os << "r" << r.id();
+ if(r.getRidgeT()->tested){
+ os << " tested";
+ }
+ if(r.getRidgeT()->nonconvex){
+ os << " nonconvex";
+ }
+ os << endl;
+ os << r.vertices().print(" vertices:");
+ if(r.getRidgeT()->top && r.getRidgeT()->bottom){
+ os << " between f" << r.topFacet().id() << " and f" << r.bottomFacet().id() << endl;
+ }else if(r.getRidgeT()->top){
+ os << " top f" << r.topFacet().id() << endl;
+ }else if(r.getRidgeT()->bottom){
+ os << " bottom f" << r.bottomFacet().id() << endl;
+ }
+
+ return os;
+}//<< PrintRidge
diff --git a/xs/src/qhull/src/libqhullcpp/QhullRidge.h b/xs/src/qhull/src/libqhullcpp/QhullRidge.h
new file mode 100644
index 000000000..924340fb0
--- /dev/null
+++ b/xs/src/qhull/src/libqhullcpp/QhullRidge.h
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/libqhullcpp/QhullRidge.h#4 $$Change: 2079 $
+** $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
+**
+****************************************************************************/
+
+#ifndef QHULLRIDGE_H
+#define QHULLRIDGE_H
+
+#include "libqhull_r/qhull_ra.h"
+#include "libqhullcpp/QhullSet.h"
+#include "libqhullcpp/QhullVertex.h"
+#include "libqhullcpp/QhullVertexSet.h"
+#include "libqhullcpp/QhullFacet.h"
+
+#include <ostream>
+
+namespace orgQhull {
+
+#//!\name Used here
+ class Qhull;
+ class QhullVertex;
+ class QhullVertexSet;
+ class QhullFacet;
+
+#//!\name Defined here
+ //! QhullRidge -- Qhull's ridge structure, ridgeT [libqhull.h], as a C++ class
+ class QhullRidge;
+ typedef QhullSet<QhullRidge> QhullRidgeSet;
+ typedef QhullSetIterator<QhullRidge> QhullRidgeSetIterator;
+ // see QhullSets.h for QhullRidgeSet and QhullRidgeSetIterator -- avoids circular references
+
+/************************
+a ridge is hull_dim-1 simplex between two neighboring facets. If the
+facets are non-simplicial, there may be more than one ridge between
+two facets. E.G. a 4-d hypercube has two triangles between each pair
+of neighboring facets.
+
+topological information:
+ vertices a set of vertices
+ top,bottom neighboring facets with orientation
+
+geometric information:
+ tested True if ridge is clearly convex
+ nonconvex True if ridge is non-convex
+*/
+
+class QhullRidge {
+
+#//!\name Defined here
+public:
+ typedef ridgeT * base_type; // for QhullRidgeSet
+
+#//!\name Fields
+private:
+ ridgeT * qh_ridge; //!< Corresponding ridgeT, never 0
+ QhullQh * qh_qh; //!< QhullQh/qhT for ridgeT, may be 0
+
+#//!\name Class objects
+ static ridgeT s_empty_ridge;
+
+public:
+#//!\name Constants
+
+#//!\name Constructors
+ QhullRidge() : qh_ridge(&s_empty_ridge), qh_qh(0) {}
+ explicit QhullRidge(const Qhull &q);
+ QhullRidge(const Qhull &q, ridgeT *r);
+ explicit QhullRidge(QhullQh *qqh) : qh_ridge(&s_empty_ridge), qh_qh(qqh) {}
+ QhullRidge(QhullQh *qqh, ridgeT *r) : qh_ridge(r ? r : &s_empty_ridge), qh_qh(qqh) {}
+ // Creates an alias. Does not copy QhullRidge. Needed for return by value and parameter passing
+ QhullRidge(const QhullRidge &other) : qh_ridge(other.qh_ridge), qh_qh(other.qh_qh) {}
+ // Creates an alias. Does not copy QhullRidge. Needed for vector<QhullRidge>
+ QhullRidge & operator=(const QhullRidge &other) { qh_ridge= other.qh_ridge; qh_qh= other.qh_qh; return *this; }
+ ~QhullRidge() {}
+
+#//!\name GetSet
+ QhullFacet bottomFacet() const { return QhullFacet(qh_qh, qh_ridge->bottom); }
+ int dimension() const { return ((qh_qh && qh_qh->hull_dim) ? qh_qh->hull_dim-1 : 0); }
+ ridgeT * getBaseT() const { return getRidgeT(); } //!< For QhullSet<QhullRidge>
+ ridgeT * getRidgeT() const { return qh_ridge; }
+ countT id() const { return qh_ridge->id; }
+ bool isValid() const { return (qh_qh && qh_ridge != &s_empty_ridge); }
+ bool operator==(const QhullRidge &other) const { return qh_ridge==other.qh_ridge; }
+ bool operator!=(const QhullRidge &other) const { return !operator==(other); }
+ QhullFacet otherFacet(const QhullFacet &f) const { return QhullFacet(qh_qh, (qh_ridge->top==f.getFacetT() ? qh_ridge->bottom : qh_ridge->top)); }
+ QhullFacet topFacet() const { return QhullFacet(qh_qh, qh_ridge->top); }
+
+#//!\name foreach
+ bool hasNextRidge3d(const QhullFacet &f) const;
+ QhullRidge nextRidge3d(const QhullFacet &f) const { return nextRidge3d(f, 0); }
+ QhullRidge nextRidge3d(const QhullFacet &f, QhullVertex *nextVertex) const;
+ QhullVertexSet vertices() const { return QhullVertexSet(qh_qh, qh_ridge->vertices); }
+
+#//!\name IO
+
+ struct PrintRidge{
+ const QhullRidge *ridge;
+ const char * print_message; //!< non-null message
+ PrintRidge(const char *message, const QhullRidge &r) : ridge(&r), print_message(message) {}
+ };//PrintRidge
+ PrintRidge print(const char* message) const { return PrintRidge(message, *this); }
+};//class QhullRidge
+
+}//namespace orgQhull
+
+std::ostream &operator<<(std::ostream &os, const orgQhull::QhullRidge &r);
+std::ostream &operator<<(std::ostream &os, const orgQhull::QhullRidge::PrintRidge &pr);
+
+#endif // QHULLRIDGE_H
diff --git a/xs/src/qhull/src/libqhullcpp/QhullSet.cpp b/xs/src/qhull/src/libqhullcpp/QhullSet.cpp
new file mode 100644
index 000000000..dfdc3c51f
--- /dev/null
+++ b/xs/src/qhull/src/libqhullcpp/QhullSet.cpp
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/libqhullcpp/QhullSet.cpp#3 $$Change: 2066 $
+** $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
+**
+****************************************************************************/
+
+#//! QhullSet -- Qhull's set structure, setT, as a C++ class
+
+#include "libqhullcpp/QhullSet.h"
+
+#include "libqhullcpp/Qhull.h"
+#include "libqhullcpp/QhullError.h"
+
+#ifdef _MSC_VER // Microsoft Visual C++ -- warning level 4
+#endif
+
+namespace orgQhull {
+
+#//!\name Class objects
+
+setT QhullSetBase::
+s_empty_set;
+
+#//!\name Constructors
+
+QhullSetBase::
+QhullSetBase(const Qhull &q, setT *s)
+: qh_set(s ? s : &s_empty_set)
+, qh_qh(q.qh())
+{
+}
+
+#//!\name Class methods
+
+// Same code for qh_setsize [qset_r.c] and QhullSetBase::count [static]
+countT QhullSetBase::
+count(const setT *set)
+{
+ countT size;
+ const setelemT *sizep;
+
+ if (!set){
+ return(0);
+ }
+ sizep= SETsizeaddr_(set);
+ if ((size= sizep->i)) {
+ size--;
+ if (size > set->maxsize) {
+ // FIXUP QH11022 How to add additional output to a error? -- qh_setprint(qhmem.ferr, "set: ", set);
+ throw QhullError(10032, "QhullSet internal error: current set size %d is greater than maximum size %d\n",
+ size, set->maxsize);
+ }
+ }else{
+ size= set->maxsize;
+ }
+ return size;
+}//count
+
+}//namespace orgQhull
+
diff --git a/xs/src/qhull/src/libqhullcpp/QhullSet.h b/xs/src/qhull/src/libqhullcpp/QhullSet.h
new file mode 100644
index 000000000..afb6b51d9
--- /dev/null
+++ b/xs/src/qhull/src/libqhullcpp/QhullSet.h
@@ -0,0 +1,462 @@
+/****************************************************************************
+**
+** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/libqhullcpp/QhullSet.h#6 $$Change: 2079 $
+** $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
+**
+****************************************************************************/
+
+#ifndef QhullSet_H
+#define QhullSet_H
+
+#include "libqhull_r/qhull_ra.h"
+#include "libqhullcpp/QhullError.h"
+#include "libqhullcpp/QhullQh.h"
+
+#include <cstddef> // ptrdiff_t, size_t
+
+#ifndef QHULL_NO_STL
+#include <vector>
+#endif
+
+#ifdef QHULL_USES_QT
+ #include <QtCore/QList>
+#endif
+
+namespace orgQhull {
+
+#//!\name Used here
+ class Qhull;
+
+#//!\name Defined here
+ class QhullSetBase; //! Base class for QhullSet<T>
+ //! QhullSet<T> defined below
+ //! QhullSetIterator<T> defined below
+ //! \see QhullPointSet, QhullLinkedList<T>
+
+//! QhullSetBase is a wrapper for Qhull's setT of void* pointers
+//! \see libqhull_r/qset.h
+class QhullSetBase {
+
+private:
+#//!\name Fields --
+ setT * qh_set;
+ QhullQh * qh_qh; //! Provides access to setT memory allocator
+
+#//!\name Class objects
+ static setT s_empty_set; //! Used if setT* is NULL
+
+public:
+#//!\name Constructors
+ QhullSetBase(const Qhull &q, setT *s);
+ QhullSetBase(QhullQh *qqh, setT *s) : qh_set(s ? s : &s_empty_set), qh_qh(qqh) {}
+ //! Copy constructor copies the pointer but not the set. Needed for return by value and parameter passing.
+ QhullSetBase(const QhullSetBase &other) : qh_set(other.qh_set), qh_qh(other.qh_qh) {}
+ QhullSetBase & operator=(const QhullSetBase &other) { qh_set= other.qh_set; qh_qh= other.qh_qh; return *this; }
+ ~QhullSetBase() {}
+
+private:
+ //!disabled since memory allocation for QhullSet not defined
+ QhullSetBase() {}
+public:
+
+#//!\name GetSet
+ countT count() const { return QhullSetBase::count(qh_set); }
+ void defineAs(setT *s) { qh_set= s ? s : &s_empty_set; } //!< Not type-safe since setT may contain any type
+ void forceEmpty() { qh_set= &s_empty_set; }
+ setT * getSetT() const { return qh_set; }
+ bool isEmpty() const { return SETempty_(qh_set); }
+ QhullQh * qh() const { return qh_qh; }
+ setT ** referenceSetT() { return &qh_set; }
+ size_t size() const { return QhullSetBase::count(qh_set); }
+
+#//!\name Element
+protected:
+ void ** beginPointer() const { return &qh_set->e[0].p; }
+ void ** elementPointer(countT idx) const { QHULL_ASSERT(idx>=0 && idx<qh_set->maxsize); return &SETelem_(qh_set, idx); }
+ //! Always points to 0
+ void ** endPointer() const { return qh_setendpointer(qh_set); }
+
+#//!\name Class methods
+public:
+ static countT count(const setT *set);
+ //s may be null
+ static bool isEmpty(const setT *s) { return SETempty_(s); }
+};//QhullSetBase
+
+
+//! QhullSet<T> -- A read-only wrapper to Qhull's collection class, setT.
+//! QhullSet is similar to STL's <vector> and Qt's QVector
+//! QhullSet is unrelated to STL and Qt's set and map types (e.g., QSet and QMap)
+//! T is a Qhull type that defines 'base_type' and getBaseT() (e.g., QhullFacet with base_type 'facetT *'
+//! A QhullSet does not own its contents -- erase(), clear(), removeFirst(), removeLast(), pop_back(), pop_front(), fromStdList() not defined
+//! QhullSetIterator is faster than STL-style iterator/const_iterator
+//! Qhull's FOREACHelement_() [qset_r.h] maybe more efficient than QhullSet. It uses a NULL terminator instead of an end pointer. STL requires an end pointer.
+//! Derived from QhullLinkedList.h and Qt/core/tools/qlist.h w/o QT_STRICT_ITERATORS
+template <typename T>
+class QhullSet : public QhullSetBase {
+
+private:
+#//!\name Fields -- see QhullSetBase
+
+#//!\name Class objects
+ static setT s_empty_set; //! Workaround for no setT allocator. Used if setT* is NULL
+
+public:
+#//!\name Defined here
+ class iterator;
+ class const_iterator;
+ typedef typename QhullSet<T>::iterator Iterator;
+ typedef typename QhullSet<T>::const_iterator ConstIterator;
+
+#//!\name Constructors
+ QhullSet<T>(const Qhull &q, setT *s) : QhullSetBase(q, s) { }
+ QhullSet<T>(QhullQh *qqh, setT *s) : QhullSetBase(qqh, s) { }
+ //Conversion from setT* is not type-safe. Implicit conversion for void* to T
+ //Copy constructor copies pointer but not contents. Needed for return by value.
+ QhullSet<T>(const QhullSet<T> &other) : QhullSetBase(other) {}
+ QhullSet<T> & operator=(const QhullSet<T> &other) { QhullSetBase::operator=(other); return *this; }
+ ~QhullSet<T>() {}
+
+private:
+ //!Disable default constructor. See QhullSetBase
+ QhullSet<T>();
+public:
+
+#//!\name Conversion
+
+#ifndef QHULL_NO_STL
+ std::vector<T> toStdVector() const;
+#endif
+#ifdef QHULL_USES_QT
+ QList<typename T> toQList() const;
+#endif
+
+#//!\name GetSet -- see QhullSetBase for count(), empty(), isEmpty(), size()
+ using QhullSetBase::count;
+ using QhullSetBase::isEmpty;
+ // operator== defined for QhullSets of the same type
+ bool operator==(const QhullSet<T> &other) const { return qh_setequal(getSetT(), other.getSetT()); }
+ bool operator!=(const QhullSet<T> &other) const { return !operator==(other); }
+
+#//!\name Element access
+ // Constructs T. Cannot return reference.
+ const T at(countT idx) const { return operator[](idx); }
+ // Constructs T. Cannot return reference.
+ const T back() const { return last(); }
+ T back() { return last(); }
+ //! end element is NULL
+ const typename T::base_type * constData() const { return reinterpret_cast<const typename T::base_type *>(beginPointer()); }
+ typename T::base_type * data() { return reinterpret_cast<typename T::base_type *>(beginPointer()); }
+ const typename T::base_type *data() const { return reinterpret_cast<const typename T::base_type *>(beginPointer()); }
+ typename T::base_type * endData() { return reinterpret_cast<typename T::base_type *>(endPointer()); }
+ const typename T::base_type * endData() const { return reinterpret_cast<const typename T::base_type *>(endPointer()); }
+ // Constructs T. Cannot return reference.
+ const T first() const { QHULL_ASSERT(!isEmpty()); return T(qh(), *data()); }
+ T first() { QHULL_ASSERT(!isEmpty()); return T(qh(), *data()); }
+ // Constructs T. Cannot return reference.
+ const T front() const { return first(); }
+ T front() { return first(); }
+ // Constructs T. Cannot return reference.
+ const T last() const { QHULL_ASSERT(!isEmpty()); return T(qh(), *(endData()-1)); }
+ T last() { QHULL_ASSERT(!isEmpty()); return T(qh(), *(endData()-1)); }
+ // mid() not available. No setT constructor
+ // Constructs T. Cannot return reference.
+ const T operator[](countT idx) const { const typename T::base_type *p= reinterpret_cast<typename T::base_type *>(elementPointer(idx)); QHULL_ASSERT(idx>=0 && p < endData()); return T(qh(), *p); }
+ T operator[](countT idx) { typename T::base_type *p= reinterpret_cast<typename T::base_type *>(elementPointer(idx)); QHULL_ASSERT(idx>=0 && p < endData()); return T(qh(), *p); }
+ const T second() const { return operator[](1); }
+ T second() { return operator[](1); }
+ T value(countT idx) const;
+ T value(countT idx, const T &defaultValue) const;
+
+#//!\name Read-write -- Not available, no setT constructor
+
+#//!\name iterator
+ iterator begin() { return iterator(qh(), reinterpret_cast<typename T::base_type *>(beginPointer())); }
+ const_iterator begin() const { return const_iterator(qh(), data()); }
+ const_iterator constBegin() const { return const_iterator(qh(), data()); }
+ const_iterator constEnd() const { return const_iterator(qh(), endData()); }
+ iterator end() { return iterator(qh(), endData()); }
+ const_iterator end() const { return const_iterator(qh(), endData()); }
+
+#//!\name Search
+ bool contains(const T &t) const;
+ countT count(const T &t) const;
+ countT indexOf(const T &t) const { /* no qh_qh */ return qh_setindex(getSetT(), t.getBaseT()); }
+ countT lastIndexOf(const T &t) const;
+
+ // before const_iterator for conversion with comparison operators
+ class iterator {
+ friend class const_iterator;
+ private:
+ typename T::base_type * i; // e.g., facetT**, first for debugger
+ QhullQh * qh_qh;
+
+ public:
+ typedef ptrdiff_t difference_type;
+ typedef std::bidirectional_iterator_tag iterator_category;
+ typedef T value_type;
+
+ iterator(QhullQh *qqh, typename T::base_type *p) : i(p), qh_qh(qqh) {}
+ iterator(const iterator &o) : i(o.i), qh_qh(o.qh_qh) {}
+ iterator & operator=(const iterator &o) { i= o.i; qh_qh= o.qh_qh; return *this; }
+
+ // Constructs T. Cannot return reference.
+ T operator*() const { return T(qh_qh, *i); }
+ //operator->() n/a, value-type
+ // Constructs T. Cannot return reference.
+ T operator[](countT idx) const { return T(qh_qh, *(i+idx)); } //!< No error checking
+ bool operator==(const iterator &o) const { return i == o.i; }
+ bool operator!=(const iterator &o) const { return !operator==(o); }
+ bool operator==(const const_iterator &o) const { return (i==reinterpret_cast<const iterator &>(o).i); }
+ bool operator!=(const const_iterator &o) const { return !operator==(o); }
+
+ //! Assumes same point set
+ countT operator-(const iterator &o) const { return (countT)(i-o.i); } //WARN64
+ bool operator>(const iterator &o) const { return i>o.i; }
+ bool operator<=(const iterator &o) const { return !operator>(o); }
+ bool operator<(const iterator &o) const { return i<o.i; }
+ bool operator>=(const iterator &o) const { return !operator<(o); }
+ bool operator>(const const_iterator &o) const { return (i > reinterpret_cast<const iterator &>(o).i); }
+ bool operator<=(const const_iterator &o) const { return !operator>(o); }
+ bool operator<(const const_iterator &o) const { return (i < reinterpret_cast<const iterator &>(o).i); }
+ bool operator>=(const const_iterator &o) const { return !operator<(o); }
+
+ //! No error checking
+ iterator & operator++() { ++i; return *this; }
+ iterator operator++(int) { iterator o= *this; ++i; return o; }
+ iterator & operator--() { --i; return *this; }
+ iterator operator--(int) { iterator o= *this; --i; return o; }
+ iterator operator+(countT j) const { return iterator(qh_qh, i+j); }
+ iterator operator-(countT j) const { return operator+(-j); }
+ iterator & operator+=(countT j) { i += j; return *this; }
+ iterator & operator-=(countT j) { i -= j; return *this; }
+ };//QhullPointSet::iterator
+
+ class const_iterator {
+ private:
+ const typename T::base_type * i; // e.g., const facetT**, first for debugger
+ QhullQh * qh_qh;
+
+ public:
+ typedef ptrdiff_t difference_type;
+ typedef std::random_access_iterator_tag iterator_category;
+ typedef T value_type;
+
+ const_iterator(QhullQh *qqh, const typename T::base_type * p) : i(p), qh_qh(qqh) {}
+ const_iterator(const const_iterator &o) : i(o.i), qh_qh(o.qh_qh) {}
+ const_iterator(const iterator &o) : i(o.i), qh_qh(o.qh_qh) {}
+ const_iterator &operator=(const const_iterator &o) { i= o.i; qh_qh= o.qh_qh; return *this; }
+
+ // Constructs T. Cannot return reference. Retaining 'const T' return type for consistency with QList/QVector
+ const T operator*() const { return T(qh_qh, *i); }
+ const T operator[](countT idx) const { return T(qh_qh, *(i+idx)); } //!< No error checking
+ //operator->() n/a, value-type
+ bool operator==(const const_iterator &o) const { return i == o.i; }
+ bool operator!=(const const_iterator &o) const { return !operator==(o); }
+
+ //! Assumes same point set
+ countT operator-(const const_iterator &o) { return (countT)(i-o.i); } //WARN64
+ bool operator>(const const_iterator &o) const { return i>o.i; }
+ bool operator<=(const const_iterator &o) const { return !operator>(o); }
+ bool operator<(const const_iterator &o) const { return i<o.i; }
+ bool operator>=(const const_iterator &o) const { return !operator<(o); }
+
+ //!< No error checking
+ const_iterator &operator++() { ++i; return *this; }
+ const_iterator operator++(int) { const_iterator o= *this; ++i; return o; }
+ const_iterator &operator--() { --i; return *this; }
+ const_iterator operator--(int) { const_iterator o= *this; --i; return o; }
+ const_iterator operator+(int j) const { return const_iterator(qh_qh, i+j); }
+ const_iterator operator-(int j) const { return operator+(-j); }
+ const_iterator &operator+=(int j) { i += j; return *this; }
+ const_iterator &operator-=(int j) { i -= j; return *this; }
+ };//QhullPointSet::const_iterator
+
+};//class QhullSet
+
+
+//! Faster then interator/const_iterator due to T::base_type
+template <typename T>
+class QhullSetIterator {
+
+#//!\name Subtypes
+ typedef typename QhullSet<T>::const_iterator const_iterator;
+
+private:
+#//!\name Fields
+ const typename T::base_type * i; // e.g., facetT**, first for debugger
+ const typename T::base_type * begin_i; // must be initialized after i
+ const typename T::base_type * end_i;
+ QhullQh * qh_qh;
+
+public:
+#//!\name Constructors
+ QhullSetIterator<T>(const QhullSet<T> &s) : i(s.data()), begin_i(i), end_i(s.endData()), qh_qh(s.qh()) {}
+ QhullSetIterator<T>(const QhullSetIterator<T> &o) : i(o.i), begin_i(o.begin_i), end_i(o.end_i), qh_qh(o.qh_qh) {}
+ QhullSetIterator<T> &operator=(const QhullSetIterator<T> &o) { i= o.i; begin_i= o.begin_i; end_i= o.end_i; qh_qh= o.qh_qh; return *this; }
+
+#//!\name ReadOnly
+ countT countRemaining() { return (countT)(end_i-i); } // WARN64
+
+#//!\name Search
+ bool findNext(const T &t);
+ bool findPrevious(const T &t);
+
+#//!\name Foreach
+ bool hasNext() const { return i != end_i; }
+ bool hasPrevious() const { return i != begin_i; }
+ T next() { return T(qh_qh, *i++); }
+ T peekNext() const { return T(qh_qh, *i); }
+ T peekPrevious() const { const typename T::base_type *p = i; return T(qh_qh, *--p); }
+ T previous() { return T(qh_qh, *--i); }
+ void toBack() { i = end_i; }
+ void toFront() { i = begin_i; }
+};//class QhullSetIterator
+
+#//!\name == Definitions =========================================
+
+#//!\name Conversions
+
+// See qt-qhull.cpp for QList conversion
+
+#ifndef QHULL_NO_STL
+template <typename T>
+std::vector<T> QhullSet<T>::
+toStdVector() const
+{
+ typename QhullSet<T>::const_iterator i = begin();
+ typename QhullSet<T>::const_iterator e = end();
+ std::vector<T> vs;
+ while(i!=e){
+ vs.push_back(*i++);
+ }
+ return vs;
+}//toStdVector
+#endif //QHULL_NO_STL
+
+#ifdef QHULL_USES_QT
+template <typename T>
+QList<T> QhullSet<T>::
+toQList() const
+{
+ QhullSet<T>::const_iterator i= begin();
+ QhullSet<T>::const_iterator e= end();
+ QList<T> vs;
+ while(i!=e){
+ vs.append(*i++);
+ }
+ return vs;
+}//toQList
+#endif
+
+#//!\name Element
+
+template <typename T>
+T QhullSet<T>::
+value(countT idx) const
+{
+ // Avoid call to qh_setsize() and assert in elementPointer()
+ const typename T::base_type *p= reinterpret_cast<const typename T::base_type *>(&SETelem_(getSetT(), idx));
+ return (idx>=0 && p<endData()) ? T(qh(), *p) : T(qh());
+}//value
+
+template <typename T>
+T QhullSet<T>::
+value(countT idx, const T &defaultValue) const
+{
+ // Avoid call to qh_setsize() and assert in elementPointer()
+ const typename T::base_type *p= reinterpret_cast<const typename T::base_type *>(&SETelem_(getSetT(), idx));
+ return (idx>=0 && p<endData() ? T(qh(), *p) : defaultValue);
+}//value
+
+#//!\name Search
+
+template <typename T>
+bool QhullSet<T>::
+contains(const T &t) const
+{
+ setT *s= getSetT();
+ void *p= t.getBaseT(); // contains() is not inline for better error reporting
+ int result= qh_setin(s, p);
+ return result!=0;
+}//contains
+
+template <typename T>
+countT QhullSet<T>::
+count(const T &t) const
+{
+ countT n= 0;
+ const typename T::base_type *i= data();
+ const typename T::base_type *e= endData();
+ typename T::base_type p= t.getBaseT();
+ while(i<e){
+ if(*i==p){
+ n++;
+ }
+ i++;
+ }
+ return n;
+}//count
+
+template <typename T>
+countT QhullSet<T>::
+lastIndexOf(const T &t) const
+{
+ const typename T::base_type *b= data();
+ const typename T::base_type *i= endData();
+ typename T::base_type p= t.getBaseT();
+ while(--i>=b){
+ if(*i==p){
+ break;
+ }
+ }
+ return (countT)(i-b); // WARN64
+}//lastIndexOf
+
+#//!\name QhullSetIterator
+
+template <typename T>
+bool QhullSetIterator<T>::
+findNext(const T &t)
+{
+ typename T::base_type p= t.getBaseT();
+ while(i!=end_i){
+ if(*(++i)==p){
+ return true;
+ }
+ }
+ return false;
+}//findNext
+
+template <typename T>
+bool QhullSetIterator<T>::
+findPrevious(const T &t)
+{
+ typename T::base_type p= t.getBaseT();
+ while(i!=begin_i){
+ if(*(--i)==p){
+ return true;
+ }
+ }
+ return false;
+}//findPrevious
+
+}//namespace orgQhull
+
+
+#//!\name == Global namespace =========================================
+
+template <typename T>
+std::ostream &
+operator<<(std::ostream &os, const orgQhull::QhullSet<T> &qs)
+{
+ const typename T::base_type *i= qs.data();
+ const typename T::base_type *e= qs.endData();
+ while(i!=e){
+ os << T(qs.qh(), *i++);
+ }
+ return os;
+}//operator<<
+
+#endif // QhullSet_H
diff --git a/xs/src/qhull/src/libqhullcpp/QhullSets.h b/xs/src/qhull/src/libqhullcpp/QhullSets.h
new file mode 100644
index 000000000..d0f200cbc
--- /dev/null
+++ b/xs/src/qhull/src/libqhullcpp/QhullSets.h
@@ -0,0 +1,27 @@
+/****************************************************************************
+**
+** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/libqhullcpp/QhullSets.h#2 $$Change: 2066 $
+** $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
+**
+****************************************************************************/
+
+#ifndef QHULLSETS_H
+#define QHULLSETS_H
+
+#include "libqhullcpp/QhullSet.h"
+
+namespace orgQhull {
+
+ //See: QhullFacetSet.h
+ //See: QhullPointSet.h
+ //See: QhullVertexSet.h
+
+ // Avoid circular references between QhullFacet, QhullRidge, and QhullVertex
+ class QhullRidge;
+ typedef QhullSet<QhullRidge> QhullRidgeSet;
+ typedef QhullSetIterator<QhullRidge> QhullRidgeSetIterator;
+
+}//namespace orgQhull
+
+#endif // QHULLSETS_H
diff --git a/xs/src/qhull/src/libqhullcpp/QhullStat.cpp b/xs/src/qhull/src/libqhullcpp/QhullStat.cpp
new file mode 100644
index 000000000..c4fe6c491
--- /dev/null
+++ b/xs/src/qhull/src/libqhullcpp/QhullStat.cpp
@@ -0,0 +1,42 @@
+/****************************************************************************
+**
+** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/libqhullcpp/QhullStat.cpp#3 $$Change: 2066 $
+** $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
+**
+****************************************************************************/
+
+#//! QhullStat -- Qhull's global data structure, statT, as a C++ class
+
+#include "libqhullcpp/QhullStat.h"
+
+#include "libqhullcpp/QhullError.h"
+
+#include <sstream>
+#include <iostream>
+
+using std::cerr;
+using std::string;
+using std::vector;
+using std::ostream;
+
+#ifdef _MSC_VER // Microsoft Visual C++ -- warning level 4
+#endif
+
+namespace orgQhull {
+
+#//!\name Constructor, destructor, etc.
+
+//! If qh_QHpointer==0, invoke with placement new on qh_stat;
+QhullStat::
+QhullStat()
+{
+}//QhullStat
+
+QhullStat::
+~QhullStat()
+{
+}//~QhullStat
+
+}//namespace orgQhull
+
diff --git a/xs/src/qhull/src/libqhullcpp/QhullStat.h b/xs/src/qhull/src/libqhullcpp/QhullStat.h
new file mode 100644
index 000000000..54bde8fc7
--- /dev/null
+++ b/xs/src/qhull/src/libqhullcpp/QhullStat.h
@@ -0,0 +1,49 @@
+/****************************************************************************
+**
+** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/libqhullcpp/QhullStat.h#2 $$Change: 2079 $
+** $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
+**
+****************************************************************************/
+
+#ifndef QHULLSTAT_H
+#define QHULLSTAT_H
+
+#include "libqhull_r/qhull_ra.h"
+
+#include <string>
+#include <vector>
+
+namespace orgQhull {
+
+#//!\name defined here
+ //! QhullStat -- Qhull's statistics, qhstatT, as a C++ class
+ //! Statistics defined with zzdef_() control Qhull's behavior, summarize its result, and report precision problems.
+ class QhullStat;
+
+class QhullStat : public qhstatT {
+
+private:
+#//!\name Fields (empty) -- POD type equivalent to qhstatT. No data or virtual members
+
+public:
+#//!\name Constants
+
+#//!\name class methods
+
+#//!\name constructor, assignment, destructor, invariant
+ QhullStat();
+ ~QhullStat();
+
+private:
+ //!disable copy constructor and assignment
+ QhullStat(const QhullStat &);
+ QhullStat & operator=(const QhullStat &);
+public:
+
+#//!\name Access
+};//class QhullStat
+
+}//namespace orgQhull
+
+#endif // QHULLSTAT_H
diff --git a/xs/src/qhull/src/libqhullcpp/QhullVertex.cpp b/xs/src/qhull/src/libqhullcpp/QhullVertex.cpp
new file mode 100644
index 000000000..fd7aef089
--- /dev/null
+++ b/xs/src/qhull/src/libqhullcpp/QhullVertex.cpp
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/libqhullcpp/QhullVertex.cpp#3 $$Change: 2066 $
+** $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
+**
+****************************************************************************/
+
+#//! QhullVertex -- Qhull's vertex structure, vertexT, as a C++ class
+
+#include "libqhullcpp/QhullVertex.h"
+
+#include "libqhullcpp/Qhull.h"
+#include "libqhullcpp/QhullPoint.h"
+#include "libqhullcpp/QhullFacetSet.h"
+#include "libqhullcpp/QhullFacet.h"
+
+#ifdef _MSC_VER // Microsoft Visual C++ -- warning level 4
+#pragma warning( disable : 4611) // interaction between '_setjmp' and C++ object destruction is non-portable
+#pragma warning( disable : 4996) // function was declared deprecated(strcpy, localtime, etc.)
+#endif
+
+namespace orgQhull {
+
+#//!\name Class objects
+vertexT QhullVertex::
+s_empty_vertex= {0,0,0,0,0,
+ 0,0,0,0,0,
+ 0};
+
+#//!\name Constructors
+
+QhullVertex::QhullVertex(const Qhull &q)
+: qh_vertex(&s_empty_vertex)
+, qh_qh(q.qh())
+{
+}//Default
+
+QhullVertex::QhullVertex(const Qhull &q, vertexT *v)
+: qh_vertex(v ? v : &s_empty_vertex)
+, qh_qh(q.qh())
+{
+}//vertexT
+
+#//!\name foreach
+
+//! Return neighboring facets for a vertex
+//! If neither merging nor Voronoi diagram, requires Qhull::defineVertexNeighborFacets() beforehand.
+QhullFacetSet QhullVertex::
+neighborFacets() const
+{
+ if(!neighborFacetsDefined()){
+ throw QhullError(10034, "Qhull error: neighboring facets of vertex %d not defined. Please call Qhull::defineVertexNeighborFacets() beforehand.", id());
+ }
+ return QhullFacetSet(qh_qh, qh_vertex->neighbors);
+}//neighborFacets
+
+}//namespace orgQhull
+
+#//!\name Global functions
+
+using std::endl;
+using std::ostream;
+using std::string;
+using std::vector;
+using orgQhull::QhullPoint;
+using orgQhull::QhullFacet;
+using orgQhull::QhullFacetSet;
+using orgQhull::QhullFacetSetIterator;
+using orgQhull::QhullVertex;
+
+//! Duplicate of qh_printvertex [io_r.c]
+ostream &
+operator<<(ostream &os, const QhullVertex::PrintVertex &pr)
+{
+ QhullVertex v= *pr.vertex;
+ QhullPoint p= v.point();
+ if(*pr.print_message){
+ os << pr.print_message << " ";
+ }else{
+ os << "- ";
+ }
+ os << "p" << p.id() << " (v" << v.id() << "): ";
+ const realT *c= p.coordinates();
+ for(int k= p.dimension(); k--; ){
+ os << " " << *c++; // FIXUP QH11010 %5.2g
+ }
+ if(v.getVertexT()->deleted){
+ os << " deleted";
+ }
+ if(v.getVertexT()->delridge){
+ os << " ridgedeleted";
+ }
+ os << endl;
+ if(v.neighborFacetsDefined()){
+ QhullFacetSetIterator i= v.neighborFacets();
+ if(i.hasNext()){
+ os << " neighborFacets:";
+ countT count= 0;
+ while(i.hasNext()){
+ if(++count % 100 == 0){
+ os << endl << " ";
+ }
+ QhullFacet f= i.next();
+ os << " f" << f.id();
+ }
+ os << endl;
+ }
+ }
+ return os;
+}//<< PrintVertex
+
diff --git a/xs/src/qhull/src/libqhullcpp/QhullVertex.h b/xs/src/qhull/src/libqhullcpp/QhullVertex.h
new file mode 100644
index 000000000..0137766db
--- /dev/null
+++ b/xs/src/qhull/src/libqhullcpp/QhullVertex.h
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/libqhullcpp/QhullVertex.h#4 $$Change: 2079 $
+** $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
+**
+****************************************************************************/
+
+#ifndef QHULLVERTEX_H
+#define QHULLVERTEX_H
+
+#include "libqhull_r/qhull_ra.h"
+#include "libqhullcpp/QhullPoint.h"
+#include "libqhullcpp/QhullLinkedList.h"
+#include "libqhullcpp/QhullSet.h"
+
+#include <ostream>
+
+namespace orgQhull {
+
+#//!\name Used here
+ class QhullFacetSet;
+
+#//!\name Defined here
+ //! QhullVertex -- Qhull's vertex structure, vertexT [libqhull_r.h], as a C++ class
+ class QhullVertex;
+ typedef QhullLinkedList<QhullVertex> QhullVertexList;
+ typedef QhullLinkedListIterator<QhullVertex> QhullVertexListIterator;
+
+
+/*********************
+ topological information:
+ next,previous doubly-linked list of all vertices
+ neighborFacets set of adjacent facets (only if qh.VERTEXneighbors)
+
+ geometric information:
+ point array of DIM coordinates
+*/
+
+class QhullVertex {
+
+#//!\name Defined here
+public:
+ typedef vertexT * base_type; // for QhullVertexSet
+
+private:
+#//!\name Fields
+ vertexT * qh_vertex; //!< Corresponding vertexT, never 0
+ QhullQh * qh_qh; //!< QhullQh/qhT for vertexT, may be 0
+
+#//!\name Class objects
+ static vertexT s_empty_vertex; // needed for shallow copy
+
+public:
+#//!\name Constants
+
+#//!\name Constructors
+ QhullVertex() : qh_vertex(&s_empty_vertex), qh_qh(0) {}
+ explicit QhullVertex(const Qhull &q);
+ QhullVertex(const Qhull &q, vertexT *v);
+ explicit QhullVertex(QhullQh *qqh) : qh_vertex(&s_empty_vertex), qh_qh(qqh) {}
+ QhullVertex(QhullQh *qqh, vertexT *v) : qh_vertex(v ? v : &s_empty_vertex), qh_qh(qqh) {}
+ // Creates an alias. Does not copy QhullVertex. Needed for return by value and parameter passing
+ QhullVertex(const QhullVertex &other) : qh_vertex(other.qh_vertex), qh_qh(other.qh_qh) {}
+ // Creates an alias. Does not copy QhullVertex. Needed for vector<QhullVertex>
+ QhullVertex & operator=(const QhullVertex &other) { qh_vertex= other.qh_vertex; qh_qh= other.qh_qh; return *this; }
+ ~QhullVertex() {}
+
+#//!\name GetSet
+ int dimension() const { return (qh_qh ? qh_qh->hull_dim : 0); }
+ vertexT * getBaseT() const { return getVertexT(); } //!< For QhullSet<QhullVertex>
+ vertexT * getVertexT() const { return qh_vertex; }
+ countT id() const { return qh_vertex->id; }
+ bool isValid() const { return (qh_qh && qh_vertex != &s_empty_vertex); }
+ //! True if defineVertexNeighborFacets() already called. Auotomatically set for facet merging, Voronoi diagrams
+ bool neighborFacetsDefined() const { return qh_vertex->neighbors != 0; }
+ QhullVertex next() const { return QhullVertex(qh_qh, qh_vertex->next); }
+ bool operator==(const QhullVertex &other) const { return qh_vertex==other.qh_vertex; }
+ bool operator!=(const QhullVertex &other) const { return !operator==(other); }
+ QhullPoint point() const { return QhullPoint(qh_qh, qh_vertex->point); }
+ QhullVertex previous() const { return QhullVertex(qh_qh, qh_vertex->previous); }
+ QhullQh * qh() const { return qh_qh; }
+
+#//!\name foreach
+ //See also QhullVertexList
+ QhullFacetSet neighborFacets() const;
+
+#//!\name IO
+ struct PrintVertex{
+ const QhullVertex *vertex;
+ const char * print_message; //!< non-null message
+ PrintVertex(const char *message, const QhullVertex &v) : vertex(&v), print_message(message) {}
+ };//PrintVertex
+ PrintVertex print(const char *message) const { return PrintVertex(message, *this); }
+};//class QhullVertex
+
+}//namespace orgQhull
+
+#//!\name GLobal
+
+std::ostream &operator<<(std::ostream &os, const orgQhull::QhullVertex::PrintVertex &pr);
+inline std::ostream &operator<<(std::ostream &os, const orgQhull::QhullVertex &v) { os << v.print(""); return os; }
+
+#endif // QHULLVERTEX_H
diff --git a/xs/src/qhull/src/libqhullcpp/QhullVertexSet.cpp b/xs/src/qhull/src/libqhullcpp/QhullVertexSet.cpp
new file mode 100644
index 000000000..00ba62d19
--- /dev/null
+++ b/xs/src/qhull/src/libqhullcpp/QhullVertexSet.cpp
@@ -0,0 +1,161 @@
+/****************************************************************************
+**
+** Copyright (c) 2009-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/libqhullcpp/QhullVertexSet.cpp#3 $$Change: 2066 $
+** $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
+**
+****************************************************************************/
+
+#//! QhullVertexSet -- Qhull's linked Vertexs, as a C++ class
+
+#include "libqhullcpp/QhullVertexSet.h"
+
+#include "libqhullcpp/QhullVertex.h"
+#include "libqhullcpp/QhullPoint.h"
+#include "libqhullcpp/QhullRidge.h"
+#include "libqhullcpp/QhullVertex.h"
+#include "libqhullcpp/Qhull.h"
+
+using std::string;
+using std::vector;
+
+#ifdef _MSC_VER // Microsoft Visual C++ -- warning level 4
+#pragma warning( disable : 4611) /* interaction between '_setjmp' and C++ object destruction is non-portable */
+ /* setjmp should not be implemented with 'catch' */
+#endif
+
+namespace orgQhull {
+
+QhullVertexSet::
+QhullVertexSet(const Qhull &q, facetT *facetlist, setT *facetset, bool allfacets)
+: QhullSet<QhullVertex>(q.qh(), 0)
+, qhsettemp_defined(false)
+{
+ QH_TRY_(q.qh()){ // no object creation -- destructors skipped on longjmp()
+ setT *vertices= qh_facetvertices(q.qh(), facetlist, facetset, allfacets);
+ defineAs(vertices);
+ qhsettemp_defined= true;
+ }
+ q.qh()->NOerrexit= true;
+ q.qh()->maybeThrowQhullMessage(QH_TRY_status);
+}//QhullVertexSet facetlist facetset
+
+//! Return tempory QhullVertexSet of vertices for a list and/or a set of facets
+//! Sets qhsettemp_defined (disallows copy constructor and assignment to prevent double-free)
+QhullVertexSet::
+QhullVertexSet(QhullQh *qqh, facetT *facetlist, setT *facetset, bool allfacets)
+: QhullSet<QhullVertex>(qqh, 0)
+, qhsettemp_defined(false)
+{
+ QH_TRY_(qh()){ // no object creation -- destructors skipped on longjmp()
+ setT *vertices= qh_facetvertices(qh(), facetlist, facetset, allfacets);
+ defineAs(vertices);
+ qhsettemp_defined= true;
+ }
+ qh()->NOerrexit= true;
+ qh()->maybeThrowQhullMessage(QH_TRY_status);
+}//QhullVertexSet facetlist facetset
+
+//! Copy constructor for argument passing and returning a result
+//! Only copies a pointer to the set.
+//! Throws an error if qhsettemp_defined, otherwise have a double-free
+//!\todo Convert QhullVertexSet to a shared pointer with reference counting
+QhullVertexSet::
+QhullVertexSet(const QhullVertexSet &other)
+: QhullSet<QhullVertex>(other)
+, qhsettemp_defined(false)
+{
+ if(other.qhsettemp_defined){
+ throw QhullError(10077, "QhullVertexSet: Cannot use copy constructor since qhsettemp_defined (e.g., QhullVertexSet for a set and/or list of QhFacet). Contains %d vertices", other.count());
+ }
+}//copy constructor
+
+//! Copy assignment only copies a pointer to the set.
+//! Throws an error if qhsettemp_defined, otherwise have a double-free
+QhullVertexSet & QhullVertexSet::
+operator=(const QhullVertexSet &other)
+{
+ QhullSet<QhullVertex>::operator=(other);
+ qhsettemp_defined= false;
+ if(other.qhsettemp_defined){
+ throw QhullError(10078, "QhullVertexSet: Cannot use copy constructor since qhsettemp_defined (e.g., QhullVertexSet for a set and/or list of QhFacet). Contains %d vertices", other.count());
+ }
+ return *this;
+}//assignment
+
+void QhullVertexSet::
+freeQhSetTemp()
+{
+ if(qhsettemp_defined){
+ qhsettemp_defined= false;
+ QH_TRY_(qh()){ // no object creation -- destructors skipped on longjmp()
+ qh_settempfree(qh(), referenceSetT()); // errors if not top of tempstack or if qhmem corrupted
+ }
+ qh()->NOerrexit= true;
+ qh()->maybeThrowQhullMessage(QH_TRY_status, QhullError::NOthrow);
+ }
+}//freeQhSetTemp
+
+QhullVertexSet::
+~QhullVertexSet()
+{
+ freeQhSetTemp();
+}//~QhullVertexSet
+
+//FIXUP -- Move conditional, QhullVertexSet code to QhullVertexSet.cpp
+#ifndef QHULL_NO_STL
+std::vector<QhullVertex> QhullVertexSet::
+toStdVector() const
+{
+ QhullSetIterator<QhullVertex> i(*this);
+ std::vector<QhullVertex> vs;
+ while(i.hasNext()){
+ QhullVertex v= i.next();
+ vs.push_back(v);
+ }
+ return vs;
+}//toStdVector
+#endif //QHULL_NO_STL
+
+}//namespace orgQhull
+
+#//!\name Global functions
+
+using std::endl;
+using std::ostream;
+using orgQhull::QhullPoint;
+using orgQhull::QhullVertex;
+using orgQhull::QhullVertexSet;
+using orgQhull::QhullVertexSetIterator;
+
+//! Print Vertex identifiers to stream. Space prefix. From qh_printVertexheader [io_r.c]
+ostream &
+operator<<(ostream &os, const QhullVertexSet::PrintIdentifiers &pr)
+{
+ os << pr.print_message;
+ for(QhullVertexSet::const_iterator i= pr.vertex_set->begin(); i!=pr.vertex_set->end(); ++i){
+ const QhullVertex v= *i;
+ os << " v" << v.id();
+ }
+ os << endl;
+ return os;
+}//<<QhullVertexSet::PrintIdentifiers
+
+//! Duplicate of printvertices [io_r.c]
+ostream &
+operator<<(ostream &os, const QhullVertexSet::PrintVertexSet &pr){
+
+ os << pr.print_message;
+ const QhullVertexSet *vs= pr.vertex_set;
+ QhullVertexSetIterator i= *vs;
+ while(i.hasNext()){
+ const QhullVertex v= i.next();
+ const QhullPoint p= v.point();
+ os << " p" << p.id() << "(v" << v.id() << ")";
+ }
+ os << endl;
+
+ return os;
+}//<< PrintVertexSet
+
+
diff --git a/xs/src/qhull/src/libqhullcpp/QhullVertexSet.h b/xs/src/qhull/src/libqhullcpp/QhullVertexSet.h
new file mode 100644
index 000000000..a3a11c124
--- /dev/null
+++ b/xs/src/qhull/src/libqhullcpp/QhullVertexSet.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (c) 2009-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/libqhullcpp/QhullVertexSet.h#2 $$Change: 2066 $
+** $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
+**
+****************************************************************************/
+
+#ifndef QHULLVERTEXSET_H
+#define QHULLVERTEXSET_H
+
+#include "libqhullcpp/QhullSet.h"
+#include "libqhullcpp/QhullVertex.h"
+
+#include <ostream>
+
+namespace orgQhull {
+
+#//!\name Used here
+
+#//!\name Defined here
+ //! QhullVertexSet -- a set of Qhull Vertices, as a C++ class.
+ //! See Qhull
+ class QhullVertexSet;
+ typedef QhullSetIterator<QhullVertex> QhullVertexSetIterator;
+
+class QhullVertexSet : public QhullSet<QhullVertex> {
+
+private:
+#//!\name Fields
+ bool qhsettemp_defined; //! Set was allocated with qh_settemp()
+
+public:
+#//!\name Constructor
+ QhullVertexSet(const Qhull &q, setT *s) : QhullSet<QhullVertex>(q, s), qhsettemp_defined(false) {}
+ QhullVertexSet(const Qhull &q, facetT *facetlist, setT *facetset, bool allfacets);
+ //Conversion from setT* is not type-safe. Implicit conversion for void* to T
+ QhullVertexSet(QhullQh *qqh, setT *s) : QhullSet<QhullVertex>(qqh, s), qhsettemp_defined(false) {}
+ QhullVertexSet(QhullQh *qqh, facetT *facetlist, setT *facetset, bool allfacets);
+ //Copy constructor and assignment copies pointer but not contents. Throws error if qhsettemp_defined. Needed for return by value.
+ QhullVertexSet(const QhullVertexSet &other);
+ QhullVertexSet & operator=(const QhullVertexSet &other);
+ ~QhullVertexSet();
+
+private: //!Default constructor disabled. Will implement allocation later
+ QhullVertexSet();
+public:
+
+#//!\name Destructor
+ void freeQhSetTemp();
+
+#//!\name Conversion
+#ifndef QHULL_NO_STL
+ std::vector<QhullVertex> toStdVector() const;
+#endif //QHULL_NO_STL
+#ifdef QHULL_USES_QT
+ QList<QhullVertex> toQList() const;
+#endif //QHULL_USES_QT
+
+#//!\name IO
+ struct PrintVertexSet{
+ const QhullVertexSet *vertex_set;
+ const char * print_message; //!< non-null message
+
+ PrintVertexSet(const char *message, const QhullVertexSet *s) : vertex_set(s), print_message(message) {}
+ };//PrintVertexSet
+ const PrintVertexSet print(const char *message) const { return PrintVertexSet(message, this); }
+
+ struct PrintIdentifiers{
+ const QhullVertexSet *vertex_set;
+ const char * print_message; //!< non-null message
+ PrintIdentifiers(const char *message, const QhullVertexSet *s) : vertex_set(s), print_message(message) {}
+ };//PrintIdentifiers
+ PrintIdentifiers printIdentifiers(const char *message) const { return PrintIdentifiers(message, this); }
+
+};//class QhullVertexSet
+
+}//namespace orgQhull
+
+#//!\name Global
+
+std::ostream &operator<<(std::ostream &os, const orgQhull::QhullVertexSet::PrintVertexSet &pr);
+std::ostream &operator<<(std::ostream &os, const orgQhull::QhullVertexSet::PrintIdentifiers &p);
+inline std::ostream &operator<<(std::ostream &os, const orgQhull::QhullVertexSet &vs) { os << vs.print(""); return os; }
+
+#endif // QHULLVERTEXSET_H
diff --git a/xs/src/qhull/src/libqhullcpp/RboxPoints.cpp b/xs/src/qhull/src/libqhullcpp/RboxPoints.cpp
new file mode 100644
index 000000000..d7acd9fce
--- /dev/null
+++ b/xs/src/qhull/src/libqhullcpp/RboxPoints.cpp
@@ -0,0 +1,224 @@
+/****************************************************************************
+**
+** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/libqhullcpp/RboxPoints.cpp#3 $$Change: 2066 $
+** $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
+**
+****************************************************************************/
+
+#include "libqhullcpp/RboxPoints.h"
+
+#include "libqhullcpp/QhullError.h"
+
+#include <iostream>
+
+using std::cerr;
+using std::endl;
+using std::istream;
+using std::ostream;
+using std::ostringstream;
+using std::string;
+using std::vector;
+using std::ws;
+
+#ifdef _MSC_VER // Microsoft Visual C++ -- warning level 4
+#pragma warning( disable : 4996) // function was declared deprecated(strcpy, localtime, etc.)
+#endif
+
+namespace orgQhull {
+
+#//! RboxPoints -- generate random PointCoordinates for qhull (rbox)
+
+
+#//!\name Constructors
+RboxPoints::
+RboxPoints()
+: PointCoordinates("rbox")
+, rbox_new_count(0)
+, rbox_status(qh_ERRnone)
+, rbox_message()
+{
+ allocateQhullQh();
+}
+
+//! Allocate and generate points according to rboxCommand
+//! For rbox commands, see http://www.qhull.org/html/rbox.htm or html/rbox.htm
+//! Same as appendPoints()
+RboxPoints::
+RboxPoints(const char *rboxCommand)
+: PointCoordinates("rbox")
+, rbox_new_count(0)
+, rbox_status(qh_ERRnone)
+, rbox_message()
+{
+ allocateQhullQh();
+ // rbox arguments added to comment() via qh_rboxpoints > qh_fprintf_rbox
+ appendPoints(rboxCommand);
+}
+
+RboxPoints::
+~RboxPoints()
+{
+ delete qh();
+ resetQhullQh(0);
+}
+
+// RboxPoints and qh_rboxpoints has several fields in qhT (rbox_errexit..cpp_object)
+// It shares last_random with qh_rand and qh_srand
+// The other fields are unused
+void RboxPoints::
+allocateQhullQh()
+{
+ QHULL_LIB_CHECK /* Check for compatible library */
+ resetQhullQh(new QhullQh);
+}//allocateQhullQh
+
+#//!\name Messaging
+
+void RboxPoints::
+clearRboxMessage()
+{
+ rbox_status= qh_ERRnone;
+ rbox_message.clear();
+}//clearRboxMessage
+
+std::string RboxPoints::
+rboxMessage() const
+{
+ if(rbox_status!=qh_ERRnone){
+ return rbox_message;
+ }
+ if(isEmpty()){
+ return "rbox warning: no points generated\n";
+ }
+ return "rbox: OK\n";
+}//rboxMessage
+
+int RboxPoints::
+rboxStatus() const
+{
+ return rbox_status;
+}
+
+bool RboxPoints::
+hasRboxMessage() const
+{
+ return (rbox_status!=qh_ERRnone);
+}
+
+#//!\name Methods
+
+//! Appends points as defined by rboxCommand
+//! Appends rboxCommand to comment
+//! For rbox commands, see http://www.qhull.org/html/rbox.htm or html/rbox.htm
+void RboxPoints::
+appendPoints(const char *rboxCommand)
+{
+ string s("rbox ");
+ s += rboxCommand;
+ char *command= const_cast<char*>(s.c_str());
+ if(qh()->cpp_object){
+ throw QhullError(10001, "Qhull error: conflicting user of cpp_object for RboxPoints::appendPoints() or corrupted qh_qh");
+ }
+ if(extraCoordinatesCount()!=0){
+ throw QhullError(10067, "Qhull error: Extra coordinates (%d) prior to calling RboxPoints::appendPoints. Was %s", extraCoordinatesCount(), 0, 0.0, comment().c_str());
+ }
+ countT previousCount= count();
+ qh()->cpp_object= this; // for qh_fprintf_rbox()
+ int status= qh_rboxpoints(qh(), command);
+ qh()->cpp_object= 0;
+ if(rbox_status==qh_ERRnone){
+ rbox_status= status;
+ }
+ if(rbox_status!=qh_ERRnone){
+ throw QhullError(rbox_status, rbox_message);
+ }
+ if(extraCoordinatesCount()!=0){
+ throw QhullError(10002, "Qhull error: extra coordinates (%d) for PointCoordinates (%x)", extraCoordinatesCount(), 0, 0.0, coordinates());
+ }
+ if(previousCount+newCount()!=count()){
+ throw QhullError(10068, "Qhull error: rbox specified %d points but got %d points for command '%s'", newCount(), count()-previousCount, 0.0, comment().c_str());
+ }
+}//appendPoints
+
+}//namespace orgQhull
+
+#//!\name Global functions
+
+/*-<a href="qh-user.htm#TOC"
+>-------------------------------</a><a name="qh_fprintf_rbox">-</a>
+
+ qh_fprintf_rbox(qh, fp, msgcode, format, list of args )
+ fp is ignored (replaces qh_fprintf_rbox() in userprintf_rbox.c)
+ cpp_object == RboxPoints
+
+notes:
+ only called from qh_rboxpoints()
+ same as fprintf() and Qhull.qh_fprintf()
+ fgets() is not trapped like fprintf()
+ Do not throw errors from here. Use qh_errexit_rbox;
+ A similar technique can be used for qh_fprintf to capture all of its output
+*/
+extern "C"
+void qh_fprintf_rbox(qhT *qh, FILE*, int msgcode, const char *fmt, ... ) {
+ va_list args;
+
+ using namespace orgQhull;
+
+ if(!qh->cpp_object){
+ qh_errexit_rbox(qh, 10072);
+ }
+ RboxPoints *out= reinterpret_cast<RboxPoints *>(qh->cpp_object);
+ va_start(args, fmt);
+ if(msgcode<MSG_OUTPUT){
+ char newMessage[MSG_MAXLEN];
+ // RoadError provides the message tag
+ vsnprintf(newMessage, sizeof(newMessage), fmt, args);
+ out->rbox_message += newMessage;
+ if(out->rbox_status<MSG_ERROR || out->rbox_status>=MSG_STDERR){
+ out->rbox_status= msgcode;
+ }
+ va_end(args);
+ return;
+ }
+ switch(msgcode){
+ case 9391:
+ case 9392:
+ out->rbox_message += "RboxPoints error: options 'h', 'n' not supported.\n";
+ qh_errexit_rbox(qh, 10010);
+ /* never returns */
+ case 9393: // FIXUP QH11026 countT vs. int
+ {
+ int dimension= va_arg(args, int);
+ string command(va_arg(args, char*));
+ countT count= va_arg(args, countT);
+ out->setDimension(dimension);
+ out->appendComment(" \"");
+ out->appendComment(command.substr(command.find(' ')+1));
+ out->appendComment("\"");
+ out->setNewCount(count);
+ out->reservePoints();
+ }
+ break;
+ case 9407:
+ *out << va_arg(args, int);
+ // fall through
+ case 9405:
+ *out << va_arg(args, int);
+ // fall through
+ case 9403:
+ *out << va_arg(args, int);
+ break;
+ case 9408:
+ *out << va_arg(args, double);
+ // fall through
+ case 9406:
+ *out << va_arg(args, double);
+ // fall through
+ case 9404:
+ *out << va_arg(args, double);
+ break;
+ }
+ va_end(args);
+} /* qh_fprintf_rbox */
+
diff --git a/xs/src/qhull/src/libqhullcpp/RboxPoints.h b/xs/src/qhull/src/libqhullcpp/RboxPoints.h
new file mode 100644
index 000000000..e8ec72b14
--- /dev/null
+++ b/xs/src/qhull/src/libqhullcpp/RboxPoints.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/libqhullcpp/RboxPoints.h#4 $$Change: 2079 $
+** $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
+**
+****************************************************************************/
+
+#ifndef RBOXPOINTS_H
+#define RBOXPOINTS_H
+
+#include "libqhull_r/qhull_ra.h"
+#include "libqhullcpp/QhullPoint.h"
+#include "libqhullcpp/PointCoordinates.h"
+
+#include <stdarg.h>
+#include <string>
+#include <vector>
+#include <istream>
+#include <ostream>
+#include <sstream>
+
+namespace orgQhull {
+
+#//!\name Defined here
+ //! RboxPoints -- generate random PointCoordinates for Qhull
+ class RboxPoints;
+
+class RboxPoints : public PointCoordinates {
+
+private:
+#//!\name Fields and friends
+ //! PointCoordinates.qh() is owned by RboxPoints
+ countT rbox_new_count; //! Number of points for PointCoordinates
+ int rbox_status; //! error status from rboxpoints. qh_ERRnone if none.
+ std::string rbox_message; //! stderr from rboxpoints
+
+ // '::' is required for friend references
+ friend void ::qh_fprintf_rbox(qhT *qh, FILE *fp, int msgcode, const char *fmt, ... );
+
+public:
+#//!\name Construct
+ RboxPoints();
+ explicit RboxPoints(const char *rboxCommand);
+ ~RboxPoints();
+private: // Disable copy constructor and assignment. RboxPoints owns QhullQh.
+ RboxPoints(const RboxPoints &);
+ RboxPoints &operator=(const RboxPoints &);
+private:
+ void allocateQhullQh();
+
+public:
+#//!\name GetSet
+ void clearRboxMessage();
+ countT newCount() const { return rbox_new_count; }
+ std::string rboxMessage() const;
+ int rboxStatus() const;
+ bool hasRboxMessage() const;
+ void setNewCount(countT pointCount) { QHULL_ASSERT(pointCount>=0); rbox_new_count= pointCount; }
+
+#//!\name Modify
+ void appendPoints(const char* rboxCommand);
+ using PointCoordinates::appendPoints;
+ void reservePoints() { reserveCoordinates((count()+newCount())*dimension()); }
+};//class RboxPoints
+
+}//namespace orgQhull
+
+#endif // RBOXPOINTS_H
diff --git a/xs/src/qhull/src/libqhullcpp/RoadError.cpp b/xs/src/qhull/src/libqhullcpp/RoadError.cpp
new file mode 100644
index 000000000..1d41ec1bc
--- /dev/null
+++ b/xs/src/qhull/src/libqhullcpp/RoadError.cpp
@@ -0,0 +1,158 @@
+/****************************************************************************
+**
+** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/libqhullcpp/RoadError.cpp#2 $$Change: 2066 $
+** $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
+**
+****************************************************************************/
+
+#//! RoadError -- All exceptions thrown by Qhull are RoadErrors
+#//! Do not throw RoadError's from destructors. Use e.logError() instead.
+
+#include "libqhullcpp/RoadError.h"
+
+#include <string>
+#include <sstream>
+#include <iostream>
+
+using std::cerr;
+using std::cout;
+using std::string;
+
+#ifdef _MSC_VER // Microsoft Visual C++ -- warning level 4
+#endif
+
+namespace orgQhull {
+
+#//!\name Class fields
+
+//! Identifies error messages from Qhull and Road for web searches.
+//! See QhullError.h#QHULLlastError and user.h#MSG_ERROR
+const char * RoadError::
+ROADtag= "QH";
+
+std::ostringstream RoadError::
+global_log;
+
+#//!\name Constructor
+
+RoadError::
+RoadError()
+: error_code(0)
+, log_event()
+, error_message()
+{ }
+
+RoadError::
+RoadError(const RoadError &other)
+: error_code(other.error_code)
+, log_event(other.log_event)
+, error_message(other.error_message)
+{
+}//copy construct
+
+RoadError::
+RoadError(int code, const std::string &message)
+: error_code(code)
+, log_event(message.c_str())
+, error_message(log_event.toString(ROADtag, error_code))
+{
+ log_event.cstr_1= error_message.c_str(); // overwrites initial value
+}
+
+RoadError::
+RoadError(int code, const char *fmt)
+: error_code(code)
+, log_event(fmt)
+, error_message()
+{ }
+
+RoadError::
+RoadError(int code, const char *fmt, int d)
+: error_code(code)
+, log_event(fmt, d)
+, error_message()
+{ }
+
+RoadError::
+RoadError(int code, const char *fmt, int d, int d2)
+: error_code(code)
+, log_event(fmt, d, d2)
+, error_message()
+{ }
+
+RoadError::
+RoadError(int code, const char *fmt, int d, int d2, float f)
+: error_code(code)
+, log_event(fmt, d, d2, f)
+, error_message()
+{ }
+
+RoadError::
+RoadError(int code, const char *fmt, int d, int d2, float f, const char *s)
+: error_code(code)
+, log_event(fmt, d, d2, f, s)
+, error_message(log_event.toString(ROADtag, code)) // char * may go out of scope
+{ }
+
+RoadError::
+RoadError(int code, const char *fmt, int d, int d2, float f, const void *x)
+: error_code(code)
+, log_event(fmt, d, d2, f, x)
+, error_message()
+{ }
+
+RoadError::
+RoadError(int code, const char *fmt, int d, int d2, float f, int i)
+: error_code(code)
+, log_event(fmt, d, d2, f, i)
+, error_message()
+{ }
+
+RoadError::
+RoadError(int code, const char *fmt, int d, int d2, float f, long long i)
+: error_code(code)
+, log_event(fmt, d, d2, f, i)
+, error_message()
+{ }
+
+RoadError::
+RoadError(int code, const char *fmt, int d, int d2, float f, double e)
+: error_code(code)
+, log_event(fmt, d, d2, f, e)
+, error_message()
+{ }
+
+RoadError & RoadError::
+operator=(const RoadError &other)
+{
+ error_code= other.error_code;
+ error_message= other.error_message;
+ log_event= other.log_event;
+ return *this;
+}//operator=
+
+#//!\name Virtual
+const char * RoadError::
+what() const throw()
+{
+ if(error_message.empty()){
+ error_message= log_event.toString(ROADtag, error_code);
+ }
+ return error_message.c_str();
+}//what
+
+#//!\name Updates
+
+//! Log error instead of throwing it.
+//! Not reentrant, so avoid using it if possible
+//!\todo Redesign with a thread-local stream or a reentrant ostringstream
+void RoadError::
+logErrorLastResort() const
+{
+ global_log << what() << endl;
+}//logError
+
+
+}//namespace orgQhull
+
diff --git a/xs/src/qhull/src/libqhullcpp/RoadError.h b/xs/src/qhull/src/libqhullcpp/RoadError.h
new file mode 100644
index 000000000..1c9f6cdd5
--- /dev/null
+++ b/xs/src/qhull/src/libqhullcpp/RoadError.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/libqhullcpp/RoadError.h#4 $$Change: 2079 $
+** $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
+**
+****************************************************************************/
+
+#ifndef ROADERROR_H
+#define ROADERROR_H
+
+#include "libqhull_r/user_r.h" /* for QHULL_CRTDBG */
+#include "libqhullcpp/RoadLogEvent.h"
+
+#include <iostream>
+#include <sstream>
+#include <stdexcept>
+#include <string>
+
+using std::endl;
+
+namespace orgQhull {
+
+#//!\name Defined here
+ //! RoadError -- Report and log errors
+ //! See discussion in Saylan, G., "Practical C++ error handling in hybrid environments," Dr. Dobb's Journal, p. 50-55, March 2007.
+ //! He uses an auto_ptr to track a stringstream. It constructs a string on the fly. RoadError uses the copy constructor to transform RoadLogEvent into a string
+ class RoadError;
+
+class RoadError : public std::exception {
+
+private:
+#//!\name Fields
+ int error_code; //! Non-zero code (not logged), maybe returned as program status
+ RoadLogEvent log_event; //! Format string w/ arguments
+ mutable std::string error_message; //! Formated error message. Must be after log_event.
+
+#//!\name Class fields
+ static const char * ROADtag;
+ static std::ostringstream global_log; //!< May be replaced with any ostream object
+ //!< Not reentrant -- only used by RoadError::logErrorLastResort()
+
+public:
+#//!\name Constants
+
+#//!\name Constructors
+ RoadError();
+ RoadError(const RoadError &other); //! Called on throw, generates error_message
+ RoadError(int code, const std::string &message);
+ RoadError(int code, const char *fmt);
+ RoadError(int code, const char *fmt, int d);
+ RoadError(int code, const char *fmt, int d, int d2);
+ RoadError(int code, const char *fmt, int d, int d2, float f);
+ RoadError(int code, const char *fmt, int d, int d2, float f, const char *s);
+ RoadError(int code, const char *fmt, int d, int d2, float f, const void *x);
+ RoadError(int code, const char *fmt, int d, int d2, float f, int i);
+ RoadError(int code, const char *fmt, int d, int d2, float f, long long i);
+ RoadError(int code, const char *fmt, int d, int d2, float f, double e);
+
+ RoadError & operator=(const RoadError &other);
+ ~RoadError() throw() {};
+
+#//!\name Class methods
+
+ static void clearGlobalLog() { global_log.seekp(0); }
+ static bool emptyGlobalLog() { return global_log.tellp()<=0; }
+ static const char *stringGlobalLog() { return global_log.str().c_str(); }
+
+#//!\name Virtual
+ virtual const char *what() const throw();
+
+#//!\name GetSet
+ bool isValid() const { return log_event.isValid(); }
+ int errorCode() const { return error_code; };
+ // FIXUP QH11021 should RoadError provide errorMessage(). Currently what()
+ RoadLogEvent roadLogEvent() const { return log_event; };
+
+#//!\name Update
+ void logErrorLastResort() const;
+};//class RoadError
+
+}//namespace orgQhull
+
+#//!\name Global
+
+inline std::ostream & operator<<(std::ostream &os, const orgQhull::RoadError &e) { return os << e.what(); }
+
+#endif // ROADERROR_H
diff --git a/xs/src/qhull/src/libqhullcpp/RoadLogEvent.cpp b/xs/src/qhull/src/libqhullcpp/RoadLogEvent.cpp
new file mode 100644
index 000000000..9a9cf5960
--- /dev/null
+++ b/xs/src/qhull/src/libqhullcpp/RoadLogEvent.cpp
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/libqhullcpp/RoadLogEvent.cpp#3 $$Change: 2066 $
+** $Date: 2016/01/18 $$Author: bbarber $
+**
+****************************************************************************/
+
+#//! RoadLogEvent -- All exceptions thrown by Qhull are RoadErrors
+
+#include "libqhullcpp/RoadLogEvent.h"
+
+#include <string>
+#include <sstream>
+#include <iostream>
+
+using std::cout;
+using std::endl;
+using std::ostringstream;
+using std::string;
+
+#ifdef _MSC_VER // Microsoft Visual C++ -- warning level 4
+#endif
+
+namespace orgQhull {
+
+#//!\name Conversion
+string RoadLogEvent::
+toString(const char *tag, int code) const
+{
+ ostringstream os;
+ if(tag && code){
+ os << tag << code;
+ if(format_string){
+ os << " ";
+ }
+ }
+ if(!format_string){
+ return os.str();
+ }
+ const char *s= format_string;
+ int dCount= 0; // Count of %d
+ int fCount= 0; // Count of %f
+ char extraCode= '\0';
+ while(*s){
+ if(*s!='%'){
+ os << *s++;
+ }else{
+ char c= *++s;
+ s++;
+ switch(c){
+ case 'd':
+ if(++dCount>2){
+ os << " ERROR_three_%d_in_format ";
+ }else if(dCount==2){
+ os << int_2;
+ }else{
+ os << int_1;
+ }
+ break;
+ case 'e':
+ if(firstExtraCode(os, c, &extraCode)){
+ os << double_1;
+ }
+ break;
+ case 'f':
+ if(++fCount>1){
+ os << " ERROR_two_%f_in_format ";
+ }else{
+ os << float_1;
+ }
+ break;
+ case 'i':
+ if(firstExtraCode(os, c, &extraCode)){
+ os << int64_1;
+ }
+ break;
+ case 's':
+ if(firstExtraCode(os, c, &extraCode)){
+ os << cstr_1;
+ }
+ break;
+ case 'u':
+ if(firstExtraCode(os, c, &extraCode)){
+ os << "0x" << std::hex << int64_1 << std::dec;
+ }
+ break;
+ case 'x':
+ if(firstExtraCode(os, c, &extraCode)){
+ os << void_1;
+ }
+ break;
+ case '%':
+ os << c;
+ break;
+ default:
+ os << " ERROR_%" << c << "_not_defined_in_format";
+ break;
+ }
+ }
+ }
+ if(s[-1]!='\n'){
+ os << endl;
+ }
+ return os.str();
+}//toString
+
+#//!\name Class helpers (static)
+
+//! True if this char is the first extra code
+bool RoadLogEvent::
+firstExtraCode(std::ostream &os, char c, char *extraCode){
+ if(*extraCode){
+ os << " ERROR_%" << *extraCode << "_and_%" << c << "_in_format ";
+ return false;
+ }
+ *extraCode= c;
+ return true;
+}//firstExtraCode
+
+}//namespace orgQhull
+
diff --git a/xs/src/qhull/src/libqhullcpp/RoadLogEvent.h b/xs/src/qhull/src/libqhullcpp/RoadLogEvent.h
new file mode 100644
index 000000000..7c4cfba0d
--- /dev/null
+++ b/xs/src/qhull/src/libqhullcpp/RoadLogEvent.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/libqhullcpp/RoadLogEvent.h#1 $$Change: 1981 $
+** $DateTime: 2015/09/28 20:26:32 $$Author: bbarber $
+**
+****************************************************************************/
+
+#ifndef ROADLOGEVENT_H
+#define ROADLOGEVENT_H
+
+#include <ostream>
+#include <stdexcept>
+#include <string>
+
+namespace orgQhull {
+
+#//!\name Defined here
+ //! RoadLogEvent -- Record an event for the RoadLog
+ struct RoadLogEvent;
+
+struct RoadLogEvent {
+
+public:
+#//!\name Fields
+ const char * format_string; //! Format string (a literal with format codes, for logging)
+ int int_1; //! Integer argument (%d, for logging)
+ int int_2; //! Integer argument (%d, for logging)
+ float float_1; //! Float argument (%f, for logging)
+ union { //! One additional argument (for logging)
+ const char *cstr_1; //! Cstr argument (%s) -- type checked at construct-time
+ const void *void_1; //! Void* argument (%x) -- Use upper-case codes for object types
+ long long int64_1; //! signed int64 (%i). Ambiguous if unsigned is also defined.
+ double double_1; //! Double argument (%e)
+ };
+
+#//!\name Constants
+
+#//!\name Constructors
+ RoadLogEvent() : format_string(0), int_1(0), int_2(0), float_1(0), int64_1(0) {};
+ explicit RoadLogEvent(const char *fmt) : format_string(fmt), int_1(0), int_2(0), float_1(0), int64_1(0) {};
+ RoadLogEvent(const char *fmt, int d) : format_string(fmt), int_1(d), int_2(0), float_1(0), int64_1(0) {};
+ RoadLogEvent(const char *fmt, int d, int d2) : format_string(fmt), int_1(d), int_2(d2), float_1(0), int64_1(0) {};
+ RoadLogEvent(const char *fmt, int d, int d2, float f) : format_string(fmt), int_1(d), int_2(d2), float_1(f), int64_1(0) {};
+ RoadLogEvent(const char *fmt, int d, int d2, float f, const char *s) : format_string(fmt), int_1(d), int_2(d2), float_1(f), cstr_1(s) {};
+ RoadLogEvent(const char *fmt, int d, int d2, float f, const void *x) : format_string(fmt), int_1(d), int_2(d2), float_1(f), void_1(x) {};
+ RoadLogEvent(const char *fmt, int d, int d2, float f, int i) : format_string(fmt), int_1(d), int_2(d2), float_1(f), int64_1(i) {};
+ RoadLogEvent(const char *fmt, int d, int d2, float f, long long i) : format_string(fmt), int_1(d), int_2(d2), float_1(f), int64_1(i) {};
+ RoadLogEvent(const char *fmt, int d, int d2, float f, double g) : format_string(fmt), int_1(d), int_2(d2), float_1(f), double_1(g) {};
+ ~RoadLogEvent() {};
+ //! Default copy constructor and assignment
+
+#//!\name GetSet
+ bool isValid() const { return format_string!=0; }
+ int int1() const { return int_1; };
+ int int2() const { return int_2; };
+ float float1() const { return float_1; };
+ const char * format() const { return format_string; };
+ const char * cstr1() const { return cstr_1; };
+ const void * void1() const { return void_1; };
+ long long int64() const { return int64_1; };
+ double double1() const { return double_1; };
+
+#//!\name Conversion
+
+ std::string toString(const char* tag, int code) const;
+
+private:
+#//!\name Class helpers
+ static bool firstExtraCode(std::ostream &os, char c, char *extraCode);
+
+
+};//class RoadLogEvent
+
+}//namespace orgQhull
+
+#endif // ROADLOGEVENT_H
diff --git a/xs/src/qhull/src/libqhullcpp/functionObjects.h b/xs/src/qhull/src/libqhullcpp/functionObjects.h
new file mode 100644
index 000000000..3645c0713
--- /dev/null
+++ b/xs/src/qhull/src/libqhullcpp/functionObjects.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/libqhullcpp/functionObjects.h#1 $$Change: 1981 $
+** $DateTime: 2015/09/28 20:26:32 $$Author: bbarber $
+**
+****************************************************************************/
+
+#ifndef QHFUNCTIONOBJECTS_H
+#define QHFUNCTIONOBJECTS_H
+
+#include <stdlib.h>
+#include <math.h>
+
+namespace orgQhull {
+
+#//!\name Defined here
+
+ //! Sum of absolute values of the elements in a container
+ class AbsoluteSumOf;
+ //! Sum of the elements in a container
+ class SumOf;
+ //! Sum of squares of the elements in a container
+ class SumSquaresOf;
+
+#//!\name Class
+
+//! Absolute sum of the elements in a container
+class AbsoluteSumOf
+{
+private:
+ double sum;
+public:
+ inline AbsoluteSumOf() : sum(0.0) {}
+ inline void operator()(double v) { sum += fabs(v); }
+ inline operator double() { return sum; }
+};//AbsoluteSumOf
+
+//! Sum of the elements in a container
+class SumOf
+{
+private:
+ double sum;
+public:
+ inline SumOf() : sum(0.0) {}
+ inline void operator()(double v) { sum += v; }
+ inline operator double() { return sum; }
+};//SumOf
+
+
+//! Sum of squares of the elements in a container
+class SumSquaresOf
+{
+private:
+ double sum;
+public:
+ inline SumSquaresOf() : sum(0.0) {}
+ inline void operator()(double v) { sum += v*v; }
+ inline operator double() { return sum; }
+};//SumSquaresOf
+
+
+}//orgQhull
+
+
+#endif //QHFUNCTIONOBJECTS_H
+
diff --git a/xs/src/qhull/src/libqhullcpp/libqhullcpp.pro b/xs/src/qhull/src/libqhullcpp/libqhullcpp.pro
new file mode 100644
index 000000000..89b967bef
--- /dev/null
+++ b/xs/src/qhull/src/libqhullcpp/libqhullcpp.pro
@@ -0,0 +1,71 @@
+# -------------------------------------------------
+# libqhullcpp.pro -- Qt project for Qhull cpp shared library
+#
+# It uses reentrant Qhull
+# -------------------------------------------------
+
+include(../qhull-warn.pri)
+
+DESTDIR = ../../lib
+TEMPLATE = lib
+# Do not create libqhullcpp as a shared library. Qhull C++ classes may change layout and size.
+CONFIG += staticlib warn_on
+CONFIG -= qt rtti
+build_pass:CONFIG(debug, debug|release):{
+ TARGET = qhullcpp_d
+ OBJECTS_DIR = Debug
+}else:build_pass:CONFIG(release, debug|release):{
+ TARGET = qhullcpp
+ OBJECTS_DIR = Release
+}
+MOC_DIR = moc
+
+INCLUDEPATH += ../../src
+INCLUDEPATH += $$PWD # for MOC_DIR
+
+CONFIG += qhull_warn_shadow qhull_warn_conversion
+
+SOURCES += ../libqhullcpp/Coordinates.cpp
+SOURCES += ../libqhullcpp/PointCoordinates.cpp
+SOURCES += ../libqhullcpp/Qhull.cpp
+SOURCES += ../libqhullcpp/QhullFacet.cpp
+SOURCES += ../libqhullcpp/QhullFacetList.cpp
+SOURCES += ../libqhullcpp/QhullFacetSet.cpp
+SOURCES += ../libqhullcpp/QhullHyperplane.cpp
+SOURCES += ../libqhullcpp/QhullPoint.cpp
+SOURCES += ../libqhullcpp/QhullPoints.cpp
+SOURCES += ../libqhullcpp/QhullPointSet.cpp
+SOURCES += ../libqhullcpp/QhullQh.cpp
+SOURCES += ../libqhullcpp/QhullRidge.cpp
+SOURCES += ../libqhullcpp/QhullSet.cpp
+SOURCES += ../libqhullcpp/QhullStat.cpp
+SOURCES += ../libqhullcpp/QhullVertex.cpp
+SOURCES += ../libqhullcpp/QhullVertexSet.cpp
+SOURCES += ../libqhullcpp/RboxPoints.cpp
+SOURCES += ../libqhullcpp/RoadError.cpp
+SOURCES += ../libqhullcpp/RoadLogEvent.cpp
+
+HEADERS += ../libqhullcpp/Coordinates.h
+HEADERS += ../libqhullcpp/functionObjects.h
+HEADERS += ../libqhullcpp/PointCoordinates.h
+HEADERS += ../libqhullcpp/Qhull.h
+HEADERS += ../libqhullcpp/QhullError.h
+HEADERS += ../libqhullcpp/QhullFacet.h
+HEADERS += ../libqhullcpp/QhullFacetList.h
+HEADERS += ../libqhullcpp/QhullFacetSet.h
+HEADERS += ../libqhullcpp/QhullHyperplane.h
+HEADERS += ../libqhullcpp/QhullIterator.h
+HEADERS += ../libqhullcpp/QhullLinkedList.h
+HEADERS += ../libqhullcpp/QhullPoint.h
+HEADERS += ../libqhullcpp/QhullPoints.h
+HEADERS += ../libqhullcpp/QhullPointSet.h
+HEADERS += ../libqhullcpp/QhullQh.h
+HEADERS += ../libqhullcpp/QhullRidge.h
+HEADERS += ../libqhullcpp/QhullSet.h
+HEADERS += ../libqhullcpp/QhullSets.h
+HEADERS += ../libqhullcpp/QhullStat.h
+HEADERS += ../libqhullcpp/QhullVertex.h
+HEADERS += ../libqhullcpp/QhullVertexSet.h
+HEADERS += ../libqhullcpp/RboxPoints.h
+HEADERS += ../libqhullcpp/RoadError.h
+HEADERS += ../libqhullcpp/RoadLogEvent.h
diff --git a/xs/src/qhull/src/libqhullcpp/qt-qhull.cpp b/xs/src/qhull/src/libqhullcpp/qt-qhull.cpp
new file mode 100644
index 000000000..895f591a8
--- /dev/null
+++ b/xs/src/qhull/src/libqhullcpp/qt-qhull.cpp
@@ -0,0 +1,130 @@
+/****************************************************************************
+**
+** Copyright (c) 2009-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/libqhullcpp/qt-qhull.cpp#1 $$Change: 1981 $
+** $DateTime: 2015/09/28 20:26:32 $$Author: bbarber $
+**
+****************************************************************************/
+
+#include <QList>
+#include "qhulltest/RoadTest.h"
+
+#ifndef QHULL_USES_QT
+#define QHULL_USES_QT 1
+#endif
+
+#include "Coordinates.h"
+#include "QhullFacetList.h"
+#include "QhullFacetSet.h"
+#include "QhullHyperplane.h"
+#include "QhullPoint.h"
+#include "QhullPoints.h"
+#include "QhullPointSet.h"
+#include "QhullVertex.h"
+#include "QhullVertexSet.h"
+
+namespace orgQhull {
+
+#//!\name Conversions
+
+QList<coordT> Coordinates::
+toQList() const
+{
+ CoordinatesIterator i(*this);
+ QList<coordT> cs;
+ while(i.hasNext()){
+ cs.append(i.next());
+ }
+ return cs;
+}//toQList
+
+QList<QhullFacet> QhullFacetList::
+toQList() const
+{
+ QhullLinkedListIterator<QhullFacet> i(*this);
+ QList<QhullFacet> vs;
+ while(i.hasNext()){
+ QhullFacet f= i.next();
+ if(isSelectAll() || f.isGood()){
+ vs.append(f);
+ }
+ }
+ return vs;
+}//toQList
+
+//! Same as PrintVertices
+QList<QhullVertex> QhullFacetList::
+vertices_toQList() const
+{
+ QList<QhullVertex> vs;
+ QhullVertexSet qvs(qh(), first().getFacetT(), NULL, isSelectAll());
+ for(QhullVertexSet::iterator i=qvs.begin(); i!=qvs.end(); ++i){
+ vs.push_back(*i);
+ }
+ return vs;
+}//vertices_toQList
+
+QList<QhullFacet> QhullFacetSet::
+toQList() const
+{
+ QhullSetIterator<QhullFacet> i(*this);
+ QList<QhullFacet> vs;
+ while(i.hasNext()){
+ QhullFacet f= i.next();
+ if(isSelectAll() || f.isGood()){
+ vs.append(f);
+ }
+ }
+ return vs;
+}//toQList
+
+#ifdef QHULL_USES_QT
+QList<coordT> QhullHyperplane::
+toQList() const
+{
+ QhullHyperplaneIterator i(*this);
+ QList<coordT> fs;
+ while(i.hasNext()){
+ fs.append(i.next());
+ }
+ fs.append(hyperplane_offset);
+ return fs;
+}//toQList
+#endif //QHULL_USES_QT
+
+QList<coordT> QhullPoint::
+toQList() const
+{
+ QhullPointIterator i(*this);
+ QList<coordT> vs;
+ while(i.hasNext()){
+ vs.append(i.next());
+ }
+ return vs;
+}//toQList
+
+QList<QhullPoint> QhullPoints::
+toQList() const
+{
+ QhullPointsIterator i(*this);
+ QList<QhullPoint> vs;
+ while(i.hasNext()){
+ vs.append(i.next());
+ }
+ return vs;
+}//toQList
+
+/******
+QList<QhullPoint> QhullPointSet::
+toQList() const
+{
+ QhullPointSetIterator i(*this);
+ QList<QhullPoint> vs;
+ while(i.hasNext()){
+ vs.append(i.next());
+ }
+ return vs;
+}//toQList
+*/
+}//orgQhull
+
diff --git a/xs/src/qhull/src/libqhullcpp/usermem_r-cpp.cpp b/xs/src/qhull/src/libqhullcpp/usermem_r-cpp.cpp
new file mode 100644
index 000000000..bb9534d09
--- /dev/null
+++ b/xs/src/qhull/src/libqhullcpp/usermem_r-cpp.cpp
@@ -0,0 +1,93 @@
+/*<html><pre> -<a href="qh-user_r.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ usermem_r-cpp.cpp
+
+ Redefine qh_exit() as 'throw std::runtime_error("QH10003 ...")'
+
+ This file is not included in the Qhull builds.
+
+ qhull_r calls qh_exit() when qh_errexit() is not available. For example,
+ it calls qh_exit() if you linked the wrong qhull library.
+
+ The C++ interface avoids most of the calls to qh_exit().
+
+ If needed, include usermem_r-cpp.o before libqhullstatic_r.a. You may need to
+ override duplicate symbol errors (e.g. /FORCE:MULTIPLE for DevStudio). It
+ may produce a warning about throwing an error from C code.
+*/
+
+extern "C" {
+ void qh_exit(int exitcode);
+}
+
+#include <stdexcept>
+#include <stdlib.h>
+
+/*-<a href="qh-user_r.htm#TOC"
+ >-------------------------------</a><a name="qh_exit">-</a>
+
+ qh_exit( exitcode )
+ exit program
+
+ notes:
+ same as exit()
+*/
+void qh_exit(int exitcode) {
+ exitcode= exitcode;
+ throw std::runtime_error("QH10003 Qhull error. See stderr or errfile.");
+} /* exit */
+
+/*-<a href="qh-user_r.htm#TOC"
+ >-------------------------------</a><a name="qh_fprintf_stderr">-</a>
+
+ qh_fprintf_stderr( msgcode, format, list of args )
+ fprintf to stderr with msgcode (non-zero)
+
+ notes:
+ qh_fprintf_stderr() is called when qh->ferr is not defined, usually due to an initialization error
+
+ It is typically followed by qh_errexit().
+
+ Redefine this function to avoid using stderr
+
+ Use qh_fprintf [userprintf_r.c] for normal printing
+*/
+void qh_fprintf_stderr(int msgcode, const char *fmt, ... ) {
+ va_list args;
+
+ va_start(args, fmt);
+ if(msgcode)
+ fprintf(stderr, "QH%.4d ", msgcode);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+} /* fprintf_stderr */
+
+/*-<a href="qh-user_r.htm#TOC"
+>-------------------------------</a><a name="qh_free">-</a>
+
+ qh_free(qhT *qh, mem )
+ free memory
+
+ notes:
+ same as free()
+ No calls to qh_errexit()
+*/
+void qh_free(void *mem) {
+ free(mem);
+} /* free */
+
+/*-<a href="qh-user_r.htm#TOC"
+ >-------------------------------</a><a name="qh_malloc">-</a>
+
+ qh_malloc( mem )
+ allocate memory
+
+ notes:
+ same as malloc()
+*/
+void *qh_malloc(size_t size) {
+ return malloc(size);
+} /* malloc */
+
+
diff --git a/xs/src/qhull/src/libqhullstatic/libqhullstatic.pro b/xs/src/qhull/src/libqhullstatic/libqhullstatic.pro
new file mode 100644
index 000000000..1a516db73
--- /dev/null
+++ b/xs/src/qhull/src/libqhullstatic/libqhullstatic.pro
@@ -0,0 +1,19 @@
+# -------------------------------------------------
+# libqhullstatic.pro -- Qt project for Qhull static library
+# Built with qh_QHpointer=0. See libqhullp.pro
+# -------------------------------------------------
+
+include(../qhull-warn.pri)
+include(../qhull-libqhull-src.pri)
+
+DESTDIR = ../../lib
+TEMPLATE = lib
+CONFIG += staticlib warn_on
+CONFIG -= qt
+build_pass:CONFIG(debug, debug|release):{
+ TARGET = qhullstatic_d
+ OBJECTS_DIR = Debug
+}else:build_pass:CONFIG(release, debug|release):{
+ TARGET = qhullstatic
+ OBJECTS_DIR = Release
+}
diff --git a/xs/src/qhull/src/libqhullstatic_r/libqhullstatic_r.pro b/xs/src/qhull/src/libqhullstatic_r/libqhullstatic_r.pro
new file mode 100644
index 000000000..2f5bf4d07
--- /dev/null
+++ b/xs/src/qhull/src/libqhullstatic_r/libqhullstatic_r.pro
@@ -0,0 +1,21 @@
+# -------------------------------------------------
+# libqhullstatic_r.pro -- Qt project for Qhull static library
+#
+# It uses reeentrant Qhull
+# -------------------------------------------------
+
+include(../qhull-warn.pri)
+
+DESTDIR = ../../lib
+TEMPLATE = lib
+CONFIG += staticlib warn_on
+CONFIG -= qt
+build_pass:CONFIG(debug, debug|release):{
+ TARGET = qhullstatic_rd
+ OBJECTS_DIR = Debug
+}else:build_pass:CONFIG(release, debug|release):{
+ TARGET = qhullstatic_r
+ OBJECTS_DIR = Release
+}
+
+include(../qhull-libqhull-src_r.pri)
diff --git a/xs/src/qhull/src/qconvex/qconvex.c b/xs/src/qhull/src/qconvex/qconvex.c
new file mode 100644
index 000000000..41bd666da
--- /dev/null
+++ b/xs/src/qhull/src/qconvex/qconvex.c
@@ -0,0 +1,326 @@
+/*<html><pre> -<a href="../libqhull/qh-qhull.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ qconvex.c
+ compute convex hulls using qhull
+
+ see unix.c for full interface
+
+ Copyright (c) 1993-2015, The Geometry Center
+*/
+
+#include "libqhull/libqhull.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+
+#if __cplusplus
+extern "C" {
+ int isatty(int);
+}
+
+#elif _MSC_VER
+#include <io.h>
+#define isatty _isatty
+/* int _isatty(int); */
+
+#else
+int isatty(int); /* returns 1 if stdin is a tty
+ if "Undefined symbol" this can be deleted along with call in main() */
+#endif
+
+/*-<a href="../libqhull/qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="prompt">-</a>
+
+ qh_prompt
+ long prompt for qconvex
+
+ notes:
+ restricted version of libqhull.c
+
+ see:
+ concise prompt below
+*/
+
+/* duplicated in qconvex.htm */
+char hidden_options[]=" d v H Qbb Qf Qg Qm Qr Qu Qv Qx Qz TR E V Fp Gt Q0 Q1 Q2 Q3 Q4 Q5 Q6 Q7 Q8 Q9 ";
+
+char qh_prompta[]= "\n\
+qconvex- compute the convex hull\n\
+ http://www.qhull.org %s\n\
+\n\
+input (stdin):\n\
+ first lines: dimension and number of points (or vice-versa).\n\
+ other lines: point coordinates, best if one point per line\n\
+ comments: start with a non-numeric character\n\
+\n\
+options:\n\
+ Qt - triangulated output\n\
+ QJ - joggled input instead of merged facets\n\
+ Qc - keep coplanar points with nearest facet\n\
+ Qi - keep interior points with nearest facet\n\
+\n\
+Qhull control options:\n\
+ Qbk:n - scale coord k so that low bound is n\n\
+ QBk:n - scale coord k so that upper bound is n (QBk is %2.2g)\n\
+ QbB - scale input to unit cube centered at the origin\n\
+ Qbk:0Bk:0 - remove k-th coordinate from input\n\
+ QJn - randomly joggle input in range [-n,n]\n\
+ QRn - random rotation (n=seed, n=0 time, n=-1 time/no rotate)\n\
+%s%s%s%s"; /* split up qh_prompt for Visual C++ */
+char qh_promptb[]= "\
+ Qs - search all points for the initial simplex\n\
+ QGn - good facet if visible from point n, -n for not visible\n\
+ QVn - good facet if it includes point n, -n if not\n\
+\n\
+";
+char qh_promptc[]= "\
+Trace options:\n\
+ T4 - trace at level n, 4=all, 5=mem/gauss, -1= events\n\
+ Tc - check frequently during execution\n\
+ Ts - print statistics\n\
+ Tv - verify result: structure, convexity, and point inclusion\n\
+ Tz - send all output to stdout\n\
+ TFn - report summary when n or more facets created\n\
+ TI file - input data from file, no spaces or single quotes\n\
+ TO file - output results to file, may be enclosed in single quotes\n\
+ TPn - turn on tracing when point n added to hull\n\
+ TMn - turn on tracing at merge n\n\
+ TWn - trace merge facets when width > n\n\
+ TVn - stop qhull after adding point n, -n for before (see TCn)\n\
+ TCn - stop qhull after building cone for point n (see TVn)\n\
+\n\
+Precision options:\n\
+ Cn - radius of centrum (roundoff added). Merge facets if non-convex\n\
+ An - cosine of maximum angle. Merge facets if cosine > n or non-convex\n\
+ C-0 roundoff, A-0.99/C-0.01 pre-merge, A0.99/C0.01 post-merge\n\
+ Rn - randomly perturb computations by a factor of [1-n,1+n]\n\
+ Un - max distance below plane for a new, coplanar point\n\
+ Wn - min facet width for outside point (before roundoff)\n\
+\n\
+Output formats (may be combined; if none, produces a summary to stdout):\n\
+ f - facet dump\n\
+ G - Geomview output (see below)\n\
+ i - vertices incident to each facet\n\
+ m - Mathematica output (2-d and 3-d)\n\
+ n - normals with offsets\n\
+ o - OFF file format (dim, points and facets; Voronoi regions)\n\
+ p - point coordinates \n\
+ s - summary (stderr)\n\
+\n\
+";
+char qh_promptd[]= "\
+More formats:\n\
+ Fa - area for each facet\n\
+ FA - compute total area and volume for option 's'\n\
+ Fc - count plus coplanar points for each facet\n\
+ use 'Qc' (default) for coplanar and 'Qi' for interior\n\
+ FC - centrum for each facet\n\
+ Fd - use cdd format for input (homogeneous with offset first)\n\
+ FD - use cdd format for numeric output (offset first)\n\
+ FF - facet dump without ridges\n\
+ Fi - inner plane for each facet\n\
+ FI - ID for each facet\n\
+ Fm - merge count for each facet (511 max)\n\
+ Fn - count plus neighboring facets for each facet\n\
+ FN - count plus neighboring facets for each point\n\
+ Fo - outer plane (or max_outside) for each facet\n\
+ FO - options and precision constants\n\
+ FP - nearest vertex for each coplanar point\n\
+ FQ - command used for qconvex\n\
+ Fs - summary: #int (8), dimension, #points, tot vertices, tot facets,\n\
+ for output: #vertices, #facets,\n\
+ #coplanar points, #non-simplicial facets\n\
+ #real (2), max outer plane, min vertex\n\
+ FS - sizes: #int (0) \n\
+ #real (2) tot area, tot volume\n\
+ Ft - triangulation with centrums for non-simplicial facets (OFF format)\n\
+ Fv - count plus vertices for each facet\n\
+ FV - average of vertices (a feasible point for 'H')\n\
+ Fx - extreme points (in order for 2-d)\n\
+\n\
+";
+char qh_prompte[]= "\
+Geomview output (2-d, 3-d, and 4-d)\n\
+ Ga - all points as dots\n\
+ Gp - coplanar points and vertices as radii\n\
+ Gv - vertices as spheres\n\
+ Gi - inner planes only\n\
+ Gn - no planes\n\
+ Go - outer planes only\n\
+ Gc - centrums\n\
+ Gh - hyperplane intersections\n\
+ Gr - ridges\n\
+ GDn - drop dimension n in 3-d and 4-d output\n\
+\n\
+Print options:\n\
+ PAn - keep n largest facets by area\n\
+ Pdk:n - drop facet if normal[k] <= n (default 0.0)\n\
+ PDk:n - drop facet if normal[k] >= n\n\
+ Pg - print good facets (needs 'QGn' or 'QVn')\n\
+ PFn - keep facets whose area is at least n\n\
+ PG - print neighbors of good facets\n\
+ PMn - keep n facets with most merges\n\
+ Po - force output. If error, output neighborhood of facet\n\
+ Pp - do not report precision problems\n\
+\n\
+ . - list of all options\n\
+ - - one line descriptions of all options\n\
+ -V - version\n\
+";
+/* for opts, don't assign 'e' or 'E' to a flag (already used for exponent) */
+
+/*-<a href="../libqhull/qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="prompt2">-</a>
+
+ qh_prompt2
+ synopsis for qhull
+*/
+char qh_prompt2[]= "\n\
+qconvex- compute the convex hull. Qhull %s\n\
+ input (stdin): dimension, number of points, point coordinates\n\
+ comments start with a non-numeric character\n\
+\n\
+options (qconvex.htm):\n\
+ Qt - triangulated output\n\
+ QJ - joggled input instead of merged facets\n\
+ Tv - verify result: structure, convexity, and point inclusion\n\
+ . - concise list of all options\n\
+ - - one-line description of all options\n\
+ -V - version\n\
+\n\
+output options (subset):\n\
+ s - summary of results (default)\n\
+ i - vertices incident to each facet\n\
+ n - normals with offsets\n\
+ p - vertex coordinates (includes coplanar points if 'Qc')\n\
+ Fx - extreme points (convex hull vertices)\n\
+ FA - report total area and volume\n\
+ FS - compute total area and volume\n\
+ o - OFF format (dim, n, points, facets)\n\
+ G - Geomview output (2-d, 3-d, and 4-d)\n\
+ m - Mathematica output (2-d and 3-d)\n\
+ QVn - print facets that include point n, -n if not\n\
+ TO file- output results to file, may be enclosed in single quotes\n\
+\n\
+examples:\n\
+ rbox c D2 | qconvex s n rbox c D2 | qconvex i\n\
+ rbox c D2 | qconvex o rbox 1000 s | qconvex s Tv FA\n\
+ rbox c d D2 | qconvex s Qc Fx rbox y 1000 W0 | qconvex s n\n\
+ rbox y 1000 W0 | qconvex s QJ rbox d G1 D12 | qconvex QR0 FA Pp\n\
+ rbox c D7 | qconvex FA TF1000\n\
+\n\
+";
+/* for opts, don't assign 'e' or 'E' to a flag (already used for exponent) */
+
+/*-<a href="../libqhull/qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="prompt3">-</a>
+
+ qh_prompt3
+ concise prompt for qhull
+*/
+char qh_prompt3[]= "\n\
+Qhull %s.\n\
+Except for 'F.' and 'PG', upper-case options take an argument.\n\
+\n\
+ incidences mathematica normals OFF_format points\n\
+ summary facet_dump\n\
+\n\
+ Farea FArea_total Fcoplanars FCentrums Fd_cdd_in\n\
+ FD_cdd_out FFacet_xridge Finner FIDs Fmerges\n\
+ Fneighbors FNeigh_vertex Fouter FOptions FPoint_near\n\
+ FQhull Fsummary FSize Fvertices FVertex_ave\n\
+ Fxtremes FMaple\n\
+\n\
+ Gvertices Gpoints Gall_points Gno_planes Ginner\n\
+ Gcentrums Ghyperplanes Gridges Gouter GDrop_dim\n\
+\n\
+ PArea_keep Pdrop d0:0D0 PFacet_area_keep Pgood PGood_neighbors\n\
+ PMerge_keep Poutput_forced Pprecision_not\n\
+\n\
+ QbBound 0:0.5 QbB_scale_box Qcoplanar QGood_point Qinterior\n\
+ QJoggle Qrandom QRotate Qsearch_1st Qtriangulate\n\
+ QVertex_good\n\
+\n\
+ T4_trace Tcheck_often Tstatistics Tverify Tz_stdout\n\
+ TFacet_log TInput_file TPoint_trace TMerge_trace TOutput_file\n\
+ TWide_trace TVertex_stop TCone_stop\n\
+\n\
+ Angle_max Centrum_size Random_dist Ucoplanar_max Wide_outside\n\
+";
+
+/*-<a href="../libqhull/qh-qhull.htm"
+ >-------------------------------</a><a name="main">-</a>
+
+ main( argc, argv )
+ processes the command line, calls qhull() to do the work, and exits
+
+ design:
+ initializes data structures
+ reads points
+ finishes initialization
+ computes convex hull and other structures
+ checks the result
+ writes the output
+ frees memory
+*/
+int main(int argc, char *argv[]) {
+ int curlong, totlong; /* used !qh_NOmem */
+ int exitcode, numpoints, dim;
+ coordT *points;
+ boolT ismalloc;
+
+ QHULL_LIB_CHECK /* Check for compatible library */
+
+ if ((argc == 1) && isatty( 0 /*stdin*/)) {
+ fprintf(stdout, qh_prompt2, qh_version);
+ exit(qh_ERRnone);
+ }
+ if (argc > 1 && *argv[1] == '-' && !*(argv[1]+1)) {
+ fprintf(stdout, qh_prompta, qh_version, qh_DEFAULTbox,
+ qh_promptb, qh_promptc, qh_promptd, qh_prompte);
+ exit(qh_ERRnone);
+ }
+ if (argc > 1 && *argv[1] == '.' && !*(argv[1]+1)) {
+ fprintf(stdout, qh_prompt3, qh_version);
+ exit(qh_ERRnone);
+ }
+ if (argc > 1 && *argv[1] == '-' && *(argv[1]+1)=='V') {
+ fprintf(stdout, "%s\n", qh_version2);
+ exit(qh_ERRnone);
+ }
+ qh_init_A(stdin, stdout, stderr, argc, argv); /* sets qh qhull_command */
+ exitcode= setjmp(qh errexit); /* simple statement for CRAY J916 */
+ if (!exitcode) {
+ qh NOerrexit = False;
+ qh_checkflags(qh qhull_command, hidden_options);
+ qh_initflags(qh qhull_command);
+ points= qh_readpoints(&numpoints, &dim, &ismalloc);
+ if (dim >= 5) {
+ qh_option("Qxact_merge", NULL, NULL);
+ qh MERGEexact= True; /* 'Qx' always */
+ }
+ qh_init_B(points, numpoints, dim, ismalloc);
+ qh_qhull();
+ qh_check_output();
+ qh_produce_output();
+ if (qh VERIFYoutput && !qh FORCEoutput && !qh STOPpoint && !qh STOPcone)
+ qh_check_points();
+ exitcode= qh_ERRnone;
+ }
+ qh NOerrexit= True; /* no more setjmp */
+#ifdef qh_NOmem
+ qh_freeqhull(qh_ALL);
+#else
+ qh_freeqhull(!qh_ALL);
+ qh_memfreeshort(&curlong, &totlong);
+ if (curlong || totlong)
+ qh_fprintf_stderr(6263, "qhull internal warning (main): did not free %d bytes of long memory(%d pieces)\n",
+ totlong, curlong);
+#endif
+ return exitcode;
+} /* main */
+
diff --git a/xs/src/qhull/src/qconvex/qconvex.pro b/xs/src/qhull/src/qconvex/qconvex.pro
new file mode 100644
index 000000000..1bf631bff
--- /dev/null
+++ b/xs/src/qhull/src/qconvex/qconvex.pro
@@ -0,0 +1,9 @@
+# -------------------------------------------------
+# qconvex.pro -- Qt project file for qconvex.exe
+# -------------------------------------------------
+
+include(../qhull-app-c.pri)
+
+TARGET = qconvex
+
+SOURCES += qconvex.c
diff --git a/xs/src/qhull/src/qconvex/qconvex_r.c b/xs/src/qhull/src/qconvex/qconvex_r.c
new file mode 100644
index 000000000..abf68ce37
--- /dev/null
+++ b/xs/src/qhull/src/qconvex/qconvex_r.c
@@ -0,0 +1,328 @@
+/*<html><pre> -<a href="../libqhull/qh-qhull.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ qconvex.c
+ compute convex hulls using qhull
+
+ see unix.c for full interface
+
+ Copyright (c) 1993-2015, The Geometry Center
+*/
+
+#include "libqhull_r/libqhull_r.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+
+#if __cplusplus
+extern "C" {
+ int isatty(int);
+}
+
+#elif _MSC_VER
+#include <io.h>
+#define isatty _isatty
+/* int _isatty(int); */
+
+#else
+int isatty(int); /* returns 1 if stdin is a tty
+ if "Undefined symbol" this can be deleted along with call in main() */
+#endif
+
+/*-<a href="../libqhull/qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="prompt">-</a>
+
+ qh_prompt
+ long prompt for qconvex
+
+ notes:
+ restricted version of libqhull.c
+
+ see:
+ concise prompt below
+*/
+
+/* duplicated in qconvex.htm */
+char hidden_options[]=" d v H Qbb Qf Qg Qm Qr Qu Qv Qx Qz TR E V Fp Gt Q0 Q1 Q2 Q3 Q4 Q5 Q6 Q7 Q8 Q9 ";
+
+char qh_prompta[]= "\n\
+qconvex- compute the convex hull\n\
+ http://www.qhull.org %s\n\
+\n\
+input (stdin):\n\
+ first lines: dimension and number of points (or vice-versa).\n\
+ other lines: point coordinates, best if one point per line\n\
+ comments: start with a non-numeric character\n\
+\n\
+options:\n\
+ Qt - triangulated output\n\
+ QJ - joggled input instead of merged facets\n\
+ Qc - keep coplanar points with nearest facet\n\
+ Qi - keep interior points with nearest facet\n\
+\n\
+Qhull control options:\n\
+ Qbk:n - scale coord k so that low bound is n\n\
+ QBk:n - scale coord k so that upper bound is n (QBk is %2.2g)\n\
+ QbB - scale input to unit cube centered at the origin\n\
+ Qbk:0Bk:0 - remove k-th coordinate from input\n\
+ QJn - randomly joggle input in range [-n,n]\n\
+ QRn - random rotation (n=seed, n=0 time, n=-1 time/no rotate)\n\
+%s%s%s%s"; /* split up qh_prompt for Visual C++ */
+char qh_promptb[]= "\
+ Qs - search all points for the initial simplex\n\
+ QGn - good facet if visible from point n, -n for not visible\n\
+ QVn - good facet if it includes point n, -n if not\n\
+\n\
+";
+char qh_promptc[]= "\
+Trace options:\n\
+ T4 - trace at level n, 4=all, 5=mem/gauss, -1= events\n\
+ Tc - check frequently during execution\n\
+ Ts - print statistics\n\
+ Tv - verify result: structure, convexity, and point inclusion\n\
+ Tz - send all output to stdout\n\
+ TFn - report summary when n or more facets created\n\
+ TI file - input data from file, no spaces or single quotes\n\
+ TO file - output results to file, may be enclosed in single quotes\n\
+ TPn - turn on tracing when point n added to hull\n\
+ TMn - turn on tracing at merge n\n\
+ TWn - trace merge facets when width > n\n\
+ TVn - stop qhull after adding point n, -n for before (see TCn)\n\
+ TCn - stop qhull after building cone for point n (see TVn)\n\
+\n\
+Precision options:\n\
+ Cn - radius of centrum (roundoff added). Merge facets if non-convex\n\
+ An - cosine of maximum angle. Merge facets if cosine > n or non-convex\n\
+ C-0 roundoff, A-0.99/C-0.01 pre-merge, A0.99/C0.01 post-merge\n\
+ Rn - randomly perturb computations by a factor of [1-n,1+n]\n\
+ Un - max distance below plane for a new, coplanar point\n\
+ Wn - min facet width for outside point (before roundoff)\n\
+\n\
+Output formats (may be combined; if none, produces a summary to stdout):\n\
+ f - facet dump\n\
+ G - Geomview output (see below)\n\
+ i - vertices incident to each facet\n\
+ m - Mathematica output (2-d and 3-d)\n\
+ n - normals with offsets\n\
+ o - OFF file format (dim, points and facets; Voronoi regions)\n\
+ p - point coordinates \n\
+ s - summary (stderr)\n\
+\n\
+";
+char qh_promptd[]= "\
+More formats:\n\
+ Fa - area for each facet\n\
+ FA - compute total area and volume for option 's'\n\
+ Fc - count plus coplanar points for each facet\n\
+ use 'Qc' (default) for coplanar and 'Qi' for interior\n\
+ FC - centrum for each facet\n\
+ Fd - use cdd format for input (homogeneous with offset first)\n\
+ FD - use cdd format for numeric output (offset first)\n\
+ FF - facet dump without ridges\n\
+ Fi - inner plane for each facet\n\
+ FI - ID for each facet\n\
+ Fm - merge count for each facet (511 max)\n\
+ Fn - count plus neighboring facets for each facet\n\
+ FN - count plus neighboring facets for each point\n\
+ Fo - outer plane (or max_outside) for each facet\n\
+ FO - options and precision constants\n\
+ FP - nearest vertex for each coplanar point\n\
+ FQ - command used for qconvex\n\
+ Fs - summary: #int (8), dimension, #points, tot vertices, tot facets,\n\
+ for output: #vertices, #facets,\n\
+ #coplanar points, #non-simplicial facets\n\
+ #real (2), max outer plane, min vertex\n\
+ FS - sizes: #int (0) \n\
+ #real (2) tot area, tot volume\n\
+ Ft - triangulation with centrums for non-simplicial facets (OFF format)\n\
+ Fv - count plus vertices for each facet\n\
+ FV - average of vertices (a feasible point for 'H')\n\
+ Fx - extreme points (in order for 2-d)\n\
+\n\
+";
+char qh_prompte[]= "\
+Geomview output (2-d, 3-d, and 4-d)\n\
+ Ga - all points as dots\n\
+ Gp - coplanar points and vertices as radii\n\
+ Gv - vertices as spheres\n\
+ Gi - inner planes only\n\
+ Gn - no planes\n\
+ Go - outer planes only\n\
+ Gc - centrums\n\
+ Gh - hyperplane intersections\n\
+ Gr - ridges\n\
+ GDn - drop dimension n in 3-d and 4-d output\n\
+\n\
+Print options:\n\
+ PAn - keep n largest facets by area\n\
+ Pdk:n - drop facet if normal[k] <= n (default 0.0)\n\
+ PDk:n - drop facet if normal[k] >= n\n\
+ Pg - print good facets (needs 'QGn' or 'QVn')\n\
+ PFn - keep facets whose area is at least n\n\
+ PG - print neighbors of good facets\n\
+ PMn - keep n facets with most merges\n\
+ Po - force output. If error, output neighborhood of facet\n\
+ Pp - do not report precision problems\n\
+\n\
+ . - list of all options\n\
+ - - one line descriptions of all options\n\
+ -V - version\n\
+";
+/* for opts, don't assign 'e' or 'E' to a flag (already used for exponent) */
+
+/*-<a href="../libqhull/qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="prompt2">-</a>
+
+ qh_prompt2
+ synopsis for qhull
+*/
+char qh_prompt2[]= "\n\
+qconvex- compute the convex hull. Qhull %s\n\
+ input (stdin): dimension, number of points, point coordinates\n\
+ comments start with a non-numeric character\n\
+\n\
+options (qconvex.htm):\n\
+ Qt - triangulated output\n\
+ QJ - joggled input instead of merged facets\n\
+ Tv - verify result: structure, convexity, and point inclusion\n\
+ . - concise list of all options\n\
+ - - one-line description of all options\n\
+ -V - version\n\
+\n\
+output options (subset):\n\
+ s - summary of results (default)\n\
+ i - vertices incident to each facet\n\
+ n - normals with offsets\n\
+ p - vertex coordinates (includes coplanar points if 'Qc')\n\
+ Fx - extreme points (convex hull vertices)\n\
+ FA - report total area and volume\n\
+ FS - compute total area and volume\n\
+ o - OFF format (dim, n, points, facets)\n\
+ G - Geomview output (2-d, 3-d, and 4-d)\n\
+ m - Mathematica output (2-d and 3-d)\n\
+ QVn - print facets that include point n, -n if not\n\
+ TO file- output results to file, may be enclosed in single quotes\n\
+\n\
+examples:\n\
+ rbox c D2 | qconvex s n rbox c D2 | qconvex i\n\
+ rbox c D2 | qconvex o rbox 1000 s | qconvex s Tv FA\n\
+ rbox c d D2 | qconvex s Qc Fx rbox y 1000 W0 | qconvex s n\n\
+ rbox y 1000 W0 | qconvex s QJ rbox d G1 D12 | qconvex QR0 FA Pp\n\
+ rbox c D7 | qconvex FA TF1000\n\
+\n\
+";
+/* for opts, don't assign 'e' or 'E' to a flag (already used for exponent) */
+
+/*-<a href="../libqhull/qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="prompt3">-</a>
+
+ qh_prompt3
+ concise prompt for qhull
+*/
+char qh_prompt3[]= "\n\
+Qhull %s.\n\
+Except for 'F.' and 'PG', upper-case options take an argument.\n\
+\n\
+ incidences mathematica normals OFF_format points\n\
+ summary facet_dump\n\
+\n\
+ Farea FArea_total Fcoplanars FCentrums Fd_cdd_in\n\
+ FD_cdd_out FFacet_xridge Finner FIDs Fmerges\n\
+ Fneighbors FNeigh_vertex Fouter FOptions FPoint_near\n\
+ FQhull Fsummary FSize Fvertices FVertex_ave\n\
+ Fxtremes FMaple\n\
+\n\
+ Gvertices Gpoints Gall_points Gno_planes Ginner\n\
+ Gcentrums Ghyperplanes Gridges Gouter GDrop_dim\n\
+\n\
+ PArea_keep Pdrop d0:0D0 PFacet_area_keep Pgood PGood_neighbors\n\
+ PMerge_keep Poutput_forced Pprecision_not\n\
+\n\
+ QbBound 0:0.5 QbB_scale_box Qcoplanar QGood_point Qinterior\n\
+ QJoggle Qrandom QRotate Qsearch_1st Qtriangulate\n\
+ QVertex_good\n\
+\n\
+ T4_trace Tcheck_often Tstatistics Tverify Tz_stdout\n\
+ TFacet_log TInput_file TPoint_trace TMerge_trace TOutput_file\n\
+ TWide_trace TVertex_stop TCone_stop\n\
+\n\
+ Angle_max Centrum_size Random_dist Ucoplanar_max Wide_outside\n\
+";
+
+/*-<a href="../libqhull/qh-qhull.htm"
+ >-------------------------------</a><a name="main">-</a>
+
+ main( argc, argv )
+ processes the command line, calls qhull() to do the work, and exits
+
+ design:
+ initializes data structures
+ reads points
+ finishes initialization
+ computes convex hull and other structures
+ checks the result
+ writes the output
+ frees memory
+*/
+int main(int argc, char *argv[]) {
+ int curlong, totlong; /* used !qh_NOmem */
+ int exitcode, numpoints, dim;
+ coordT *points;
+ boolT ismalloc;
+ qhT qh_qh;
+ qhT *qh= &qh_qh;
+
+ QHULL_LIB_CHECK /* Check for compatible library */
+
+ if ((argc == 1) && isatty( 0 /*stdin*/)) {
+ fprintf(stdout, qh_prompt2, qh_version);
+ exit(qh_ERRnone);
+ }
+ if (argc > 1 && *argv[1] == '-' && !*(argv[1]+1)) {
+ fprintf(stdout, qh_prompta, qh_version, qh_DEFAULTbox,
+ qh_promptb, qh_promptc, qh_promptd, qh_prompte);
+ exit(qh_ERRnone);
+ }
+ if (argc > 1 && *argv[1] == '.' && !*(argv[1]+1)) {
+ fprintf(stdout, qh_prompt3, qh_version);
+ exit(qh_ERRnone);
+ }
+ if (argc > 1 && *argv[1] == '-' && *(argv[1]+1)=='V') {
+ fprintf(stdout, "%s\n", qh_version2);
+ exit(qh_ERRnone);
+ }
+ qh_init_A(qh, stdin, stdout, stderr, argc, argv); /* sets qh->qhull_command */
+ exitcode= setjmp(qh->errexit); /* simple statement for CRAY J916 */
+ if (!exitcode) {
+ qh->NOerrexit = False;
+ qh_checkflags(qh, qh->qhull_command, hidden_options);
+ qh_initflags(qh, qh->qhull_command);
+ points= qh_readpoints(qh, &numpoints, &dim, &ismalloc);
+ if (dim >= 5) {
+ qh_option(qh, "Qxact_merge", NULL, NULL);
+ qh->MERGEexact= True; /* 'Qx' always */
+ }
+ qh_init_B(qh, points, numpoints, dim, ismalloc);
+ qh_qhull(qh);
+ qh_check_output(qh);
+ qh_produce_output(qh);
+ if (qh->VERIFYoutput && !qh->FORCEoutput && !qh->STOPpoint && !qh->STOPcone)
+ qh_check_points(qh);
+ exitcode= qh_ERRnone;
+ }
+ qh->NOerrexit= True; /* no more setjmp */
+#ifdef qh_NOmem
+ qh_freeqhull(qh, qh_ALL);
+#else
+ qh_freeqhull(qh, !qh_ALL);
+ qh_memfreeshort(qh, &curlong, &totlong);
+ if (curlong || totlong)
+ qh_fprintf_stderr(6263, "qhull internal warning (main): did not free %d bytes of long memory(%d pieces)\n",
+ totlong, curlong);
+#endif
+ return exitcode;
+} /* main */
+
diff --git a/xs/src/qhull/src/qdelaunay/qdelaun.c b/xs/src/qhull/src/qdelaunay/qdelaun.c
new file mode 100644
index 000000000..9011d9fcc
--- /dev/null
+++ b/xs/src/qhull/src/qdelaunay/qdelaun.c
@@ -0,0 +1,315 @@
+/*<html><pre> -<a href="../libqhull/qh-qhull.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ qdelaun.c
+ compute Delaunay triangulations and furthest-point Delaunay
+ triangulations using qhull
+
+ see unix.c for full interface
+
+ Copyright (c) 1993-2015, The Geometry Center
+*/
+
+#include "libqhull/libqhull.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+
+#if __cplusplus
+extern "C" {
+ int isatty(int);
+}
+
+#elif _MSC_VER
+#include <io.h>
+#define isatty _isatty
+/* int _isatty(int); */
+
+#else
+int isatty(int); /* returns 1 if stdin is a tty
+ if "Undefined symbol" this can be deleted along with call in main() */
+#endif
+
+/*-<a href="../libqhull/qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="prompt">-</a>
+
+ qh_prompt
+ long prompt for qhull
+
+ notes:
+ restricted version of libqhull.c
+
+ see:
+ concise prompt below
+*/
+
+/* duplicated in qdelau_f.htm and qdelaun.htm */
+char hidden_options[]=" d n v H U Qb QB Qc Qf Qg Qi Qm Qr QR Qv Qx TR E V FC Fi Fo Ft Fp FV Q0 Q1 Q2 Q3 Q4 Q5 Q6 Q7 Q8 Q9 ";
+
+char qh_prompta[]= "\n\
+qdelaunay- compute the Delaunay triangulation\n\
+ http://www.qhull.org %s\n\
+\n\
+input (stdin):\n\
+ first lines: dimension and number of points (or vice-versa).\n\
+ other lines: point coordinates, best if one point per line\n\
+ comments: start with a non-numeric character\n\
+\n\
+options:\n\
+ Qu - compute furthest-site Delaunay triangulation\n\
+ Qt - triangulated output\n\
+ QJ - joggled input instead of merged facets\n\
+\n\
+Qhull control options:\n\
+ QJn - randomly joggle input in range [-n,n]\n\
+%s%s%s%s"; /* split up qh_prompt for Visual C++ */
+char qh_promptb[]= "\
+ Qs - search all points for the initial simplex\n\
+ Qz - add point-at-infinity to Delaunay triangulation\n\
+ QGn - print Delaunay region if visible from point n, -n if not\n\
+ QVn - print Delaunay regions that include point n, -n if not\n\
+\n\
+";
+char qh_promptc[]= "\
+Trace options:\n\
+ T4 - trace at level n, 4=all, 5=mem/gauss, -1= events\n\
+ Tc - check frequently during execution\n\
+ Ts - print statistics\n\
+ Tv - verify result: structure, convexity, and in-circle test\n\
+ Tz - send all output to stdout\n\
+ TFn - report summary when n or more facets created\n\
+ TI file - input data from file, no spaces or single quotes\n\
+ TO file - output results to file, may be enclosed in single quotes\n\
+ TPn - turn on tracing when point n added to hull\n\
+ TMn - turn on tracing at merge n\n\
+ TWn - trace merge facets when width > n\n\
+ TVn - stop qhull after adding point n, -n for before (see TCn)\n\
+ TCn - stop qhull after building cone for point n (see TVn)\n\
+\n\
+Precision options:\n\
+ Cn - radius of centrum (roundoff added). Merge facets if non-convex\n\
+ An - cosine of maximum angle. Merge facets if cosine > n or non-convex\n\
+ C-0 roundoff, A-0.99/C-0.01 pre-merge, A0.99/C0.01 post-merge\n\
+ Rn - randomly perturb computations by a factor of [1-n,1+n]\n\
+ Wn - min facet width for outside point (before roundoff)\n\
+\n\
+Output formats (may be combined; if none, produces a summary to stdout):\n\
+ f - facet dump\n\
+ G - Geomview output (see below)\n\
+ i - vertices incident to each Delaunay region\n\
+ m - Mathematica output (2-d only, lifted to a paraboloid)\n\
+ o - OFF format (dim, points, and facets as a paraboloid)\n\
+ p - point coordinates (lifted to a paraboloid)\n\
+ s - summary (stderr)\n\
+\n\
+";
+char qh_promptd[]= "\
+More formats:\n\
+ Fa - area for each Delaunay region\n\
+ FA - compute total area for option 's'\n\
+ Fc - count plus coincident points for each Delaunay region\n\
+ Fd - use cdd format for input (homogeneous with offset first)\n\
+ FD - use cdd format for numeric output (offset first)\n\
+ FF - facet dump without ridges\n\
+ FI - ID of each Delaunay region\n\
+ Fm - merge count for each Delaunay region (511 max)\n\
+ FM - Maple output (2-d only, lifted to a paraboloid)\n\
+ Fn - count plus neighboring region for each Delaunay region\n\
+ FN - count plus neighboring region for each point\n\
+ FO - options and precision constants\n\
+ FP - nearest point and distance for each coincident point\n\
+ FQ - command used for qdelaunay\n\
+ Fs - summary: #int (8), dimension, #points, tot vertices, tot facets,\n\
+ for output: #vertices, #Delaunay regions,\n\
+ #coincident points, #non-simplicial regions\n\
+ #real (2), max outer plane, min vertex\n\
+ FS - sizes: #int (0)\n\
+ #real (2), tot area, 0\n\
+ Fv - count plus vertices for each Delaunay region\n\
+ Fx - extreme points of Delaunay triangulation (on convex hull)\n\
+\n\
+";
+char qh_prompte[]= "\
+Geomview options (2-d and 3-d)\n\
+ Ga - all points as dots\n\
+ Gp - coplanar points and vertices as radii\n\
+ Gv - vertices as spheres\n\
+ Gi - inner planes only\n\
+ Gn - no planes\n\
+ Go - outer planes only\n\
+ Gc - centrums\n\
+ Gh - hyperplane intersections\n\
+ Gr - ridges\n\
+ GDn - drop dimension n in 3-d and 4-d output\n\
+ Gt - transparent outer ridges to view 3-d Delaunay\n\
+\n\
+Print options:\n\
+ PAn - keep n largest Delaunay regions by area\n\
+ Pdk:n - drop facet if normal[k] <= n (default 0.0)\n\
+ PDk:n - drop facet if normal[k] >= n\n\
+ Pg - print good Delaunay regions (needs 'QGn' or 'QVn')\n\
+ PFn - keep Delaunay regions whose area is at least n\n\
+ PG - print neighbors of good regions (needs 'QGn' or 'QVn')\n\
+ PMn - keep n Delaunay regions with most merges\n\
+ Po - force output. If error, output neighborhood of facet\n\
+ Pp - do not report precision problems\n\
+\n\
+ . - list of all options\n\
+ - - one line descriptions of all options\n\
+ -V - version\n\
+";
+/* for opts, don't assign 'e' or 'E' to a flag (already used for exponent) */
+
+/*-<a href="../libqhull/qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="prompt2">-</a>
+
+ qh_prompt2
+ synopsis for qhull
+*/
+char qh_prompt2[]= "\n\
+qdelaunay- compute the Delaunay triangulation. Qhull %s\n\
+ input (stdin): dimension, number of points, point coordinates\n\
+ comments start with a non-numeric character\n\
+\n\
+options (qdelaun.htm):\n\
+ Qu - furthest-site Delaunay triangulation\n\
+ Qt - triangulated output\n\
+ QJ - joggled input instead of merged facets\n\
+ Tv - verify result: structure, convexity, and in-circle test\n\
+ . - concise list of all options\n\
+ - - one-line description of all options\n\
+ -V - version\n\
+\n\
+output options (subset):\n\
+ s - summary of results (default)\n\
+ i - vertices incident to each Delaunay region\n\
+ Fx - extreme points (vertices of the convex hull)\n\
+ o - OFF format (shows the points lifted to a paraboloid)\n\
+ G - Geomview output (2-d and 3-d points lifted to a paraboloid)\n\
+ m - Mathematica output (2-d inputs lifted to a paraboloid)\n\
+ QVn - print Delaunay regions that include point n, -n if not\n\
+ TO file- output results to file, may be enclosed in single quotes\n\
+\n\
+examples:\n\
+ rbox c P0 D2 | qdelaunay s o rbox c P0 D2 | qdelaunay i\n\
+ rbox c P0 D2 | qdelaunay Fv rbox c P0 D2 | qdelaunay s Qu Fv\n\
+ rbox c G1 d D2 | qdelaunay s i rbox c G1 d D2 | qdelaunay Qt\n\
+ rbox M3,4 z 100 D2 | qdelaunay s rbox M3,4 z 100 D2 | qdelaunay s Qt\n\
+\n\
+";
+/* for opts, don't assign 'e' or 'E' to a flag (already used for exponent) */
+
+/*-<a href="../libqhull/qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="prompt3">-</a>
+
+ qh_prompt3
+ concise prompt for qhull
+*/
+char qh_prompt3[]= "\n\
+Qhull %s.\n\
+Except for 'F.' and 'PG', upper-case options take an argument.\n\
+\n\
+ incidences mathematica OFF_format points_lifted summary\n\
+ facet_dump\n\
+\n\
+ Farea FArea_total Fcoincident Fd_cdd_in FD_cdd_out\n\
+ FF_dump_xridge FIDs Fmerges Fneighbors FNeigh_vertex\n\
+ FOptions FPoint_near FQdelaun Fsummary FSize\n\
+ Fvertices Fxtremes FMaple\n\
+\n\
+ Gvertices Gpoints Gall_points Gno_planes Ginner\n\
+ Gcentrums Ghyperplanes Gridges Gouter GDrop_dim\n\
+ Gtransparent\n\
+\n\
+ PArea_keep Pdrop d0:0D0 Pgood PFacet_area_keep\n\
+ PGood_neighbors PMerge_keep Poutput_forced Pprecision_not\n\
+\n\
+ QGood_point QJoggle Qsearch_1st Qtriangulate QupperDelaunay\n\
+ QVertex_good Qzinfinite\n\
+\n\
+ T4_trace Tcheck_often Tstatistics Tverify Tz_stdout\n\
+ TFacet_log TInput_file TPoint_trace TMerge_trace TOutput_file\n\
+ TWide_trace TVertex_stop TCone_stop\n\
+\n\
+ Angle_max Centrum_size Random_dist Wide_outside\n\
+";
+
+/*-<a href="../libqhull/qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="main">-</a>
+
+ main( argc, argv )
+ processes the command line, calls qhull() to do the work, and exits
+
+ design:
+ initializes data structures
+ reads points
+ finishes initialization
+ computes convex hull and other structures
+ checks the result
+ writes the output
+ frees memory
+*/
+int main(int argc, char *argv[]) {
+ int curlong, totlong; /* used !qh_NOmem */
+ int exitcode, numpoints, dim;
+ coordT *points;
+ boolT ismalloc;
+
+ QHULL_LIB_CHECK /* Check for compatible library */
+
+ if ((argc == 1) && isatty( 0 /*stdin*/)) {
+ fprintf(stdout, qh_prompt2, qh_version);
+ exit(qh_ERRnone);
+ }
+ if (argc > 1 && *argv[1] == '-' && !*(argv[1]+1)) {
+ fprintf(stdout, qh_prompta, qh_version,
+ qh_promptb, qh_promptc, qh_promptd, qh_prompte);
+ exit(qh_ERRnone);
+ }
+ if (argc > 1 && *argv[1] == '.' && !*(argv[1]+1)) {
+ fprintf(stdout, qh_prompt3, qh_version);
+ exit(qh_ERRnone);
+ }
+ if (argc > 1 && *argv[1] == '-' && *(argv[1]+1)=='V') {
+ fprintf(stdout, "%s\n", qh_version2);
+ exit(qh_ERRnone);
+ }
+ qh_init_A(stdin, stdout, stderr, argc, argv); /* sets qh qhull_command */
+ exitcode= setjmp(qh errexit); /* simple statement for CRAY J916 */
+ if (!exitcode) {
+ qh NOerrexit = False;
+ qh_option("delaunay Qbbound-last", NULL, NULL);
+ qh DELAUNAY= True; /* 'd' */
+ qh SCALElast= True; /* 'Qbb' */
+ qh KEEPcoplanar= True; /* 'Qc', to keep coplanars in 'p' */
+ qh_checkflags(qh qhull_command, hidden_options);
+ qh_initflags(qh qhull_command);
+ points= qh_readpoints(&numpoints, &dim, &ismalloc);
+ if (dim >= 5) {
+ qh_option("Qxact_merge", NULL, NULL);
+ qh MERGEexact= True; /* 'Qx' always */
+ }
+ qh_init_B(points, numpoints, dim, ismalloc);
+ qh_qhull();
+ qh_check_output();
+ qh_produce_output();
+ if (qh VERIFYoutput && !qh FORCEoutput && !qh STOPpoint && !qh STOPcone)
+ qh_check_points();
+ exitcode= qh_ERRnone;
+ }
+ qh NOerrexit= True; /* no more setjmp */
+#ifdef qh_NOmem
+ qh_freeqhull(qh_ALL);
+#else
+ qh_freeqhull(!qh_ALL);
+ qh_memfreeshort(&curlong, &totlong);
+ if (curlong || totlong)
+ qh_fprintf_stderr(6263, "qhull internal warning (main): did not free %d bytes of long memory(%d pieces)\n",
+ totlong, curlong);
+#endif
+ return exitcode;
+} /* main */
+
diff --git a/xs/src/qhull/src/qdelaunay/qdelaun_r.c b/xs/src/qhull/src/qdelaunay/qdelaun_r.c
new file mode 100644
index 000000000..0854b8bb9
--- /dev/null
+++ b/xs/src/qhull/src/qdelaunay/qdelaun_r.c
@@ -0,0 +1,317 @@
+/*<html><pre> -<a href="../libqhull/qh-qhull.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ qdelaun.c
+ compute Delaunay triangulations and furthest-point Delaunay
+ triangulations using qhull
+
+ see unix.c for full interface
+
+ Copyright (c) 1993-2015, The Geometry Center
+*/
+
+#include "libqhull_r/libqhull_r.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+
+#if __cplusplus
+extern "C" {
+ int isatty(int);
+}
+
+#elif _MSC_VER
+#include <io.h>
+#define isatty _isatty
+/* int _isatty(int); */
+
+#else
+int isatty(int); /* returns 1 if stdin is a tty
+ if "Undefined symbol" this can be deleted along with call in main() */
+#endif
+
+/*-<a href="../libqhull/qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="prompt">-</a>
+
+ qh_prompt
+ long prompt for qhull
+
+ notes:
+ restricted version of libqhull.c
+
+ see:
+ concise prompt below
+*/
+
+/* duplicated in qdelau_f.htm and qdelaun.htm */
+char hidden_options[]=" d n v H U Qb QB Qc Qf Qg Qi Qm Qr QR Qv Qx TR E V FC Fi Fo Ft Fp FV Q0 Q1 Q2 Q3 Q4 Q5 Q6 Q7 Q8 Q9 ";
+
+char qh_prompta[]= "\n\
+qdelaunay- compute the Delaunay triangulation\n\
+ http://www.qhull.org %s\n\
+\n\
+input (stdin):\n\
+ first lines: dimension and number of points (or vice-versa).\n\
+ other lines: point coordinates, best if one point per line\n\
+ comments: start with a non-numeric character\n\
+\n\
+options:\n\
+ Qu - compute furthest-site Delaunay triangulation\n\
+ Qt - triangulated output\n\
+ QJ - joggled input instead of merged facets\n\
+\n\
+Qhull control options:\n\
+ QJn - randomly joggle input in range [-n,n]\n\
+%s%s%s%s"; /* split up qh_prompt for Visual C++ */
+char qh_promptb[]= "\
+ Qs - search all points for the initial simplex\n\
+ Qz - add point-at-infinity to Delaunay triangulation\n\
+ QGn - print Delaunay region if visible from point n, -n if not\n\
+ QVn - print Delaunay regions that include point n, -n if not\n\
+\n\
+";
+char qh_promptc[]= "\
+Trace options:\n\
+ T4 - trace at level n, 4=all, 5=mem/gauss, -1= events\n\
+ Tc - check frequently during execution\n\
+ Ts - print statistics\n\
+ Tv - verify result: structure, convexity, and in-circle test\n\
+ Tz - send all output to stdout\n\
+ TFn - report summary when n or more facets created\n\
+ TI file - input data from file, no spaces or single quotes\n\
+ TO file - output results to file, may be enclosed in single quotes\n\
+ TPn - turn on tracing when point n added to hull\n\
+ TMn - turn on tracing at merge n\n\
+ TWn - trace merge facets when width > n\n\
+ TVn - stop qhull after adding point n, -n for before (see TCn)\n\
+ TCn - stop qhull after building cone for point n (see TVn)\n\
+\n\
+Precision options:\n\
+ Cn - radius of centrum (roundoff added). Merge facets if non-convex\n\
+ An - cosine of maximum angle. Merge facets if cosine > n or non-convex\n\
+ C-0 roundoff, A-0.99/C-0.01 pre-merge, A0.99/C0.01 post-merge\n\
+ Rn - randomly perturb computations by a factor of [1-n,1+n]\n\
+ Wn - min facet width for outside point (before roundoff)\n\
+\n\
+Output formats (may be combined; if none, produces a summary to stdout):\n\
+ f - facet dump\n\
+ G - Geomview output (see below)\n\
+ i - vertices incident to each Delaunay region\n\
+ m - Mathematica output (2-d only, lifted to a paraboloid)\n\
+ o - OFF format (dim, points, and facets as a paraboloid)\n\
+ p - point coordinates (lifted to a paraboloid)\n\
+ s - summary (stderr)\n\
+\n\
+";
+char qh_promptd[]= "\
+More formats:\n\
+ Fa - area for each Delaunay region\n\
+ FA - compute total area for option 's'\n\
+ Fc - count plus coincident points for each Delaunay region\n\
+ Fd - use cdd format for input (homogeneous with offset first)\n\
+ FD - use cdd format for numeric output (offset first)\n\
+ FF - facet dump without ridges\n\
+ FI - ID of each Delaunay region\n\
+ Fm - merge count for each Delaunay region (511 max)\n\
+ FM - Maple output (2-d only, lifted to a paraboloid)\n\
+ Fn - count plus neighboring region for each Delaunay region\n\
+ FN - count plus neighboring region for each point\n\
+ FO - options and precision constants\n\
+ FP - nearest point and distance for each coincident point\n\
+ FQ - command used for qdelaunay\n\
+ Fs - summary: #int (8), dimension, #points, tot vertices, tot facets,\n\
+ for output: #vertices, #Delaunay regions,\n\
+ #coincident points, #non-simplicial regions\n\
+ #real (2), max outer plane, min vertex\n\
+ FS - sizes: #int (0)\n\
+ #real (2), tot area, 0\n\
+ Fv - count plus vertices for each Delaunay region\n\
+ Fx - extreme points of Delaunay triangulation (on convex hull)\n\
+\n\
+";
+char qh_prompte[]= "\
+Geomview options (2-d and 3-d)\n\
+ Ga - all points as dots\n\
+ Gp - coplanar points and vertices as radii\n\
+ Gv - vertices as spheres\n\
+ Gi - inner planes only\n\
+ Gn - no planes\n\
+ Go - outer planes only\n\
+ Gc - centrums\n\
+ Gh - hyperplane intersections\n\
+ Gr - ridges\n\
+ GDn - drop dimension n in 3-d and 4-d output\n\
+ Gt - transparent outer ridges to view 3-d Delaunay\n\
+\n\
+Print options:\n\
+ PAn - keep n largest Delaunay regions by area\n\
+ Pdk:n - drop facet if normal[k] <= n (default 0.0)\n\
+ PDk:n - drop facet if normal[k] >= n\n\
+ Pg - print good Delaunay regions (needs 'QGn' or 'QVn')\n\
+ PFn - keep Delaunay regions whose area is at least n\n\
+ PG - print neighbors of good regions (needs 'QGn' or 'QVn')\n\
+ PMn - keep n Delaunay regions with most merges\n\
+ Po - force output. If error, output neighborhood of facet\n\
+ Pp - do not report precision problems\n\
+\n\
+ . - list of all options\n\
+ - - one line descriptions of all options\n\
+ -V - version\n\
+";
+/* for opts, don't assign 'e' or 'E' to a flag (already used for exponent) */
+
+/*-<a href="../libqhull/qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="prompt2">-</a>
+
+ qh_prompt2
+ synopsis for qhull
+*/
+char qh_prompt2[]= "\n\
+qdelaunay- compute the Delaunay triangulation. Qhull %s\n\
+ input (stdin): dimension, number of points, point coordinates\n\
+ comments start with a non-numeric character\n\
+\n\
+options (qdelaun.htm):\n\
+ Qu - furthest-site Delaunay triangulation\n\
+ Qt - triangulated output\n\
+ QJ - joggled input instead of merged facets\n\
+ Tv - verify result: structure, convexity, and in-circle test\n\
+ . - concise list of all options\n\
+ - - one-line description of all options\n\
+ -V - version\n\
+\n\
+output options (subset):\n\
+ s - summary of results (default)\n\
+ i - vertices incident to each Delaunay region\n\
+ Fx - extreme points (vertices of the convex hull)\n\
+ o - OFF format (shows the points lifted to a paraboloid)\n\
+ G - Geomview output (2-d and 3-d points lifted to a paraboloid)\n\
+ m - Mathematica output (2-d inputs lifted to a paraboloid)\n\
+ QVn - print Delaunay regions that include point n, -n if not\n\
+ TO file- output results to file, may be enclosed in single quotes\n\
+\n\
+examples:\n\
+ rbox c P0 D2 | qdelaunay s o rbox c P0 D2 | qdelaunay i\n\
+ rbox c P0 D2 | qdelaunay Fv rbox c P0 D2 | qdelaunay s Qu Fv\n\
+ rbox c G1 d D2 | qdelaunay s i rbox c G1 d D2 | qdelaunay Qt\n\
+ rbox M3,4 z 100 D2 | qdelaunay s rbox M3,4 z 100 D2 | qdelaunay s Qt\n\
+\n\
+";
+/* for opts, don't assign 'e' or 'E' to a flag (already used for exponent) */
+
+/*-<a href="../libqhull/qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="prompt3">-</a>
+
+ qh_prompt3
+ concise prompt for qhull
+*/
+char qh_prompt3[]= "\n\
+Qhull %s.\n\
+Except for 'F.' and 'PG', upper-case options take an argument.\n\
+\n\
+ incidences mathematica OFF_format points_lifted summary\n\
+ facet_dump\n\
+\n\
+ Farea FArea_total Fcoincident Fd_cdd_in FD_cdd_out\n\
+ FF_dump_xridge FIDs Fmerges Fneighbors FNeigh_vertex\n\
+ FOptions FPoint_near FQdelaun Fsummary FSize\n\
+ Fvertices Fxtremes FMaple\n\
+\n\
+ Gvertices Gpoints Gall_points Gno_planes Ginner\n\
+ Gcentrums Ghyperplanes Gridges Gouter GDrop_dim\n\
+ Gtransparent\n\
+\n\
+ PArea_keep Pdrop d0:0D0 Pgood PFacet_area_keep\n\
+ PGood_neighbors PMerge_keep Poutput_forced Pprecision_not\n\
+\n\
+ QGood_point QJoggle Qsearch_1st Qtriangulate QupperDelaunay\n\
+ QVertex_good Qzinfinite\n\
+\n\
+ T4_trace Tcheck_often Tstatistics Tverify Tz_stdout\n\
+ TFacet_log TInput_file TPoint_trace TMerge_trace TOutput_file\n\
+ TWide_trace TVertex_stop TCone_stop\n\
+\n\
+ Angle_max Centrum_size Random_dist Wide_outside\n\
+";
+
+/*-<a href="../libqhull/qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="main">-</a>
+
+ main( argc, argv )
+ processes the command line, calls qhull() to do the work, and exits
+
+ design:
+ initializes data structures
+ reads points
+ finishes initialization
+ computes convex hull and other structures
+ checks the result
+ writes the output
+ frees memory
+*/
+int main(int argc, char *argv[]) {
+ int curlong, totlong; /* used !qh_NOmem */
+ int exitcode, numpoints, dim;
+ coordT *points;
+ boolT ismalloc;
+ qhT qh_qh;
+ qhT *qh= &qh_qh;
+
+ QHULL_LIB_CHECK /* Check for compatible library */
+
+ if ((argc == 1) && isatty( 0 /*stdin*/)) {
+ fprintf(stdout, qh_prompt2, qh_version);
+ exit(qh_ERRnone);
+ }
+ if (argc > 1 && *argv[1] == '-' && !*(argv[1]+1)) {
+ fprintf(stdout, qh_prompta, qh_version,
+ qh_promptb, qh_promptc, qh_promptd, qh_prompte);
+ exit(qh_ERRnone);
+ }
+ if (argc > 1 && *argv[1] == '.' && !*(argv[1]+1)) {
+ fprintf(stdout, qh_prompt3, qh_version);
+ exit(qh_ERRnone);
+ }
+ if (argc > 1 && *argv[1] == '-' && *(argv[1]+1)=='V') {
+ fprintf(stdout, "%s\n", qh_version2);
+ exit(qh_ERRnone);
+ }
+ qh_init_A(qh, stdin, stdout, stderr, argc, argv); /* sets qh->qhull_command */
+ exitcode= setjmp(qh->errexit); /* simple statement for CRAY J916 */
+ if (!exitcode) {
+ qh->NOerrexit = False;
+ qh_option(qh, "delaunay Qbbound-last", NULL, NULL);
+ qh->DELAUNAY= True; /* 'd' */
+ qh->SCALElast= True; /* 'Qbb' */
+ qh->KEEPcoplanar= True; /* 'Qc', to keep coplanars in 'p' */
+ qh_checkflags(qh, qh->qhull_command, hidden_options);
+ qh_initflags(qh, qh->qhull_command);
+ points= qh_readpoints(qh, &numpoints, &dim, &ismalloc);
+ if (dim >= 5) {
+ qh_option(qh, "Qxact_merge", NULL, NULL);
+ qh->MERGEexact= True; /* 'Qx' always */
+ }
+ qh_init_B(qh, points, numpoints, dim, ismalloc);
+ qh_qhull(qh);
+ qh_check_output(qh);
+ qh_produce_output(qh);
+ if (qh->VERIFYoutput && !qh->FORCEoutput && !qh->STOPpoint && !qh->STOPcone)
+ qh_check_points(qh);
+ exitcode= qh_ERRnone;
+ }
+ qh->NOerrexit= True; /* no more setjmp */
+#ifdef qh_NOmem
+ qh_freeqhull(qh, qh_ALL);
+#else
+ qh_freeqhull(qh, !qh_ALL);
+ qh_memfreeshort(qh, &curlong, &totlong);
+ if (curlong || totlong)
+ qh_fprintf_stderr(6263, "qhull internal warning (main): did not free %d bytes of long memory(%d pieces)\n",
+ totlong, curlong);
+#endif
+ return exitcode;
+} /* main */
+
diff --git a/xs/src/qhull/src/qdelaunay/qdelaunay.pro b/xs/src/qhull/src/qdelaunay/qdelaunay.pro
new file mode 100644
index 000000000..138ac0589
--- /dev/null
+++ b/xs/src/qhull/src/qdelaunay/qdelaunay.pro
@@ -0,0 +1,9 @@
+# -------------------------------------------------
+# qdelaunay.pro -- Qt project file for qvoronoi.exe
+# -------------------------------------------------
+
+include(../qhull-app-c.pri)
+
+TARGET = qdelaunay
+
+SOURCES += qdelaun.c
diff --git a/xs/src/qhull/src/qhalf/qhalf.c b/xs/src/qhull/src/qhalf/qhalf.c
new file mode 100644
index 000000000..4a5889ed7
--- /dev/null
+++ b/xs/src/qhull/src/qhalf/qhalf.c
@@ -0,0 +1,316 @@
+/*<html><pre> -<a href="../libqhull/qh-qhull.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ qhalf.c
+ compute the intersection of halfspaces about a point
+
+ see unix.c for full interface
+
+ Copyright (c) 1993-2015, The Geometry Center
+*/
+
+#include "libqhull/libqhull.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+
+#if __cplusplus
+extern "C" {
+ int isatty(int);
+}
+
+#elif _MSC_VER
+#include <io.h>
+#define isatty _isatty
+/* int _isatty(int); */
+
+#else
+int isatty(int); /* returns 1 if stdin is a tty
+ if "Undefined symbol" this can be deleted along with call in main() */
+#endif
+
+/*-<a href="../libqhull/qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="prompt">-</a>
+
+ qh_prompt
+ long prompt for qhull
+
+ notes:
+ restricted version of libqhull.c
+
+ see:
+ concise prompt below
+*/
+
+/* duplicated in qhalf.htm */
+char hidden_options[]=" d n v Qbb QbB Qf Qg Qm Qr QR Qv Qx Qz TR E V Fa FA FC FD FS Ft FV Gt Q0 Q1 Q2 Q3 Q4 Q5 Q6 Q7 Q8 Q9 ";
+
+char qh_prompta[]= "\n\
+qhalf- compute the intersection of halfspaces about a point\n\
+ http://www.qhull.org %s\n\
+\n\
+input (stdin):\n\
+ optional interior point: dimension, 1, coordinates\n\
+ first lines: dimension+1 and number of halfspaces\n\
+ other lines: halfspace coefficients followed by offset\n\
+ comments: start with a non-numeric character\n\
+\n\
+options:\n\
+ Hn,n - specify coordinates of interior point\n\
+ Qt - triangulated output\n\
+ QJ - joggled input instead of merged facets\n\
+ Qc - keep coplanar halfspaces\n\
+ Qi - keep other redundant halfspaces\n\
+\n\
+Qhull control options:\n\
+ QJn - randomly joggle input in range [-n,n]\n\
+%s%s%s%s"; /* split up qh_prompt for Visual C++ */
+char qh_promptb[]= "\
+ Qbk:0Bk:0 - remove k-th coordinate from input\n\
+ Qs - search all halfspaces for the initial simplex\n\
+ QGn - print intersection if visible to halfspace n, -n for not\n\
+ QVn - print intersections for halfspace n, -n if not\n\
+\n\
+";
+char qh_promptc[]= "\
+Trace options:\n\
+ T4 - trace at level n, 4=all, 5=mem/gauss, -1= events\n\
+ Tc - check frequently during execution\n\
+ Ts - print statistics\n\
+ Tv - verify result: structure, convexity, and redundancy\n\
+ Tz - send all output to stdout\n\
+ TFn - report summary when n or more facets created\n\
+ TI file - input data from file, no spaces or single quotes\n\
+ TO file - output results to file, may be enclosed in single quotes\n\
+ TPn - turn on tracing when halfspace n added to intersection\n\
+ TMn - turn on tracing at merge n\n\
+ TWn - trace merge facets when width > n\n\
+ TVn - stop qhull after adding halfspace n, -n for before (see TCn)\n\
+ TCn - stop qhull after building cone for halfspace n (see TVn)\n\
+\n\
+Precision options:\n\
+ Cn - radius of centrum (roundoff added). Merge facets if non-convex\n\
+ An - cosine of maximum angle. Merge facets if cosine > n or non-convex\n\
+ C-0 roundoff, A-0.99/C-0.01 pre-merge, A0.99/C0.01 post-merge\n\
+ Rn - randomly perturb computations by a factor of [1-n,1+n]\n\
+ Un - max distance below plane for a new, coplanar halfspace\n\
+ Wn - min facet width for outside halfspace (before roundoff)\n\
+\n\
+Output formats (may be combined; if none, produces a summary to stdout):\n\
+ f - facet dump\n\
+ G - Geomview output (dual convex hull)\n\
+ i - non-redundant halfspaces incident to each intersection\n\
+ m - Mathematica output (dual convex hull)\n\
+ o - OFF format (dual convex hull: dimension, points, and facets)\n\
+ p - vertex coordinates of dual convex hull (coplanars if 'Qc' or 'Qi')\n\
+ s - summary (stderr)\n\
+\n\
+";
+char qh_promptd[]= "\
+More formats:\n\
+ Fc - count plus redundant halfspaces for each intersection\n\
+ - Qc (default) for coplanar and Qi for other redundant\n\
+ Fd - use cdd format for input (homogeneous with offset first)\n\
+ FF - facet dump without ridges\n\
+ FI - ID of each intersection\n\
+ Fm - merge count for each intersection (511 max)\n\
+ FM - Maple output (dual convex hull)\n\
+ Fn - count plus neighboring intersections for each intersection\n\
+ FN - count plus intersections for each non-redundant halfspace\n\
+ FO - options and precision constants\n\
+ Fp - dim, count, and intersection coordinates\n\
+ FP - nearest halfspace and distance for each redundant halfspace\n\
+ FQ - command used for qhalf\n\
+ Fs - summary: #int (8), dim, #halfspaces, #non-redundant, #intersections\n\
+ for output: #non-redundant, #intersections, #coplanar\n\
+ halfspaces, #non-simplicial intersections\n\
+ #real (2), max outer plane, min vertex\n\
+ Fv - count plus non-redundant halfspaces for each intersection\n\
+ Fx - non-redundant halfspaces\n\
+\n\
+";
+char qh_prompte[]= "\
+Geomview output (2-d, 3-d and 4-d; dual convex hull)\n\
+ Ga - all points (i.e., transformed halfspaces) as dots\n\
+ Gp - coplanar points and vertices as radii\n\
+ Gv - vertices (i.e., non-redundant halfspaces) as spheres\n\
+ Gi - inner planes (i.e., halfspace intersections) only\n\
+ Gn - no planes\n\
+ Go - outer planes only\n\
+ Gc - centrums\n\
+ Gh - hyperplane intersections\n\
+ Gr - ridges\n\
+ GDn - drop dimension n in 3-d and 4-d output\n\
+\n\
+Print options:\n\
+ PAn - keep n largest facets (i.e., intersections) by area\n\
+ Pdk:n- drop facet if normal[k] <= n (default 0.0)\n\
+ PDk:n- drop facet if normal[k] >= n\n\
+ Pg - print good facets (needs 'QGn' or 'QVn')\n\
+ PFn - keep facets whose area is at least n\n\
+ PG - print neighbors of good facets\n\
+ PMn - keep n facets with most merges\n\
+ Po - force output. If error, output neighborhood of facet\n\
+ Pp - do not report precision problems\n\
+\n\
+ . - list of all options\n\
+ - - one line descriptions of all options\n\
+ -V - version\n\
+";
+/* for opts, don't assign 'e' or 'E' to a flag (already used for exponent) */
+
+/*-<a href="../libqhull/qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="prompt2">-</a>
+
+ qh_prompt2
+ synopsis for qhull
+*/
+char qh_prompt2[]= "\n\
+qhalf- halfspace intersection about a point. Qhull %s\n\
+ input (stdin): [dim, 1, interior point], dim+1, n, coefficients+offset\n\
+ comments start with a non-numeric character\n\
+\n\
+options (qhalf.htm):\n\
+ Hn,n - specify coordinates of interior point\n\
+ Qt - triangulated output\n\
+ QJ - joggled input instead of merged facets\n\
+ Tv - verify result: structure, convexity, and redundancy\n\
+ . - concise list of all options\n\
+ - - one-line description of all options\n\
+ -V - version\n\
+\n\
+output options (subset):\n\
+ s - summary of results (default)\n\
+ Fp - intersection coordinates\n\
+ Fv - non-redundant halfspaces incident to each intersection\n\
+ Fx - non-redundant halfspaces\n\
+ o - OFF file format (dual convex hull)\n\
+ G - Geomview output (dual convex hull)\n\
+ m - Mathematica output (dual convex hull)\n\
+ QVn - print intersections for halfspace n, -n if not\n\
+ TO file - output results to file, may be enclosed in single quotes\n\
+\n\
+examples:\n\
+ rbox d | qconvex FQ n | qhalf s H0,0,0 Fp\n\
+ rbox c | qconvex FQ FV n | qhalf s i\n\
+ rbox c | qconvex FQ FV n | qhalf s o\n\
+\n\
+";
+/* for opts, don't assign 'e' or 'E' to a flag (already used for exponent) */
+
+/*-<a href="../libqhull/qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="prompt3">-</a>
+
+ qh_prompt3
+ concise prompt for qhull
+*/
+char qh_prompt3[]= "\n\
+Qhull %s.\n\
+Except for 'F.' and 'PG', upper_case options take an argument.\n\
+\n\
+ incidences Geomview mathematica OFF_format point_dual\n\
+ summary facet_dump\n\
+\n\
+ Fc_redundant Fd_cdd_in FF_dump_xridge FIDs Fmerges\n\
+ Fneighbors FN_intersect FOptions Fp_coordinates FP_nearest\n\
+ FQhalf Fsummary Fv_halfspace FMaple Fx_non_redundant\n\
+\n\
+ Gvertices Gpoints Gall_points Gno_planes Ginner\n\
+ Gcentrums Ghyperplanes Gridges Gouter GDrop_dim\n\
+\n\
+ PArea_keep Pdrop d0:0D0 Pgood PFacet_area_keep\n\
+ PGood_neighbors PMerge_keep Poutput_forced Pprecision_not\n\
+\n\
+ Qbk:0Bk:0_drop Qcoplanar QG_half_good Qi_redundant QJoggle\n\
+ Qsearch_1st Qtriangulate QVertex_good\n\
+\n\
+ T4_trace Tcheck_often Tstatistics Tverify Tz_stdout\n\
+ TFacet_log TInput_file TPoint_trace TMerge_trace TOutput_file\n\
+ TWide_trace TVertex_stop TCone_stop\n\
+\n\
+ Angle_max Centrum_size Random_dist Ucoplanar_max Wide_outside\n\
+";
+
+/*-<a href="../libqhull/qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="main">-</a>
+
+ main( argc, argv )
+ processes the command line, calls qhull() to do the work, and exits
+
+ design:
+ initializes data structures
+ reads points
+ finishes initialization
+ computes convex hull and other structures
+ checks the result
+ writes the output
+ frees memory
+*/
+int main(int argc, char *argv[]) {
+ int curlong, totlong; /* used !qh_NOmem */
+ int exitcode, numpoints, dim;
+ coordT *points;
+ boolT ismalloc;
+
+ QHULL_LIB_CHECK /* Check for compatible library */
+
+ if ((argc == 1) && isatty( 0 /*stdin*/)) {
+ fprintf(stdout, qh_prompt2, qh_version);
+ exit(qh_ERRnone);
+ }
+ if (argc > 1 && *argv[1] == '-' && !*(argv[1]+1)) {
+ fprintf(stdout, qh_prompta, qh_version,
+ qh_promptb, qh_promptc, qh_promptd, qh_prompte);
+ exit(qh_ERRnone);
+ }
+ if (argc > 1 && *argv[1] == '.' && !*(argv[1]+1)) {
+ fprintf(stdout, qh_prompt3, qh_version);
+ exit(qh_ERRnone);
+ }
+ if (argc > 1 && *argv[1] == '-' && *(argv[1]+1)=='V') {
+ fprintf(stdout, "%s\n", qh_version2);
+ exit(qh_ERRnone);
+ }
+ qh_init_A(stdin, stdout, stderr, argc, argv); /* sets qh qhull_command */
+ exitcode= setjmp(qh errexit); /* simple statement for CRAY J916 */
+ if (!exitcode) {
+ qh NOerrexit = False;
+ qh_option("Halfspace", NULL, NULL);
+ qh HALFspace= True; /* 'H' */
+ qh_checkflags(qh qhull_command, hidden_options);
+ qh_initflags(qh qhull_command);
+ if (qh SCALEinput) {
+ fprintf(qh ferr, "\
+qhull error: options 'Qbk:n' and 'QBk:n' are not used with qhalf.\n\
+ Use 'Qbk:0Bk:0 to drop dimension k.\n");
+ qh_errexit(qh_ERRinput, NULL, NULL);
+ }
+ points= qh_readpoints(&numpoints, &dim, &ismalloc);
+ if (dim >= 5) {
+ qh_option("Qxact_merge", NULL, NULL);
+ qh MERGEexact= True; /* 'Qx' always */
+ }
+ qh_init_B(points, numpoints, dim, ismalloc);
+ qh_qhull();
+ qh_check_output();
+ qh_produce_output();
+ if (qh VERIFYoutput && !qh FORCEoutput && !qh STOPpoint && !qh STOPcone)
+ qh_check_points();
+ exitcode= qh_ERRnone;
+ }
+ qh NOerrexit= True; /* no more setjmp */
+#ifdef qh_NOmem
+ qh_freeqhull(qh_ALL);
+#else
+ qh_freeqhull(!qh_ALL);
+ qh_memfreeshort(&curlong, &totlong);
+ if (curlong || totlong)
+ qh_fprintf_stderr(6263, "qhull internal warning (main): did not free %d bytes of long memory(%d pieces)\n",
+ totlong, curlong);
+#endif
+ return exitcode;
+} /* main */
+
diff --git a/xs/src/qhull/src/qhalf/qhalf.pro b/xs/src/qhull/src/qhalf/qhalf.pro
new file mode 100644
index 000000000..ebad38789
--- /dev/null
+++ b/xs/src/qhull/src/qhalf/qhalf.pro
@@ -0,0 +1,9 @@
+# -------------------------------------------------
+# qhalf.pro -- Qt project file for qconvex.exe
+# -------------------------------------------------
+
+include(../qhull-app-c.pri)
+
+TARGET = qhalf
+
+SOURCES += qhalf.c
diff --git a/xs/src/qhull/src/qhalf/qhalf_r.c b/xs/src/qhull/src/qhalf/qhalf_r.c
new file mode 100644
index 000000000..c49d777f9
--- /dev/null
+++ b/xs/src/qhull/src/qhalf/qhalf_r.c
@@ -0,0 +1,318 @@
+/*<html><pre> -<a href="../libqhull/qh-qhull.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ qhalf.c
+ compute the intersection of halfspaces about a point
+
+ see unix.c for full interface
+
+ Copyright (c) 1993-2015, The Geometry Center
+*/
+
+#include "libqhull_r/libqhull_r.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+
+#if __cplusplus
+extern "C" {
+ int isatty(int);
+}
+
+#elif _MSC_VER
+#include <io.h>
+#define isatty _isatty
+/* int _isatty(int); */
+
+#else
+int isatty(int); /* returns 1 if stdin is a tty
+ if "Undefined symbol" this can be deleted along with call in main() */
+#endif
+
+/*-<a href="../libqhull/qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="prompt">-</a>
+
+ qh_prompt
+ long prompt for qhull
+
+ notes:
+ restricted version of libqhull.c
+
+ see:
+ concise prompt below
+*/
+
+/* duplicated in qhalf.htm */
+char hidden_options[]=" d n v Qbb QbB Qf Qg Qm Qr QR Qv Qx Qz TR E V Fa FA FC FD FS Ft FV Gt Q0 Q1 Q2 Q3 Q4 Q5 Q6 Q7 Q8 Q9 ";
+
+char qh_prompta[]= "\n\
+qhalf- compute the intersection of halfspaces about a point\n\
+ http://www.qhull.org %s\n\
+\n\
+input (stdin):\n\
+ optional interior point: dimension, 1, coordinates\n\
+ first lines: dimension+1 and number of halfspaces\n\
+ other lines: halfspace coefficients followed by offset\n\
+ comments: start with a non-numeric character\n\
+\n\
+options:\n\
+ Hn,n - specify coordinates of interior point\n\
+ Qt - triangulated output\n\
+ QJ - joggled input instead of merged facets\n\
+ Qc - keep coplanar halfspaces\n\
+ Qi - keep other redundant halfspaces\n\
+\n\
+Qhull control options:\n\
+ QJn - randomly joggle input in range [-n,n]\n\
+%s%s%s%s"; /* split up qh_prompt for Visual C++ */
+char qh_promptb[]= "\
+ Qbk:0Bk:0 - remove k-th coordinate from input\n\
+ Qs - search all halfspaces for the initial simplex\n\
+ QGn - print intersection if visible to halfspace n, -n for not\n\
+ QVn - print intersections for halfspace n, -n if not\n\
+\n\
+";
+char qh_promptc[]= "\
+Trace options:\n\
+ T4 - trace at level n, 4=all, 5=mem/gauss, -1= events\n\
+ Tc - check frequently during execution\n\
+ Ts - print statistics\n\
+ Tv - verify result: structure, convexity, and redundancy\n\
+ Tz - send all output to stdout\n\
+ TFn - report summary when n or more facets created\n\
+ TI file - input data from file, no spaces or single quotes\n\
+ TO file - output results to file, may be enclosed in single quotes\n\
+ TPn - turn on tracing when halfspace n added to intersection\n\
+ TMn - turn on tracing at merge n\n\
+ TWn - trace merge facets when width > n\n\
+ TVn - stop qhull after adding halfspace n, -n for before (see TCn)\n\
+ TCn - stop qhull after building cone for halfspace n (see TVn)\n\
+\n\
+Precision options:\n\
+ Cn - radius of centrum (roundoff added). Merge facets if non-convex\n\
+ An - cosine of maximum angle. Merge facets if cosine > n or non-convex\n\
+ C-0 roundoff, A-0.99/C-0.01 pre-merge, A0.99/C0.01 post-merge\n\
+ Rn - randomly perturb computations by a factor of [1-n,1+n]\n\
+ Un - max distance below plane for a new, coplanar halfspace\n\
+ Wn - min facet width for outside halfspace (before roundoff)\n\
+\n\
+Output formats (may be combined; if none, produces a summary to stdout):\n\
+ f - facet dump\n\
+ G - Geomview output (dual convex hull)\n\
+ i - non-redundant halfspaces incident to each intersection\n\
+ m - Mathematica output (dual convex hull)\n\
+ o - OFF format (dual convex hull: dimension, points, and facets)\n\
+ p - vertex coordinates of dual convex hull (coplanars if 'Qc' or 'Qi')\n\
+ s - summary (stderr)\n\
+\n\
+";
+char qh_promptd[]= "\
+More formats:\n\
+ Fc - count plus redundant halfspaces for each intersection\n\
+ - Qc (default) for coplanar and Qi for other redundant\n\
+ Fd - use cdd format for input (homogeneous with offset first)\n\
+ FF - facet dump without ridges\n\
+ FI - ID of each intersection\n\
+ Fm - merge count for each intersection (511 max)\n\
+ FM - Maple output (dual convex hull)\n\
+ Fn - count plus neighboring intersections for each intersection\n\
+ FN - count plus intersections for each non-redundant halfspace\n\
+ FO - options and precision constants\n\
+ Fp - dim, count, and intersection coordinates\n\
+ FP - nearest halfspace and distance for each redundant halfspace\n\
+ FQ - command used for qhalf\n\
+ Fs - summary: #int (8), dim, #halfspaces, #non-redundant, #intersections\n\
+ for output: #non-redundant, #intersections, #coplanar\n\
+ halfspaces, #non-simplicial intersections\n\
+ #real (2), max outer plane, min vertex\n\
+ Fv - count plus non-redundant halfspaces for each intersection\n\
+ Fx - non-redundant halfspaces\n\
+\n\
+";
+char qh_prompte[]= "\
+Geomview output (2-d, 3-d and 4-d; dual convex hull)\n\
+ Ga - all points (i.e., transformed halfspaces) as dots\n\
+ Gp - coplanar points and vertices as radii\n\
+ Gv - vertices (i.e., non-redundant halfspaces) as spheres\n\
+ Gi - inner planes (i.e., halfspace intersections) only\n\
+ Gn - no planes\n\
+ Go - outer planes only\n\
+ Gc - centrums\n\
+ Gh - hyperplane intersections\n\
+ Gr - ridges\n\
+ GDn - drop dimension n in 3-d and 4-d output\n\
+\n\
+Print options:\n\
+ PAn - keep n largest facets (i.e., intersections) by area\n\
+ Pdk:n- drop facet if normal[k] <= n (default 0.0)\n\
+ PDk:n- drop facet if normal[k] >= n\n\
+ Pg - print good facets (needs 'QGn' or 'QVn')\n\
+ PFn - keep facets whose area is at least n\n\
+ PG - print neighbors of good facets\n\
+ PMn - keep n facets with most merges\n\
+ Po - force output. If error, output neighborhood of facet\n\
+ Pp - do not report precision problems\n\
+\n\
+ . - list of all options\n\
+ - - one line descriptions of all options\n\
+ -V - version\n\
+";
+/* for opts, don't assign 'e' or 'E' to a flag (already used for exponent) */
+
+/*-<a href="../libqhull/qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="prompt2">-</a>
+
+ qh_prompt2
+ synopsis for qhull
+*/
+char qh_prompt2[]= "\n\
+qhalf- halfspace intersection about a point. Qhull %s\n\
+ input (stdin): [dim, 1, interior point], dim+1, n, coefficients+offset\n\
+ comments start with a non-numeric character\n\
+\n\
+options (qhalf.htm):\n\
+ Hn,n - specify coordinates of interior point\n\
+ Qt - triangulated output\n\
+ QJ - joggled input instead of merged facets\n\
+ Tv - verify result: structure, convexity, and redundancy\n\
+ . - concise list of all options\n\
+ - - one-line description of all options\n\
+ -V - version\n\
+\n\
+output options (subset):\n\
+ s - summary of results (default)\n\
+ Fp - intersection coordinates\n\
+ Fv - non-redundant halfspaces incident to each intersection\n\
+ Fx - non-redundant halfspaces\n\
+ o - OFF file format (dual convex hull)\n\
+ G - Geomview output (dual convex hull)\n\
+ m - Mathematica output (dual convex hull)\n\
+ QVn - print intersections for halfspace n, -n if not\n\
+ TO file - output results to file, may be enclosed in single quotes\n\
+\n\
+examples:\n\
+ rbox d | qconvex FQ n | qhalf s H0,0,0 Fp\n\
+ rbox c | qconvex FQ FV n | qhalf s i\n\
+ rbox c | qconvex FQ FV n | qhalf s o\n\
+\n\
+";
+/* for opts, don't assign 'e' or 'E' to a flag (already used for exponent) */
+
+/*-<a href="../libqhull/qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="prompt3">-</a>
+
+ qh_prompt3
+ concise prompt for qhull
+*/
+char qh_prompt3[]= "\n\
+Qhull %s.\n\
+Except for 'F.' and 'PG', upper_case options take an argument.\n\
+\n\
+ incidences Geomview mathematica OFF_format point_dual\n\
+ summary facet_dump\n\
+\n\
+ Fc_redundant Fd_cdd_in FF_dump_xridge FIDs Fmerges\n\
+ Fneighbors FN_intersect FOptions Fp_coordinates FP_nearest\n\
+ FQhalf Fsummary Fv_halfspace FMaple Fx_non_redundant\n\
+\n\
+ Gvertices Gpoints Gall_points Gno_planes Ginner\n\
+ Gcentrums Ghyperplanes Gridges Gouter GDrop_dim\n\
+\n\
+ PArea_keep Pdrop d0:0D0 Pgood PFacet_area_keep\n\
+ PGood_neighbors PMerge_keep Poutput_forced Pprecision_not\n\
+\n\
+ Qbk:0Bk:0_drop Qcoplanar QG_half_good Qi_redundant QJoggle\n\
+ Qsearch_1st Qtriangulate QVertex_good\n\
+\n\
+ T4_trace Tcheck_often Tstatistics Tverify Tz_stdout\n\
+ TFacet_log TInput_file TPoint_trace TMerge_trace TOutput_file\n\
+ TWide_trace TVertex_stop TCone_stop\n\
+\n\
+ Angle_max Centrum_size Random_dist Ucoplanar_max Wide_outside\n\
+";
+
+/*-<a href="../libqhull/qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="main">-</a>
+
+ main( argc, argv )
+ processes the command line, calls qhull() to do the work, and exits
+
+ design:
+ initializes data structures
+ reads points
+ finishes initialization
+ computes convex hull and other structures
+ checks the result
+ writes the output
+ frees memory
+*/
+int main(int argc, char *argv[]) {
+ int curlong, totlong; /* used !qh_NOmem */
+ int exitcode, numpoints, dim;
+ coordT *points;
+ boolT ismalloc;
+ qhT qh_qh;
+ qhT *qh= &qh_qh;
+
+ QHULL_LIB_CHECK /* Check for compatible library */
+
+ if ((argc == 1) && isatty( 0 /*stdin*/)) {
+ fprintf(stdout, qh_prompt2, qh_version);
+ exit(qh_ERRnone);
+ }
+ if (argc > 1 && *argv[1] == '-' && !*(argv[1]+1)) {
+ fprintf(stdout, qh_prompta, qh_version,
+ qh_promptb, qh_promptc, qh_promptd, qh_prompte);
+ exit(qh_ERRnone);
+ }
+ if (argc > 1 && *argv[1] == '.' && !*(argv[1]+1)) {
+ fprintf(stdout, qh_prompt3, qh_version);
+ exit(qh_ERRnone);
+ }
+ if (argc > 1 && *argv[1] == '-' && *(argv[1]+1)=='V') {
+ fprintf(stdout, "%s\n", qh_version2);
+ exit(qh_ERRnone);
+ }
+ qh_init_A(qh, stdin, stdout, stderr, argc, argv); /* sets qh->qhull_command */
+ exitcode= setjmp(qh->errexit); /* simple statement for CRAY J916 */
+ if (!exitcode) {
+ qh->NOerrexit = False;
+ qh_option(qh, "Halfspace", NULL, NULL);
+ qh->HALFspace= True; /* 'H' */
+ qh_checkflags(qh, qh->qhull_command, hidden_options);
+ qh_initflags(qh, qh->qhull_command);
+ if (qh->SCALEinput) {
+ fprintf(qh->ferr, "\
+qhull error: options 'Qbk:n' and 'QBk:n' are not used with qhalf.\n\
+ Use 'Qbk:0Bk:0 to drop dimension k.\n");
+ qh_errexit(qh, qh_ERRinput, NULL, NULL);
+ }
+ points= qh_readpoints(qh, &numpoints, &dim, &ismalloc);
+ if (dim >= 5) {
+ qh_option(qh, "Qxact_merge", NULL, NULL);
+ qh->MERGEexact= True; /* 'Qx' always */
+ }
+ qh_init_B(qh, points, numpoints, dim, ismalloc);
+ qh_qhull(qh);
+ qh_check_output(qh);
+ qh_produce_output(qh);
+ if (qh->VERIFYoutput && !qh->FORCEoutput && !qh->STOPpoint && !qh->STOPcone)
+ qh_check_points(qh);
+ exitcode= qh_ERRnone;
+ }
+ qh->NOerrexit= True; /* no more setjmp */
+#ifdef qh_NOmem
+ qh_freeqhull(qh, qh_ALL);
+#else
+ qh_freeqhull(qh, !qh_ALL);
+ qh_memfreeshort(qh, &curlong, &totlong);
+ if (curlong || totlong)
+ qh_fprintf_stderr(6263, "qhull internal warning (main): did not free %d bytes of long memory(%d pieces)\n",
+ totlong, curlong);
+#endif
+ return exitcode;
+} /* main */
+
diff --git a/xs/src/qhull/src/qhull-all.pro b/xs/src/qhull/src/qhull-all.pro
new file mode 100644
index 000000000..1d3a0ac6f
--- /dev/null
+++ b/xs/src/qhull/src/qhull-all.pro
@@ -0,0 +1,94 @@
+# -------------------------------------------------
+# qhull-all.pro -- Qt project to build executables and static libraries
+#
+# To build with Qt on mingw
+# Download Qt SDK, install Perl
+# /c/qt/2010.05/qt> ./configure -static -platform win32-g++ -fast -no-qt3support
+#
+# To build DevStudio sln and proj files (Qhull ships with cmake derived files)
+# qmake is in Qt's bin directory
+# mkdir -p build && cd build && qmake -tp vc -r ../src/qhull-all.pro
+# Additional Library Directories -- C:\qt\Qt5.2.0\5.2.0\msvc2012_64\lib
+# libqhullcpp and libqhullstatic refered to $(QTDIR) but apparently didn't retrieve (should be %QTDIR%?)
+# libqhull_r also needs ..\..\lib
+# Need to change build/x64/Debug/*.lib to lib/ (or copy libs by hand, each time)
+# Additional Build Dependencies
+# See README.txt -- Need to add Build Dependencies, disable rtti, rename targets to qhull.dll, qhull6_p.dll and qhull6_pd.dll
+# -------------------------------------------------
+
+TEMPLATE = subdirs
+CONFIG += ordered
+
+SUBDIRS += libqhull_r #shared library with reentrant code
+SUBDIRS += libqhullstatic #static library
+SUBDIRS += libqhullstatic_r #static library with reentrant code
+SUBDIRS += libqhullcpp #static library for C++ interface with libqhullstatic_r
+
+SUBDIRS += qhull #qhull program linked to libqhullstatic_r
+SUBDIRS += rbox
+SUBDIRS += qconvex #qhull programs linked to libqhullstatic
+SUBDIRS += qdelaunay
+SUBDIRS += qhalf
+SUBDIRS += qvoronoi
+
+SUBDIRS += user_eg #user programs linked to libqhull_r
+SUBDIRS += user_eg2
+SUBDIRS += user_eg3 #user program with libqhullcpp and libqhullstatic_r
+
+SUBDIRS += qhulltest #C++ test program with Qt, libqhullcpp, and libqhullstatic_r
+SUBDIRS += testqset #test program for qset.c with mem.c
+SUBDIRS += testqset_r #test program for qset_r.c with mem_r.c
+ #See eg/q_test for qhull tests
+
+OTHER_FILES += Changes.txt
+OTHER_FILES += CMakeLists.txt
+OTHER_FILES += Make-config.sh
+OTHER_FILES += ../Announce.txt
+OTHER_FILES += ../CMakeLists.txt
+OTHER_FILES += ../COPYING.txt
+OTHER_FILES += ../File_id.diz
+OTHER_FILES += ../index.htm
+OTHER_FILES += ../Makefile
+OTHER_FILES += ../README.txt
+OTHER_FILES += ../REGISTER.txt
+OTHER_FILES += ../eg/q_eg
+OTHER_FILES += ../eg/q_egtest
+OTHER_FILES += ../eg/q_test
+OTHER_FILES += ../html/index.htm
+OTHER_FILES += ../html/qconvex.htm
+OTHER_FILES += ../html/qdelau_f.htm
+OTHER_FILES += ../html/qdelaun.htm
+OTHER_FILES += ../html/qhalf.htm
+OTHER_FILES += ../html/qh-code.htm
+OTHER_FILES += ../html/qh-eg.htm
+OTHER_FILES += ../html/qh-faq.htm
+OTHER_FILES += ../html/qh-get.htm
+OTHER_FILES += ../html/qh-impre.htm
+OTHER_FILES += ../html/qh-optc.htm
+OTHER_FILES += ../html/qh-optf.htm
+OTHER_FILES += ../html/qh-optg.htm
+OTHER_FILES += ../html/qh-opto.htm
+OTHER_FILES += ../html/qh-optp.htm
+OTHER_FILES += ../html/qh-optq.htm
+OTHER_FILES += ../html/qh-optt.htm
+OTHER_FILES += ../html/qh-quick.htm
+OTHER_FILES += ../html/qhull.htm
+OTHER_FILES += ../html/qhull.man
+OTHER_FILES += ../html/qhull.txt
+OTHER_FILES += ../html/qhull-cpp.xml
+OTHER_FILES += ../html/qvoron_f.htm
+OTHER_FILES += ../html/qvoronoi.htm
+OTHER_FILES += ../html/rbox.htm
+OTHER_FILES += ../html/rbox.man
+OTHER_FILES += ../html/rbox.txt
+OTHER_FILES += ../src/libqhull/Makefile
+OTHER_FILES += ../src/libqhull_r/Makefile
+OTHER_FILES += ../src/libqhull_r/qhull_r-exports.def
+OTHER_FILES += ../src/qconvex/qconvex_r.c
+OTHER_FILES += ../src/qdelaunay/qdelaun_r.c
+OTHER_FILES += ../src/qhalf/qhalf_r.c
+OTHER_FILES += ../src/qhull/rbox_r.c
+OTHER_FILES += ../src/qvoronoi/qvoronoi_r.c
+OTHER_FILES += ../src/qhull/unix.c
+OTHER_FILES += ../src/user_eg/user_eg.c
+OTHER_FILES += ../src/user_eg2/user_eg2.c
diff --git a/xs/src/qhull/src/qhull-app-c.pri b/xs/src/qhull/src/qhull-app-c.pri
new file mode 100644
index 000000000..05e5a00f2
--- /dev/null
+++ b/xs/src/qhull/src/qhull-app-c.pri
@@ -0,0 +1,24 @@
+# -------------------------------------------------
+# qhull-app-c.pri -- Qt include project for C qhull applications linked to libqhull
+# -------------------------------------------------
+
+include(qhull-warn.pri)
+
+DESTDIR = ../../bin
+TEMPLATE = app
+CONFIG += console warn_on
+CONFIG -= qt
+
+LIBS += -L../../lib
+build_pass:CONFIG(debug, debug|release){
+ LIBS += -lqhullstatic_d
+ OBJECTS_DIR = Debug
+}else:build_pass:CONFIG(release, debug|release){
+ LIBS += -lqhullstatic
+ OBJECTS_DIR = Release
+}
+win32-msvc* : QMAKE_LFLAGS += /INCREMENTAL:NO
+
+INCLUDEPATH += ..
+CONFIG += qhull_warn_conversion
+
diff --git a/xs/src/qhull/src/qhull-app-c_r.pri b/xs/src/qhull/src/qhull-app-c_r.pri
new file mode 100644
index 000000000..9c2ef5600
--- /dev/null
+++ b/xs/src/qhull/src/qhull-app-c_r.pri
@@ -0,0 +1,26 @@
+# -------------------------------------------------
+# qhull-app-c_r.pri -- Qt include project for C qhull applications linked to qhullstatic_r
+#
+# It uses reentrant Qhull
+# -------------------------------------------------
+
+include(qhull-warn.pri)
+
+DESTDIR = ../../bin
+TEMPLATE = app
+CONFIG += console warn_on
+CONFIG -= qt
+
+LIBS += -L../../lib
+build_pass:CONFIG(debug, debug|release){
+ LIBS += -lqhullstatic_rd
+ OBJECTS_DIR = Debug
+}else:build_pass:CONFIG(release, debug|release){
+ LIBS += -lqhullstatic_r
+ OBJECTS_DIR = Release
+}
+win32-msvc* : QMAKE_LFLAGS += /INCREMENTAL:NO
+
+INCLUDEPATH += ..
+CONFIG += qhull_warn_conversion
+
diff --git a/xs/src/qhull/src/qhull-app-cpp.pri b/xs/src/qhull/src/qhull-app-cpp.pri
new file mode 100644
index 000000000..a6f17d8ec
--- /dev/null
+++ b/xs/src/qhull/src/qhull-app-cpp.pri
@@ -0,0 +1,23 @@
+# -------------------------------------------------
+# qhull-app-cpp.pri -- Qt include project for qhull as C++ classes
+# -------------------------------------------------
+
+include(qhull-warn.pri)
+
+DESTDIR = ../../bin
+TEMPLATE = app
+CONFIG += console warn_on
+CONFIG -= rtti
+LIBS += -L../../lib
+build_pass:CONFIG(debug, debug|release){
+ LIBS += -lqhullcpp_d
+ LIBS += -lqhullstatic_rd # Must be last, otherwise qh_fprintf,etc. are loaded from here instead of qhullcpp-d.lib
+ OBJECTS_DIR = Debug
+}else:build_pass:CONFIG(release, debug|release){
+ LIBS += -lqhullcpp
+ LIBS += -lqhullstatic_r # Must be last, otherwise qh_fprintf,etc. are loaded from here instead of qhullcpp.lib
+ OBJECTS_DIR = Release
+}
+win32-msvc* : QMAKE_LFLAGS += /INCREMENTAL:NO
+
+INCLUDEPATH += ../../src # "libqhull_r/qhull_a.h"
diff --git a/xs/src/qhull/src/qhull-app-shared.pri b/xs/src/qhull/src/qhull-app-shared.pri
new file mode 100644
index 000000000..1f4026a6a
--- /dev/null
+++ b/xs/src/qhull/src/qhull-app-shared.pri
@@ -0,0 +1,27 @@
+# -------------------------------------------------
+# qhull-app-shared.pri -- Deprecated Qt include project for C qhull applications linked with libqhull (shared library)
+# -------------------------------------------------
+
+include(qhull-warn.pri)
+
+DESTDIR = ../../bin
+TEMPLATE = app
+CONFIG += console warn_on
+CONFIG -= qt
+
+LIBS += -L../../lib
+build_pass:CONFIG(debug, debug|release){
+ LIBS += -lqhull_d
+ OBJECTS_DIR = Debug
+}else:build_pass:CONFIG(release, debug|release){
+ LIBS += -lqhull
+ OBJECTS_DIR = Release
+}
+win32-msvc* : QMAKE_LFLAGS += /INCREMENTAL:NO
+
+win32-msvc* : DEFINES += qh_dllimport # libqhull/user.h
+
+INCLUDEPATH += ../libqhull
+CONFIG += qhull_warn_conversion
+
+
diff --git a/xs/src/qhull/src/qhull-app-shared_r.pri b/xs/src/qhull/src/qhull-app-shared_r.pri
new file mode 100644
index 000000000..e55c1a65f
--- /dev/null
+++ b/xs/src/qhull/src/qhull-app-shared_r.pri
@@ -0,0 +1,29 @@
+# -------------------------------------------------
+# qhull-app-shared_r.pri -- Qt include project for C qhull applications linked with libqhull_r (shared library)
+#
+# It uses reentrant Qhull
+# -------------------------------------------------
+
+include(qhull-warn.pri)
+
+DESTDIR = ../../bin
+TEMPLATE = app
+CONFIG += console warn_on
+CONFIG -= qt
+
+LIBS += -L../../lib
+build_pass:CONFIG(debug, debug|release){
+ LIBS += -lqhull_rd
+ OBJECTS_DIR = Debug
+}else:build_pass:CONFIG(release, debug|release){
+ LIBS += -lqhull_r
+ OBJECTS_DIR = Release
+}
+win32-msvc* : QMAKE_LFLAGS += /INCREMENTAL:NO
+
+win32-msvc* : DEFINES += qh_dllimport # libqhull_r/user.h
+
+INCLUDEPATH += ..
+CONFIG += qhull_warn_conversion
+
+
diff --git a/xs/src/qhull/src/qhull-libqhull-src.pri b/xs/src/qhull/src/qhull-libqhull-src.pri
new file mode 100644
index 000000000..e7aff3f78
--- /dev/null
+++ b/xs/src/qhull/src/qhull-libqhull-src.pri
@@ -0,0 +1,39 @@
+# -------------------------------------------------
+# qhull-libqhull-src.pri -- Qt include project for libqhull sources and headers
+# libqhull.pro, libqhullp.pro, and libqhulldll.pro are the same for SOURCES and HEADERS
+# -------------------------------------------------
+
+# Order object files by frequency of execution. Small files at end.
+# Current directory is caller
+
+# libqhull/libqhull.pro and ../qhull-libqhull-src.pri have the same SOURCES and HEADERS
+SOURCES += ../libqhull/global.c
+SOURCES += ../libqhull/stat.c
+SOURCES += ../libqhull/geom2.c
+SOURCES += ../libqhull/poly2.c
+SOURCES += ../libqhull/merge.c
+SOURCES += ../libqhull/libqhull.c
+SOURCES += ../libqhull/geom.c
+SOURCES += ../libqhull/poly.c
+SOURCES += ../libqhull/qset.c
+SOURCES += ../libqhull/mem.c
+SOURCES += ../libqhull/random.c
+SOURCES += ../libqhull/usermem.c
+SOURCES += ../libqhull/userprintf.c
+SOURCES += ../libqhull/io.c
+SOURCES += ../libqhull/user.c
+SOURCES += ../libqhull/rboxlib.c
+SOURCES += ../libqhull/userprintf_rbox.c
+
+# [2014] qmake locates the headers in the shadow build directory not the src directory
+HEADERS += ../libqhull/geom.h
+HEADERS += ../libqhull/io.h
+HEADERS += ../libqhull/libqhull.h
+HEADERS += ../libqhull/mem.h
+HEADERS += ../libqhull/merge.h
+HEADERS += ../libqhull/poly.h
+HEADERS += ../libqhull/random.h
+HEADERS += ../libqhull/qhull_a.h
+HEADERS += ../libqhull/qset.h
+HEADERS += ../libqhull/stat.h
+HEADERS += ../libqhull/user.h
diff --git a/xs/src/qhull/src/qhull-libqhull-src_r.pri b/xs/src/qhull/src/qhull-libqhull-src_r.pri
new file mode 100644
index 000000000..3b53291b1
--- /dev/null
+++ b/xs/src/qhull/src/qhull-libqhull-src_r.pri
@@ -0,0 +1,39 @@
+# -------------------------------------------------
+# qhull-libqhull-src_r.pri -- Qt include project for libqhull_r sources and headers
+#
+# It uses reentrant Qhull
+# -------------------------------------------------
+
+# Order object files by frequency of execution. Small files at end.
+# Current directory is caller
+
+# libqhull_r/libqhull_r.pro and ../qhull-libqhull-src_r.pri have the same SOURCES and HEADERS
+SOURCES += ../libqhull_r/global_r.c
+SOURCES += ../libqhull_r/stat_r.c
+SOURCES += ../libqhull_r/geom2_r.c
+SOURCES += ../libqhull_r/poly2_r.c
+SOURCES += ../libqhull_r/merge_r.c
+SOURCES += ../libqhull_r/libqhull_r.c
+SOURCES += ../libqhull_r/geom_r.c
+SOURCES += ../libqhull_r/poly_r.c
+SOURCES += ../libqhull_r/qset_r.c
+SOURCES += ../libqhull_r/mem_r.c
+SOURCES += ../libqhull_r/random_r.c
+SOURCES += ../libqhull_r/usermem_r.c
+SOURCES += ../libqhull_r/userprintf_r.c
+SOURCES += ../libqhull_r/io_r.c
+SOURCES += ../libqhull_r/user_r.c
+SOURCES += ../libqhull_r/rboxlib_r.c
+SOURCES += ../libqhull_r/userprintf_rbox_r.c
+
+HEADERS += ../libqhull_r/geom_r.h
+HEADERS += ../libqhull_r/io_r.h
+HEADERS += ../libqhull_r/libqhull_r.h
+HEADERS += ../libqhull_r/mem_r.h
+HEADERS += ../libqhull_r/merge_r.h
+HEADERS += ../libqhull_r/poly_r.h
+HEADERS += ../libqhull_r/random_r.h
+HEADERS += ../libqhull_r/qhull_ra.h
+HEADERS += ../libqhull_r/qset_r.h
+HEADERS += ../libqhull_r/stat_r.h
+HEADERS += ../libqhull_r/user_r.h
diff --git a/xs/src/qhull/src/qhull-warn.pri b/xs/src/qhull/src/qhull-warn.pri
new file mode 100644
index 000000000..7d0e7fa2f
--- /dev/null
+++ b/xs/src/qhull/src/qhull-warn.pri
@@ -0,0 +1,57 @@
+# -------------------------------------------------
+# qhull-warn.pri -- Qt project warnings for warn_on
+# CONFIG += qhull_warn_all # Qhull compiles with all warnings except for qhull_warn_shadow and qhull_warn_conversion
+# CONFIG += qhull_warn_conversion # Warn in Qt and Qhull about conversion errors
+# CONFIG += qhull_warn_error # Turn warnings into errors
+# CONFIG += qhull_warn_shadow # Warn in Qt about shadowing of functions and fields
+# -------------------------------------------------
+
+# [apr'11] VERSION works erratically for msvc builds
+# VERSION = 7.2.0
+qhull_SOVERSION = 7
+
+# Uncomment to report warnings as errors
+#CONFIG += qhull_warn_error
+
+*g++{
+ qhull_warn_error{
+ QMAKE_CFLAGS_WARN_ON += -Werror
+ QMAKE_CXXFLAGS_WARN_ON += -Werror
+ }
+
+ QMAKE_CFLAGS_WARN_ON += -Wcast-qual -Wextra -Wshadow -Wwrite-strings
+
+ QMAKE_CXXFLAGS_WARN_ON += -Wcast-qual -Wextra -Wwrite-strings
+ QMAKE_CXXFLAGS_WARN_ON += -Wno-sign-conversion
+
+ qhull_warn_shadow{
+ QMAKE_CXXFLAGS_WARN_ON += -Wshadow # Shadowing occurs in Qt, e.g., nested foreach
+ }
+
+ qhull_warn_conversion{
+ QMAKE_CFLAGS_WARN_ON += -Wno-sign-conversion # libqhullstatic has many size_t vs. int warnings
+ QMAKE_CFLAGS_WARN_ON += -Wconversion # libqhullstatic has no workaround for bit-field conversions
+ QMAKE_CXXFLAGS_WARN_ON += -Wconversion # Qt has conversion errors for qbitarray and qpalette
+ }
+
+ qhull_warn_all{
+ QMAKE_CFLAGS_WARN_ON += -Waddress -Warray-bounds -Wchar-subscripts -Wclobbered -Wcomment -Wempty-body
+ QMAKE_CFLAGS_WARN_ON += -Wformat -Wignored-qualifiers -Wimplicit-function-declaration -Wimplicit-int
+ QMAKE_CFLAGS_WARN_ON += -Wmain -Wmissing-braces -Wmissing-field-initializers -Wmissing-parameter-type
+ QMAKE_CFLAGS_WARN_ON += -Wnonnull -Wold-style-declaration -Woverride-init -Wparentheses
+ QMAKE_CFLAGS_WARN_ON += -Wpointer-sign -Wreturn-type -Wsequence-point -Wsign-compare
+ QMAKE_CFLAGS_WARN_ON += -Wsign-compare -Wstrict-aliasing -Wstrict-overflow=1 -Wswitch
+ QMAKE_CFLAGS_WARN_ON += -Wtrigraphs -Wtype-limits -Wuninitialized -Wuninitialized
+ QMAKE_CFLAGS_WARN_ON += -Wunknown-pragmas -Wunused-function -Wunused-label -Wunused-parameter
+ QMAKE_CFLAGS_WARN_ON += -Wunused-value -Wunused-variable -Wvolatile-register-var
+
+ QMAKE_CXXFLAGS_WARN_ON += -Waddress -Warray-bounds -Wc++0x-compat -Wchar-subscripts
+ QMAKE_CXXFLAGS_WARN_ON += -Wclobbered -Wcomment -Wempty-body -Wenum-compare
+ QMAKE_CXXFLAGS_WARN_ON += -Wformat -Wignored-qualifiers -Wmain -Wmissing-braces
+ QMAKE_CXXFLAGS_WARN_ON += -Wmissing-field-initializers -Wparentheses -Wreorder -Wreturn-type
+ QMAKE_CXXFLAGS_WARN_ON += -Wsequence-point -Wsign-compare -Wsign-compare -Wstrict-aliasing
+ QMAKE_CXXFLAGS_WARN_ON += -Wstrict-overflow=1 -Wswitch -Wtrigraphs -Wtype-limits
+ QMAKE_CXXFLAGS_WARN_ON += -Wuninitialized -Wunknown-pragmas -Wunused-function -Wunused-label
+ QMAKE_CXXFLAGS_WARN_ON += -Wunused-parameter -Wunused-value -Wunused-variable -Wvolatile-register-var
+ }
+}
diff --git a/xs/src/qhull/src/qhull/qhull.pro b/xs/src/qhull/src/qhull/qhull.pro
new file mode 100644
index 000000000..839372856
--- /dev/null
+++ b/xs/src/qhull/src/qhull/qhull.pro
@@ -0,0 +1,9 @@
+# -------------------------------------------------
+# qhull.pro -- Qt project file for qhull.exe with libqhullstatic_r
+# -------------------------------------------------
+
+include(../qhull-app-c_r.pri)
+
+TARGET = qhull
+
+SOURCES += unix_r.c
diff --git a/xs/src/qhull/src/qhull/unix.c b/xs/src/qhull/src/qhull/unix.c
new file mode 100644
index 000000000..892a819c3
--- /dev/null
+++ b/xs/src/qhull/src/qhull/unix.c
@@ -0,0 +1,372 @@
+/*<html><pre> -<a href="../libqhull/qh-qhull.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ unix.c
+ command line interface to qhull
+ includes SIOUX interface for Macintoshes
+
+ see qh-qhull.htm
+
+ Copyright (c) 1993-2015 The Geometry Center.
+ $Id: //main/2015/qhull/src/qhull/unix.c#4 $$Change: 2066 $
+ $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
+*/
+
+#include "libqhull/libqhull.h"
+#include "libqhull/qset.h"
+
+#include <ctype.h>
+#include <math.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#if __cplusplus
+extern "C" {
+ int isatty(int);
+}
+
+#elif _MSC_VER
+#include <io.h>
+#define isatty _isatty
+/* int _isatty(int); */
+
+#else
+int isatty(int); /* returns 1 if stdin is a tty
+ if "Undefined symbol" this can be deleted along with call in main() */
+#endif
+
+/*-<a href="../libqhull/qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="prompt">-</a>
+
+ qh_prompt
+ long prompt for qhull
+
+ see:
+ concise prompt below
+*/
+char qh_prompta[]= "\n\
+qhull- compute convex hulls and related structures.\n\
+ http://www.qhull.org %s\n\
+\n\
+input (stdin):\n\
+ first lines: dimension and number of points (or vice-versa).\n\
+ other lines: point coordinates, best if one point per line\n\
+ comments: start with a non-numeric character\n\
+ halfspaces: use dim plus one and put offset after coefficients.\n\
+ May be preceded by a single interior point ('H').\n\
+\n\
+options:\n\
+ d - Delaunay triangulation by lifting points to a paraboloid\n\
+ d Qu - furthest-site Delaunay triangulation (upper convex hull)\n\
+ v - Voronoi diagram (dual of the Delaunay triangulation)\n\
+ v Qu - furthest-site Voronoi diagram\n\
+ Hn,n,... - halfspace intersection about point [n,n,0,...]\n\
+ Qt - triangulated output\n\
+ QJ - joggled input instead of merged facets\n\
+ Qc - keep coplanar points with nearest facet\n\
+ Qi - keep interior points with nearest facet\n\
+\n\
+Qhull control options:\n\
+ Qbk:n - scale coord k so that low bound is n\n\
+ QBk:n - scale coord k so that upper bound is n (QBk is %2.2g)\n\
+ QbB - scale input to unit cube centered at the origin\n\
+ Qbb - scale last coordinate to [0,m] for Delaunay triangulations\n\
+ Qbk:0Bk:0 - remove k-th coordinate from input\n\
+ QJn - randomly joggle input in range [-n,n]\n\
+ QRn - random rotation (n=seed, n=0 time, n=-1 time/no rotate)\n\
+%s%s%s%s"; /* split up qh_prompt for Visual C++ */
+char qh_promptb[]= "\
+ Qf - partition point to furthest outside facet\n\
+ Qg - only build good facets (needs 'QGn', 'QVn', or 'PdD')\n\
+ Qm - only process points that would increase max_outside\n\
+ Qr - process random outside points instead of furthest ones\n\
+ Qs - search all points for the initial simplex\n\
+ Qu - for 'd' or 'v', compute upper hull without point at-infinity\n\
+ returns furthest-site Delaunay triangulation\n\
+ Qv - test vertex neighbors for convexity\n\
+ Qx - exact pre-merges (skips coplanar and angle-coplanar facets)\n\
+ Qz - add point-at-infinity to Delaunay triangulation\n\
+ QGn - good facet if visible from point n, -n for not visible\n\
+ QVn - good facet if it includes point n, -n if not\n\
+ Q0 - turn off default premerge with 'C-0'/'Qx'\n\
+ Q1 - sort merges by type instead of angle\n\
+ Q2 - merge all non-convex at once instead of independent sets\n\
+ Q3 - do not merge redundant vertices\n\
+ Q4 - avoid old->new merges\n\
+ Q5 - do not correct outer planes at end of qhull\n\
+ Q6 - do not pre-merge concave or coplanar facets\n\
+ Q7 - depth-first processing instead of breadth-first\n\
+ Q8 - do not process near-inside points\n\
+ Q9 - process furthest of furthest points\n\
+ Q10 - no special processing for narrow distributions\n\
+ Q11 - copy normals and recompute centrums for tricoplanar facets\n\
+ Q12 - no error on wide merge due to duplicate ridge\n\
+\n\
+";
+char qh_promptc[]= "\
+Topts- Trace options:\n\
+ T4 - trace at level n, 4=all, 5=mem/gauss, -1= events\n\
+ Ta - annotate output with message codes\n\
+ Tc - check frequently during execution\n\
+ Ts - print statistics\n\
+ Tv - verify result: structure, convexity, and point inclusion\n\
+ Tz - send all output to stdout\n\
+ TFn - report summary when n or more facets created\n\
+ TI file - input data from file, no spaces or single quotes\n\
+ TO file - output results to file, may be enclosed in single quotes\n\
+ TPn - turn on tracing when point n added to hull\n\
+ TMn - turn on tracing at merge n\n\
+ TWn - trace merge facets when width > n\n\
+ TRn - rerun qhull n times. Use with 'QJn'\n\
+ TVn - stop qhull after adding point n, -n for before (see TCn)\n\
+ TCn - stop qhull after building cone for point n (see TVn)\n\
+\n\
+Precision options:\n\
+ Cn - radius of centrum (roundoff added). Merge facets if non-convex\n\
+ An - cosine of maximum angle. Merge facets if cosine > n or non-convex\n\
+ C-0 roundoff, A-0.99/C-0.01 pre-merge, A0.99/C0.01 post-merge\n\
+ En - max roundoff error for distance computation\n\
+ Rn - randomly perturb computations by a factor of [1-n,1+n]\n\
+ Vn - min distance above plane for a visible facet (default 3C-n or En)\n\
+ Un - max distance below plane for a new, coplanar point (default Vn)\n\
+ Wn - min facet width for outside point (before roundoff, default 2Vn)\n\
+\n\
+Output formats (may be combined; if none, produces a summary to stdout):\n\
+ f - facet dump\n\
+ G - Geomview output (see below)\n\
+ i - vertices incident to each facet\n\
+ m - Mathematica output (2-d and 3-d)\n\
+ o - OFF format (dim, points and facets; Voronoi regions)\n\
+ n - normals with offsets\n\
+ p - vertex coordinates or Voronoi vertices (coplanar points if 'Qc')\n\
+ s - summary (stderr)\n\
+\n\
+";
+char qh_promptd[]= "\
+More formats:\n\
+ Fa - area for each facet\n\
+ FA - compute total area and volume for option 's'\n\
+ Fc - count plus coplanar points for each facet\n\
+ use 'Qc' (default) for coplanar and 'Qi' for interior\n\
+ FC - centrum or Voronoi center for each facet\n\
+ Fd - use cdd format for input (homogeneous with offset first)\n\
+ FD - use cdd format for numeric output (offset first)\n\
+ FF - facet dump without ridges\n\
+ Fi - inner plane for each facet\n\
+ for 'v', separating hyperplanes for bounded Voronoi regions\n\
+ FI - ID of each facet\n\
+ Fm - merge count for each facet (511 max)\n\
+ FM - Maple output (2-d and 3-d)\n\
+ Fn - count plus neighboring facets for each facet\n\
+ FN - count plus neighboring facets for each point\n\
+ Fo - outer plane (or max_outside) for each facet\n\
+ for 'v', separating hyperplanes for unbounded Voronoi regions\n\
+ FO - options and precision constants\n\
+ Fp - dim, count, and intersection coordinates (halfspace only)\n\
+ FP - nearest vertex and distance for each coplanar point\n\
+ FQ - command used for qhull\n\
+ Fs - summary: #int (8), dimension, #points, tot vertices, tot facets,\n\
+ output: #vertices, #facets, #coplanars, #nonsimplicial\n\
+ #real (2), max outer plane, min vertex\n\
+ FS - sizes: #int (0)\n\
+ #real (2) tot area, tot volume\n\
+ Ft - triangulation with centrums for non-simplicial facets (OFF format)\n\
+ Fv - count plus vertices for each facet\n\
+ for 'v', Voronoi diagram as Voronoi vertices for pairs of sites\n\
+ FV - average of vertices (a feasible point for 'H')\n\
+ Fx - extreme points (in order for 2-d)\n\
+\n\
+";
+char qh_prompte[]= "\
+Geomview options (2-d, 3-d, and 4-d; 2-d Voronoi)\n\
+ Ga - all points as dots\n\
+ Gp - coplanar points and vertices as radii\n\
+ Gv - vertices as spheres\n\
+ Gi - inner planes only\n\
+ Gn - no planes\n\
+ Go - outer planes only\n\
+ Gc - centrums\n\
+ Gh - hyperplane intersections\n\
+ Gr - ridges\n\
+ GDn - drop dimension n in 3-d and 4-d output\n\
+ Gt - for 3-d 'd', transparent outer ridges\n\
+\n\
+Print options:\n\
+ PAn - keep n largest facets by area\n\
+ Pdk:n - drop facet if normal[k] <= n (default 0.0)\n\
+ PDk:n - drop facet if normal[k] >= n\n\
+ Pg - print good facets (needs 'QGn' or 'QVn')\n\
+ PFn - keep facets whose area is at least n\n\
+ PG - print neighbors of good facets\n\
+ PMn - keep n facets with most merges\n\
+ Po - force output. If error, output neighborhood of facet\n\
+ Pp - do not report precision problems\n\
+\n\
+ . - list of all options\n\
+ - - one line descriptions of all options\n\
+ -V - version\n\
+";
+/* for opts, don't assign 'e' or 'E' to a flag (already used for exponent) */
+
+/*-<a href="../libqhull/qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="prompt2">-</a>
+
+ qh_prompt2
+ synopsis for qhull
+*/
+char qh_prompt2[]= "\n\
+qhull- compute convex hulls and related structures. Qhull %s\n\
+ input (stdin): dimension, n, point coordinates\n\
+ comments start with a non-numeric character\n\
+ halfspace: use dim+1 and put offsets after coefficients\n\
+\n\
+options (qh-quick.htm):\n\
+ d - Delaunay triangulation by lifting points to a paraboloid\n\
+ d Qu - furthest-site Delaunay triangulation (upper convex hull)\n\
+ v - Voronoi diagram as the dual of the Delaunay triangulation\n\
+ v Qu - furthest-site Voronoi diagram\n\
+ H1,1 - Halfspace intersection about [1,1,0,...] via polar duality\n\
+ Qt - triangulated output\n\
+ QJ - joggled input instead of merged facets\n\
+ Tv - verify result: structure, convexity, and point inclusion\n\
+ . - concise list of all options\n\
+ - - one-line description of each option\n\
+ -V - version\n\
+\n\
+Output options (subset):\n\
+ s - summary of results (default)\n\
+ i - vertices incident to each facet\n\
+ n - normals with offsets\n\
+ p - vertex coordinates (if 'Qc', includes coplanar points)\n\
+ if 'v', Voronoi vertices\n\
+ Fp - halfspace intersections\n\
+ Fx - extreme points (convex hull vertices)\n\
+ FA - compute total area and volume\n\
+ o - OFF format (if 'v', outputs Voronoi regions)\n\
+ G - Geomview output (2-d, 3-d and 4-d)\n\
+ m - Mathematica output (2-d and 3-d)\n\
+ QVn - print facets that include point n, -n if not\n\
+ TO file- output results to file, may be enclosed in single quotes\n\
+\n\
+examples:\n\
+ rbox D4 | qhull Tv rbox 1000 s | qhull Tv s FA\n\
+ rbox 10 D2 | qhull d QJ s i TO result rbox 10 D2 | qhull v Qbb Qt p\n\
+ rbox 10 D2 | qhull d Qu QJ m rbox 10 D2 | qhull v Qu QJ o\n\
+ rbox c d D2 | qhull Qc s f Fx | more rbox c | qhull FV n | qhull H Fp\n\
+ rbox d D12 | qhull QR0 FA rbox c D7 | qhull FA TF1000\n\
+ rbox y 1000 W0 | qhull rbox c | qhull n\n\
+\n\
+";
+/* for opts, don't assign 'e' or 'E' to a flag (already used for exponent) */
+
+/*-<a href="../libqhull/qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="prompt3">-</a>
+
+ qh_prompt3
+ concise prompt for qhull
+*/
+char qh_prompt3[]= "\n\
+Qhull %s.\n\
+Except for 'F.' and 'PG', upper-case options take an argument.\n\
+\n\
+ delaunay voronoi Geomview Halfspace facet_dump\n\
+ incidences mathematica normals OFF_format points\n\
+ summary\n\
+\n\
+ Farea FArea-total Fcoplanars FCentrums Fd-cdd-in\n\
+ FD-cdd-out FF-dump-xridge Finner FIDs Fmerges\n\
+ Fneighbors FNeigh-vertex Fouter FOptions Fpoint-intersect\n\
+ FPoint_near FQhull Fsummary FSize Ftriangles\n\
+ Fvertices Fvoronoi FVertex-ave Fxtremes FMaple\n\
+\n\
+ Gvertices Gpoints Gall_points Gno_planes Ginner\n\
+ Gcentrums Ghyperplanes Gridges Gouter GDrop_dim\n\
+ Gtransparent\n\
+\n\
+ PArea-keep Pdrop d0:0D0 Pgood PFacet_area_keep\n\
+ PGood_neighbors PMerge-keep Poutput_forced Pprecision_not\n\
+\n\
+ QbBound 0:0.5 Qbk:0Bk:0_drop QbB-scale-box Qbb-scale-last Qcoplanar\n\
+ Qfurthest Qgood_only QGood_point Qinterior Qmax_out\n\
+ QJoggle Qrandom QRotate Qsearch_1st Qtriangulate\n\
+ QupperDelaunay QVertex_good Qvneighbors Qxact_merge Qzinfinite\n\
+\n\
+ Q0_no_premerge Q1_no_angle Q2_no_independ Q3_no_redundant Q4_no_old\n\
+ Q5_no_check_out Q6_no_concave Q7_depth_first Q8_no_near_in Q9_pick_furthest\n\
+ Q10_no_narrow Q11_trinormals Q12_no_wide_dup\n\
+\n\
+ T4_trace Tannotate Tcheck_often Tstatistics Tverify\n\
+ Tz_stdout TFacet_log TInput_file TPoint_trace TMerge_trace\n\
+ TOutput_file TRerun TWide_trace TVertex_stop TCone_stop\n\
+\n\
+ Angle_max Centrum_size Error_round Random_dist Visible_min\n\
+ Ucoplanar_max Wide_outside\n\
+";
+
+/*-<a href="../libqhull/qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="main">-</a>
+
+ main( argc, argv )
+ processes the command line, calls qhull() to do the work, and exits
+
+ design:
+ initializes data structures
+ reads points
+ finishes initialization
+ computes convex hull and other structures
+ checks the result
+ writes the output
+ frees memory
+*/
+int main(int argc, char *argv[]) {
+ int curlong, totlong; /* used !qh_NOmem */
+ int exitcode, numpoints, dim;
+ coordT *points;
+ boolT ismalloc;
+
+ QHULL_LIB_CHECK /* Check for compatible library */
+
+ if ((argc == 1) && isatty( 0 /*stdin*/)) {
+ fprintf(stdout, qh_prompt2, qh_version);
+ exit(qh_ERRnone);
+ }
+ if (argc > 1 && *argv[1] == '-' && !*(argv[1]+1)) {
+ fprintf(stdout, qh_prompta, qh_version, qh_DEFAULTbox,
+ qh_promptb, qh_promptc, qh_promptd, qh_prompte);
+ exit(qh_ERRnone);
+ }
+ if (argc > 1 && *argv[1] == '.' && !*(argv[1]+1)) {
+ fprintf(stdout, qh_prompt3, qh_version);
+ exit(qh_ERRnone);
+ }
+ if (argc > 1 && *argv[1] == '-' && *(argv[1]+1)=='V') {
+ fprintf(stdout, "%s\n", qh_version2);
+ exit(qh_ERRnone);
+ }
+ qh_init_A(stdin, stdout, stderr, argc, argv); /* sets qh qhull_command */
+ exitcode= setjmp(qh errexit); /* simple statement for CRAY J916 */
+ if (!exitcode) {
+ qh_initflags(qh qhull_command);
+ points= qh_readpoints(&numpoints, &dim, &ismalloc);
+ qh_init_B(points, numpoints, dim, ismalloc);
+ qh_qhull();
+ qh_check_output();
+ qh_produce_output();
+ if (qh VERIFYoutput && !qh FORCEoutput && !qh STOPpoint && !qh STOPcone)
+ qh_check_points();
+ exitcode= qh_ERRnone;
+ }
+ qh NOerrexit= True; /* no more setjmp */
+#ifdef qh_NOmem
+ qh_freeqhull( qh_ALL);
+#else
+ qh_freeqhull( !qh_ALL);
+ qh_memfreeshort(&curlong, &totlong);
+ if (curlong || totlong)
+ qh_fprintf_stderr(6263, "qhull internal warning (main): did not free %d bytes of long memory(%d pieces)\n",
+ totlong, curlong);
+#endif
+ return exitcode;
+} /* main */
+
diff --git a/xs/src/qhull/src/qhull/unix_r.c b/xs/src/qhull/src/qhull/unix_r.c
new file mode 100644
index 000000000..3f999f7fa
--- /dev/null
+++ b/xs/src/qhull/src/qhull/unix_r.c
@@ -0,0 +1,374 @@
+/*<html><pre> -<a href="../libqhull/qh-qhull.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ unix.c
+ command line interface to qhull
+ includes SIOUX interface for Macintoshes
+
+ see qh-qhull.htm
+
+ Copyright (c) 1993-2015 The Geometry Center.
+ $Id: //main/2015/qhull/src/qhull/unix_r.c#6 $$Change: 2066 $
+ $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
+*/
+
+#include "libqhull_r/libqhull_r.h"
+
+#include <ctype.h>
+#include <math.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#if __cplusplus
+extern "C" {
+ int isatty(int);
+}
+
+#elif _MSC_VER
+#include <io.h>
+#define isatty _isatty
+/* int _isatty(int); */
+
+#else
+int isatty(int); /* returns 1 if stdin is a tty
+ if "Undefined symbol" this can be deleted along with call in main() */
+#endif
+
+/*-<a href="../libqhull/qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="prompt">-</a>
+
+ qh_prompt
+ long prompt for qhull
+
+ see:
+ concise prompt below
+*/
+char qh_prompta[]= "\n\
+qhull- compute convex hulls and related structures.\n\
+ http://www.qhull.org %s\n\
+\n\
+input (stdin):\n\
+ first lines: dimension and number of points (or vice-versa).\n\
+ other lines: point coordinates, best if one point per line\n\
+ comments: start with a non-numeric character\n\
+ halfspaces: use dim plus one and put offset after coefficients.\n\
+ May be preceded by a single interior point ('H').\n\
+\n\
+options:\n\
+ d - Delaunay triangulation by lifting points to a paraboloid\n\
+ d Qu - furthest-site Delaunay triangulation (upper convex hull)\n\
+ v - Voronoi diagram (dual of the Delaunay triangulation)\n\
+ v Qu - furthest-site Voronoi diagram\n\
+ Hn,n,... - halfspace intersection about point [n,n,0,...]\n\
+ Qt - triangulated output\n\
+ QJ - joggled input instead of merged facets\n\
+ Qc - keep coplanar points with nearest facet\n\
+ Qi - keep interior points with nearest facet\n\
+\n\
+Qhull control options:\n\
+ Qbk:n - scale coord k so that low bound is n\n\
+ QBk:n - scale coord k so that upper bound is n (QBk is %2.2g)\n\
+ QbB - scale input to unit cube centered at the origin\n\
+ Qbb - scale last coordinate to [0,m] for Delaunay triangulations\n\
+ Qbk:0Bk:0 - remove k-th coordinate from input\n\
+ QJn - randomly joggle input in range [-n,n]\n\
+ QRn - random rotation (n=seed, n=0 time, n=-1 time/no rotate)\n\
+%s%s%s%s"; /* split up qh_prompt for Visual C++ */
+char qh_promptb[]= "\
+ Qf - partition point to furthest outside facet\n\
+ Qg - only build good facets (needs 'QGn', 'QVn', or 'PdD')\n\
+ Qm - only process points that would increase max_outside\n\
+ Qr - process random outside points instead of furthest ones\n\
+ Qs - search all points for the initial simplex\n\
+ Qu - for 'd' or 'v', compute upper hull without point at-infinity\n\
+ returns furthest-site Delaunay triangulation\n\
+ Qv - test vertex neighbors for convexity\n\
+ Qx - exact pre-merges (skips coplanar and angle-coplanar facets)\n\
+ Qz - add point-at-infinity to Delaunay triangulation\n\
+ QGn - good facet if visible from point n, -n for not visible\n\
+ QVn - good facet if it includes point n, -n if not\n\
+ Q0 - turn off default premerge with 'C-0'/'Qx'\n\
+ Q1 - sort merges by type instead of angle\n\
+ Q2 - merge all non-convex at once instead of independent sets\n\
+ Q3 - do not merge redundant vertices\n\
+ Q4 - avoid old->new merges\n\
+ Q5 - do not correct outer planes at end of qhull\n\
+ Q6 - do not pre-merge concave or coplanar facets\n\
+ Q7 - depth-first processing instead of breadth-first\n\
+ Q8 - do not process near-inside points\n\
+ Q9 - process furthest of furthest points\n\
+ Q10 - no special processing for narrow distributions\n\
+ Q11 - copy normals and recompute centrums for tricoplanar facets\n\
+ Q12 - no error on wide merge due to duplicate ridge\n\
+\n\
+";
+char qh_promptc[]= "\
+Topts- Trace options:\n\
+ T4 - trace at level n, 4=all, 5=mem/gauss, -1= events\n\
+ Ta - annotate output with message codes\n\
+ Tc - check frequently during execution\n\
+ Ts - print statistics\n\
+ Tv - verify result: structure, convexity, and point inclusion\n\
+ Tz - send all output to stdout\n\
+ TFn - report summary when n or more facets created\n\
+ TI file - input data from file, no spaces or single quotes\n\
+ TO file - output results to file, may be enclosed in single quotes\n\
+ TPn - turn on tracing when point n added to hull\n\
+ TMn - turn on tracing at merge n\n\
+ TWn - trace merge facets when width > n\n\
+ TRn - rerun qhull n times. Use with 'QJn'\n\
+ TVn - stop qhull after adding point n, -n for before (see TCn)\n\
+ TCn - stop qhull after building cone for point n (see TVn)\n\
+\n\
+Precision options:\n\
+ Cn - radius of centrum (roundoff added). Merge facets if non-convex\n\
+ An - cosine of maximum angle. Merge facets if cosine > n or non-convex\n\
+ C-0 roundoff, A-0.99/C-0.01 pre-merge, A0.99/C0.01 post-merge\n\
+ En - max roundoff error for distance computation\n\
+ Rn - randomly perturb computations by a factor of [1-n,1+n]\n\
+ Vn - min distance above plane for a visible facet (default 3C-n or En)\n\
+ Un - max distance below plane for a new, coplanar point (default Vn)\n\
+ Wn - min facet width for outside point (before roundoff, default 2Vn)\n\
+\n\
+Output formats (may be combined; if none, produces a summary to stdout):\n\
+ f - facet dump\n\
+ G - Geomview output (see below)\n\
+ i - vertices incident to each facet\n\
+ m - Mathematica output (2-d and 3-d)\n\
+ o - OFF format (dim, points and facets; Voronoi regions)\n\
+ n - normals with offsets\n\
+ p - vertex coordinates or Voronoi vertices (coplanar points if 'Qc')\n\
+ s - summary (stderr)\n\
+\n\
+";
+char qh_promptd[]= "\
+More formats:\n\
+ Fa - area for each facet\n\
+ FA - compute total area and volume for option 's'\n\
+ Fc - count plus coplanar points for each facet\n\
+ use 'Qc' (default) for coplanar and 'Qi' for interior\n\
+ FC - centrum or Voronoi center for each facet\n\
+ Fd - use cdd format for input (homogeneous with offset first)\n\
+ FD - use cdd format for numeric output (offset first)\n\
+ FF - facet dump without ridges\n\
+ Fi - inner plane for each facet\n\
+ for 'v', separating hyperplanes for bounded Voronoi regions\n\
+ FI - ID of each facet\n\
+ Fm - merge count for each facet (511 max)\n\
+ FM - Maple output (2-d and 3-d)\n\
+ Fn - count plus neighboring facets for each facet\n\
+ FN - count plus neighboring facets for each point\n\
+ Fo - outer plane (or max_outside) for each facet\n\
+ for 'v', separating hyperplanes for unbounded Voronoi regions\n\
+ FO - options and precision constants\n\
+ Fp - dim, count, and intersection coordinates (halfspace only)\n\
+ FP - nearest vertex and distance for each coplanar point\n\
+ FQ - command used for qhull\n\
+ Fs - summary: #int (8), dimension, #points, tot vertices, tot facets,\n\
+ output: #vertices, #facets, #coplanars, #nonsimplicial\n\
+ #real (2), max outer plane, min vertex\n\
+ FS - sizes: #int (0)\n\
+ #real (2) tot area, tot volume\n\
+ Ft - triangulation with centrums for non-simplicial facets (OFF format)\n\
+ Fv - count plus vertices for each facet\n\
+ for 'v', Voronoi diagram as Voronoi vertices for pairs of sites\n\
+ FV - average of vertices (a feasible point for 'H')\n\
+ Fx - extreme points (in order for 2-d)\n\
+\n\
+";
+char qh_prompte[]= "\
+Geomview options (2-d, 3-d, and 4-d; 2-d Voronoi)\n\
+ Ga - all points as dots\n\
+ Gp - coplanar points and vertices as radii\n\
+ Gv - vertices as spheres\n\
+ Gi - inner planes only\n\
+ Gn - no planes\n\
+ Go - outer planes only\n\
+ Gc - centrums\n\
+ Gh - hyperplane intersections\n\
+ Gr - ridges\n\
+ GDn - drop dimension n in 3-d and 4-d output\n\
+ Gt - for 3-d 'd', transparent outer ridges\n\
+\n\
+Print options:\n\
+ PAn - keep n largest facets by area\n\
+ Pdk:n - drop facet if normal[k] <= n (default 0.0)\n\
+ PDk:n - drop facet if normal[k] >= n\n\
+ Pg - print good facets (needs 'QGn' or 'QVn')\n\
+ PFn - keep facets whose area is at least n\n\
+ PG - print neighbors of good facets\n\
+ PMn - keep n facets with most merges\n\
+ Po - force output. If error, output neighborhood of facet\n\
+ Pp - do not report precision problems\n\
+\n\
+ . - list of all options\n\
+ - - one line descriptions of all options\n\
+ -V - version\n\
+";
+/* for opts, don't assign 'e' or 'E' to a flag (already used for exponent) */
+
+/*-<a href="../libqhull/qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="prompt2">-</a>
+
+ qh_prompt2
+ synopsis for qhull
+*/
+char qh_prompt2[]= "\n\
+qhull- compute convex hulls and related structures. Qhull %s\n\
+ input (stdin): dimension, n, point coordinates\n\
+ comments start with a non-numeric character\n\
+ halfspace: use dim+1 and put offsets after coefficients\n\
+\n\
+options (qh-quick.htm):\n\
+ d - Delaunay triangulation by lifting points to a paraboloid\n\
+ d Qu - furthest-site Delaunay triangulation (upper convex hull)\n\
+ v - Voronoi diagram as the dual of the Delaunay triangulation\n\
+ v Qu - furthest-site Voronoi diagram\n\
+ H1,1 - Halfspace intersection about [1,1,0,...] via polar duality\n\
+ Qt - triangulated output\n\
+ QJ - joggled input instead of merged facets\n\
+ Tv - verify result: structure, convexity, and point inclusion\n\
+ . - concise list of all options\n\
+ - - one-line description of each option\n\
+ -V - version\n\
+\n\
+Output options (subset):\n\
+ s - summary of results (default)\n\
+ i - vertices incident to each facet\n\
+ n - normals with offsets\n\
+ p - vertex coordinates (if 'Qc', includes coplanar points)\n\
+ if 'v', Voronoi vertices\n\
+ Fp - halfspace intersections\n\
+ Fx - extreme points (convex hull vertices)\n\
+ FA - compute total area and volume\n\
+ o - OFF format (if 'v', outputs Voronoi regions)\n\
+ G - Geomview output (2-d, 3-d and 4-d)\n\
+ m - Mathematica output (2-d and 3-d)\n\
+ QVn - print facets that include point n, -n if not\n\
+ TO file- output results to file, may be enclosed in single quotes\n\
+\n\
+examples:\n\
+ rbox D4 | qhull Tv rbox 1000 s | qhull Tv s FA\n\
+ rbox 10 D2 | qhull d QJ s i TO result rbox 10 D2 | qhull v Qbb Qt p\n\
+ rbox 10 D2 | qhull d Qu QJ m rbox 10 D2 | qhull v Qu QJ o\n\
+ rbox c d D2 | qhull Qc s f Fx | more rbox c | qhull FV n | qhull H Fp\n\
+ rbox d D12 | qhull QR0 FA rbox c D7 | qhull FA TF1000\n\
+ rbox y 1000 W0 | qhull rbox c | qhull n\n\
+\n\
+";
+/* for opts, don't assign 'e' or 'E' to a flag (already used for exponent) */
+
+/*-<a href="../libqhull/qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="prompt3">-</a>
+
+ qh_prompt3
+ concise prompt for qhull
+*/
+char qh_prompt3[]= "\n\
+Qhull %s.\n\
+Except for 'F.' and 'PG', upper-case options take an argument.\n\
+\n\
+ delaunay voronoi Geomview Halfspace facet_dump\n\
+ incidences mathematica normals OFF_format points\n\
+ summary\n\
+\n\
+ Farea FArea-total Fcoplanars FCentrums Fd-cdd-in\n\
+ FD-cdd-out FF-dump-xridge Finner FIDs Fmerges\n\
+ Fneighbors FNeigh-vertex Fouter FOptions Fpoint-intersect\n\
+ FPoint_near FQhull Fsummary FSize Ftriangles\n\
+ Fvertices Fvoronoi FVertex-ave Fxtremes FMaple\n\
+\n\
+ Gvertices Gpoints Gall_points Gno_planes Ginner\n\
+ Gcentrums Ghyperplanes Gridges Gouter GDrop_dim\n\
+ Gtransparent\n\
+\n\
+ PArea-keep Pdrop d0:0D0 Pgood PFacet_area_keep\n\
+ PGood_neighbors PMerge-keep Poutput_forced Pprecision_not\n\
+\n\
+ QbBound 0:0.5 Qbk:0Bk:0_drop QbB-scale-box Qbb-scale-last Qcoplanar\n\
+ Qfurthest Qgood_only QGood_point Qinterior Qmax_out\n\
+ QJoggle Qrandom QRotate Qsearch_1st Qtriangulate\n\
+ QupperDelaunay QVertex_good Qvneighbors Qxact_merge Qzinfinite\n\
+\n\
+ Q0_no_premerge Q1_no_angle Q2_no_independ Q3_no_redundant Q4_no_old\n\
+ Q5_no_check_out Q6_no_concave Q7_depth_first Q8_no_near_in Q9_pick_furthest\n\
+ Q10_no_narrow Q11_trinormals Q12_no_wide_dup\n\
+\n\
+ T4_trace Tannotate Tcheck_often Tstatistics Tverify\n\
+ Tz_stdout TFacet_log TInput_file TPoint_trace TMerge_trace\n\
+ TOutput_file TRerun TWide_trace TVertex_stop TCone_stop\n\
+\n\
+ Angle_max Centrum_size Error_round Random_dist Visible_min\n\
+ Ucoplanar_max Wide_outside\n\
+";
+
+/*-<a href="../libqhull/qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="main">-</a>
+
+ main( argc, argv )
+ processes the command line, calls qhull() to do the work, and exits
+
+ design:
+ initializes data structures
+ reads points
+ finishes initialization
+ computes convex hull and other structures
+ checks the result
+ writes the output
+ frees memory
+*/
+int main(int argc, char *argv[]) {
+ int curlong, totlong; /* used !qh_NOmem */
+ int exitcode, numpoints, dim;
+ coordT *points;
+ boolT ismalloc;
+ qhT qh_qh;
+ qhT *qh= &qh_qh;
+
+ QHULL_LIB_CHECK /* Check for compatible library */
+
+ if ((argc == 1) && isatty( 0 /*stdin*/)) {
+ fprintf(stdout, qh_prompt2, qh_version);
+ exit(qh_ERRnone);
+ }
+ if (argc > 1 && *argv[1] == '-' && !*(argv[1]+1)) {
+ fprintf(stdout, qh_prompta, qh_version, qh_DEFAULTbox,
+ qh_promptb, qh_promptc, qh_promptd, qh_prompte);
+ exit(qh_ERRnone);
+ }
+ if (argc > 1 && *argv[1] == '.' && !*(argv[1]+1)) {
+ fprintf(stdout, qh_prompt3, qh_version);
+ exit(qh_ERRnone);
+ }
+ if (argc > 1 && *argv[1] == '-' && *(argv[1]+1)=='V') {
+ fprintf(stdout, "%s\n", qh_version2);
+ exit(qh_ERRnone);
+ }
+ qh_init_A(qh, stdin, stdout, stderr, argc, argv); /* sets qh->qhull_command */
+ exitcode= setjmp(qh->errexit); /* simple statement for CRAY J916 */
+ if (!exitcode) {
+ qh->NOerrexit = False;
+ qh_initflags(qh, qh->qhull_command);
+ points= qh_readpoints(qh, &numpoints, &dim, &ismalloc);
+ qh_init_B(qh, points, numpoints, dim, ismalloc);
+ qh_qhull(qh);
+ qh_check_output(qh);
+ qh_produce_output(qh);
+ if (qh->VERIFYoutput && !qh->FORCEoutput && !qh->STOPpoint && !qh->STOPcone)
+ qh_check_points(qh);
+ exitcode= qh_ERRnone;
+ }
+ qh->NOerrexit= True; /* no more setjmp */
+#ifdef qh_NOmem
+ qh_freeqhull(qh, qh_ALL);
+#else
+ qh_freeqhull(qh, !qh_ALL);
+ qh_memfreeshort(qh, &curlong, &totlong);
+ if (curlong || totlong)
+ qh_fprintf_stderr(6263, "qhull internal warning (main): did not free %d bytes of long memory(%d pieces)\n",
+ totlong, curlong);
+#endif
+ return exitcode;
+} /* main */
+
diff --git a/xs/src/qhull/src/qhulltest/Coordinates_test.cpp b/xs/src/qhull/src/qhulltest/Coordinates_test.cpp
new file mode 100644
index 000000000..3e8658a5b
--- /dev/null
+++ b/xs/src/qhull/src/qhulltest/Coordinates_test.cpp
@@ -0,0 +1,539 @@
+/****************************************************************************
+**
+** Copyright (c) 2009-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/qhulltest/Coordinates_test.cpp#2 $$Change: 2062 $
+** $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $
+**
+****************************************************************************/
+
+//pre-compiled headers
+#include <iostream>
+#include "qhulltest/RoadTest.h" // QT_VERSION
+
+#include "libqhullcpp/Coordinates.h"
+#include "libqhullcpp/QhullError.h"
+#include "libqhullcpp/RboxPoints.h"
+#include "libqhullcpp/Qhull.h"
+
+using std::cout;
+using std::endl;
+using std::ostringstream;
+using std::ostream;
+using std::string;
+
+namespace orgQhull {
+
+class Coordinates_test : public RoadTest
+{
+ Q_OBJECT
+
+#//!\name Test slots
+private slots:
+ void t_construct();
+ void t_convert();
+ void t_element();
+ void t_readonly();
+ void t_operator();
+ void t_const_iterator();
+ void t_iterator();
+ void t_coord_iterator();
+ void t_mutable_coord_iterator();
+ void t_readwrite();
+ void t_search();
+ void t_io();
+};//Coordinates_test
+
+void
+add_Coordinates_test()
+{
+ new Coordinates_test(); // RoadTest::s_testcases
+}
+
+void Coordinates_test::
+t_construct()
+{
+ Coordinates c;
+ QCOMPARE(c.size(), 0U);
+ QVERIFY(c.isEmpty());
+ c << 1.0;
+ QCOMPARE(c.count(), 1);
+ Coordinates c2(c);
+ c2 << 2.0;
+ QCOMPARE(c2.count(), 2);
+ Coordinates c3;
+ c3 = c2;
+ QCOMPARE(c3.count(), 2);
+ QCOMPARE(c3[0]+c3[1], 3.0);
+ QVERIFY(c2==c3);
+ std::vector<coordT> vc;
+ vc.push_back(3.0);
+ vc.push_back(4.0);
+ Coordinates c4(vc);
+ QCOMPARE(c4[0]+c4[1], 7.0);
+ Coordinates c5(c3);
+ QVERIFY(c5==c3);
+ c5= vc;
+ QVERIFY(c5!=c3);
+ QVERIFY(c5==c4);
+}//t_construct
+
+void Coordinates_test::
+t_convert()
+{
+ Coordinates c;
+ c << 1.0 << 3.0;
+ QCOMPARE(c.data()[1], 3.0);
+ coordT *c2= c.data();
+ const coordT *c3= c.data();
+ QCOMPARE(c2, c3);
+ std::vector<coordT> vc= c.toStdVector();
+ QCOMPARE((size_t)vc.size(), c.size());
+ for(int k= (int)vc.size(); k--; ){
+ QCOMPARE(vc[k], c[k]);
+ }
+ QList<coordT> qc= c.toQList();
+ QCOMPARE(qc.count(), c.count());
+ for(int k= qc.count(); k--; ){
+ QCOMPARE(qc[k], c[k]);
+ }
+ Coordinates c4;
+ c4= std::vector<double>(2, 0.0);
+ QCOMPARE(c4.back(), 0.0);
+ Coordinates c5(std::vector<double>(2, 0.0));
+ QCOMPARE(c4.size(), c5.size());
+ QVERIFY(c4==c5);
+}//t_convert
+
+void Coordinates_test::
+t_element()
+{
+ Coordinates c;
+ c << 1.0 << -2.0;
+ c.at(1)= -3;
+ QCOMPARE(c.at(1), -3.0);
+ QCOMPARE(c.back(), -3.0);
+ QCOMPARE(c.front(), 1.0);
+ c[1]= -2.0;
+ QCOMPARE(c[1],-2.0);
+ QCOMPARE(c.first(), 1.0);
+ c.first()= 2.0;
+ QCOMPARE(c.first(), 2.0);
+ QCOMPARE(c.last(), -2.0);
+ c.last()= 0.0;
+ QCOMPARE(c.first()+c.last(), 2.0);
+ coordT *c4= &c.first();
+ const coordT *c5= &c.first();
+ QCOMPARE(c4, c5);
+ coordT *c6= &c.last();
+ const coordT *c7= &c.last();
+ QCOMPARE(c6, c7);
+ Coordinates c2= c.mid(1);
+ QCOMPARE(c2.count(), 1);
+ c << 3.0;
+ Coordinates c3= c.mid(1,1);
+ QCOMPARE(c2, c3);
+ QCOMPARE(c3.value(-1, -1.0), -1.0);
+ QCOMPARE(c3.value(3, 4.0), 4.0);
+ QCOMPARE(c.value(2, 4.0), 3.0);
+}//t_element
+
+void Coordinates_test::
+t_readonly()
+{
+ Coordinates c;
+ QCOMPARE(c.size(), 0u);
+ QCOMPARE(c.count(), 0);
+ QVERIFY(c.isEmpty());
+ c << 1.0 << -2.0;
+ QCOMPARE(c.size(), 2u);
+ QCOMPARE(c.count(), 2);
+ QVERIFY(!c.isEmpty());
+}//t_readonly
+
+void Coordinates_test::
+t_operator()
+{
+ Coordinates c;
+ Coordinates c2(c);
+ QVERIFY(c==c2);
+ QVERIFY(!(c!=c2));
+ c << 1.0;
+ QVERIFY(!(c==c2));
+ QVERIFY(c!=c2);
+ c2 << 1.0;
+ QVERIFY(c==c2);
+ QVERIFY(!(c!=c2));
+ c[0]= 0.0;
+ QVERIFY(c!=c2);
+ Coordinates c3= c+c2;
+ QCOMPARE(c3.count(), 2);
+ QCOMPARE(c3[0], 0.0);
+ QCOMPARE(c3[1], 1.0);
+ c3 += c3;
+ QCOMPARE(c3.count(), 4);
+ QCOMPARE(c3[2], 0.0);
+ QCOMPARE(c3[3], 1.0);
+ c3 += c2;
+ QCOMPARE(c3[4], 1.0);
+ c3 += 5.0;
+ QCOMPARE(c3.count(), 6);
+ QCOMPARE(c3[5], 5.0);
+ // << checked above
+}//t_operator
+
+void Coordinates_test::
+t_const_iterator()
+{
+ Coordinates c;
+ QCOMPARE(c.begin(), c.end());
+ // begin and end checked elsewhere
+ c << 1.0 << 3.0;
+ Coordinates::const_iterator i= c.begin();
+ QCOMPARE(*i, 1.0);
+ QCOMPARE(i[1], 3.0);
+ // i[1]= -3.0; // compiler error
+ // operator-> is not applicable to double
+ QCOMPARE(*i++, 1.0);
+ QCOMPARE(*i, 3.0);
+ QCOMPARE(*i--, 3.0);
+ QCOMPARE(*i, 1.0);
+ QCOMPARE(*(i+1), 3.0);
+ QCOMPARE(*++i, 3.0);
+ QCOMPARE(*(i-1), 1.0);
+ QCOMPARE(*--i, 1.0);
+ QVERIFY(i==c.begin());
+ QVERIFY(i==c.constBegin());
+ QVERIFY(i!=c.end());
+ QVERIFY(i!=c.constEnd());
+ QVERIFY(i<c.end());
+ QVERIFY(i>=c.begin());
+ QVERIFY(i+1<=c.end());
+ QVERIFY(i+1>c.begin());
+ Coordinates::iterator i2= c.begin();
+ Coordinates::const_iterator i3(i2);
+ QCOMPARE(*i3, 1.0);
+ QCOMPARE(i3[1], 3.0);
+}//t_const_iterator
+
+void Coordinates_test::
+t_iterator()
+{
+ Coordinates c;
+ QCOMPARE(c.begin(), c.end());
+ // begin and end checked elsewhere
+ c << 1.0 << 3.0;
+ Coordinates::iterator i= c.begin();
+ QCOMPARE(*i, 1.0);
+ QCOMPARE(i[1], 3.0);
+ *i= -1.0;
+ QCOMPARE(*i, -1.0);
+ i[1]= -3.0;
+ QCOMPARE(i[1], -3.0);
+ *i= 1.0;
+ // operator-> is not applicable to double
+ QCOMPARE(*i++, 1.0);
+ QCOMPARE(*i, -3.0);
+ *i= 3.0;
+ QCOMPARE(*i--, 3.0);
+ QCOMPARE(*i, 1.0);
+ QCOMPARE(*(i+1), 3.0);
+ QCOMPARE(*++i, 3.0);
+ QCOMPARE(*(i-1), 1.0);
+ QCOMPARE(*--i, 1.0);
+ QVERIFY(i==c.begin());
+ QVERIFY(i==c.constBegin());
+ QVERIFY(i!=c.end());
+ QVERIFY(i!=c.constEnd());
+ QVERIFY(i<c.end());
+ QVERIFY(i>=c.begin());
+ QVERIFY(i+1<=c.end());
+ QVERIFY(i+1>c.begin());
+}//t_iterator
+
+void Coordinates_test::
+t_coord_iterator()
+{
+ Coordinates c;
+ c << 1.0 << 3.0;
+ CoordinatesIterator i(c);
+ CoordinatesIterator i2= c;
+ QVERIFY(i.findNext(1.0));
+ QVERIFY(!i.findNext(2.0));
+ QVERIFY(!i.findNext(3.0));
+ QVERIFY(i.findPrevious(3.0));
+ QVERIFY(!i.findPrevious(2.0));
+ QVERIFY(!i.findPrevious(1.0));
+ QVERIFY(i2.findNext(3.0));
+ QVERIFY(i2.findPrevious(3.0));
+ QVERIFY(i2.findNext(3.0));
+ QVERIFY(i2.findPrevious(1.0));
+ QVERIFY(i2.hasNext());
+ QVERIFY(!i2.hasPrevious());
+ QVERIFY(i.hasNext());
+ QVERIFY(!i.hasPrevious());
+ i.toBack();
+ i2.toFront();
+ QVERIFY(!i.hasNext());
+ QVERIFY(i.hasPrevious());
+ QVERIFY(i2.hasNext());
+ QVERIFY(!i2.hasPrevious());
+ Coordinates c2;
+ i2= c2;
+ QVERIFY(!i2.hasNext());
+ QVERIFY(!i2.hasPrevious());
+ i2.toBack();
+ QVERIFY(!i2.hasNext());
+ QVERIFY(!i2.hasPrevious());
+ QCOMPARE(i.peekPrevious(), 3.0);
+ QCOMPARE(i.previous(), 3.0);
+ QCOMPARE(i.previous(), 1.0);
+ QVERIFY(!i.hasPrevious());
+ QCOMPARE(i.peekNext(), 1.0);
+ // i.peekNext()= 1.0; // compiler error
+ QCOMPARE(i.next(), 1.0);
+ QCOMPARE(i.peekNext(), 3.0);
+ QCOMPARE(i.next(), 3.0);
+ QVERIFY(!i.hasNext());
+ i.toFront();
+ QCOMPARE(i.next(), 1.0);
+}//t_coord_iterator
+
+void Coordinates_test::
+t_mutable_coord_iterator()
+{
+ // Same tests as CoordinatesIterator
+ Coordinates c;
+ c << 1.0 << 3.0;
+ MutableCoordinatesIterator i(c);
+ MutableCoordinatesIterator i2= c;
+ QVERIFY(i.findNext(1.0));
+ QVERIFY(!i.findNext(2.0));
+ QVERIFY(!i.findNext(3.0));
+ QVERIFY(i.findPrevious(3.0));
+ QVERIFY(!i.findPrevious(2.0));
+ QVERIFY(!i.findPrevious(1.0));
+ QVERIFY(i2.findNext(3.0));
+ QVERIFY(i2.findPrevious(3.0));
+ QVERIFY(i2.findNext(3.0));
+ QVERIFY(i2.findPrevious(1.0));
+ QVERIFY(i2.hasNext());
+ QVERIFY(!i2.hasPrevious());
+ QVERIFY(i.hasNext());
+ QVERIFY(!i.hasPrevious());
+ i.toBack();
+ i2.toFront();
+ QVERIFY(!i.hasNext());
+ QVERIFY(i.hasPrevious());
+ QVERIFY(i2.hasNext());
+ QVERIFY(!i2.hasPrevious());
+ Coordinates c2;
+ i2= c2;
+ QVERIFY(!i2.hasNext());
+ QVERIFY(!i2.hasPrevious());
+ i2.toBack();
+ QVERIFY(!i2.hasNext());
+ QVERIFY(!i2.hasPrevious());
+ QCOMPARE(i.peekPrevious(), 3.0);
+ QCOMPARE(i.peekPrevious(), 3.0);
+ QCOMPARE(i.previous(), 3.0);
+ QCOMPARE(i.previous(), 1.0);
+ QVERIFY(!i.hasPrevious());
+ QCOMPARE(i.peekNext(), 1.0);
+ QCOMPARE(i.next(), 1.0);
+ QCOMPARE(i.peekNext(), 3.0);
+ QCOMPARE(i.next(), 3.0);
+ QVERIFY(!i.hasNext());
+ i.toFront();
+ QCOMPARE(i.next(), 1.0);
+
+ // Mutable tests
+ i.toFront();
+ i.peekNext()= -1.0;
+ QCOMPARE(i.peekNext(), -1.0);
+ QCOMPARE((i.next()= 1.0), 1.0);
+ QCOMPARE(i.peekPrevious(), 1.0);
+ i.remove();
+ QCOMPARE(c.count(), 1);
+ i.remove();
+ QCOMPARE(c.count(), 1);
+ QCOMPARE(i.peekNext(), 3.0);
+ i.insert(1.0);
+ i.insert(2.0);
+ QCOMPARE(c.count(), 3);
+ QCOMPARE(i.peekNext(), 3.0);
+ QCOMPARE(i.peekPrevious(), 2.0);
+ i.peekPrevious()= -2.0;
+ QCOMPARE(i.peekPrevious(), -2.0);
+ QCOMPARE((i.previous()= 2.0), 2.0);
+ QCOMPARE(i.peekNext(), 2.0);
+ i.toBack();
+ i.remove();
+ QCOMPARE(c.count(), 3); // unchanged
+ i.toFront();
+ i.remove();
+ QCOMPARE(c.count(), 3); // unchanged
+ QCOMPARE(i.peekNext(), 1.0);
+ i.remove();
+ QCOMPARE(c.count(), 3); // unchanged
+ i.insert(0.0);
+ QCOMPARE(c.count(), 4);
+ QCOMPARE(i.value(), 0.0);
+ QCOMPARE(i.peekPrevious(), 0.0);
+ i.setValue(-10.0);
+ QCOMPARE(c.count(), 4); // unchanged
+ QCOMPARE(i.peekNext(), 1.0);
+ QCOMPARE(i.peekPrevious(), -10.0);
+ i.findNext(1.0);
+ i.setValue(-1.0);
+ QCOMPARE(i.peekPrevious(), -1.0);
+ i.setValue(1.0);
+ QCOMPARE(i.peekPrevious(), 1.0);
+ QCOMPARE(i.value(), 1.0);
+ i.findPrevious(1.0);
+ i.setValue(-1.0);
+ QCOMPARE(i.peekNext(), -1.0);
+ i.toBack();
+ QCOMPARE(i.previous(), 3.0);
+ i.setValue(-3.0);
+ QCOMPARE(i.peekNext(), -3.0);
+ double d= i.value();
+ QCOMPARE(d, -3.0);
+ QCOMPARE(i.previous(), 2.0);
+}//t_mutable_coord_iterator
+
+void Coordinates_test::
+t_readwrite()
+{
+ Coordinates c;
+ c.clear();
+ QCOMPARE(c.count(), 0);
+ c << 1.0 << 3.0;
+ c.clear();
+ QCOMPARE(c.count(), 0);
+ coordT c2[4]= { 0.0, 1.0, 2.0, 3.0};
+ c.append(4, c2);
+ QCOMPARE(c.count(), 4);
+ QCOMPARE(c[0], 0.0);
+ QCOMPARE(c[1], 1.0);
+ QCOMPARE(c[3], 3.0);
+ c.clear();
+ c << 1.0 << 3.0;
+ c.erase(c.begin(), c.end());
+ QCOMPARE(c.count(), 0);
+ c << 1.0 << 0.0;
+ Coordinates::iterator i= c.erase(c.begin());
+ QCOMPARE(*i, 0.0);
+ i= c.insert(c.end(), 1.0);
+ QCOMPARE(*i, 1.0);
+ QCOMPARE(c.count(), 2);
+ c.pop_back();
+ QCOMPARE(c.count(), 1); // 0
+ QCOMPARE(c[0], 0.0);
+ c.push_back(2.0);
+ QCOMPARE(c.count(), 2);
+ c.append(3.0);
+ QCOMPARE(c.count(), 3); // 0, 2, 3
+ QCOMPARE(c[2], 3.0);
+ c.insert(0, 4.0);
+ QCOMPARE(c[0], 4.0);
+ QCOMPARE(c[3], 3.0);
+ c.insert(c.count(), 5.0);
+ QCOMPARE(c.count(), 5); // 4, 0, 2, 3, 5
+ QCOMPARE(c[4], 5.0);
+ c.move(4, 0);
+ QCOMPARE(c.count(), 5); // 5, 4, 0, 2, 3
+ QCOMPARE(c[0], 5.0);
+ c.pop_front();
+ QCOMPARE(c.count(), 4);
+ QCOMPARE(c[0], 4.0);
+ c.prepend(6.0);
+ QCOMPARE(c.count(), 5); // 6, 4, 0, 2, 3
+ QCOMPARE(c[0], 6.0);
+ c.push_front(7.0);
+ QCOMPARE(c.count(), 6);
+ QCOMPARE(c[0], 7.0);
+ c.removeAt(1);
+ QCOMPARE(c.count(), 5); // 7, 4, 0, 2, 3
+ QCOMPARE(c[1], 4.0);
+ c.removeFirst();
+ QCOMPARE(c.count(), 4); // 4, 0, 2, 3
+ QCOMPARE(c[0], 4.0);
+ c.removeLast();
+ QCOMPARE(c.count(), 3);
+ QCOMPARE(c.last(), 2.0);
+ c.replace(2, 8.0);
+ QCOMPARE(c.count(), 3); // 4, 0, 8
+ QCOMPARE(c[2], 8.0);
+ c.swap(0, 2);
+ QCOMPARE(c[2], 4.0);
+ double d= c.takeAt(2);
+ QCOMPARE(c.count(), 2); // 8, 0
+ QCOMPARE(d, 4.0);
+ double d2= c.takeFirst();
+ QCOMPARE(c.count(), 1); // 0
+ QCOMPARE(d2, 8.0);
+ double d3= c.takeLast();
+ QVERIFY(c.isEmpty()); \
+ QCOMPARE(d3, 0.0);
+}//t_readwrite
+
+void Coordinates_test::
+t_search()
+{
+ Coordinates c;
+ c << 1.0 << 3.0 << 1.0;
+ QVERIFY(c.contains(1.0));
+ QVERIFY(c.contains(3.0));
+ QVERIFY(!c.contains(0.0));
+ QCOMPARE(c.count(1.0), 2);
+ QCOMPARE(c.count(3.0), 1);
+ QCOMPARE(c.count(0.0), 0);
+ QCOMPARE(c.indexOf(1.0), 0);
+ QCOMPARE(c.indexOf(3.0), 1);
+ QCOMPARE(c.indexOf(1.0, -1), 2);
+ QCOMPARE(c.indexOf(3.0, -1), -1);
+ QCOMPARE(c.indexOf(3.0, -2), 1);
+ QCOMPARE(c.indexOf(1.0, -3), 0);
+ QCOMPARE(c.indexOf(1.0, -4), 0);
+ QCOMPARE(c.indexOf(1.0, 1), 2);
+ QCOMPARE(c.indexOf(3.0, 2), -1);
+ QCOMPARE(c.indexOf(1.0, 2), 2);
+ QCOMPARE(c.indexOf(1.0, 3), -1);
+ QCOMPARE(c.indexOf(1.0, 4), -1);
+ QCOMPARE(c.lastIndexOf(1.0), 2);
+ QCOMPARE(c.lastIndexOf(3.0), 1);
+ QCOMPARE(c.lastIndexOf(1.0, -1), 2);
+ QCOMPARE(c.lastIndexOf(3.0, -1), 1);
+ QCOMPARE(c.lastIndexOf(3.0, -2), 1);
+ QCOMPARE(c.lastIndexOf(1.0, -3), 0);
+ QCOMPARE(c.lastIndexOf(1.0, -4), -1);
+ QCOMPARE(c.lastIndexOf(1.0, 1), 0);
+ QCOMPARE(c.lastIndexOf(3.0, 2), 1);
+ QCOMPARE(c.lastIndexOf(1.0, 2), 2);
+ QCOMPARE(c.lastIndexOf(1.0, 3), 2);
+ QCOMPARE(c.lastIndexOf(1.0, 4), 2);
+ c.removeAll(3.0);
+ QCOMPARE(c.count(), 2);
+ c.removeAll(4.0);
+ QCOMPARE(c.count(), 2);
+ c.removeAll(1.0);
+ QCOMPARE(c.count(), 0);
+ c.removeAll(4.0);
+ QCOMPARE(c.count(), 0);
+}//t_search
+
+void Coordinates_test::
+t_io()
+{
+ Coordinates c;
+ c << 1.0 << 2.0 << 3.0;
+ ostringstream os;
+ os << "Coordinates 1-2-3\n" << c;
+ cout << os.str();
+ QString s= QString::fromStdString(os.str());
+ QCOMPARE(s.count("2"), 2);
+}//t_io
+
+}//orgQhull
+
+#include "moc/Coordinates_test.moc"
diff --git a/xs/src/qhull/src/qhulltest/PointCoordinates_test.cpp b/xs/src/qhull/src/qhulltest/PointCoordinates_test.cpp
new file mode 100644
index 000000000..09285954d
--- /dev/null
+++ b/xs/src/qhull/src/qhulltest/PointCoordinates_test.cpp
@@ -0,0 +1,478 @@
+/****************************************************************************
+**
+** Copyright (c) 2009-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/qhulltest/PointCoordinates_test.cpp#2 $$Change: 2062 $
+** $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $
+**
+****************************************************************************/
+
+//pre-compiled headers
+#include <iostream>
+#include "qhulltest/RoadTest.h" // QT_VERSION
+
+#include "libqhullcpp/PointCoordinates.h"
+#include "libqhullcpp/QhullError.h"
+#include "libqhullcpp/RboxPoints.h"
+#include "libqhullcpp/Qhull.h"
+
+using std::cout;
+using std::endl;
+using std::ostringstream;
+using std::ostream;
+using std::string;
+using std::stringstream;
+
+namespace orgQhull {
+
+class PointCoordinates_test : public RoadTest
+{
+ Q_OBJECT
+
+#//!\name Test slots
+private slots:
+ void t_construct_q();
+ void t_construct_qh();
+ void t_convert();
+ void t_getset();
+ void t_element();
+ void t_foreach();
+ void t_search();
+ void t_modify();
+ void t_append_points();
+ void t_coord_iterator();
+ void t_io();
+};//PointCoordinates_test
+
+void
+add_PointCoordinates_test()
+{
+ new PointCoordinates_test(); // RoadTest::s_testcases
+}
+
+void PointCoordinates_test::
+t_construct_q()
+{
+ Qhull q;
+ PointCoordinates pc(q);
+ QCOMPARE(pc.size(), 0U);
+ QCOMPARE(pc.coordinateCount(), 0);
+ QCOMPARE(pc.dimension(), 0);
+ QCOMPARE(pc.coordinates(), (coordT *)0);
+ QVERIFY(pc.isEmpty());
+ pc.checkValid();
+ PointCoordinates pc7(q, 2, "test explicit dimension");
+ QCOMPARE(pc7.dimension(), 2);
+ QCOMPARE(pc7.count(), 0);
+ QVERIFY(pc7.isEmpty());
+ QCOMPARE(pc7.comment(), std::string("test explicit dimension"));
+ pc7.checkValid();
+ PointCoordinates pc2(q, "Test pc2");
+ QCOMPARE(pc2.count(), 0);
+ QVERIFY(pc2.isEmpty());
+ QCOMPARE(pc2.comment(), std::string("Test pc2"));
+ pc2.checkValid();
+ PointCoordinates pc3(q, 3, "Test 3-d pc3");
+ QCOMPARE(pc3.dimension(), 3);
+ QVERIFY(pc3.isEmpty());
+ pc3.checkValid();
+ coordT c[]= { 0.0, 1.0, 2.0, 3.0, 4.0, 5.0 };
+ PointCoordinates pc4(q, 2, "Test 2-d pc4", 6, c);
+ QCOMPARE(pc4.dimension(), 2);
+ QCOMPARE(pc4.count(), 3);
+ QCOMPARE(pc4.size(), 3u);
+ QVERIFY(!pc4.isEmpty());
+ pc4.checkValid();
+ QhullPoint p= pc4[2];
+ QCOMPARE(p[1], 5.0);
+ // QhullPoint refers to PointCoordinates
+ p[1] += 1.0;
+ QCOMPARE(pc4[2][1], 6.0);
+ PointCoordinates pc5(q, 4, "Test 4-d pc5 with insufficient coordinates", 6, c);
+ QCOMPARE(pc5.dimension(), 4);
+ QCOMPARE(pc5.count(), 1);
+ QCOMPARE(pc5.extraCoordinatesCount(), 2);
+ QCOMPARE(pc5.extraCoordinates()[1], 5.0);
+ QVERIFY(!pc5.isEmpty());;
+ std::vector<coordT> vc;
+ vc.push_back(3.0);
+ vc.push_back(4.0);
+ vc.push_back(5.0);
+ vc.push_back(6.0);
+ vc.push_back(7.0);
+ vc.push_back(9.0);
+ pc5.append(2, &vc[3]); // Copy of vc[]
+ pc5.checkValid();
+ QhullPoint p5(q, 4, &vc[1]);
+ QCOMPARE(pc5[1], p5);
+ PointCoordinates pc6(pc5); // Makes copy of point_coordinates
+ QCOMPARE(pc6[1], p5);
+ QVERIFY(pc6==pc5);
+ QhullPoint p6= pc5[1]; // Refers to pc5.coordinates
+ pc5[1][0] += 1.0;
+ QCOMPARE(pc5[1], p6);
+ QVERIFY(pc5[1]!=p5);
+ QVERIFY(pc6!=pc5);
+ pc6= pc5;
+ QVERIFY(pc6==pc5);
+ PointCoordinates pc8(q);
+ pc6= pc8;
+ QVERIFY(pc6!=pc5);
+ QVERIFY(pc6.isEmpty());
+}//t_construct_q
+
+void PointCoordinates_test::
+t_construct_qh()
+{
+ QhullQh qh;
+ PointCoordinates pc(&qh);
+ QCOMPARE(pc.size(), 0U);
+ QCOMPARE(pc.coordinateCount(), 0);
+ QCOMPARE(pc.dimension(), 0);
+ QCOMPARE(pc.coordinates(), (coordT *)0);
+ QVERIFY(pc.isEmpty());
+ pc.checkValid();
+ PointCoordinates pc7(&qh, 2, "test explicit dimension");
+ QCOMPARE(pc7.dimension(), 2);
+ QCOMPARE(pc7.count(), 0);
+ QVERIFY(pc7.isEmpty());
+ QCOMPARE(pc7.comment(), std::string("test explicit dimension"));
+ pc7.checkValid();
+ PointCoordinates pc2(&qh, "Test pc2");
+ QCOMPARE(pc2.count(), 0);
+ QVERIFY(pc2.isEmpty());
+ QCOMPARE(pc2.comment(), std::string("Test pc2"));
+ pc2.checkValid();
+ PointCoordinates pc3(&qh, 3, "Test 3-d pc3");
+ QCOMPARE(pc3.dimension(), 3);
+ QVERIFY(pc3.isEmpty());
+ pc3.checkValid();
+ coordT c[]= { 0.0, 1.0, 2.0, 3.0, 4.0, 5.0 };
+ PointCoordinates pc4(&qh, 2, "Test 2-d pc4", 6, c);
+ QCOMPARE(pc4.dimension(), 2);
+ QCOMPARE(pc4.count(), 3);
+ QCOMPARE(pc4.size(), 3u);
+ QVERIFY(!pc4.isEmpty());
+ pc4.checkValid();
+ QhullPoint p= pc4[2];
+ QCOMPARE(p[1], 5.0);
+ // QhullPoint refers to PointCoordinates
+ p[1] += 1.0;
+ QCOMPARE(pc4[2][1], 6.0);
+ PointCoordinates pc5(&qh, 4, "Test 4-d pc5 with insufficient coordinates", 6, c);
+ QCOMPARE(pc5.dimension(), 4);
+ QCOMPARE(pc5.count(), 1);
+ QCOMPARE(pc5.extraCoordinatesCount(), 2);
+ QCOMPARE(pc5.extraCoordinates()[1], 5.0);
+ QVERIFY(!pc5.isEmpty());;
+ std::vector<coordT> vc;
+ vc.push_back(3.0);
+ vc.push_back(4.0);
+ vc.push_back(5.0);
+ vc.push_back(6.0);
+ vc.push_back(7.0);
+ vc.push_back(9.0);
+ pc5.append(2, &vc[3]); // Copy of vc[]
+ pc5.checkValid();
+ QhullPoint p5(&qh, 4, &vc[1]);
+ QCOMPARE(pc5[1], p5);
+ PointCoordinates pc6(pc5); // Makes copy of point_coordinates
+ QCOMPARE(pc6[1], p5);
+ QVERIFY(pc6==pc5);
+ QhullPoint p6= pc5[1]; // Refers to pc5.coordinates
+ pc5[1][0] += 1.0;
+ QCOMPARE(pc5[1], p6);
+ QVERIFY(pc5[1]!=p5);
+ QVERIFY(pc6!=pc5);
+ pc6= pc5;
+ QVERIFY(pc6==pc5);
+ PointCoordinates pc8(&qh);
+ pc6= pc8;
+ QVERIFY(pc6!=pc5);
+ QVERIFY(pc6.isEmpty());
+}//t_construct_qh
+
+void PointCoordinates_test::
+t_convert()
+{
+ Qhull q;
+ //defineAs tested above
+ coordT c[]= {0.0, 1.0, 2.0, 3.0, 4.0, 5.0};
+ PointCoordinates ps(q, 3, "two 3-d points", 6, c);
+ QCOMPARE(ps.dimension(), 3);
+ QCOMPARE(ps.size(), 2u);
+ const coordT *c2= ps.constData();
+ QVERIFY(c!=c2);
+ QCOMPARE(c[0], c2[0]);
+ const coordT *c3= ps.data();
+ QCOMPARE(c3, c2);
+ coordT *c4= ps.data();
+ QCOMPARE(c4, c2);
+ std::vector<coordT> vs= ps.toStdVector();
+ QCOMPARE(vs.size(), 6u);
+ QCOMPARE(vs[5], 5.0);
+ QList<coordT> qs= ps.toQList();
+ QCOMPARE(qs.size(), 6);
+ QCOMPARE(qs[5], 5.0);
+}//t_convert
+
+void PointCoordinates_test::
+t_getset()
+{
+ // See t_construct() for test of coordinates, coordinateCount, dimension, empty, isEmpty, ==, !=
+ // See t_construct() for test of checkValid, comment, setDimension
+ Qhull q;
+ PointCoordinates pc(q, "Coordinates c");
+ pc.setComment("New comment");
+ QCOMPARE(pc.comment(), std::string("New comment"));
+ pc.checkValid();
+ pc.makeValid(); // A no-op
+ pc.checkValid();
+ Coordinates cs= pc.getCoordinates();
+ QVERIFY(cs.isEmpty());
+ PointCoordinates pc2(pc);
+ pc.setDimension(3);
+ QVERIFY(pc2!=pc);
+ coordT c[]= {0.0, 1.0, 2.0, 3.0, 4.0, 5.0};
+ pc.append(6, c);
+ pc.checkValid();
+ pc.makeValid(); // A no-op
+ QhullPoint p= pc[0];
+ QCOMPARE(p[2], 2.0);
+ try{
+ pc.setDimension(2);
+ QFAIL("setDimension(2) did not fail for 3-d.");
+ }catch (const std::exception &e) {
+ const char *s= e.what();
+ cout << "INFO : Caught " << s;
+ }
+}//t_getset
+
+void PointCoordinates_test::
+t_element()
+{
+ Qhull q;
+ coordT c[]= {0.0, 1.0, 2.0, 3.0, 4.0, 5.0};
+ PointCoordinates pc(q, 2, "2-d points", 6, c);
+ QhullPoint p= pc.at(0);
+ QCOMPARE(p, pc[0]);
+ QCOMPARE(p, pc.first());
+ QCOMPARE(p, pc.value(0));
+ p= pc.back();
+ QCOMPARE(p, pc[2]);
+ QCOMPARE(p, pc.last());
+ QCOMPARE(p, pc.value(2));
+ QhullPoints ps= pc.mid(1, 2);
+ QCOMPARE(ps[1], p);
+}//t_element
+
+void PointCoordinates_test::
+t_foreach()
+{
+ Qhull q;
+ coordT c[]= {0.0, 1.0, 2.0, 3.0, 4.0, 5.0};
+ PointCoordinates pc(q, 2, "2-d points", 6, c);
+ QhullPoints::Iterator i= pc.begin();
+ QhullPoint p= pc[0];
+ QCOMPARE(*i, p);
+ QCOMPARE((*i)[0], 0.0);
+ QhullPoint p3= pc[2];
+ i= pc.end();
+ QCOMPARE(i[-1], p3);
+ const PointCoordinates pc2(q, 2, "2-d points", 6, c);
+ QhullPoints::ConstIterator i2= pc.begin();
+ const QhullPoint p0= pc2[0];
+ QCOMPARE(*i2, p0);
+ QCOMPARE((*i2)[0], 0.0);
+ QhullPoints::ConstIterator i3= i2;
+ QCOMPARE(i3, i2);
+ QCOMPARE((*i3)[0], 0.0);
+ i3= pc.constEnd();
+ --i3;
+ QhullPoint p2= pc2[2];
+ QCOMPARE(*i3, p2);
+ i= pc.end();
+ QVERIFY(i-1==i3);
+ i2= pc2.end();
+ QVERIFY(i2-1!=i3);
+ QCOMPARE(*(i2-1), *i3);
+ foreach(QhullPoint p3, pc){ //Qt only
+ QVERIFY(p3[0]>=0.0);
+ QVERIFY(p3[0]<=5.0);
+ }
+ Coordinates::ConstIterator i4= pc.beginCoordinates();
+ QCOMPARE(*i4, 0.0);
+ Coordinates::Iterator i5= pc.beginCoordinates();
+ QCOMPARE(*i5, 0.0);
+ i4= pc.beginCoordinates(1);
+ QCOMPARE(*i4, 2.0);
+ i5= pc.beginCoordinates(1);
+ QCOMPARE(*i5, 2.0);
+ i4= pc.endCoordinates();
+ QCOMPARE(*--i4, 5.0);
+ i5= pc.endCoordinates();
+ QCOMPARE(*--i5, 5.0);
+}//t_foreach
+
+void PointCoordinates_test::
+t_search()
+{
+ Qhull q;
+ coordT c[]= {0.0, 1.0, 2.0, 3.0, 4.0, 5.0};
+ PointCoordinates pc(q, 2, "2-d points", 6, c);
+ QhullPoint p0= pc[0];
+ QhullPoint p2= pc[2];
+ QVERIFY(pc.contains(p0));
+ QVERIFY(pc.contains(p2));
+ QCOMPARE(pc.count(p0), 1);
+ QCOMPARE(pc.indexOf(p2), 2);
+ QCOMPARE(pc.lastIndexOf(p0), 0);
+}//t_search
+
+void PointCoordinates_test::
+t_modify()
+{
+ Qhull q;
+ coordT c[]= {0.0, 1.0, 2.0, 3.0, 4.0, 5.0};
+ PointCoordinates pc(q, 2, "2-d points", 6, c);
+ coordT c3[]= {0.0, 1.0, 2.0, 3.0, 4.0, 5.0};
+ PointCoordinates pc5(q, 2, "test explicit dimension");
+ pc5.append(6, c3); // 0-5
+ QVERIFY(pc5==pc);
+ PointCoordinates pc2(q, 2, "2-d");
+ coordT c2[]= {6.0, 7.0, 8.0, 9.0, 10.0, 11.0};
+ pc2.append(6, c2);
+ QCOMPARE(pc2.count(), 3);
+ pc2.append(14.0);
+ QCOMPARE(pc2.count(), 3);
+ QCOMPARE(pc2.extraCoordinatesCount(), 1);
+ pc2.append(15.0); // 6-11, 14,15
+ QCOMPARE(pc2.count(), 4);
+ QCOMPARE(pc2.extraCoordinatesCount(), 0);
+ QhullPoint p(pc[0]);
+ pc2.append(p); // 6-11, 14,15, 0,1
+ QCOMPARE(pc2.count(), 5);
+ QCOMPARE(pc2.extraCoordinatesCount(), 0);
+ QCOMPARE(pc2.lastIndexOf(p), 4);
+ pc.append(pc2); // Invalidates p
+ QCOMPARE(pc.count(), 8); // 0-11, 14,15, 0,1
+ QCOMPARE(pc.extraCoordinatesCount(), 0);
+ QCOMPARE(pc.lastIndexOf(pc[0]), 7);
+ pc.appendComment(" operators");
+ QCOMPARE(pc.comment(), std::string("2-d points operators"));
+ pc.checkValid();
+ // see t_append_points for appendPoints
+ PointCoordinates pc3= pc+pc2;
+ pc3.checkValid();
+ QCOMPARE(pc3.count(), 13);
+ QCOMPARE(pc3[6][0], 14.0);
+ QCOMPARE(pc3[8][0], 6.0);
+ pc3 += pc;
+ QCOMPARE(pc3.count(), 21);
+ QCOMPARE(pc3[14][0], 2.0);
+ pc3 += 12.0;
+ pc3 += 14.0;
+ QCOMPARE(pc3.count(), 22);
+ QCOMPARE(pc3.last()[0], 12.0);
+ // QhullPoint p3= pc3.first(); // += throws error because append may move the data
+ QhullPoint p3= pc2.first();
+ pc3 += p3;
+ QCOMPARE(pc3.count(), 23);
+ QCOMPARE(pc3.last()[0], 6.0);
+ pc3 << pc;
+ QCOMPARE(pc3.count(), 31);
+ QCOMPARE(pc3.last()[0], 0.0);
+ pc3 << 12.0 << 14.0;
+ QCOMPARE(pc3.count(), 32);
+ QCOMPARE(pc3.last()[0], 12.0);
+ PointCoordinates pc4(pc3);
+ pc4.reserveCoordinates(100);
+ QVERIFY(pc3==pc4);
+}//t_modify
+
+void PointCoordinates_test::
+t_append_points()
+{
+ Qhull q;
+ PointCoordinates pc(q, 2, "stringstream");
+ stringstream s("2 3 1 2 3 4 5 6");
+ pc.appendPoints(s);
+ QCOMPARE(pc.count(), 3);
+}//t_append_points
+
+void PointCoordinates_test::
+t_coord_iterator()
+{
+ Qhull q;
+ PointCoordinates c(q, 2, "2-d");
+ c << 0.0 << 1.0 << 2.0 << 3.0 << 4.0 << 5.0;
+ PointCoordinatesIterator i(c);
+ QhullPoint p0(c[0]);
+ QhullPoint p1(c[1]);
+ QhullPoint p2(c[2]);
+ coordT c2[] = {-1.0, -2.0};
+ QhullPoint p3(q, 2, c2);
+ PointCoordinatesIterator i2= c;
+ QVERIFY(i.findNext(p1));
+ QVERIFY(!i.findNext(p1));
+ QVERIFY(!i.findNext(p2));
+ QVERIFY(!i.findNext(p3));
+ QVERIFY(i.findPrevious(p2));
+ QVERIFY(!i.findPrevious(p2));
+ QVERIFY(!i.findPrevious(p0));
+ QVERIFY(!i.findPrevious(p3));
+ QVERIFY(i2.findNext(p2));
+ QVERIFY(i2.findPrevious(p0));
+ QVERIFY(i2.findNext(p1));
+ QVERIFY(i2.findPrevious(p0));
+ QVERIFY(i2.hasNext());
+ QVERIFY(!i2.hasPrevious());
+ QVERIFY(i.hasNext());
+ QVERIFY(!i.hasPrevious());
+ i.toBack();
+ i2.toFront();
+ QVERIFY(!i.hasNext());
+ QVERIFY(i.hasPrevious());
+ QVERIFY(i2.hasNext());
+ QVERIFY(!i2.hasPrevious());
+ PointCoordinates c3(q);
+ PointCoordinatesIterator i3= c3;
+ QVERIFY(!i3.hasNext());
+ QVERIFY(!i3.hasPrevious());
+ i3.toBack();
+ QVERIFY(!i3.hasNext());
+ QVERIFY(!i3.hasPrevious());
+ QCOMPARE(i.peekPrevious(), p2);
+ QCOMPARE(i.previous(), p2);
+ QCOMPARE(i.previous(), p1);
+ QCOMPARE(i.previous(), p0);
+ QVERIFY(!i.hasPrevious());
+ QCOMPARE(i.peekNext(), p0);
+ // i.peekNext()= 1.0; // compiler error
+ QCOMPARE(i.next(), p0);
+ QCOMPARE(i.peekNext(), p1);
+ QCOMPARE(i.next(), p1);
+ QCOMPARE(i.next(), p2);
+ QVERIFY(!i.hasNext());
+ i.toFront();
+ QCOMPARE(i.next(), p0);
+}//t_coord_iterator
+
+void PointCoordinates_test::
+t_io()
+{
+ Qhull q;
+ PointCoordinates c(q);
+ ostringstream os;
+ os << "PointCoordinates 0-d\n" << c;
+ c.setDimension(2);
+ c << 1.0 << 2.0 << 3.0 << 1.0 << 2.0 << 3.0;
+ os << "PointCoordinates 1,2 3,1 2,3\n" << c;
+ cout << os.str();
+ QString s= QString::fromStdString(os.str());
+ QCOMPARE(s.count("0"), 3);
+ QCOMPARE(s.count("2"), 5);
+}//t_io
+
+}//orgQhull
+
+#include "moc/PointCoordinates_test.moc"
diff --git a/xs/src/qhull/src/qhulltest/QhullFacetList_test.cpp b/xs/src/qhull/src/qhulltest/QhullFacetList_test.cpp
new file mode 100644
index 000000000..5a09d01da
--- /dev/null
+++ b/xs/src/qhull/src/qhulltest/QhullFacetList_test.cpp
@@ -0,0 +1,196 @@
+/****************************************************************************
+**
+** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/qhulltest/QhullFacetList_test.cpp#3 $$Change: 2062 $
+** $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $
+**
+****************************************************************************/
+
+//pre-compiled headers
+#include <iostream>
+#include "qhulltest/RoadTest.h" // QT_VERSION
+
+#include "libqhullcpp/QhullFacetList.h"
+#include "libqhullcpp/QhullError.h"
+#include "libqhullcpp/QhullFacet.h"
+#include "libqhullcpp/QhullVertex.h"
+#include "libqhullcpp/QhullVertexSet.h"
+#include "libqhullcpp/Qhull.h"
+#include "libqhullcpp/RboxPoints.h"
+
+using std::cout;
+using std::endl;
+using std::ostringstream;
+using std::ostream;
+using std::string;
+
+namespace orgQhull {
+
+class QhullFacetList_test : public RoadTest
+{
+ Q_OBJECT
+
+#//!\name Test slots
+private slots:
+ void cleanup();
+ void t_construct_qh();
+ void t_construct_q();
+ void t_convert();
+ void t_readonly();
+ void t_foreach();
+ void t_io();
+};//QhullFacetList_test
+
+void
+add_QhullFacetList_test()
+{
+ new QhullFacetList_test(); // RoadTest::s_testcases
+}
+
+//Executed after each testcase
+void QhullFacetList_test::
+cleanup()
+{
+ RoadTest::cleanup();
+}
+
+void QhullFacetList_test::
+t_construct_qh()
+{
+ RboxPoints rcube("c");
+ Qhull q(rcube,"QR0"); // rotated unit cube
+ QhullFacetList fs2= q.facetList();
+ QVERIFY(!fs2.isEmpty());
+ QCOMPARE(fs2.count(),6);
+ QhullFacetList fs3(q.endFacet(), q.endFacet());
+ QVERIFY(fs3.isEmpty());
+ QhullFacetList fs4(q.endFacet().previous(), q.endFacet());
+ QCOMPARE(fs4.count(), 1);
+ QhullFacetList fs5(q.beginFacet(), q.endFacet());
+ QCOMPARE(fs2.count(), fs5.count());
+ QVERIFY(fs2==fs5);
+ QhullFacetList fs6= fs2; // copy constructor
+ QVERIFY(fs6==fs2);
+ std::vector<QhullFacet> fv= fs2.toStdVector();
+ QCOMPARE(fv.size(), 6u);
+}//t_construct_qh
+
+void QhullFacetList_test::
+t_construct_q()
+{
+ RboxPoints rcube("c");
+ Qhull q(rcube,"QR0"); // rotated unit cube
+ QhullFacetList fs2= q.facetList();
+ QVERIFY(!fs2.isEmpty());
+ QCOMPARE(fs2.count(),6);
+ QhullFacetList fs3(q.endFacet(), q.endFacet());
+ QVERIFY(fs3.isEmpty());
+ QhullFacetList fs4(q.endFacet().previous(), q.endFacet());
+ QCOMPARE(fs4.count(), 1);
+ QhullFacetList fs5(q.beginFacet(), q.endFacet());
+ QCOMPARE(fs2.count(), fs5.count());
+ QVERIFY(fs2==fs5);
+ QhullFacetList fs6= fs2; // copy constructor
+ QVERIFY(fs6==fs2);
+ std::vector<QhullFacet> fv= fs2.toStdVector();
+ QCOMPARE(fv.size(), 6u);
+}//t_construct_q
+
+void QhullFacetList_test::
+t_convert()
+{
+ RboxPoints rcube("c");
+ Qhull q(rcube,"QR0 QV2"); // rotated unit cube
+ QhullFacetList fs2= q.facetList();
+ QVERIFY(!fs2.isSelectAll());
+ QVERIFY(!fs2.isEmpty());
+ QCOMPARE(fs2.count(),3);
+ std::vector<QhullFacet> fv= fs2.toStdVector();
+ QCOMPARE(fv.size(), 3u);
+ QList<QhullFacet> fv2= fs2.toQList();
+ QCOMPARE(fv2.size(), 3);
+ std::vector<QhullVertex> fv5= fs2.vertices_toStdVector();
+ QCOMPARE(fv5.size(), 7u);
+ QList<QhullVertex> fv6= fs2.vertices_toQList();
+ QCOMPARE(fv6.size(), 7);
+ fs2.selectAll();
+ QVERIFY(fs2.isSelectAll());
+ std::vector<QhullFacet> fv3= fs2.toStdVector();
+ QCOMPARE(fv3.size(), 6u);
+ QList<QhullFacet> fv4= fs2.toQList();
+ QCOMPARE(fv4.size(), 6);
+ std::vector<QhullVertex> fv7= fs2.vertices_toStdVector();
+ QCOMPARE(fv7.size(), 8u);
+ QList<QhullVertex> fv8= fs2.vertices_toQList();
+ QCOMPARE(fv8.size(), 8);
+}//t_convert
+
+//! Spot check properties and read-only. See QhullLinkedList_test
+void QhullFacetList_test::
+t_readonly()
+{
+ RboxPoints rcube("c");
+ Qhull q(rcube,"QV0"); // good facets are adjacent to point 0
+ QhullFacetList fs= q.facetList();
+ QVERIFY(!fs.isSelectAll());
+ QCOMPARE(fs.count(), 3);
+ QCOMPARE(fs.first(), q.firstFacet());
+ fs.selectAll();
+ QVERIFY(fs.isSelectAll());
+ QCOMPARE(fs.count(), 6);
+ fs.selectGood();
+ QVERIFY(!fs.isSelectAll());
+ QCOMPARE(fs.count(), 3);
+ fs.selectAll();
+ QVERIFY(fs.isSelectAll());
+ QCOMPARE(fs.count(), 6);
+ QhullFacet f= fs.first();
+ QhullFacet f2= fs.last();
+ fs.selectAll();
+ QVERIFY(fs.contains(f));
+ QVERIFY(fs.contains(f2));
+ QVERIFY(f.isGood());
+ QVERIFY(!f2.isGood());
+ fs.selectGood();
+ QVERIFY(fs.contains(f));
+ QVERIFY(!fs.contains(f2));
+}//t_readonly
+
+void QhullFacetList_test::
+t_foreach()
+{
+ RboxPoints rcube("c");
+ // Spot check predicates and accessors. See QhullLinkedList_test
+ Qhull q(rcube,"Qt QR0"); // triangulation of rotated unit cube
+ QhullFacetList fs= q.facetList();
+ QVERIFY(fs.contains(q.firstFacet()));
+ QhullFacet f= q.firstFacet().next();
+ QVERIFY(fs.contains(f));
+ QCOMPARE(fs.first(), *fs.begin());
+ QCOMPARE(*(fs.end()-1), fs.last());
+ QCOMPARE(fs.first(), q.firstFacet());
+ QCOMPARE(*fs.begin(), q.beginFacet());
+ QCOMPARE(*fs.end(), q.endFacet());
+}//t_foreach
+
+void QhullFacetList_test::
+t_io()
+{
+ RboxPoints rcube("c");
+ {
+ Qhull q(rcube,"QR0 QV0"); // good facets are adjacent to point 0
+ QhullFacetList fs= q.facetList();
+ ostringstream os;
+ os << fs.print("Show all of FacetList\n");
+ os << "\nFacets only\n" << fs;
+ os << "\nVertices only\n" << fs.printVertices();
+ cout << os.str();
+ QString facets= QString::fromStdString(os.str());
+ QCOMPARE(facets.count("(v"), 2*7+12*3*2);
+ QCOMPARE(facets.count(QRegExp("f\\d")), 2*3*7 + 13*3*2);
+ }
+}//t_io
+
+}//orgQhull
+
+#include "moc/QhullFacetList_test.moc"
diff --git a/xs/src/qhull/src/qhulltest/QhullFacetSet_test.cpp b/xs/src/qhull/src/qhulltest/QhullFacetSet_test.cpp
new file mode 100644
index 000000000..a7fe123a2
--- /dev/null
+++ b/xs/src/qhull/src/qhulltest/QhullFacetSet_test.cpp
@@ -0,0 +1,153 @@
+/****************************************************************************
+**
+** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/qhulltest/QhullFacetSet_test.cpp#3 $$Change: 2062 $
+** $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $
+**
+****************************************************************************/
+
+//pre-compiled headers
+#include <iostream>
+#include "qhulltest/RoadTest.h" // QT_VERSION
+
+#include "libqhullcpp/QhullFacetSet.h"
+#include "libqhullcpp/QhullError.h"
+#include "libqhullcpp/QhullFacet.h"
+#include "libqhullcpp/Qhull.h"
+#include "libqhullcpp/RboxPoints.h"
+
+using std::cout;
+using std::endl;
+using std::ostringstream;
+using std::ostream;
+using std::string;
+
+namespace orgQhull {
+
+class QhullFacetSet_test : public RoadTest
+{
+ Q_OBJECT
+
+#//!\name Test slots
+private slots:
+ void cleanup();
+ void t_construct();
+ void t_convert();
+ void t_readonly();
+ void t_foreach();
+ void t_io();
+};//QhullFacetSet_test
+
+void
+add_QhullFacetSet_test()
+{
+ new QhullFacetSet_test(); // RoadTest::s_testcases
+}
+
+//Executed after each testcase
+void QhullFacetSet_test::
+cleanup()
+{
+ RoadTest::cleanup();
+}
+
+void QhullFacetSet_test::
+t_construct()
+{
+ RboxPoints rcube("c");
+ Qhull q(rcube,"QR0"); // rotated unit cube
+ QhullFacet f= q.firstFacet();
+ QhullFacetSet fs2= f.neighborFacets();
+ QVERIFY(!fs2.isEmpty());
+ QCOMPARE(fs2.count(),4);
+ QhullFacetSet fs4= fs2; // copy constructor
+ QVERIFY(fs4==fs2);
+ QhullFacetSet fs3(q, q.qh()->facet_mergeset);
+ QVERIFY(fs3.isEmpty());
+}//t_construct
+
+void QhullFacetSet_test::
+t_convert()
+{
+ RboxPoints rcube("c");
+ Qhull q2(rcube,"QR0 QV2"); // rotated unit cube
+ QhullFacet f2= q2.firstFacet();
+ QhullFacetSet fs2= f2.neighborFacets();
+ QVERIFY(!fs2.isSelectAll());
+ QCOMPARE(fs2.count(),2);
+ std::vector<QhullFacet> fv= fs2.toStdVector();
+ QCOMPARE(fv.size(), 2u);
+ QList<QhullFacet> fv2= fs2.toQList();
+ QCOMPARE(fv2.size(), 2);
+ fs2.selectAll();
+ QVERIFY(fs2.isSelectAll());
+ std::vector<QhullFacet> fv3= fs2.toStdVector();
+ QCOMPARE(fv3.size(), 4u);
+ QList<QhullFacet> fv4= fs2.toQList();
+ QCOMPARE(fv4.size(), 4);
+}//t_convert
+
+//! Spot check properties and read-only. See QhullSet_test
+void QhullFacetSet_test::
+t_readonly()
+{
+ RboxPoints rcube("c");
+ Qhull q(rcube,"QV0"); // good facets are adjacent to point 0
+ QhullFacetSet fs= q.firstFacet().neighborFacets();
+ QVERIFY(!fs.isSelectAll());
+ QCOMPARE(fs.count(), 2);
+ fs.selectAll();
+ QVERIFY(fs.isSelectAll());
+ QCOMPARE(fs.count(), 4);
+ fs.selectGood();
+ QVERIFY(!fs.isSelectAll());
+ QCOMPARE(fs.count(), 2);
+ QhullFacet f= fs.first();
+ QhullFacet f2= fs.last();
+ fs.selectAll();
+ QVERIFY(fs.contains(f));
+ QVERIFY(fs.contains(f2));
+ QVERIFY(f.isGood());
+ QVERIFY(!f2.isGood());
+ fs.selectGood();
+ QVERIFY(fs.contains(f));
+ QVERIFY(!fs.contains(f2));
+}//t_readonly
+
+void QhullFacetSet_test::
+t_foreach()
+{
+ RboxPoints rcube("c");
+ // Spot check predicates and accessors. See QhullLinkedList_test
+ Qhull q(rcube,"QR0"); // rotated unit cube
+ QhullFacetSet fs= q.firstFacet().neighborFacets();
+ QVERIFY(!fs.contains(q.firstFacet()));
+ QVERIFY(fs.contains(fs.first()));
+ QhullFacet f= q.firstFacet().next();
+ if(!fs.contains(f)){ // check if 'f' is the facet opposite firstFacet()
+ f= f.next();
+ }
+ QVERIFY(fs.contains(f));
+ QCOMPARE(fs.first(), *fs.begin());
+ QCOMPARE(*(fs.end()-1), fs.last());
+}//t_foreach
+
+void QhullFacetSet_test::
+t_io()
+{
+ RboxPoints rcube("c");
+ {
+ Qhull q(rcube,"QR0 QV0"); // good facets are adjacent to point 0
+ QhullFacetSet fs= q.firstFacet().neighborFacets();
+ ostringstream os;
+ os << fs.print("Neighbors of first facet with point 0");
+ os << fs.printIdentifiers("\nFacet identifiers: ");
+ cout << os.str();
+ QString facets= QString::fromStdString(os.str());
+ QCOMPARE(facets.count(QRegExp(" f[0-9]")), 2+13*2);
+ }
+}//t_io
+
+}//orgQhull
+
+#include "moc/QhullFacetSet_test.moc"
diff --git a/xs/src/qhull/src/qhulltest/QhullFacet_test.cpp b/xs/src/qhull/src/qhulltest/QhullFacet_test.cpp
new file mode 100644
index 000000000..271f63753
--- /dev/null
+++ b/xs/src/qhull/src/qhulltest/QhullFacet_test.cpp
@@ -0,0 +1,283 @@
+/****************************************************************************
+**
+** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/qhulltest/QhullFacet_test.cpp#4 $$Change: 2062 $
+** $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $
+**
+****************************************************************************/
+
+//pre-compiled headers
+#include <iostream>
+#include "qhulltest/RoadTest.h" // QT_VERSION
+
+#include "libqhullcpp/QhullFacet.h"
+#include "libqhullcpp/QhullError.h"
+#include "libqhullcpp/Coordinates.h"
+#include "libqhullcpp/RboxPoints.h"
+#include "libqhullcpp/QhullFacetList.h"
+#include "libqhullcpp/QhullFacetSet.h"
+#include "libqhullcpp/QhullPointSet.h"
+#include "libqhullcpp/QhullRidge.h"
+#include "libqhullcpp/Qhull.h"
+
+using std::cout;
+using std::endl;
+using std::ostringstream;
+using std::ostream;
+using std::string;
+
+namespace orgQhull {
+
+class QhullFacet_test : public RoadTest
+{
+ Q_OBJECT
+
+#//!\name Test slots
+private slots:
+ void cleanup();
+ void t_construct_qh();
+ void t_constructConvert();
+ void t_getSet();
+ void t_value();
+ void t_foreach();
+ void t_io();
+};//QhullFacet_test
+
+void
+add_QhullFacet_test()
+{
+ new QhullFacet_test(); // RoadTest::s_testcases
+}
+
+//Executed after each testcase
+void QhullFacet_test::
+cleanup()
+{
+ RoadTest::cleanup();
+}
+
+void QhullFacet_test::
+t_construct_qh()
+{
+ // Qhull.runQhull() constructs QhullFacets as facetT
+ QhullQh qh;
+ QhullFacet f(&qh);
+ QVERIFY(!f.isValid());
+ QCOMPARE(f.dimension(),0);
+}//t_construct_qh
+
+void QhullFacet_test::
+t_constructConvert()
+{
+ // Qhull.runQhull() constructs QhullFacets as facetT
+ Qhull q2;
+ QhullFacet f(q2);
+ QVERIFY(!f.isValid());
+ QCOMPARE(f.dimension(),0);
+ RboxPoints rcube("c");
+ Qhull q(rcube,"Qt QR0"); // triangulation of rotated unit cube
+ QhullFacet f2(q.beginFacet());
+ QCOMPARE(f2.dimension(),3);
+ f= f2; // copy assignment
+ QVERIFY(f.isValid());
+ QCOMPARE(f.dimension(),3);
+ QhullFacet f5= f2;
+ QVERIFY(f5==f2);
+ QVERIFY(f5==f);
+ QhullFacet f3(q, f2.getFacetT());
+ QCOMPARE(f,f3);
+ QhullFacet f4(q, f2.getBaseT());
+ QCOMPARE(f,f4);
+}//t_constructConvert
+
+void QhullFacet_test::
+t_getSet()
+{
+ RboxPoints rcube("c");
+ {
+ Qhull q(rcube,"Qt QR0"); // triangulation of rotated unit cube
+ cout << " rbox c | qhull Qt QR0 QR" << q.rotateRandom() << " distanceEpsilon " << q.distanceEpsilon() << endl;
+ QCOMPARE(q.facetCount(), 12);
+ QCOMPARE(q.vertexCount(), 8);
+ QhullFacetListIterator i(q.facetList());
+ while(i.hasNext()){
+ const QhullFacet f= i.next();
+ cout << f.id() << endl;
+ QCOMPARE(f.dimension(),3);
+ QVERIFY(f.id()>0 && f.id()<=39);
+ QVERIFY(f.isValid());
+ if(i.hasNext()){
+ QCOMPARE(f.next(), i.peekNext());
+ QVERIFY(f.next()!=f);
+ }
+ QVERIFY(i.hasPrevious());
+ QCOMPARE(f, i.peekPrevious());
+ }
+
+ // test tricoplanarOwner
+ QhullFacet facet = q.beginFacet();
+ QhullFacet tricoplanarOwner = facet.tricoplanarOwner();
+ int tricoplanarCount= 0;
+ i.toFront();
+ while(i.hasNext()){
+ const QhullFacet f= i.next();
+ if(f.tricoplanarOwner()==tricoplanarOwner){
+ tricoplanarCount++;
+ }
+ }
+ QCOMPARE(tricoplanarCount, 2);
+ int tricoplanarCount2= 0;
+ foreach (QhullFacet f, q.facetList()){ // Qt only
+ QhullHyperplane h= f.hyperplane();
+ cout << "Hyperplane: " << h;
+ QCOMPARE(h.count(), 3);
+ QCOMPARE(h.offset(), -0.5);
+ double n= h.norm();
+ QCOMPARE(n, 1.0);
+ QhullHyperplane hi= f.innerplane();
+ QCOMPARE(hi.count(), 3);
+ double innerOffset= hi.offset()+0.5;
+ cout << "InnerPlane: " << hi << " innerOffset+0.5 " << innerOffset << endl;
+ QVERIFY(innerOffset >= 0.0-(2*q.distanceEpsilon())); // A guessed epsilon. It needs to account for roundoff due to rotation of the vertices
+ QhullHyperplane ho= f.outerplane();
+ QCOMPARE(ho.count(), 3);
+ double outerOffset= ho.offset()+0.5;
+ cout << "OuterPlane: " << ho << " outerOffset+0.5 " << outerOffset << endl;
+ QVERIFY(outerOffset <= 0.0+(2*q.distanceEpsilon())); // A guessed epsilon. It needs to account for roundoff due to rotation of the vertices
+ QVERIFY(outerOffset-innerOffset < 1e-7);
+ for(int k= 0; k<3; k++){
+ QVERIFY(ho[k]==hi[k]);
+ QVERIFY(ho[k]==h[k]);
+ }
+ QhullPoint center= f.getCenter();
+ cout << "Center: " << center;
+ double d= f.distance(center);
+ QVERIFY(d < innerOffset-outerOffset);
+ QhullPoint center2= f.getCenter(qh_PRINTcentrums);
+ QCOMPARE(center, center2);
+ if(f.tricoplanarOwner()==tricoplanarOwner){
+ tricoplanarCount2++;
+ }
+ cout << endl;
+ }
+ QCOMPARE(tricoplanarCount2, 2);
+ Qhull q2(rcube,"d Qz Qt QR0"); // 3-d triangulation of Delaunay triangulation (the cube)
+ cout << " rbox c | qhull d Qz Qt QR0 QR" << q2.rotateRandom() << " distanceEpsilon " << q2.distanceEpsilon() << endl;
+ QhullFacet f2= q2.firstFacet();
+ QhullPoint center3= f2.getCenter(qh_PRINTtriangles);
+ QCOMPARE(center3.dimension(), 3);
+ QhullPoint center4= f2.getCenter();
+ QCOMPARE(center4.dimension(), 4);
+ for(int k= 0; k<3; k++){
+ QVERIFY(center4[k]==center3[k]);
+ }
+ Qhull q3(rcube,"v Qz QR0"); // Voronoi diagram of a cube (one vertex)
+ cout << " rbox c | qhull v Qz QR0 QR" << q3.rotateRandom() << " distanceEpsilon " << q3.distanceEpsilon() << endl;
+
+ q3.setFactorEpsilon(400); // Voronoi vertices are not necessarily within distance episilon
+ QhullPoint origin= q3.inputOrigin();
+ int voronoiCount= 0;
+ foreach(QhullFacet f, q3.facetList()){ //Qt only
+ if(f.isGood()){
+ ++voronoiCount;
+ QhullPoint p= f.voronoiVertex();
+ cout << p.print("Voronoi vertex: ")
+ << " Is it within " << q3.factorEpsilon() << " * distanceEpsilon (" << q3.distanceEpsilon() << ") of the origin?" << endl;
+ QCOMPARE(p, origin);
+ }
+ }
+ QCOMPARE(voronoiCount, 1);
+ }
+}//t_getSet
+
+void QhullFacet_test::
+t_value()
+{
+ RboxPoints rcube("c");
+ {
+ Qhull q(rcube, "");
+ coordT c[]= {0.0, 0.0, 0.0};
+ foreach (QhullFacet f, q.facetList()){ // Qt only
+ double d= f.distance(q.origin());
+ QCOMPARE(d, -0.5);
+ double d0= f.distance(c);
+ QCOMPARE(d0, -0.5);
+ double facetArea= f.facetArea();
+ QCOMPARE(facetArea, 1.0);
+ #if qh_MAXoutside
+ double maxoutside= f.getFacetT()->maxoutside;
+ QVERIFY(maxoutside<1e-7);
+ #endif
+ }
+ }
+}//t_value
+
+void QhullFacet_test::
+t_foreach()
+{
+ RboxPoints rcube("c W0 300"); // cube plus 300 points on its surface
+ {
+ Qhull q(rcube, "QR0 Qc"); // keep coplanars, thick facet, and rotate the cube
+ int coplanarCount= 0;
+ foreach(const QhullFacet f, q.facetList()){
+ QhullPointSet coplanars= f.coplanarPoints();
+ coplanarCount += coplanars.count();
+ QhullFacetSet neighbors= f.neighborFacets();
+ QCOMPARE(neighbors.count(), 4);
+ QhullPointSet outsides= f.outsidePoints();
+ QCOMPARE(outsides.count(), 0);
+ QhullRidgeSet ridges= f.ridges();
+ QCOMPARE(ridges.count(), 4);
+ QhullVertexSet vertices= f.vertices();
+ QCOMPARE(vertices.count(), 4);
+ int ridgeCount= 0;
+ QhullRidge r= ridges.first();
+ for(int r0= r.id(); ridgeCount==0 || r.id()!=r0; r= r.nextRidge3d(f)){
+ ++ridgeCount;
+ if(!r.hasNextRidge3d(f)){
+ QFAIL("Unexpected simplicial facet. They only have ridges to non-simplicial neighbors.");
+ }
+ }
+ QCOMPARE(ridgeCount, 4);
+ }
+ QCOMPARE(coplanarCount, 300);
+ }
+}//t_foreach
+
+void QhullFacet_test::
+t_io()
+{
+ RboxPoints rcube("c");
+ {
+ Qhull q(rcube, "");
+ QhullFacet f= q.beginFacet();
+ cout << f;
+ ostringstream os;
+ os << f.print("\nWith a message\n");
+ os << "\nPrint header for the same facet\n";
+ os << f.printHeader();
+ os << "\nPrint each component\n";
+ os << f.printFlags(" - flags:");
+ os << f.printCenter(qh_PRINTfacets, " - center: ");
+ os << f.printRidges();
+ cout << os.str();
+ ostringstream os2;
+ os2 << f;
+ QString facetString2= QString::fromStdString(os2.str());
+ facetString2.replace(QRegExp("\\s\\s+"), " ");
+ ostringstream os3;
+ q.qh()->setOutputStream(&os3);
+ q.outputQhull("f");
+ QString facetsString= QString::fromStdString(os3.str());
+ QString facetString3= facetsString.mid(facetsString.indexOf("- f1\n"));
+ facetString3= facetString3.left(facetString3.indexOf("\n- f")+1);
+ facetString3.replace(QRegExp("\\s\\s+"), " ");
+ QCOMPARE(facetString2, facetString3);
+ }
+}//t_io
+
+// toQhullFacet is static_cast only
+
+}//orgQhull
+
+#include "moc/QhullFacet_test.moc"
diff --git a/xs/src/qhull/src/qhulltest/QhullHyperplane_test.cpp b/xs/src/qhull/src/qhulltest/QhullHyperplane_test.cpp
new file mode 100644
index 000000000..d016989a9
--- /dev/null
+++ b/xs/src/qhull/src/qhulltest/QhullHyperplane_test.cpp
@@ -0,0 +1,429 @@
+/****************************************************************************
+**
+** Copyright (c) 2009-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/qhulltest/QhullHyperplane_test.cpp#4 $$Change: 2064 $
+** $DateTime: 2016/01/18 12:36:08 $$Author: bbarber $
+**
+****************************************************************************/
+
+//pre-compiled headers
+#include <iostream>
+#include "qhulltest/RoadTest.h" // QT_VERSION
+
+#include "libqhullcpp/QhullHyperplane.h"
+#include "libqhullcpp/QhullError.h"
+#include "libqhullcpp/RboxPoints.h"
+#include "libqhullcpp/QhullFacet.h"
+#include "libqhullcpp/QhullFacetList.h"
+#include "libqhullcpp/QhullFacetSet.h"
+#include "libqhullcpp/Qhull.h"
+
+#include <numeric>
+#include <vector>
+
+using std::cout;
+using std::endl;
+using std::ostringstream;
+using std::ostream;
+using std::string;
+
+namespace orgQhull {
+
+class QhullHyperplane_test : public RoadTest
+{
+ Q_OBJECT
+
+#//!\name Test slots
+private slots:
+ void cleanup();
+ void t_construct();
+ void t_construct_qh();
+ void t_convert();
+ void t_readonly();
+ void t_define();
+ void t_value();
+ void t_operator();
+ void t_iterator();
+ void t_const_iterator();
+ void t_qhullHyperplane_iterator();
+ void t_io();
+};//QhullHyperplane_test
+
+void
+add_QhullHyperplane_test()
+{
+ new QhullHyperplane_test(); // RoadTest::s_testcases
+}
+
+//Executed after each testcase
+void QhullHyperplane_test::
+cleanup()
+{
+ RoadTest::cleanup();
+}
+
+void QhullHyperplane_test::
+t_construct()
+{
+ QhullHyperplane h4;
+ QVERIFY(!h4.isValid());
+ QCOMPARE(h4.dimension(), 0);
+ // Qhull.runQhull() constructs QhullFacets as facetT
+ RboxPoints rcube("c");
+ Qhull q(rcube,"Qt QR0"); // triangulation of rotated unit cube
+ QhullHyperplane h(q);
+ QVERIFY(!h.isValid());
+ QCOMPARE(h.dimension(), 0);
+ QCOMPARE(h.coordinates(),static_cast<double *>(0));
+ QhullFacet f= q.firstFacet();
+ QhullHyperplane h2(f.hyperplane());
+ QVERIFY(h2.isValid());
+ QCOMPARE(h2.dimension(), 3);
+ h= h2;
+ QCOMPARE(h, h2);
+ QhullHyperplane h3(q, h2.dimension(), h2.coordinates(), h2.offset());
+ QCOMPARE(h2, h3);
+ QhullHyperplane h5= h2; // copy constructor
+ QVERIFY(h5==h2);
+}//t_construct
+
+void QhullHyperplane_test::
+t_construct_qh()
+{
+ // Qhull.runQhull() constructs QhullFacets as facetT
+ RboxPoints rcube("c");
+ Qhull q(rcube,"Qt QR0"); // triangulation of rotated unit cube
+ QhullFacet f= q.firstFacet();
+ QhullHyperplane h2(f.hyperplane());
+ QVERIFY(h2.isValid());
+ QCOMPARE(h2.dimension(), 3);
+ // h= h2; // copy assignment disabled, ambiguous
+ QhullHyperplane h3(q.qh(), h2.dimension(), h2.coordinates(), h2.offset());
+ QCOMPARE(h2, h3);
+ QhullHyperplane h5= h2; // copy constructor
+ QVERIFY(h5==h2);
+}//t_construct_qh
+
+void QhullHyperplane_test::
+t_convert()
+{
+ RboxPoints rcube("c");
+ Qhull q(rcube,"Qt QR0"); // triangulation of rotated unit cube
+ QhullHyperplane h= q.firstFacet().hyperplane();
+ std::vector<double> fs= h.toStdVector();
+ QCOMPARE(fs.size(), 4u);
+ double offset= fs.back();
+ fs.pop_back();
+ QCOMPARE(offset, -0.5);
+
+ double squareNorm= inner_product(fs.begin(), fs.end(), fs.begin(), 0.0);
+ QCOMPARE(squareNorm, 1.0);
+ QList<double> qs= h.toQList();
+ QCOMPARE(qs.size(), 4);
+ double offset2= qs.takeLast();
+ QCOMPARE(offset2, -0.5);
+ double squareNorm2= std::inner_product(qs.begin(), qs.end(), qs.begin(), 0.0);
+ QCOMPARE(squareNorm2, 1.0);
+}//t_convert
+
+void QhullHyperplane_test::
+t_readonly()
+{
+ RboxPoints rcube("c");
+ {
+ Qhull q(rcube,"Qt QR0"); // triangulation of rotated unit cube
+ QhullFacetList fs= q.facetList();
+ QhullFacetListIterator i(fs);
+ while(i.hasNext()){
+ QhullFacet f= i.next();
+ QhullHyperplane h= f.hyperplane();
+ int id= f.id();
+ cout << "h" << id << endl;
+ QVERIFY(h.isValid());
+ QCOMPARE(h.dimension(),3);
+ const coordT *c= h.coordinates();
+ coordT *c2= h.coordinates();
+ QCOMPARE(c, c2);
+ const coordT *c3= h.begin();
+ QCOMPARE(c, c3);
+ QCOMPARE(h.offset(), -0.5);
+ int j= h.end()-h.begin();
+ QCOMPARE(j, 3);
+ double squareNorm= std::inner_product(h.begin(), h.end(), h.begin(), 0.0);
+ QCOMPARE(squareNorm, 1.0);
+ }
+ QhullHyperplane h2= fs.first().hyperplane();
+ QhullHyperplane h3= fs.last().hyperplane();
+ QVERIFY(h2!=h3);
+ QVERIFY(h3.coordinates()!=h2.coordinates());
+ }
+}//t_readonly
+
+void QhullHyperplane_test::
+t_define()
+{
+ RboxPoints rcube("c");
+ {
+ Qhull q(rcube,"Qt QR0"); // triangulation of rotated unit cube
+ QhullFacetList fs= q.facetList();
+ QhullHyperplane h= fs.first().hyperplane();
+ QhullHyperplane h2= h;
+ QVERIFY(h==h2);
+ QhullHyperplane h3= fs.last().hyperplane();
+ QVERIFY(h2!=h3);
+
+ QhullHyperplane h4= h3;
+ h4.defineAs(h2);
+ QVERIFY(h2==h4);
+ QhullHyperplane p5= h3;
+ p5.defineAs(h2.dimension(), h2.coordinates(), h2.offset());
+ QVERIFY(h2==p5);
+ QhullHyperplane h6= h3;
+ h6.setCoordinates(h2.coordinates());
+ QCOMPARE(h2.coordinates(), h6.coordinates());
+ h6.setOffset(h2.offset());
+ QCOMPARE(h2.offset(), h6.offset());
+ QVERIFY(h2==h6);
+ h6.setDimension(2);
+ QCOMPARE(h6.dimension(), 2);
+ QVERIFY(h2!=h6);
+ }
+}//t_define
+
+void QhullHyperplane_test::
+t_value()
+{
+ RboxPoints rcube("c G1");
+ Qhull q(rcube,"Qt QR0"); // triangulation of rotated unit cube
+ QhullFacet f= q.firstFacet();
+ QhullFacet f2= f.neighborFacets().at(0);
+ const QhullHyperplane h= f.hyperplane();
+ const QhullHyperplane h2= f2.hyperplane(); // At right angles
+ double dist= h.distance(q.origin());
+ QCOMPARE(dist, -1.0);
+ double norm= h.norm();
+ QCOMPARE(norm, 1.0);
+ double angle= h.hyperplaneAngle(h2);
+ cout << "angle " << angle << endl;
+ QCOMPARE(angle+1.0, 1.0); // qFuzzyCompare does not work for 0.0
+ QVERIFY(h==h);
+ QVERIFY(h!=h2);
+}//t_value
+
+void QhullHyperplane_test::
+t_operator()
+{
+ RboxPoints rcube("c");
+ Qhull q(rcube,"Qt QR0"); // triangulation of rotated unit cube
+ const QhullHyperplane h= q.firstFacet().hyperplane();
+ //operator== and operator!= tested elsewhere
+ const coordT *c= h.coordinates();
+ for(int k=h.dimension(); k--; ){
+ QCOMPARE(c[k], h[k]);
+ }
+ //h[0]= 10.0; // compiler error, const
+ QhullHyperplane h2= q.firstFacet().hyperplane();
+ h2[0]= 10.0; // Overwrites Hyperplane coordinate!
+ QCOMPARE(h2[0], 10.0);
+}//t_operator
+
+void QhullHyperplane_test::
+t_iterator()
+{
+ RboxPoints rcube("c");
+ {
+ Qhull q(rcube,"QR0"); // rotated unit cube
+ QhullHyperplane h= q.firstFacet().hyperplane();
+ QCOMPARE(h.count(), 3);
+ QCOMPARE(h.size(), 3u);
+ QhullHyperplane::Iterator i= h.begin();
+ QhullHyperplane::iterator i2= h.begin();
+ QVERIFY(i==i2);
+ QVERIFY(i>=i2);
+ QVERIFY(i<=i2);
+ i= h.begin();
+ QVERIFY(i==i2);
+ i2= h.end();
+ QVERIFY(i!=i2);
+ double d3= *i;
+ i2--;
+ double d2= *i2;
+ QCOMPARE(d3, h[0]);
+ QCOMPARE(d2, h[2]);
+ QhullHyperplane::Iterator i3(i2);
+ QCOMPARE(*i2, *i3);
+
+ (i3= i)++;
+ QCOMPARE((*i3), h[1]);
+ QVERIFY(i==i);
+ QVERIFY(i!=i2);
+ QVERIFY(i<i2);
+ QVERIFY(i<=i2);
+ QVERIFY(i2>i);
+ QVERIFY(i2>=i);
+
+ QhullHyperplane::ConstIterator i4= h.begin();
+ QVERIFY(i==i4); // iterator COMP const_iterator
+ QVERIFY(i<=i4);
+ QVERIFY(i>=i4);
+ QVERIFY(i4==i); // const_iterator COMP iterator
+ QVERIFY(i4<=i);
+ QVERIFY(i4>=i);
+ QVERIFY(i>=i4);
+ QVERIFY(i4<=i);
+ QVERIFY(i2!=i4);
+ QVERIFY(i2>i4);
+ QVERIFY(i2>=i4);
+ QVERIFY(i4!=i2);
+ QVERIFY(i4<i2);
+ QVERIFY(i4<=i2);
+ ++i4;
+ QVERIFY(i<i4);
+ QVERIFY(i<=i4);
+ QVERIFY(i4>i);
+ QVERIFY(i4>=i);
+
+ i= h.begin();
+ i2= h.begin();
+ QCOMPARE(i, i2++);
+ QCOMPARE(*i2, h[1]);
+ QCOMPARE(++i, i2);
+ QCOMPARE(i, i2--);
+ QCOMPARE(i2, h.begin());
+ QCOMPARE(--i, i2);
+ QCOMPARE(i2 += 3, h.end());
+ QCOMPARE(i2 -= 3, h.begin());
+ QCOMPARE(i2+0, h.begin());
+ QCOMPARE(i2+3, h.end());
+ i2 += 3;
+ i= i2-0;
+ QCOMPARE(i, i2);
+ i= i2-3;
+ QCOMPARE(i, h.begin());
+ QCOMPARE(i2-i, 3);
+
+ //h.begin end tested above
+
+ // QhullHyperplane is const-only
+ }
+}//t_iterator
+
+void QhullHyperplane_test::
+t_const_iterator()
+{
+ RboxPoints rcube("c");
+ {
+ Qhull q(rcube,"QR0"); // rotated unit cube
+ QhullHyperplane h= q.firstFacet().hyperplane();
+ QhullHyperplane::ConstIterator i= h.begin();
+ QhullHyperplane::const_iterator i2= h.begin();
+ QVERIFY(i==i2);
+ QVERIFY(i>=i2);
+ QVERIFY(i<=i2);
+ i= h.begin();
+ QVERIFY(i==i2);
+ i2= h.end();
+ QVERIFY(i!=i2);
+ double d3= *i;
+ i2--;
+ double d2= *i2;
+ QCOMPARE(d3, h[0]);
+ QCOMPARE(d2, h[2]);
+ QhullHyperplane::ConstIterator i3(i2);
+ QCOMPARE(*i2, *i3);
+
+ (i3= i)++;
+ QCOMPARE((*i3), h[1]);
+ QVERIFY(i==i);
+ QVERIFY(i!=i2);
+ QVERIFY(i<i2);
+ QVERIFY(i<=i2);
+ QVERIFY(i2>i);
+ QVERIFY(i2>=i);
+
+ // See t_iterator for const_iterator COMP iterator
+
+ i= h.begin();
+ i2= h.constBegin();
+ QCOMPARE(i, i2++);
+ QCOMPARE(*i2, h[1]);
+ QCOMPARE(++i, i2);
+ QCOMPARE(i, i2--);
+ QCOMPARE(i2, h.constBegin());
+ QCOMPARE(--i, i2);
+ QCOMPARE(i2+=3, h.constEnd());
+ QCOMPARE(i2-=3, h.constBegin());
+ QCOMPARE(i2+0, h.constBegin());
+ QCOMPARE(i2+3, h.constEnd());
+ i2 += 3;
+ i= i2-0;
+ QCOMPARE(i, i2);
+ i= i2-3;
+ QCOMPARE(i, h.constBegin());
+ QCOMPARE(i2-i, 3);
+
+ // QhullHyperplane is const-only
+ }
+}//t_const_iterator
+
+void QhullHyperplane_test::
+t_qhullHyperplane_iterator()
+{
+ RboxPoints rcube("c");
+ Qhull q(rcube,"QR0"); // rotated unit cube
+ QhullHyperplane h = q.firstFacet().hyperplane();
+ QhullHyperplaneIterator i2(h);
+ QCOMPARE(h.dimension(), 3);
+ QhullHyperplaneIterator i= h;
+ QVERIFY(i2.hasNext());
+ QVERIFY(!i2.hasPrevious());
+ QVERIFY(i.hasNext());
+ QVERIFY(!i.hasPrevious());
+ i2.toBack();
+ i.toFront();
+ QVERIFY(!i2.hasNext());
+ QVERIFY(i2.hasPrevious());
+ QVERIFY(i.hasNext());
+ QVERIFY(!i.hasPrevious());
+
+ // i at front, i2 at end/back, 3 coordinates
+ QCOMPARE(i.peekNext(), h[0]);
+ QCOMPARE(i2.peekPrevious(), h[2]);
+ QCOMPARE(i2.previous(), h[2]);
+ QCOMPARE(i2.previous(), h[1]);
+ QCOMPARE(i2.previous(), h[0]);
+ QVERIFY(!i2.hasPrevious());
+ QCOMPARE(i.peekNext(), h[0]);
+ // i.peekNext()= 1.0; // compiler error, i is const
+ QCOMPARE(i.next(), h[0]);
+ QCOMPARE(i.peekNext(), h[1]);
+ QCOMPARE(i.next(), h[1]);
+ QCOMPARE(i.next(), h[2]);
+ QVERIFY(!i.hasNext());
+ i.toFront();
+ QCOMPARE(i.next(), h[0]);
+}//t_qhullHyperplane_iterator
+
+void QhullHyperplane_test::
+t_io()
+{
+ RboxPoints rcube("c");
+ {
+ Qhull q(rcube, "");
+ QhullHyperplane h= q.firstFacet().hyperplane();
+ ostringstream os;
+ os << "Hyperplane:\n";
+ os << h;
+ os << h.print("message");
+ os << h.print(" and a message ", " offset ");
+ cout << os.str();
+ QString s= QString::fromStdString(os.str());
+ QCOMPARE(s.count("1"), 3);
+ // QCOMPARE(s.count(QRegExp("f\\d")), 3*7 + 13*3*2);
+ }
+}//t_io
+
+
+}//orgQhull
+
+#include "moc/QhullHyperplane_test.moc"
diff --git a/xs/src/qhull/src/qhulltest/QhullLinkedList_test.cpp b/xs/src/qhull/src/qhulltest/QhullLinkedList_test.cpp
new file mode 100644
index 000000000..e0cde4050
--- /dev/null
+++ b/xs/src/qhull/src/qhulltest/QhullLinkedList_test.cpp
@@ -0,0 +1,330 @@
+/****************************************************************************
+**
+** Copyright (c) 2009-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/qhulltest/QhullLinkedList_test.cpp#3 $$Change: 2062 $
+** $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $
+**
+****************************************************************************/
+
+//pre-compiled headers
+#include <QtCore/QList>
+#include "qhulltest/RoadTest.h"
+
+#include "libqhullcpp/QhullLinkedList.h"
+#include "libqhullcpp/Qhull.h"
+#include "libqhullcpp/QhullVertex.h"
+#include "libqhullcpp/RboxPoints.h"
+
+namespace orgQhull {
+
+class QhullLinkedList_test : public RoadTest
+{
+ Q_OBJECT
+
+#//!\name Test slots
+private slots:
+ void cleanup();
+ void t_construct();
+ void t_convert();
+ void t_element();
+ void t_search();
+ void t_iterator();
+ void t_const_iterator();
+ void t_QhullLinkedList_iterator();
+ void t_io();
+};//QhullLinkedList_test
+
+void
+add_QhullLinkedList_test()
+{
+ new QhullLinkedList_test(); // RoadTest::s_testcases
+}
+
+//Executed after each testcase
+void QhullLinkedList_test::
+cleanup()
+{
+ RoadTest::cleanup();
+}
+
+void QhullLinkedList_test::
+t_construct()
+{
+ // QhullLinkedList vs; //private (compiler error). No memory allocation
+ RboxPoints rcube("c");
+ {
+ Qhull q(rcube,"Qt QR0"); // triangulation of rotated unit cube
+ QCOMPARE(q.facetCount(), 12);
+ QhullVertexList vs = QhullVertexList(q.beginVertex(), q.endVertex());
+ QCOMPARE(vs.count(), 8);
+ QCOMPARE(vs.size(), 8u);
+ QVERIFY(!vs.isEmpty());
+ QhullVertexList vs2 = q.vertexList();
+ QCOMPARE(vs2.count(), 8);
+ QCOMPARE(vs2.size(),8u);
+ QVERIFY(!vs2.isEmpty());
+ QVERIFY(vs==vs2);
+ // vs= vs2; // disabled. Would not copy the vertices
+ QhullVertexList vs3= vs2; // copy constructor
+ QVERIFY(vs3==vs2);
+ }
+}//t_construct
+
+void QhullLinkedList_test::
+t_convert()
+{
+ RboxPoints rcube("c");
+ {
+ Qhull q(rcube,"Qt QR0"); // triangulation of rotated unit cube
+ QCOMPARE(q.facetCount(), 12);
+ QhullVertexList vs = q.vertexList();
+ QCOMPARE(vs.size(), 8u);
+ QVERIFY(!vs.isEmpty());
+ std::vector<QhullVertex> vs2= vs.toStdVector();
+ QCOMPARE(vs2.size(), vs.size());
+ QhullVertexList::Iterator i= vs.begin();
+ for(int k= 0; k<(int)vs2.size(); k++){
+ QCOMPARE(vs2[k], *i++);
+ }
+ QList<QhullVertex> vs3= vs.toQList();
+ QCOMPARE(vs3.count(), vs.count());
+ i= vs.begin();
+ for(int k= 0; k<vs3.count(); k++){
+ QCOMPARE(vs3[k], *i++);
+ }
+ QhullVertexList vs4(q.endVertex(), q.endVertex());
+ QVERIFY(vs4.isEmpty());
+ QVERIFY(vs==vs);
+ QVERIFY(vs4==vs4);
+ QVERIFY(vs!=vs4);
+ }
+}//t_convert
+
+//ReadOnly tested by t_convert
+
+void QhullLinkedList_test::
+t_element()
+{
+ RboxPoints rcube("c");
+ Qhull q(rcube,"QR0"); // rotated unit cube
+ QhullVertexList vs = q.vertexList();
+ QhullVertex v= vs.first();
+ QCOMPARE(v.previous(), QhullVertex(NULL));
+ QCOMPARE(vs.front(), vs.first());
+ QhullVertex v2= vs.last();
+ QCOMPARE(v2.next().next(), QhullVertex(NULL)); // sentinel has NULL next
+ QCOMPARE(vs.back(), v2);
+ QCOMPARE(vs.back(), vs.last());
+}//t_element
+
+void QhullLinkedList_test::
+t_search()
+{
+ RboxPoints rcube("c");
+ Qhull q(rcube,"QR0"); // rotated unit cube
+ QhullVertexList vs = q.vertexList();
+ QhullVertex v(q);
+ QVERIFY(!vs.contains(v));
+ QCOMPARE(vs.count(v), 0);
+ QhullVertex v2= *vs.begin();
+ QhullVertex v3= vs.last();
+ QVERIFY(vs.contains(v2));
+ QCOMPARE(vs.count(v2), 1);
+ QVERIFY(vs.contains(v3));
+ QCOMPARE(vs.count(v3), 1);
+}//t_search
+
+void QhullLinkedList_test::
+t_iterator()
+{
+ RboxPoints rcube("c");
+ {
+ Qhull q(rcube,"QR0"); // rotated unit cube
+ QhullVertexList vs = q.vertexList();
+ QhullVertexList::Iterator i= vs.begin();
+ QhullVertexList::iterator i2= vs.begin();
+ QVERIFY(i==i2);
+ // No comparisons
+ i= vs.begin();
+ QVERIFY(i==i2);
+ i2= vs.end();
+ QVERIFY(i!=i2);
+ QhullVertex v3(*i);
+ i2--;
+ QhullVertex v8= *i2;
+ QhullVertex v= vs.first();
+ QhullVertex v2= v.next();
+ QCOMPARE(v3.id(), v.id());
+ QCOMPARE(v8.id(), vs.last().id());
+ QhullVertexList::Iterator i3(i2);
+ QCOMPARE(*i2, *i3);
+
+ (i3= i)++;
+ QCOMPARE((*i3).id(), v2.id());
+ QVERIFY(i==i);
+ QVERIFY(i!=i2);
+
+ QhullVertexList::ConstIterator i4= vs.begin();
+ QVERIFY(i==i4); // iterator COMP const_iterator
+ QVERIFY(i4==i); // const_iterator COMP iterator
+ QVERIFY(i2!=i4);
+ QVERIFY(i4!=i2);
+ ++i4;
+
+ i= vs.begin();
+ i2= vs.begin();
+ QCOMPARE(i, i2++);
+ QCOMPARE(*i2, v2);
+ QCOMPARE(++i, i2);
+ QCOMPARE(i, i2--);
+ QCOMPARE(i2, vs.begin());
+ QCOMPARE(--i, i2);
+ QCOMPARE(i2 += 8, vs.end());
+ QCOMPARE(i2 -= 8, vs.begin());
+ QCOMPARE(i2+0, vs.begin());
+ QCOMPARE(i2+8, vs.end());
+ i2 += 8;
+ i= i2-0;
+ QCOMPARE(i, i2);
+ i= i2-8;
+ QCOMPARE(i, vs.begin());
+
+ //vs.begin end tested above
+
+ // QhullVertexList is const-only
+ }
+}//t_iterator
+
+void QhullLinkedList_test::
+t_const_iterator()
+{
+ RboxPoints rcube("c");
+ {
+ Qhull q(rcube,"QR0"); // rotated unit cube
+ QhullVertexList vs = q.vertexList();
+ QhullVertexList::ConstIterator i= vs.begin();
+ QhullVertexList::const_iterator i2= vs.begin();
+ QVERIFY(i==i2);
+ i= vs.begin();
+ QVERIFY(i==i2);
+ i2= vs.end();
+ QVERIFY(i!=i2);
+ QhullVertex v3(*i);
+ i2--;
+ QhullVertex v8= *i2;
+ QhullVertex v= vs.first();
+ QhullVertex v2= v.next();
+ QCOMPARE(v3.id(), v.id());
+ QCOMPARE(v8.id(), vs.last().id());
+ QhullVertexList::ConstIterator i3(i2);
+ QCOMPARE(*i2, *i3);
+
+ (i3= i)++;
+ QCOMPARE((*i3).id(), v2.id());
+ QVERIFY(i==i);
+ QVERIFY(i!=i2);
+
+ // See t_iterator for const_iterator COMP iterator
+
+ i= vs.begin();
+ i2= vs.constBegin();
+ QCOMPARE(i, i2++);
+ QCOMPARE(*i2, v2);
+ QCOMPARE(++i, i2);
+ QCOMPARE(i, i2--);
+ QCOMPARE(i2, vs.constBegin());
+ QCOMPARE(--i, i2);
+ QCOMPARE(i2 += 8, vs.constEnd());
+ QCOMPARE(i2 -= 8, vs.constBegin());
+ QCOMPARE(i2+0, vs.constBegin());
+ QCOMPARE(i2+8, vs.constEnd());
+ i2 += 8;
+ i= i2-0;
+ QCOMPARE(i, i2);
+ i= i2-8;
+ QCOMPARE(i, vs.constBegin());
+
+ // QhullVertexList is const-only
+ }
+}//t_const_iterator
+
+void QhullLinkedList_test::
+t_QhullLinkedList_iterator()
+{
+ RboxPoints rcube("c");
+ Qhull q(rcube,"QR0"); // rotated unit cube
+ QhullVertexList vs(q.endVertex(), q.endVertex());
+ QhullVertexListIterator i= vs;
+ QCOMPARE(vs.count(), 0);
+ QVERIFY(!i.hasNext());
+ QVERIFY(!i.hasPrevious());
+ i.toBack();
+ QVERIFY(!i.hasNext());
+ QVERIFY(!i.hasPrevious());
+
+ QhullVertexList vs2 = q.vertexList();
+ QhullVertexListIterator i2(vs2);
+ QCOMPARE(vs2.count(), 8);
+ i= vs2;
+ QVERIFY(i2.hasNext());
+ QVERIFY(!i2.hasPrevious());
+ QVERIFY(i.hasNext());
+ QVERIFY(!i.hasPrevious());
+ i2.toBack();
+ i.toFront();
+ QVERIFY(!i2.hasNext());
+ QVERIFY(i2.hasPrevious());
+ QVERIFY(i.hasNext());
+ QVERIFY(!i.hasPrevious());
+
+ // i at front, i2 at end/back, 4 neighbors
+ QhullVertexList vs3 = q.vertexList(); // same as vs2
+ QhullVertex v3(vs3.first());
+ QhullVertex v4= vs3.first();
+ QCOMPARE(v3, v4);
+ QVERIFY(v3==v4);
+ QhullVertex v5(v4.next());
+ QVERIFY(v4!=v5);
+ QhullVertex v6(v5.next());
+ QhullVertex v7(v6.next());
+ QhullVertex v8(vs3.last());
+ QCOMPARE(i2.peekPrevious(), v8);
+ i2.previous();
+ i2.previous();
+ i2.previous();
+ i2.previous();
+ QCOMPARE(i2.previous(), v7);
+ QCOMPARE(i2.previous(), v6);
+ QCOMPARE(i2.previous(), v5);
+ QCOMPARE(i2.previous(), v4);
+ QVERIFY(!i2.hasPrevious());
+ QCOMPARE(i.peekNext(), v4);
+ // i.peekNext()= 1.0; // compiler error
+ QCOMPARE(i.next(), v4);
+ QCOMPARE(i.peekNext(), v5);
+ QCOMPARE(i.next(), v5);
+ QCOMPARE(i.next(), v6);
+ QCOMPARE(i.next(), v7);
+ i.next();
+ i.next();
+ i.next();
+ QCOMPARE(i.next(), v8);
+ QVERIFY(!i.hasNext());
+ i.toFront();
+ QCOMPARE(i.next(), v4);
+}//t_QhullLinkedList_iterator
+
+void QhullLinkedList_test::
+t_io()
+{
+ RboxPoints rcube("c");
+ Qhull q(rcube,"QR0"); // rotated unit cube
+ QhullVertexList vs(q.endVertex(), q.endVertex());
+ std::cout << "INFO: empty QhullVertextList" << vs << std::endl;
+ QhullVertexList vs2= q.vertexList();
+ std::cout << "INFO: " << vs2 << std::endl;
+}//t_io
+
+}//namespace orgQhull
+
+#include "moc/QhullLinkedList_test.moc"
diff --git a/xs/src/qhull/src/qhulltest/QhullPointSet_test.cpp b/xs/src/qhull/src/qhulltest/QhullPointSet_test.cpp
new file mode 100644
index 000000000..ff633a518
--- /dev/null
+++ b/xs/src/qhull/src/qhulltest/QhullPointSet_test.cpp
@@ -0,0 +1,378 @@
+/****************************************************************************
+**
+** Copyright (p) 2009-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/qhulltest/QhullPointSet_test.cpp#3 $$Change: 2062 $
+** $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $
+**
+****************************************************************************/
+
+//pre-compiled header
+#include <iostream>
+#include "RoadTest.h" // QT_VERSION
+
+#include "libqhullcpp/QhullPointSet.h"
+#include "libqhullcpp/RboxPoints.h"
+#include "libqhullcpp/QhullPoint.h"
+#include "libqhullcpp/QhullFacet.h"
+#include "libqhullcpp/QhullFacetList.h"
+#include "libqhullcpp/Qhull.h"
+
+using std::cout;
+using std::endl;
+using std::ostringstream;
+
+namespace orgQhull {
+
+class QhullPointSet_test : public RoadTest
+{
+ Q_OBJECT
+
+#//!\name Test slots
+private slots:
+ void cleanup();
+ void t_construct();
+ void t_convert();
+ void t_element();
+ void t_iterator();
+ void t_const_iterator();
+ void t_search();
+ void t_pointset_iterator();
+ void t_io();
+};//QhullPointSet_test
+
+void
+add_QhullPointSet_test()
+{
+ new QhullPointSet_test(); // RoadTest::s_testcases
+}
+
+//Executed after each testcase
+void QhullPointSet_test::
+cleanup()
+{
+ RoadTest::cleanup();
+}
+
+void QhullPointSet_test::
+t_construct()
+{
+ // Default constructor is disallowed (i.e., private)
+ RboxPoints rcube("c W0 1000");
+ Qhull q(rcube,"Qc"); // cube with 1000 coplanar points
+ int coplanarCount= 0;
+ foreach(QhullFacet f, q.facetList()){
+ QhullPointSet ps(q, f.getFacetT()->outsideset);
+ QVERIFY(ps.isEmpty());
+ QCOMPARE(ps.count(), 0);
+ QCOMPARE(ps.size(), 0u);
+ QhullPointSet ps2(q.qh(), f.getFacetT()->coplanarset);
+ QVERIFY(!ps2.isEmpty());
+ coplanarCount += ps2.count();
+ QCOMPARE(ps2.count(), (int)ps2.size());
+ QhullPointSet ps3(ps2);
+ QVERIFY(!ps3.isEmpty());
+ QCOMPARE(ps3.count(), ps2.count());
+ QVERIFY(ps3==ps2);
+ QVERIFY(ps3!=ps);
+ QhullPointSet ps4= ps3;
+ QVERIFY(ps4==ps2);
+ }
+ QCOMPARE(coplanarCount, 1000);
+}//t_construct
+
+void QhullPointSet_test::
+t_convert()
+{
+ RboxPoints rcube("c W0 1000");
+ Qhull q(rcube,"Qc"); // cube with 1000 coplanar points
+ QhullFacet f= q.firstFacet();
+ QhullPointSet ps= f.coplanarPoints();
+ QVERIFY(ps.count()>=1); // Sometimes no coplanar points
+ std::vector<QhullPoint> vs= ps.toStdVector();
+ QCOMPARE(vs.size(), ps.size());
+ QhullPoint p= ps[0];
+ QhullPoint p2= vs[0];
+ QCOMPARE(p, p2);
+ QList<QhullPoint> qs= ps.toQList();
+ QCOMPARE(qs.size(), static_cast<int>(ps.size()));
+ QhullPoint p3= qs[0];
+ QCOMPARE(p3, p);
+}//t_convert
+
+// readonly tested in t_construct
+// empty, isEmpty, ==, !=, size
+
+void QhullPointSet_test::
+t_element()
+{
+ RboxPoints rcube("c W0 1000");
+ Qhull q(rcube,"Qc"); // cube with 1000 coplanar points
+ QhullFacet f= q.firstFacet();
+ QhullPointSet ps= f.coplanarPoints();
+ QVERIFY(ps.count()>=3); // Sometimes no coplanar points
+ QhullPoint p= ps[0];
+ QCOMPARE(p, ps[0]);
+ QhullPoint p2= ps[ps.count()-1];
+ QCOMPARE(ps.at(1), ps[1]);
+ QCOMPARE(ps.second(), ps[1]);
+ QCOMPARE(ps.first(), p);
+ QCOMPARE(ps.front(), ps.first());
+ QCOMPARE(ps.last(), p2);
+ QCOMPARE(ps.back(), ps.last());
+ QhullPoint p8(q);
+ QCOMPARE(ps.value(2), ps[2]);
+ QCOMPARE(ps.value(-1), p8);
+ QCOMPARE(ps.value(ps.count()), p8);
+ QCOMPARE(ps.value(ps.count(), p), p);
+ QVERIFY(ps.value(1, p)!=p);
+ QhullPointSet ps8= f.coplanarPoints();
+ QhullPointSet::Iterator i= ps8.begin();
+ foreach(QhullPoint p9, ps){ // Qt only
+ QCOMPARE(p9.dimension(), 3);
+ QCOMPARE(p9, *i++);
+ }
+}//t_element
+
+void QhullPointSet_test::
+t_iterator()
+{
+ RboxPoints rcube("c W0 1000");
+ Qhull q(rcube,"Qc"); // cube with 1000 coplanar points
+ QhullFacet f= q.firstFacet();
+ QhullPointSet ps= f.coplanarPoints();
+ QVERIFY(ps.count()>=3); // Sometimes no coplanar points
+ QhullPointSet::Iterator i= ps.begin();
+ QhullPointSet::iterator i2= ps.begin();
+ QVERIFY(i==i2);
+ QVERIFY(i>=i2);
+ QVERIFY(i<=i2);
+ i= ps.begin();
+ QVERIFY(i==i2);
+ i2= ps.end();
+ QVERIFY(i!=i2);
+ QhullPoint p= *i;
+ QCOMPARE(p.dimension(), q.dimension());
+ QCOMPARE(p, ps[0]);
+ i2--;
+ QhullPoint p2= *i2;
+ QCOMPARE(p2.dimension(), q.dimension());
+ QCOMPARE(p2, ps.last());
+ QhullPointSet::Iterator i5(i2);
+ QCOMPARE(*i2, *i5);
+ QhullPointSet::Iterator i3= i+1;
+ QVERIFY(i!=i3);
+ QCOMPARE(i[1], *i3);
+ (i3= i)++;
+ QCOMPARE((*i3)[0], ps[1][0]);
+ QCOMPARE((*i3).dimension(), 3);
+
+ QVERIFY(i==i);
+ QVERIFY(i!=i3);
+ QVERIFY(i<i3);
+ QVERIFY(i<=i3);
+ QVERIFY(i3>i);
+ QVERIFY(i3>=i);
+
+ QhullPointSet::ConstIterator i4= ps.begin();
+ QVERIFY(i==i4); // iterator COMP const_iterator
+ QVERIFY(i<=i4);
+ QVERIFY(i>=i4);
+ QVERIFY(i4==i); // const_iterator COMP iterator
+ QVERIFY(i4<=i);
+ QVERIFY(i4>=i);
+ QVERIFY(i>=i4);
+ QVERIFY(i4<=i);
+ QVERIFY(i2!=i4);
+ QVERIFY(i2>i4);
+ QVERIFY(i2>=i4);
+ QVERIFY(i4!=i2);
+ QVERIFY(i4<i2);
+ QVERIFY(i4<=i2);
+ ++i4;
+ QVERIFY(i!=i4); // iterator COMP const_iterator
+ QVERIFY(i<i4);
+ QVERIFY(i<=i4);
+ QVERIFY(i4>i);
+ QVERIFY(i4>=i);
+ i4= ps.constBegin();
+ QVERIFY(i==i4); // iterator COMP const_iterator
+ QCOMPARE(i4+ps.count(), ps.constEnd());
+
+ i= ps.begin();
+ i2= ps.begin();
+ QCOMPARE(i, i2++);
+ QCOMPARE(*i2, ps[1]);
+ QCOMPARE(++i, i2);
+ QCOMPARE(i, i2--);
+ QCOMPARE(i2, ps.begin());
+ QCOMPARE(--i, i2);
+ QCOMPARE(i2+=ps.count(), ps.end());
+ QCOMPARE(i2-=ps.count(), ps.begin());
+ QCOMPARE(i2+0, ps.begin());
+ QCOMPARE(i2+ps.count(), ps.end());
+ i2 += ps.count();
+ i= i2-0;
+ QCOMPARE(i, i2);
+ i= i2-ps.count();
+ QCOMPARE(i, ps.begin());
+ QCOMPARE(i2-i, ps.count());
+
+ //ps.begin end tested above
+
+ // QhullPointSet is const-only
+}//t_iterator
+
+void QhullPointSet_test::
+t_const_iterator()
+{
+ RboxPoints rcube("c W0 1000");
+ Qhull q(rcube,"Qc"); // cube with 1000 coplanar points
+ QhullFacet f= q.firstFacet();
+ QhullPointSet ps= f.coplanarPoints();
+ QVERIFY(ps.count()>=3); // Sometimes no coplanar points
+ QhullPointSet::ConstIterator i= ps.begin();
+ QhullPointSet::const_iterator i2= ps.begin();
+ QVERIFY(i==i2);
+ QVERIFY(i>=i2);
+ QVERIFY(i<=i2);
+
+ // See t_iterator for const_iterator COMP iterator
+
+ i= ps.begin();
+ QVERIFY(i==i2);
+ i2= ps.end();
+ QVERIFY(i!=i2);
+ QhullPoint p= *i; // QhullPoint is the base class for QhullPointSet::iterator
+ QCOMPARE(p.dimension(), q.dimension());
+ QCOMPARE(p, ps[0]);
+ i2--;
+ QhullPoint p2= *i2;
+ QCOMPARE(p2.dimension(), q.dimension());
+ QCOMPARE(p2, ps.last());
+ QhullPointSet::ConstIterator i5(i2);
+ QCOMPARE(*i2, *i5);
+
+
+ QhullPointSet::ConstIterator i3= i+1;
+ QVERIFY(i!=i3);
+ QCOMPARE(i[1], *i3);
+
+ QVERIFY(i==i);
+ QVERIFY(i!=i3);
+ QVERIFY(i<i3);
+ QVERIFY(i<=i3);
+ QVERIFY(i3>i);
+ QVERIFY(i3>=i);
+
+ // QhullPointSet is const-only
+}//t_const_iterator
+
+
+void QhullPointSet_test::
+t_search()
+{
+ RboxPoints rcube("c W0 1000");
+ Qhull q(rcube,"Qc"); // cube with 1000 coplanar points
+ QhullFacet f= q.firstFacet();
+ QhullPointSet ps= f.coplanarPoints();
+ QVERIFY(ps.count()>=3); // Sometimes no coplanar points
+ QhullPoint p= ps.first();
+ QhullPoint p2= ps.last();
+ QVERIFY(ps.contains(p));
+ QVERIFY(ps.contains(p2));
+ QVERIFY(p!=p2);
+ QhullPoint p3= ps[2];
+ QVERIFY(ps.contains(p3));
+ QVERIFY(p!=p3);
+ QCOMPARE(ps.indexOf(p), 0);
+ QCOMPARE(ps.indexOf(p2), ps.count()-1);
+ QCOMPARE(ps.indexOf(p3), 2);
+ QhullPoint p4(q);
+ QCOMPARE(ps.indexOf(p4), -1);
+ QCOMPARE(ps.lastIndexOf(p), 0);
+ QCOMPARE(ps.lastIndexOf(p2), ps.count()-1);
+ QCOMPARE(ps.lastIndexOf(p3), 2);
+ QCOMPARE(ps.lastIndexOf(p4), -1);
+}//t_search
+
+void QhullPointSet_test::
+t_pointset_iterator()
+{
+ RboxPoints rcube("c W0 1000");
+ Qhull q(rcube,"Qc"); // cube with 1000 coplanar points
+ QhullFacet f= q.firstFacet();
+ QhullPointSet ps2= f.outsidePoints();
+ QVERIFY(ps2.count()==0); // No outside points after constructing the convex hull
+ QhullPointSetIterator i2= ps2;
+ QCOMPARE(i2.countRemaining(), 0);
+ QVERIFY(!i2.hasNext());
+ QVERIFY(!i2.hasPrevious());
+ i2.toBack();
+ QVERIFY(!i2.hasNext());
+ QVERIFY(!i2.hasPrevious());
+
+ QhullPointSet ps= f.coplanarPoints();
+ QVERIFY(ps.count()>=3); // Sometimes no coplanar points
+ QhullPointSetIterator i(ps);
+ i2= ps;
+ QCOMPARE(i2.countRemaining(), ps.count());
+ QVERIFY(i2.hasNext());
+ QVERIFY(!i2.hasPrevious());
+ QVERIFY(i.hasNext());
+ QVERIFY(!i.hasPrevious());
+ i2.toBack();
+ QCOMPARE(i2.countRemaining(), 0);
+ i.toFront();
+ QCOMPARE(i.countRemaining(), ps.count());
+ QCOMPARE(i2.countRemaining(), 0);
+ QVERIFY(!i2.hasNext());
+ QVERIFY(i2.hasPrevious());
+ QVERIFY(i.hasNext());
+ QVERIFY(!i.hasPrevious());
+
+ QhullPoint p= ps[0];
+ QhullPoint p2(ps[0]);
+ QCOMPARE(p, p2);
+ QVERIFY(p==p2);
+ QhullPoint p3(ps.last());
+ // p2[0]= 0.0;
+ QVERIFY(p==p2);
+ QCOMPARE(i2.peekPrevious(), p3);
+ QCOMPARE(i2.previous(), p3);
+ QCOMPARE(i2.previous(), ps[ps.count()-2]);
+ QVERIFY(i2.hasPrevious());
+ QCOMPARE(i.peekNext(), p);
+ // i.peekNext()= 1.0; // compiler error
+ QCOMPARE(i.next(), p);
+ QCOMPARE(i.countRemaining(), ps.count()-1);
+ QhullPoint p4= i.peekNext();
+ QVERIFY(p4!=p3);
+ QCOMPARE(i.next(), p4);
+ QVERIFY(i.hasNext());
+ i.toFront();
+ QCOMPARE(i.next(), p);
+}//t_pointset_iterator
+
+void QhullPointSet_test::
+t_io()
+{
+ ostringstream os;
+ RboxPoints rcube("c W0 120");
+ Qhull q(rcube,"Qc"); // cube with 100 coplanar points
+ QhullFacet f= q.firstFacet();
+ QhullPointSet ps= f.coplanarPoints();
+ QVERIFY(ps.count()>=3); // Sometimes no coplanar points
+ os << "QhullPointSet from coplanarPoints\n" << ps << endl;
+ os << ps.print("\nWith message\n");
+ os << ps.printIdentifiers("\nCoplanar points: ");
+ os << "\nAs a point set:\n";
+ os << ps;
+ cout << os.str();
+ QString s= QString::fromStdString(os.str());
+ QCOMPARE(s.count(" 0.5\n"), 3*ps.count());
+ QCOMPARE(s.count("p"), ps.count()+4);
+}//t_io
+
+}//orgQhull
+
+#include "moc/QhullPointSet_test.moc"
diff --git a/xs/src/qhull/src/qhulltest/QhullPoint_test.cpp b/xs/src/qhull/src/qhulltest/QhullPoint_test.cpp
new file mode 100644
index 000000000..1528086a1
--- /dev/null
+++ b/xs/src/qhull/src/qhulltest/QhullPoint_test.cpp
@@ -0,0 +1,437 @@
+/****************************************************************************
+**
+** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/qhulltest/QhullPoint_test.cpp#3 $$Change: 2062 $
+** $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $
+**
+****************************************************************************/
+
+//pre-compiled headers
+#include <iostream>
+#include "RoadTest.h" // QT_VERSION
+
+#include "libqhullcpp/QhullPoint.h"
+#include "libqhullcpp/Coordinates.h"
+#include "libqhullcpp/RboxPoints.h"
+#include "libqhullcpp/QhullError.h"
+#include "libqhullcpp/QhullFacet.h"
+#include "libqhullcpp/QhullPoint.h"
+#include "libqhullcpp/Qhull.h"
+
+#include <numeric>
+
+using std::cout;
+using std::endl;
+using std::ostringstream;
+using std::ostream;
+using std::string;
+
+namespace orgQhull {
+
+class QhullPoint_test : public RoadTest
+{
+ Q_OBJECT
+
+#//!\name Test slots
+private slots:
+ void cleanup();
+ void t_construct();
+ void t_convert();
+ void t_readonly();
+ void t_define();
+ void t_operator();
+ void t_iterator();
+ void t_const_iterator();
+ void t_qhullpoint_iterator();
+ void t_method();
+ void t_io();
+};//QhullPoint_test
+
+void
+add_QhullPoint_test()
+{
+ new QhullPoint_test(); // RoadTest::s_testcases
+}
+
+//Executed after each test
+void QhullPoint_test::
+cleanup()
+{
+ RoadTest::cleanup();
+}
+
+void QhullPoint_test::
+t_construct()
+{
+ QhullPoint p12;
+ QVERIFY(!p12.isValid());
+ QCOMPARE(p12.coordinates(), (coordT *)0);
+ QCOMPARE(p12.dimension(), 0);
+ QCOMPARE(p12.qh(), (QhullQh *)0);
+ QCOMPARE(p12.id(), -3);
+ QCOMPARE(p12.begin(), p12.end());
+ QCOMPARE(p12.constBegin(), p12.constEnd());
+
+ RboxPoints rcube("c");
+ Qhull q(rcube, "Qt QR0"); // triangulation of rotated unit cube
+ QhullPoint p(q);
+ QVERIFY(!p.isValid());
+ QCOMPARE(p.dimension(),3);
+ QCOMPARE(p.coordinates(),static_cast<double *>(0));
+ QhullPoint p7(q.qh());
+ QCOMPARE(p, p7);
+
+ // copy constructor and copy assignment
+ QhullVertex v2(q.beginVertex());
+ QhullPoint p2(v2.point());
+ QVERIFY(p2.isValid());
+ QCOMPARE(p2.dimension(),3);
+ QVERIFY(p2!=p12);
+ p= p2;
+ QCOMPARE(p, p2);
+
+ QhullPoint p3(q, p2.dimension(), p2.coordinates());
+ QCOMPARE(p3, p2);
+ QhullPoint p8(q, p2.coordinates()); // Qhull defines dimension
+ QCOMPARE(p8, p2);
+ QhullPoint p9(q.qh(), p2.dimension(), p2.coordinates());
+ QCOMPARE(p9, p2);
+ QhullPoint p10(q.qh(), p2.coordinates()); // Qhull defines dimension
+ QCOMPARE(p10, p2);
+
+ Coordinates c;
+ c << 0.0 << 0.0 << 0.0;
+ QhullPoint p6(q, c);
+ QCOMPARE(p6, q.origin());
+ QhullPoint p11(q.qh(), c);
+ QCOMPARE(p11, q.origin());
+
+ QhullPoint p5= p2; // copy constructor
+ QVERIFY(p5==p2);
+}//t_construct
+
+void QhullPoint_test::
+t_convert()
+{
+ RboxPoints rcube("c");
+ Qhull q(rcube,"Qt QR0"); // triangulation of rotated unit cube
+ QhullVertex v= q.firstVertex();
+ QhullPoint p= v.point();
+ std::vector<double> vs= p.toStdVector();
+ QCOMPARE(vs.size(), 3u);
+ for(int k=3; k--; ){
+ QCOMPARE(vs[k], p[k]);
+ }
+ QList<double> qs= p.toQList();
+ QCOMPARE(qs.size(), 3);
+ for(int k=3; k--; ){
+ QCOMPARE(qs[k], p[k]);
+ }
+}//t_convert
+
+void QhullPoint_test::
+t_readonly()
+{
+ RboxPoints rcube("c");
+ {
+ Qhull q(rcube,"Qt QR0"); // triangulation of rotated unit cube
+ QhullVertexList vs= q.vertexList();
+ cout << "Point ids in 'rbox c'\n";
+ QhullVertexListIterator i(vs);
+ while(i.hasNext()){
+ QhullPoint p= i.next().point();
+ int id= p.id();
+ cout << "p" << id << endl;
+ QVERIFY(p.isValid());
+ QCOMPARE(p.dimension(),3);
+ QCOMPARE(id, p.id());
+ QVERIFY(p.id()>=0 && p.id()<9);
+ const coordT *c= p.coordinates();
+ coordT *c2= p.coordinates();
+ QCOMPARE(c, c2);
+ QCOMPARE(p.dimension(), 3);
+ QCOMPARE(q.qh(), p.qh());
+ }
+ QhullPoint p2= vs.first().point();
+ QhullPoint p3= vs.last().point();
+ QVERIFY(p2!=p3);
+ QVERIFY(p3.coordinates()!=p2.coordinates());
+ }
+}//t_readonly
+
+void QhullPoint_test::
+t_define()
+{
+ RboxPoints rcube("c");
+ {
+ Qhull q(rcube,"Qt QR0"); // triangulation of rotated unit cube
+ QhullVertexList vs= q.vertexList();
+ QhullPoint p= vs.first().point();
+ QhullPoint p2= p;
+ QVERIFY(p==p2);
+ QhullPoint p3= vs.last().point();
+ QVERIFY(p2!=p3);
+ int idx= (p3.coordinates()-p2.coordinates())/p2.dimension();
+ QVERIFY(idx>-8 && idx<8);
+ p2.advancePoint(idx);
+ QVERIFY(p2==p3);
+ p2.advancePoint(-idx);
+ QVERIFY(p2==p);
+ p2.advancePoint(0);
+ QVERIFY(p2==p);
+
+ QhullPoint p4= p3;
+ QVERIFY(p4==p3);
+ p4.defineAs(p2);
+ QVERIFY(p2==p4);
+ QhullPoint p5= p3;
+ p5.defineAs(p2.dimension(), p2.coordinates());
+ QVERIFY(p2==p5);
+ QhullPoint p6= p3;
+ p6.setCoordinates(p2.coordinates());
+ QCOMPARE(p2.coordinates(), p6.coordinates());
+ QVERIFY(p2==p6);
+ p6.setDimension(2);
+ QCOMPARE(p6.dimension(), 2);
+ QVERIFY(p2!=p6);
+ }
+}//t_define
+
+void QhullPoint_test::
+t_operator()
+{
+ RboxPoints rcube("c");
+ Qhull q(rcube,"Qt QR0"); // triangulation of rotated unit cube
+ const QhullPoint p= q.firstVertex().point();
+ //operator== and operator!= tested elsewhere
+ const coordT *c= p.coordinates();
+ for(int k=p.dimension(); k--; ){
+ QCOMPARE(c[k], p[k]);
+ }
+ //p[0]= 10.0; // compiler error, const
+ QhullPoint p2= q.firstVertex().point();
+ p2[0]= 10.0; // Overwrites point coordinate
+ QCOMPARE(p2[0], 10.0);
+}//t_operator
+
+void QhullPoint_test::
+t_iterator()
+{
+ RboxPoints rcube("c");
+ {
+ Qhull q(rcube,"QR0"); // rotated unit cube
+ QhullPoint p2(q);
+ QCOMPARE(p2.begin(), p2.end());
+
+ QhullPoint p= q.firstVertex().point();
+ QhullPoint::Iterator i= p.begin();
+ QhullPoint::iterator i2= p.begin();
+ QVERIFY(i==i2);
+ QVERIFY(i>=i2);
+ QVERIFY(i<=i2);
+ i= p.begin();
+ QVERIFY(i==i2);
+ i2= p.end();
+ QVERIFY(i!=i2);
+ double d3= *i;
+ i2--;
+ double d2= *i2;
+ QCOMPARE(d3, p[0]);
+ QCOMPARE(d2, p[2]);
+ QhullPoint::Iterator i3(i2);
+ QCOMPARE(*i2, *i3);
+
+ (i3= i)++;
+ QCOMPARE((*i3), p[1]);
+ QVERIFY(i==i);
+ QVERIFY(i!=i2);
+ QVERIFY(i<i2);
+ QVERIFY(i<=i2);
+ QVERIFY(i2>i);
+ QVERIFY(i2>=i);
+
+ QhullPoint::ConstIterator i4= p.begin();
+ QVERIFY(i==i4); // iterator COMP const_iterator
+ QVERIFY(i<=i4);
+ QVERIFY(i>=i4);
+ QVERIFY(i4==i); // const_iterator COMP iterator
+ QVERIFY(i4<=i);
+ QVERIFY(i4>=i);
+ QVERIFY(i>=i4);
+ QVERIFY(i4<=i);
+ QVERIFY(i2!=i4);
+ QVERIFY(i2>i4);
+ QVERIFY(i2>=i4);
+ QVERIFY(i4!=i2);
+ QVERIFY(i4<i2);
+ QVERIFY(i4<=i2);
+ ++i4;
+ QVERIFY(i<i4);
+ QVERIFY(i<=i4);
+ QVERIFY(i4>i);
+ QVERIFY(i4>=i);
+
+ i= p.begin();
+ i2= p.begin();
+ QCOMPARE(i, i2++);
+ QCOMPARE(*i2, p[1]);
+ QCOMPARE(++i, i2);
+ QCOMPARE(i, i2--);
+ QCOMPARE(i2, p.begin());
+ QCOMPARE(--i, i2);
+ QCOMPARE(i2 += 3, p.end());
+ QCOMPARE(i2 -= 3, p.begin());
+ QCOMPARE(i2+0, p.begin());
+ QCOMPARE(i2+3, p.end());
+ i2 += 3;
+ i= i2-0;
+ QCOMPARE(i, i2);
+ i= i2-3;
+ QCOMPARE(i, p.begin());
+ QCOMPARE(i2-i, 3);
+
+ //p.begin end tested above
+
+ // QhullPoint is const-only
+ }
+}//t_iterator
+
+void QhullPoint_test::
+t_const_iterator()
+{
+ RboxPoints rcube("c");
+ {
+ Qhull q(rcube,"QR0"); // rotated unit cube
+ QhullPoint p= q.firstVertex().point();
+ QhullPoint::ConstIterator i= p.begin();
+ QhullPoint::const_iterator i2= p.begin();
+ QVERIFY(i==i2);
+ QVERIFY(i>=i2);
+ QVERIFY(i<=i2);
+ i= p.begin();
+ QVERIFY(i==i2);
+ i2= p.end();
+ QVERIFY(i!=i2);
+ double d3= *i;
+ i2--;
+ double d2= *i2;
+ QCOMPARE(d3, p[0]);
+ QCOMPARE(d2, p[2]);
+ QhullPoint::ConstIterator i3(i2);
+ QCOMPARE(*i2, *i3);
+
+ (i3= i)++;
+ QCOMPARE((*i3), p[1]);
+ QVERIFY(i==i);
+ QVERIFY(i!=i2);
+ QVERIFY(i<i2);
+ QVERIFY(i<=i2);
+ QVERIFY(i2>i);
+ QVERIFY(i2>=i);
+
+ // See t_iterator for const_iterator COMP iterator
+
+ i= p.begin();
+ i2= p.constBegin();
+ QCOMPARE(i, i2++);
+ QCOMPARE(*i2, p[1]);
+ QCOMPARE(++i, i2);
+ QCOMPARE(i, i2--);
+ QCOMPARE(i2, p.constBegin());
+ QCOMPARE(--i, i2);
+ QCOMPARE(i2+=3, p.constEnd());
+ QCOMPARE(i2-=3, p.constBegin());
+ QCOMPARE(i2+0, p.constBegin());
+ QCOMPARE(i2+3, p.constEnd());
+ i2 += 3;
+ i= i2-0;
+ QCOMPARE(i, i2);
+ i= i2-3;
+ QCOMPARE(i, p.constBegin());
+ QCOMPARE(i2-i, 3);
+
+ // QhullPoint is const-only
+ }
+}//t_const_iterator
+
+void QhullPoint_test::
+t_qhullpoint_iterator()
+{
+ RboxPoints rcube("c");
+ Qhull q(rcube,"QR0"); // rotated unit cube
+
+ QhullPoint p2(q);
+ QhullPointIterator i= p2;
+ QCOMPARE(p2.dimension(), 3);
+ QVERIFY(!i.hasNext());
+ QVERIFY(!i.hasPrevious());
+ i.toBack();
+ QVERIFY(!i.hasNext());
+ QVERIFY(!i.hasPrevious());
+
+ QhullPoint p = q.firstVertex().point();
+ QhullPointIterator i2(p);
+ QCOMPARE(p.dimension(), 3);
+ i= p;
+ QVERIFY(i2.hasNext());
+ QVERIFY(!i2.hasPrevious());
+ QVERIFY(i.hasNext());
+ QVERIFY(!i.hasPrevious());
+ i2.toBack();
+ i.toFront();
+ QVERIFY(!i2.hasNext());
+ QVERIFY(i2.hasPrevious());
+ QVERIFY(i.hasNext());
+ QVERIFY(!i.hasPrevious());
+
+ // i at front, i2 at end/back, 3 coordinates
+ QCOMPARE(i.peekNext(), p[0]);
+ QCOMPARE(i2.peekPrevious(), p[2]);
+ QCOMPARE(i2.previous(), p[2]);
+ QCOMPARE(i2.previous(), p[1]);
+ QCOMPARE(i2.previous(), p[0]);
+ QVERIFY(!i2.hasPrevious());
+ QCOMPARE(i.peekNext(), p[0]);
+ // i.peekNext()= 1.0; // compiler error, i is const
+ QCOMPARE(i.next(), p[0]);
+ QCOMPARE(i.peekNext(), p[1]);
+ QCOMPARE(i.next(), p[1]);
+ QCOMPARE(i.next(), p[2]);
+ QVERIFY(!i.hasNext());
+ i.toFront();
+ QCOMPARE(i.next(), p[0]);
+}//t_qhullpoint_iterator
+
+void QhullPoint_test::
+t_method()
+{
+ // advancePoint tested above
+ RboxPoints rcube("c");
+ Qhull q(rcube, "");
+ QhullPoint p = q.firstVertex().point();
+ double dist= p.distance(q.origin());
+ QCOMPARE(dist, sqrt(double(2.0+1.0))/2); // half diagonal of unit cube
+}//t_qhullpoint_iterator
+
+void QhullPoint_test::
+t_io()
+{
+ RboxPoints rcube("c");
+ {
+ Qhull q(rcube, "");
+ QhullPoint p= q.beginVertex().point();
+ ostringstream os;
+ os << "Point:\n";
+ os << p;
+ os << "Point w/ print:\n";
+ os << p.print(" message ");
+ os << p.printWithIdentifier(" Point with id and a message ");
+ cout << os.str();
+ QString s= QString::fromStdString(os.str());
+ QCOMPARE(s.count("p"), 2);
+ }
+}//t_io
+
+}//orgQhull
+
+#include "moc/QhullPoint_test.moc"
diff --git a/xs/src/qhull/src/qhulltest/QhullPoints_test.cpp b/xs/src/qhull/src/qhulltest/QhullPoints_test.cpp
new file mode 100644
index 000000000..c2d8347e2
--- /dev/null
+++ b/xs/src/qhull/src/qhulltest/QhullPoints_test.cpp
@@ -0,0 +1,561 @@
+/****************************************************************************
+**
+** Copyright (p) 2009-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/qhulltest/QhullPoints_test.cpp#3 $$Change: 2062 $
+** $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $
+**
+****************************************************************************/
+
+//pre-compiled header
+#include <iostream>
+#include "qhulltest/RoadTest.h" // QT_VERSION
+
+#include "libqhullcpp/QhullPoints.h"
+#include "libqhullcpp/RboxPoints.h"
+#include "libqhullcpp/Qhull.h"
+
+using std::cout;
+using std::endl;
+using std::ostringstream;
+
+namespace orgQhull {
+
+class QhullPoints_test : public RoadTest
+{
+ Q_OBJECT
+
+#//!\name Test slots
+private slots:
+ void cleanup();
+ void t_construct_q();
+ void t_construct_qh();
+ void t_convert();
+ void t_getset();
+ void t_element();
+ void t_iterator();
+ void t_const_iterator();
+ void t_search();
+ void t_points_iterator();
+ void t_io();
+};//QhullPoints_test
+
+void
+add_QhullPoints_test()
+{
+ new QhullPoints_test(); // RoadTest::s_testcases
+}
+
+//Executed after each testcase
+void QhullPoints_test::
+cleanup()
+{
+ RoadTest::cleanup();
+}
+
+void QhullPoints_test::
+t_construct_q()
+{
+ Qhull q;
+ QhullPoints ps(q);
+ QCOMPARE(ps.dimension(), 0);
+ QVERIFY(ps.isEmpty());
+ QCOMPARE(ps.count(), 0);
+ QCOMPARE(ps.size(), 0u);
+ QCOMPARE(ps.coordinateCount(), 0);
+ coordT c[]= {0.0, 1.0, 2.0, 3.0, 4.0, 5.0};
+ QhullPoints ps2(q);
+ ps2.defineAs(2, 6, c);
+ QCOMPARE(ps2.dimension(), 2);
+ QVERIFY(!ps2.isEmpty());
+ QCOMPARE(ps2.count(), 3);
+ QCOMPARE(ps2.size(), 3u);
+ QCOMPARE(ps2.coordinates(), c);
+ QhullPoints ps3(q, 2, 6, c);
+ QCOMPARE(ps3.dimension(), 2);
+ QVERIFY(!ps3.isEmpty());
+ QCOMPARE(ps3.coordinates(), ps2.coordinates());
+ QVERIFY(ps3==ps2);
+ QVERIFY(ps3!=ps);
+ QhullPoints ps4= ps3;
+ QVERIFY(ps4==ps3);
+ // ps4= ps3; //compiler error
+ QhullPoints ps5(ps4);
+ QVERIFY(ps5==ps4);
+ QVERIFY(!(ps5!=ps4));
+ coordT c2[]= {0.0, 1.0, 2.0, 3.0, 4.0, 5.0};
+ QhullPoints ps6(q, 2, 6, c2);
+ QVERIFY(ps6==ps2);
+
+ RboxPoints rbox("c D2");
+ Qhull q2(rbox, "");
+ QhullPoints ps8(q2);
+ QCOMPARE(ps8.dimension(), 2);
+ QCOMPARE(ps8.count(), 0);
+ QCOMPARE(ps8.size(), 0u);
+ QCOMPARE(ps8.coordinateCount(), 0);
+ coordT c3[]= {0.0, 1.0, 2.0, 3.0, 4.0, 5.0};
+ QhullPoints ps9(q2, 6, c3);
+ QCOMPARE(ps9.dimension(), 2);
+ QCOMPARE(ps9.coordinateCount(), 6);
+ QCOMPARE(ps9.count(), 3);
+ QCOMPARE(ps9.coordinates(), c3);
+ QCOMPARE(ps9, ps2); // DISTround
+ c3[1]= 1.0+1e-17;
+ QCOMPARE(ps9, ps2); // DISTround
+ c3[1]= 1.0+1e-15;
+ QVERIFY(ps9!=ps2); // DISTround
+
+ ps9.defineAs(6, c2);
+ QCOMPARE(ps9.dimension(), 2);
+ QCOMPARE(ps9.coordinateCount(), 6);
+ QCOMPARE(ps9.count(), 3);
+ QCOMPARE(ps9.coordinates(), c2);
+}//t_construct_q
+
+void QhullPoints_test::
+t_construct_qh()
+{
+ Qhull q;
+ QhullQh *qh= q.qh();
+ QhullPoints ps(qh);
+ QCOMPARE(ps.dimension(), 0);
+ QVERIFY(ps.isEmpty());
+ QCOMPARE(ps.count(), 0);
+ QCOMPARE(ps.size(), 0u);
+ QCOMPARE(ps.coordinateCount(), 0);
+ coordT c[]= {0.0, 1.0, 2.0, 3.0, 4.0, 5.0};
+ QhullPoints ps2(qh);
+ ps2.defineAs(2, 6, c);
+ QCOMPARE(ps2.dimension(), 2);
+ QVERIFY(!ps2.isEmpty());
+ QCOMPARE(ps2.count(), 3);
+ QCOMPARE(ps2.size(), 3u);
+ QCOMPARE(ps2.coordinates(), c);
+ QhullPoints ps3(qh, 2, 6, c);
+ QCOMPARE(ps3.dimension(), 2);
+ QVERIFY(!ps3.isEmpty());
+ QCOMPARE(ps3.coordinates(), ps2.coordinates());
+ QVERIFY(ps3==ps2);
+ QVERIFY(ps3!=ps);
+ QhullPoints ps4= ps3;
+ QVERIFY(ps4==ps3);
+ // ps4= ps3; //compiler error
+ QhullPoints ps5(ps4);
+ QVERIFY(ps5==ps4);
+ QVERIFY(!(ps5!=ps4));
+ coordT c2[]= {0.0, 1.0, 2.0, 3.0, 4.0, 5.0};
+ QhullPoints ps6(qh, 2, 6, c2);
+ QVERIFY(ps6==ps2);
+
+ RboxPoints rbox("c D2");
+ Qhull q2(rbox, "");
+ QhullPoints ps8(q2.qh());
+ QCOMPARE(ps8.dimension(), 2);
+ QCOMPARE(ps8.count(), 0);
+ QCOMPARE(ps8.size(), 0u);
+ QCOMPARE(ps8.coordinateCount(), 0);
+ coordT c3[]= {10.0, 11.0, 12.0, 13.0, 14.0, 15.0};
+ QhullPoints ps9(q2.qh(), 6, c3);
+ QCOMPARE(ps9.dimension(), 2);
+ QCOMPARE(ps9.coordinateCount(), 6);
+ QCOMPARE(ps9.count(), 3);
+ QCOMPARE(ps9.coordinates(), c3);
+ ps9.defineAs(6, c2);
+ QCOMPARE(ps9.dimension(), 2);
+ QCOMPARE(ps9.coordinateCount(), 6);
+ QCOMPARE(ps9.count(), 3);
+ QCOMPARE(ps9.coordinates(), c2);
+}//t_construct_qh
+
+void QhullPoints_test::
+t_convert()
+{
+ Qhull q;
+ //defineAs tested above
+ coordT c[]= {0.0, 1.0, 2.0, 3.0, 4.0, 5.0};
+ QhullPoints ps(q, 3, 6, c);
+ QCOMPARE(ps.dimension(), 3);
+ QCOMPARE(ps.size(), 2u);
+ const coordT *c2= ps.constData();
+ QCOMPARE(c, c2);
+ const coordT *c3= ps.data();
+ QCOMPARE(c, c3);
+ coordT *c4= ps.data();
+ QCOMPARE(c, c4);
+ std::vector<QhullPoint> vs= ps.toStdVector();
+ QCOMPARE(vs.size(), 2u);
+ QhullPoint p= vs[1];
+ QCOMPARE(p[2], 5.0);
+ QList<QhullPoint> qs= ps.toQList();
+ QCOMPARE(qs.size(), 2);
+ QhullPoint p2= qs[1];
+ QCOMPARE(p2[2], 5.0);
+}//t_convert
+
+void QhullPoints_test::
+t_getset()
+{
+ Qhull q;
+ //See t_construct for coordinates, count, defineAs, dimension, isempty, ==, !=, size
+ coordT c[]= {0.0, 1.0, 2.0, 3.0, 4.0, 5.0};
+ QhullPoints ps(q, 3, 6, c);
+ QhullPoints ps2(q, 3, 6, c);
+ QCOMPARE(ps2.dimension(), 3);
+ QCOMPARE(ps2.coordinates(), c);
+ QCOMPARE(ps2.count(), 2);
+ QCOMPARE(ps2.coordinateCount(), 6);
+ coordT c2[]= {-1.0, -2.0, -3.0, -4.0, -5.0, -6.0};
+ ps2.defineAs(6, c2);
+ QCOMPARE(ps2.coordinates(), c2);
+ QCOMPARE(ps2.count(), 2);
+ QCOMPARE(ps2.size(), 2u);
+ QCOMPARE(ps2.dimension(), 3);
+ QVERIFY(!ps2.isEmpty());
+ QVERIFY(ps!=ps2);
+ // ps2= ps; // assignment not available, compiler error
+ ps2.defineAs(ps);
+ QVERIFY(ps==ps2);
+ ps2.setDimension(2);
+ QCOMPARE(ps2.dimension(), 2);
+ QCOMPARE(ps2.coordinates(), c);
+ QVERIFY(!ps2.isEmpty());
+ QCOMPARE(ps2.count(), 3);
+ QCOMPARE(ps2.size(), 3u);
+ QVERIFY(ps!=ps2);
+ QhullPoints ps3(ps2);
+ ps3.setDimension(3);
+ ps3.defineAs(5, c2);
+ QCOMPARE(ps3.count(), 1);
+ QCOMPARE(ps3.extraCoordinatesCount(), 2);
+ QCOMPARE(ps3.extraCoordinates()[0], -4.0);
+ QVERIFY(ps3.includesCoordinates(ps3.data()));
+ QVERIFY(ps3.includesCoordinates(ps3.data()+ps3.count()-1));
+ QVERIFY(!ps3.includesCoordinates(ps3.data()-1));
+ QVERIFY(!ps3.includesCoordinates(ps3.data()+ps3.coordinateCount()));
+}//t_getset
+
+
+void QhullPoints_test::
+t_element()
+{
+ Qhull q;
+ coordT c[]= {0.0, 1.0, 2.0, 3.0, 4.0, 5.0};
+ QhullPoints ps(q, 2, 6, c);
+ QCOMPARE(ps.count(), 3);
+ QhullPoint p(q, 2, c);
+ QCOMPARE(ps[0], p);
+ QCOMPARE(ps.at(1), ps[1]);
+ QCOMPARE(ps.first(), p);
+ QCOMPARE(ps.front(), ps.first());
+ QCOMPARE(ps.last(), ps.at(2));
+ QCOMPARE(ps.back(), ps.last());
+ QhullPoints ps2= ps.mid(2);
+ QCOMPARE(ps2.count(), 1);
+ QhullPoints ps3= ps.mid(3);
+ QVERIFY(ps3.isEmpty());
+ QhullPoints ps4= ps.mid(10);
+ QVERIFY(ps4.isEmpty());
+ QhullPoints ps5= ps.mid(-1);
+ QVERIFY(ps5.isEmpty());
+ QhullPoints ps6= ps.mid(1, 1);
+ QCOMPARE(ps6.count(), 1);
+ QCOMPARE(ps6[0], ps[1]);
+ QhullPoints ps7= ps.mid(1, 10);
+ QCOMPARE(ps7.count(), 2);
+ QCOMPARE(ps7[1], ps[2]);
+ QhullPoint p8(q);
+ QCOMPARE(ps.value(2), ps[2]);
+ QCOMPARE(ps.value(-1), p8);
+ QCOMPARE(ps.value(3), p8);
+ QCOMPARE(ps.value(3, p), p);
+ QVERIFY(ps.value(1, p)!=p);
+ foreach(QhullPoint p9, ps){ // Qt only
+ QCOMPARE(p9.dimension(), 2);
+ QVERIFY(p9[0]==0.0 || p9[0]==2.0 || p9[0]==4.0);
+ }
+}//t_element
+
+void QhullPoints_test::
+t_iterator()
+{
+ Qhull q;
+ coordT c[]= {0.0, 1.0, 2.0};
+ QhullPoints ps(q, 1, 3, c);
+ QCOMPARE(ps.dimension(), 1);
+ QhullPoints::Iterator i(ps);
+ QhullPoints::iterator i2= ps.begin();
+ QVERIFY(i==i2);
+ QVERIFY(i>=i2);
+ QVERIFY(i<=i2);
+ i= ps.begin();
+ QVERIFY(i==i2);
+ i2= ps.end();
+ QVERIFY(i!=i2);
+ QhullPoint p(i); // QhullPoint is the base class for QhullPoints::iterator
+ QCOMPARE(p.dimension(), ps.dimension());
+ QCOMPARE(p.coordinates(), ps.coordinates());
+ i2--;
+ QhullPoint p2= *i2;
+ QCOMPARE(p[0], 0.0);
+ QCOMPARE(p2[0], 2.0);
+ QhullPoints::Iterator i5(i2);
+ QCOMPARE(*i2, *i5);
+ coordT c3[]= {0.0, -1.0, -2.0};
+ QhullPoints::Iterator i3(q, 1, c3);
+ QVERIFY(i!=i3);
+ QCOMPARE(*i, *i3);
+
+ (i3= i)++;
+ QCOMPARE((*i3)[0], 1.0);
+ QCOMPARE(i3->dimension(), 1);
+ QCOMPARE(i3[0][0], 1.0);
+ QCOMPARE(i3[0], ps[1]);
+
+ QVERIFY(i==i);
+ QVERIFY(i!=i2);
+ QVERIFY(i<i2);
+ QVERIFY(i<=i2);
+ QVERIFY(i2>i);
+ QVERIFY(i2>=i);
+
+ QhullPoints::ConstIterator i4(q, 1, c);
+ QVERIFY(i==i4); // iterator COMP const_iterator
+ QVERIFY(i<=i4);
+ QVERIFY(i>=i4);
+ QVERIFY(i4==i); // const_iterator COMP iterator
+ QVERIFY(i4<=i);
+ QVERIFY(i4>=i);
+ QVERIFY(i>=i4);
+ QVERIFY(i4<=i);
+ QVERIFY(i2!=i4);
+ QVERIFY(i2>i4);
+ QVERIFY(i2>=i4);
+ QVERIFY(i4!=i2);
+ QVERIFY(i4<i2);
+ QVERIFY(i4<=i2);
+ ++i4;
+ QVERIFY(i<i4);
+ QVERIFY(i<=i4);
+ QVERIFY(i4>i);
+ QVERIFY(i4>=i);
+
+ i= ps.begin();
+ i2= ps.begin();
+ QCOMPARE(i, i2++);
+ QCOMPARE(*i2, ps[1]);
+ QCOMPARE(++i, i2);
+ QCOMPARE(i, i2--);
+ QCOMPARE(i2, ps.begin());
+ QCOMPARE(--i, i2);
+ QCOMPARE(i2+=3, ps.end());
+ QCOMPARE(i2-=3, ps.begin());
+ QCOMPARE(i2+0, ps.begin());
+ QCOMPARE(i2+3, ps.end());
+ i2 += 3;
+ i= i2-0;
+ QCOMPARE(i, i2);
+ i= i2-3;
+ QCOMPARE(i, ps.begin());
+ QCOMPARE(i2-i, 3);
+
+ //ps.begin end tested above
+
+ // QhullPoints is const-only
+}//t_iterator
+
+void QhullPoints_test::
+t_const_iterator()
+{
+ Qhull q;
+ coordT c[]= {0.0, 1.0, 2.0};
+ const QhullPoints ps(q, 1, 3, c);
+ QhullPoints::ConstIterator i(ps);
+ QhullPoints::const_iterator i2= ps.begin();
+ QVERIFY(i==i2);
+ QVERIFY(i>=i2);
+ QVERIFY(i<=i2);
+ i= ps.begin();
+ QVERIFY(i==i2);
+ i2= ps.end();
+ QVERIFY(i!=i2);
+ QhullPoint p(i);
+ QCOMPARE(p.dimension(), ps.dimension());
+ QCOMPARE(p.coordinates(), ps.coordinates());
+ i2--;
+ QhullPoint p2= *i2;
+ QCOMPARE(p[0], 0.0);
+ QCOMPARE(p2[0], 2.0);
+ QhullPoints::ConstIterator i5(i2);
+ QCOMPARE(*i2, *i5);
+ coordT c3[]= {0.0, -1.0, -2.0};
+ QhullPoints::ConstIterator i3(q, 1, c3);
+ QVERIFY(i!=i3);
+ QCOMPARE(*i, *i3);
+
+ (i3= i)++;
+ QCOMPARE((*i3)[0], 1.0);
+ QCOMPARE(i3->dimension(), 1);
+ QCOMPARE(i3[0][0], 1.0);
+ QCOMPARE(i3[0][0], 1.0);
+ QCOMPARE(i3[0], ps[1]);
+
+ QVERIFY(i==i);
+ QVERIFY(i!=i2);
+ QVERIFY(i<i2);
+ QVERIFY(i<=i2);
+ QVERIFY(i2>i);
+ QVERIFY(i2>=i);
+
+ // See t_iterator for const_iterator COMP iterator
+
+ i= ps.begin();
+ i2= ps.constBegin();
+ QCOMPARE(i, i2++);
+ QCOMPARE(*i2, ps[1]);
+ QCOMPARE(++i, i2);
+ QCOMPARE(i, i2--);
+ QCOMPARE(i2, ps.constBegin());
+ QCOMPARE(--i, i2);
+ QCOMPARE(i2+=3, ps.constEnd());
+ QCOMPARE(i2-=3, ps.constBegin());
+ QCOMPARE(i2+0, ps.constBegin());
+ QCOMPARE(i2+3, ps.constEnd());
+ i2 += 3;
+ i= i2-0;
+ QCOMPARE(i, i2);
+ i= i2-3;
+ QCOMPARE(i, ps.constBegin());
+ QCOMPARE(i2-i, 3);
+
+ // QhullPoints is const-only
+}//t_const_iterator
+
+
+void QhullPoints_test::
+t_search()
+{
+ Qhull q;
+ coordT c[]= {0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 0, 1};
+ QhullPoints ps(q, 2, 8, c); //2-d array of 4 points
+ QhullPoint p= ps.first();
+ QhullPoint p2= ps.last();
+ QVERIFY(ps.contains(p));
+ QVERIFY(ps.contains(p2));
+ QVERIFY(p==p2);
+ QhullPoint p5= ps[2];
+ QVERIFY(p!=p5);
+ QVERIFY(ps.contains(p5));
+ coordT c2[]= {0.0, 1.0, 2.0, 3.0};
+ QhullPoint p3(q, 2, c2); //2-d point
+ QVERIFY(ps.contains(p3));
+ QhullPoint p4(q, 3, c2); //3-d point
+ QVERIFY(!ps.contains(p4));
+ p4.defineAs(2, c); //2-d point
+ QVERIFY(ps.contains(p4));
+ p4.defineAs(2, c+1); //2-d point
+ QVERIFY(!ps.contains(p4));
+ QhullPoint p6(q, 2, c2+2); //2-d point
+ QCOMPARE(ps.count(p), 2);
+ QCOMPARE(ps.count(p2), 2);
+ QCOMPARE(ps.count(p3), 2);
+ QCOMPARE(ps.count(p4), 0);
+ QCOMPARE(ps.count(p6), 1);
+ QCOMPARE(ps.indexOf(&ps[0][0]), 0);
+ //QCOMPARE(ps.indexOf(ps.end()), -1); //ps.end() is a QhullPoint which may match
+ QCOMPARE(ps.indexOf(0), -1);
+ QCOMPARE(ps.indexOf(&ps[3][0]), 3);
+ QCOMPARE(ps.indexOf(&ps[3][1], QhullError::NOthrow), 3);
+ QCOMPARE(ps.indexOf(ps.data()+ps.coordinateCount(), QhullError::NOthrow), -1);
+ QCOMPARE(ps.indexOf(p), 0);
+ QCOMPARE(ps.indexOf(p2), 0);
+ QCOMPARE(ps.indexOf(p3), 0);
+ QCOMPARE(ps.indexOf(p4), -1);
+ QCOMPARE(ps.indexOf(p5), 2);
+ QCOMPARE(ps.indexOf(p6), 1);
+ QCOMPARE(ps.lastIndexOf(p), 3);
+ QCOMPARE(ps.lastIndexOf(p4), -1);
+ QCOMPARE(ps.lastIndexOf(p6), 1);
+ QhullPoints ps3(q);
+ QCOMPARE(ps3.indexOf(ps3.data()), -1);
+ QCOMPARE(ps3.indexOf(ps3.data()+1, QhullError::NOthrow), -1);
+ QCOMPARE(ps3.indexOf(p), -1);
+ QCOMPARE(ps3.lastIndexOf(p), -1);
+ QhullPoints ps4(q, 2, 0, c);
+ QCOMPARE(ps4.indexOf(p), -1);
+ QCOMPARE(ps4.lastIndexOf(p), -1);
+}//t_search
+
+void QhullPoints_test::
+t_points_iterator()
+{
+ Qhull q;
+ coordT c2[]= {0.0};
+ QhullPoints ps2(q, 0, 0, c2); // 0-dimensional
+ QhullPointsIterator i2= ps2;
+ QVERIFY(!i2.hasNext());
+ QVERIFY(!i2.hasPrevious());
+ i2.toBack();
+ QVERIFY(!i2.hasNext());
+ QVERIFY(!i2.hasPrevious());
+
+ coordT c[]= {0.0, 1.0, 2.0, 3.0, 4.0, 5.0};
+ QhullPoints ps(q, 3, 6, c); // 3-dimensional
+ QhullPointsIterator i(ps);
+ i2= ps;
+ QVERIFY(i2.hasNext());
+ QVERIFY(!i2.hasPrevious());
+ QVERIFY(i.hasNext());
+ QVERIFY(!i.hasPrevious());
+ i2.toBack();
+ i.toFront();
+ QVERIFY(!i2.hasNext());
+ QVERIFY(i2.hasPrevious());
+ QVERIFY(i.hasNext());
+ QVERIFY(!i.hasPrevious());
+
+ QhullPoint p= ps[0];
+ QhullPoint p2(ps[0]);
+ QCOMPARE(p, p2);
+ QVERIFY(p==p2);
+ QhullPoint p3(ps[1]);
+ // p2[0]= 0.0;
+ QVERIFY(p==p2);
+ QCOMPARE(i2.peekPrevious(), p3);
+ QCOMPARE(i2.previous(), p3);
+ QCOMPARE(i2.previous(), p);
+ QVERIFY(!i2.hasPrevious());
+ QCOMPARE(i.peekNext(), p);
+ // i.peekNext()= 1.0; // compiler error
+ QCOMPARE(i.next(), p);
+ QCOMPARE(i.peekNext(), p3);
+ QCOMPARE(i.next(), p3);
+ QVERIFY(!i.hasNext());
+ i.toFront();
+ QCOMPARE(i.next(), p);
+}//t_points_iterator
+
+void QhullPoints_test::
+t_io()
+{
+ Qhull q;
+ QhullPoints ps(q);
+ ostringstream os;
+ os << "Empty QhullPoints\n" << ps << endl;
+ coordT c[]= {0.0, 1.0, 2.0, 3.0, 4.0, 5.0};
+ QhullPoints ps2(q, 3, 6, c); // 3-dimensional explicit
+ os << "QhullPoints from c[]\n" << ps2 << endl;
+
+ RboxPoints rcube("c");
+ Qhull q2(rcube,"Qt QR0"); // triangulation of rotated unit cube
+ QhullPoints ps3= q2.points();
+ os << "QhullPoints\n" << ps3;
+ os << ps3.print("message\n");
+ os << ps3.printWithIdentifier("w/ identifiers\n");
+ cout << os.str();
+ QString s= QString::fromStdString(os.str());
+ QCOMPARE(s.count("p"), 8+1);
+}//t_io
+
+}//orgQhull
+
+#include "moc/QhullPoints_test.moc"
diff --git a/xs/src/qhull/src/qhulltest/QhullRidge_test.cpp b/xs/src/qhull/src/qhulltest/QhullRidge_test.cpp
new file mode 100644
index 000000000..420a7f06d
--- /dev/null
+++ b/xs/src/qhull/src/qhulltest/QhullRidge_test.cpp
@@ -0,0 +1,159 @@
+/****************************************************************************
+**
+** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/qhulltest/QhullRidge_test.cpp#3 $$Change: 2062 $
+** $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $
+**
+****************************************************************************/
+
+//pre-compiled headers
+#include <iostream>
+#include "RoadTest.h" // QT_VERSION
+
+#include "libqhullcpp/QhullRidge.h"
+#include "libqhullcpp/QhullError.h"
+#include "libqhullcpp/RboxPoints.h"
+#include "libqhullcpp/QhullFacet.h"
+#include "libqhullcpp/Qhull.h"
+
+using std::cout;
+using std::endl;
+using std::ostringstream;
+using std::ostream;
+using std::string;
+
+namespace orgQhull {
+
+class QhullRidge_test : public RoadTest
+{
+ Q_OBJECT
+
+#//!\name Test slots
+private slots:
+ void cleanup();
+ void t_construct();
+ void t_getSet();
+ void t_foreach();
+ void t_io();
+};//QhullRidge_test
+
+void
+add_QhullRidge_test()
+{
+ new QhullRidge_test(); // RoadTest::s_testcases
+}
+
+//Executed after each testcase
+void QhullRidge_test::
+cleanup()
+{
+ RoadTest::cleanup();
+}
+
+void QhullRidge_test::
+t_construct()
+{
+ // Qhull.runQhull() constructs QhullFacets as facetT
+ RboxPoints rcube("c");
+ Qhull q(rcube,"QR0"); // triangulation of rotated unit cube
+ QhullRidge r(q);
+ QVERIFY(!r.isValid());
+ QCOMPARE(r.dimension(),2);
+ QhullFacet f(q.firstFacet());
+ QhullRidgeSet rs(f.ridges());
+ QVERIFY(!rs.isEmpty()); // Simplicial facets do not have ridges()
+ QhullRidge r2(rs.first());
+ QCOMPARE(r2.dimension(), 2); // One dimension lower than the facet
+ r= r2;
+ QVERIFY(r.isValid());
+ QCOMPARE(r.dimension(), 2);
+ QhullRidge r3(q, r2.getRidgeT());
+ QCOMPARE(r,r3);
+ QhullRidge r4(q, r2.getBaseT());
+ QCOMPARE(r,r4);
+ QhullRidge r5= r2; // copy constructor
+ QVERIFY(r5==r2);
+ QVERIFY(r5==r);
+}//t_construct
+
+void QhullRidge_test::
+t_getSet()
+{
+ RboxPoints rcube("c");
+ {
+ Qhull q(rcube,"QR0"); // triangulation of rotated unit cube
+ QCOMPARE(q.facetCount(), 6);
+ QCOMPARE(q.vertexCount(), 8);
+ QhullFacet f(q.firstFacet());
+ QhullRidgeSet rs= f.ridges();
+ QhullRidgeSetIterator i(rs);
+ while(i.hasNext()){
+ const QhullRidge r= i.next();
+ cout << r.id() << endl;
+ QVERIFY(r.bottomFacet()!=r.topFacet());
+ QCOMPARE(r.dimension(), 2); // Ridge one-dimension less than facet
+ QVERIFY(r.id()>=0 && r.id()<9*27);
+ QVERIFY(r.isValid());
+ QVERIFY(r==r);
+ QVERIFY(r==i.peekPrevious());
+ QCOMPARE(r.otherFacet(r.bottomFacet()),r.topFacet());
+ QCOMPARE(r.otherFacet(r.topFacet()),r.bottomFacet());
+ }
+ }
+}//t_getSet
+
+void QhullRidge_test::
+t_foreach()
+{
+ RboxPoints rcube("c"); // cube
+ {
+ Qhull q(rcube, "QR0"); // rotated cube
+ QhullFacet f(q.firstFacet());
+ foreach (const QhullRidge &r, f.ridges()){ // Qt only
+ QhullVertexSet vs= r.vertices();
+ QCOMPARE(vs.count(), 2);
+ foreach (const QhullVertex &v, vs){ // Qt only
+ QVERIFY(f.vertices().contains(v));
+ }
+ }
+ QhullRidgeSet rs= f.ridges();
+ QhullRidge r= rs.first();
+ QhullRidge r2= r;
+ QList<QhullVertex> vs;
+ int count= 0;
+ while(!count || r2!=r){
+ ++count;
+ QhullVertex v(q);
+ QVERIFY2(r2.hasNextRidge3d(f),"A cube should only have non-simplicial facets.");
+ QhullRidge r3= r2.nextRidge3d(f, &v);
+ QVERIFY(!vs.contains(v));
+ vs << v;
+ r2= r2.nextRidge3d(f);
+ QCOMPARE(r3, r2);
+ }
+ QCOMPARE(vs.count(), rs.count());
+ QCOMPARE(count, rs.count());
+ }
+}//t_foreach
+
+void QhullRidge_test::
+t_io()
+{
+ RboxPoints rcube("c");
+ {
+ Qhull q(rcube, "");
+ QhullFacet f(q.firstFacet());
+ QhullRidgeSet rs= f.ridges();
+ QhullRidge r= rs.first();
+ ostringstream os;
+ os << "Ridges\n" << rs << "Ridge\n" << r;
+ os << r.print("\nRidge with message");
+ cout << os.str();
+ QString s= QString::fromStdString(os.str());
+ QCOMPARE(s.count(" r"), 6);
+ }
+}//t_io
+
+}//orgQhull
+
+#include "moc/QhullRidge_test.moc"
diff --git a/xs/src/qhull/src/qhulltest/QhullSet_test.cpp b/xs/src/qhull/src/qhulltest/QhullSet_test.cpp
new file mode 100644
index 000000000..87fcf4acf
--- /dev/null
+++ b/xs/src/qhull/src/qhulltest/QhullSet_test.cpp
@@ -0,0 +1,434 @@
+/****************************************************************************
+**
+** Copyright (c) 2009-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/qhulltest/QhullSet_test.cpp#3 $$Change: 2062 $
+** $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $
+**
+****************************************************************************/
+
+//pre-compiled headers
+#include <iostream>
+#include "RoadTest.h" // QT_VERSION
+
+#include "libqhullcpp/QhullRidge.h"
+#include "libqhullcpp/QhullFacetSet.h"
+#include "libqhullcpp/Qhull.h"
+#include "libqhullcpp/RboxPoints.h"
+
+#include <QtCore/QList>
+
+using std::cout;
+using std::endl;
+
+namespace orgQhull {
+
+class QhullSet_test : public RoadTest
+{
+ Q_OBJECT
+
+#//!\name Test slots
+private slots:
+ void cleanup();
+ void t_qhullsetbase();
+ void t_convert();
+ void t_element();
+ void t_search();
+ void t_iterator();
+ void t_const_iterator();
+ void t_qhullset_iterator();
+ void t_io();
+};//QhullSet_test
+
+void
+add_QhullSet_test()
+{
+ new QhullSet_test(); // RoadTest::s_testcases
+}
+
+//Executed after each testcase
+void QhullSet_test::
+cleanup()
+{
+ RoadTest::cleanup();
+}
+
+// Test QhullFacetSet and QhullSet.
+// Use QhullRidgeSet to test methods overloaded by QhullFacetSet
+
+void QhullSet_test::
+t_qhullsetbase()
+{
+ RboxPoints rcube("c");
+ {
+ Qhull q(rcube,"QR0"); // triangulation of rotated unit cube
+ // Fake an empty set. Default constructor not defined. No memory allocation.
+ QhullFacet f4 = q.beginFacet();
+ QhullFacetSet fs = f4.neighborFacets();
+ fs.defineAs(q.qh()->other_points); // Force an empty set
+ QVERIFY(fs.isEmpty());
+ QCOMPARE(fs.count(), 0);
+ QCOMPARE(fs.size(), 0u);
+ QCOMPARE(fs.begin(), fs.end()); // beginPointer(), endPointer()
+ QVERIFY(QhullSetBase::isEmpty(fs.getSetT()));
+
+ QhullRidgeSet rs = f4.ridges();
+ QVERIFY(!rs.isEmpty());
+ QCOMPARE(rs.count(), 4);
+ QCOMPARE(rs.size(), 4u);
+ QVERIFY(rs.begin()!=rs.end());
+ QVERIFY(!QhullSetBase::isEmpty(rs.getSetT()));
+ QhullRidgeSet rs2= rs; // copy constructor
+ // rs= rs2; // disabled. Would not copy ridges
+ QCOMPARE(rs2, rs);
+
+ QCOMPARE(q.facetCount(), 6);
+ QhullFacet f = q.beginFacet();
+ QhullFacetSet fs2 = f.neighborFacets();
+ QCOMPARE(fs2.count(), 4);
+ QCOMPARE(fs2.size(), 4u);
+ QVERIFY(!fs2.isEmpty());
+ QVERIFY(!QhullSetBase::isEmpty(fs2.getSetT()));
+ QVERIFY(fs!=fs2);
+ setT *s= fs2.getSetT();
+ fs.defineAs(s);
+ QVERIFY(fs==fs2);
+ QCOMPARE(fs[1], fs2[1]); // elementPointer
+ QhullFacetSet fs3(fs2);
+ QVERIFY(fs3==fs);
+ // fs= fs2; // disabled. Would not copy facets
+ QhullFacetSet fs4= fs2; // copy constructor
+ QVERIFY(fs4==fs2);
+ }
+}//t_qhullsetbase
+
+// constructors tested by t_qhullsetbase
+
+void QhullSet_test::
+t_convert()
+{
+ RboxPoints rcube("c");
+ {
+ Qhull q(rcube,"QR0"); // rotated unit cube
+ QhullFacet f= q.firstFacet();
+ f= f.next();
+ QhullRidgeSet rs= f.ridges();
+ QCOMPARE(rs.count(),4);
+ std::vector<QhullRidge> rv= rs.toStdVector();
+ QCOMPARE(rv.size(), 4u);
+ QList<QhullRidge> rv2= rs.toQList();
+ QCOMPARE(rv2.size(), 4);
+ std::vector<QhullRidge>::iterator i= rv.begin();
+ foreach(QhullRidge r, rv2){ // Qt only
+ QhullRidge r2= *i++;
+ QCOMPARE(r, r2);
+ }
+
+ Qhull q2(rcube,"Qt QR0"); // triangulation of rotated unit cube
+ QCOMPARE(q2.facetCount(), 12);
+ QhullFacet f2 = q2.beginFacet();
+ QhullFacetSet fs = f2.neighborFacets();
+ QCOMPARE(fs.size(), 3U);
+ std::vector<QhullFacet> vs= fs.toStdVector();
+ QCOMPARE(vs.size(), fs.size());
+ for(int k= fs.count(); k--; ){
+ QCOMPARE(vs[k], fs[k]);
+ }
+ QList<QhullFacet> qv= fs.toQList();
+ QCOMPARE(qv.count(), fs.count());
+ for(int k= fs.count(); k--; ){
+ QCOMPARE(qv[k], fs[k]);
+ }
+ }
+}//t_convert
+
+//ReadOnly (count, isEmpty) tested by t_convert
+// operator== tested by t_search
+
+void QhullSet_test::
+t_element()
+{
+ RboxPoints rcube("c");
+ Qhull q(rcube,"QR0"); // rotated unit cube
+ QhullFacet f = q.beginFacet();
+ QhullFacetSet fs = f.neighborFacets();
+
+ QCOMPARE(fs.at(1), fs[1]);
+ QCOMPARE(fs.first(), fs[0]);
+ QCOMPARE(fs.front(), fs.first());
+ QCOMPARE(fs.last(), fs.at(3));
+ QCOMPARE(fs.back(), fs.last());
+ facetT **d = fs.data();
+ facetT * const *d2= fs.data();
+ facetT * const *d3= fs.constData();
+ QVERIFY(d==d2);
+ QVERIFY(d2==d3);
+ QCOMPARE(QhullFacet(q, *d), fs.first());
+ QhullFacetSet::iterator i(q.qh(), d+4);
+ QCOMPARE(i, fs.end());
+ QCOMPARE(d[4], static_cast<facetT *>(0));
+ QhullFacet f4(q, d[4]);
+ QVERIFY(!f4.isValid());
+ QCOMPARE(fs.second(), fs[1]);
+ const QhullFacet f2= fs.second();
+ QVERIFY(f2==fs[1]);
+ const QhullFacet f3= fs[1];
+ QCOMPARE(f2, f3);
+
+ QCOMPARE(fs.value(2), fs[2]);
+ QCOMPARE(fs.value(-1), QhullFacet());
+ QCOMPARE(fs.value(10), QhullFacet());
+ QCOMPARE(fs.value(2, f), fs[2]);
+ QCOMPARE(fs.value(4, f), f);
+ // mid() not available (read-only)
+}//t_element
+
+void QhullSet_test::
+t_search()
+{
+ RboxPoints rcube("c");
+ Qhull q(rcube,"QR0"); // rotated unit cube
+ QhullFacet f = q.beginFacet();
+ QhullFacetSet fs = f.neighborFacets();
+ QhullFacet f2= *fs.begin();
+ QhullFacet f3= fs.last();
+ QVERIFY(fs.contains(f2));
+ QVERIFY(fs.contains(f3));
+ QVERIFY(!fs.contains(f));
+
+ QhullFacetSet fs2= f2.neighborFacets();
+ QVERIFY(fs==fs);
+ QVERIFY(fs!=fs2);
+ QCOMPARE(fs.count(f2), 1);
+ QCOMPARE(fs.count(f3), 1);
+ QCOMPARE(fs.count(f), 0);
+ QCOMPARE(fs.indexOf(f2), 0);
+ QCOMPARE(fs.indexOf(f3), 3);
+ QCOMPARE(fs.indexOf(f), -1);
+ QCOMPARE(fs.lastIndexOf(f2), 0);
+ QCOMPARE(fs.lastIndexOf(f3), 3);
+ QCOMPARE(fs.lastIndexOf(f), -1);
+}//t_search
+
+void QhullSet_test::
+t_iterator()
+{
+ RboxPoints rcube("c");
+ {
+ Qhull q(rcube,"QR0"); // rotated unit cube
+ QhullFacet f = q.beginFacet();
+ QhullFacetSet fs = f.neighborFacets();
+ QhullFacetSet::Iterator i= fs.begin();
+ QhullFacetSet::iterator i2= fs.begin();
+ QVERIFY(i==i2);
+ QVERIFY(i>=i2);
+ QVERIFY(i<=i2);
+ i= fs.begin();
+ QVERIFY(i==i2);
+ i2= fs.end();
+ QVERIFY(i!=i2);
+ QhullFacet f3(*i);
+ i2--;
+ QhullFacet f2= *i2;
+ QCOMPARE(f3.id(), fs[0].id());
+ QCOMPARE(f2.id(), fs[3].id());
+ QhullFacetSet::Iterator i3(i2);
+ QCOMPARE(*i2, *i3);
+
+ (i3= i)++;
+ QCOMPARE((*i3).id(), fs[1].id());
+ QVERIFY(i==i);
+ QVERIFY(i!=i2);
+ QVERIFY(i<i2);
+ QVERIFY(i<=i2);
+ QVERIFY(i2>i);
+ QVERIFY(i2>=i);
+
+ QhullFacetSet::ConstIterator i4= fs.begin();
+ QVERIFY(i==i4); // iterator COMP const_iterator
+ QVERIFY(i<=i4);
+ QVERIFY(i>=i4);
+ QVERIFY(i4==i); // const_iterator COMP iterator
+ QVERIFY(i4<=i);
+ QVERIFY(i4>=i);
+ QVERIFY(i>=i4);
+ QVERIFY(i4<=i);
+ QVERIFY(i2!=i4);
+ QVERIFY(i2>i4);
+ QVERIFY(i2>=i4);
+ QVERIFY(i4!=i2);
+ QVERIFY(i4<i2);
+ QVERIFY(i4<=i2);
+ ++i4;
+ QVERIFY(i<i4);
+ QVERIFY(i<=i4);
+ QVERIFY(i4>i);
+ QVERIFY(i4>=i);
+
+ i= fs.begin();
+ i2= fs.begin();
+ QCOMPARE(i, i2++);
+ QCOMPARE(*i2, fs[1]);
+ QCOMPARE(++i, i2);
+ QCOMPARE(i, i2--);
+ QCOMPARE(i2, fs.begin());
+ QCOMPARE(--i, i2);
+ QCOMPARE(i2 += 4, fs.end());
+ QCOMPARE(i2 -= 4, fs.begin());
+ QCOMPARE(i2+0, fs.begin());
+ QCOMPARE(i2+4, fs.end());
+ i2 += 4;
+ i= i2-0;
+ QCOMPARE(i, i2);
+ i= i2-4;
+ QCOMPARE(i, fs.begin());
+ QCOMPARE(i2-i, 4);
+
+ //fs.begin end tested above
+
+ // QhullFacetSet is const-only
+ }
+}//t_iterator
+
+void QhullSet_test::
+t_const_iterator()
+{
+ RboxPoints rcube("c");
+ {
+ Qhull q(rcube,"QR0"); // rotated unit cube
+ QhullFacet f = q.beginFacet();
+ QhullFacetSet fs = f.neighborFacets();
+ QhullFacetSet::ConstIterator i= fs.begin();
+ QhullFacetSet::const_iterator i2= fs.begin();
+ QVERIFY(i==i2);
+ QVERIFY(i>=i2);
+ QVERIFY(i<=i2);
+ i= fs.begin();
+ QVERIFY(i==i2);
+ i2= fs.end();
+ QVERIFY(i!=i2);
+ QhullFacet f3(*i);
+ i2--;
+ QhullFacet f2= *i2;
+ QCOMPARE(f3.id(), fs[0].id());
+ QCOMPARE(f2.id(), fs[3].id());
+ QhullFacetSet::ConstIterator i3(i2);
+ QCOMPARE(*i2, *i3);
+
+ (i3= i)++;
+ QCOMPARE((*i3).id(), fs[1].id());
+ QVERIFY(i==i);
+ QVERIFY(i!=i2);
+ QVERIFY(i<i2);
+ QVERIFY(i<=i2);
+ QVERIFY(i2>i);
+ QVERIFY(i2>=i);
+
+ // See t_iterator for const_iterator COMP iterator
+
+ i= fs.begin();
+ i2= fs.constBegin();
+ QCOMPARE(i, i2++);
+ QCOMPARE(*i2, fs[1]);
+ QCOMPARE(++i, i2);
+ QCOMPARE(i, i2--);
+ QCOMPARE(i2, fs.constBegin());
+ QCOMPARE(--i, i2);
+ QCOMPARE(i2+=4, fs.constEnd());
+ QCOMPARE(i2-=4, fs.constBegin());
+ QCOMPARE(i2+0, fs.constBegin());
+ QCOMPARE(i2+4, fs.constEnd());
+ i2 += 4;
+ i= i2-0;
+ QCOMPARE(i, i2);
+ i= i2-4;
+ QCOMPARE(i, fs.constBegin());
+ QCOMPARE(i2-i, 4);
+
+ // QhullFacetSet is const-only
+ }
+}//t_const_iterator
+
+void QhullSet_test::
+t_qhullset_iterator()
+{
+ RboxPoints rcube("c");
+ Qhull q(rcube,"QR0"); // rotated unit cube
+ // Fake an empty set. Default constructor not defined. No memory allocation.
+ QhullFacet f = q.beginFacet();
+ QhullFacetSet fs = f.neighborFacets();
+ fs.defineAs(q.qh()->other_points);
+ QhullFacetSetIterator i(fs);
+ QCOMPARE(fs.count(), 0);
+ QVERIFY(!i.hasNext());
+ QVERIFY(!i.hasPrevious());
+ i.toBack();
+ QVERIFY(!i.hasNext());
+ QVERIFY(!i.hasPrevious());
+
+ QhullFacet f2 = q.beginFacet();
+ QhullFacetSet fs2 = f2.neighborFacets();
+ QhullFacetSetIterator i2(fs2);
+ QCOMPARE(fs2.count(), 4);
+ i= fs2;
+ QVERIFY(i2.hasNext());
+ QVERIFY(!i2.hasPrevious());
+ QVERIFY(i.hasNext());
+ QVERIFY(!i.hasPrevious());
+ i2.toBack();
+ i.toFront();
+ QVERIFY(!i2.hasNext());
+ QVERIFY(i2.hasPrevious());
+ QVERIFY(i.hasNext());
+ QVERIFY(!i.hasPrevious());
+
+ // i at front, i2 at end/back, 4 neighbors
+ QhullFacetSet fs3 = f2.neighborFacets(); // same as fs2
+ QhullFacet f3(fs2[0]);
+ QhullFacet f4= fs3[0];
+ QCOMPARE(f3, f4);
+ QVERIFY(f3==f4);
+ QhullFacet f5(fs3[1]);
+ QVERIFY(f4!=f5);
+ QhullFacet f6(fs3[2]);
+ QhullFacet f7(fs3[3]);
+ QCOMPARE(i2.peekPrevious(), f7);
+ QCOMPARE(i2.previous(), f7);
+ QCOMPARE(i2.previous(), f6);
+ QCOMPARE(i2.previous(), f5);
+ QCOMPARE(i2.previous(), f4);
+ QVERIFY(!i2.hasPrevious());
+ QCOMPARE(i.peekNext(), f4);
+ // i.peekNext()= 1.0; // compiler error
+ QCOMPARE(i.next(), f4);
+ QCOMPARE(i.peekNext(), f5);
+ QCOMPARE(i.next(), f5);
+ QCOMPARE(i.next(), f6);
+ QCOMPARE(i.next(), f7);
+ QVERIFY(!i.hasNext());
+ i.toFront();
+ QCOMPARE(i.next(), f4);
+}//t_qhullset_iterator
+
+void QhullSet_test::
+t_io()
+{
+ RboxPoints rcube("c");
+ Qhull q(rcube,"QR0"); // rotated unit cube
+ // Fake an empty set. Default constructor not defined. No memory allocation.
+ QhullFacet f= q.beginFacet();
+ QhullFacetSet fs= f.neighborFacets();
+ fs.defineAs(q.qh()->other_points);
+ cout << "INFO: empty set" << fs << std::endl;
+ QhullFacet f2= q.beginFacet();
+ QhullFacetSet fs2= f2.neighborFacets();
+ cout << "INFO: Neighboring facets\n";
+ cout << fs2 << std::endl;
+
+ QhullRidgeSet rs= f.ridges();
+ cout << "INFO: Ridges for a facet\n";
+ cout << rs << std::endl;
+}//t_io
+
+}//namespace orgQhull
+
+#include "moc/QhullSet_test.moc"
diff --git a/xs/src/qhull/src/qhulltest/QhullVertexSet_test.cpp b/xs/src/qhull/src/qhulltest/QhullVertexSet_test.cpp
new file mode 100644
index 000000000..41caacd29
--- /dev/null
+++ b/xs/src/qhull/src/qhulltest/QhullVertexSet_test.cpp
@@ -0,0 +1,152 @@
+/****************************************************************************
+**
+** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/qhulltest/QhullVertexSet_test.cpp#3 $$Change: 2062 $
+** $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $
+**
+****************************************************************************/
+
+//pre-compiled headers
+#include <iostream>
+#include "qhulltest/RoadTest.h" // QT_VERSION
+
+#include "libqhullcpp/QhullVertexSet.h"
+#include "libqhullcpp/QhullVertex.h"
+#include "libqhullcpp/Qhull.h"
+#include "libqhullcpp/QhullError.h"
+#include "libqhullcpp/QhullFacet.h"
+#include "libqhullcpp/RboxPoints.h"
+
+using std::cout;
+using std::endl;
+using std::ostringstream;
+using std::ostream;
+using std::string;
+
+namespace orgQhull {
+
+class QhullVertexSet_test : public RoadTest
+{
+ Q_OBJECT
+
+#//!\name Test slots
+private slots:
+ void cleanup();
+ void t_construct();
+ void t_convert();
+ void t_readonly();
+ void t_foreach();
+ void t_io();
+};//QhullVertexSet_test
+
+void
+add_QhullVertexSet_test()
+{
+ new QhullVertexSet_test(); // RoadTest::s_testcases
+}
+
+//Executed after each testcase
+void QhullVertexSet_test::
+cleanup()
+{
+ RoadTest::cleanup();
+}
+
+void QhullVertexSet_test::
+t_construct()
+{
+ RboxPoints rcube("c");
+ Qhull q(rcube,"QR0"); // rotated unit cube
+ cout << "INFO : Cube rotated by QR" << q.rotateRandom() << std::endl;
+ QhullFacet f= q.firstFacet();
+ QhullVertexSet vs= f.vertices();
+ QVERIFY(!vs.isEmpty());
+ QCOMPARE(vs.count(),4);
+ QhullVertexSet vs4= vs; // copy constructor
+ QVERIFY(vs4==vs);
+ QhullVertexSet vs3(q, q.qh()->del_vertices);
+ QVERIFY(vs3.isEmpty());
+}//t_construct
+
+void QhullVertexSet_test::
+t_convert()
+{
+ RboxPoints rcube("c");
+ Qhull q(rcube,"QR0 QV2"); // rotated unit cube with "good" facets adjacent to point 0
+ cout << "INFO : Cube rotated by QR" << q.rotateRandom() << std::endl;
+ QhullFacet f= q.firstFacet();
+ QhullVertexSet vs2= f.vertices();
+ QCOMPARE(vs2.count(),4);
+ std::vector<QhullVertex> fv= vs2.toStdVector();
+ QCOMPARE(fv.size(), 4u);
+ QList<QhullVertex> fv2= vs2.toQList();
+ QCOMPARE(fv2.size(), 4);
+ std::vector<QhullVertex> fv3= vs2.toStdVector();
+ QCOMPARE(fv3.size(), 4u);
+ QList<QhullVertex> fv4= vs2.toQList();
+ QCOMPARE(fv4.size(), 4);
+}//t_convert
+
+//! Spot check properties and read-only. See QhullSet_test
+void QhullVertexSet_test::
+t_readonly()
+{
+ RboxPoints rcube("c");
+ Qhull q(rcube,"QV0"); // good facets are adjacent to point 0
+ QhullVertexSet vs= q.firstFacet().vertices();
+ QCOMPARE(vs.count(), 4);
+ QCOMPARE(vs.count(), 4);
+ QhullVertex v= vs.first();
+ QhullVertex v2= vs.last();
+ QVERIFY(vs.contains(v));
+ QVERIFY(vs.contains(v2));
+}//t_readonly
+
+void QhullVertexSet_test::
+t_foreach()
+{
+ RboxPoints rcube("c");
+ // Spot check predicates and accessors. See QhullLinkedList_test
+ Qhull q(rcube,"QR0"); // rotated unit cube
+ cout << "INFO : Cube rotated by QR" << q.rotateRandom() << std::endl;
+ QhullVertexSet vs= q.firstFacet().vertices();
+ QVERIFY(vs.contains(vs.first()));
+ QVERIFY(vs.contains(vs.last()));
+ QCOMPARE(vs.first(), *vs.begin());
+ QCOMPARE(*(vs.end()-1), vs.last());
+}//t_foreach
+
+void QhullVertexSet_test::
+t_io()
+{
+ RboxPoints rcube("c");
+ {
+ Qhull q(rcube,"QR0 QV0"); // good facets are adjacent to point 0
+ cout << "INFO : Cube rotated by QR" << q.rotateRandom() << std::endl;
+ QhullVertexSet vs= q.firstFacet().vertices();
+ ostringstream os;
+ os << vs.print("Vertices of first facet with point 0");
+ os << vs.printIdentifiers("\nVertex identifiers: ");
+ cout<< os.str();
+ QString vertices= QString::fromStdString(os.str());
+ QCOMPARE(vertices.count(QRegExp(" v[0-9]")), 4);
+ }
+}//t_io
+
+#ifdef QHULL_USES_QT
+QList<QhullVertex> QhullVertexSet::
+toQList() const
+{
+ QhullSetIterator<QhullVertex> i(*this);
+ QList<QhullVertex> vs;
+ while(i.hasNext()){
+ QhullVertex v= i.next();
+ vs.append(v);
+ }
+ return vs;
+}//toQList
+#endif //QHULL_USES_QT
+
+}//orgQhull
+
+#include "moc/QhullVertexSet_test.moc"
diff --git a/xs/src/qhull/src/qhulltest/QhullVertex_test.cpp b/xs/src/qhull/src/qhulltest/QhullVertex_test.cpp
new file mode 100644
index 000000000..fb6ec9640
--- /dev/null
+++ b/xs/src/qhull/src/qhulltest/QhullVertex_test.cpp
@@ -0,0 +1,184 @@
+/****************************************************************************
+**
+** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/qhulltest/QhullVertex_test.cpp#3 $$Change: 2062 $
+** $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $
+**
+****************************************************************************/
+
+//pre-compiled headers
+#include <iostream>
+#include "RoadTest.h" // QT_VERSION
+
+#include "libqhullcpp/QhullVertex.h"
+#include "libqhullcpp/Coordinates.h"
+#include "libqhullcpp/QhullError.h"
+#include "libqhullcpp/RboxPoints.h"
+#include "libqhullcpp/QhullFacet.h"
+#include "libqhullcpp/QhullFacetSet.h"
+#include "libqhullcpp/QhullVertexSet.h"
+#include "libqhullcpp/Qhull.h"
+
+using std::cout;
+using std::endl;
+using std::ostringstream;
+using std::ostream;
+using std::string;
+
+namespace orgQhull {
+
+class QhullVertex_test : public RoadTest
+{
+ Q_OBJECT
+
+#//!\name Test slots
+private slots:
+ void cleanup();
+ void t_constructConvert();
+ void t_getSet();
+ void t_foreach();
+ void t_io();
+};//QhullVertex_test
+
+void
+add_QhullVertex_test()
+{
+ new QhullVertex_test(); // RoadTest::s_testcases
+}
+
+//Executed after each testcase
+void QhullVertex_test::
+cleanup()
+{
+ RoadTest::cleanup();
+}
+
+void QhullVertex_test::
+t_constructConvert()
+{
+ QhullVertex v6;
+ QVERIFY(!v6.isValid());
+ QCOMPARE(v6.dimension(),0);
+ // Qhull.runQhull() constructs QhullFacets as facetT
+ RboxPoints rcube("c");
+ Qhull q(rcube,"Qt QR0"); // triangulation of rotated unit cube
+ QhullVertex v(q);
+ QVERIFY(!v.isValid());
+ QCOMPARE(v.dimension(),3);
+ QhullVertex v2(q.beginVertex());
+ QCOMPARE(v2.dimension(),3);
+ v= v2; // copy assignment
+ QVERIFY(v.isValid());
+ QCOMPARE(v.dimension(),3);
+ QhullVertex v5= v2; // copy constructor
+ QVERIFY(v5==v2);
+ QVERIFY(v5==v);
+ QhullVertex v3(q, v2.getVertexT());
+ QCOMPARE(v,v3);
+ QhullVertex v4(q, v2.getBaseT());
+ QCOMPARE(v,v4);
+}//t_constructConvert
+
+void QhullVertex_test::
+t_getSet()
+{
+ RboxPoints rcube("c");
+ {
+ Qhull q(rcube,"Qt QR0"); // triangulation of rotated unit cube
+ QCOMPARE(q.facetCount(), 12);
+ QCOMPARE(q.vertexCount(), 8);
+
+ // Also spot-test QhullVertexList. See QhullLinkedList_test.cpp
+ QhullVertexList vs= q.vertexList();
+ QhullVertexListIterator i(vs);
+ while(i.hasNext()){
+ const QhullVertex v= i.next();
+ cout << v.id() << endl;
+ QCOMPARE(v.dimension(),3);
+ QVERIFY(v.id()>=0 && v.id()<9);
+ QVERIFY(v.isValid());
+ if(i.hasNext()){
+ QCOMPARE(v.next(), i.peekNext());
+ QVERIFY(v.next()!=v);
+ QVERIFY(v.next().previous()==v);
+ }
+ QVERIFY(i.hasPrevious());
+ QCOMPARE(v, i.peekPrevious());
+ }
+
+ // test point()
+ foreach (QhullVertex v, q.vertexList()){ // Qt only
+ QhullPoint p= v.point();
+ int j= p.id();
+ cout << "Point " << j << ":\n" << p << endl;
+ QVERIFY(j>=0 && j<8);
+ }
+ }
+}//t_getSet
+
+void QhullVertex_test::
+t_foreach()
+{
+ RboxPoints rcube("c W0 300"); // 300 points on surface of cube
+ {
+ Qhull q(rcube, "QR0 Qc"); // keep coplanars, thick facet, and rotate the cube
+ foreach (QhullVertex v, q.vertexList()){ // Qt only
+ QhullFacetSet fs= v.neighborFacets();
+ QCOMPARE(fs.count(), 3);
+ foreach (QhullFacet f, fs){ // Qt only
+ QVERIFY(f.vertices().contains(v));
+ }
+ }
+ }
+}//t_foreach
+
+void QhullVertex_test::
+t_io()
+{
+ RboxPoints rcube("c");
+ {
+ Qhull q(rcube, "");
+ QhullVertex v= q.beginVertex();
+ ostringstream os;
+ os << "Vertex and vertices:\n";
+ os << v;
+ QhullVertexSet vs= q.firstFacet().vertices();
+ os << vs;
+ os << "\nVertex and vertices with message:\n";
+ os << v.print("Vertex");
+ os << vs.print("\nVertices:");
+ cout << os.str();
+ QString s= QString::fromStdString(os.str());
+ QCOMPARE(s.count("(v"), 10);
+ QCOMPARE(s.count(": f"), 2);
+ }
+ RboxPoints r10("10 D3"); // Without QhullVertex::facetNeighbors
+ {
+ Qhull q(r10, "");
+ QhullVertex v= q.beginVertex();
+ ostringstream os;
+ os << "\nTry again with simplicial facets. No neighboring facets listed for vertices.\n";
+ os << "Vertex and vertices:\n";
+ os << v;
+ q.defineVertexNeighborFacets();
+ os << "This time with neighborFacets() defined for all vertices:\n";
+ os << v;
+ cout << os.str();
+ QString s= QString::fromStdString(os.str());
+ QCOMPARE(s.count(": f"), 1);
+
+ Qhull q2(r10, "v"); // Voronoi diagram
+ QhullVertex v2= q2.beginVertex();
+ ostringstream os2;
+ os2 << "\nTry again with Voronoi diagram of simplicial facets. Neighboring facets automatically defined for vertices.\n";
+ os2 << "Vertex and vertices:\n";
+ os2 << v2;
+ cout << os2.str();
+ QString s2= QString::fromStdString(os2.str());
+ QCOMPARE(s2.count(": f"), 1);
+ }
+}//t_io
+
+}//orgQhull
+
+#include "moc/QhullVertex_test.moc"
diff --git a/xs/src/qhull/src/qhulltest/Qhull_test.cpp b/xs/src/qhull/src/qhulltest/Qhull_test.cpp
new file mode 100644
index 000000000..cc3918a05
--- /dev/null
+++ b/xs/src/qhull/src/qhulltest/Qhull_test.cpp
@@ -0,0 +1,360 @@
+/****************************************************************************
+**
+** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/qhulltest/Qhull_test.cpp#4 $$Change: 2062 $
+** $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $
+**
+****************************************************************************/
+
+//pre-compiled headers
+#include <iostream>
+#include "qhulltest/RoadTest.h" // QT_VERSION
+
+#include "libqhullcpp/Qhull.h"
+#include "libqhullcpp/QhullError.h"
+#include "libqhullcpp/RboxPoints.h"
+#include "libqhullcpp/QhullFacetList.h"
+
+using std::cout;
+using std::endl;
+using std::string;
+
+namespace orgQhull {
+
+//! Test C++ interface to Qhull
+//! See eg/q_test for tests of Qhull commands
+class Qhull_test : public RoadTest
+{
+ Q_OBJECT
+
+#//!\name Test slots
+private slots:
+ void cleanup();
+ void t_construct();
+ void t_attribute();
+ void t_message();
+ void t_getSet();
+ void t_getQh();
+ void t_getValue();
+ void t_foreach();
+ void t_modify();
+};//Qhull_test
+
+void
+add_Qhull_test()
+{
+ new Qhull_test(); // RoadTest::s_testcases
+}
+
+//Executed after each testcase
+void Qhull_test::
+cleanup()
+{
+ RoadTest::cleanup();
+}
+
+void Qhull_test::
+t_construct()
+{
+ {
+ Qhull q;
+ QCOMPARE(q.dimension(),0);
+ QVERIFY(q.qh()!=0);
+ QCOMPARE(QString(q.qhullCommand()),QString(""));
+ QCOMPARE(QString(q.rboxCommand()),QString(""));
+ try{
+ QCOMPARE(q.area(),0.0);
+ QFAIL("area() did not fail.");
+ }catch (const std::exception &e) {
+ cout << "INFO : Caught " << e.what();
+ }
+ }
+ {
+ RboxPoints rbox("10000");
+ Qhull q(rbox, "QR0"); // Random points in a randomly rotated cube.
+ QCOMPARE(q.dimension(),3);
+ QVERIFY(q.volume() < 1.0);
+ QVERIFY(q.volume() > 0.99);
+ }
+ {
+ double points[] = {
+ 0, 0,
+ 1, 0,
+ 1, 1
+ };
+ Qhull q("triangle", 2, 3, points, "");
+ QCOMPARE(q.dimension(),2);
+ QCOMPARE(q.facetCount(),3);
+ QCOMPARE(q.vertexCount(),3);
+ QCOMPARE(q.dimension(),2);
+ QCOMPARE(q.area(), 2.0+sqrt(2.0)); // length of boundary
+ QCOMPARE(q.volume(), 0.5); // the 2-d area
+ }
+}//t_construct
+
+void Qhull_test::
+t_attribute()
+{
+ RboxPoints rcube("c");
+ {
+ double normals[] = {
+ 0, -1, -0.5,
+ -1, 0, -0.5,
+ 1, 0, -0.5,
+ 0, 1, -0.5
+ };
+ Qhull q;
+ Coordinates feasible;
+ feasible << 0.0 << 0.0;
+ q.setFeasiblePoint(feasible);
+ Coordinates c(std::vector<double>(2, 0.0));
+ QVERIFY(q.feasiblePoint()==c);
+ q.setOutputStream(&cout);
+ q.runQhull("normals of square", 3, 4, normals, "H"); // halfspace intersect
+ QVERIFY(q.feasiblePoint()==c); // from qh.feasible_point after runQhull()
+ QCOMPARE(q.facetList().count(), 4); // Vertices of square
+ cout << "Expecting summary of halfspace intersection\n";
+ q.outputQhull();
+ q.qh()->disableOutputStream(); // Same as q.disableOutputStream()
+ cout << "Expecting no output from qh_fprintf() in Qhull.cpp\n";
+ q.outputQhull();
+ }
+}//t_attribute
+
+//! No QhullMessage for errors outside of qhull
+void Qhull_test::
+t_message()
+{
+ RboxPoints rcube("c");
+ {
+ Qhull q;
+ QCOMPARE(q.qhullMessage(), string(""));
+ QCOMPARE(q.qhullStatus(), qh_ERRnone);
+ QVERIFY(!q.hasQhullMessage());
+ try{
+ q.runQhull(rcube, "Fd");
+ QFAIL("runQhull Fd did not fail.");
+ }catch (const std::exception &e) {
+ const char *s= e.what();
+ cout << "INFO : Caught " << s;
+ QCOMPARE(QString::fromStdString(s).left(6), QString("QH6029"));
+ // FIXUP QH11025 -- review decision to clearQhullMessage at QhullError() // Cleared when copied to QhullError
+ QVERIFY(!q.hasQhullMessage());
+ // QCOMPARE(q.qhullMessage(), QString::fromStdString(s).remove(0, 7));
+ // QCOMPARE(q.qhullStatus(), 6029);
+ q.clearQhullMessage();
+ QVERIFY(!q.hasQhullMessage());
+ }
+ q.appendQhullMessage("Append 1");
+ QVERIFY(q.hasQhullMessage());
+ QCOMPARE(QString::fromStdString(q.qhullMessage()), QString("Append 1"));
+ q.appendQhullMessage("\nAppend 2\n");
+ QCOMPARE(QString::fromStdString(q.qhullMessage()), QString("Append 1\nAppend 2\n"));
+ q.clearQhullMessage();
+ QVERIFY(!q.hasQhullMessage());
+ QCOMPARE(QString::fromStdString(q.qhullMessage()), QString(""));
+ }
+ {
+ cout << "INFO : Error stream without output stream\n";
+ Qhull q;
+ q.setErrorStream(&cout);
+ q.setOutputStream(0);
+ try{
+ q.runQhull(rcube, "Fd");
+ QFAIL("runQhull Fd did not fail.");
+ }catch (const QhullError &e) {
+ cout << "INFO : Caught " << e;
+ QCOMPARE(e.errorCode(), 6029);
+ }
+ //FIXUP QH11025 Qhullmessage cleared when QhullError thrown. Switched to e
+ //QVERIFY(q.hasQhullMessage());
+ //QCOMPARE(QString::fromStdString(q.qhullMessage()).left(6), QString("QH6029"));
+ q.clearQhullMessage();
+ QVERIFY(!q.hasQhullMessage());
+ }
+ {
+ cout << "INFO : Error output sent to output stream without error stream\n";
+ Qhull q;
+ q.setErrorStream(0);
+ q.setOutputStream(&cout);
+ try{
+ q.runQhull(rcube, "Tz H0");
+ QFAIL("runQhull TZ did not fail.");
+ }catch (const std::exception &e) {
+ const char *s= e.what();
+ cout << "INFO : Caught " << s;
+ QCOMPARE(QString::fromLatin1(s).left(6), QString("QH6023"));
+ }
+ //FIXUP QH11025 Qhullmessage cleared when QhullError thrown. Switched to e
+ //QVERIFY(q.hasQhullMessage());
+ //QCOMPARE(QString::fromStdString(q.qhullMessage()).left(17), QString("qhull: no message"));
+ //QCOMPARE(q.qhullStatus(), 6023);
+ q.clearQhullMessage();
+ QVERIFY(!q.hasQhullMessage());
+ }
+ {
+ cout << "INFO : No error stream or output stream\n";
+ Qhull q;
+ q.setErrorStream(0);
+ q.setOutputStream(0);
+ try{
+ q.runQhull(rcube, "Fd");
+ QFAIL("outputQhull did not fail.");
+ }catch (const std::exception &e) {
+ const char *s= e.what();
+ cout << "INFO : Caught " << s;
+ QCOMPARE(QString::fromLatin1(s).left(6), QString("QH6029"));
+ }
+ //FIXUP QH11025 Qhullmessage cleared when QhullError thrown. Switched to e
+ //QVERIFY(q.hasQhullMessage());
+ //QCOMPARE(QString::fromStdString(q.qhullMessage()).left(9), QString("qhull err"));
+ //QCOMPARE(q.qhullStatus(), 6029);
+ q.clearQhullMessage();
+ QVERIFY(!q.hasQhullMessage());
+ }
+}//t_message
+
+void Qhull_test::
+t_getSet()
+{
+ RboxPoints rcube("c");
+ {
+ Qhull q;
+ QVERIFY(!q.initialized());
+ q.runQhull(rcube, "s");
+ QVERIFY(q.initialized());
+ QCOMPARE(q.dimension(), 3);
+ QhullPoint p= q.origin();
+ QCOMPARE(p.dimension(), 3);
+ QCOMPARE(p[0]+p[1]+p[2], 0.0);
+ q.setErrorStream(&cout);
+ q.outputQhull();
+ }
+ {
+ Qhull q;
+ q.runQhull(rcube, "");
+ q.setOutputStream(&cout);
+ q.outputQhull();
+ }
+}//t_getSet
+
+void Qhull_test::
+t_getQh()
+{
+ RboxPoints rcube("c");
+ {
+ Qhull q;
+ q.runQhull(rcube, "s");
+ QCOMPARE(QString(q.qhullCommand()), QString("qhull s"));
+ QCOMPARE(QString(q.rboxCommand()), QString("rbox \"c\""));
+ QCOMPARE(q.facetCount(), 6);
+ QCOMPARE(q.vertexCount(), 8);
+ // Sample fields from Qhull's qhT [libqhull.h]
+ QCOMPARE(q.qh()->ALLpoints, 0u);
+ QCOMPARE(q.qh()->GOODpoint, 0);
+ QCOMPARE(q.qh()->IStracing, 0);
+ QCOMPARE(q.qh()->MAXcoplanar+1.0, 1.0); // fuzzy compare
+ QCOMPARE(q.qh()->MERGING, 1u);
+ QCOMPARE(q.qh()->input_dim, 3);
+ QCOMPARE(QString(q.qh()->qhull_options).left(8), QString(" run-id"));
+ QCOMPARE(q.qh()->num_facets, 6);
+ QCOMPARE(q.qh()->hasTriangulation, 0u);
+ QCOMPARE(q.qh()->max_outside - q.qh()->min_vertex + 1.0, 1.0); // fuzzy compare
+ QCOMPARE(*q.qh()->gm_matrix+1.0, 1.0); // fuzzy compare
+ }
+}//t_getQh
+
+void Qhull_test::
+t_getValue()
+{
+ RboxPoints rcube("c");
+ {
+ Qhull q;
+ q.runQhull(rcube, "");
+ QCOMPARE(q.area(), 6.0);
+ QCOMPARE(q.volume(), 1.0);
+ }
+}//t_getValue
+
+void Qhull_test::
+t_foreach()
+{
+ RboxPoints rcube("c");
+ {
+ Qhull q;
+ QCOMPARE(q.beginFacet(),q.endFacet());
+ QCOMPARE(q.beginVertex(),q.endVertex());
+ q.runQhull(rcube, "");
+ QCOMPARE(q.facetList().count(), 6);
+
+ // defineVertexNeighborFacets() tested in QhullVertex_test::t_io()
+
+ QhullFacetList facets(q.beginFacet(), q.endFacet());
+ QCOMPARE(facets.count(), 6);
+ QCOMPARE(q.firstFacet(), q.beginFacet());
+ QhullVertexList vertices(q.beginVertex(), q.endVertex());
+ QCOMPARE(vertices.count(), 8);
+ QCOMPARE(q.firstVertex(), q.beginVertex());
+ QhullPoints ps= q.points();
+ QCOMPARE(ps.count(), 8);
+ QhullPointSet ps2= q.otherPoints();
+ QCOMPARE(ps2.count(), 0);
+ // ps2= q.otherPoints(); //disabled, would not copy the points
+ QCOMPARE(q.facetCount(), 6);
+ QCOMPARE(q.vertexCount(), 8);
+ coordT *c= q.pointCoordinateBegin(); // of q.points()
+ QVERIFY(*c==0.5 || *c==-0.5);
+ coordT *c3= q.pointCoordinateEnd();
+ QVERIFY(c3[-1]==0.5 || c3[-1]==-0.5);
+ QCOMPARE(c3-c, 8*3);
+ QCOMPARE(q.vertexList().count(), 8);
+ }
+}//t_foreach
+
+void Qhull_test::
+t_modify()
+{
+ //addPoint() tested in t_foreach
+ RboxPoints diamond("d");
+ Qhull q(diamond, "o");
+ q.setOutputStream(&cout);
+ cout << "Expecting vertexList and facetList of a 3-d diamond.\n";
+ q.outputQhull();
+ cout << "Expecting normals of a 3-d diamond.\n";
+ q.outputQhull("n");
+ // runQhull tested in t_attribute(), t_message(), etc.
+}//t_modify
+
+}//orgQhull
+
+// Redefine Qhull's usermem_r.c in order to report erroneous calls to qh_exit
+void qh_exit(int exitcode) {
+ cout << "FAIL! : Qhull called qh_exit(). Qhull's error handling not available.\n.. See the corresponding Qhull:qhull_message or setErrorStream().\n";
+ exit(exitcode);
+}
+void qh_fprintf_stderr(int msgcode, const char *fmt, ... ) {
+ va_list args;
+
+ va_start(args, fmt);
+ if(msgcode)
+ fprintf(stderr, "QH%.4d ", msgcode);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+} /* fprintf_stderr */
+void qh_free(void *mem) {
+ free(mem);
+}
+void *qh_malloc(size_t size) {
+ return malloc(size);
+}
+
+#if 0
+template<> char * QTest::
+toString(const std::string &s)
+{
+ QByteArray ba = s.c_str();
+ return qstrdup(ba.data());
+}
+#endif
+
+#include "moc/Qhull_test.moc"
diff --git a/xs/src/qhull/src/qhulltest/RboxPoints_test.cpp b/xs/src/qhull/src/qhulltest/RboxPoints_test.cpp
new file mode 100644
index 000000000..4f4ea984f
--- /dev/null
+++ b/xs/src/qhull/src/qhulltest/RboxPoints_test.cpp
@@ -0,0 +1,215 @@
+/****************************************************************************
+**
+** Copyright (c) 2006-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/qhulltest/RboxPoints_test.cpp#2 $$Change: 2062 $
+** $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $
+**
+****************************************************************************/
+
+//pre-compiled headers
+#include <iostream>
+#include "RoadTest.h" // QT_VERSION
+
+#include "libqhullcpp/RboxPoints.h"
+#include "libqhullcpp/QhullError.h"
+
+using std::cout;
+using std::endl;
+using std::ostringstream;
+using std::string;
+using std::stringstream;
+
+namespace orgQhull {
+
+//! Test C++ interface to Rbox
+//! See eg/q_test for tests of rbox commands
+class RboxPoints_test : public RoadTest
+{
+ Q_OBJECT
+
+#//!\name Test slots
+private slots:
+ void t_construct();
+ void t_error();
+ void t_test();
+ void t_getSet();
+ void t_foreach();
+ void t_change();
+ void t_ostream();
+};
+
+void
+add_RboxPoints_test()
+{
+ new RboxPoints_test(); // RoadTest::s_testcases
+}
+
+void RboxPoints_test::
+t_construct()
+{
+ RboxPoints rp;
+ QCOMPARE(rp.dimension(), 0);
+ QCOMPARE(rp.count(), 0);
+ QVERIFY(QString::fromStdString(rp.comment()) != QString(""));
+ QVERIFY(rp.isEmpty());
+ QVERIFY(!rp.hasRboxMessage());
+ QCOMPARE(rp.rboxStatus(), qh_ERRnone);
+ QCOMPARE(QString::fromStdString(rp.rboxMessage()), QString("rbox warning: no points generated\n"));
+
+ RboxPoints rp2("c"); // 3-d cube
+ QCOMPARE(rp2.dimension(), 3);
+ QCOMPARE(rp2.count(), 8);
+ QCOMPARE(QString::fromStdString(rp2.comment()), QString("rbox \"c\""));
+ QVERIFY(!rp2.isEmpty());
+ QVERIFY(!rp2.hasRboxMessage());
+ QCOMPARE(rp2.rboxStatus(), qh_ERRnone);
+ QCOMPARE(QString::fromStdString(rp2.rboxMessage()), QString("rbox: OK\n"));
+}//t_construct
+
+void RboxPoints_test::
+t_error()
+{
+ RboxPoints rp;
+ try{
+ rp.appendPoints("D0 c");
+ QFAIL("'D0 c' did not fail.");
+ }catch (const std::exception &e) {
+ const char *s= e.what();
+ cout << "INFO : Caught " << s;
+ QCOMPARE(QString(s).left(6), QString("QH6189"));
+ QVERIFY(rp.hasRboxMessage());
+ QCOMPARE(QString::fromStdString(rp.rboxMessage()).left(8), QString("rbox err"));
+ QCOMPARE(rp.rboxStatus(), 6189);
+ rp.clearRboxMessage();
+ QVERIFY(!rp.hasRboxMessage());
+ }
+ try{
+ RboxPoints rp2;
+ rp2.setDimension(-1);
+ QFAIL("setDimension(-1) did not fail.");
+ }catch (const RoadError &e) {
+ const char *s= e.what();
+ cout << "INFO : Caught " << s;
+ QCOMPARE(QString(s).left(7), QString("QH10062"));
+ QCOMPARE(e.errorCode(), 10062);
+ QCOMPARE(QString::fromStdString(e.what()), QString(s));
+ RoadLogEvent logEvent= e.roadLogEvent();
+ QCOMPARE(logEvent.int1(), -1);
+ }
+}//t_error
+
+void RboxPoints_test::
+t_test()
+{
+ // isEmpty -- t_construct
+}//t_test
+
+void RboxPoints_test::
+t_getSet()
+{
+ // comment -- t_construct
+ // count -- t_construct
+ // dimension -- t_construct
+
+ RboxPoints rp;
+ QCOMPARE(rp.dimension(), 0);
+ rp.setDimension(2);
+ QCOMPARE(rp.dimension(), 2);
+ try{
+ rp.setDimension(102);
+ QFAIL("setDimension(102) did not fail.");
+ }catch (const std::exception &e) {
+ cout << "INFO : Caught " << e.what();
+ }
+ QCOMPARE(rp.newCount(), 0);
+ rp.appendPoints("D2 P1 P2");
+ QCOMPARE(rp.count(), 2);
+ QCOMPARE(rp.newCount(), 2); // From previous appendPoints();
+ PointCoordinates pc(rp.qh(), 2, "Test qh() and <<");
+ pc << 1.0 << 0.0 << 2.0 << 0.0;
+ QCOMPARE(pc.dimension(), 2);
+ QCOMPARE(pc.count(), 2);
+ QVERIFY(rp==pc);
+ rp.setNewCount(10); // Normally only used by appendPoints for rbox processing
+ QCOMPARE(rp.newCount(), 10);
+ rp.reservePoints();
+ QVERIFY(rp==pc);
+}//t_getSet
+
+void RboxPoints_test::
+t_foreach()
+{
+ RboxPoints rp("c");
+ Coordinates::ConstIterator cci= rp.beginCoordinates();
+ orgQhull::Coordinates::Iterator ci= rp.beginCoordinates();
+ QCOMPARE(*cci, -0.5);
+ QCOMPARE(*ci, *cci);
+ int i=1;
+ while(++cci<rp.endCoordinates()){
+ QVERIFY(++ci<rp.endCoordinates());
+ QCOMPARE(*cci, *ci);
+ i++;
+ }
+ QVERIFY(++ci==rp.endCoordinates());
+ QCOMPARE(i, 8*3);
+ orgQhull::Coordinates::Iterator ci4= rp.beginCoordinates(4);
+ QCOMPARE(rp.endCoordinates()-ci4, 4*3);
+ orgQhull::Coordinates::ConstIterator cci4= rp.beginCoordinates(4);
+ orgQhull::Coordinates::ConstIterator cci5= rp.endCoordinates();
+ QCOMPARE(cci5-cci4, 4*3);
+}//t_foreach
+
+void RboxPoints_test::
+t_change()
+{
+ RboxPoints rp("c D2");
+ stringstream s;
+ s << "4 count" << endl;
+ s << "2 dimension" << endl;
+ s << "1 2 3 4 5 6 7 8" << endl;
+ rp.appendPoints(s);
+ QCOMPARE(rp.count(), 8);
+ orgQhull::Coordinates::Iterator ci= rp.beginCoordinates(7);
+ QCOMPARE(*ci, 7.0);
+ try{
+ stringstream s2;
+ s2 << "4 count" << endl;
+ s2 << "2 dimension" << endl;
+ s2 << "1 2 3 4 5 6 7 " << endl;
+ rp.appendPoints(s2);
+ QFAIL("incomplete appendPoints() did not fail.");
+ }catch (const std::exception &e) {
+ cout << "INFO : Caught " << e.what();
+ }
+ RboxPoints rp2;
+ rp2.append(rp);
+ QCOMPARE(rp2.count(), 8);
+ orgQhull::Coordinates::ConstIterator cci2= rp2.beginCoordinates(6);
+ QCOMPARE(*(cci2+1), 6.0);
+ rp2.appendPoints("D2 10 P0");
+ QCOMPARE(rp2.count(), 19);
+ orgQhull::Coordinates::ConstIterator cie= rp2.beginCoordinates(8);
+ QCOMPARE(*cie, 0.0);
+ RboxPoints rp3;
+ coordT points[] = { 0, 1,1,0,1,1,0,0};
+ rp3.setDimension(2);
+ rp3.append(8,points);
+ QCOMPARE(rp3.count(), 4);
+ orgQhull::Coordinates::Iterator ci3= rp3.beginCoordinates(3);
+ QCOMPARE(*ci3, 0.0);
+}//t_change
+
+void RboxPoints_test::
+t_ostream()
+{
+ RboxPoints rp("c D2");
+ ostringstream oss;
+ oss << rp;
+ string s= oss.str();
+ QString qs= QString::fromStdString(s);
+ QCOMPARE(qs.count("-0.5"), 4);
+}//t_ostream
+
+}//orgQhull
+
+#include "moc/RboxPoints_test.moc"
diff --git a/xs/src/qhull/src/qhulltest/RoadTest.cpp b/xs/src/qhull/src/qhulltest/RoadTest.cpp
new file mode 100644
index 000000000..05c87f3de
--- /dev/null
+++ b/xs/src/qhull/src/qhulltest/RoadTest.cpp
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/qhulltest/RoadTest.cpp#2 $$Change: 2062 $
+** $Date: 2016/01/17 $$Author: bbarber $
+**
+****************************************************************************/
+
+//pre-compiled headers
+#include <iostream>
+#include "RoadTest.h" // QT_VERSION
+
+#include <stdexcept>
+
+using std::cout;
+using std::endl;
+
+namespace orgQhull {
+
+#//!\name class variable
+
+QList<RoadTest*> RoadTest::
+s_testcases;
+
+int RoadTest::
+s_test_count= 0;
+
+int RoadTest::
+s_test_fail= 0;
+
+QStringList RoadTest::
+s_failed_tests;
+
+#//!\name Slot
+
+//! Executed after each test
+void RoadTest::
+cleanup()
+{
+ s_test_count++;
+ if(QTest::currentTestFailed()){
+ recordFailedTest();
+ }
+}//cleanup
+
+#//!\name Helper
+
+void RoadTest::
+recordFailedTest()
+{
+ s_test_fail++;
+ QString className= metaObject()->className();
+ s_failed_tests << className + "::" + QTest::currentTestFunction();
+}
+
+#//!\name class function
+
+void RoadTest::
+deleteTests()
+{
+ foreach(RoadTest *testcase, s_testcases){
+ delete testcase;
+ }
+ s_failed_tests.clear();
+}
+
+int RoadTest::
+runTests(QStringList arguments)
+{
+ int result= 0; // assume success
+
+ foreach(RoadTest *testcase, s_testcases){
+ try{
+ result += QTest::qExec(testcase, arguments);
+ }catch(const std::exception &e){
+ cout << "FAIL! : Threw error ";
+ cout << e.what() << endl;
+ s_test_count++;
+ testcase->recordFailedTest();
+ // Qt 4.5.2 OK. In Qt 4.3.3, qtestcase did not clear currentTestObject
+ }
+ }
+ if(s_test_fail){
+ cout << "Failed " << s_test_fail << " of " << s_test_count << " tests.\n";
+ cout << s_failed_tests.join("\n").toLocal8Bit().constData() << std::endl;
+ }else{
+ cout << "Passed " << s_test_count << " tests.\n";
+ }
+ return result;
+}//runTests
+
+}//orgQhull
+
+#include "moc/moc_RoadTest.cpp"
diff --git a/xs/src/qhull/src/qhulltest/RoadTest.h b/xs/src/qhull/src/qhulltest/RoadTest.h
new file mode 100644
index 000000000..adfe0bf8c
--- /dev/null
+++ b/xs/src/qhull/src/qhulltest/RoadTest.h
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/qhulltest/RoadTest.h#2 $$Change: 2062 $
+** $Date: 2016/01/17 $$Author: bbarber $
+**
+****************************************************************************/
+
+#ifndef ROADTEST_H
+#define ROADTEST_H
+
+//pre-compiled with RoadTest.h
+#include <QObject> // Qt C++ Framework
+#include <QtTest/QtTest>
+
+#define QHULL_USES_QT 1
+
+namespace orgQhull {
+
+#//!\name Defined here
+
+ //! RoadTest -- Generic test for Qt's QTest
+ class RoadTest;
+ //! TESTadd_(t) -- Add a RoadTest
+
+/** Test Name objects using Qt's QTestLib
+
+Template:
+
+class Name_test : public RoadTest
+{
+ Q_OBJECT
+#//!\name Test slot
+private slots:
+ void t_name();
+ //Executed before any test
+ void initTestCase();
+ void init(); // Each test
+ //Executed after each test
+ void cleanup(); //RoadTest::cleanup();
+ // Executed after last test
+ void cleanupTestCase();
+};
+
+void
+add_Name_test()
+{
+ new Name_test(); // RoadTest::s_testcases
+}
+
+Send additional output to cout
+*/
+
+class RoadTest : public QObject
+{
+ Q_OBJECT
+
+#//!\name Class globals
+protected:
+ static QList<RoadTest *>
+ s_testcases; ///! List of testcases to execute. Initialized via add_...()
+ static int s_test_count; ///! Total number of tests executed
+ static int s_test_fail; ///! Number of failed tests
+ static QStringList s_failed_tests; ///! List of failed tests
+
+#//!\name Test slots
+public slots:
+ void cleanup();
+
+public:
+#//!\name Constructors, etc.
+ RoadTest() { s_testcases.append(this); }
+ virtual ~RoadTest() {} // Derived from QObject
+
+#//!\name Helper
+ void recordFailedTest();
+
+
+#//!\name Class functions
+ static void deleteTests();
+ static int runTests(QStringList arguments);
+
+};//RoadTest
+
+#define TESTadd_(t) extern void t(); t();
+
+
+}//orgQhull
+
+namespace QTest{
+
+template<>
+inline char *
+toString(const std::string &s)
+{
+ return qstrdup(s.c_str());
+}
+
+}//namespace QTest
+
+#endif //ROADTEST_H
+
diff --git a/xs/src/qhull/src/qhulltest/qhulltest.cpp b/xs/src/qhull/src/qhulltest/qhulltest.cpp
new file mode 100644
index 000000000..5bfe16e9c
--- /dev/null
+++ b/xs/src/qhull/src/qhulltest/qhulltest.cpp
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
+** $Id: //main/2015/qhull/src/qhulltest/qhulltest.cpp#5 $$Change: 2079 $
+** $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
+**
+****************************************************************************/
+
+//pre-compiled headers
+#include "libqhull_r/user_r.h"
+
+#include <iostream>
+#include "RoadTest.h" // QT_VERSION
+
+#include "libqhullcpp/RoadError.h"
+#include "libqhull_r/qhull_ra.h"
+
+#include <sstream>
+#include <stdexcept>
+#include <string>
+
+using std::cout;
+using std::endl;
+
+namespace orgQhull {
+
+void addQhullTests(QStringList &args)
+{
+ TESTadd_(add_Qhull_test);
+
+ if(args.contains("--all")){
+ args.removeAll("--all");
+ // up-to-date
+ TESTadd_(add_Coordinates_test);
+ TESTadd_(add_PointCoordinates_test);
+ TESTadd_(add_QhullFacet_test);
+ TESTadd_(add_QhullFacetList_test);
+ TESTadd_(add_QhullFacetSet_test);
+ TESTadd_(add_QhullHyperplane_test);
+ TESTadd_(add_QhullLinkedList_test);
+ TESTadd_(add_QhullPoint_test);
+ TESTadd_(add_QhullPoints_test);
+ TESTadd_(add_QhullPointSet_test);
+ TESTadd_(add_QhullRidge_test);
+ TESTadd_(add_QhullSet_test);
+ TESTadd_(add_QhullVertex_test);
+ TESTadd_(add_QhullVertexSet_test);
+ TESTadd_(add_RboxPoints_test);
+ // qhullStat
+ TESTadd_(add_Qhull_test);
+ }//--all
+}//addQhullTests
+
+int main(int argc, char *argv[])
+{
+
+ QCoreApplication app(argc, argv);
+ QStringList args= app.arguments();
+ bool isAll= args.contains("--all");
+
+ QHULL_LIB_CHECK /* Check for compatible library */
+
+ addQhullTests(args);
+ int status=1010;
+ try{
+ status= RoadTest::runTests(args);
+ }catch(const std::exception &e){
+ cout << "FAIL! : runTests() did not catch error\n";
+ cout << e.what() << endl;
+ if(!RoadError::emptyGlobalLog()){
+ cout << RoadError::stringGlobalLog() << endl;
+ RoadError::clearGlobalLog();
+ }
+ }
+ if(!RoadError::emptyGlobalLog()){
+ cout << RoadError::stringGlobalLog() << endl;
+ RoadError::clearGlobalLog();
+ }
+ if(isAll){
+ cout << "Finished test of libqhullcpp. Test libqhull_r with eg/q_test after building libqhull_r/Makefile" << endl;
+ }else{
+ cout << "Finished test of one class. Test all classes with 'qhulltest --all'" << endl;
+ }
+ RoadTest::deleteTests();
+ return status;
+}
+
+}//orgQhull
+
+int main(int argc, char *argv[])
+{
+ return orgQhull::main(argc, argv); // Needs RoadTest:: for TESTadd_() linkage
+}
+
diff --git a/xs/src/qhull/src/qhulltest/qhulltest.pro b/xs/src/qhull/src/qhulltest/qhulltest.pro
new file mode 100644
index 000000000..0da34d375
--- /dev/null
+++ b/xs/src/qhull/src/qhulltest/qhulltest.pro
@@ -0,0 +1,36 @@
+# -------------------------------------------------
+# qhulltest.pro -- Qt project for qhulltest.exe (QTestLib)
+# cd $qh/build/qhulltest && qmake -tp vc -r ../../src/qhulltest/qhulltest.pro
+# -------------------------------------------------
+
+include(../qhull-app-cpp.pri)
+
+TARGET = qhulltest
+QT += testlib
+MOC_DIR = moc
+INCLUDEPATH += .. # for MOC_DIR
+
+PRECOMPILED_HEADER = RoadTest.h
+
+HEADERS += RoadTest.h
+
+SOURCES += ../libqhullcpp/qt-qhull.cpp
+SOURCES += Coordinates_test.cpp
+SOURCES += PointCoordinates_test.cpp
+SOURCES += Qhull_test.cpp
+SOURCES += QhullFacet_test.cpp
+SOURCES += QhullFacetList_test.cpp
+SOURCES += QhullFacetSet_test.cpp
+SOURCES += QhullHyperplane_test.cpp
+SOURCES += QhullLinkedList_test.cpp
+SOURCES += QhullPoint_test.cpp
+SOURCES += QhullPoints_test.cpp
+SOURCES += QhullPointSet_test.cpp
+SOURCES += QhullRidge_test.cpp
+SOURCES += QhullSet_test.cpp
+SOURCES += qhulltest.cpp
+SOURCES += QhullVertex_test.cpp
+SOURCES += QhullVertexSet_test.cpp
+SOURCES += RboxPoints_test.cpp
+SOURCES += RoadTest.cpp
+
diff --git a/xs/src/qhull/src/qvoronoi/qvoronoi.c b/xs/src/qhull/src/qvoronoi/qvoronoi.c
new file mode 100644
index 000000000..b93d23711
--- /dev/null
+++ b/xs/src/qhull/src/qvoronoi/qvoronoi.c
@@ -0,0 +1,303 @@
+/*<html><pre> -<a href="../libqhull/qh-qhull.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ qvoronoi.c
+ compute Voronoi diagrams and furthest-point Voronoi
+ diagrams using qhull
+
+ see unix.c for full interface
+
+ Copyright (c) 1993-2015, The Geometry Center
+*/
+
+#include "libqhull/libqhull.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+
+#if __cplusplus
+extern "C" {
+ int isatty(int);
+}
+
+#elif _MSC_VER
+#include <io.h>
+#define isatty _isatty
+/* int _isatty(int); */
+
+#else
+int isatty(int); /* returns 1 if stdin is a tty
+ if "Undefined symbol" this can be deleted along with call in main() */
+#endif
+
+/*-<a href="../libqhull/qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="prompt">-</a>
+
+ qh_prompt
+ long prompt for qhull
+
+ notes:
+ restricted version of libqhull.c
+
+ see:
+ concise prompt below
+*/
+
+/* duplicated in qvoron_f.htm and qvoronoi.htm
+ QJ and Qt are deprecated, but allowed for backwards compatibility
+*/
+char hidden_options[]=" d n m v H U Qb QB Qc Qf Qg Qi Qm Qr QR Qv Qx TR E V Fa FA FC Fp FS Ft FV Pv Gt Q0 Q1 Q2 Q3 Q4 Q5 Q6 Q7 Q8 Q9 ";
+
+char qh_prompta[]= "\n\
+qvoronoi- compute the Voronoi diagram\n\
+ http://www.qhull.org %s\n\
+\n\
+input (stdin):\n\
+ first lines: dimension and number of points (or vice-versa).\n\
+ other lines: point coordinates, best if one point per line\n\
+ comments: start with a non-numeric character\n\
+\n\
+options:\n\
+ Qu - compute furthest-site Voronoi diagram\n\
+\n\
+Qhull control options:\n\
+ Qz - add point-at-infinity to Voronoi diagram\n\
+%s%s%s%s"; /* split up qh_prompt for Visual C++ */
+char qh_promptb[]= "\
+ Qs - search all points for the initial simplex\n\
+ QGn - Voronoi vertices if visible from point n, -n if not\n\
+ QVn - Voronoi vertices for input point n, -n if not\n\
+\n\
+";
+char qh_promptc[]= "\
+Trace options:\n\
+ T4 - trace at level n, 4=all, 5=mem/gauss, -1= events\n\
+ Tc - check frequently during execution\n\
+ Ts - statistics\n\
+ Tv - verify result: structure, convexity, and in-circle test\n\
+ Tz - send all output to stdout\n\
+ TFn - report summary when n or more facets created\n\
+ TI file - input data from file, no spaces or single quotes\n\
+ TO file - output results to file, may be enclosed in single quotes\n\
+ TPn - turn on tracing when point n added to hull\n\
+ TMn - turn on tracing at merge n\n\
+ TWn - trace merge facets when width > n\n\
+ TVn - stop qhull after adding point n, -n for before (see TCn)\n\
+ TCn - stop qhull after building cone for point n (see TVn)\n\
+\n\
+Precision options:\n\
+ Cn - radius of centrum (roundoff added). Merge facets if non-convex\n\
+ An - cosine of maximum angle. Merge facets if cosine > n or non-convex\n\
+ C-0 roundoff, A-0.99/C-0.01 pre-merge, A0.99/C0.01 post-merge\n\
+ Rn - randomly perturb computations by a factor of [1-n,1+n]\n\
+ Wn - min facet width for non-coincident point (before roundoff)\n\
+\n\
+Output formats (may be combined; if none, produces a summary to stdout):\n\
+ s - summary to stderr\n\
+ p - Voronoi vertices\n\
+ o - OFF format (dim, Voronoi vertices, and Voronoi regions)\n\
+ i - Delaunay regions (use 'Pp' to avoid warning)\n\
+ f - facet dump\n\
+\n\
+";
+char qh_promptd[]= "\
+More formats:\n\
+ Fc - count plus coincident points (by Voronoi vertex)\n\
+ Fd - use cdd format for input (homogeneous with offset first)\n\
+ FD - use cdd format for output (offset first)\n\
+ FF - facet dump without ridges\n\
+ Fi - separating hyperplanes for bounded Voronoi regions\n\
+ FI - ID for each Voronoi vertex\n\
+ Fm - merge count for each Voronoi vertex (511 max)\n\
+ Fn - count plus neighboring Voronoi vertices for each Voronoi vertex\n\
+ FN - count and Voronoi vertices for each Voronoi region\n\
+ Fo - separating hyperplanes for unbounded Voronoi regions\n\
+ FO - options and precision constants\n\
+ FP - nearest point and distance for each coincident point\n\
+ FQ - command used for qvoronoi\n\
+ Fs - summary: #int (8), dimension, #points, tot vertices, tot facets,\n\
+ for output: #Voronoi regions, #Voronoi vertices,\n\
+ #coincident points, #non-simplicial regions\n\
+ #real (2), max outer plane and min vertex\n\
+ Fv - Voronoi diagram as Voronoi vertices between adjacent input sites\n\
+ Fx - extreme points of Delaunay triangulation (on convex hull)\n\
+\n\
+";
+char qh_prompte[]= "\
+Geomview options (2-d only)\n\
+ Ga - all points as dots\n\
+ Gp - coplanar points and vertices as radii\n\
+ Gv - vertices as spheres\n\
+ Gi - inner planes only\n\
+ Gn - no planes\n\
+ Go - outer planes only\n\
+ Gc - centrums\n\
+ Gh - hyperplane intersections\n\
+ Gr - ridges\n\
+ GDn - drop dimension n in 3-d and 4-d output\n\
+\n\
+Print options:\n\
+ PAn - keep n largest Voronoi vertices by 'area'\n\
+ Pdk:n - drop facet if normal[k] <= n (default 0.0)\n\
+ PDk:n - drop facet if normal[k] >= n\n\
+ Pg - print good Voronoi vertices (needs 'QGn' or 'QVn')\n\
+ PFn - keep Voronoi vertices whose 'area' is at least n\n\
+ PG - print neighbors of good Voronoi vertices\n\
+ PMn - keep n Voronoi vertices with most merges\n\
+ Po - force output. If error, output neighborhood of facet\n\
+ Pp - do not report precision problems\n\
+\n\
+ . - list of all options\n\
+ - - one line descriptions of all options\n\
+ -V - version\n\
+";
+/* for opts, don't assign 'e' or 'E' to a flag (already used for exponent) */
+
+/*-<a href="../libqhull/qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="prompt2">-</a>
+
+ qh_prompt2
+ synopsis for qhull
+*/
+char qh_prompt2[]= "\n\
+qvoronoi- compute the Voronoi diagram. Qhull %s\n\
+ input (stdin): dimension, number of points, point coordinates\n\
+ comments start with a non-numeric character\n\
+\n\
+options (qvoronoi.htm):\n\
+ Qu - compute furthest-site Voronoi diagram\n\
+ Tv - verify result: structure, convexity, and in-circle test\n\
+ . - concise list of all options\n\
+ - - one-line description of all options\n\
+ -V - version\n\
+\n\
+output options (subset):\n\
+ s - summary of results (default)\n\
+ p - Voronoi vertices\n\
+ o - OFF file format (dim, Voronoi vertices, and Voronoi regions)\n\
+ FN - count and Voronoi vertices for each Voronoi region\n\
+ Fv - Voronoi diagram as Voronoi vertices between adjacent input sites\n\
+ Fi - separating hyperplanes for bounded regions, 'Fo' for unbounded\n\
+ G - Geomview output (2-d only)\n\
+ QVn - Voronoi vertices for input point n, -n if not\n\
+ TO file- output results to file, may be enclosed in single quotes\n\
+\n\
+examples:\n\
+rbox c P0 D2 | qvoronoi s o rbox c P0 D2 | qvoronoi Fi\n\
+rbox c P0 D2 | qvoronoi Fo rbox c P0 D2 | qvoronoi Fv\n\
+rbox c P0 D2 | qvoronoi s Qu Fv rbox c P0 D2 | qvoronoi Qu Fo\n\
+rbox c G1 d D2 | qvoronoi s p rbox c P0 D2 | qvoronoi s Fv QV0\n\
+\n\
+";
+/* for opts, don't assign 'e' or 'E' to a flag (already used for exponent) */
+
+/*-<a href="../libqhull/qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="prompt3">-</a>
+
+ qh_prompt3
+ concise prompt for qhull
+*/
+char qh_prompt3[]= "\n\
+Qhull %s.\n\
+Except for 'F.' and 'PG', upper-case options take an argument.\n\
+\n\
+ OFF_format p_vertices i_delaunay summary facet_dump\n\
+\n\
+ Fcoincident Fd_cdd_in FD_cdd_out FF-dump-xridge Fi_bounded\n\
+ Fxtremes Fmerges Fneighbors FNeigh_region FOptions\n\
+ Fo_unbounded FPoint_near FQvoronoi Fsummary Fvoronoi\n\
+ FIDs\n\
+\n\
+ Gvertices Gpoints Gall_points Gno_planes Ginner\n\
+ Gcentrums Ghyperplanes Gridges Gouter GDrop_dim\n\
+\n\
+ PArea_keep Pdrop d0:0D0 Pgood PFacet_area_keep\n\
+ PGood_neighbors PMerge_keep Poutput_forced Pprecision_not\n\
+\n\
+ QG_vertex_good Qsearch_1st Qupper_voronoi QV_point_good Qzinfinite\n\
+ T4_trace Tcheck_often Tstatistics Tverify Tz_stdout\n\
+ TFacet_log TInput_file TPoint_trace TMerge_trace TOutput_file\n\
+ TWide_trace TVertex_stop TCone_stop\n\
+\n\
+ Angle_max Centrum_size Random_dist Wide_outside\n\
+";
+
+/*-<a href="../libqhull/qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="main">-</a>
+
+ main( argc, argv )
+ processes the command line, calls qhull() to do the work, and exits
+
+ design:
+ initializes data structures
+ reads points
+ finishes initialization
+ computes convex hull and other structures
+ checks the result
+ writes the output
+ frees memory
+*/
+int main(int argc, char *argv[]) {
+ int curlong, totlong; /* used !qh_NOmem */
+ int exitcode, numpoints, dim;
+ coordT *points;
+ boolT ismalloc;
+
+ QHULL_LIB_CHECK /* Check for compatible library */
+
+ if ((argc == 1) && isatty( 0 /*stdin*/)) {
+ fprintf(stdout, qh_prompt2, qh_version);
+ exit(qh_ERRnone);
+ }
+ if (argc > 1 && *argv[1] == '-' && !*(argv[1]+1)) {
+ fprintf(stdout, qh_prompta, qh_version,
+ qh_promptb, qh_promptc, qh_promptd, qh_prompte);
+ exit(qh_ERRnone);
+ }
+ if (argc > 1 && *argv[1] == '.' && !*(argv[1]+1)) {
+ fprintf(stdout, qh_prompt3, qh_version);
+ exit(qh_ERRnone);
+ }
+ if (argc > 1 && *argv[1] == '-' && *(argv[1]+1)=='V') {
+ fprintf(stdout, "%s\n", qh_version2);
+ exit(qh_ERRnone);
+ }
+ qh_init_A(stdin, stdout, stderr, argc, argv); /* sets qh qhull_command */
+ exitcode= setjmp(qh errexit); /* simple statement for CRAY J916 */
+ if (!exitcode) {
+ qh NOerrexit= False;
+ qh_option("voronoi _bbound-last _coplanar-keep", NULL, NULL);
+ qh DELAUNAY= True; /* 'v' */
+ qh VORONOI= True;
+ qh SCALElast= True; /* 'Qbb' */
+ qh_checkflags(qh qhull_command, hidden_options);
+ qh_initflags(qh qhull_command);
+ points= qh_readpoints(&numpoints, &dim, &ismalloc);
+ if (dim >= 5) {
+ qh_option("_merge-exact", NULL, NULL);
+ qh MERGEexact= True; /* 'Qx' always */
+ }
+ qh_init_B(points, numpoints, dim, ismalloc);
+ qh_qhull();
+ qh_check_output();
+ qh_produce_output();
+ if (qh VERIFYoutput && !qh FORCEoutput && !qh STOPpoint && !qh STOPcone)
+ qh_check_points();
+ exitcode= qh_ERRnone;
+ }
+ qh NOerrexit= True; /* no more setjmp */
+#ifdef qh_NOmem
+ qh_freeqhull(qh_ALL);
+#else
+ qh_freeqhull(!qh_ALL);
+ qh_memfreeshort(&curlong, &totlong);
+ if (curlong || totlong)
+ qh_fprintf_stderr(6263, "qhull internal warning (main): did not free %d bytes of long memory(%d pieces)\n",
+ totlong, curlong);
+#endif
+ return exitcode;
+} /* main */
+
diff --git a/xs/src/qhull/src/qvoronoi/qvoronoi.pro b/xs/src/qhull/src/qvoronoi/qvoronoi.pro
new file mode 100644
index 000000000..4646c8447
--- /dev/null
+++ b/xs/src/qhull/src/qvoronoi/qvoronoi.pro
@@ -0,0 +1,9 @@
+# -------------------------------------------------
+# qvoronoi.pro -- Qt project file for qvoronoi.exe
+# -------------------------------------------------
+
+include(../qhull-app-c.pri)
+
+TARGET = qvoronoi
+
+SOURCES += qvoronoi.c
diff --git a/xs/src/qhull/src/qvoronoi/qvoronoi_r.c b/xs/src/qhull/src/qvoronoi/qvoronoi_r.c
new file mode 100644
index 000000000..6323c8b49
--- /dev/null
+++ b/xs/src/qhull/src/qvoronoi/qvoronoi_r.c
@@ -0,0 +1,305 @@
+/*<html><pre> -<a href="../libqhull/qh-qhull.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ qvoronoi.c
+ compute Voronoi diagrams and furthest-point Voronoi
+ diagrams using qhull
+
+ see unix.c for full interface
+
+ Copyright (c) 1993-2015, The Geometry Center
+*/
+
+#include "libqhull_r/libqhull_r.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+
+#if __cplusplus
+extern "C" {
+ int isatty(int);
+}
+
+#elif _MSC_VER
+#include <io.h>
+#define isatty _isatty
+/* int _isatty(int); */
+
+#else
+int isatty(int); /* returns 1 if stdin is a tty
+ if "Undefined symbol" this can be deleted along with call in main() */
+#endif
+
+/*-<a href="../libqhull/qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="prompt">-</a>
+
+ qh_prompt
+ long prompt for qhull
+
+ notes:
+ restricted version of libqhull.c
+
+ see:
+ concise prompt below
+*/
+
+/* duplicated in qvoron_f.htm and qvoronoi.htm
+ QJ and Qt are deprecated, but allowed for backwards compatibility
+*/
+char hidden_options[]=" d n m v H U Qb QB Qc Qf Qg Qi Qm Qr QR Qv Qx TR E V Fa FA FC Fp FS Ft FV Pv Gt Q0 Q1 Q2 Q3 Q4 Q5 Q6 Q7 Q8 Q9 ";
+
+char qh_prompta[]= "\n\
+qvoronoi- compute the Voronoi diagram\n\
+ http://www.qhull.org %s\n\
+\n\
+input (stdin):\n\
+ first lines: dimension and number of points (or vice-versa).\n\
+ other lines: point coordinates, best if one point per line\n\
+ comments: start with a non-numeric character\n\
+\n\
+options:\n\
+ Qu - compute furthest-site Voronoi diagram\n\
+\n\
+Qhull control options:\n\
+ Qz - add point-at-infinity to Voronoi diagram\n\
+%s%s%s%s"; /* split up qh_prompt for Visual C++ */
+char qh_promptb[]= "\
+ Qs - search all points for the initial simplex\n\
+ QGn - Voronoi vertices if visible from point n, -n if not\n\
+ QVn - Voronoi vertices for input point n, -n if not\n\
+\n\
+";
+char qh_promptc[]= "\
+Trace options:\n\
+ T4 - trace at level n, 4=all, 5=mem/gauss, -1= events\n\
+ Tc - check frequently during execution\n\
+ Ts - statistics\n\
+ Tv - verify result: structure, convexity, and in-circle test\n\
+ Tz - send all output to stdout\n\
+ TFn - report summary when n or more facets created\n\
+ TI file - input data from file, no spaces or single quotes\n\
+ TO file - output results to file, may be enclosed in single quotes\n\
+ TPn - turn on tracing when point n added to hull\n\
+ TMn - turn on tracing at merge n\n\
+ TWn - trace merge facets when width > n\n\
+ TVn - stop qhull after adding point n, -n for before (see TCn)\n\
+ TCn - stop qhull after building cone for point n (see TVn)\n\
+\n\
+Precision options:\n\
+ Cn - radius of centrum (roundoff added). Merge facets if non-convex\n\
+ An - cosine of maximum angle. Merge facets if cosine > n or non-convex\n\
+ C-0 roundoff, A-0.99/C-0.01 pre-merge, A0.99/C0.01 post-merge\n\
+ Rn - randomly perturb computations by a factor of [1-n,1+n]\n\
+ Wn - min facet width for non-coincident point (before roundoff)\n\
+\n\
+Output formats (may be combined; if none, produces a summary to stdout):\n\
+ s - summary to stderr\n\
+ p - Voronoi vertices\n\
+ o - OFF format (dim, Voronoi vertices, and Voronoi regions)\n\
+ i - Delaunay regions (use 'Pp' to avoid warning)\n\
+ f - facet dump\n\
+\n\
+";
+char qh_promptd[]= "\
+More formats:\n\
+ Fc - count plus coincident points (by Voronoi vertex)\n\
+ Fd - use cdd format for input (homogeneous with offset first)\n\
+ FD - use cdd format for output (offset first)\n\
+ FF - facet dump without ridges\n\
+ Fi - separating hyperplanes for bounded Voronoi regions\n\
+ FI - ID for each Voronoi vertex\n\
+ Fm - merge count for each Voronoi vertex (511 max)\n\
+ Fn - count plus neighboring Voronoi vertices for each Voronoi vertex\n\
+ FN - count and Voronoi vertices for each Voronoi region\n\
+ Fo - separating hyperplanes for unbounded Voronoi regions\n\
+ FO - options and precision constants\n\
+ FP - nearest point and distance for each coincident point\n\
+ FQ - command used for qvoronoi\n\
+ Fs - summary: #int (8), dimension, #points, tot vertices, tot facets,\n\
+ for output: #Voronoi regions, #Voronoi vertices,\n\
+ #coincident points, #non-simplicial regions\n\
+ #real (2), max outer plane and min vertex\n\
+ Fv - Voronoi diagram as Voronoi vertices between adjacent input sites\n\
+ Fx - extreme points of Delaunay triangulation (on convex hull)\n\
+\n\
+";
+char qh_prompte[]= "\
+Geomview options (2-d only)\n\
+ Ga - all points as dots\n\
+ Gp - coplanar points and vertices as radii\n\
+ Gv - vertices as spheres\n\
+ Gi - inner planes only\n\
+ Gn - no planes\n\
+ Go - outer planes only\n\
+ Gc - centrums\n\
+ Gh - hyperplane intersections\n\
+ Gr - ridges\n\
+ GDn - drop dimension n in 3-d and 4-d output\n\
+\n\
+Print options:\n\
+ PAn - keep n largest Voronoi vertices by 'area'\n\
+ Pdk:n - drop facet if normal[k] <= n (default 0.0)\n\
+ PDk:n - drop facet if normal[k] >= n\n\
+ Pg - print good Voronoi vertices (needs 'QGn' or 'QVn')\n\
+ PFn - keep Voronoi vertices whose 'area' is at least n\n\
+ PG - print neighbors of good Voronoi vertices\n\
+ PMn - keep n Voronoi vertices with most merges\n\
+ Po - force output. If error, output neighborhood of facet\n\
+ Pp - do not report precision problems\n\
+\n\
+ . - list of all options\n\
+ - - one line descriptions of all options\n\
+ -V - version\n\
+";
+/* for opts, don't assign 'e' or 'E' to a flag (already used for exponent) */
+
+/*-<a href="../libqhull/qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="prompt2">-</a>
+
+ qh_prompt2
+ synopsis for qhull
+*/
+char qh_prompt2[]= "\n\
+qvoronoi- compute the Voronoi diagram. Qhull %s\n\
+ input (stdin): dimension, number of points, point coordinates\n\
+ comments start with a non-numeric character\n\
+\n\
+options (qvoronoi.htm):\n\
+ Qu - compute furthest-site Voronoi diagram\n\
+ Tv - verify result: structure, convexity, and in-circle test\n\
+ . - concise list of all options\n\
+ - - one-line description of all options\n\
+ -V - version\n\
+\n\
+output options (subset):\n\
+ s - summary of results (default)\n\
+ p - Voronoi vertices\n\
+ o - OFF file format (dim, Voronoi vertices, and Voronoi regions)\n\
+ FN - count and Voronoi vertices for each Voronoi region\n\
+ Fv - Voronoi diagram as Voronoi vertices between adjacent input sites\n\
+ Fi - separating hyperplanes for bounded regions, 'Fo' for unbounded\n\
+ G - Geomview output (2-d only)\n\
+ QVn - Voronoi vertices for input point n, -n if not\n\
+ TO file- output results to file, may be enclosed in single quotes\n\
+\n\
+examples:\n\
+rbox c P0 D2 | qvoronoi s o rbox c P0 D2 | qvoronoi Fi\n\
+rbox c P0 D2 | qvoronoi Fo rbox c P0 D2 | qvoronoi Fv\n\
+rbox c P0 D2 | qvoronoi s Qu Fv rbox c P0 D2 | qvoronoi Qu Fo\n\
+rbox c G1 d D2 | qvoronoi s p rbox c P0 D2 | qvoronoi s Fv QV0\n\
+\n\
+";
+/* for opts, don't assign 'e' or 'E' to a flag (already used for exponent) */
+
+/*-<a href="../libqhull/qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="prompt3">-</a>
+
+ qh_prompt3
+ concise prompt for qhull
+*/
+char qh_prompt3[]= "\n\
+Qhull %s.\n\
+Except for 'F.' and 'PG', upper-case options take an argument.\n\
+\n\
+ OFF_format p_vertices i_delaunay summary facet_dump\n\
+\n\
+ Fcoincident Fd_cdd_in FD_cdd_out FF-dump-xridge Fi_bounded\n\
+ Fxtremes Fmerges Fneighbors FNeigh_region FOptions\n\
+ Fo_unbounded FPoint_near FQvoronoi Fsummary Fvoronoi\n\
+ FIDs\n\
+\n\
+ Gvertices Gpoints Gall_points Gno_planes Ginner\n\
+ Gcentrums Ghyperplanes Gridges Gouter GDrop_dim\n\
+\n\
+ PArea_keep Pdrop d0:0D0 Pgood PFacet_area_keep\n\
+ PGood_neighbors PMerge_keep Poutput_forced Pprecision_not\n\
+\n\
+ QG_vertex_good Qsearch_1st Qupper_voronoi QV_point_good Qzinfinite\n\
+ T4_trace Tcheck_often Tstatistics Tverify Tz_stdout\n\
+ TFacet_log TInput_file TPoint_trace TMerge_trace TOutput_file\n\
+ TWide_trace TVertex_stop TCone_stop\n\
+\n\
+ Angle_max Centrum_size Random_dist Wide_outside\n\
+";
+
+/*-<a href="../libqhull/qh-qhull.htm#TOC"
+ >-------------------------------</a><a name="main">-</a>
+
+ main( argc, argv )
+ processes the command line, calls qhull() to do the work, and exits
+
+ design:
+ initializes data structures
+ reads points
+ finishes initialization
+ computes convex hull and other structures
+ checks the result
+ writes the output
+ frees memory
+*/
+int main(int argc, char *argv[]) {
+ int curlong, totlong; /* used !qh_NOmem */
+ int exitcode, numpoints, dim;
+ coordT *points;
+ boolT ismalloc;
+ qhT qh_qh;
+ qhT *qh= &qh_qh;
+
+ QHULL_LIB_CHECK /* Check for compatible library */
+
+ if ((argc == 1) && isatty( 0 /*stdin*/)) {
+ fprintf(stdout, qh_prompt2, qh_version);
+ exit(qh_ERRnone);
+ }
+ if (argc > 1 && *argv[1] == '-' && !*(argv[1]+1)) {
+ fprintf(stdout, qh_prompta, qh_version,
+ qh_promptb, qh_promptc, qh_promptd, qh_prompte);
+ exit(qh_ERRnone);
+ }
+ if (argc > 1 && *argv[1] == '.' && !*(argv[1]+1)) {
+ fprintf(stdout, qh_prompt3, qh_version);
+ exit(qh_ERRnone);
+ }
+ if (argc > 1 && *argv[1] == '-' && *(argv[1]+1)=='V') {
+ fprintf(stdout, "%s\n", qh_version2);
+ exit(qh_ERRnone);
+ }
+ qh_init_A(qh, stdin, stdout, stderr, argc, argv); /* sets qh->qhull_command */
+ exitcode= setjmp(qh->errexit); /* simple statement for CRAY J916 */
+ if (!exitcode) {
+ qh->NOerrexit = False;
+ qh_option(qh, "voronoi _bbound-last _coplanar-keep", NULL, NULL);
+ qh->DELAUNAY= True; /* 'v' */
+ qh->VORONOI= True;
+ qh->SCALElast= True; /* 'Qbb' */
+ qh_checkflags(qh, qh->qhull_command, hidden_options);
+ qh_initflags(qh, qh->qhull_command);
+ points= qh_readpoints(qh, &numpoints, &dim, &ismalloc);
+ if (dim >= 5) {
+ qh_option(qh, "_merge-exact", NULL, NULL);
+ qh->MERGEexact= True; /* 'Qx' always */
+ }
+ qh_init_B(qh, points, numpoints, dim, ismalloc);
+ qh_qhull(qh);
+ qh_check_output(qh);
+ qh_produce_output(qh);
+ if (qh->VERIFYoutput && !qh->FORCEoutput && !qh->STOPpoint && !qh->STOPcone)
+ qh_check_points(qh);
+ exitcode= qh_ERRnone;
+ }
+ qh->NOerrexit= True; /* no more setjmp */
+#ifdef qh_NOmem
+ qh_freeqhull(qh, qh_ALL);
+#else
+ qh_freeqhull(qh, !qh_ALL);
+ qh_memfreeshort(qh, &curlong, &totlong);
+ if (curlong || totlong)
+ qh_fprintf_stderr(6263, "qhull internal warning (main): did not free %d bytes of long memory(%d pieces)\n",
+ totlong, curlong);
+#endif
+ return exitcode;
+} /* main */
+
diff --git a/xs/src/qhull/src/rbox/rbox.c b/xs/src/qhull/src/rbox/rbox.c
new file mode 100644
index 000000000..d7c51b1aa
--- /dev/null
+++ b/xs/src/qhull/src/rbox/rbox.c
@@ -0,0 +1,88 @@
+/*<html><pre> -<a href="../libqhull/index.htm#TOC"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ rbox.c
+ rbox program for generating input points for qhull.
+
+ notes:
+ 50 points generated for 'rbox D4'
+
+*/
+
+#include "libqhull/libqhull.h"
+#include "libqhull/random.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef _MSC_VER /* Microsoft Visual C++ -- warning level 4 */
+#pragma warning( disable : 4706) /* assignment within conditional function */
+#endif
+
+char prompt[]= "\n\
+-rbox- generate various point distributions. Default is random in cube.\n\
+\n\
+args (any order, space separated): Version: 2016/01/18\n\
+ 3000 number of random points in cube, lens, spiral, sphere or grid\n\
+ D3 dimension 3-d\n\
+ c add a unit cube to the output ('c G2.0' sets size)\n\
+ d add a unit diamond to the output ('d G2.0' sets size)\n\
+ l generate a regular 3-d spiral\n\
+ r generate a regular polygon, ('r s Z1 G0.1' makes a cone)\n\
+ s generate cospherical points\n\
+ x generate random points in simplex, may use 'r' or 'Wn'\n\
+ y same as 'x', plus simplex\n\
+ Cn,r,m add n nearly coincident points within radius r of m points\n\
+ Pn,m,r add point [n,m,r] first, pads with 0, maybe repeated\n\
+\n\
+ Ln lens distribution of radius n. Also 's', 'r', 'G', 'W'.\n\
+ Mn,m,r lattice(Mesh) rotated by [n,-m,0], [m,n,0], [0,0,r], ...\n\
+ '27 M1,0,1' is {0,1,2} x {0,1,2} x {0,1,2}. Try 'M3,4 z'.\n\
+ W0.1 random distribution within 0.1 of the cube's or sphere's surface\n\
+ Z0.5 s random points in a 0.5 disk projected to a sphere\n\
+ Z0.5 s G0.6 same as Z0.5 within a 0.6 gap\n\
+\n\
+ Bn bounding box coordinates, default %2.2g\n\
+ h output as homogeneous coordinates for cdd\n\
+ n remove command line from the first line of output\n\
+ On offset coordinates by n\n\
+ t use time as the random number seed(default is command line)\n\
+ tn use n as the random number seed\n\
+ z print integer coordinates, default 'Bn' is %2.2g\n\
+";
+
+/*--------------------------------------------
+-rbox- main procedure of rbox application
+*/
+int main(int argc, char **argv) {
+ char *command;
+ int command_size;
+ int return_status;
+
+ QHULL_LIB_CHECK_RBOX
+
+ if (argc == 1) {
+ printf(prompt, qh_DEFAULTbox, qh_DEFAULTzbox);
+ return 1;
+ }
+ if (argc == 2 && strcmp(argv[1], "D4")==0)
+ qh_fprintf_stderr(0, "\nStarting the rbox smoketest for qhull. An immediate failure indicates\nthat non-reentrant rbox was linked to reentrant routines. An immediate\nfailure of qhull may indicate that qhull was linked to the wrong\nqhull library. Also try 'rbox D4 | qhull T1'\n");
+
+ command_size= qh_argv_to_command_size(argc, argv);
+ if ((command= (char *)qh_malloc((size_t)command_size))) {
+ if (!qh_argv_to_command(argc, argv, command, command_size)) {
+ qh_fprintf_stderr(6264, "rbox internal error: allocated insufficient memory (%d) for arguments\n", command_size);
+ return_status= qh_ERRinput;
+ }else{
+ return_status= qh_rboxpoints(stdout, stderr, command);
+ }
+ qh_free(command);
+ }else {
+ qh_fprintf_stderr(6265, "rbox error: insufficient memory for %d bytes\n", command_size);
+ return_status= qh_ERRmem;
+ }
+ return return_status;
+}/*main*/
+
diff --git a/xs/src/qhull/src/rbox/rbox.pro b/xs/src/qhull/src/rbox/rbox.pro
new file mode 100644
index 000000000..6c21bdb6d
--- /dev/null
+++ b/xs/src/qhull/src/rbox/rbox.pro
@@ -0,0 +1,9 @@
+# -------------------------------------------------
+# rbox.pro -- Qt project for rbox.exe with libqhullstatic
+# -------------------------------------------------
+
+include(../qhull-app-c.pri)
+
+TARGET = rbox
+
+SOURCES += rbox.c
diff --git a/xs/src/qhull/src/rbox/rbox_r.c b/xs/src/qhull/src/rbox/rbox_r.c
new file mode 100644
index 000000000..6ec74d914
--- /dev/null
+++ b/xs/src/qhull/src/rbox/rbox_r.c
@@ -0,0 +1,78 @@
+
+/*<html><pre> -<a href="../libqhull/index.htm#TOC"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ rbox.c
+ rbox program for generating input points for qhull.
+
+ notes:
+ 50 points generated for 'rbox D4'
+
+*/
+
+#include "libqhull_r/libqhull_r.h"
+#include "libqhull/random_r.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef _MSC_VER /* Microsoft Visual C++ -- warning level 4 */
+#pragma warning( disable : 4706) /* assignment within conditional function */
+#endif
+
+char prompt[]= "\n\
+-rbox- generate various point distributions. Default is random in cube.\n\
+\n\
+args (any order, space separated): Version: 2016/01/18 r\n\
+ 3000 number of random points in cube, lens, spiral, sphere or grid\n\
+ D3 dimension 3-d\n\
+ c add a unit cube to the output ('c G2.0' sets size)\n\
+ d add a unit diamond to the output ('d G2.0' sets size)\n\
+ l generate a regular 3-d spiral\n\
+ r generate a regular polygon, ('r s Z1 G0.1' makes a cone)\n\
+ s generate cospherical points\n\
+ x generate random points in simplex, may use 'r' or 'Wn'\n\
+ y same as 'x', plus simplex\n\
+ Cn,r,m add n nearly coincident points within radius r of m points\n\
+ Pn,m,r add point [n,m,r] first, pads with 0, maybe repeated\n\
+\n\
+ Ln lens distribution of radius n. Also 's', 'r', 'G', 'W'.\n\
+ Mn,m,r lattice(Mesh) rotated by [n,-m,0], [m,n,0], [0,0,r], ...\n\
+ '27 M1,0,1' is {0,1,2} x {0,1,2} x {0,1,2}. Try 'M3,4 z'.\n\
+ W0.1 random distribution within 0.1 of the cube's or sphere's surface\n\
+ Z0.5 s random points in a 0.5 disk projected to a sphere\n\
+ Z0.5 s G0.6 same as Z0.5 within a 0.6 gap\n\
+\n\
+ Bn bounding box coordinates, default %2.2g\n\
+ h output as homogeneous coordinates for cdd\n\
+ n remove command line from the first line of output\n\
+ On offset coordinates by n\n\
+ t use time as the random number seed(default is command line)\n\
+ tn use n as the random number seed\n\
+ z print integer coordinates, default 'Bn' is %2.2g\n\
+";
+
+/*--------------------------------------------
+-rbox- main procedure of rbox application
+*/
+int main(int argc, char **argv) {
+ int return_status;
+ qhT qh_qh;
+ qhT *qh= &qh_qh;
+
+ QHULL_LIB_CHECK_RBOX
+
+ if (argc == 1) {
+ printf(prompt, qh_DEFAULTbox, qh_DEFAULTzbox);
+ return 1;
+ }
+ if (argc == 2 && strcmp(argv[1], "D4")==0)
+ qh_fprintf_stderr(0, "\nStarting the rbox smoketest for qhull. An immediate failure indicates\nthat reentrant rbox was linked to non-reentrant routines. An immediate\nfailure of qhull may indicate that qhull was linked to the wrong\nqhull library. Also try 'rbox D4 | qhull T1'\n");
+
+ qh_init_A(qh, stdin, stdout, stderr, argc, argv); /*no qh_errexit, sets qh->qhull_command */
+ return_status= qh_rboxpoints(qh, qh->qhull_command); /* Traps its own errors, qh_errexit_rbox() */
+ return return_status;
+}/*main*/
+
diff --git a/xs/src/qhull/src/testqset/testqset.c b/xs/src/qhull/src/testqset/testqset.c
new file mode 100644
index 000000000..61057eef9
--- /dev/null
+++ b/xs/src/qhull/src/testqset/testqset.c
@@ -0,0 +1,891 @@
+/*<html><pre> -<a href="../libqhull/index.htm#TOC"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ testset.c -- test qset.c and its use of mem.c
+
+ The test sets are pointers to int. Normally a set is a pointer to a type (e.g., facetT, ridgeT, etc.).
+ For consistency in notation, an "int" is typedef'd to i2T
+
+Functions and macros from qset.h. Counts occurrences in this test. Does not correspond to thoroughness.
+ qh_setaddsorted -- 4 tests
+ qh_setaddnth -- 1 test
+ qh_setappend -- 7 tests
+ qh_setappend_set -- 1 test
+ qh_setappend2ndlast -- 1 test
+ qh_setcheck -- lots of tests
+ qh_setcompact -- 7 tests
+ qh_setcopy -- 3 tests
+ qh_setdel -- 1 tests
+ qh_setdellast -- 1 tests
+ qh_setdelnth -- 2 tests
+ qh_setdelnthsorted -- 2 tests
+ qh_setdelsorted -- 1 test
+ qh_setduplicate -- not testable here
+ qh_setequal -- 4 tests
+ qh_setequal_except -- 2 tests
+ qh_setequal_skip -- 2 tests
+ qh_setfree -- 11+ tests
+ qh_setfree2 -- not testable here
+ qh_setfreelong -- 2 tests
+ qh_setin -- 3 tests
+ qh_setindex -- 4 tests
+ qh_setlarger -- 1 test
+ qh_setlast -- 2 tests
+ qh_setnew -- 6 tests
+ qh_setnew_delnthsorted
+ qh_setprint -- tested elsewhere
+ qh_setreplace -- 1 test
+ qh_setsize -- 9+ tests
+ qh_settemp -- 2 tests
+ qh_settempfree -- 1 test
+ qh_settempfree_all -- 1 test
+ qh_settemppop -- 1 test
+ qh_settemppush -- 1 test
+ qh_settruncate -- 3 tests
+ qh_setunique -- 3 tests
+ qh_setzero -- 1 test
+ FOREACHint_ -- 2 test
+ FOREACHint4_
+ FOREACHint_i_ -- 1 test
+ FOREACHintreverse_
+ FOREACHintreverse12_
+ FOREACHsetelement_ -- 1 test
+ FOREACHsetelement_i_ -- 1 test
+ FOREACHsetelementreverse_ -- 1 test
+ FOREACHsetelementreverse12_ -- 1 test
+ SETelem_ -- 3 tests
+ SETelemaddr_ -- 2 tests
+ SETelemt_ -- not tested (generic)
+ SETempty_ -- 1 test
+ SETfirst_ -- 4 tests
+ SETfirstt_ -- 2 tests
+ SETindex_ -- 2 tests
+ SETref_ -- 2 tests
+ SETreturnsize_ -- 2 tests
+ SETsecond_ -- 1 test
+ SETsecondt_ -- 2 tests
+ SETtruncate_ -- 2 tests
+
+ Copyright (c) 2012-2015 C.B. Barber. All rights reserved.
+ $Id: //main/2015/qhull/src/testqset/testqset.c#4 $$Change: 2062 $
+ $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $
+*/
+
+#include "libqhull/user.h" /* QHULL_CRTDBG */
+#include "libqhull/qset.h"
+#include "libqhull/mem.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+typedef int i2T;
+#define MAXerrorCount 100 /* quit after n errors */
+
+#define FOREACHint_( ints ) FOREACHsetelement_( i2T, ints, i2)
+#define FOREACHint4_( ints ) FOREACHsetelement_( i2T, ints, i4)
+#define FOREACHint_i_( ints ) FOREACHsetelement_i_( i2T, ints, i2)
+#define FOREACHintreverse_( ints ) FOREACHsetelementreverse_( i2T, ints, i2)
+#define FOREACHintreverse12_( ints ) FOREACHsetelementreverse12_( i2T, ints, i2)
+
+enum {
+ MAXint= 0x7fffffff,
+};
+
+char prompt[]= "testqset N [M] [T5] -- Test qset.c and mem.c\n\
+ \n\
+ If this test fails then qhull will not work.\n\
+ \n\
+ Test qsets of 0..N integers with a check every M iterations (default ~log10)\n\
+ Additional checking and logging if M is 1\n\
+ \n\
+ T5 turns on memory logging (qset does not log)\n\
+ \n\
+ For example:\n\
+ testqset 10000\n\
+";
+
+int error_count= 0; /* Global error_count. checkSetContents() keeps its own error count. It exits on too many errors */
+
+/* Macros normally defined in geom.h */
+#define fmax_( a,b ) ( ( a ) < ( b ) ? ( b ) : ( a ) )
+
+/* Macros normally defined in user.h */
+
+#define realT double
+#define qh_MEMalign ((int)(fmax_(sizeof(realT), sizeof(void *))))
+#define qh_MEMbufsize 0x10000 /* allocate 64K memory buffers */
+#define qh_MEMinitbuf 0x20000 /* initially allocate 128K buffer */
+
+/* Macros normally defined in QhullSet.h */
+
+
+/* Functions normally defined in user.h for usermem.c */
+
+void qh_exit(int exitcode);
+void qh_fprintf_stderr(int msgcode, const char *fmt, ... );
+void qh_free(void *mem);
+void *qh_malloc(size_t size);
+
+/* Normally defined in user.c */
+
+void qh_errexit(int exitcode, void *f, void *r)
+{
+ (void)f; /* unused */
+ (void)r; /* unused */
+ qh_exit(exitcode);
+}
+
+/* Normally defined in userprintf.c */
+
+void qh_fprintf(FILE *fp, int msgcode, const char *fmt, ... )
+{
+ static int needs_cr= 0; /* True if qh_fprintf needs a CR */
+
+ size_t fmtlen= strlen(fmt);
+ va_list args;
+
+ if (!fp) {
+ /* Do not use qh_fprintf_stderr. This is a standalone program */
+ fprintf(stderr, "QH6232 qh_fprintf: fp not defined for '%s'", fmt);
+ qh_errexit(6232, NULL, NULL);
+ }
+ if(fmtlen>0){
+ if(fmt[fmtlen-1]=='\n'){
+ if(needs_cr && fmtlen>1){
+ fprintf(fp, "\n");
+ }
+ needs_cr= 0;
+ }else{
+ needs_cr= 1;
+ }
+ }
+ if(msgcode>=6000 && msgcode<7000){
+ fprintf(fp, "Error TQ%d ", msgcode);
+ }
+ va_start(args, fmt);
+ vfprintf(fp, fmt, args);
+ va_end(args);
+}
+
+/* Defined below in order of use */
+int main(int argc, char **argv);
+void readOptions(int argc, char **argv, const char *promptstr, int *numInts, int *checkEvery, int *traceLevel);
+void setupMemory(int tracelevel, int numInts, int **intarray);
+
+void testSetappendSettruncate(int numInts, int *intarray, int checkEvery);
+void testSetdelSetadd(int numInts, int *intarray, int checkEvery);
+void testSetappendSet(int numInts, int *intarray, int checkEvery);
+void testSetcompactCopy(int numInts, int *intarray, int checkEvery);
+void testSetequalInEtc(int numInts, int *intarray, int checkEvery);
+void testSettemp(int numInts, int *intarray, int checkEvery);
+void testSetlastEtc(int numInts, int *intarray, int checkEvery);
+void testSetdelsortedEtc(int numInts, int *intarray, int checkEvery);
+
+int log_i(setT *set, const char *s, int i, int numInts, int checkEvery);
+void checkSetContents(const char *name, setT *set, int count, int rangeA, int rangeB, int rangeC);
+
+int main(int argc, char **argv) {
+ int *intarray= NULL;
+ int numInts;
+ int checkEvery= MAXint;
+ int curlong, totlong;
+ int traceLevel= 4; /* 4 normally, no tracing since qset does not log. 5 for memory tracing */
+
+#if defined(_MSC_VER) && defined(_DEBUG) && defined(QHULL_CRTDBG) /* user.h */
+ _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_DELAY_FREE_MEM_DF | _CRTDBG_LEAK_CHECK_DF | _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) );
+ _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG );
+ _CrtSetReportFile( _CRT_ERROR, _CRTDBG_FILE_STDERR );
+#endif
+
+ readOptions(argc, argv, prompt, &numInts, &checkEvery, &traceLevel);
+ setupMemory(traceLevel, numInts, &intarray);
+
+ testSetappendSettruncate(numInts, intarray, checkEvery);
+ testSetdelSetadd(numInts, intarray, checkEvery);
+ testSetappendSet(numInts, intarray, checkEvery);
+ testSetcompactCopy(numInts, intarray, checkEvery);
+ testSetequalInEtc(numInts, intarray, checkEvery);
+ testSettemp(numInts, intarray, checkEvery);
+ testSetlastEtc(numInts, intarray, checkEvery);
+ testSetdelsortedEtc(numInts, intarray, checkEvery);
+ printf("\n\nNot testing qh_setduplicate and qh_setfree2.\n These routines use heap-allocated set contents. See qhull tests.\n");
+
+ qh_memstatistics(stdout);
+ qh_memfreeshort(&curlong, &totlong);
+ if (curlong || totlong){
+ qh_fprintf(stderr, 8043, "qh_memfreeshort: did not free %d bytes of long memory(%d pieces)\n", totlong, curlong);
+ error_count++;
+ }
+ if(error_count){
+ qh_fprintf(stderr, 8012, "testqset: %d errors\n\n", error_count);
+ exit(1);
+ }else{
+ printf("testqset: OK\n\n");
+ }
+ return 0;
+}/*main*/
+
+void readOptions(int argc, char **argv, const char *promptstr, int *numInts, int *checkEvery, int *traceLevel)
+{
+ long numIntsArg;
+ long checkEveryArg;
+ char *endp;
+ int isTracing= 0;
+
+ if (argc < 2 || argc > 4) {
+ printf("%s", promptstr);
+ exit(0);
+ }
+ numIntsArg= strtol(argv[1], &endp, 10);
+ if(numIntsArg<1){
+ qh_fprintf(stderr, 6301, "First argument should be 1 or greater. Got '%s'\n", argv[1]);
+ exit(1);
+ }
+ if(numIntsArg>MAXint){
+ qh_fprintf(stderr, 6302, "qset does not currently support 64-bit ints. Maximum count is %d\n", MAXint);
+ exit(1);
+ }
+ *numInts= (int)numIntsArg;
+
+ if(argc==3 && argv[2][0]=='T' && argv[2][1]=='5' ){
+ isTracing= 1;
+ *traceLevel= 5;
+ }
+ if(argc==4 || (argc==3 && !isTracing)){
+ checkEveryArg= strtol(argv[2], &endp, 10);
+ if(checkEveryArg<1){
+ qh_fprintf(stderr, 6321, "checkEvery argument should be 1 or greater. Got '%s'\n", argv[2]);
+ exit(1);
+ }
+ if(checkEveryArg>MAXint){
+ qh_fprintf(stderr, 6322, "qset does not currently support 64-bit ints. Maximum checkEvery is %d\n", MAXint);
+ exit(1);
+ }
+ if(argc==4){
+ if(argv[3][0]=='T' && argv[3][1]=='5' ){
+ isTracing= 1;
+ *traceLevel= 5;
+ }else{
+ qh_fprintf(stderr, 6242, "Optional third argument must be 'T5'. Got '%s'\n", argv[3]);
+ exit(1);
+ }
+ }
+ *checkEvery= (int)checkEveryArg;
+ }
+}/*readOptions*/
+
+void setupMemory(int tracelevel, int numInts, int **intarray)
+{
+ int i;
+ if(numInts<0 || numInts*(int)sizeof(int)<0){
+ qh_fprintf(stderr, 6303, "qset does not currently support 64-bit ints. Integer overflow\n");
+ exit(1);
+ }
+ *intarray= qh_malloc(numInts * sizeof(int));
+ if(!*intarray){
+ qh_fprintf(stderr, 6304, "Failed to allocate %d bytes of memory\n", numInts * sizeof(int));
+ exit(1);
+ }
+ for(i= 0; i<numInts; i++){
+ (*intarray)[i] =i;
+ }
+
+ qh_meminit(stderr);
+ qh_meminitbuffers(tracelevel, qh_MEMalign, 4 /*sizes*/, qh_MEMbufsize,qh_MEMinitbuf);
+ qh_memsize(10);
+ qh_memsize(20);
+ qh_memsize(30);
+ qh_memsize(40);
+ qh_memsetup();
+
+ qh_fprintf(stderr, 8001, "SETelemsize is %d bytes for pointer-to-int\n", SETelemsize);
+}/*setupMemmory*/
+
+void testSetappendSettruncate(int numInts, int *intarray, int checkEvery)
+{
+ setT *ints= qh_setnew(4);
+ int i, isCheck;
+
+ qh_fprintf(stderr, 8002, "\n\nTesting qh_setappend 0..%d. Test", numInts-1);
+ for(i= 0; i<numInts; i++){
+ isCheck= log_i(ints, "i", i, numInts, checkEvery);
+ qh_setappend(&ints, intarray+i);
+ if(isCheck){
+ checkSetContents("qh_setappend", ints, i+1, 0, -1, -1);
+ }
+ }
+
+ qh_fprintf(stderr, 8014, "\n\nTesting qh_settruncate %d and 0. Test", numInts/2);
+ if(numInts>=2){
+ isCheck= log_i(ints, "n", numInts/2, numInts, checkEvery);
+ qh_settruncate(ints, numInts/2);
+ checkSetContents("qh_settruncate by half", ints, numInts/2, 0, -1, -1);
+ }
+ isCheck= log_i(ints, "n", 0, numInts, checkEvery);
+ qh_settruncate(ints, 0);
+ checkSetContents("qh_settruncate", ints, 0, -1, -1, -1);
+
+ qh_fprintf(stderr, 8003, "\n\nTesting qh_setappend2ndlast 0,0..%d. Test 0", numInts-1);
+ qh_setfree(&ints);
+ ints= qh_setnew(4);
+ qh_setappend(&ints, intarray+0);
+ for(i= 0; i<numInts; i++){
+ isCheck= log_i(ints, "i", i, numInts, checkEvery);
+ qh_setappend2ndlast(&ints, intarray+i);
+ if(isCheck){
+ checkSetContents("qh_setappend2ndlast", ints, i+2, 0, 0, -1);
+ }
+ }
+ qh_fprintf(stderr, 8015, "\n\nTesting SETtruncate_ %d and 0. Test", numInts/2);
+ if(numInts>=2){
+ isCheck= log_i(ints, "n", numInts/2, numInts, checkEvery);
+ SETtruncate_(ints, numInts/2);
+ checkSetContents("SETtruncate_ by half", ints, numInts/2, 0, -1, -1);
+ }
+ isCheck= log_i(ints, "n", 0, numInts, checkEvery);
+ SETtruncate_(ints, 0);
+ checkSetContents("SETtruncate_", ints, 0, -1, -1, -1);
+
+ qh_setfree(&ints);
+}/*testSetappendSettruncate*/
+
+void testSetdelSetadd(int numInts, int *intarray, int checkEvery)
+{
+ setT *ints=qh_setnew(1);
+ int i,j,isCheck;
+
+ qh_fprintf(stderr, 8003, "\n\nTesting qh_setdelnthsorted and qh_setaddnth 1..%d. Test", numInts-1);
+ for(j=1; j<numInts; j++){ /* size 0 not valid */
+ if(log_i(ints, "j", j, numInts, MAXint)){
+ for(i= qh_setsize(ints); i<j; i++){
+ qh_setappend(&ints, intarray+i);
+ }
+ checkSetContents("qh_setappend", ints, j, 0, -1, -1);
+ for(i= 0; i<j && i<100; i++){ /* otherwise too slow */
+ isCheck= log_i(ints, "", i, numInts, checkEvery);
+ (void)isCheck; /* unused */
+ qh_setdelnthsorted(ints, i);
+ qh_setaddnth(&ints, i, intarray+i);
+ if(checkEvery==1){
+ checkSetContents("qh_setdelnthsorted qh_setaddnth", ints, j, 0, -1, -1);
+ }
+ }
+ checkSetContents("qh_setdelnthsorted qh_setaddnth 2", ints, j, 0, -1, -1);
+ }
+ }
+ qh_setfree(&ints);
+}/*testSetdelSetadd*/
+
+void testSetappendSet(int numInts, int *intarray, int checkEvery)
+{
+ setT *ints=qh_setnew(1);
+ setT *ints2;
+ int i,j,k;
+
+ qh_fprintf(stderr, 8016, "\n\nTesting qh_setappend_set 0..%d. Test", numInts-1);
+ for(j=0; j<numInts; j++){
+ if(log_i(ints, "j", j, numInts, numInts)){
+ for(i= qh_setsize(ints); i<j; i++){
+ qh_setappend(&ints, intarray+i);
+ }
+ if(checkEvery==1){
+ checkSetContents("qh_setappend", ints, j, 0, -1, -1);
+ }
+ ints2= qh_setnew(j==0 ? 0 : j-1); /* One less than needed */
+ for(i= 0; i<=j && i<=20; i++){ /* otherwise too slow */
+ if(log_i(ints, "", i, numInts, numInts)){
+ for(k= qh_setsize(ints2); k<i; k++){
+ qh_setappend(&ints2, intarray+k);
+ }
+ if(checkEvery==1){
+ checkSetContents("qh_setappend 2", ints2, i, 0, -1, -1);
+ }
+ qh_setappend_set(&ints, ints2);
+ checkSetContents("qh_setappend_set", ints, i+j, 0, (j==0 ? -1 : 0), -1);
+ qh_settruncate(ints, j);
+ if(checkEvery==1){
+ checkSetContents("qh_settruncate", ints, j, 0, -1, -1);
+ }
+ }
+ }
+ qh_setfree(&ints2);
+ }
+ }
+ qh_setfree(&ints);
+}/*testSetappendSet*/
+
+void testSetcompactCopy(int numInts, int *intarray, int checkEvery)
+{
+ setT *ints= qh_setnew(20);
+ setT *ints2= NULL;
+ int i,j,k;
+
+ qh_fprintf(stderr, 8017, "\n\nTesting qh_setcompact and qh_setcopy 0..%d. Test", numInts-1);
+ for(j=0; j<numInts; j++){
+ if(log_i(ints, "j", j, numInts, checkEvery)){
+ for(i= qh_setsize(ints); i<j; i++){ /* Test i<j to test the empty set */
+ for(k= 0; k<i%7; k++){
+ qh_setappend(&ints, NULL);
+ }
+ qh_setappend(&ints, intarray+i);
+ }
+ qh_setfree(&ints2);
+ ints2= qh_setcopy(ints, 0);
+ qh_setcompact(ints);
+ qh_setcompact(ints2);
+ checkSetContents("qh_setcompact", ints, j, 0, 0, -1);
+ checkSetContents("qh_setcompact", ints2, j, 0, 0, -1);
+ qh_setcompact(ints);
+ checkSetContents("qh_setcompact", ints, j, 0, 0, -1);
+ }
+ }
+ qh_setfree(&ints);
+ qh_setfree(&ints2);
+}/*testSetcompactCopy*/
+
+void testSetdelsortedEtc(int numInts, int *intarray, int checkEvery)
+{
+ setT *ints= qh_setnew(1);
+ setT *ints2= NULL;
+ int i,j;
+
+ qh_fprintf(stderr, 8018, "\n\nTesting qh_setdel*, qh_setaddsorted, and 0..%d. Test", numInts-1);
+ for(j=0; j<numInts; j++){
+ if(log_i(ints, "j", j, numInts, checkEvery)){
+ for(i= qh_setsize(ints); i<j; i++){ /* Test i<j to test the empty set */
+ qh_setaddsorted(&ints, intarray+i);
+ }
+ checkSetContents("qh_setaddsorted", ints, j, 0, 0, -1);
+ if(j>3){
+ qh_setdelsorted(ints, intarray+i/2);
+ checkSetContents("qh_setdelsorted", ints, j-1, 0, i/2+1, -1);
+ qh_setaddsorted(&ints, intarray+i/2);
+ checkSetContents("qh_setaddsorted i/2", ints, j, 0, 0, -1);
+ }
+ qh_setdellast(ints);
+ checkSetContents("qh_setdellast", ints, (j ? j-1 : 0), 0, -1, -1);
+ if(j>0){
+ qh_setaddsorted(&ints, intarray+j-1);
+ checkSetContents("qh_setaddsorted j-1", ints, j, 0, -1, -1);
+ }
+ if(j>4){
+ qh_setdelnthsorted(ints, i/2);
+ if (checkEvery==1)
+ checkSetContents("qh_setdelnthsorted", ints, j-1, 0, i/2+1, -1);
+ /* test qh_setdelnth and move-to-front */
+ qh_setdelsorted(ints, intarray+i/2+1);
+ checkSetContents("qh_setdelsorted 2", ints, j-2, 0, i/2+2, -1);
+ qh_setaddsorted(&ints, intarray+i/2+1);
+ if (checkEvery==1)
+ checkSetContents("qh_setaddsorted i/2+1", ints, j-1, 0, i/2+1, -1);
+ qh_setaddsorted(&ints, intarray+i/2);
+ checkSetContents("qh_setaddsorted i/2 again", ints, j, 0, -1, -1);
+ }
+ qh_setfree(&ints2);
+ ints2= qh_setcopy(ints, 0);
+ qh_setcompact(ints);
+ qh_setcompact(ints2);
+ checkSetContents("qh_setcompact", ints, j, 0, 0, -1);
+ checkSetContents("qh_setcompact 2", ints2, j, 0, 0, -1);
+ qh_setcompact(ints);
+ checkSetContents("qh_setcompact 3", ints, j, 0, 0, -1);
+ qh_setfree(&ints2);
+ }
+ }
+ qh_setfreelong(&ints);
+ if(ints){
+ qh_setfree(&ints); /* Was quick memory */
+ }
+}/*testSetdelsortedEtc*/
+
+void testSetequalInEtc(int numInts, int *intarray, int checkEvery)
+{
+ setT *ints= NULL;
+ setT *ints2= NULL;
+ setT *ints3= NULL;
+ int i,j,n;
+
+ qh_fprintf(stderr, 8019, "\n\nTesting qh_setequal*, qh_setin*, qh_setdel, qh_setdelnth, and qh_setlarger 0..%d. Test", numInts-1);
+ for(j=0; j<numInts; j++){
+ if(log_i(ints, "j", j, numInts, checkEvery)){
+ n= qh_setsize(ints);
+ qh_setlarger(&ints);
+ checkSetContents("qh_setlarger", ints, n, 0, -1, -1);
+ for(i= qh_setsize(ints); i<j; i++){ /* Test i<j to test the empty set */
+ qh_setappend(&ints, intarray+i);
+ }
+ checkSetContents("qh_setappend", ints, j, 0, -1, -1);
+ if(!qh_setequal(ints, ints)){
+ qh_fprintf(stderr, 6300, "testSetequalInEtc: set not equal to itself at length %d\n", j);
+ error_count++;
+ }
+ if(j==0 && !qh_setequal(ints, ints2)){
+ qh_fprintf(stderr, 6323, "testSetequalInEtc: empty set not equal to null set\n");
+ error_count++;
+ }
+ if(j>0){
+ if(qh_setequal(ints, ints2)){
+ qh_fprintf(stderr, 6324, "testSetequalInEtc: non-empty set equal to empty set\n", j);
+ error_count++;
+ }
+ qh_setfree(&ints3);
+ ints3= qh_setcopy(ints, 0);
+ checkSetContents("qh_setreplace", ints3, j, 0, -1, -1);
+ qh_setreplace(ints3, intarray+j/2, intarray+j/2+1);
+ if(j==1){
+ checkSetContents("qh_setreplace 2", ints3, j, j/2+1, -1, -1);
+ }else if(j==2){
+ checkSetContents("qh_setreplace 3", ints3, j, 0, j/2+1, -1);
+ }else{
+ checkSetContents("qh_setreplace 3", ints3, j, 0, j/2+1, j/2+1);
+ }
+ if(qh_setequal(ints, ints3)){
+ qh_fprintf(stderr, 6325, "testSetequalInEtc: modified set equal to original set at %d/2\n", j);
+ error_count++;
+ }
+ if(!qh_setequal_except(ints, intarray+j/2, ints3, intarray+j/2+1)){
+ qh_fprintf(stderr, 6326, "qh_setequal_except: modified set not equal to original set except modified\n", j);
+ error_count++;
+ }
+ if(qh_setequal_except(ints, intarray+j/2, ints3, intarray)){
+ qh_fprintf(stderr, 6327, "qh_setequal_except: modified set equal to original set with wrong excepts\n", j);
+ error_count++;
+ }
+ if(!qh_setequal_skip(ints, j/2, ints3, j/2)){
+ qh_fprintf(stderr, 6328, "qh_setequal_skip: modified set not equal to original set except modified\n", j);
+ error_count++;
+ }
+ if(j>2 && qh_setequal_skip(ints, j/2, ints3, 0)){
+ qh_fprintf(stderr, 6329, "qh_setequal_skip: modified set equal to original set with wrong excepts\n", j);
+ error_count++;
+ }
+ if(intarray+j/2+1!=qh_setdel(ints3, intarray+j/2+1)){
+ qh_fprintf(stderr, 6330, "qh_setdel: failed to find added element\n", j);
+ error_count++;
+ }
+ checkSetContents("qh_setdel", ints3, j-1, 0, j-1, (j==1 ? -1 : j/2+1)); /* swaps last element with deleted element */
+ if(j>3){
+ qh_setdelnth(ints3, j/2); /* Delete at the same location as the original replace, for only one out-of-order element */
+ checkSetContents("qh_setdelnth", ints3, j-2, 0, j-2, (j==2 ? -1 : j/2+1));
+ }
+ if(qh_setin(ints3, intarray+j/2)){
+ qh_fprintf(stderr, 6331, "qh_setin: found deleted element\n");
+ error_count++;
+ }
+ if(j>4 && !qh_setin(ints3, intarray+1)){
+ qh_fprintf(stderr, 6332, "qh_setin: did not find second element\n");
+ error_count++;
+ }
+ if(j>4 && !qh_setin(ints3, intarray+j-2)){
+ qh_fprintf(stderr, 6333, "qh_setin: did not find last element\n");
+ error_count++;
+ }
+ if(-1!=qh_setindex(ints2, intarray)){
+ qh_fprintf(stderr, 6334, "qh_setindex: found element in empty set\n");
+ error_count++;
+ }
+ if(-1!=qh_setindex(ints3, intarray+j/2)){
+ qh_fprintf(stderr, 6335, "qh_setindex: found deleted element in set\n");
+ error_count++;
+ }
+ if(0!=qh_setindex(ints, intarray)){
+ qh_fprintf(stderr, 6336, "qh_setindex: did not find first in set\n");
+ error_count++;
+ }
+ if(j-1!=qh_setindex(ints, intarray+j-1)){
+ qh_fprintf(stderr, 6337, "qh_setindex: did not find last in set\n");
+ error_count++;
+ }
+ }
+ qh_setfree(&ints2);
+ }
+ }
+ qh_setfree(&ints3);
+ qh_setfreelong(&ints);
+ if(ints){
+ qh_setfree(&ints); /* Was quick memory */
+ }
+}/*testSetequalInEtc*/
+
+
+void testSetlastEtc(int numInts, int *intarray, int checkEvery)
+{
+ setT *ints= NULL;
+ setT *ints2= NULL;
+ int i,j,prepend;
+
+ qh_fprintf(stderr, 8020, "\n\nTesting qh_setlast, qh_setnew_delnthsorted, qh_setunique, and qh_setzero 0..%d. Test", numInts-1);
+ for(j=0; j<numInts; j++){
+ if(log_i(ints, "j", j, numInts, checkEvery)){
+ for(i= qh_setsize(ints); i<j; i++){ /* Test i<j to test the empty set */
+ if(!qh_setunique(&ints, intarray+i)){
+ qh_fprintf(stderr, 6340, "qh_setunique: not able to append next element %d\n", i);
+ error_count++;
+ }
+ if(checkEvery==1){
+ checkSetContents("qh_setunique", ints, i+1, 0, -1, -1);
+ }
+ if(qh_setunique(&ints, intarray+i)){
+ qh_fprintf(stderr, 6341, "qh_setunique: appended next element twice %d\n", i);
+ error_count++;
+ }
+ if(qh_setunique(&ints, intarray+i/2)){
+ qh_fprintf(stderr, 6346, "qh_setunique: appended middle element twice %d/2\n", i);
+ error_count++;
+ }
+ }
+ checkSetContents("qh_setunique 2", ints, j, 0, -1, -1);
+ if(j==0 && NULL!=qh_setlast(ints)){
+ qh_fprintf(stderr, 6339, "qh_setlast: returned last element of empty set\n");
+ error_count++;
+ }
+ if(j>0){
+ if(intarray+j-1!=qh_setlast(ints)){
+ qh_fprintf(stderr, 6338, "qh_setlast: wrong last element\n");
+ error_count++;
+ }
+ prepend= (j<100 ? j/4 : 0);
+ ints2= qh_setnew_delnthsorted(ints, qh_setsize(ints), j/2, prepend);
+ if(qh_setsize(ints2)!=j+prepend-1){
+ qh_fprintf(stderr, 6345, "qh_setnew_delnthsorted: Expecting %d elements, got %d\n", j+prepend-1, qh_setsize(ints2));
+ error_count++;
+ }
+ /* Define prepended elements. Otherwise qh_setdelnthsorted may fail */
+ for(i= 0; i<prepend; i++){
+ void **p= &SETelem_(ints2, i);
+ *p= intarray+0;
+ }
+ for(i= 0; i<prepend; i++){
+ qh_setdelnthsorted(ints2, 0); /* delete undefined prefix */
+ }
+ checkSetContents("qh_setnew_delnthsorted", ints2, j-1, 0, j/2+1, -1);
+ if(j>2){
+ qh_setzero(ints2, j/2, j-1); /* max size may be j-1 */
+ if(qh_setsize(ints2)!=j-1){
+ qh_fprintf(stderr, 6342, "qh_setzero: Expecting %d elements, got %d\n", j, qh_setsize(ints2));
+ error_count++;
+ }
+ qh_setcompact(ints2);
+ checkSetContents("qh_setzero", ints2, j/2, 0, -1, -1);
+ }
+ }
+ qh_setfree(&ints2);
+ }
+ }
+ qh_setfreelong(&ints);
+ if(ints){
+ qh_setfree(&ints); /* Was quick memory */
+ }
+}/*testSetlastEtc*/
+
+void testSettemp(int numInts, int *intarray, int checkEvery)
+{
+ setT *ints= NULL;
+ setT *ints2= NULL;
+ setT *ints3= NULL;
+ int i,j;
+
+ qh_fprintf(stderr, 8021, "\n\nTesting qh_settemp* 0..%d. Test", numInts-1);
+ for(j=0; j<numInts; j++){
+ if(log_i(ints, "j", j, numInts, checkEvery)){
+ if(j<20){
+ for(i=0; i<j; i++){
+ ints2= qh_settemp(j);
+ }
+ qh_settempfree_all();
+ }
+ for(i= qh_setsize(ints); i<j; i++){ /* Test i<j to test the empty set */
+ qh_setappend(&ints, intarray+i);
+ }
+ ints2= qh_settemp(j);
+ if(j>0){
+ qh_settemppush(ints);
+ ints3= qh_settemppop();
+ if(ints!=ints3){
+ qh_fprintf(stderr, 6343, "qh_settemppop: didn't pop the push\n");
+ error_count++;
+ }
+ }
+ qh_settempfree(&ints2);
+ }
+ }
+ qh_setfreelong(&ints);
+ if(ints){
+ qh_setfree(&ints); /* Was quick memory */
+ }
+}/*testSettemp*/
+
+/* Check that a set contains count elements
+ Ranges are consecutive (e.g., 1,2,3,...) starting with first, mid, and last
+ Use -1 for missing ranges
+ Returns -1 if should check results
+*/
+int log_i(setT *set, const char *s, int i, int numInts, int checkEvery)
+{
+ int j= i;
+ int scale= 1;
+ int e= 0;
+ int *i2, **i2p;
+
+ if(*s || checkEvery==1){
+ if(i<10){
+ qh_fprintf(stderr, 8004, " %s%d", s, i);
+ }else{
+ if(i==11 && checkEvery==1){
+ qh_fprintf(stderr, 8005, "\nResults after 10: ");
+ FOREACHint_(set){
+ qh_fprintf(stderr, 8006, " %d", *i2);
+ }
+ qh_fprintf(stderr, 8007, " Continue");
+ }
+ while((j= j/10)>=1){
+ scale *= 10;
+ e++;
+ }
+ if(i==numInts-1){
+ qh_fprintf(stderr, 8008, " %s%d", s, i);
+ }else if(i==scale){
+ if(i<=1000){
+ qh_fprintf(stderr, 8010, " %s%d", s, i);
+ }else{
+ qh_fprintf(stderr, 8009, " %s1e%d", s, e);
+ }
+ }
+ }
+ }
+ if(i<1000 || i%checkEvery==0 || i== scale || i==numInts-1){
+ return 1;
+ }
+ return 0;
+}/*log_i*/
+
+/* Check that a set contains count elements
+ Ranges are consecutive (e.g., 1,2,3,...) starting with first, mid, and last
+ Use -1 for missing ranges
+*/
+void checkSetContents(const char *name, setT *set, int count, int rangeA, int rangeB, int rangeC)
+{
+
+ i2T *i2, **i2p;
+ int i2_i, i2_n;
+ int prev= -1; /* avoid warning */
+ int i;
+ int first= -3;
+ int second= -3;
+ int rangeCount=1;
+ int actualSize= 0;
+
+ qh_setcheck(set, name, 0);
+ if(set){
+ SETreturnsize_(set, actualSize); /* normally used only when speed is critical */
+ if(*qh_setendpointer(set)!=NULL){
+ qh_fprintf(stderr, 6344, "%s: qh_setendpointer(), 0x%x, is not NULL terminator of set 0x%x", name, qh_setendpointer(set), set);
+ error_count++;
+ }
+ }
+ if(actualSize!=qh_setsize(set)){
+ qh_fprintf(stderr, 6305, "%s: SETreturnsize_() returned %d while qh_setsize() returns %d\n", name, actualSize, qh_setsize(set));
+ error_count++;
+ }else if(actualSize!=count){
+ qh_fprintf(stderr, 6306, "%s: Expecting %d elements for set. Got %d elements\n", name, count, actualSize);
+ error_count++;
+ }
+ if(SETempty_(set)){
+ if(count!=0){
+ qh_fprintf(stderr, 6307, "%s: Got empty set instead of count %d, rangeA %d, rangeB %d, rangeC %d\n", name, count, rangeA, rangeB, rangeC);
+ error_count++;
+ }
+ }else{
+ /* Must be first, otherwise trips msvc 8 */
+ i2T **p= SETaddr_(set, i2T);
+ if(*p!=SETfirstt_(set, i2T)){
+ qh_fprintf(stderr, 6309, "%s: SETaddr_(set, i2t) [%p] is not the same as SETfirst_(set) [%p]\n", name, SETaddr_(set, i2T), SETfirst_(set));
+ error_count++;
+ }
+ first= *(int *)SETfirst_(set);
+ if(SETfirst_(set)!=SETfirstt_(set, i2T)){
+ qh_fprintf(stderr, 6308, "%s: SETfirst_(set) [%p] is not the same as SETfirstt_(set, i2T [%p]\n", name, SETfirst_(set), SETfirstt_(set, i2T));
+ error_count++;
+ }
+ if(qh_setsize(set)>1){
+ second= *(int *)SETsecond_(set);
+ if(SETsecond_(set)!=SETsecondt_(set, i2T)){
+ qh_fprintf(stderr, 6310, "%s: SETsecond_(set) [%p] is not the same as SETsecondt_(set, i2T) [%p]\n", name, SETsecond_(set), SETsecondt_(set, i2T));
+ error_count++;
+ }
+ }
+ }
+ /* Test first run of ints in set*/
+ i= 0;
+ FOREACHint_(set){
+ if(i2!=SETfirst_(set) && *i2!=prev+1){
+ break;
+ }
+ prev= *i2;
+ if(SETindex_(set, i2)!=i){
+ qh_fprintf(stderr, 6311, "%s: Expecting SETIndex_(set, pointer-to-%d) to be %d. Got %d\n", name, *i2, i, SETindex_(set, i2));
+ error_count++;;
+ }
+ if(i2!=SETref_(i2)){
+ qh_fprintf(stderr, 6312, "%s: SETref_(i2) [%p] does not point to i2 (the %d'th element)\n", name, SETref_(i2), i);
+ error_count++;;
+ }
+ i++;
+ }
+ FOREACHint_i_(set){
+ /* Must be first conditional, otherwise it trips up msvc 8 */
+ i2T **p= SETelemaddr_(set, i2_i, i2T);
+ if(i2!=*p){
+ qh_fprintf(stderr, 6320, "%s: SETelemaddr_(set, %d, i2T) [%p] does not point to i2\n", name, i2_i, SETelemaddr_(set, i2_i, int));
+ error_count++;;
+ }
+ if(i2_i==0){
+ if(first!=*i2){
+ qh_fprintf(stderr, 6314, "%s: First element is %d instead of SETfirst %d\n", name, *i2, first);
+ error_count++;;
+ }
+ if(rangeA!=*i2){
+ qh_fprintf(stderr, 6315, "%s: starts with %d instead of rangeA %d\n", name, *i2, rangeA);
+ error_count++;;
+ }
+ prev= rangeA;
+ }else{
+ if(i2_i==1 && second!=*i2){
+ qh_fprintf(stderr, 6316, "%s: Second element is %d instead of SETsecond %d\n", name, *i2, second);
+ error_count++;;
+ }
+ if(prev+1==*i2){
+ prev++;
+ }else{
+ if(*i2==rangeB){
+ prev= rangeB;
+ rangeB= -1;
+ rangeCount++;
+ }else if(rangeB==-1 && *i2==rangeC){
+ prev= rangeC;
+ rangeC= -1;
+ rangeCount++;
+ }else{
+ prev++;
+ qh_fprintf(stderr, 6317, "%s: Expecting %d'th element to be %d. Got %d\n", name, i2_i, prev, *i2);
+ error_count++;
+ }
+ }
+ }
+ if(i2!=SETelem_(set, i2_i)){
+ qh_fprintf(stderr, 6318, "%s: SETelem_(set, %d) [%p] is not i2 [%p] (the %d'th element)\n", name, i2_i, SETelem_(set, i2_i), i2, i2_i);
+ error_count++;;
+ }
+ if(SETelemt_(set, i2_i, i2T)!=SETelem_(set, i2_i)){ /* Normally SETelemt_ is used for generic sets */
+ qh_fprintf(stderr, 6319, "%s: SETelemt_(set, %d, i2T) [%p] is not SETelem_(set, %d) [%p] (the %d'th element)\n", name, i2_i, SETelemt_(set, i2_i, int), i2_i, SETelem_(set, i2_i), i2_i);
+ error_count++;;
+ }
+ }
+ if(error_count>=MAXerrorCount){
+ qh_fprintf(stderr, 8011, "testqset: Stop testing after %d errors\n", error_count);
+ exit(1);
+ }
+}/*checkSetContents*/
+
diff --git a/xs/src/qhull/src/testqset/testqset.pro b/xs/src/qhull/src/testqset/testqset.pro
new file mode 100644
index 000000000..3f69048aa
--- /dev/null
+++ b/xs/src/qhull/src/testqset/testqset.pro
@@ -0,0 +1,30 @@
+# -------------------------------------------------
+# testqset.pro -- Qt project file for testqset.exe
+# -------------------------------------------------
+
+include(../qhull-warn.pri)
+
+TARGET = testqset
+
+DESTDIR = ../../bin
+TEMPLATE = app
+CONFIG += console warn_on
+CONFIG -= qt
+CONFIG += qhull_warn_conversion
+
+build_pass:CONFIG(debug, debug|release){
+ OBJECTS_DIR = Debug
+}else:build_pass:CONFIG(release, debug|release){
+ OBJECTS_DIR = Release
+}
+
+INCLUDEPATH += ..
+
+SOURCES += testqset.c
+SOURCES += ../libqhull/qset.c
+SOURCES += ../libqhull/mem.c
+SOURCES += ../libqhull/usermem.c
+
+HEADERS += ../libqhull/mem.h
+HEADERS += ../libqhull/qset.h
+
diff --git a/xs/src/qhull/src/testqset_r/testqset_r.c b/xs/src/qhull/src/testqset_r/testqset_r.c
new file mode 100644
index 000000000..9a6d496e4
--- /dev/null
+++ b/xs/src/qhull/src/testqset_r/testqset_r.c
@@ -0,0 +1,890 @@
+/*<html><pre> -<a href="../libqhull/index.htm#TOC"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ testset.c -- test qset.c and its use of mem.c
+
+ The test sets are pointers to int. Normally a set is a pointer to a type (e.g., facetT, ridgeT, etc.).
+ For consistency in notation, an "int" is typedef'd to i2T
+
+Functions and macros from qset.h. Counts occurrences in this test. Does not correspond to thoroughness.
+ qh_setaddsorted -- 4 tests
+ qh_setaddnth -- 1 test
+ qh_setappend -- 7 tests
+ qh_setappend_set -- 1 test
+ qh_setappend2ndlast -- 1 test
+ qh_setcheck -- lots of tests
+ qh_setcompact -- 7 tests
+ qh_setcopy -- 3 tests
+ qh_setdel -- 1 tests
+ qh_setdellast -- 1 tests
+ qh_setdelnth -- 2 tests
+ qh_setdelnthsorted -- 2 tests
+ qh_setdelsorted -- 1 test
+ qh_setduplicate -- not testable here
+ qh_setequal -- 4 tests
+ qh_setequal_except -- 2 tests
+ qh_setequal_skip -- 2 tests
+ qh_setfree -- 11+ tests
+ qh_setfree2 -- not testable here
+ qh_setfreelong -- 2 tests
+ qh_setin -- 3 tests
+ qh_setindex -- 4 tests
+ qh_setlarger -- 1 test
+ qh_setlast -- 2 tests
+ qh_setnew -- 6 tests
+ qh_setnew_delnthsorted
+ qh_setprint -- tested elsewhere
+ qh_setreplace -- 1 test
+ qh_setsize -- 9+ tests
+ qh_settemp -- 2 tests
+ qh_settempfree -- 1 test
+ qh_settempfree_all -- 1 test
+ qh_settemppop -- 1 test
+ qh_settemppush -- 1 test
+ qh_settruncate -- 3 tests
+ qh_setunique -- 3 tests
+ qh_setzero -- 1 test
+ FOREACHint_ -- 2 test
+ FOREACHint4_
+ FOREACHint_i_ -- 1 test
+ FOREACHintreverse_
+ FOREACHintreverse12_
+ FOREACHsetelement_ -- 1 test
+ FOREACHsetelement_i_ -- 1 test
+ FOREACHsetelementreverse_ -- 1 test
+ FOREACHsetelementreverse12_ -- 1 test
+ SETelem_ -- 3 tests
+ SETelemaddr_ -- 2 tests
+ SETelemt_ -- not tested (generic)
+ SETempty_ -- 1 test
+ SETfirst_ -- 4 tests
+ SETfirstt_ -- 2 tests
+ SETindex_ -- 2 tests
+ SETref_ -- 2 tests
+ SETreturnsize_ -- 2 tests
+ SETsecond_ -- 1 test
+ SETsecondt_ -- 2 tests
+ SETtruncate_ -- 2 tests
+
+ Copyright (c) 2012-2015 C.B. Barber. All rights reserved.
+ $Id: //main/2015/qhull/src/testqset_r/testqset_r.c#5 $$Change: 2064 $
+ $DateTime: 2016/01/18 12:36:08 $$Author: bbarber $
+*/
+
+#include "libqhull_r/user_r.h" /* QHULL_CRTDBG */
+#include "libqhull_r/qset_r.h"
+#include "libqhull_r/mem_r.h"
+#include "libqhull_r/libqhull_r.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+typedef int i2T;
+#define MAXerrorCount 100 /* quit after n errors */
+
+#define FOREACHint_( ints ) FOREACHsetelement_( i2T, ints, i2)
+#define FOREACHint4_( ints ) FOREACHsetelement_( i2T, ints, i4)
+#define FOREACHint_i_( qh, ints ) FOREACHsetelement_i_( qh, i2T, ints, i2)
+#define FOREACHintreverse_( qh, ints ) FOREACHsetelementreverse_( qh, i2T, ints, i2)
+#define FOREACHintreverse12_( ints ) FOREACHsetelementreverse12_( i2T, ints, i2)
+
+enum {
+ MAXint= 0x7fffffff,
+};
+
+char prompt[]= "testqset_r N [M] [T5] -- Test reentrant qset_r.c and mem_r.c\n\
+ \n\
+ If this test fails then reentrant Qhull will not work.\n\
+ \n\
+ Test qsets of 0..N integers with a check every M iterations (default ~log10)\n\
+ Additional checking and logging if M is 1\n\
+ \n\
+ T5 turns on memory logging (qset does not log)\n\
+ \n\
+ For example:\n\
+ testqset_r 10000\n\
+";
+
+int error_count= 0; /* Global error_count. checkSetContents(qh) keeps its own error count. It exits on too many errors */
+
+/* Macros normally defined in geom.h */
+#define fmax_( a,b ) ( ( a ) < ( b ) ? ( b ) : ( a ) )
+
+/* Macros normally defined in QhullSet.h */
+
+/* Functions normally defined in user_r.h for usermem_r.c */
+
+void qh_exit(int exitcode);
+void qh_fprintf_stderr(int msgcode, const char *fmt, ... );
+void qh_free(void *mem);
+void *qh_malloc(size_t size);
+
+/* Normally defined in user_r.c */
+
+void qh_errexit(qhT *qh, int exitcode, facetT *f, ridgeT *r)
+{
+ (void)f; /* unused */
+ (void)r; /* unused */
+ (void)qh; /* unused */
+ qh_exit(exitcode);
+}
+
+/* Normally defined in userprintf.c */
+
+void qh_fprintf(qhT *qh, FILE *fp, int msgcode, const char *fmt, ... )
+{
+ static int needs_cr= 0; /* True if qh_fprintf needs a CR. testqset_r is not itself reentrant */
+
+ size_t fmtlen= strlen(fmt);
+ va_list args;
+
+ if (!fp) {
+ /* Do not use qh_fprintf_stderr. This is a standalone program */
+ if(!qh)
+ fprintf(stderr, "QH6241 qh_fprintf: fp and qh not defined for '%s'", fmt);
+ else
+ fprintf(stderr, "QH6232 qh_fprintf: fp is 0. Was wrong qh_fprintf called for '%s'", fmt);
+ qh_errexit(qh, 6232, NULL, NULL);
+ }
+ if(fmtlen>0){
+ if(fmt[fmtlen-1]=='\n'){
+ if(needs_cr && fmtlen>1){
+ fprintf(fp, "\n");
+ }
+ needs_cr= 0;
+ }else{
+ needs_cr= 1;
+ }
+ }
+ if(msgcode>=6000 && msgcode<7000){
+ fprintf(fp, "Error TQ%d ", msgcode);
+ }
+ va_start(args, fmt);
+ vfprintf(fp, fmt, args);
+ va_end(args);
+}
+
+/* Defined below in order of use */
+int main(int argc, char **argv);
+void readOptions(qhT *qh, int argc, char **argv, const char *promptstr, int *numInts, int *checkEvery, int *traceLevel);
+void setupMemory(qhT *qh, int tracelevel, int numInts, int **intarray);
+
+void testSetappendSettruncate(qhT *qh, int numInts, int *intarray, int checkEvery);
+void testSetdelSetadd(qhT *qh, int numInts, int *intarray, int checkEvery);
+void testSetappendSet(qhT *qh, int numInts, int *intarray, int checkEvery);
+void testSetcompactCopy(qhT *qh, int numInts, int *intarray, int checkEvery);
+void testSetequalInEtc(qhT *qh, int numInts, int *intarray, int checkEvery);
+void testSettemp(qhT *qh, int numInts, int *intarray, int checkEvery);
+void testSetlastEtc(qhT *qh, int numInts, int *intarray, int checkEvery);
+void testSetdelsortedEtc(qhT *qh, int numInts, int *intarray, int checkEvery);
+
+int log_i(qhT *qh, setT *set, const char *s, int i, int numInts, int checkEvery);
+void checkSetContents(qhT *qh, const char *name, setT *set, int count, int rangeA, int rangeB, int rangeC);
+
+int main(int argc, char **argv) {
+ int *intarray= NULL;
+ int numInts;
+ int checkEvery= MAXint;
+ int curlong, totlong;
+ int traceLevel= 4; /* 4 normally, no tracing since qset does not log. Option 'T5' for memory tracing */
+ qhT qh_qh;
+ qhT *qh= &qh_qh;
+
+#if defined(_MSC_VER) && defined(_DEBUG) && defined(QHULL_CRTDBG) /* user_r.h */
+ _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_DELAY_FREE_MEM_DF | _CRTDBG_LEAK_CHECK_DF | _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) );
+ _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG );
+ _CrtSetReportFile( _CRT_ERROR, _CRTDBG_FILE_STDERR );
+#endif
+
+ readOptions(qh, argc, argv, prompt, &numInts, &checkEvery, &traceLevel);
+ setupMemory(qh, traceLevel, numInts, &intarray);
+
+ testSetappendSettruncate(qh, numInts, intarray, checkEvery);
+ testSetdelSetadd(qh, numInts, intarray, checkEvery);
+ testSetappendSet(qh, numInts, intarray, checkEvery);
+ testSetcompactCopy(qh, numInts, intarray, checkEvery);
+ testSetequalInEtc(qh, numInts, intarray, checkEvery);
+ testSettemp(qh, numInts, intarray, checkEvery);
+ testSetlastEtc(qh, numInts, intarray, checkEvery);
+ testSetdelsortedEtc(qh, numInts, intarray, checkEvery);
+ printf("\n\nNot testing qh_setduplicate and qh_setfree2.\n These routines use heap-allocated set contents. See qhull tests.\n");
+
+ qh_memstatistics(qh, stdout);
+ qh_memfreeshort(qh, &curlong, &totlong);
+ if (curlong || totlong){
+ qh_fprintf(qh, stderr, 8043, "qh_memfreeshort: did not free %d bytes of long memory(%d pieces)\n", totlong, curlong);
+ error_count++;
+ }
+ if(error_count){
+ qh_fprintf(qh, stderr, 8012, "testqset: %d errors\n\n", error_count);
+ exit(1);
+ }else{
+ printf("testqset_r: OK\n\n");
+ }
+ return 0;
+}/*main*/
+
+void readOptions(qhT *qh, int argc, char **argv, const char *promptstr, int *numInts, int *checkEvery, int *traceLevel)
+{
+ long numIntsArg;
+ long checkEveryArg;
+ char *endp;
+ int isTracing= 0;
+
+ if (argc < 2 || argc > 4) {
+ printf("%s", promptstr);
+ exit(0);
+ }
+ numIntsArg= strtol(argv[1], &endp, 10);
+ if(numIntsArg<1){
+ qh_fprintf(qh, stderr, 6301, "First argument should be 1 or greater. Got '%s'\n", argv[1]);
+ exit(1);
+ }
+ if(numIntsArg>MAXint){
+ qh_fprintf(qh, stderr, 6302, "qset does not currently support 64-bit ints. Maximum count is %d\n", MAXint);
+ exit(1);
+ }
+ *numInts= (int)numIntsArg;
+
+ if(argc==3 && argv[2][0]=='T' && argv[2][1]=='5' ){
+ isTracing= 1;
+ *traceLevel= 5;
+ }
+ if(argc==4 || (argc==3 && !isTracing)){
+ checkEveryArg= strtol(argv[2], &endp, 10);
+ if(checkEveryArg<1){
+ qh_fprintf(qh, stderr, 6321, "checkEvery argument should be 1 or greater. Got '%s'\n", argv[2]);
+ exit(1);
+ }
+ if(checkEveryArg>MAXint){
+ qh_fprintf(qh, stderr, 6322, "qset does not currently support 64-bit ints. Maximum checkEvery is %d\n", MAXint);
+ exit(1);
+ }
+ if(argc==4){
+ if(argv[3][0]=='T' && argv[3][1]=='5' ){
+ isTracing= 1;
+ *traceLevel= 5;
+ }else{
+ qh_fprintf(qh, stderr, 6242, "Optional third argument must be 'T5'. Got '%s'\n", argv[3]);
+ exit(1);
+ }
+ }
+ *checkEvery= (int)checkEveryArg;
+ }
+}/*readOptions*/
+
+void setupMemory(qhT *qh, int tracelevel, int numInts, int **intarray)
+{
+ int i;
+ if(numInts<0 || numInts*(int)sizeof(int)<0){
+ qh_fprintf(qh, stderr, 6303, "qset does not currently support 64-bit ints. Integer overflow\n");
+ exit(1);
+ }
+ *intarray= qh_malloc(numInts * sizeof(int));
+ if(!*intarray){
+ qh_fprintf(qh, stderr, 6304, "Failed to allocate %d bytes of memory\n", numInts * sizeof(int));
+ exit(1);
+ }
+ for(i= 0; i<numInts; i++){
+ (*intarray)[i] =i;
+ }
+
+ qh_meminit(qh, stderr);
+ qh_meminitbuffers(qh, tracelevel, qh_MEMalign, 4 /*sizes*/, qh_MEMbufsize, qh_MEMinitbuf);
+ qh_memsize(qh, 10);
+ qh_memsize(qh, 20);
+ qh_memsize(qh, 30);
+ qh_memsize(qh, 40);
+ qh_memsetup(qh);
+
+ qh_fprintf(qh, stderr, 8001, "SETelemsize is %d bytes for pointer-to-int\n", SETelemsize);
+}/*setupMemmory*/
+
+void testSetappendSettruncate(qhT *qh, int numInts, int *intarray, int checkEvery)
+{
+ setT *ints= qh_setnew(qh, 4);
+ int i, isCheck;
+
+ qh_fprintf(qh, stderr, 8002, "\n\nTesting qh_setappend 0..%d. Test", numInts-1);
+ for(i= 0; i<numInts; i++){
+ isCheck= log_i(qh, ints, "i", i, numInts, checkEvery);
+ qh_setappend(qh, &ints, intarray+i);
+ if(isCheck){
+ checkSetContents(qh, "qh_setappend", ints, i+1, 0, -1, -1);
+ }
+ }
+
+ qh_fprintf(qh, stderr, 8014, "\n\nTesting qh_settruncate %d and 0. Test", numInts/2);
+ if(numInts>=2){
+ isCheck= log_i(qh, ints, "n", numInts/2, numInts, checkEvery);
+ qh_settruncate(qh, ints, numInts/2);
+ checkSetContents(qh, "qh_settruncate by half", ints, numInts/2, 0, -1, -1);
+ }
+ isCheck= log_i(qh, ints, "n", 0, numInts, checkEvery);
+ qh_settruncate(qh, ints, 0);
+ checkSetContents(qh, "qh_settruncate", ints, 0, -1, -1, -1);
+
+ qh_fprintf(qh, stderr, 8003, "\n\nTesting qh_setappend2ndlast 0,0..%d. Test 0", numInts-1);
+ qh_setfree(qh, &ints);
+ ints= qh_setnew(qh, 4);
+ qh_setappend(qh, &ints, intarray+0);
+ for(i= 0; i<numInts; i++){
+ isCheck= log_i(qh, ints, "i", i, numInts, checkEvery);
+ qh_setappend2ndlast(qh, &ints, intarray+i);
+ if(isCheck){
+ checkSetContents(qh, "qh_setappend2ndlast", ints, i+2, 0, 0, -1);
+ }
+ }
+ qh_fprintf(qh, stderr, 8015, "\n\nTesting SETtruncate_ %d and 0. Test", numInts/2);
+ if(numInts>=2){
+ isCheck= log_i(qh, ints, "n", numInts/2, numInts, checkEvery);
+ SETtruncate_(ints, numInts/2);
+ checkSetContents(qh, "SETtruncate_ by half", ints, numInts/2, 0, -1, -1);
+ }
+ isCheck= log_i(qh, ints, "n", 0, numInts, checkEvery);
+ SETtruncate_(ints, 0);
+ checkSetContents(qh, "SETtruncate_", ints, 0, -1, -1, -1);
+
+ qh_setfree(qh, &ints);
+}/*testSetappendSettruncate*/
+
+void testSetdelSetadd(qhT *qh, int numInts, int *intarray, int checkEvery)
+{
+ setT *ints=qh_setnew(qh, 1);
+ int i,j,isCheck;
+
+ qh_fprintf(qh, stderr, 8003, "\n\nTesting qh_setdelnthsorted and qh_setaddnth 1..%d. Test", numInts-1);
+ for(j=1; j<numInts; j++){ /* size 0 not valid */
+ if(log_i(qh, ints, "j", j, numInts, MAXint)){
+ for(i= qh_setsize(qh, ints); i<j; i++){
+ qh_setappend(qh, &ints, intarray+i);
+ }
+ checkSetContents(qh, "qh_setappend", ints, j, 0, -1, -1);
+ for(i= 0; i<j && i<100; i++){ /* otherwise too slow */
+ isCheck= log_i(qh, ints, "", i, numInts, checkEvery);
+ (void)isCheck; /* unused */
+ qh_setdelnthsorted(qh, ints, i);
+ qh_setaddnth(qh, &ints, i, intarray+i);
+ if(checkEvery==1){
+ checkSetContents(qh, "qh_setdelnthsorted qh_setaddnth", ints, j, 0, -1, -1);
+ }
+ }
+ checkSetContents(qh, "qh_setdelnthsorted qh_setaddnth 2", ints, j, 0, -1, -1);
+ }
+ }
+ qh_setfree(qh, &ints);
+}/*testSetdelSetadd*/
+
+void testSetappendSet(qhT *qh, int numInts, int *intarray, int checkEvery)
+{
+ setT *ints=qh_setnew(qh, 1);
+ setT *ints2;
+ int i,j,k;
+
+ qh_fprintf(qh, stderr, 8016, "\n\nTesting qh_setappend_set 0..%d. Test", numInts-1);
+ for(j=0; j<numInts; j++){
+ if(log_i(qh, ints, "j", j, numInts, numInts)){
+ for(i= qh_setsize(qh, ints); i<j; i++){
+ qh_setappend(qh, &ints, intarray+i);
+ }
+ if(checkEvery==1){
+ checkSetContents(qh, "qh_setappend", ints, j, 0, -1, -1);
+ }
+ ints2= qh_setnew(qh, j==0 ? 0 : j-1); /* One less than needed */
+ for(i= 0; i<=j && i<=20; i++){ /* otherwise too slow */
+ if(log_i(qh, ints, "", i, numInts, numInts)){
+ for(k= qh_setsize(qh, ints2); k<i; k++){
+ qh_setappend(qh, &ints2, intarray+k);
+ }
+ if(checkEvery==1){
+ checkSetContents(qh, "qh_setappend 2", ints2, i, 0, -1, -1);
+ }
+ qh_setappend_set(qh, &ints, ints2);
+ checkSetContents(qh, "qh_setappend_set", ints, i+j, 0, (j==0 ? -1 : 0), -1);
+ qh_settruncate(qh, ints, j);
+ if(checkEvery==1){
+ checkSetContents(qh, "qh_settruncate", ints, j, 0, -1, -1);
+ }
+ }
+ }
+ qh_setfree(qh, &ints2);
+ }
+ }
+ qh_setfree(qh, &ints);
+}/*testSetappendSet*/
+
+void testSetcompactCopy(qhT *qh, int numInts, int *intarray, int checkEvery)
+{
+ setT *ints= qh_setnew(qh, 20);
+ setT *ints2= NULL;
+ int i,j,k;
+
+ qh_fprintf(qh, stderr, 8017, "\n\nTesting qh_setcompact and qh_setcopy 0..%d. Test", numInts-1);
+ for(j=0; j<numInts; j++){
+ if(log_i(qh, ints, "j", j, numInts, checkEvery)){
+ for(i= qh_setsize(qh, ints); i<j; i++){ /* Test i<j to test the empty set */
+ for(k= 0; k<i%7; k++){
+ qh_setappend(qh, &ints, NULL);
+ }
+ qh_setappend(qh, &ints, intarray+i);
+ }
+ qh_setfree(qh, &ints2);
+ ints2= qh_setcopy(qh, ints, 0);
+ qh_setcompact(qh, ints);
+ qh_setcompact(qh, ints2);
+ checkSetContents(qh, "qh_setcompact", ints, j, 0, 0, -1);
+ checkSetContents(qh, "qh_setcompact", ints2, j, 0, 0, -1);
+ qh_setcompact(qh, ints);
+ checkSetContents(qh, "qh_setcompact", ints, j, 0, 0, -1);
+ }
+ }
+ qh_setfree(qh, &ints);
+ qh_setfree(qh, &ints2);
+}/*testSetcompactCopy*/
+
+void testSetdelsortedEtc(qhT *qh, int numInts, int *intarray, int checkEvery)
+{
+ setT *ints= qh_setnew(qh, 1);
+ setT *ints2= NULL;
+ int i,j;
+
+ qh_fprintf(qh, stderr, 8018, "\n\nTesting qh_setdel*, qh_setaddsorted, and 0..%d. Test", numInts-1);
+ for(j=0; j<numInts; j++){
+ if(log_i(qh, ints, "j", j, numInts, checkEvery)){
+ for(i= qh_setsize(qh, ints); i<j; i++){ /* Test i<j to test the empty set */
+ qh_setaddsorted(qh, &ints, intarray+i);
+ }
+ checkSetContents(qh, "qh_setaddsorted", ints, j, 0, 0, -1);
+ if(j>3){
+ qh_setdelsorted(ints, intarray+i/2);
+ checkSetContents(qh, "qh_setdelsorted", ints, j-1, 0, i/2+1, -1);
+ qh_setaddsorted(qh, &ints, intarray+i/2);
+ checkSetContents(qh, "qh_setaddsorted i/2", ints, j, 0, 0, -1);
+ }
+ qh_setdellast(ints);
+ checkSetContents(qh, "qh_setdellast", ints, (j ? j-1 : 0), 0, -1, -1);
+ if(j>0){
+ qh_setaddsorted(qh, &ints, intarray+j-1);
+ checkSetContents(qh, "qh_setaddsorted j-1", ints, j, 0, -1, -1);
+ }
+ if(j>4){
+ qh_setdelnthsorted(qh, ints, i/2);
+ if (checkEvery==1)
+ checkSetContents(qh, "qh_setdelnthsorted", ints, j-1, 0, i/2+1, -1);
+ /* test qh_setdelnth and move-to-front */
+ qh_setdelsorted(ints, intarray+i/2+1);
+ checkSetContents(qh, "qh_setdelsorted 2", ints, j-2, 0, i/2+2, -1);
+ qh_setaddsorted(qh, &ints, intarray+i/2+1);
+ if (checkEvery==1)
+ checkSetContents(qh, "qh_setaddsorted i/2+1", ints, j-1, 0, i/2+1, -1);
+ qh_setaddsorted(qh, &ints, intarray+i/2);
+ checkSetContents(qh, "qh_setaddsorted i/2 again", ints, j, 0, -1, -1);
+ }
+ qh_setfree(qh, &ints2);
+ ints2= qh_setcopy(qh, ints, 0);
+ qh_setcompact(qh, ints);
+ qh_setcompact(qh, ints2);
+ checkSetContents(qh, "qh_setcompact", ints, j, 0, 0, -1);
+ checkSetContents(qh, "qh_setcompact 2", ints2, j, 0, 0, -1);
+ qh_setcompact(qh, ints);
+ checkSetContents(qh, "qh_setcompact 3", ints, j, 0, 0, -1);
+ qh_setfree(qh, &ints2);
+ }
+ }
+ qh_setfreelong(qh, &ints);
+ if(ints){
+ qh_setfree(qh, &ints); /* Was quick memory */
+ }
+}/*testSetdelsortedEtc*/
+
+void testSetequalInEtc(qhT *qh, int numInts, int *intarray, int checkEvery)
+{
+ setT *ints= NULL;
+ setT *ints2= NULL;
+ setT *ints3= NULL;
+ int i,j,n;
+
+ qh_fprintf(qh, stderr, 8019, "\n\nTesting qh_setequal*, qh_setin*, qh_setdel, qh_setdelnth, and qh_setlarger 0..%d. Test", numInts-1);
+ for(j=0; j<numInts; j++){
+ if(log_i(qh, ints, "j", j, numInts, checkEvery)){
+ n= qh_setsize(qh, ints);
+ qh_setlarger(qh, &ints);
+ checkSetContents(qh, "qh_setlarger", ints, n, 0, -1, -1);
+ for(i= qh_setsize(qh, ints); i<j; i++){ /* Test i<j to test the empty set */
+ qh_setappend(qh, &ints, intarray+i);
+ }
+ checkSetContents(qh, "qh_setappend", ints, j, 0, -1, -1);
+ if(!qh_setequal(ints, ints)){
+ qh_fprintf(qh, stderr, 6300, "testSetequalInEtc: set not equal to itself at length %d\n", j);
+ error_count++;
+ }
+ if(j==0 && !qh_setequal(ints, ints2)){
+ qh_fprintf(qh, stderr, 6323, "testSetequalInEtc: empty set not equal to null set\n");
+ error_count++;
+ }
+ if(j>0){
+ if(qh_setequal(ints, ints2)){
+ qh_fprintf(qh, stderr, 6324, "testSetequalInEtc: non-empty set equal to empty set\n", j);
+ error_count++;
+ }
+ qh_setfree(qh, &ints3);
+ ints3= qh_setcopy(qh, ints, 0);
+ checkSetContents(qh, "qh_setreplace", ints3, j, 0, -1, -1);
+ qh_setreplace(qh, ints3, intarray+j/2, intarray+j/2+1);
+ if(j==1){
+ checkSetContents(qh, "qh_setreplace 2", ints3, j, j/2+1, -1, -1);
+ }else if(j==2){
+ checkSetContents(qh, "qh_setreplace 3", ints3, j, 0, j/2+1, -1);
+ }else{
+ checkSetContents(qh, "qh_setreplace 3", ints3, j, 0, j/2+1, j/2+1);
+ }
+ if(qh_setequal(ints, ints3)){
+ qh_fprintf(qh, stderr, 6325, "testSetequalInEtc: modified set equal to original set at %d/2\n", j);
+ error_count++;
+ }
+ if(!qh_setequal_except(ints, intarray+j/2, ints3, intarray+j/2+1)){
+ qh_fprintf(qh, stderr, 6326, "qh_setequal_except: modified set not equal to original set except modified\n", j);
+ error_count++;
+ }
+ if(qh_setequal_except(ints, intarray+j/2, ints3, intarray)){
+ qh_fprintf(qh, stderr, 6327, "qh_setequal_except: modified set equal to original set with wrong excepts\n", j);
+ error_count++;
+ }
+ if(!qh_setequal_skip(ints, j/2, ints3, j/2)){
+ qh_fprintf(qh, stderr, 6328, "qh_setequal_skip: modified set not equal to original set except modified\n", j);
+ error_count++;
+ }
+ if(j>2 && qh_setequal_skip(ints, j/2, ints3, 0)){
+ qh_fprintf(qh, stderr, 6329, "qh_setequal_skip: modified set equal to original set with wrong excepts\n", j);
+ error_count++;
+ }
+ if(intarray+j/2+1!=qh_setdel(ints3, intarray+j/2+1)){
+ qh_fprintf(qh, stderr, 6330, "qh_setdel: failed to find added element\n", j);
+ error_count++;
+ }
+ checkSetContents(qh, "qh_setdel", ints3, j-1, 0, j-1, (j==1 ? -1 : j/2+1)); /* swaps last element with deleted element */
+ if(j>3){
+ qh_setdelnth(qh, ints3, j/2); /* Delete at the same location as the original replace, for only one out-of-order element */
+ checkSetContents(qh, "qh_setdelnth", ints3, j-2, 0, j-2, (j==2 ? -1 : j/2+1));
+ }
+ if(qh_setin(ints3, intarray+j/2)){
+ qh_fprintf(qh, stderr, 6331, "qh_setin: found deleted element\n");
+ error_count++;
+ }
+ if(j>4 && !qh_setin(ints3, intarray+1)){
+ qh_fprintf(qh, stderr, 6332, "qh_setin: did not find second element\n");
+ error_count++;
+ }
+ if(j>4 && !qh_setin(ints3, intarray+j-2)){
+ qh_fprintf(qh, stderr, 6333, "qh_setin: did not find last element\n");
+ error_count++;
+ }
+ if(-1!=qh_setindex(ints2, intarray)){
+ qh_fprintf(qh, stderr, 6334, "qh_setindex: found element in empty set\n");
+ error_count++;
+ }
+ if(-1!=qh_setindex(ints3, intarray+j/2)){
+ qh_fprintf(qh, stderr, 6335, "qh_setindex: found deleted element in set\n");
+ error_count++;
+ }
+ if(0!=qh_setindex(ints, intarray)){
+ qh_fprintf(qh, stderr, 6336, "qh_setindex: did not find first in set\n");
+ error_count++;
+ }
+ if(j-1!=qh_setindex(ints, intarray+j-1)){
+ qh_fprintf(qh, stderr, 6337, "qh_setindex: did not find last in set\n");
+ error_count++;
+ }
+ }
+ qh_setfree(qh, &ints2);
+ }
+ }
+ qh_setfree(qh, &ints3);
+ qh_setfreelong(qh, &ints);
+ if(ints){
+ qh_setfree(qh, &ints); /* Was quick memory */
+ }
+}/*testSetequalInEtc*/
+
+
+void testSetlastEtc(qhT *qh, int numInts, int *intarray, int checkEvery)
+{
+ setT *ints= NULL;
+ setT *ints2= NULL;
+ int i,j,prepend;
+
+ qh_fprintf(qh, stderr, 8020, "\n\nTesting qh_setlast, qh_setnew_delnthsorted, qh_setunique, and qh_setzero 0..%d. Test", numInts-1);
+ for(j=0; j<numInts; j++){
+ if(log_i(qh, ints, "j", j, numInts, checkEvery)){
+ for(i= qh_setsize(qh, ints); i<j; i++){ /* Test i<j to test the empty set */
+ if(!qh_setunique(qh, &ints, intarray+i)){
+ qh_fprintf(qh, stderr, 6340, "qh_setunique: not able to append next element %d\n", i);
+ error_count++;
+ }
+ if(checkEvery==1){
+ checkSetContents(qh, "qh_setunique", ints, i+1, 0, -1, -1);
+ }
+ if(qh_setunique(qh, &ints, intarray+i)){
+ qh_fprintf(qh, stderr, 6341, "qh_setunique: appended next element twice %d\n", i);
+ error_count++;
+ }
+ if(qh_setunique(qh, &ints, intarray+i/2)){
+ qh_fprintf(qh, stderr, 6346, "qh_setunique: appended middle element twice %d/2\n", i);
+ error_count++;
+ }
+ }
+ checkSetContents(qh, "qh_setunique 2", ints, j, 0, -1, -1);
+ if(j==0 && NULL!=qh_setlast(ints)){
+ qh_fprintf(qh, stderr, 6339, "qh_setlast: returned last element of empty set\n");
+ error_count++;
+ }
+ if(j>0){
+ if(intarray+j-1!=qh_setlast(ints)){
+ qh_fprintf(qh, stderr, 6338, "qh_setlast: wrong last element\n");
+ error_count++;
+ }
+ prepend= (j<100 ? j/4 : 0);
+ ints2= qh_setnew_delnthsorted(qh, ints, qh_setsize(qh, ints), j/2, prepend);
+ if(qh_setsize(qh, ints2)!=j+prepend-1){
+ qh_fprintf(qh, stderr, 6345, "qh_setnew_delnthsorted: Expecting %d elements, got %d\n", j+prepend-1, qh_setsize(qh, ints2));
+ error_count++;
+ }
+ /* Define prepended elements. Otherwise qh_setdelnthsorted may fail */
+ for(i= 0; i<prepend; i++){
+ void **p= &SETelem_(ints2, i);
+ *p= intarray+0;
+ }
+ for(i= 0; i<prepend; i++){
+ qh_setdelnthsorted(qh, ints2, 0); /* delete undefined prefix */
+ }
+ checkSetContents(qh, "qh_setnew_delnthsorted", ints2, j-1, 0, j/2+1, -1);
+ if(j>2){
+ qh_setzero(qh, ints2, j/2, j-1); /* max size may be j-1 */
+ if(qh_setsize(qh, ints2)!=j-1){
+ qh_fprintf(qh, stderr, 6342, "qh_setzero: Expecting %d elements, got %d\n", j, qh_setsize(qh, ints2));
+ error_count++;
+ }
+ qh_setcompact(qh, ints2);
+ checkSetContents(qh, "qh_setzero", ints2, j/2, 0, -1, -1);
+ }
+ }
+ qh_setfree(qh, &ints2);
+ }
+ }
+ qh_setfreelong(qh, &ints);
+ if(ints){
+ qh_setfree(qh, &ints); /* Was quick memory */
+ }
+}/*testSetlastEtc*/
+
+void testSettemp(qhT *qh, int numInts, int *intarray, int checkEvery)
+{
+ setT *ints= NULL;
+ setT *ints2= NULL;
+ setT *ints3= NULL;
+ int i,j;
+
+ qh_fprintf(qh, stderr, 8021, "\n\nTesting qh_settemp* 0..%d. Test", numInts-1);
+ for(j=0; j<numInts; j++){
+ if(log_i(qh, ints, "j", j, numInts, checkEvery)){
+ if(j<20){
+ for(i=0; i<j; i++){
+ ints2= qh_settemp(qh, j);
+ }
+ qh_settempfree_all(qh);
+ }
+ for(i= qh_setsize(qh, ints); i<j; i++){ /* Test i<j to test the empty set */
+ qh_setappend(qh, &ints, intarray+i);
+ }
+ ints2= qh_settemp(qh, j);
+ if(j>0){
+ qh_settemppush(qh, ints);
+ ints3= qh_settemppop(qh);
+ if(ints!=ints3){
+ qh_fprintf(qh, stderr, 6343, "qh_settemppop: didn't pop the push\n");
+ error_count++;
+ }
+ }
+ qh_settempfree(qh, &ints2);
+ }
+ }
+ qh_setfreelong(qh, &ints);
+ if(ints){
+ qh_setfree(qh, &ints); /* Was quick memory */
+ }
+}/*testSettemp*/
+
+/* Check that a set contains count elements
+ Ranges are consecutive (e.g., 1,2,3,...) starting with first, mid, and last
+ Use -1 for missing ranges
+ Returns -1 if should check results
+*/
+int log_i(qhT *qh, setT *set, const char *s, int i, int numInts, int checkEvery)
+{
+ int j= i;
+ int scale= 1;
+ int e= 0;
+ int *i2, **i2p;
+
+ if(*s || checkEvery==1){
+ if(i<10){
+ qh_fprintf(qh, stderr, 8004, " %s%d", s, i);
+ }else{
+ if(i==11 && checkEvery==1){
+ qh_fprintf(qh, stderr, 8005, "\nResults after 10: ");
+ FOREACHint_(set){
+ qh_fprintf(qh, stderr, 8006, " %d", *i2);
+ }
+ qh_fprintf(qh, stderr, 8007, " Continue");
+ }
+ while((j= j/10)>=1){
+ scale *= 10;
+ e++;
+ }
+ if(i==numInts-1){
+ qh_fprintf(qh, stderr, 8008, " %s%d", s, i);
+ }else if(i==scale){
+ if(i<=1000){
+ qh_fprintf(qh, stderr, 8010, " %s%d", s, i);
+ }else{
+ qh_fprintf(qh, stderr, 8009, " %s1e%d", s, e);
+ }
+ }
+ }
+ }
+ if(i<1000 || i%checkEvery==0 || i== scale || i==numInts-1){
+ return 1;
+ }
+ return 0;
+}/*log_i*/
+
+/* Check that a set contains count elements
+ Ranges are consecutive (e.g., 1,2,3,...) starting with first, mid, and last
+ Use -1 for missing ranges
+*/
+void checkSetContents(qhT *qh, const char *name, setT *set, int count, int rangeA, int rangeB, int rangeC)
+{
+
+ i2T *i2, **i2p;
+ int i2_i, i2_n;
+ int prev= -1; /* avoid warning */
+ int i;
+ int first= -3;
+ int second= -3;
+ int rangeCount=1;
+ int actualSize= 0;
+
+ qh_setcheck(qh, set, name, 0);
+ if(set){
+ SETreturnsize_(set, actualSize); /* normally used only when speed is critical */
+ if(*qh_setendpointer(set)!=NULL){
+ qh_fprintf(qh, stderr, 6344, "%s: qh_setendpointer(set), 0x%x, is not NULL terminator of set 0x%x", name, qh_setendpointer(set), set);
+ error_count++;
+ }
+ }
+ if(actualSize!=qh_setsize(qh, set)){
+ qh_fprintf(qh, stderr, 6305, "%s: SETreturnsize_(qh) returned %d while qh_setsize(qh) returns %d\n", name, actualSize, qh_setsize(qh, set));
+ error_count++;
+ }else if(actualSize!=count){
+ qh_fprintf(qh, stderr, 6306, "%s: Expecting %d elements for set. Got %d elements\n", name, count, actualSize);
+ error_count++;
+ }
+ if(SETempty_(set)){
+ if(count!=0){
+ qh_fprintf(qh, stderr, 6307, "%s: Got empty set instead of count %d, rangeA %d, rangeB %d, rangeC %d\n", name, count, rangeA, rangeB, rangeC);
+ error_count++;
+ }
+ }else{
+ /* Must be first, otherwise trips msvc 8 */
+ i2T **p= SETaddr_(set, i2T);
+ if(*p!=SETfirstt_(set, i2T)){
+ qh_fprintf(qh, stderr, 6309, "%s: SETaddr_(set, i2t) [%p] is not the same as SETfirst_(set) [%p]\n", name, SETaddr_(set, i2T), SETfirst_(set));
+ error_count++;
+ }
+ first= *(int *)SETfirst_(set);
+ if(SETfirst_(set)!=SETfirstt_(set, i2T)){
+ qh_fprintf(qh, stderr, 6308, "%s: SETfirst_(set) [%p] is not the same as SETfirstt_(set, i2T [%p]\n", name, SETfirst_(set), SETfirstt_(set, i2T));
+ error_count++;
+ }
+ if(qh_setsize(qh, set)>1){
+ second= *(int *)SETsecond_(set);
+ if(SETsecond_(set)!=SETsecondt_(set, i2T)){
+ qh_fprintf(qh, stderr, 6310, "%s: SETsecond_(set) [%p] is not the same as SETsecondt_(set, i2T) [%p]\n", name, SETsecond_(set), SETsecondt_(set, i2T));
+ error_count++;
+ }
+ }
+ }
+ /* Test first run of ints in set*/
+ i= 0;
+ FOREACHint_(set){
+ if(i2!=SETfirst_(set) && *i2!=prev+1){
+ break;
+ }
+ prev= *i2;
+ if(SETindex_(set, i2)!=i){
+ qh_fprintf(qh, stderr, 6311, "%s: Expecting SETindex_(set, pointer-to-%d) to be %d. Got %d\n", name, *i2, i, SETindex_(set, i2));
+ error_count++;;
+ }
+ if(i2!=SETref_(i2)){
+ qh_fprintf(qh, stderr, 6312, "%s: SETref_(i2) [%p] does not point to i2 (the %d'th element)\n", name, SETref_(i2), i);
+ error_count++;;
+ }
+ i++;
+ }
+ FOREACHint_i_(qh, set){
+ /* Must be first conditional, otherwise it trips up msvc 8 */
+ i2T **p= SETelemaddr_(set, i2_i, i2T);
+ if(i2!=*p){
+ qh_fprintf(qh, stderr, 6320, "%s: SETelemaddr_(set, %d, i2T) [%p] does not point to i2\n", name, i2_i, SETelemaddr_(set, i2_i, int));
+ error_count++;;
+ }
+ if(i2_i==0){
+ if(first!=*i2){
+ qh_fprintf(qh, stderr, 6314, "%s: First element is %d instead of SETfirst %d\n", name, *i2, first);
+ error_count++;;
+ }
+ if(rangeA!=*i2){
+ qh_fprintf(qh, stderr, 6315, "%s: starts with %d instead of rangeA %d\n", name, *i2, rangeA);
+ error_count++;;
+ }
+ prev= rangeA;
+ }else{
+ if(i2_i==1 && second!=*i2){
+ qh_fprintf(qh, stderr, 6316, "%s: Second element is %d instead of SETsecond %d\n", name, *i2, second);
+ error_count++;;
+ }
+ if(prev+1==*i2){
+ prev++;
+ }else{
+ if(*i2==rangeB){
+ prev= rangeB;
+ rangeB= -1;
+ rangeCount++;
+ }else if(rangeB==-1 && *i2==rangeC){
+ prev= rangeC;
+ rangeC= -1;
+ rangeCount++;
+ }else{
+ prev++;
+ qh_fprintf(qh, stderr, 6317, "%s: Expecting %d'th element to be %d. Got %d\n", name, i2_i, prev, *i2);
+ error_count++;
+ }
+ }
+ }
+ if(i2!=SETelem_(set, i2_i)){
+ qh_fprintf(qh, stderr, 6318, "%s: SETelem_(set, %d) [%p] is not i2 [%p] (the %d'th element)\n", name, i2_i, SETelem_(set, i2_i), i2, i2_i);
+ error_count++;;
+ }
+ if(SETelemt_(set, i2_i, i2T)!=SETelem_(set, i2_i)){ /* Normally SETelemt_ is used for generic sets */
+ qh_fprintf(qh, stderr, 6319, "%s: SETelemt_(set, %d, i2T) [%p] is not SETelem_(set, %d) [%p] (the %d'th element)\n", name, i2_i, SETelemt_(set, i2_i, int), i2_i, SETelem_(set, i2_i), i2_i);
+ error_count++;;
+ }
+ }
+ if(error_count>=MAXerrorCount){
+ qh_fprintf(qh, stderr, 8011, "testqset: Stop testing after %d errors\n", error_count);
+ exit(1);
+ }
+}/*checkSetContents*/
+
diff --git a/xs/src/qhull/src/testqset_r/testqset_r.pro b/xs/src/qhull/src/testqset_r/testqset_r.pro
new file mode 100644
index 000000000..951e0624e
--- /dev/null
+++ b/xs/src/qhull/src/testqset_r/testqset_r.pro
@@ -0,0 +1,30 @@
+# -------------------------------------------------
+# testqset_r.pro -- Qt project file for testqset_r.exe
+# -------------------------------------------------
+
+include(../qhull-warn.pri)
+
+TARGET = testqset_r
+
+DESTDIR = ../../bin
+TEMPLATE = app
+CONFIG += console warn_on
+CONFIG -= qt
+CONFIG += qhull_warn_conversion
+
+build_pass:CONFIG(debug, debug|release){
+ OBJECTS_DIR = Debug
+}else:build_pass:CONFIG(release, debug|release){
+ OBJECTS_DIR = Release
+}
+
+INCLUDEPATH += ..
+
+SOURCES += testqset_r.c
+SOURCES += ../libqhull_r/qset_r.c
+SOURCES += ../libqhull_r/mem_r.c
+SOURCES += ../libqhull_r/usermem_r.c
+
+HEADERS += ../libqhull_r/mem_r.h
+HEADERS += ../libqhull_r/qset_r.h
+
diff --git a/xs/src/qhull/src/user_eg/user_eg.c b/xs/src/qhull/src/user_eg/user_eg.c
new file mode 100644
index 000000000..9c5fee51b
--- /dev/null
+++ b/xs/src/qhull/src/user_eg/user_eg.c
@@ -0,0 +1,330 @@
+/*<html><pre> -<a href="../libqhull/qh-user.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ user_eg.c
+ sample code for calling qhull() from an application
+
+ call with:
+
+ user_eg "cube/diamond options" "delaunay options" "halfspace options"
+
+ for example:
+
+ user_eg # return summaries
+
+ user_eg "n" "o" "Fp" # return normals, OFF, points
+
+ user_eg "n Qt" "o" "Fp" # triangulated cube
+
+ user_eg "QR0 p" "QR0 v p" "QR0 Fp" # rotate input and return points
+ # 'v' returns Voronoi
+ # transform is rotated for halfspaces
+
+ main() makes three runs of qhull.
+
+ 1) compute the convex hull of a cube
+
+ 2a) compute the Delaunay triangulation of random points
+
+ 2b) find the Delaunay triangle closest to a point.
+
+ 3) compute the halfspace intersection of a diamond
+
+ notes:
+
+ For another example, see main() in unix.c and user_eg2.c.
+ These examples, call qh_qhull() directly. They allow
+ tighter control on the code loaded with Qhull.
+
+ For a C++ example, see user_eg3/user_eg3_r.cpp
+
+ Summaries are sent to stderr if other output formats are used
+
+ compiled by 'make bin/user_eg'
+
+ see libqhull.h for data structures, macros, and user-callable functions.
+*/
+
+#define qh_QHimport
+#include "libqhull/qhull_a.h"
+
+/*-------------------------------------------------
+-internal function prototypes
+*/
+void print_summary(void);
+void makecube(coordT *points, int numpoints, int dim);
+void makeDelaunay(coordT *points, int numpoints, int dim, int seed);
+void findDelaunay(int dim);
+void makehalf(coordT *points, int numpoints, int dim);
+
+/*-------------------------------------------------
+-print_summary()
+*/
+void print_summary(void) {
+ facetT *facet;
+ int k;
+
+ printf("\n%d vertices and %d facets with normals:\n",
+ qh num_vertices, qh num_facets);
+ FORALLfacets {
+ for (k=0; k < qh hull_dim; k++)
+ printf("%6.2g ", facet->normal[k]);
+ printf("\n");
+ }
+}
+
+/*--------------------------------------------------
+-makecube- set points to vertices of cube
+ points is numpoints X dim
+*/
+void makecube(coordT *points, int numpoints, int dim) {
+ int j,k;
+ coordT *point;
+
+ for (j=0; j<numpoints; j++) {
+ point= points + j*dim;
+ for (k=dim; k--; ) {
+ if (j & ( 1 << k))
+ point[k]= 1.0;
+ else
+ point[k]= -1.0;
+ }
+ }
+} /*.makecube.*/
+
+/*--------------------------------------------------
+-makeDelaunay- set points for dim Delaunay triangulation of random points
+ points is numpoints X dim.
+notes:
+ makeDelaunay() in user_eg2.c uses qh_setdelaunay() to project points in place.
+*/
+void makeDelaunay(coordT *points, int numpoints, int dim, int seed) {
+ int j,k;
+ coordT *point, realr;
+
+ printf("seed: %d\n", seed);
+ qh_RANDOMseed_( seed);
+ for (j=0; j<numpoints; j++) {
+ point= points + j*dim;
+ for (k= 0; k < dim; k++) {
+ realr= qh_RANDOMint;
+ point[k]= 2.0 * realr/(qh_RANDOMmax+1) - 1.0;
+ }
+ }
+} /*.makeDelaunay.*/
+
+/*--------------------------------------------------
+-findDelaunay- find Delaunay triangle for [0.5,0.5,...]
+ assumes dim < 100
+notes:
+ calls qh_setdelaunay() to project the point to a parabaloid
+warning:
+ This is not implemented for tricoplanar facets ('Qt'),
+ See <a href="../html/qh-code.htm#findfacet">locate a facet with qh_findbestfacet()</a>
+*/
+void findDelaunay(int dim) {
+ int k;
+ coordT point[ 100];
+ boolT isoutside;
+ realT bestdist;
+ facetT *facet;
+ vertexT *vertex, **vertexp;
+
+ for (k= 0; k < dim; k++)
+ point[k]= 0.5;
+ qh_setdelaunay(dim+1, 1, point);
+ facet= qh_findbestfacet(point, qh_ALL, &bestdist, &isoutside);
+ if (facet->tricoplanar) {
+ fprintf(stderr, "findDelaunay: not implemented for triangulated, non-simplicial Delaunay regions (tricoplanar facet, f%d).\n",
+ facet->id);
+ qh_errexit(qh_ERRqhull, facet, NULL);
+ }
+ FOREACHvertex_(facet->vertices) {
+ for (k=0; k < dim; k++)
+ printf("%5.2f ", vertex->point[k]);
+ printf("\n");
+ }
+} /*.findDelaunay.*/
+
+/*--------------------------------------------------
+-makehalf- set points to halfspaces for a (dim)-dimensional diamond
+ points is numpoints X dim+1
+
+ each halfspace consists of dim coefficients followed by an offset
+*/
+void makehalf(coordT *points, int numpoints, int dim) {
+ int j,k;
+ coordT *point;
+
+ for (j=0; j<numpoints; j++) {
+ point= points + j*(dim+1);
+ point[dim]= -1.0; /* offset */
+ for (k=dim; k--; ) {
+ if (j & ( 1 << k))
+ point[k]= 1.0;
+ else
+ point[k]= -1.0;
+ }
+ }
+} /*.makehalf.*/
+
+#define DIM 3 /* dimension of points, must be < 31 for SIZEcube */
+#define SIZEcube (1<<DIM)
+#define SIZEdiamond (2*DIM)
+#define TOTpoints (SIZEcube + SIZEdiamond)
+
+/*--------------------------------------------------
+-main- derived from Qhull-template in user.c
+
+ see program header
+
+ this contains three runs of Qhull for convex hull, Delaunay
+ triangulation or Voronoi vertices, and halfspace intersection
+
+*/
+int main(int argc, char *argv[]) {
+ int dim= DIM; /* dimension of points */
+ int numpoints; /* number of points */
+ coordT points[(DIM+1)*TOTpoints]; /* array of coordinates for each point */
+ coordT *rows[TOTpoints];
+ boolT ismalloc= False; /* True if qhull should free points in qh_freeqhull() or reallocation */
+ char flags[250]; /* option flags for qhull, see qh-quick.htm */
+ FILE *outfile= stdout; /* output from qh_produce_output()
+ use NULL to skip qh_produce_output() */
+ FILE *errfile= stderr; /* error messages from qhull code */
+ int exitcode; /* 0 if no error from qhull */
+ facetT *facet; /* set by FORALLfacets */
+ int curlong, totlong; /* memory remaining after qh_memfreeshort */
+ int i;
+
+ QHULL_LIB_CHECK
+
+ printf("This is the output from user_eg.c\n\n\
+It shows how qhull() may be called from an application using the qhull\n\
+shared library. user_eg is not part of qhull itself. If it appears\n\
+accidently, please remove user_eg.c from your project. If it fails\n\
+immediately, user_eg.c was incorrectly linked to the reentrant library.\n\
+Also try 'user_eg T1 2>&1'\n\n");
+
+#if qh_QHpointer /* see user.h */
+ if (qh_qh){
+ printf("QH6233: Qhull link error. The global variable qh_qh was not initialized\n\
+to NULL by global.c. Please compile user_eg.c with -Dqh_QHpointer_dllimport\n\
+as well as -Dqh_QHpointer, or use libqhullstatic, or use a different tool chain.\n\n");
+ return -1;
+ }
+#endif
+
+ /*
+ Run 1: convex hull
+ */
+ printf( "\ncompute convex hull of cube after rotating input\n");
+ sprintf(flags, "qhull s Tcv %s", argc >= 2 ? argv[1] : "");
+ numpoints= SIZEcube;
+ makecube(points, numpoints, DIM);
+ for (i=numpoints; i--; )
+ rows[i]= points+dim*i;
+ qh_printmatrix(outfile, "input", rows, numpoints, dim);
+ exitcode= qh_new_qhull(dim, numpoints, points, ismalloc,
+ flags, outfile, errfile);
+ if (!exitcode) { /* if no error */
+ /* 'qh facet_list' contains the convex hull */
+ print_summary();
+ FORALLfacets {
+ /* ... your code ... */
+ }
+ }
+ qh_freeqhull(!qh_ALL); /* free long memory */
+ qh_memfreeshort(&curlong, &totlong); /* free short memory and memory allocator */
+ if (curlong || totlong)
+ fprintf(errfile, "qhull internal warning (user_eg, #1): did not free %d bytes of long memory (%d pieces)\n", totlong, curlong);
+
+ /*
+ Run 2: Delaunay triangulation
+ */
+
+ printf( "\ncompute %d-d Delaunay triangulation\n", dim);
+ sprintf(flags, "qhull s d Tcv %s", argc >= 3 ? argv[2] : "");
+ numpoints= SIZEcube;
+ makeDelaunay(points, numpoints, dim, (int)time(NULL));
+ for (i=numpoints; i--; )
+ rows[i]= points+dim*i;
+ qh_printmatrix(outfile, "input", rows, numpoints, dim);
+ exitcode= qh_new_qhull(dim, numpoints, points, ismalloc,
+ flags, outfile, errfile);
+ if (!exitcode) { /* if no error */
+ /* 'qh facet_list' contains the convex hull */
+ /* If you want a Voronoi diagram ('v') and do not request output (i.e., outfile=NULL),
+ call qh_setvoronoi_all() after qh_new_qhull(). */
+ print_summary();
+ FORALLfacets {
+ /* ... your code ... */
+ }
+ printf( "\nfind %d-d Delaunay triangle closest to [0.5, 0.5, ...]\n", dim);
+ exitcode= setjmp(qh errexit);
+ if (!exitcode) {
+ /* Trap Qhull errors in findDelaunay(). Without the setjmp(), Qhull
+ will exit() after reporting an error */
+ qh NOerrexit= False;
+ findDelaunay(DIM);
+ }
+ qh NOerrexit= True;
+ }
+#if qh_QHpointer /* see user.h */
+ {
+ qhT *oldqhA, *oldqhB;
+ coordT pointsB[DIM*TOTpoints]; /* array of coordinates for each point */
+
+ printf( "\nsave first triangulation and compute a new triangulation\n");
+ oldqhA= qh_save_qhull();
+ sprintf(flags, "qhull s d Tcv %s", argc >= 3 ? argv[2] : "");
+ numpoints= SIZEcube;
+ makeDelaunay(pointsB, numpoints, dim, (int)time(NULL)+1);
+ for (i=numpoints; i--; )
+ rows[i]= pointsB+dim*i;
+ qh_printmatrix(outfile, "input", rows, numpoints, dim);
+ exitcode= qh_new_qhull(dim, numpoints, pointsB, ismalloc,
+ flags, outfile, errfile);
+ if (!exitcode)
+ print_summary();
+ printf( "\nsave second triangulation and restore first one\n");
+ oldqhB= qh_save_qhull();
+ qh_restore_qhull(&oldqhA);
+ print_summary();
+ printf( "\nfree first triangulation and restore second one.\n");
+ qh_freeqhull(qh_ALL); /* free short and long memory used by first call */
+ /* do not use qh_memfreeshort */
+ qh_restore_qhull(&oldqhB);
+ print_summary();
+ }
+#endif
+ qh_freeqhull(!qh_ALL); /* free long memory */
+ qh_memfreeshort(&curlong, &totlong); /* free short memory and memory allocator */
+ if (curlong || totlong)
+ fprintf(errfile, "qhull internal warning (user_eg, #2): did not free %d bytes of long memory (%d pieces)\n", totlong, curlong);
+
+ /*
+ Run 3: halfspace intersection about the origin
+ */
+ printf( "\ncompute halfspace intersection about the origin for a diamond\n");
+ sprintf(flags, "qhull H0 s Tcv %s", argc >= 4 ? argv[3] : "Fp");
+ numpoints= SIZEcube;
+ makehalf(points, numpoints, dim);
+ for (i=numpoints; i--; )
+ rows[i]= points+(dim+1)*i;
+ qh_printmatrix(outfile, "input as halfspace coefficients + offsets", rows, numpoints, dim+1);
+ /* use qh_sethalfspace_all to transform the halfspaces yourself.
+ If so, set 'qh feasible_point and do not use option 'Hn,...' [it would retransform the halfspaces]
+ */
+ exitcode= qh_new_qhull(dim+1, numpoints, points, ismalloc,
+ flags, outfile, errfile);
+ if (!exitcode)
+ print_summary();
+ qh_freeqhull(!qh_ALL);
+ qh_memfreeshort(&curlong, &totlong);
+ if (curlong || totlong) /* could also check previous runs */
+ fprintf(stderr, "qhull internal warning (user_eg, #3): did not free %d bytes of long memory (%d pieces)\n",
+ totlong, curlong);
+ return exitcode;
+} /* main */
+
diff --git a/xs/src/qhull/src/user_eg/user_eg.pro b/xs/src/qhull/src/user_eg/user_eg.pro
new file mode 100644
index 000000000..9dda01009
--- /dev/null
+++ b/xs/src/qhull/src/user_eg/user_eg.pro
@@ -0,0 +1,11 @@
+# -------------------------------------------------
+# user_eg.pro -- Qt project for Qhull demonstration using shared Qhull library
+#
+# It uses reentrant Qhull
+# -------------------------------------------------
+
+include(../qhull-app-shared_r.pri)
+
+TARGET = user_eg
+
+SOURCES += user_eg_r.c
diff --git a/xs/src/qhull/src/user_eg/user_eg_r.c b/xs/src/qhull/src/user_eg/user_eg_r.c
new file mode 100644
index 000000000..21b0ccf4e
--- /dev/null
+++ b/xs/src/qhull/src/user_eg/user_eg_r.c
@@ -0,0 +1,326 @@
+/*<html><pre> -<a href="../libqhull_r/qh-user_r.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ user_eg_r.c
+ sample code for calling qhull() from an application. Uses reentrant libqhull_r
+
+ call with:
+
+ user_eg "cube/diamond options" "delaunay options" "halfspace options"
+
+ for example:
+
+ user_eg # return summaries
+
+ user_eg "n" "o" "Fp" # return normals, OFF, points
+
+ user_eg "n Qt" "o" "Fp" # triangulated cube
+
+ user_eg "QR0 p" "QR0 v p" "QR0 Fp" # rotate input and return points
+ # 'v' returns Voronoi
+ # transform is rotated for halfspaces
+
+ main() makes three runs of qhull.
+
+ 1) compute the convex hull of a cube
+
+ 2a) compute the Delaunay triangulation of random points
+
+ 2b) find the Delaunay triangle closest to a point.
+
+ 3) compute the halfspace intersection of a diamond
+
+ notes:
+
+ For another example, see main() in unix_r.c and user_eg2_r.c.
+ These examples, call qh_qhull() directly. They allow
+ tighter control on the code loaded with Qhull.
+
+ For a C++ example, see user_eg3/user_eg3_r.cpp
+
+ Summaries are sent to stderr if other output formats are used
+
+ compiled by 'make bin/user_eg'
+
+ see libqhull.h for data structures, macros, and user-callable functions.
+*/
+
+#define qh_QHimport
+#include "libqhull_r/qhull_ra.h"
+
+/*-------------------------------------------------
+-internal function prototypes
+*/
+void print_summary(qhT *qh);
+void makecube(coordT *points, int numpoints, int dim);
+void makeDelaunay(qhT *qh, coordT *points, int numpoints, int dim, int seed);
+void findDelaunay(qhT *qh, int dim);
+void makehalf(coordT *points, int numpoints, int dim);
+
+/*-------------------------------------------------
+-print_summary(qh)
+*/
+void print_summary(qhT *qh) {
+ facetT *facet;
+ int k;
+
+ printf("\n%d vertices and %d facets with normals:\n",
+ qh->num_vertices, qh->num_facets);
+ FORALLfacets {
+ for (k=0; k < qh->hull_dim; k++)
+ printf("%6.2g ", facet->normal[k]);
+ printf("\n");
+ }
+}
+
+/*--------------------------------------------------
+-makecube- set points to vertices of cube
+ points is numpoints X dim
+*/
+void makecube(coordT *points, int numpoints, int dim) {
+ int j,k;
+ coordT *point;
+
+ for (j=0; j<numpoints; j++) {
+ point= points + j*dim;
+ for (k=dim; k--; ) {
+ if (j & ( 1 << k))
+ point[k]= 1.0;
+ else
+ point[k]= -1.0;
+ }
+ }
+} /*.makecube.*/
+
+/*--------------------------------------------------
+-makeDelaunay- set points for dim Delaunay triangulation of random points
+ points is numpoints X dim.
+notes:
+ makeDelaunay() in user_eg2.c uses qh_setdelaunay() to project points in place.
+*/
+void makeDelaunay(qhT *qh, coordT *points, int numpoints, int dim, int seed) {
+ int j,k;
+ coordT *point, realr;
+
+ printf("seed: %d\n", seed);
+ qh_RANDOMseed_(qh, seed);
+ for (j=0; j<numpoints; j++) {
+ point= points + j*dim;
+ for (k= 0; k < dim; k++) {
+ realr= qh_RANDOMint;
+ point[k]= 2.0 * realr/(qh_RANDOMmax+1) - 1.0;
+ }
+ }
+} /*.makeDelaunay.*/
+
+/*--------------------------------------------------
+-findDelaunay- find Delaunay triangle for [0.5,0.5,...]
+ assumes dim < 100
+notes:
+ calls qh_setdelaunay() to project the point to a parabaloid
+warning:
+ This is not implemented for tricoplanar facets ('Qt'),
+ See <a href="../html/qh-code.htm#findfacet">locate a facet with qh_findbestfacet()</a>
+*/
+void findDelaunay(qhT *qh, int dim) {
+ int k;
+ coordT point[ 100];
+ boolT isoutside;
+ realT bestdist;
+ facetT *facet;
+ vertexT *vertex, **vertexp;
+
+ for (k= 0; k < dim; k++)
+ point[k]= 0.5;
+ qh_setdelaunay(qh, dim+1, 1, point);
+ facet= qh_findbestfacet(qh, point, qh_ALL, &bestdist, &isoutside);
+ if (facet->tricoplanar) {
+ fprintf(stderr, "findDelaunay: not implemented for triangulated, non-simplicial Delaunay regions (tricoplanar facet, f%d).\n",
+ facet->id);
+ qh_errexit(qh, qh_ERRqhull, facet, NULL);
+ }
+ FOREACHvertex_(facet->vertices) {
+ for (k=0; k < dim; k++)
+ printf("%5.2f ", vertex->point[k]);
+ printf("\n");
+ }
+} /*.findDelaunay.*/
+
+/*--------------------------------------------------
+-makehalf- set points to halfspaces for a (dim)-dimensional diamond
+ points is numpoints X dim+1
+
+ each halfspace consists of dim coefficients followed by an offset
+*/
+void makehalf(coordT *points, int numpoints, int dim) {
+ int j,k;
+ coordT *point;
+
+ for (j=0; j<numpoints; j++) {
+ point= points + j*(dim+1);
+ point[dim]= -1.0; /* offset */
+ for (k=dim; k--; ) {
+ if (j & ( 1 << k))
+ point[k]= 1.0;
+ else
+ point[k]= -1.0;
+ }
+ }
+} /*.makehalf.*/
+
+#define DIM 3 /* dimension of points, must be < 31 for SIZEcube */
+#define SIZEcube (1<<DIM)
+#define SIZEdiamond (2*DIM)
+#define TOTpoints (SIZEcube + SIZEdiamond)
+
+/*--------------------------------------------------
+-main- derived from Qhull-template in user.c
+
+ see program header
+
+ this contains three runs of Qhull for convex hull, Delaunay
+ triangulation or Voronoi vertices, and halfspace intersection
+
+*/
+int main(int argc, char *argv[]) {
+ int dim= DIM; /* dimension of points */
+ int numpoints; /* number of points */
+ coordT points[(DIM+1)*TOTpoints]; /* array of coordinates for each point */
+ coordT *rows[TOTpoints];
+ boolT ismalloc= False; /* True if qhull should free points in qh_freeqhull() or reallocation */
+ char flags[250]; /* option flags for qhull, see qh-quick.htm */
+ FILE *outfile= stdout; /* output from qh_produce_output()
+ use NULL to skip qh_produce_output() */
+ FILE *errfile= stderr; /* error messages from qhull code */
+ int exitcode; /* 0 if no error from qhull */
+ facetT *facet; /* set by FORALLfacets */
+ int curlong, totlong; /* memory remaining after qh_memfreeshort */
+ int i;
+
+ qhT qh_qh; /* Qhull's data structure. First argument of most calls */
+ qhT *qh= &qh_qh;
+
+ QHULL_LIB_CHECK
+
+ qh_zero(qh, errfile);
+
+ printf("This is the output from user_eg_r.c\n\n\
+It shows how qhull() may be called from an application using the qhull\n\
+reentrant library. user_eg is not part of qhull itself. If it appears\n\
+accidently, please remove user_eg_r.c from your project. If it fails\n\
+immediately, user_eg_r.c was incorrectly linked to the non-reentrant library.\n\
+Also try 'user_eg T1 2>&1'\n\n");
+
+ /*
+ Run 1: convex hull
+ */
+ printf( "\ncompute convex hull of cube after rotating input\n");
+ sprintf(flags, "qhull s Tcv %s", argc >= 2 ? argv[1] : "");
+ numpoints= SIZEcube;
+ makecube(points, numpoints, DIM);
+ for (i=numpoints; i--; )
+ rows[i]= points+dim*i;
+ qh_printmatrix(qh, outfile, "input", rows, numpoints, dim);
+ exitcode= qh_new_qhull(qh, dim, numpoints, points, ismalloc,
+ flags, outfile, errfile);
+ if (!exitcode) { /* if no error */
+ /* 'qh->facet_list' contains the convex hull */
+ print_summary(qh);
+ FORALLfacets {
+ /* ... your code ... */
+ }
+ }
+ qh_freeqhull(qh, !qh_ALL); /* free long memory */
+ qh_memfreeshort(qh, &curlong, &totlong); /* free short memory and memory allocator */
+ if (curlong || totlong)
+ fprintf(errfile, "qhull internal warning (user_eg, #1): did not free %d bytes of long memory (%d pieces)\n", totlong, curlong);
+
+ /*
+ Run 2: Delaunay triangulation, reusing the previous qh/qh_qh
+ */
+
+ printf( "\ncompute %d-d Delaunay triangulation\n", dim);
+ sprintf(flags, "qhull s d Tcv %s", argc >= 3 ? argv[2] : "");
+ numpoints= SIZEcube;
+ makeDelaunay(qh, points, numpoints, dim, (int)time(NULL));
+ for (i=numpoints; i--; )
+ rows[i]= points+dim*i;
+ qh_printmatrix(qh, outfile, "input", rows, numpoints, dim);
+ exitcode= qh_new_qhull(qh, dim, numpoints, points, ismalloc,
+ flags, outfile, errfile);
+ if (!exitcode) { /* if no error */
+ /* 'qh->facet_list' contains the convex hull */
+ /* If you want a Voronoi diagram ('v') and do not request output (i.e., outfile=NULL),
+ call qh_setvoronoi_all() after qh_new_qhull(). */
+ print_summary(qh);
+ FORALLfacets {
+ /* ... your code ... */
+ }
+ printf( "\nfind %d-d Delaunay triangle closest to [0.5, 0.5, ...]\n", dim);
+ exitcode= setjmp(qh->errexit);
+ if (!exitcode) {
+ /* Trap Qhull errors in findDelaunay(). Without the setjmp(), Qhull
+ will exit() after reporting an error */
+ qh->NOerrexit= False;
+ findDelaunay(qh, DIM);
+ }
+ qh->NOerrexit= True;
+ }
+ {
+ coordT pointsB[DIM*TOTpoints]; /* array of coordinates for each point */
+
+ qhT qh_qhB; /* Create a new instance of Qhull (qhB) */
+ qhT *qhB= &qh_qhB;
+ qh_zero(qhB, errfile);
+
+ printf( "\nCompute a new triangulation as a separate instance of Qhull\n");
+ sprintf(flags, "qhull s d Tcv %s", argc >= 3 ? argv[2] : "");
+ numpoints= SIZEcube;
+ makeDelaunay(qhB, pointsB, numpoints, dim, (int)time(NULL)+1);
+ for (i=numpoints; i--; )
+ rows[i]= pointsB+dim*i;
+ qh_printmatrix(qhB, outfile, "input", rows, numpoints, dim);
+ exitcode= qh_new_qhull(qhB, dim, numpoints, pointsB, ismalloc,
+ flags, outfile, errfile);
+ if (!exitcode)
+ print_summary(qhB);
+ printf( "\nFree memory allocated by the new instance of Qhull, and redisplay the old results.\n");
+ qh_freeqhull(qhB, !qh_ALL); /* free long memory */
+ qh_memfreeshort(qhB, &curlong, &totlong); /* free short memory and memory allocator */
+ if (curlong || totlong)
+ fprintf(errfile, "qhull internal warning (user_eg, #4): did not free %d bytes of long memory (%d pieces)\n", totlong, curlong);
+
+ printf( "\n\n");
+ print_summary(qh); /* The other instance is unchanged */
+ /* Exiting the block frees qh_qhB */
+ }
+ qh_freeqhull(qh, !qh_ALL); /* free long memory */
+ qh_memfreeshort(qh, &curlong, &totlong); /* free short memory and memory allocator */
+ if (curlong || totlong)
+ fprintf(errfile, "qhull internal warning (user_eg, #2): did not free %d bytes of long memory (%d pieces)\n", totlong, curlong);
+
+ /*
+ Run 3: halfspace intersection about the origin
+ */
+ printf( "\ncompute halfspace intersection about the origin for a diamond\n");
+ sprintf(flags, "qhull H0 s Tcv %s", argc >= 4 ? argv[3] : "Fp");
+ numpoints= SIZEcube;
+ makehalf(points, numpoints, dim);
+ for (i=numpoints; i--; )
+ rows[i]= points+(dim+1)*i;
+ qh_printmatrix(qh, outfile, "input as halfspace coefficients + offsets", rows, numpoints, dim+1);
+ /* use qh_sethalfspace_all to transform the halfspaces yourself.
+ If so, set 'qh->feasible_point and do not use option 'Hn,...' [it would retransform the halfspaces]
+ */
+ exitcode= qh_new_qhull(qh, dim+1, numpoints, points, ismalloc,
+ flags, outfile, errfile);
+ if (!exitcode)
+ print_summary(qh);
+ qh_freeqhull(qh, !qh_ALL);
+ qh_memfreeshort(qh, &curlong, &totlong);
+ if (curlong || totlong) /* could also check previous runs */
+ fprintf(stderr, "qhull internal warning (user_eg, #3): did not free %d bytes of long memory (%d pieces)\n",
+ totlong, curlong);
+ return exitcode;
+} /* main */
+
diff --git a/xs/src/qhull/src/user_eg2/user_eg2.c b/xs/src/qhull/src/user_eg2/user_eg2.c
new file mode 100644
index 000000000..a455f025d
--- /dev/null
+++ b/xs/src/qhull/src/user_eg2/user_eg2.c
@@ -0,0 +1,746 @@
+/*<html><pre> -<a href="../libqhull/qh-user.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ user_eg2.c
+
+ sample code for calling qhull() from an application.
+
+ See user_eg.c for a simpler method using qh_new_qhull().
+ The method used here and in unix.c gives you additional
+ control over Qhull.
+
+ See user_eg3/user_eg3_r.cpp for a C++ example
+
+ call with:
+
+ user_eg2 "triangulated cube/diamond options" "delaunay options" "halfspace options"
+
+ for example:
+
+ user_eg2 # return summaries
+
+ user_eg2 "n" "o" "Fp" # return normals, OFF, points
+
+ user_eg2 "QR0 p" "QR0 v p" "QR0 Fp" # rotate input and return points
+ # 'v' returns Voronoi
+ # transform is rotated for halfspaces
+
+ main() makes three runs of qhull.
+
+ 1) compute the convex hull of a cube, and incrementally add a diamond
+
+ 2a) compute the Delaunay triangulation of random points, and add points.
+
+ 2b) find the Delaunay triangle closest to a point.
+
+ 3) compute the halfspace intersection of a diamond, and add a cube
+
+ notes:
+
+ summaries are sent to stderr if other output formats are used
+
+ derived from unix.c and compiled by 'make bin/user_eg2'
+
+ see libqhull.h for data structures, macros, and user-callable functions.
+
+ If you want to control all output to stdio and input to stdin,
+ set the #if below to "1" and delete all lines that contain "io.c".
+ This prevents the loading of io.o. Qhull will
+ still write to 'qh ferr' (stderr) for error reporting and tracing.
+
+ Defining #if 1, also prevents user.o from being loaded.
+*/
+
+#include "libqhull/qhull_a.h"
+
+/*-------------------------------------------------
+-internal function prototypes
+*/
+void print_summary(void);
+void makecube(coordT *points, int numpoints, int dim);
+void adddiamond(coordT *points, int numpoints, int numnew, int dim);
+void makeDelaunay(coordT *points, int numpoints, int dim);
+void addDelaunay(coordT *points, int numpoints, int numnew, int dim);
+void findDelaunay(int dim);
+void makehalf(coordT *points, int numpoints, int dim);
+void addhalf(coordT *points, int numpoints, int numnew, int dim, coordT *feasible);
+
+/*-------------------------------------------------
+-print_summary()
+*/
+void print_summary(void) {
+ facetT *facet;
+ int k;
+
+ printf("\n%d vertices and %d facets with normals:\n",
+ qh num_vertices, qh num_facets);
+ FORALLfacets {
+ for (k=0; k < qh hull_dim; k++)
+ printf("%6.2g ", facet->normal[k]);
+ printf("\n");
+ }
+}
+
+/*--------------------------------------------------
+-makecube- set points to vertices of cube
+ points is numpoints X dim
+*/
+void makecube(coordT *points, int numpoints, int dim) {
+ int j,k;
+ coordT *point;
+
+ for (j=0; j<numpoints; j++) {
+ point= points + j*dim;
+ for (k=dim; k--; ) {
+ if (j & ( 1 << k))
+ point[k]= 1.0;
+ else
+ point[k]= -1.0;
+ }
+ }
+} /*.makecube.*/
+
+/*--------------------------------------------------
+-adddiamond- add diamond to convex hull
+ points is numpoints+numnew X dim.
+
+notes:
+ qh_addpoint() does not make a copy of the point coordinates.
+
+ For inside points and some outside points, qh_findbestfacet performs
+ an exhaustive search for a visible facet. Algorithms that retain
+ previously constructed hulls should be faster for on-line construction
+ of the convex hull.
+*/
+void adddiamond(coordT *points, int numpoints, int numnew, int dim) {
+ int j,k;
+ coordT *point;
+ facetT *facet;
+ boolT isoutside;
+ realT bestdist;
+
+ for (j= 0; j < numnew ; j++) {
+ point= points + (numpoints+j)*dim;
+ if (points == qh first_point) /* in case of 'QRn' */
+ qh num_points= numpoints+j+1;
+ /* qh.num_points sets the size of the points array. You may
+ allocate the points elsewhere. If so, qh_addpoint records
+ the point's address in qh other_points
+ */
+ for (k=dim; k--; ) {
+ if (j/2 == k)
+ point[k]= (j & 1) ? 2.0 : -2.0;
+ else
+ point[k]= 0.0;
+ }
+ facet= qh_findbestfacet(point, !qh_ALL, &bestdist, &isoutside);
+ if (isoutside) {
+ if (!qh_addpoint(point, facet, False))
+ break; /* user requested an early exit with 'TVn' or 'TCn' */
+ }
+ printf("%d vertices and %d facets\n",
+ qh num_vertices, qh num_facets);
+ /* qh_produce_output(); */
+ }
+ if (qh DOcheckmax)
+ qh_check_maxout();
+ else if (qh KEEPnearinside)
+ qh_nearcoplanar();
+} /*.adddiamond.*/
+
+/*--------------------------------------------------
+-makeDelaunay- set points for dim-1 Delaunay triangulation of random points
+ points is numpoints X dim. Each point is projected to a paraboloid.
+*/
+void makeDelaunay(coordT *points, int numpoints, int dim) {
+ int j,k, seed;
+ coordT *point, realr;
+
+ seed= (int)time(NULL); /* time_t to int */
+ printf("seed: %d\n", seed);
+ qh_RANDOMseed_( seed);
+ for (j=0; j<numpoints; j++) {
+ point= points + j*dim;
+ for (k= 0; k < dim-1; k++) {
+ realr= qh_RANDOMint;
+ point[k]= 2.0 * realr/(qh_RANDOMmax+1) - 1.0;
+ }
+ }
+ qh_setdelaunay(dim, numpoints, points);
+} /*.makeDelaunay.*/
+
+/*--------------------------------------------------
+-addDelaunay- add points to dim-1 Delaunay triangulation
+ points is numpoints+numnew X dim. Each point is projected to a paraboloid.
+notes:
+ qh_addpoint() does not make a copy of the point coordinates.
+
+ Since qh_addpoint() is not given a visible facet, it performs a directed
+ search of all facets. Algorithms that retain previously
+ constructed hulls may be faster.
+*/
+void addDelaunay(coordT *points, int numpoints, int numnew, int dim) {
+ int j,k;
+ coordT *point, realr;
+ facetT *facet;
+ realT bestdist;
+ boolT isoutside;
+
+ for (j= 0; j < numnew ; j++) {
+ point= points + (numpoints+j)*dim;
+ if (points == qh first_point) /* in case of 'QRn' */
+ qh num_points= numpoints+j+1;
+ /* qh.num_points sets the size of the points array. You may
+ allocate the point elsewhere. If so, qh_addpoint records
+ the point's address in qh other_points
+ */
+ for (k= 0; k < dim-1; k++) {
+ realr= qh_RANDOMint;
+ point[k]= 2.0 * realr/(qh_RANDOMmax+1) - 1.0;
+ }
+ qh_setdelaunay(dim, 1, point);
+ facet= qh_findbestfacet(point, !qh_ALL, &bestdist, &isoutside);
+ if (isoutside) {
+ if (!qh_addpoint(point, facet, False))
+ break; /* user requested an early exit with 'TVn' or 'TCn' */
+ }
+ qh_printpoint(stdout, "added point", point);
+ printf("%d points, %d extra points, %d vertices, and %d facets in total\n",
+ qh num_points, qh_setsize(qh other_points),
+ qh num_vertices, qh num_facets);
+
+ /* qh_produce_output(); */
+ }
+ if (qh DOcheckmax)
+ qh_check_maxout();
+ else if (qh KEEPnearinside)
+ qh_nearcoplanar();
+} /*.addDelaunay.*/
+
+/*--------------------------------------------------
+-findDelaunay- find Delaunay triangle for [0.5,0.5,...]
+ assumes dim < 100
+notes:
+ calls qh_setdelaunay() to project the point to a parabaloid
+warning:
+ This is not implemented for tricoplanar facets ('Qt'),
+ See <a href="../html/qh-code.htm#findfacet">locate a facet with qh_findbestfacet()</a>
+*/
+void findDelaunay(int dim) {
+ int k;
+ coordT point[ 100];
+ boolT isoutside;
+ realT bestdist;
+ facetT *facet;
+ vertexT *vertex, **vertexp;
+
+ for (k= 0; k < dim-1; k++)
+ point[k]= 0.5;
+ qh_setdelaunay(dim, 1, point);
+ facet= qh_findbestfacet(point, qh_ALL, &bestdist, &isoutside);
+ if (facet->tricoplanar) {
+ fprintf(stderr, "findDelaunay: not implemented for triangulated, non-simplicial Delaunay regions (tricoplanar facet, f%d).\n",
+ facet->id);
+ qh_errexit(qh_ERRqhull, facet, NULL);
+ }
+ FOREACHvertex_(facet->vertices) {
+ for (k=0; k < dim-1; k++)
+ printf("%5.2f ", vertex->point[k]);
+ printf("\n");
+ }
+} /*.findDelaunay.*/
+
+/*--------------------------------------------------
+-makehalf- set points to halfspaces for a (dim)-d diamond
+ points is numpoints X dim+1
+
+ each halfspace consists of dim coefficients followed by an offset
+*/
+void makehalf(coordT *points, int numpoints, int dim) {
+ int j,k;
+ coordT *point;
+
+ for (j=0; j<numpoints; j++) {
+ point= points + j*(dim+1);
+ point[dim]= -1.0; /* offset */
+ for (k=dim; k--; ) {
+ if (j & ( 1 << k))
+ point[k]= 1.0;
+ else
+ point[k]= -1.0;
+ }
+ }
+} /*.makehalf.*/
+
+/*--------------------------------------------------
+-addhalf- add halfspaces for a (dim)-d cube to the intersection
+ points is numpoints+numnew X dim+1
+notes:
+ assumes dim < 100.
+
+ For makehalf(), points is the initial set of halfspaces with offsets.
+ It is transformed by qh_sethalfspace_all into a
+ (dim)-d set of newpoints. Qhull computed the convex hull of newpoints -
+ this is equivalent to the halfspace intersection of the
+ orginal halfspaces.
+
+ For addhalf(), the remainder of points stores the transforms of
+ the added halfspaces. Qhull computes the convex hull of newpoints
+ and the added points. qh_addpoint() does not make a copy of these points.
+
+ Since halfspace intersection is equivalent to a convex hull,
+ qh_findbestfacet may perform an exhaustive search
+ for a visible facet. Algorithms that retain previously constructed
+ intersections should be faster for on-line construction.
+*/
+void addhalf(coordT *points, int numpoints, int numnew, int dim, coordT *feasible) {
+ int j,k;
+ coordT *point, normal[100], offset, *next;
+ facetT *facet;
+ boolT isoutside;
+ realT bestdist;
+
+ for (j= 0; j < numnew ; j++) {
+ offset= -1.0;
+ for (k=dim; k--; ) {
+ if (j/2 == k) {
+ normal[k]= sqrt((coordT)dim); /* to normalize as in makehalf */
+ if (j & 1)
+ normal[k]= -normal[k];
+ }else
+ normal[k]= 0.0;
+ }
+ point= points + (numpoints+j)* (dim+1); /* does not use point[dim] */
+ qh_sethalfspace(dim, point, &next, normal, &offset, feasible);
+ facet= qh_findbestfacet(point, !qh_ALL, &bestdist, &isoutside);
+ if (isoutside) {
+ if (!qh_addpoint(point, facet, False))
+ break; /* user requested an early exit with 'TVn' or 'TCn' */
+ }
+ qh_printpoint(stdout, "added offset -1 and normal", normal);
+ printf("%d points, %d extra points, %d vertices, and %d facets in total\n",
+ qh num_points, qh_setsize(qh other_points),
+ qh num_vertices, qh num_facets);
+ /* qh_produce_output(); */
+ }
+ if (qh DOcheckmax)
+ qh_check_maxout();
+ else if (qh KEEPnearinside)
+ qh_nearcoplanar();
+} /*.addhalf.*/
+
+#define DIM 3 /* dimension of points, must be < 31 for SIZEcube */
+#define SIZEcube (1<<DIM)
+#define SIZEdiamond (2*DIM)
+#define TOTpoints (SIZEcube + SIZEdiamond)
+
+/*--------------------------------------------------
+-main- derived from unix.c
+
+ see program header
+
+ this contains three runs of Qhull for convex hull, Delaunay
+ triangulation or Voronoi vertices, and halfspace intersection
+
+*/
+int main(int argc, char *argv[]) {
+ boolT ismalloc;
+ int curlong, totlong, exitcode;
+ char options [2000];
+
+ QHULL_LIB_CHECK
+
+ printf("This is the output from user_eg2.c\n\n\
+It shows how qhull() may be called from an application in the same way as\n\
+qconvex. It is not part of qhull itself. If it appears accidently,\n\
+please remove user_eg2.c from your project.\n\n");
+
+#if qh_QHpointer /* see user.h */
+ if (qh_qh){
+ printf("QH6237: Qhull link error. The global variable qh_qh was not initialized\n\
+ to NULL by global.c. Please compile user_eg2.c with -Dqh_QHpointer_dllimport\n\
+ as well as -Dqh_QHpointer, or use libqhullstatic, or use a different tool chain.\n\n");
+ return -1;
+ }
+#endif
+
+
+ ismalloc= False; /* True if qh_freeqhull should 'free(array)' */
+ /*
+ Run 1: convex hull
+ */
+ qh_init_A(stdin, stdout, stderr, 0, NULL);
+ exitcode= setjmp(qh errexit);
+ if (!exitcode) {
+ coordT array[TOTpoints][DIM];
+
+ strcat(qh rbox_command, "user_eg cube");
+ sprintf(options, "qhull s Tcv Q11 %s ", argc >= 2 ? argv[1] : "");
+ qh_initflags(options);
+ printf( "\ncompute triangulated convex hull of cube after rotating input\n");
+ makecube(array[0], SIZEcube, DIM);
+ qh_init_B(array[0], SIZEcube, DIM, ismalloc);
+ qh_qhull();
+ qh_check_output();
+ qh_triangulate(); /* requires option 'Q11' if want to add points */
+ print_summary();
+ if (qh VERIFYoutput && !qh STOPpoint && !qh STOPcone)
+ qh_check_points();
+ printf( "\nadd points in a diamond\n");
+ adddiamond(array[0], SIZEcube, SIZEdiamond, DIM);
+ qh_check_output();
+ print_summary();
+ qh_produce_output(); /* delete this line to help avoid io.c */
+ if (qh VERIFYoutput && !qh STOPpoint && !qh STOPcone)
+ qh_check_points();
+ }
+ qh NOerrexit= True;
+ qh_freeqhull(!qh_ALL);
+ qh_memfreeshort(&curlong, &totlong);
+ if (curlong || totlong)
+ fprintf(stderr, "qhull warning (user_eg, run 1): did not free %d bytes of long memory (%d pieces)\n",
+ totlong, curlong);
+
+ /*
+ Run 2: Delaunay triangulation
+ */
+ qh_init_A(stdin, stdout, stderr, 0, NULL);
+ exitcode= setjmp(qh errexit);
+ if (!exitcode) {
+ coordT array[TOTpoints][DIM];
+
+ strcat(qh rbox_command, "user_eg Delaunay");
+ sprintf(options, "qhull s d Tcv %s", argc >= 3 ? argv[2] : "");
+ qh_initflags(options);
+ printf( "\ncompute %d-d Delaunay triangulation\n", DIM-1);
+ makeDelaunay(array[0], SIZEcube, DIM);
+ /* Instead of makeDelaunay with qh_setdelaunay, you may
+ produce a 2-d array of points, set DIM to 2, and set
+ qh PROJECTdelaunay to True. qh_init_B will call
+ qh_projectinput to project the points to the paraboloid
+ and add a point "at-infinity".
+ */
+ qh_init_B(array[0], SIZEcube, DIM, ismalloc);
+ qh_qhull();
+ /* If you want Voronoi ('v') without qh_produce_output(), call
+ qh_setvoronoi_all() after qh_qhull() */
+ qh_check_output();
+ print_summary();
+ qh_produce_output(); /* delete this line to help avoid io.c */
+ if (qh VERIFYoutput && !qh STOPpoint && !qh STOPcone)
+ qh_check_points();
+ printf( "\nadd points to triangulation\n");
+ addDelaunay(array[0], SIZEcube, SIZEdiamond, DIM);
+ qh_check_output();
+ qh_produce_output(); /* delete this line to help avoid io.c */
+ if (qh VERIFYoutput && !qh STOPpoint && !qh STOPcone)
+ qh_check_points();
+ printf( "\nfind Delaunay triangle closest to [0.5, 0.5, ...]\n");
+ findDelaunay(DIM);
+ }
+ qh NOerrexit= True;
+ qh_freeqhull(!qh_ALL);
+ qh_memfreeshort(&curlong, &totlong);
+ if (curlong || totlong)
+ fprintf(stderr, "qhull warning (user_eg, run 2): did not free %d bytes of long memory (%d pieces)\n",
+ totlong, curlong);
+
+ /*
+ Run 3: halfspace intersection
+ */
+ qh_init_A(stdin, stdout, stderr, 0, NULL);
+ exitcode= setjmp(qh errexit);
+ if (!exitcode) {
+ coordT array[TOTpoints][DIM+1]; /* +1 for halfspace offset */
+ pointT *points;
+
+ strcat(qh rbox_command, "user_eg halfspaces");
+ sprintf(options, "qhull H0 s Tcv %s", argc >= 4 ? argv[3] : "");
+ qh_initflags(options);
+ printf( "\ncompute halfspace intersection about the origin for a diamond\n");
+ makehalf(array[0], SIZEcube, DIM);
+ qh_setfeasible(DIM); /* from io.c, sets qh feasible_point from 'Hn,n' */
+ /* you may malloc and set qh feasible_point directly. It is only used for
+ option 'Fp' */
+ points= qh_sethalfspace_all( DIM+1, SIZEcube, array[0], qh feasible_point);
+ qh_init_B(points, SIZEcube, DIM, True); /* qh_freeqhull frees points */
+ qh_qhull();
+ qh_check_output();
+ qh_produce_output(); /* delete this line to help avoid io.c */
+ if (qh VERIFYoutput && !qh STOPpoint && !qh STOPcone)
+ qh_check_points();
+ printf( "\nadd halfspaces for cube to intersection\n");
+ addhalf(array[0], SIZEcube, SIZEdiamond, DIM, qh feasible_point);
+ qh_check_output();
+ qh_produce_output(); /* delete this line to help avoid io.c */
+ if (qh VERIFYoutput && !qh STOPpoint && !qh STOPcone)
+ qh_check_points();
+ }
+ qh NOerrexit= True;
+ qh NOerrexit= True;
+ qh_freeqhull(!qh_ALL);
+ qh_memfreeshort(&curlong, &totlong);
+ if (curlong || totlong)
+ fprintf(stderr, "qhull warning (user_eg, run 3): did not free %d bytes of long memory (%d pieces)\n",
+ totlong, curlong);
+ return exitcode;
+} /* main */
+
+#if 1 /* use 1 to prevent loading of io.o and user.o */
+/*-------------------------------------------
+-errexit- return exitcode to system after an error
+ assumes exitcode non-zero
+ prints useful information
+ see qh_errexit2() in libqhull.c for 2 facets
+*/
+void qh_errexit(int exitcode, facetT *facet, ridgeT *ridge) {
+ QHULL_UNUSED(facet);
+ QHULL_UNUSED(ridge);
+
+ if (qh ERREXITcalled) {
+ fprintf(qh ferr, "qhull error while processing previous error. Exit program\n");
+ exit(1);
+ }
+ qh ERREXITcalled= True;
+ if (!qh QHULLfinished)
+ qh hulltime= (unsigned)clock() - qh hulltime;
+ fprintf(qh ferr, "\nWhile executing: %s | %s\n", qh rbox_command, qh qhull_command);
+ fprintf(qh ferr, "Options selected:\n%s\n", qh qhull_options);
+ if (qh furthest_id >= 0) {
+ fprintf(qh ferr, "\nLast point added to hull was p%d", qh furthest_id);
+ if (zzval_(Ztotmerge))
+ fprintf(qh ferr, " Last merge was #%d.", zzval_(Ztotmerge));
+ if (qh QHULLfinished)
+ fprintf(qh ferr, "\nQhull has finished constructing the hull.");
+ else if (qh POSTmerging)
+ fprintf(qh ferr, "\nQhull has started post-merging");
+ fprintf(qh ferr, "\n\n");
+ }
+ if (qh NOerrexit) {
+ fprintf(qh ferr, "qhull error while ending program. Exit program\n");
+ exit(1);
+ }
+ if (!exitcode)
+ exitcode= qh_ERRqhull;
+ qh NOerrexit= True;
+ longjmp(qh errexit, exitcode);
+} /* errexit */
+
+
+/*-------------------------------------------
+-errprint- prints out the information of the erroneous object
+ any parameter may be NULL, also prints neighbors and geomview output
+*/
+void qh_errprint(const char *string, facetT *atfacet, facetT *otherfacet, ridgeT *atridge, vertexT *atvertex) {
+
+ fprintf(qh ferr, "%s facets f%d f%d ridge r%d vertex v%d\n",
+ string, getid_(atfacet), getid_(otherfacet), getid_(atridge),
+ getid_(atvertex));
+} /* errprint */
+
+
+void qh_printfacetlist(facetT *facetlist, setT *facets, boolT printall) {
+ facetT *facet, **facetp;
+
+ /* remove these calls to help avoid io.c */
+ qh_printbegin(qh ferr, qh_PRINTfacets, facetlist, facets, printall);/*io.c*/
+ FORALLfacet_(facetlist) /*io.c*/
+ qh_printafacet(qh ferr, qh_PRINTfacets, facet, printall); /*io.c*/
+ FOREACHfacet_(facets) /*io.c*/
+ qh_printafacet(qh ferr, qh_PRINTfacets, facet, printall); /*io.c*/
+ qh_printend(qh ferr, qh_PRINTfacets, facetlist, facets, printall); /*io.c*/
+
+ FORALLfacet_(facetlist)
+ fprintf( qh ferr, "facet f%d\n", facet->id);
+} /* printfacetlist */
+
+/* qh_printhelp_degenerate( fp )
+ prints descriptive message for precision error
+
+ notes:
+ no message if qh_QUICKhelp
+*/
+void qh_printhelp_degenerate(FILE *fp) {
+
+ if (qh MERGEexact || qh PREmerge || qh JOGGLEmax < REALmax/2)
+ qh_fprintf(fp, 9368, "\n\
+A Qhull error has occurred. Qhull should have corrected the above\n\
+precision error. Please send the input and all of the output to\n\
+qhull_bug@qhull.org\n");
+ else if (!qh_QUICKhelp) {
+ qh_fprintf(fp, 9369, "\n\
+Precision problems were detected during construction of the convex hull.\n\
+This occurs because convex hull algorithms assume that calculations are\n\
+exact, but floating-point arithmetic has roundoff errors.\n\
+\n\
+To correct for precision problems, do not use 'Q0'. By default, Qhull\n\
+selects 'C-0' or 'Qx' and merges non-convex facets. With option 'QJ',\n\
+Qhull joggles the input to prevent precision problems. See \"Imprecision\n\
+in Qhull\" (qh-impre.htm).\n\
+\n\
+If you use 'Q0', the output may include\n\
+coplanar ridges, concave ridges, and flipped facets. In 4-d and higher,\n\
+Qhull may produce a ridge with four neighbors or two facets with the same \n\
+vertices. Qhull reports these events when they occur. It stops when a\n\
+concave ridge, flipped facet, or duplicate facet occurs.\n");
+#if REALfloat
+ qh_fprintf(fp, 9370, "\
+\n\
+Qhull is currently using single precision arithmetic. The following\n\
+will probably remove the precision problems:\n\
+ - recompile qhull for realT precision(#define REALfloat 0 in user.h).\n");
+#endif
+ if (qh DELAUNAY && !qh SCALElast && qh MAXabs_coord > 1e4)
+ qh_fprintf(fp, 9371, "\
+\n\
+When computing the Delaunay triangulation of coordinates > 1.0,\n\
+ - use 'Qbb' to scale the last coordinate to [0,m] (max previous coordinate)\n");
+ if (qh DELAUNAY && !qh ATinfinity)
+ qh_fprintf(fp, 9372, "\
+When computing the Delaunay triangulation:\n\
+ - use 'Qz' to add a point at-infinity. This reduces precision problems.\n");
+
+ qh_fprintf(fp, 9373, "\
+\n\
+If you need triangular output:\n\
+ - use option 'Qt' to triangulate the output\n\
+ - use option 'QJ' to joggle the input points and remove precision errors\n\
+ - use option 'Ft'. It triangulates non-simplicial facets with added points.\n\
+\n\
+If you must use 'Q0',\n\
+try one or more of the following options. They can not guarantee an output.\n\
+ - use 'QbB' to scale the input to a cube.\n\
+ - use 'Po' to produce output and prevent partitioning for flipped facets\n\
+ - use 'V0' to set min. distance to visible facet as 0 instead of roundoff\n\
+ - use 'En' to specify a maximum roundoff error less than %2.2g.\n\
+ - options 'Qf', 'Qbb', and 'QR0' may also help\n",
+ qh DISTround);
+ qh_fprintf(fp, 9374, "\
+\n\
+To guarantee simplicial output:\n\
+ - use option 'Qt' to triangulate the output\n\
+ - use option 'QJ' to joggle the input points and remove precision errors\n\
+ - use option 'Ft' to triangulate the output by adding points\n\
+ - use exact arithmetic (see \"Imprecision in Qhull\", qh-impre.htm)\n\
+");
+ }
+} /* printhelp_degenerate */
+
+
+/* qh_printhelp_narrowhull( minangle )
+ Warn about a narrow hull
+
+ notes:
+ Alternatively, reduce qh_WARNnarrow in user.h
+
+*/
+void qh_printhelp_narrowhull(FILE *fp, realT minangle) {
+
+ qh_fprintf(fp, 9375, "qhull precision warning: \n\
+The initial hull is narrow (cosine of min. angle is %.16f).\n\
+A coplanar point may lead to a wide facet. Options 'QbB' (scale to unit box)\n\
+or 'Qbb' (scale last coordinate) may remove this warning. Use 'Pp' to skip\n\
+this warning. See 'Limitations' in qh-impre.htm.\n",
+ -minangle); /* convert from angle between normals to angle between facets */
+} /* printhelp_narrowhull */
+
+/* qh_printhelp_singular
+ prints descriptive message for singular input
+*/
+void qh_printhelp_singular(FILE *fp) {
+ facetT *facet;
+ vertexT *vertex, **vertexp;
+ realT min, max, *coord, dist;
+ int i,k;
+
+ qh_fprintf(fp, 9376, "\n\
+The input to qhull appears to be less than %d dimensional, or a\n\
+computation has overflowed.\n\n\
+Qhull could not construct a clearly convex simplex from points:\n",
+ qh hull_dim);
+ qh_printvertexlist(fp, "", qh facet_list, NULL, qh_ALL);
+ if (!qh_QUICKhelp)
+ qh_fprintf(fp, 9377, "\n\
+The center point is coplanar with a facet, or a vertex is coplanar\n\
+with a neighboring facet. The maximum round off error for\n\
+computing distances is %2.2g. The center point, facets and distances\n\
+to the center point are as follows:\n\n", qh DISTround);
+ qh_printpointid(fp, "center point", qh hull_dim, qh interior_point, -1);
+ qh_fprintf(fp, 9378, "\n");
+ FORALLfacets {
+ qh_fprintf(fp, 9379, "facet");
+ FOREACHvertex_(facet->vertices)
+ qh_fprintf(fp, 9380, " p%d", qh_pointid(vertex->point));
+ zinc_(Zdistio);
+ qh_distplane(qh interior_point, facet, &dist);
+ qh_fprintf(fp, 9381, " distance= %4.2g\n", dist);
+ }
+ if (!qh_QUICKhelp) {
+ if (qh HALFspace)
+ qh_fprintf(fp, 9382, "\n\
+These points are the dual of the given halfspaces. They indicate that\n\
+the intersection is degenerate.\n");
+ qh_fprintf(fp, 9383,"\n\
+These points either have a maximum or minimum x-coordinate, or\n\
+they maximize the determinant for k coordinates. Trial points\n\
+are first selected from points that maximize a coordinate.\n");
+ if (qh hull_dim >= qh_INITIALmax)
+ qh_fprintf(fp, 9384, "\n\
+Because of the high dimension, the min x-coordinate and max-coordinate\n\
+points are used if the determinant is non-zero. Option 'Qs' will\n\
+do a better, though much slower, job. Instead of 'Qs', you can change\n\
+the points by randomly rotating the input with 'QR0'.\n");
+ }
+ qh_fprintf(fp, 9385, "\nThe min and max coordinates for each dimension are:\n");
+ for (k=0; k < qh hull_dim; k++) {
+ min= REALmax;
+ max= -REALmin;
+ for (i=qh num_points, coord= qh first_point+k; i--; coord += qh hull_dim) {
+ maximize_(max, *coord);
+ minimize_(min, *coord);
+ }
+ qh_fprintf(fp, 9386, " %d: %8.4g %8.4g difference= %4.4g\n", k, min, max, max-min);
+ }
+ if (!qh_QUICKhelp) {
+ qh_fprintf(fp, 9387, "\n\
+If the input should be full dimensional, you have several options that\n\
+may determine an initial simplex:\n\
+ - use 'QJ' to joggle the input and make it full dimensional\n\
+ - use 'QbB' to scale the points to the unit cube\n\
+ - use 'QR0' to randomly rotate the input for different maximum points\n\
+ - use 'Qs' to search all points for the initial simplex\n\
+ - use 'En' to specify a maximum roundoff error less than %2.2g.\n\
+ - trace execution with 'T3' to see the determinant for each point.\n",
+ qh DISTround);
+#if REALfloat
+ qh_fprintf(fp, 9388, "\
+ - recompile qhull for realT precision(#define REALfloat 0 in libqhull.h).\n");
+#endif
+ qh_fprintf(fp, 9389, "\n\
+If the input is lower dimensional:\n\
+ - use 'QJ' to joggle the input and make it full dimensional\n\
+ - use 'Qbk:0Bk:0' to delete coordinate k from the input. You should\n\
+ pick the coordinate with the least range. The hull will have the\n\
+ correct topology.\n\
+ - determine the flat containing the points, rotate the points\n\
+ into a coordinate plane, and delete the other coordinates.\n\
+ - add one or more points to make the input full dimensional.\n\
+");
+ if (qh DELAUNAY && !qh ATinfinity)
+ qh_fprintf(fp, 9390, "\n\n\
+This is a Delaunay triangulation and the input is co-circular or co-spherical:\n\
+ - use 'Qz' to add a point \"at infinity\" (i.e., above the paraboloid)\n\
+ - or use 'QJ' to joggle the input and avoid co-circular data\n");
+ }
+} /* printhelp_singular */
+
+
+/*-----------------------------------------
+-user_memsizes- allocate up to 10 additional, quick allocation sizes
+*/
+void qh_user_memsizes(void) {
+
+ /* qh_memsize(size); */
+} /* user_memsizes */
+
+#endif
diff --git a/xs/src/qhull/src/user_eg2/user_eg2.pro b/xs/src/qhull/src/user_eg2/user_eg2.pro
new file mode 100644
index 000000000..c841bfe13
--- /dev/null
+++ b/xs/src/qhull/src/user_eg2/user_eg2.pro
@@ -0,0 +1,11 @@
+# -------------------------------------------------
+# user_eg2.pro -- Qt project for Qhull demonstration using the static Qhull library
+#
+# It uses reentrant Qhull
+# -------------------------------------------------
+
+include(../qhull-app-c_r.pri)
+
+TARGET = user_eg2
+
+SOURCES += user_eg2_r.c
diff --git a/xs/src/qhull/src/user_eg2/user_eg2_r.c b/xs/src/qhull/src/user_eg2/user_eg2_r.c
new file mode 100644
index 000000000..2f8b4e6c7
--- /dev/null
+++ b/xs/src/qhull/src/user_eg2/user_eg2_r.c
@@ -0,0 +1,742 @@
+/*<html><pre> -<a href="../libqhull_r/qh-user_r.htm"
+ >-------------------------------</a><a name="TOP">-</a>
+
+ user_eg2_r.c
+
+ sample code for calling qhull() from an application.
+
+ See user_eg_r.c for a simpler method using qh_new_qhull().
+ The method used here and in unix_r.c gives you additional
+ control over Qhull.
+
+ See user_eg3/user_eg3_r.cpp for a C++ example
+
+ call with:
+
+ user_eg2 "triangulated cube/diamond options" "delaunay options" "halfspace options"
+
+ for example:
+
+ user_eg2 # return summaries
+
+ user_eg2 "n" "o" "Fp" # return normals, OFF, points
+
+ user_eg2 "QR0 p" "QR0 v p" "QR0 Fp" # rotate input and return points
+ # 'v' returns Voronoi
+ # transform is rotated for halfspaces
+
+ main() makes three runs of qhull.
+
+ 1) compute the convex hull of a cube, and incrementally add a diamond
+
+ 2a) compute the Delaunay triangulation of random points, and add points.
+
+ 2b) find the Delaunay triangle closest to a point.
+
+ 3) compute the halfspace intersection of a diamond, and add a cube
+
+ notes:
+
+ summaries are sent to stderr if other output formats are used
+
+ derived from unix.c and compiled by 'make bin/user_eg2'
+
+ see libqhull.h for data structures, macros, and user-callable functions.
+
+ If you want to control all output to stdio and input to stdin,
+ set the #if below to "1" and delete all lines that contain "io.c".
+ This prevents the loading of io.o. Qhull will
+ still write to 'qh->ferr' (stderr) for error reporting and tracing.
+
+ Defining #if 1, also prevents user.o from being loaded.
+*/
+
+#include "libqhull_r/qhull_ra.h"
+
+/*-------------------------------------------------
+-internal function prototypes
+*/
+void print_summary(qhT *qh);
+void makecube(coordT *points, int numpoints, int dim);
+void adddiamond(qhT *qh, coordT *points, int numpoints, int numnew, int dim);
+void makeDelaunay(qhT *qh, coordT *points, int numpoints, int dim);
+void addDelaunay(qhT *qh, coordT *points, int numpoints, int numnew, int dim);
+void findDelaunay(qhT *qh, int dim);
+void makehalf(coordT *points, int numpoints, int dim);
+void addhalf(qhT *qh, coordT *points, int numpoints, int numnew, int dim, coordT *feasible);
+
+/*-------------------------------------------------
+-print_summary(qh)
+*/
+void print_summary(qhT *qh) {
+ facetT *facet;
+ int k;
+
+ printf("\n%d vertices and %d facets with normals:\n",
+ qh->num_vertices, qh->num_facets);
+ FORALLfacets {
+ for (k=0; k < qh->hull_dim; k++)
+ printf("%6.2g ", facet->normal[k]);
+ printf("\n");
+ }
+}
+
+/*--------------------------------------------------
+-makecube- set points to vertices of cube
+ points is numpoints X dim
+*/
+void makecube(coordT *points, int numpoints, int dim) {
+ int j,k;
+ coordT *point;
+
+ for (j=0; j<numpoints; j++) {
+ point= points + j*dim;
+ for (k=dim; k--; ) {
+ if (j & ( 1 << k))
+ point[k]= 1.0;
+ else
+ point[k]= -1.0;
+ }
+ }
+} /*.makecube.*/
+
+/*--------------------------------------------------
+-adddiamond- add diamond to convex hull
+ points is numpoints+numnew X dim.
+
+notes:
+ qh_addpoint() does not make a copy of the point coordinates.
+
+ For inside points and some outside points, qh_findbestfacet performs
+ an exhaustive search for a visible facet. Algorithms that retain
+ previously constructed hulls should be faster for on-line construction
+ of the convex hull.
+*/
+void adddiamond(qhT *qh, coordT *points, int numpoints, int numnew, int dim) {
+ int j,k;
+ coordT *point;
+ facetT *facet;
+ boolT isoutside;
+ realT bestdist;
+
+ for (j= 0; j < numnew ; j++) {
+ point= points + (numpoints+j)*dim;
+ if (points == qh->first_point) /* in case of 'QRn' */
+ qh->num_points= numpoints+j+1;
+ /* qh.num_points sets the size of the points array. You may
+ allocate the points elsewhere. If so, qh_addpoint records
+ the point's address in qh->other_points
+ */
+ for (k=dim; k--; ) {
+ if (j/2 == k)
+ point[k]= (j & 1) ? 2.0 : -2.0;
+ else
+ point[k]= 0.0;
+ }
+ facet= qh_findbestfacet(qh, point, !qh_ALL, &bestdist, &isoutside);
+ if (isoutside) {
+ if (!qh_addpoint(qh, point, facet, False))
+ break; /* user requested an early exit with 'TVn' or 'TCn' */
+ }
+ printf("%d vertices and %d facets\n",
+ qh->num_vertices, qh->num_facets);
+ /* qh_produce_output(); */
+ }
+ if (qh->DOcheckmax)
+ qh_check_maxout(qh);
+ else if (qh->KEEPnearinside)
+ qh_nearcoplanar(qh);
+} /*.adddiamond.*/
+
+/*--------------------------------------------------
+-makeDelaunay- set points for dim-1 Delaunay triangulation of random points
+ points is numpoints X dim. Each point is projected to a paraboloid.
+*/
+void makeDelaunay(qhT *qh, coordT *points, int numpoints, int dim) {
+ int j,k, seed;
+ coordT *point, realr;
+
+ seed= (int)time(NULL); /* time_t to int */
+ printf("seed: %d\n", seed);
+ qh_RANDOMseed_(qh, seed);
+ for (j=0; j<numpoints; j++) {
+ point= points + j*dim;
+ for (k= 0; k < dim-1; k++) {
+ realr= qh_RANDOMint;
+ point[k]= 2.0 * realr/(qh_RANDOMmax+1) - 1.0;
+ }
+ }
+ qh_setdelaunay(qh, dim, numpoints, points);
+} /*.makeDelaunay.*/
+
+/*--------------------------------------------------
+-addDelaunay- add points to dim-1 Delaunay triangulation
+ points is numpoints+numnew X dim. Each point is projected to a paraboloid.
+notes:
+ qh_addpoint() does not make a copy of the point coordinates.
+
+ Since qh_addpoint() is not given a visible facet, it performs a directed
+ search of all facets. Algorithms that retain previously
+ constructed hulls may be faster.
+*/
+void addDelaunay(qhT *qh, coordT *points, int numpoints, int numnew, int dim) {
+ int j,k;
+ coordT *point, realr;
+ facetT *facet;
+ realT bestdist;
+ boolT isoutside;
+
+ for (j= 0; j < numnew ; j++) {
+ point= points + (numpoints+j)*dim;
+ if (points == qh->first_point) /* in case of 'QRn' */
+ qh->num_points= numpoints+j+1;
+ /* qh.num_points sets the size of the points array. You may
+ allocate the point elsewhere. If so, qh_addpoint records
+ the point's address in qh->other_points
+ */
+ for (k= 0; k < dim-1; k++) {
+ realr= qh_RANDOMint;
+ point[k]= 2.0 * realr/(qh_RANDOMmax+1) - 1.0;
+ }
+ qh_setdelaunay(qh, dim, 1, point);
+ facet= qh_findbestfacet(qh, point, !qh_ALL, &bestdist, &isoutside);
+ if (isoutside) {
+ if (!qh_addpoint(qh, point, facet, False))
+ break; /* user requested an early exit with 'TVn' or 'TCn' */
+ }
+ qh_printpoint(qh, stdout, "added point", point);
+ printf("%d points, %d extra points, %d vertices, and %d facets in total\n",
+ qh->num_points, qh_setsize(qh, qh->other_points),
+ qh->num_vertices, qh->num_facets);
+
+ /* qh_produce_output(qh); */
+ }
+ if (qh->DOcheckmax)
+ qh_check_maxout(qh);
+ else if (qh->KEEPnearinside)
+ qh_nearcoplanar(qh);
+} /*.addDelaunay.*/
+
+/*--------------------------------------------------
+-findDelaunay- find Delaunay triangle for [0.5,0.5,...]
+ assumes dim < 100
+notes:
+ calls qh_setdelaunay() to project the point to a parabaloid
+warning:
+ This is not implemented for tricoplanar facets ('Qt'),
+ See <a href="../html/qh-code.htm#findfacet">locate a facet with qh_findbestfacet()</a>
+*/
+void findDelaunay(qhT *qh, int dim) {
+ int k;
+ coordT point[ 100];
+ boolT isoutside;
+ realT bestdist;
+ facetT *facet;
+ vertexT *vertex, **vertexp;
+
+ for (k= 0; k < dim-1; k++)
+ point[k]= 0.5;
+ qh_setdelaunay(qh, dim, 1, point);
+ facet= qh_findbestfacet(qh, point, qh_ALL, &bestdist, &isoutside);
+ if (facet->tricoplanar) {
+ fprintf(stderr, "findDelaunay: not implemented for triangulated, non-simplicial Delaunay regions (tricoplanar facet, f%d).\n",
+ facet->id);
+ qh_errexit(qh, qh_ERRqhull, facet, NULL);
+ }
+ FOREACHvertex_(facet->vertices) {
+ for (k=0; k < dim-1; k++)
+ printf("%5.2f ", vertex->point[k]);
+ printf("\n");
+ }
+} /*.findDelaunay.*/
+
+/*--------------------------------------------------
+-makehalf- set points to halfspaces for a (dim)-d diamond
+ points is numpoints X dim+1
+
+ each halfspace consists of dim coefficients followed by an offset
+*/
+void makehalf(coordT *points, int numpoints, int dim) {
+ int j,k;
+ coordT *point;
+
+ for (j=0; j<numpoints; j++) {
+ point= points + j*(dim+1);
+ point[dim]= -1.0; /* offset */
+ for (k=dim; k--; ) {
+ if (j & ( 1 << k))
+ point[k]= 1.0;
+ else
+ point[k]= -1.0;
+ }
+ }
+} /*.makehalf.*/
+
+/*--------------------------------------------------
+-addhalf- add halfspaces for a (dim)-d cube to the intersection
+ points is numpoints+numnew X dim+1
+notes:
+ assumes dim < 100.
+
+ For makehalf(), points is the initial set of halfspaces with offsets.
+ It is transformed by qh_sethalfspace_all into a
+ (dim)-d set of newpoints. Qhull computed the convex hull of newpoints -
+ this is equivalent to the halfspace intersection of the
+ orginal halfspaces.
+
+ For addhalf(), the remainder of points stores the transforms of
+ the added halfspaces. Qhull computes the convex hull of newpoints
+ and the added points. qh_addpoint() does not make a copy of these points.
+
+ Since halfspace intersection is equivalent to a convex hull,
+ qh_findbestfacet may perform an exhaustive search
+ for a visible facet. Algorithms that retain previously constructed
+ intersections should be faster for on-line construction.
+*/
+void addhalf(qhT *qh, coordT *points, int numpoints, int numnew, int dim, coordT *feasible) {
+ int j,k;
+ coordT *point, normal[100], offset, *next;
+ facetT *facet;
+ boolT isoutside;
+ realT bestdist;
+
+ for (j= 0; j < numnew ; j++) {
+ offset= -1.0;
+ for (k=dim; k--; ) {
+ if (j/2 == k) {
+ normal[k]= sqrt((coordT)dim); /* to normalize as in makehalf */
+ if (j & 1)
+ normal[k]= -normal[k];
+ }else
+ normal[k]= 0.0;
+ }
+ point= points + (numpoints+j)* (dim+1); /* does not use point[dim] */
+ qh_sethalfspace(qh, dim, point, &next, normal, &offset, feasible);
+ facet= qh_findbestfacet(qh, point, !qh_ALL, &bestdist, &isoutside);
+ if (isoutside) {
+ if (!qh_addpoint(qh, point, facet, False))
+ break; /* user requested an early exit with 'TVn' or 'TCn' */
+ }
+ qh_printpoint(qh, stdout, "added offset -1 and normal", normal);
+ printf("%d points, %d extra points, %d vertices, and %d facets in total\n",
+ qh->num_points, qh_setsize(qh, qh->other_points),
+ qh->num_vertices, qh->num_facets);
+ /* qh_produce_output(qh); */
+ }
+ if (qh->DOcheckmax)
+ qh_check_maxout(qh);
+ else if (qh->KEEPnearinside)
+ qh_nearcoplanar(qh);
+} /*.addhalf.*/
+
+#define DIM 3 /* dimension of points, must be < 31 for SIZEcube */
+#define SIZEcube (1<<DIM)
+#define SIZEdiamond (2*DIM)
+#define TOTpoints (SIZEcube + SIZEdiamond)
+
+/*--------------------------------------------------
+-main- Similar to unix.c, the main program for qhull
+
+ see program header
+
+ this contains three runs of Qhull for convex hull, Delaunay
+ triangulation or Voronoi vertices, and halfspace intersection
+
+*/
+int main(int argc, char *argv[]) {
+ boolT ismalloc;
+ int curlong, totlong, exitcode;
+ char options [2000];
+ qhT qh_qh;
+ qhT *qh= &qh_qh; /* Alternatively -- qhT *qh= (qhT*)malloc(sizeof(qhT)) */
+
+ QHULL_LIB_CHECK
+
+ printf("This is the output from user_eg2_r.c\n\n\
+It shows how qhull() may be called from an application using qhull's\n\
+static, reentrant library. user_eg2 is not part of qhull itself. If it\n\
+appears accidently, please remove user_eg2_r.c from your project. If it fails\n\
+immediately, user_eg2_r.c was incorrectly linked to the non-reentrant library.\n\
+Also try 'user_eg2 T1 2>&1'\n\n");
+
+ ismalloc= False; /* True if qh_freeqhull should 'free(array)' */
+ /*
+ Run 1: convex hull
+ */
+ qh_init_A(qh, stdin, stdout, stderr, 0, NULL);
+ exitcode= setjmp(qh->errexit);
+ if (!exitcode) {
+ coordT array[TOTpoints][DIM];
+
+ qh->NOerrexit= False;
+ strcat(qh->rbox_command, "user_eg2 cube example");
+ sprintf(options, "qhull s Tcv Q11 %s ", argc >= 2 ? argv[1] : "");
+ qh_initflags(qh, options);
+ printf( "\ncompute triangulated convex hull of cube after rotating input\n");
+ makecube(array[0], SIZEcube, DIM);
+ qh_init_B(qh, array[0], SIZEcube, DIM, ismalloc);
+ qh_qhull(qh);
+ qh_check_output(qh);
+ qh_triangulate(qh); /* requires option 'Q11' if want to add points */
+ print_summary(qh);
+ if (qh->VERIFYoutput && !qh->STOPpoint && !qh->STOPcone)
+ qh_check_points(qh);
+ printf( "\nadd points in a diamond\n");
+ adddiamond(qh, array[0], SIZEcube, SIZEdiamond, DIM);
+ qh_check_output(qh);
+ print_summary(qh);
+ qh_produce_output(qh); /* delete this line to help avoid io.c */
+ if (qh->VERIFYoutput && !qh->STOPpoint && !qh->STOPcone)
+ qh_check_points(qh);
+ }
+ qh->NOerrexit= True;
+ qh_freeqhull(qh, !qh_ALL);
+ qh_memfreeshort(qh, &curlong, &totlong);
+ if (curlong || totlong)
+ fprintf(stderr, "qhull warning (user_eg2, run 1): did not free %d bytes of long memory (%d pieces)\n",
+ totlong, curlong);
+ /*
+ Run 2: Delaunay triangulation
+ */
+ qh_init_A(qh, stdin, stdout, stderr, 0, NULL);
+ exitcode= setjmp(qh->errexit);
+ if (!exitcode) {
+ coordT array[TOTpoints][DIM];
+
+ qh->NOerrexit= False;
+ strcat(qh->rbox_command, "user_eg2 Delaunay example");
+ sprintf(options, "qhull s d Tcv %s", argc >= 3 ? argv[2] : "");
+ qh_initflags(qh, options);
+ printf( "\ncompute %d-d Delaunay triangulation\n", DIM-1);
+ makeDelaunay(qh, array[0], SIZEcube, DIM);
+ /* Instead of makeDelaunay with qh_setdelaunay, you may
+ produce a 2-d array of points, set DIM to 2, and set
+ qh->PROJECTdelaunay to True. qh_init_B will call
+ qh_projectinput to project the points to the paraboloid
+ and add a point "at-infinity".
+ */
+ qh_init_B(qh, array[0], SIZEcube, DIM, ismalloc);
+ qh_qhull(qh);
+ /* If you want Voronoi ('v') without qh_produce_output(), call
+ qh_setvoronoi_all() after qh_qhull() */
+ qh_check_output(qh);
+ print_summary(qh);
+ qh_produce_output(qh); /* delete this line to help avoid io.c */
+ if (qh->VERIFYoutput && !qh->STOPpoint && !qh->STOPcone)
+ qh_check_points(qh);
+ printf( "\nadd points to triangulation\n");
+ addDelaunay(qh, array[0], SIZEcube, SIZEdiamond, DIM);
+ qh_check_output(qh);
+ qh_produce_output(qh); /* delete this line to help avoid io.c */
+ if (qh->VERIFYoutput && !qh->STOPpoint && !qh->STOPcone)
+ qh_check_points(qh);
+ printf( "\nfind Delaunay triangle closest to [0.5, 0.5, ...]\n");
+ findDelaunay(qh, DIM);
+ }
+ qh->NOerrexit= True;
+ qh_freeqhull(qh, !qh_ALL);
+ qh_memfreeshort(qh, &curlong, &totlong);
+ if (curlong || totlong)
+ fprintf(stderr, "qhull warning (user_eg2, run 2): did not free %d bytes of long memory (%d pieces)\n",
+ totlong, curlong);
+ /*
+ Run 3: halfspace intersection
+ */
+ qh_init_A(qh, stdin, stdout, stderr, 0, NULL);
+ exitcode= setjmp(qh->errexit);
+ if (!exitcode) {
+ coordT array[TOTpoints][DIM+1]; /* +1 for halfspace offset */
+ pointT *points;
+
+ qh->NOerrexit= False;
+ strcat(qh->rbox_command, "user_eg2 halfspace example");
+ sprintf(options, "qhull H0 s Tcv %s", argc >= 4 ? argv[3] : "");
+ qh_initflags(qh, options);
+ printf( "\ncompute halfspace intersection about the origin for a diamond\n");
+ makehalf(array[0], SIZEcube, DIM);
+ qh_setfeasible(qh, DIM); /* from io.c, sets qh->feasible_point from 'Hn,n' */
+ /* you may malloc and set qh->feasible_point directly. It is only used for
+ option 'Fp' */
+ points= qh_sethalfspace_all(qh, DIM+1, SIZEcube, array[0], qh->feasible_point);
+ qh_init_B(qh, points, SIZEcube, DIM, True); /* qh_freeqhull frees points */
+ qh_qhull(qh);
+ qh_check_output(qh);
+ qh_produce_output(qh); /* delete this line to help avoid io.c */
+ if (qh->VERIFYoutput && !qh->STOPpoint && !qh->STOPcone)
+ qh_check_points(qh);
+ printf( "\nadd halfspaces for cube to intersection\n");
+ addhalf(qh, array[0], SIZEcube, SIZEdiamond, DIM, qh->feasible_point);
+ qh_check_output(qh);
+ qh_produce_output(qh); /* delete this line to help avoid io.c */
+ if (qh->VERIFYoutput && !qh->STOPpoint && !qh->STOPcone)
+ qh_check_points(qh);
+ }
+ qh->NOerrexit= True;
+ qh->NOerrexit= True;
+ qh_freeqhull(qh, !qh_ALL);
+ qh_memfreeshort(qh, &curlong, &totlong);
+ if (curlong || totlong)
+ fprintf(stderr, "qhull warning (user_eg2, run 3): did not free %d bytes of long memory (%d pieces)\n",
+ totlong, curlong);
+ return exitcode;
+} /* main */
+
+#if 1 /* use 1 to prevent loading of io.o and user.o */
+/*-------------------------------------------
+-errexit- return exitcode to system after an error
+ assumes exitcode non-zero
+ prints useful information
+ see qh_errexit2() in libqhull.c for 2 facets
+*/
+void qh_errexit(qhT *qh, int exitcode, facetT *facet, ridgeT *ridge) {
+ QHULL_UNUSED(facet);
+ QHULL_UNUSED(ridge);
+
+ if (qh->ERREXITcalled) {
+ fprintf(qh->ferr, "qhull error while processing previous error. Exit program\n");
+ exit(1);
+ }
+ qh->ERREXITcalled= True;
+ if (!qh->QHULLfinished)
+ qh->hulltime= (unsigned)clock() - qh->hulltime;
+ fprintf(qh->ferr, "\nWhile executing: %s | %s\n", qh->rbox_command, qh->qhull_command);
+ fprintf(qh->ferr, "Options selected:\n%s\n", qh->qhull_options);
+ if (qh->furthest_id >= 0) {
+ fprintf(qh->ferr, "\nLast point added to hull was p%d", qh->furthest_id);
+ if (zzval_(Ztotmerge))
+ fprintf(qh->ferr, " Last merge was #%d.", zzval_(Ztotmerge));
+ if (qh->QHULLfinished)
+ fprintf(qh->ferr, "\nQhull has finished constructing the hull.");
+ else if (qh->POSTmerging)
+ fprintf(qh->ferr, "\nQhull has started post-merging");
+ fprintf(qh->ferr, "\n\n");
+ }
+ if (qh->NOerrexit) {
+ fprintf(qh->ferr, "qhull error while ending program. Exit program\n");
+ exit(1);
+ }
+ if (!exitcode)
+ exitcode= qh_ERRqhull;
+ qh->NOerrexit= True;
+ longjmp(qh->errexit, exitcode);
+} /* errexit */
+
+
+/*-------------------------------------------
+-errprint- prints out the information of the erroneous object
+ any parameter may be NULL, also prints neighbors and geomview output
+*/
+void qh_errprint(qhT *qh, const char *string, facetT *atfacet, facetT *otherfacet, ridgeT *atridge, vertexT *atvertex) {
+
+ fprintf(qh->ferr, "%s facets f%d f%d ridge r%d vertex v%d\n",
+ string, getid_(atfacet), getid_(otherfacet), getid_(atridge),
+ getid_(atvertex));
+} /* errprint */
+
+
+void qh_printfacetlist(qhT *qh, facetT *facetlist, setT *facets, boolT printall) {
+ facetT *facet, **facetp;
+
+ /* remove these calls to help avoid io.c */
+ qh_printbegin(qh, qh->ferr, qh_PRINTfacets, facetlist, facets, printall);/*io.c*/
+ FORALLfacet_(facetlist) /*io.c*/
+ qh_printafacet(qh, qh->ferr, qh_PRINTfacets, facet, printall); /*io.c*/
+ FOREACHfacet_(facets) /*io.c*/
+ qh_printafacet(qh, qh->ferr, qh_PRINTfacets, facet, printall); /*io.c*/
+ qh_printend(qh, qh->ferr, qh_PRINTfacets, facetlist, facets, printall); /*io.c*/
+
+ FORALLfacet_(facetlist)
+ fprintf( qh->ferr, "facet f%d\n", facet->id);
+} /* printfacetlist */
+
+/* qh_printhelp_degenerate( fp )
+ prints descriptive message for precision error
+
+ notes:
+ no message if qh_QUICKhelp
+*/
+void qh_printhelp_degenerate(qhT *qh, FILE *fp) {
+
+ if (qh->MERGEexact || qh->PREmerge || qh->JOGGLEmax < REALmax/2)
+ qh_fprintf(qh, fp, 9368, "\n\
+A Qhull error has occurred. Qhull should have corrected the above\n\
+precision error. Please send the input and all of the output to\n\
+qhull_bug@qhull.org\n");
+ else if (!qh_QUICKhelp) {
+ qh_fprintf(qh, fp, 9369, "\n\
+Precision problems were detected during construction of the convex hull.\n\
+This occurs because convex hull algorithms assume that calculations are\n\
+exact, but floating-point arithmetic has roundoff errors.\n\
+\n\
+To correct for precision problems, do not use 'Q0'. By default, Qhull\n\
+selects 'C-0' or 'Qx' and merges non-convex facets. With option 'QJ',\n\
+Qhull joggles the input to prevent precision problems. See \"Imprecision\n\
+in Qhull\" (qh-impre.htm).\n\
+\n\
+If you use 'Q0', the output may include\n\
+coplanar ridges, concave ridges, and flipped facets. In 4-d and higher,\n\
+Qhull may produce a ridge with four neighbors or two facets with the same \n\
+vertices. Qhull reports these events when they occur. It stops when a\n\
+concave ridge, flipped facet, or duplicate facet occurs.\n");
+#if REALfloat
+ qh_fprintf(qh, fp, 9370, "\
+\n\
+Qhull is currently using single precision arithmetic. The following\n\
+will probably remove the precision problems:\n\
+ - recompile qhull for realT precision(#define REALfloat 0 in user.h).\n");
+#endif
+ if (qh->DELAUNAY && !qh->SCALElast && qh->MAXabs_coord > 1e4)
+ qh_fprintf(qh, fp, 9371, "\
+\n\
+When computing the Delaunay triangulation of coordinates > 1.0,\n\
+ - use 'Qbb' to scale the last coordinate to [0,m] (max previous coordinate)\n");
+ if (qh->DELAUNAY && !qh->ATinfinity)
+ qh_fprintf(qh, fp, 9372, "\
+When computing the Delaunay triangulation:\n\
+ - use 'Qz' to add a point at-infinity. This reduces precision problems.\n");
+
+ qh_fprintf(qh, fp, 9373, "\
+\n\
+If you need triangular output:\n\
+ - use option 'Qt' to triangulate the output\n\
+ - use option 'QJ' to joggle the input points and remove precision errors\n\
+ - use option 'Ft'. It triangulates non-simplicial facets with added points.\n\
+\n\
+If you must use 'Q0',\n\
+try one or more of the following options. They can not guarantee an output.\n\
+ - use 'QbB' to scale the input to a cube.\n\
+ - use 'Po' to produce output and prevent partitioning for flipped facets\n\
+ - use 'V0' to set min. distance to visible facet as 0 instead of roundoff\n\
+ - use 'En' to specify a maximum roundoff error less than %2.2g.\n\
+ - options 'Qf', 'Qbb', and 'QR0' may also help\n",
+ qh->DISTround);
+ qh_fprintf(qh, fp, 9374, "\
+\n\
+To guarantee simplicial output:\n\
+ - use option 'Qt' to triangulate the output\n\
+ - use option 'QJ' to joggle the input points and remove precision errors\n\
+ - use option 'Ft' to triangulate the output by adding points\n\
+ - use exact arithmetic (see \"Imprecision in Qhull\", qh-impre.htm)\n\
+");
+ }
+} /* printhelp_degenerate */
+
+
+/* qh_printhelp_narrowhull( minangle )
+ Warn about a narrow hull
+
+ notes:
+ Alternatively, reduce qh_WARNnarrow in user.h
+
+*/
+void qh_printhelp_narrowhull(qhT *qh, FILE *fp, realT minangle) {
+
+ qh_fprintf(qh, fp, 9375, "qhull precision warning: \n\
+The initial hull is narrow (cosine of min. angle is %.16f).\n\
+A coplanar point may lead to a wide facet. Options 'QbB' (scale to unit box)\n\
+or 'Qbb' (scale last coordinate) may remove this warning. Use 'Pp' to skip\n\
+this warning. See 'Limitations' in qh-impre.htm.\n",
+ -minangle); /* convert from angle between normals to angle between facets */
+} /* printhelp_narrowhull */
+
+/* qh_printhelp_singular
+ prints descriptive message for singular input
+*/
+void qh_printhelp_singular(qhT *qh, FILE *fp) {
+ facetT *facet;
+ vertexT *vertex, **vertexp;
+ realT min, max, *coord, dist;
+ int i,k;
+
+ qh_fprintf(qh, fp, 9376, "\n\
+The input to qhull appears to be less than %d dimensional, or a\n\
+computation has overflowed.\n\n\
+Qhull could not construct a clearly convex simplex from points:\n",
+ qh->hull_dim);
+ qh_printvertexlist(qh, fp, "", qh->facet_list, NULL, qh_ALL);
+ if (!qh_QUICKhelp)
+ qh_fprintf(qh, fp, 9377, "\n\
+The center point is coplanar with a facet, or a vertex is coplanar\n\
+with a neighboring facet. The maximum round off error for\n\
+computing distances is %2.2g. The center point, facets and distances\n\
+to the center point are as follows:\n\n", qh->DISTround);
+ qh_printpointid(qh, fp, "center point", qh->hull_dim, qh->interior_point, -1);
+ qh_fprintf(qh, fp, 9378, "\n");
+ FORALLfacets {
+ qh_fprintf(qh, fp, 9379, "facet");
+ FOREACHvertex_(facet->vertices)
+ qh_fprintf(qh, fp, 9380, " p%d", qh_pointid(qh, vertex->point));
+ zinc_(Zdistio);
+ qh_distplane(qh, qh->interior_point, facet, &dist);
+ qh_fprintf(qh, fp, 9381, " distance= %4.2g\n", dist);
+ }
+ if (!qh_QUICKhelp) {
+ if (qh->HALFspace)
+ qh_fprintf(qh, fp, 9382, "\n\
+These points are the dual of the given halfspaces. They indicate that\n\
+the intersection is degenerate.\n");
+ qh_fprintf(qh, fp, 9383,"\n\
+These points either have a maximum or minimum x-coordinate, or\n\
+they maximize the determinant for k coordinates. Trial points\n\
+are first selected from points that maximize a coordinate.\n");
+ if (qh->hull_dim >= qh_INITIALmax)
+ qh_fprintf(qh, fp, 9384, "\n\
+Because of the high dimension, the min x-coordinate and max-coordinate\n\
+points are used if the determinant is non-zero. Option 'Qs' will\n\
+do a better, though much slower, job. Instead of 'Qs', you can change\n\
+the points by randomly rotating the input with 'QR0'.\n");
+ }
+ qh_fprintf(qh, fp, 9385, "\nThe min and max coordinates for each dimension are:\n");
+ for (k=0; k < qh->hull_dim; k++) {
+ min= REALmax;
+ max= -REALmin;
+ for (i=qh->num_points, coord= qh->first_point+k; i--; coord += qh->hull_dim) {
+ maximize_(max, *coord);
+ minimize_(min, *coord);
+ }
+ qh_fprintf(qh, fp, 9386, " %d: %8.4g %8.4g difference= %4.4g\n", k, min, max, max-min);
+ }
+ if (!qh_QUICKhelp) {
+ qh_fprintf(qh, fp, 9387, "\n\
+If the input should be full dimensional, you have several options that\n\
+may determine an initial simplex:\n\
+ - use 'QJ' to joggle the input and make it full dimensional\n\
+ - use 'QbB' to scale the points to the unit cube\n\
+ - use 'QR0' to randomly rotate the input for different maximum points\n\
+ - use 'Qs' to search all points for the initial simplex\n\
+ - use 'En' to specify a maximum roundoff error less than %2.2g.\n\
+ - trace execution with 'T3' to see the determinant for each point.\n",
+ qh->DISTround);
+#if REALfloat
+ qh_fprintf(qh, fp, 9388, "\
+ - recompile qhull for realT precision(#define REALfloat 0 in libqhull.h).\n");
+#endif
+ qh_fprintf(qh, fp, 9389, "\n\
+If the input is lower dimensional:\n\
+ - use 'QJ' to joggle the input and make it full dimensional\n\
+ - use 'Qbk:0Bk:0' to delete coordinate k from the input. You should\n\
+ pick the coordinate with the least range. The hull will have the\n\
+ correct topology.\n\
+ - determine the flat containing the points, rotate the points\n\
+ into a coordinate plane, and delete the other coordinates.\n\
+ - add one or more points to make the input full dimensional.\n\
+");
+ if (qh->DELAUNAY && !qh->ATinfinity)
+ qh_fprintf(qh, fp, 9390, "\n\n\
+This is a Delaunay triangulation and the input is co-circular or co-spherical:\n\
+ - use 'Qz' to add a point \"at infinity\" (i.e., above the paraboloid)\n\
+ - or use 'QJ' to joggle the input and avoid co-circular data\n");
+ }
+} /* printhelp_singular */
+
+
+/*-----------------------------------------
+-user_memsizes- allocate up to 10 additional, quick allocation sizes
+*/
+void qh_user_memsizes(qhT *qh) {
+
+ QHULL_UNUSED(qh);
+ /* qh_memsize(qh, size); */
+} /* user_memsizes */
+
+#endif
diff --git a/xs/src/qhull/src/user_eg3/user_eg3.pro b/xs/src/qhull/src/user_eg3/user_eg3.pro
new file mode 100644
index 000000000..35372fbf9
--- /dev/null
+++ b/xs/src/qhull/src/user_eg3/user_eg3.pro
@@ -0,0 +1,12 @@
+# -------------------------------------------------
+# user_eg3.pro -- Qt project for cpp demonstration user_eg3.exe
+#
+# The C++ interface requires reentrant Qhull.
+# -------------------------------------------------
+
+include(../qhull-app-cpp.pri)
+
+TARGET = user_eg3
+CONFIG -= qt
+
+SOURCES += user_eg3_r.cpp
diff --git a/xs/src/qhull/src/user_eg3/user_eg3_r.cpp b/xs/src/qhull/src/user_eg3/user_eg3_r.cpp
new file mode 100644
index 000000000..5257872ab
--- /dev/null
+++ b/xs/src/qhull/src/user_eg3/user_eg3_r.cpp
@@ -0,0 +1,162 @@
+#//! user_eg3_r.cpp -- Invoke rbox and qhull from C++
+
+#include "libqhullcpp/RboxPoints.h"
+#include "libqhullcpp/QhullError.h"
+#include "libqhullcpp/QhullQh.h"
+#include "libqhullcpp/QhullFacet.h"
+#include "libqhullcpp/QhullFacetList.h"
+#include "libqhullcpp/QhullLinkedList.h"
+#include "libqhullcpp/QhullVertex.h"
+#include "libqhullcpp/Qhull.h"
+
+#include <cstdio> /* for printf() of help message */
+#include <ostream>
+#include <stdexcept>
+
+using std::cerr;
+using std::cin;
+using std::cout;
+using std::endl;
+
+using orgQhull::Qhull;
+using orgQhull::QhullError;
+using orgQhull::QhullFacet;
+using orgQhull::QhullFacetList;
+using orgQhull::QhullQh;
+using orgQhull::RboxPoints;
+using orgQhull::QhullVertex;
+using orgQhull::QhullVertexSet;
+
+int main(int argc, char **argv);
+int user_eg3(int argc, char **argv);
+
+char prompt[]= "\n\
+user_eg3 -- demonstrate calling rbox and qhull from C++.\n\
+\n\
+user_eg3 is statically linked to reentrant qhull. If user_eg3\n\
+fails immediately, it is probably linked to the non-reentrant qhull.\n\
+Try 'user_eg3 rbox qhull \"T1\"'\n\
+\n\
+ eg-100 Run the example in qh-code.htm\n\
+ rbox \"200 D4\" ... Generate points from rbox\n\
+ qhull \"d p\" ... Run qhull and produce output\n\
+ qhull-cout \"o\" ... Run qhull and produce output to cout\n\
+ qhull \"T1\" ... Run qhull with level-1 trace to cerr\n\
+ facets Print facets when done\n\
+\n\
+For example\n\
+ user_eg3 rbox qhull\n\
+ user_eg3 rbox qhull d\n\
+ user_eg3 rbox \"10 D2\" \"2 D2\" qhull \"s p\" facets\n\
+\n\
+";
+
+
+/*--------------------------------------------
+-user_eg3- main procedure of user_eg3 application
+*/
+int main(int argc, char **argv) {
+
+ QHULL_LIB_CHECK
+
+ if(argc==1){
+ cout << prompt;
+ return 1;
+ }
+ try{
+ return user_eg3(argc, argv);
+ }catch(QhullError &e){
+ cerr << e.what() << std::endl;
+ return e.errorCode();
+ }
+}//main
+
+int user_eg3(int argc, char **argv)
+{
+ if(strcmp(argv[1], "eg-100")==0){
+ RboxPoints rbox("100");
+ Qhull q(rbox, "");
+ QhullFacetList facets= q.facetList();
+ cout << facets;
+ return 0;
+ }
+ bool printFacets= false;
+ RboxPoints rbox;
+ Qhull qhull;
+ int readingRbox= 0;
+ int readingQhull= 0;
+ for(int i=1; i<argc; i++){
+ if(strcmp(argv[i], "rbox")==0){
+ if(readingRbox!=0 || readingQhull!=0){
+ cerr << "user_eg3 -- \"rbox\" must be first" << endl;
+ return 1;
+ }
+ readingRbox++;
+ }else if(strcmp(argv[i], "qhull")==0
+ || strcmp(argv[i], "qhull-cout")==0){
+ if(readingQhull){
+ cerr << "user_eg3 -- only one \"qhull\" or \"qhull-cout\" allowed." << endl;
+ return 1;
+ }
+ if(strcmp(argv[i], "qhull-cout")==0){
+ qhull.setOutputStream(&cout);
+ }
+ if(rbox.isEmpty()){
+ if(readingRbox){
+ rbox.appendPoints("10 D2");
+ }else{
+ cerr << "Enter dimension count coordinates. End with ^Z (Windows) or ^D (Unix).\n";
+ rbox.appendPoints(cin);
+ }
+ }
+ readingQhull++;
+ readingRbox= 0;
+ }else if(strcmp(argv[i], "facets")==0){
+ printFacets= true;
+ }else if(readingRbox){
+ readingRbox++;
+ cerr << "rbox " << argv[i] << endl;
+ rbox.appendPoints(argv[i]);
+ if(rbox.hasRboxMessage()){
+ cerr << "user_eg3 " << argv[i] << " -- " << rbox.rboxMessage();
+ return rbox.rboxStatus();
+ }
+ }else if(readingQhull){
+ if(readingQhull==1){
+ qhull.runQhull(rbox, argv[i]);
+ qhull.outputQhull();
+ }else{
+ qhull.outputQhull(argv[i]);
+ }
+ readingQhull++;
+ if(qhull.hasQhullMessage()){
+ cerr << "\nResults of " << argv[i] << "\n" << qhull.qhullMessage();
+ qhull.clearQhullMessage();
+ }
+ }else{
+ cerr << "user_eg3 error: Expecting qhull, qhull-cout, or rbox. Got " << argv[i] << endl;
+ return 1;
+ }
+ }//foreach argv
+ if(readingRbox){
+ cout << rbox;
+ return 0;
+ }
+ if(readingQhull==1){ // e.g., rbox 10 qhull
+ qhull.runQhull(rbox, "");
+ qhull.outputQhull();
+ if(qhull.hasQhullMessage()){
+ cerr << "\nResults of qhull\n" << qhull.qhullMessage();
+ qhull.clearQhullMessage();
+ }
+ }
+ if(qhull.hasOutputStream()){
+ return 0;
+ }
+ if(printFacets){
+ QhullFacetList facets= qhull.facetList();
+ cout << "\nFacets created by Qhull::runQhull()\n" << facets;
+ }
+ return 0;
+}//user_eg3
+
diff --git a/xs/src/slic3r/AppController.cpp b/xs/src/slic3r/AppController.cpp
index 58858f5fc..7bd044b25 100644
--- a/xs/src/slic3r/AppController.cpp
+++ b/xs/src/slic3r/AppController.cpp
@@ -43,15 +43,6 @@ namespace GUI {
PresetBundle* get_preset_bundle();
}
-static const PrintObjectStep STEP_SLICE = posSlice;
-static const PrintObjectStep STEP_PERIMETERS = posPerimeters;
-static const PrintObjectStep STEP_PREPARE_INFILL = posPrepareInfill;
-static const PrintObjectStep STEP_INFILL = posInfill;
-static const PrintObjectStep STEP_SUPPORTMATERIAL = posSupportMaterial;
-static const PrintStep STEP_SKIRT = psSkirt;
-static const PrintStep STEP_BRIM = psBrim;
-static const PrintStep STEP_WIPE_TOWER = psWipeTower;
-
AppControllerBoilerplate::ProgresIndicatorPtr
AppControllerBoilerplate::global_progress_indicator() {
ProgresIndicatorPtr ret;
@@ -71,193 +62,8 @@ void AppControllerBoilerplate::global_progress_indicator(
pri_data_->m.unlock();
}
-void PrintController::make_skirt()
-{
- assert(print_ != nullptr);
-
- // prerequisites
- for(auto obj : print_->objects) make_perimeters(obj);
- for(auto obj : print_->objects) infill(obj);
- for(auto obj : print_->objects) gen_support_material(obj);
-
- if(!print_->state.is_done(STEP_SKIRT)) {
- print_->state.set_started(STEP_SKIRT);
- print_->skirt.clear();
- if(print_->has_skirt()) print_->_make_skirt();
-
- print_->state.set_done(STEP_SKIRT);
- }
-}
-
-void PrintController::make_brim()
-{
- assert(print_ != nullptr);
-
- // prerequisites
- for(auto obj : print_->objects) make_perimeters(obj);
- for(auto obj : print_->objects) infill(obj);
- for(auto obj : print_->objects) gen_support_material(obj);
- make_skirt();
-
- if(!print_->state.is_done(STEP_BRIM)) {
- print_->state.set_started(STEP_BRIM);
-
- // since this method must be idempotent, we clear brim paths *before*
- // checking whether we need to generate them
- print_->brim.clear();
-
- if(print_->config.brim_width > 0) print_->_make_brim();
-
- print_->state.set_done(STEP_BRIM);
- }
-}
-
-void PrintController::make_wipe_tower()
-{
- assert(print_ != nullptr);
-
- // prerequisites
- for(auto obj : print_->objects) make_perimeters(obj);
- for(auto obj : print_->objects) infill(obj);
- for(auto obj : print_->objects) gen_support_material(obj);
- make_skirt();
- make_brim();
-
- if(!print_->state.is_done(STEP_WIPE_TOWER)) {
- print_->state.set_started(STEP_WIPE_TOWER);
-
- // since this method must be idempotent, we clear brim paths *before*
- // checking whether we need to generate them
- print_->brim.clear();
-
- if(print_->has_wipe_tower()) print_->_make_wipe_tower();
-
- print_->state.set_done(STEP_WIPE_TOWER);
- }
-}
-
-void PrintController::slice(PrintObject *pobj)
-{
- assert(pobj != nullptr && print_ != nullptr);
-
- if(pobj->state.is_done(STEP_SLICE)) return;
-
- pobj->state.set_started(STEP_SLICE);
-
- pobj->_slice();
-
- auto msg = pobj->_fix_slicing_errors();
- if(!msg.empty()) report_issue(IssueType::WARN, msg);
-
- // simplify slices if required
- if (print_->config.resolution)
- pobj->_simplify_slices(scale_(print_->config.resolution));
-
-
- if(pobj->layers.empty())
- report_issue(IssueType::ERR,
- _(L("No layers were detected. You might want to repair your "
- "STL file(s) or check their size or thickness and retry"))
- );
-
- pobj->state.set_done(STEP_SLICE);
-}
-
-void PrintController::make_perimeters(PrintObject *pobj)
-{
- assert(pobj != nullptr);
-
- slice(pobj);
-
- if (!pobj->state.is_done(STEP_PERIMETERS)) {
- pobj->_make_perimeters();
- }
-}
-
-void PrintController::infill(PrintObject *pobj)
-{
- assert(pobj != nullptr);
-
- make_perimeters(pobj);
-
- if (!pobj->state.is_done(STEP_PREPARE_INFILL)) {
- pobj->state.set_started(STEP_PREPARE_INFILL);
-
- pobj->_prepare_infill();
-
- pobj->state.set_done(STEP_PREPARE_INFILL);
- }
-
- pobj->_infill();
-}
-
-void PrintController::gen_support_material(PrintObject *pobj)
-{
- assert(pobj != nullptr);
-
- // prerequisites
- slice(pobj);
-
- if(!pobj->state.is_done(STEP_SUPPORTMATERIAL)) {
- pobj->state.set_started(STEP_SUPPORTMATERIAL);
-
- pobj->clear_support_layers();
-
- if((pobj->config.support_material || pobj->config.raft_layers > 0)
- && pobj->layers.size() > 1) {
- pobj->_generate_support_material();
- }
-
- pobj->state.set_done(STEP_SUPPORTMATERIAL);
- }
-}
-
-void PrintController::slice(AppControllerBoilerplate::ProgresIndicatorPtr pri)
-{
- auto st = pri->state();
-
- Slic3r::trace(3, "Starting the slicing process.");
-
- pri->update(st+20, _(L("Generating perimeters")));
- for(auto obj : print_->objects) make_perimeters(obj);
-
- pri->update(st+60, _(L("Infilling layers")));
- for(auto obj : print_->objects) infill(obj);
-
- pri->update(st+70, _(L("Generating support material")));
- for(auto obj : print_->objects) gen_support_material(obj);
-
- pri->message_fmt(_(L("Weight: %.1fg, Cost: %.1f")),
- print_->total_weight, print_->total_cost);
- pri->state(st+85);
-
-
- pri->update(st+88, _(L("Generating skirt")));
- make_skirt();
-
-
- pri->update(st+90, _(L("Generating brim")));
- make_brim();
-
- pri->update(st+95, _(L("Generating wipe tower")));
- make_wipe_tower();
-
- pri->update(st+100, _(L("Done")));
-
- // time to make some statistics..
-
- Slic3r::trace(3, _(L("Slicing process finished.")));
-}
-
-void PrintController::slice()
-{
- auto pri = global_progress_indicator();
- if(!pri) pri = create_progress_indicator(100, L("Slicing"));
- slice(pri);
-}
-
-void IProgressIndicator::message_fmt(
- const string &fmtstr, ...) {
+void ProgressIndicator::message_fmt(
+ const std::string &fmtstr, ...) {
std::stringstream ss;
va_list args;
va_start(args, fmtstr);
@@ -321,30 +127,34 @@ void AppController::arrange_model()
for(auto& v : bedpoints)
bed.append(Point::new_scale(v.x, v.y));
- if(pind) pind->update(0, _(L("Arranging objects...")));
+ if(pind) pind->update(0, L("Arranging objects..."));
try {
+ arr::BedShapeHint hint;
+ // TODO: from Sasha from GUI
+ hint.type = arr::BedShapeType::WHO_KNOWS;
+
arr::arrange(*model_,
min_obj_distance,
bed,
- arr::BOX,
+ hint,
false, // create many piles not just one pile
[pind, count](unsigned rem) {
if(pind)
- pind->update(count - rem, _(L("Arranging objects...")));
+ pind->update(count - rem, L("Arranging objects..."));
});
} catch(std::exception& e) {
std::cerr << e.what() << std::endl;
report_issue(IssueType::ERR,
- _(L("Could not arrange model objects! "
- "Some geometries may be invalid.")),
- _(L("Exception occurred")));
+ L("Could not arrange model objects! "
+ "Some geometries may be invalid."),
+ L("Exception occurred"));
}
// Restore previous max value
if(pind) {
pind->max(pmax);
- pind->update(0, _(L("Arranging done.")));
+ pind->update(0, L("Arranging done."));
}
});
diff --git a/xs/src/slic3r/AppController.hpp b/xs/src/slic3r/AppController.hpp
index e9252b50c..3ef47ffdc 100644
--- a/xs/src/slic3r/AppController.hpp
+++ b/xs/src/slic3r/AppController.hpp
@@ -7,7 +7,7 @@
#include <atomic>
#include <iostream>
-#include "IProgressIndicator.hpp"
+#include "ProgressIndicator.hpp"
namespace Slic3r {
@@ -15,7 +15,8 @@ class Model;
class Print;
class PrintObject;
class PrintConfig;
-
+class ProgressStatusBar;
+class DynamicPrintConfig;
/**
* @brief A boilerplate class for creating application logic. It should provide
@@ -33,7 +34,7 @@ class AppControllerBoilerplate {
public:
/// A Progress indicator object smart pointer
- using ProgresIndicatorPtr = std::shared_ptr<IProgressIndicator>;
+ using ProgresIndicatorPtr = std::shared_ptr<ProgressIndicator>;
private:
class PriData; // Some structure to store progress indication data
@@ -46,7 +47,7 @@ public:
AppControllerBoilerplate();
~AppControllerBoilerplate();
- using Path = string;
+ using Path = std::string;
using PathList = std::vector<Path>;
/// Common runtime issue types
@@ -67,20 +68,20 @@ public:
* @return Returns a list of paths choosed by the user.
*/
PathList query_destination_paths(
- const string& title,
+ const std::string& title,
const std::string& extensions) const;
/**
* @brief Same as query_destination_paths but works for directories only.
*/
PathList query_destination_dirs(
- const string& title) const;
+ const std::string& title) const;
/**
* @brief Same as query_destination_paths but returns only one path.
*/
Path query_destination_path(
- const string& title,
+ const std::string& title,
const std::string& extensions,
const std::string& hint = "") const;
@@ -95,11 +96,11 @@ public:
* title.
*/
bool report_issue(IssueType issuetype,
- const string& description,
- const string& brief);
+ const std::string& description,
+ const std::string& brief);
bool report_issue(IssueType issuetype,
- const string& description);
+ const std::string& description);
/**
* @brief Return the global progress indicator for the current controller.
@@ -150,12 +151,12 @@ protected:
*/
ProgresIndicatorPtr create_progress_indicator(
unsigned statenum,
- const string& title,
- const string& firstmsg) const;
+ const std::string& title,
+ const std::string& firstmsg) const;
ProgresIndicatorPtr create_progress_indicator(
unsigned statenum,
- const string& title) const;
+ const std::string& title) const;
// This is a global progress indicator placeholder. In the Slic3r UI it can
// contain the progress indicator on the statusbar.
@@ -167,24 +168,6 @@ protected:
*/
class PrintController: public AppControllerBoilerplate {
Print *print_ = nullptr;
-protected:
-
- void make_skirt();
- void make_brim();
- void make_wipe_tower();
-
- void make_perimeters(PrintObject *pobj);
- void infill(PrintObject *pobj);
- void gen_support_material(PrintObject *pobj);
-
- /**
- * @brief Slice one pront object.
- * @param pobj The print object.
- */
- void slice(PrintObject *pobj);
-
- void slice(ProgresIndicatorPtr pri);
-
public:
// Must be public for perl to use it
@@ -199,11 +182,6 @@ public:
return PrintController::Ptr( new PrintController(print) );
}
- /**
- * @brief Slice the loaded print scene.
- */
- void slice();
-
const PrintConfig& config() const;
};
@@ -248,7 +226,7 @@ public:
* In perl we have a progress indicating status bar on the bottom of the
* window which is defined and created in perl. We can pass the ID-s of the
* gauge and the statusbar id and make a wrapper implementation of the
- * IProgressIndicator interface so we can use this GUI widget from C++.
+ * ProgressIndicator interface so we can use this GUI widget from C++.
*
* This function should be called from perl.
*
diff --git a/xs/src/slic3r/AppControllerWx.cpp b/xs/src/slic3r/AppControllerWx.cpp
index 4e116c7b9..36a465919 100644
--- a/xs/src/slic3r/AppControllerWx.cpp
+++ b/xs/src/slic3r/AppControllerWx.cpp
@@ -32,11 +32,11 @@ void AppControllerBoilerplate::process_events()
AppControllerBoilerplate::PathList
AppControllerBoilerplate::query_destination_paths(
- const string &title,
+ const std::string &title,
const std::string &extensions) const
{
- wxFileDialog dlg(wxTheApp->GetTopWindow(), title );
+ wxFileDialog dlg(wxTheApp->GetTopWindow(), _(title) );
dlg.SetWildcard(extensions);
dlg.ShowModal();
@@ -52,11 +52,11 @@ AppControllerBoilerplate::query_destination_paths(
AppControllerBoilerplate::Path
AppControllerBoilerplate::query_destination_path(
- const string &title,
+ const std::string &title,
const std::string &extensions,
const std::string& hint) const
{
- wxFileDialog dlg(wxTheApp->GetTopWindow(), title );
+ wxFileDialog dlg(wxTheApp->GetTopWindow(), _(title) );
dlg.SetWildcard(extensions);
dlg.SetFilename(hint);
@@ -71,8 +71,8 @@ AppControllerBoilerplate::query_destination_path(
}
bool AppControllerBoilerplate::report_issue(IssueType issuetype,
- const string &description,
- const string &brief)
+ const std::string &description,
+ const std::string &brief)
{
auto icon = wxICON_INFORMATION;
auto style = wxOK|wxCENTRE;
@@ -84,15 +84,15 @@ bool AppControllerBoilerplate::report_issue(IssueType issuetype,
case IssueType::FATAL: icon = wxICON_ERROR;
}
- auto ret = wxMessageBox(description, brief, icon | style);
+ auto ret = wxMessageBox(_(description), _(brief), icon | style);
return ret != wxCANCEL;
}
bool AppControllerBoilerplate::report_issue(
AppControllerBoilerplate::IssueType issuetype,
- const string &description)
+ const std::string &description)
{
- return report_issue(issuetype, description, string());
+ return report_issue(issuetype, description, std::string());
}
wxDEFINE_EVENT(PROGRESS_STATUS_UPDATE_EVENT, wxCommandEvent);
@@ -104,10 +104,10 @@ namespace {
* the main thread as well.
*/
class GuiProgressIndicator:
- public IProgressIndicator, public wxEvtHandler {
+ public ProgressIndicator, public wxEvtHandler {
wxProgressDialog gauge_;
- using Base = IProgressIndicator;
+ using Base = ProgressIndicator;
wxString message_;
int range_; wxString title_;
bool is_asynch_ = false;
@@ -136,8 +136,8 @@ public:
/// Get the mode of parallel operation.
inline bool asynch() const { return is_asynch_; }
- inline GuiProgressIndicator(int range, const string& title,
- const string& firstmsg) :
+ inline GuiProgressIndicator(int range, const wxString& title,
+ const wxString& firstmsg) :
gauge_(title, firstmsg, range, wxTheApp->GetTopWindow(),
wxPD_APP_MODAL | wxPD_AUTO_HIDE),
message_(firstmsg),
@@ -151,11 +151,6 @@ public:
this, id_);
}
- virtual void cancel() override {
- update(max(), "Abort");
- IProgressIndicator::cancel();
- }
-
virtual void state(float val) override {
state(static_cast<unsigned>(val));
}
@@ -170,26 +165,28 @@ public:
} else _state(st);
}
- virtual void message(const string & msg) override {
- message_ = msg;
+ virtual void message(const std::string & msg) override {
+ message_ = _(msg);
}
- virtual void messageFmt(const string& fmt, ...) {
+ virtual void messageFmt(const std::string& fmt, ...) {
va_list arglist;
va_start(arglist, fmt);
- message_ = wxString::Format(wxString(fmt), arglist);
+ message_ = wxString::Format(_(fmt), arglist);
va_end(arglist);
}
- virtual void title(const string & title) override {
- title_ = title;
+ virtual void title(const std::string & title) override {
+ title_ = _(title);
}
};
}
AppControllerBoilerplate::ProgresIndicatorPtr
AppControllerBoilerplate::create_progress_indicator(
- unsigned statenum, const string& title, const string& firstmsg) const
+ unsigned statenum,
+ const std::string& title,
+ const std::string& firstmsg) const
{
auto pri =
std::make_shared<GuiProgressIndicator>(statenum, title, firstmsg);
@@ -202,20 +199,20 @@ AppControllerBoilerplate::create_progress_indicator(
}
AppControllerBoilerplate::ProgresIndicatorPtr
-AppControllerBoilerplate::create_progress_indicator(unsigned statenum,
- const string &title) const
+AppControllerBoilerplate::create_progress_indicator(
+ unsigned statenum, const std::string &title) const
{
- return create_progress_indicator(statenum, title, string());
+ return create_progress_indicator(statenum, title, std::string());
}
namespace {
// A wrapper progress indicator class around the statusbar created in perl.
-class Wrapper: public IProgressIndicator, public wxEvtHandler {
+class Wrapper: public ProgressIndicator, public wxEvtHandler {
wxGauge *gauge_;
wxStatusBar *stbar_;
- using Base = IProgressIndicator;
- std::string message_;
+ using Base = ProgressIndicator;
+ wxString message_;
AppControllerBoilerplate& ctl_;
void showProgress(bool show = true) {
@@ -223,7 +220,7 @@ class Wrapper: public IProgressIndicator, public wxEvtHandler {
}
void _state(unsigned st) {
- if( st <= IProgressIndicator::max() ) {
+ if( st <= ProgressIndicator::max() ) {
Base::state(st);
if(!gauge_->IsShown()) showProgress(true);
@@ -266,7 +263,7 @@ public:
virtual void max(float val) override {
if(val > 1.0) {
gauge_->SetRange(static_cast<int>(val));
- IProgressIndicator::max(val);
+ ProgressIndicator::max(val);
}
}
@@ -280,18 +277,18 @@ public:
}
}
- virtual void message(const string & msg) override {
- message_ = msg;
+ virtual void message(const std::string & msg) override {
+ message_ = _(msg);
}
- virtual void message_fmt(const string& fmt, ...) override {
+ virtual void message_fmt(const std::string& fmt, ...) override {
va_list arglist;
va_start(arglist, fmt);
- message_ = wxString::Format(fmt, arglist);
+ message_ = wxString::Format(_(fmt), arglist);
va_end(arglist);
}
- virtual void title(const string & /*title*/) override {}
+ virtual void title(const std::string & /*title*/) override {}
};
}
diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp
index 3f01eb20c..9cf55513c 100644
--- a/xs/src/slic3r/GUI/3DScene.cpp
+++ b/xs/src/slic3r/GUI/3DScene.cpp
@@ -202,7 +202,9 @@ const float GLVolume::SELECTED_OUTSIDE_COLOR[4] = { 0.19f, 0.58f, 1.0f, 1.0f };
GLVolume::GLVolume(float r, float g, float b, float a)
: m_angle_z(0.0f)
, m_scale_factor(1.0f)
- , m_dirty(true)
+ , m_transformed_bounding_box_dirty(true)
+ , m_transformed_convex_hull_bounding_box_dirty(true)
+ , m_convex_hull(nullptr)
, composite_id(-1)
, select_group_id(-1)
, drag_group_id(-1)
@@ -219,8 +221,6 @@ GLVolume::GLVolume(float r, float g, float b, float a)
, tverts_range(0, size_t(-1))
, qverts_range(0, size_t(-1))
{
- m_world_mat = std::vector<float>(UNIT_MATRIX, std::end(UNIT_MATRIX));
-
color[0] = r;
color[1] = g;
color[2] = b;
@@ -264,45 +264,76 @@ const Pointf3& GLVolume::get_origin() const
void GLVolume::set_origin(const Pointf3& origin)
{
- m_origin = origin;
- m_dirty = true;
+ if (m_origin != origin)
+ {
+ m_origin = origin;
+ m_transformed_bounding_box_dirty = true;
+ m_transformed_convex_hull_bounding_box_dirty = true;
+ }
}
void GLVolume::set_angle_z(float angle_z)
{
- m_angle_z = angle_z;
- m_dirty = true;
+ if (m_angle_z != angle_z)
+ {
+ m_angle_z = angle_z;
+ m_transformed_bounding_box_dirty = true;
+ m_transformed_convex_hull_bounding_box_dirty = true;
+ }
}
void GLVolume::set_scale_factor(float scale_factor)
{
- m_scale_factor = scale_factor;
- m_dirty = true;
+ if (m_scale_factor != scale_factor)
+ {
+ m_scale_factor = scale_factor;
+ m_transformed_bounding_box_dirty = true;
+ m_transformed_convex_hull_bounding_box_dirty = true;
+ }
}
-const std::vector<float>& GLVolume::world_matrix() const
+void GLVolume::set_convex_hull(const TriangleMesh& convex_hull)
{
- if (m_dirty)
- {
- Eigen::Transform<float, 3, Eigen::Affine> m = Eigen::Transform<float, 3, Eigen::Affine>::Identity();
- m.translate(Eigen::Vector3f((float)m_origin.x, (float)m_origin.y, (float)m_origin.z));
- m.rotate(Eigen::AngleAxisf(m_angle_z, Eigen::Vector3f::UnitZ()));
- m.scale(m_scale_factor);
- ::memcpy((void*)m_world_mat.data(), (const void*)m.data(), 16 * sizeof(float));
- m_dirty = false;
- }
+ m_convex_hull = &convex_hull;
+}
- return m_world_mat;
+std::vector<float> GLVolume::world_matrix() const
+{
+ std::vector<float> world_mat(UNIT_MATRIX, std::end(UNIT_MATRIX));
+ Eigen::Transform<float, 3, Eigen::Affine> m = Eigen::Transform<float, 3, Eigen::Affine>::Identity();
+ m.translate(Eigen::Vector3f((float)m_origin.x, (float)m_origin.y, (float)m_origin.z));
+ m.rotate(Eigen::AngleAxisf(m_angle_z, Eigen::Vector3f::UnitZ()));
+ m.scale(m_scale_factor);
+ ::memcpy((void*)world_mat.data(), (const void*)m.data(), 16 * sizeof(float));
+ return world_mat;
}
BoundingBoxf3 GLVolume::transformed_bounding_box() const
{
- if (m_dirty)
+ if (m_transformed_bounding_box_dirty)
+ {
m_transformed_bounding_box = bounding_box.transformed(world_matrix());
+ m_transformed_bounding_box_dirty = false;
+ }
return m_transformed_bounding_box;
}
+BoundingBoxf3 GLVolume::transformed_convex_hull_bounding_box() const
+{
+ if (m_transformed_convex_hull_bounding_box_dirty)
+ {
+ if ((m_convex_hull != nullptr) && (m_convex_hull->stl.stats.number_of_facets > 0))
+ m_transformed_convex_hull_bounding_box = m_convex_hull->transformed_bounding_box(world_matrix());
+ else
+ m_transformed_convex_hull_bounding_box = bounding_box.transformed(world_matrix());
+
+ m_transformed_convex_hull_bounding_box_dirty = false;
+ }
+
+ return m_transformed_convex_hull_bounding_box;
+}
+
void GLVolume::set_range(double min_z, double max_z)
{
this->qverts_range.first = 0;
@@ -629,6 +660,7 @@ std::vector<int> GLVolumeCollection::load_object(
if (!model_volume->modifier)
{
+ v.set_convex_hull(model_volume->get_convex_hull());
v.layer_height_texture = layer_height_texture;
if (extruder_id != -1)
v.extruder_id = extruder_id;
@@ -801,9 +833,9 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M
for (GLVolume* volume : this->volumes)
{
- if ((volume != nullptr) && !volume->is_modifier)
+ if ((volume != nullptr) && !volume->is_modifier && (!volume->is_wipe_tower || (volume->is_wipe_tower && volume->shader_outside_printer_detection_enabled)))
{
- const BoundingBoxf3& bb = volume->transformed_bounding_box();
+ const BoundingBoxf3& bb = volume->transformed_convex_hull_bounding_box();
bool contained = print_volume.contains(bb);
all_contained &= contained;
@@ -1150,7 +1182,7 @@ static void thick_lines_to_indexed_vertex_array(
b1_prev = b1;
v_prev = v;
- if (bottom_z_different)
+ if (bottom_z_different && (closed || (!is_first && !is_last)))
{
// Found a change of the layer thickness -> Add a cap at the beginning of this segment.
volume.push_quad(idx_a[BOTTOM], idx_a[RIGHT], idx_a[TOP], idx_a[LEFT]);
@@ -1158,10 +1190,10 @@ static void thick_lines_to_indexed_vertex_array(
if (! closed) {
// Terminate open paths with caps.
- if (is_first && !bottom_z_different)
+ if (is_first)
volume.push_quad(idx_a[BOTTOM], idx_a[RIGHT], idx_a[TOP], idx_a[LEFT]);
// We don't use 'else' because both cases are true if we have only one line.
- if (is_last && !bottom_z_different)
+ if (is_last)
volume.push_quad(idx_b[BOTTOM], idx_b[LEFT], idx_b[TOP], idx_b[RIGHT]);
}
diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp
index a552b32a7..5cd144c68 100644
--- a/xs/src/slic3r/GUI/3DScene.hpp
+++ b/xs/src/slic3r/GUI/3DScene.hpp
@@ -260,12 +260,16 @@ private:
float m_angle_z;
// Scale factor of the volume to be rendered.
float m_scale_factor;
- // World matrix of the volume to be rendered.
- std::vector<float> m_world_mat;
// Bounding box of this volume, in unscaled coordinates.
mutable BoundingBoxf3 m_transformed_bounding_box;
- // Whether or not is needed to recalculate the world matrix.
- mutable bool m_dirty;
+ // Whether or not is needed to recalculate the transformed bounding box.
+ mutable bool m_transformed_bounding_box_dirty;
+ // Pointer to convex hull of the original mesh, if any.
+ const TriangleMesh* m_convex_hull;
+ // Bounding box of this volume, in unscaled coordinates.
+ mutable BoundingBoxf3 m_transformed_convex_hull_bounding_box;
+ // Whether or not is needed to recalculate the transformed convex hull bounding box.
+ mutable bool m_transformed_convex_hull_bounding_box_dirty;
public:
@@ -323,13 +327,15 @@ public:
void set_origin(const Pointf3& origin);
void set_angle_z(float angle_z);
void set_scale_factor(float scale_factor);
+ void set_convex_hull(const TriangleMesh& convex_hull);
int object_idx() const { return this->composite_id / 1000000; }
int volume_idx() const { return (this->composite_id / 1000) % 1000; }
int instance_idx() const { return this->composite_id % 1000; }
- const std::vector<float>& world_matrix() const;
+ std::vector<float> world_matrix() const;
BoundingBoxf3 transformed_bounding_box() const;
+ BoundingBoxf3 transformed_convex_hull_bounding_box() const;
bool empty() const { return this->indexed_vertex_array.empty(); }
bool indexed() const { return this->indexed_vertex_array.indexed(); }
diff --git a/xs/src/slic3r/GUI/Field.cpp b/xs/src/slic3r/GUI/Field.cpp
index 85fa790a5..c7f1d48ff 100644
--- a/xs/src/slic3r/GUI/Field.cpp
+++ b/xs/src/slic3r/GUI/Field.cpp
@@ -224,24 +224,20 @@ namespace Slic3r { namespace GUI {
}), temp->GetId());
#endif // __WXGTK__
- temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent)
+ temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent& evt)
{
#ifdef __WXGTK__
- bChangedValueEvent = true;
-#else
- on_change_field();
+ if (bChangedValueEvent)
#endif //__WXGTK__
+ on_change_field();
}), temp->GetId());
#ifdef __WXGTK__
- temp->Bind(wxEVT_KEY_UP, [this](wxKeyEvent& event)
- {
- if (bChangedValueEvent) {
- on_change_field();
- bChangedValueEvent = false;
- }
- event.Skip();
- });
+ // to correct value updating on GTK we should:
+ // call on_change_field() on wxEVT_KEY_UP instead of wxEVT_TEXT
+ // and prevent value updating on wxEVT_KEY_DOWN
+ temp->Bind(wxEVT_KEY_DOWN, &TextCtrl::change_field_value, this);
+ temp->Bind(wxEVT_KEY_UP, &TextCtrl::change_field_value, this);
#endif //__WXGTK__
// select all text using Ctrl+A
@@ -267,6 +263,15 @@ namespace Slic3r { namespace GUI {
void TextCtrl::enable() { dynamic_cast<wxTextCtrl*>(window)->Enable(); dynamic_cast<wxTextCtrl*>(window)->SetEditable(true); }
void TextCtrl::disable() { dynamic_cast<wxTextCtrl*>(window)->Disable(); dynamic_cast<wxTextCtrl*>(window)->SetEditable(false); }
+#ifdef __WXGTK__
+ void TextCtrl::change_field_value(wxEvent& event)
+ {
+ if (bChangedValueEvent = event.GetEventType()==wxEVT_KEY_UP)
+ on_change_field();
+ event.Skip();
+ };
+#endif //__WXGTK__
+
void CheckBox::BUILD() {
auto size = wxSize(wxDefaultSize);
if (m_opt.height >= 0) size.SetHeight(m_opt.height);
@@ -586,6 +591,8 @@ boost::any& Choice::get_value()
m_value = static_cast<SupportMaterialPattern>(ret_enum);
else if (m_opt_id.compare("seam_position") == 0)
m_value = static_cast<SeamPosition>(ret_enum);
+ else if (m_opt_id.compare("host_type") == 0)
+ m_value = static_cast<PrintHostType>(ret_enum);
}
return m_value;
diff --git a/xs/src/slic3r/GUI/Field.hpp b/xs/src/slic3r/GUI/Field.hpp
index db8d2a408..923f0fd7e 100644
--- a/xs/src/slic3r/GUI/Field.hpp
+++ b/xs/src/slic3r/GUI/Field.hpp
@@ -222,7 +222,8 @@ inline bool is_sizer_field(const t_field& obj) { return !is_bad_field(obj) && ob
class TextCtrl : public Field {
using Field::Field;
#ifdef __WXGTK__
- bool bChangedValueEvent = false;
+ bool bChangedValueEvent = true;
+ void change_field_value(wxEvent& event);
#endif //__WXGTK__
public:
TextCtrl(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id) {}
diff --git a/xs/src/slic3r/GUI/FirmwareDialog.cpp b/xs/src/slic3r/GUI/FirmwareDialog.cpp
index 77e70c49b..d0cd9f8cf 100644
--- a/xs/src/slic3r/GUI/FirmwareDialog.cpp
+++ b/xs/src/slic3r/GUI/FirmwareDialog.cpp
@@ -551,8 +551,10 @@ void FirmwareDialog::priv::perform_upload()
// because the dialog ensures it doesn't exit before the background thread is done.
auto q = this->q;
- this->avrdude = avrdude
- .on_run([this]() {
+ avrdude
+ .on_run([this](AvrDude::Ptr avrdude) {
+ this->avrdude = std::move(avrdude);
+
try {
switch (this->hex_file.device) {
case HexFile::DEV_MK3:
diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp
index ab4095e6f..062b57b49 100644
--- a/xs/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp
@@ -1,5 +1,6 @@
#include "GLCanvas3D.hpp"
+#include "../../admesh/stl.h"
#include "../../libslic3r/libslic3r.h"
#include "../../slic3r/GUI/3DScene.hpp"
#include "../../slic3r/GUI/GLShader.hpp"
@@ -1154,6 +1155,18 @@ bool GLCanvas3D::Gizmos::init()
m_gizmos.insert(GizmosMap::value_type(Rotate, gizmo));
+ gizmo = new GLGizmoFlatten;
+ if (gizmo == nullptr)
+ return false;
+
+ if (!gizmo->init()) {
+ _reset();
+ return false;
+ }
+
+ m_gizmos.insert(GizmosMap::value_type(Flatten, gizmo));
+
+
return true;
}
@@ -1387,22 +1400,46 @@ void GLCanvas3D::Gizmos::set_angle_z(float angle_z)
reinterpret_cast<GLGizmoRotate*>(it->second)->set_angle_z(angle_z);
}
-void GLCanvas3D::Gizmos::render(const GLCanvas3D& canvas, const BoundingBoxf3& box) const
+Pointf3 GLCanvas3D::Gizmos::get_flattening_normal() const
+{
+ if (!m_enabled)
+ return Pointf3(0.f, 0.f, 0.f);
+
+ GizmosMap::const_iterator it = m_gizmos.find(Flatten);
+ return (it != m_gizmos.end()) ? reinterpret_cast<GLGizmoFlatten*>(it->second)->get_flattening_normal() : Pointf3(0.f, 0.f, 0.f);
+}
+
+void GLCanvas3D::Gizmos::set_flattening_data(const ModelObject* model_object)
+{
+ if (!m_enabled)
+ return;
+
+ GizmosMap::const_iterator it = m_gizmos.find(Flatten);
+ if (it != m_gizmos.end())
+ reinterpret_cast<GLGizmoFlatten*>(it->second)->set_flattening_data(model_object);
+}
+
+void GLCanvas3D::Gizmos::render(const GLCanvas3D& canvas, const BoundingBoxf3& box, RenderOrder render_order) const
{
if (!m_enabled)
return;
::glDisable(GL_DEPTH_TEST);
- if (box.radius() > 0.0)
- _render_current_gizmo(box);
+ if ((render_order == BeforeBed && dynamic_cast<GLGizmoFlatten*>(_get_current()))
+ || (render_order == AfterBed && !dynamic_cast<GLGizmoFlatten*>(_get_current()))) {
+ if (box.radius() > 0.0)
+ _render_current_gizmo(box);
+ }
- ::glPushMatrix();
- ::glLoadIdentity();
+ if (render_order == AfterBed) {
+ ::glPushMatrix();
+ ::glLoadIdentity();
- _render_overlay(canvas);
+ _render_overlay(canvas);
- ::glPopMatrix();
+ ::glPopMatrix();
+ }
}
void GLCanvas3D::Gizmos::render_current_gizmo_for_picking_pass(const BoundingBoxf3& box) const
@@ -2170,6 +2207,7 @@ void GLCanvas3D::update_gizmos_data()
{
m_gizmos.set_scale(model_instance->scaling_factor);
m_gizmos.set_angle_z(model_instance->rotation);
+ m_gizmos.set_flattening_data(model_object);
}
}
}
@@ -2177,6 +2215,7 @@ void GLCanvas3D::update_gizmos_data()
{
m_gizmos.set_scale(1.0f);
m_gizmos.set_angle_z(0.0f);
+ m_gizmos.set_flattening_data(nullptr);
}
}
@@ -2215,6 +2254,7 @@ void GLCanvas3D::render()
_render_axes(false);
}
_render_objects();
+ _render_gizmo(Gizmos::RenderOrder::BeforeBed);
// textured bed needs to be rendered after objects
if (!is_custom_bed)
{
@@ -2224,7 +2264,7 @@ void GLCanvas3D::render()
_render_cutting_plane();
_render_warning_texture();
_render_legend_texture();
- _render_gizmo();
+ _render_gizmo(Gizmos::RenderOrder::AfterBed);
_render_layer_editing_overlay();
m_canvas->SwapBuffers();
@@ -2760,6 +2800,16 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
m_gizmos.start_dragging();
m_mouse.drag.gizmo_volume_idx = _get_first_selected_volume_id(selected_object_idx);
m_dirty = true;
+
+ if (m_gizmos.get_current_type() == Gizmos::Flatten) {
+ // Rotate the object so the normal points downward:
+ Pointf3 normal = m_gizmos.get_flattening_normal();
+ if (normal.x != 0.f || normal.y != 0.f || normal.z != 0.f) {
+ Pointf3 axis = normal.z > 0.999f ? Pointf3(1, 0, 0) : cross(normal, Pointf3(0.f, 0.f, -1.f));
+ float angle = -acos(-normal.z);
+ m_on_gizmo_rotate_callback.call(angle, axis.x, axis.y, axis.z);
+ }
+ }
}
else
{
@@ -3017,6 +3067,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
}
_on_move(volume_idxs);
+
+ // force re-selection of the wipe tower, if needed
+ if ((volume_idxs.size() == 1) && m_volumes.volumes[volume_idxs[0]]->is_wipe_tower)
+ select_volume(volume_idxs[0]);
}
else if (!m_mouse.dragging && (m_hover_volume_id == -1) && !gizmos_overlay_contains_mouse && !m_gizmos.is_dragging() && !is_layers_editing_enabled())
{
@@ -3708,9 +3762,9 @@ void GLCanvas3D::_render_volumes(bool fake_colors) const
::glDisable(GL_LIGHTING);
}
-void GLCanvas3D::_render_gizmo() const
+void GLCanvas3D::_render_gizmo(Gizmos::RenderOrder render_order) const
{
- m_gizmos.render(*this, _selected_volumes_bounding_box());
+ m_gizmos.render(*this, _selected_volumes_bounding_box(), render_order);
}
float GLCanvas3D::_get_layers_editing_cursor_z_relative() const
diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp
index ae20882fc..f09bd4b20 100644
--- a/xs/src/slic3r/GUI/GLCanvas3D.hpp
+++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp
@@ -338,8 +338,13 @@ public:
Undefined,
Scale,
Rotate,
+ Flatten,
Num_Types
};
+ enum RenderOrder : unsigned char {
+ BeforeBed,
+ AfterBed
+ };
private:
bool m_enabled;
@@ -382,7 +387,10 @@ public:
float get_angle_z() const;
void set_angle_z(float angle_z);
- void render(const GLCanvas3D& canvas, const BoundingBoxf3& box) const;
+ void set_flattening_data(const ModelObject* model_object);
+ Pointf3 get_flattening_normal() const;
+
+ void render(const GLCanvas3D& canvas, const BoundingBoxf3& box, RenderOrder render_order) const;
void render_current_gizmo_for_picking_pass(const BoundingBoxf3& box) const;
private:
@@ -629,7 +637,7 @@ private:
void _render_legend_texture() const;
void _render_layer_editing_overlay() const;
void _render_volumes(bool fake_colors) const;
- void _render_gizmo() const;
+ void _render_gizmo(Gizmos::RenderOrder render_order) const;
float _get_layers_editing_cursor_z_relative() const;
void _perform_layer_editing_action(wxMouseEvent* evt = nullptr);
diff --git a/xs/src/slic3r/GUI/GLGizmo.cpp b/xs/src/slic3r/GUI/GLGizmo.cpp
index 47b01e8a2..bbd8f44eb 100644
--- a/xs/src/slic3r/GUI/GLGizmo.cpp
+++ b/xs/src/slic3r/GUI/GLGizmo.cpp
@@ -2,10 +2,13 @@
#include "../../libslic3r/Utils.hpp"
#include "../../libslic3r/BoundingBox.hpp"
+#include "../../libslic3r/Model.hpp"
+#include "../../libslic3r/Geometry.hpp"
#include <GL/glew.h>
#include <iostream>
+#include <numeric>
namespace Slic3r {
namespace GUI {
@@ -110,7 +113,7 @@ int GLGizmoBase::get_hover_id() const
void GLGizmoBase::set_hover_id(int id)
{
- if (id < (int)m_grabbers.size())
+ //if (id < (int)m_grabbers.size())
m_hover_id = id;
}
@@ -189,7 +192,7 @@ GLGizmoRotate::GLGizmoRotate()
, m_angle_z(0.0f)
, m_center(Pointf(0.0, 0.0))
, m_radius(0.0f)
- , m_keep_radius(false)
+ , m_keep_initial_values(false)
{
}
@@ -229,7 +232,7 @@ bool GLGizmoRotate::on_init()
void GLGizmoRotate::on_set_state()
{
- m_keep_radius = (m_state == On) ? false : true;
+ m_keep_initial_values = (m_state == On) ? false : true;
}
void GLGizmoRotate::on_update(const Pointf& mouse_pos)
@@ -255,19 +258,19 @@ void GLGizmoRotate::on_update(const Pointf& mouse_pos)
void GLGizmoRotate::on_refresh()
{
- m_keep_radius = false;
+ m_keep_initial_values = false;
}
void GLGizmoRotate::on_render(const BoundingBoxf3& box) const
{
::glDisable(GL_DEPTH_TEST);
- const Pointf3& size = box.size();
- m_center = box.center();
- if (!m_keep_radius)
+ if (!m_keep_initial_values)
{
+ const Pointf3& size = box.size();
+ m_center = box.center();
m_radius = Offset + ::sqrt(sqr(0.5f * size.x) + sqr(0.5f * size.y));
- m_keep_radius = true;
+ m_keep_initial_values = true;
}
::glLineWidth(2.0f);
@@ -502,5 +505,326 @@ void GLGizmoScale::on_render_for_picking(const BoundingBoxf3& box) const
render_grabbers();
}
+
+GLGizmoFlatten::GLGizmoFlatten()
+ : GLGizmoBase(),
+ m_normal(Pointf3(0.f, 0.f, 0.f))
+{}
+
+
+bool GLGizmoFlatten::on_init()
+{
+ std::string path = resources_dir() + "/icons/overlay/";
+
+ std::string filename = path + "layflat_off.png";
+ if (!m_textures[Off].load_from_file(filename, false))
+ return false;
+
+ filename = path + "layflat_hover.png";
+ if (!m_textures[Hover].load_from_file(filename, false))
+ return false;
+
+ filename = path + "layflat_on.png";
+ if (!m_textures[On].load_from_file(filename, false))
+ return false;
+
+ return true;
+}
+
+void GLGizmoFlatten::on_start_dragging()
+{
+ if (m_hover_id != -1)
+ m_normal = m_planes[m_hover_id].normal;
+}
+
+void GLGizmoFlatten::on_render(const BoundingBoxf3& box) const
+{
+ // the dragged_offset is a vector measuring where was the object moved
+ // with the gizmo being on. This is reset in set_flattening_data and
+ // does not work correctly when there are multiple copies.
+ if (!m_center) // this is the first bounding box that we see
+ m_center.reset(new Pointf3(box.center().x, box.center().y));
+ Pointf3 dragged_offset = box.center() - *m_center;
+
+ bool blending_was_enabled = ::glIsEnabled(GL_BLEND);
+ bool depth_test_was_enabled = ::glIsEnabled(GL_DEPTH_TEST);
+ ::glEnable(GL_BLEND);
+ ::glEnable(GL_DEPTH_TEST);
+
+ for (int i=0; i<(int)m_planes.size(); ++i) {
+ if (i == m_hover_id)
+ ::glColor4f(0.9f, 0.9f, 0.9f, 0.75f);
+ else
+ ::glColor4f(0.9f, 0.9f, 0.9f, 0.5f);
+
+ for (Pointf offset : m_instances_positions) {
+ offset += dragged_offset;
+ ::glBegin(GL_POLYGON);
+ for (const auto& vertex : m_planes[i].vertices)
+ ::glVertex3f((GLfloat)vertex.x + offset.x, (GLfloat)vertex.y + offset.y, (GLfloat)vertex.z);
+ ::glEnd();
+ }
+ }
+
+ if (!blending_was_enabled)
+ ::glDisable(GL_BLEND);
+ if (!depth_test_was_enabled)
+ ::glDisable(GL_DEPTH_TEST);
+}
+
+void GLGizmoFlatten::on_render_for_picking(const BoundingBoxf3& box) const
+{
+ static const GLfloat INV_255 = 1.0f / 255.0f;
+
+ ::glDisable(GL_DEPTH_TEST);
+
+ for (unsigned int i = 0; i < m_planes.size(); ++i)
+ {
+ ::glColor3f(1.f, 1.f, (254.0f - (float)i) * INV_255);
+ for (const Pointf& offset : m_instances_positions) {
+ ::glBegin(GL_POLYGON);
+ for (const auto& vertex : m_planes[i].vertices)
+ ::glVertex3f((GLfloat)vertex.x + offset.x, (GLfloat)vertex.y + offset.y, (GLfloat)vertex.z);
+ ::glEnd();
+ }
+ }
+}
+
+
+// TODO - remove and use Eigen instead
+static Pointf3 super_rotation(Pointf3 axis, float angle, const Pointf3& point)
+{
+ axis = normalize(axis);
+ const float& x = axis.x;
+ const float& y = axis.y;
+ const float& z = axis.z;
+ float s = sin(angle);
+ float c = cos(angle);
+ float D = 1-c;
+ float matrix[3][3] = { { c + x*x*D, x*y*D-z*s, x*z*D+y*s },
+ { y*x*D+z*s, c+y*y*D, y*z*D-x*s },
+ { z*x*D-y*s, z*y*D+x*s, c+z*z*D } };
+ float in[3] = { (float)point.x, (float)point.y, (float)point.z };
+ float out[3] = { 0, 0, 0 };
+
+ for (unsigned char i=0; i<3; ++i)
+ for (unsigned char j=0; j<3; ++j)
+ out[i] += matrix[i][j] * in[j];
+
+ return Pointf3(out[0], out[1], out[2]);
+}
+
+
+void GLGizmoFlatten::set_flattening_data(const ModelObject* model_object)
+{
+ m_center.release(); // object is not being dragged (this would not be called otherwise) - we must forget about the bounding box position...
+ m_model_object = model_object;
+
+ // ...and save the updated positions of the object instances:
+ if (m_model_object && !m_model_object->instances.empty()) {
+ m_instances_positions.clear();
+ for (const auto* instance : m_model_object->instances)
+ m_instances_positions.emplace_back(instance->offset);
+ }
+
+ if (is_plane_update_necessary())
+ update_planes();
+}
+
+void GLGizmoFlatten::update_planes()
+{
+ TriangleMesh ch;
+ for (const ModelVolume* vol : m_model_object->volumes)
+ ch.merge(vol->get_convex_hull());
+ ch = ch.convex_hull_3d();
+ ch.scale(m_model_object->instances.front()->scaling_factor);
+ ch.rotate_z(m_model_object->instances.front()->rotation);
+
+ m_planes.clear();
+
+ // Now we'll go through all the facets and append Points of facets sharing the same normal:
+ const int num_of_facets = ch.stl.stats.number_of_facets;
+ std::vector<int> facet_queue(num_of_facets, 0);
+ std::vector<bool> facet_visited(num_of_facets, false);
+ int facet_queue_cnt = 0;
+ const stl_normal* normal_ptr = nullptr;
+ while (1) {
+ // Find next unvisited triangle:
+ int facet_idx = 0;
+ for (; facet_idx < num_of_facets; ++ facet_idx)
+ if (!facet_visited[facet_idx]) {
+ facet_queue[facet_queue_cnt ++] = facet_idx;
+ facet_visited[facet_idx] = true;
+ normal_ptr = &ch.stl.facet_start[facet_idx].normal;
+ m_planes.emplace_back();
+ break;
+ }
+ if (facet_idx == num_of_facets)
+ break; // Everything was visited already
+
+ while (facet_queue_cnt > 0) {
+ int facet_idx = facet_queue[-- facet_queue_cnt];
+ const stl_normal* this_normal_ptr = &ch.stl.facet_start[facet_idx].normal;
+ //if (this_normal_ptr->x == normal_ptr->x && this_normal_ptr->y == normal_ptr->y && this_normal_ptr->z == normal_ptr->z) {
+ if (std::abs(this_normal_ptr->x-normal_ptr->x) < 0.001 && std::abs(this_normal_ptr->y-normal_ptr->y) < 0.001 && std::abs(this_normal_ptr->z-normal_ptr->z) < 0.001) {
+ stl_vertex* first_vertex = ch.stl.facet_start[facet_idx].vertex;
+ for (int j=0; j<3; ++j)
+ m_planes.back().vertices.emplace_back(first_vertex[j].x, first_vertex[j].y, first_vertex[j].z);
+
+ facet_visited[facet_idx] = true;
+ for (int j = 0; j < 3; ++ j) {
+ int neighbor_idx = ch.stl.neighbors_start[facet_idx].neighbor[j];
+ if (! facet_visited[neighbor_idx])
+ facet_queue[facet_queue_cnt ++] = neighbor_idx;
+ }
+ }
+ }
+ m_planes.back().normal = Pointf3(normal_ptr->x, normal_ptr->y, normal_ptr->z);
+
+ // if this is a just a very small triangle, remove it to speed up further calculations (it would be rejected anyway):
+ if (m_planes.back().vertices.size() == 3 &&
+ ( m_planes.back().vertices[0].distance_to(m_planes.back().vertices[1]) < 1.f
+ || m_planes.back().vertices[0].distance_to(m_planes.back().vertices[2]) < 1.f))
+ m_planes.pop_back();
+ }
+
+ // Now we'll go through all the polygons, transform the points into xy plane to process them:
+ for (unsigned int polygon_id=0; polygon_id < m_planes.size(); ++polygon_id) {
+ Pointf3s& polygon = m_planes[polygon_id].vertices;
+ const Pointf3& normal = m_planes[polygon_id].normal;
+
+ // We are going to rotate about z and y to flatten the plane
+ float angle_z = 0.f;
+ float angle_y = 0.f;
+ if (std::abs(normal.y) > 0.001)
+ angle_z = -atan2(normal.y, normal.x); // angle to rotate so that normal ends up in xz-plane
+ if (std::abs(normal.x*cos(angle_z)-normal.y*sin(angle_z)) > 0.001)
+ angle_y = - atan2(normal.x*cos(angle_z)-normal.y*sin(angle_z), normal.z); // angle to rotate to make normal point upwards
+ else {
+ // In case it already was in z-direction, we must ensure it is not the wrong way:
+ angle_y = normal.z > 0.f ? 0 : -M_PI;
+ }
+
+ // Rotate all points to the xy plane:
+ for (auto& vertex : polygon) {
+ vertex = super_rotation(Pointf3(0,0,1), angle_z, vertex);
+ vertex = super_rotation(Pointf3(0,1,0), angle_y, vertex);
+ }
+ polygon = Slic3r::Geometry::convex_hull(polygon); // To remove the inner points
+
+ // We will calculate area of the polygon and discard ones that are too small
+ // The limit is more forgiving in case the normal is in the direction of the coordinate axes
+ const float minimal_area = (std::abs(normal.x) > 0.999f || std::abs(normal.y) > 0.999f || std::abs(normal.z) > 0.999f) ? 1.f : 20.f;
+ float& area = m_planes[polygon_id].area;
+ area = 0.f;
+ for (unsigned int i = 0; i < polygon.size(); i++) // Shoelace formula
+ area += polygon[i].x*polygon[i+1 < polygon.size() ? i+1 : 0 ].y - polygon[i+1 < polygon.size() ? i+1 : 0].x*polygon[i].y;
+ area = std::abs(area/2.f);
+ if (area < minimal_area) {
+ m_planes.erase(m_planes.begin()+(polygon_id--));
+ continue;
+ }
+
+ // We will shrink the polygon a little bit so it does not touch the object edges:
+ Pointf3 centroid = std::accumulate(polygon.begin(), polygon.end(), Pointf3(0.f, 0.f, 0.f));
+ centroid.scale(1.f/polygon.size());
+ for (auto& vertex : polygon)
+ vertex = 0.9f*vertex + 0.1f*centroid;
+
+ // Polygon is now simple and convex, we'll round the corners to make them look nicer.
+ // The algorithm takes a vertex, calculates middles of respective sides and moves the vertex
+ // towards their average (controlled by 'aggressivity'). This is repeated k times.
+ // In next iterations, the neighbours are not always taken at the middle (to increase the
+ // rounding effect at the corners, where we need it most).
+ const unsigned int k = 10; // number of iterations
+ const float aggressivity = 0.2f; // agressivity
+ const unsigned int N = polygon.size();
+ std::vector<std::pair<unsigned int, unsigned int>> neighbours;
+ if (k != 0) {
+ Pointf3s points_out(2*k*N); // vector long enough to store the future vertices
+ for (unsigned int j=0; j<N; ++j) {
+ points_out[j*2*k] = polygon[j];
+ neighbours.push_back(std::make_pair((int)(j*2*k-k) < 0 ? (N-1)*2*k+k : j*2*k-k, j*2*k+k));
+ }
+
+ for (unsigned int i=0; i<k; ++i) {
+ // Calculate middle of each edge so that neighbours points to something useful:
+ for (unsigned int j=0; j<N; ++j)
+ if (i==0)
+ points_out[j*2*k+k] = 0.5f * (points_out[j*2*k] + points_out[j==N-1 ? 0 : (j+1)*2*k]);
+ else {
+ float r = 0.2+0.3/(k-1)*i; // the neighbours are not always taken in the middle
+ points_out[neighbours[j].first] = r*points_out[j*2*k] + (1-r) * points_out[neighbours[j].first-1];
+ points_out[neighbours[j].second] = r*points_out[j*2*k] + (1-r) * points_out[neighbours[j].second+1];
+ }
+ // Now we have a triangle and valid neighbours, we can do an iteration:
+ for (unsigned int j=0; j<N; ++j)
+ points_out[2*k*j] = (1-aggressivity) * points_out[2*k*j] +
+ aggressivity*0.5f*(points_out[neighbours[j].first] + points_out[neighbours[j].second]);
+
+ for (auto& n : neighbours) {
+ ++n.first;
+ --n.second;
+ }
+ }
+ polygon = points_out; // replace the coarse polygon with the smooth one that we just created
+ }
+
+ // Transform back to 3D;
+ for (auto& b : polygon) {
+ b.z += 0.1f; // raise a bit above the object surface to avoid flickering
+ b = super_rotation(Pointf3(0,1,0), -angle_y, b);
+ b = super_rotation(Pointf3(0,0,1), -angle_z, b);
+ }
+ }
+
+ // We'll sort the planes by area and only keep the 255 largest ones (because of the picking pass limitations):
+ std::sort(m_planes.rbegin(), m_planes.rend(), [](const PlaneData& a, const PlaneData& b) { return a.area < b.area; });
+ m_planes.resize(std::min((int)m_planes.size(), 255));
+
+ // Planes are finished - let's save what we calculated it from:
+ m_source_data.bounding_boxes.clear();
+ for (const auto& vol : m_model_object->volumes)
+ m_source_data.bounding_boxes.push_back(vol->get_convex_hull().bounding_box());
+ m_source_data.scaling_factor = m_model_object->instances.front()->scaling_factor;
+ m_source_data.rotation = m_model_object->instances.front()->rotation;
+ const float* first_vertex = m_model_object->volumes.front()->get_convex_hull().first_vertex();
+ m_source_data.mesh_first_point = Pointf3(first_vertex[0], first_vertex[1], first_vertex[2]);
+}
+
+// Check if the bounding boxes of each volume's convex hull is the same as before
+// and that scaling and rotation has not changed. In that case we don't have to recalculate it.
+bool GLGizmoFlatten::is_plane_update_necessary() const
+{
+ if (m_state != On || !m_model_object || m_model_object->instances.empty())
+ return false;
+
+ if (m_model_object->volumes.size() != m_source_data.bounding_boxes.size()
+ || m_model_object->instances.front()->scaling_factor != m_source_data.scaling_factor
+ || m_model_object->instances.front()->rotation != m_source_data.rotation)
+ return true;
+
+ // now compare the bounding boxes:
+ for (unsigned int i=0; i<m_model_object->volumes.size(); ++i)
+ if (m_model_object->volumes[i]->get_convex_hull().bounding_box() != m_source_data.bounding_boxes[i])
+ return true;
+
+ const float* first_vertex = m_model_object->volumes.front()->get_convex_hull().first_vertex();
+ Pointf3 first_point(first_vertex[0], first_vertex[1], first_vertex[2]);
+ if (first_point != m_source_data.mesh_first_point)
+ return true;
+
+ return false;
+}
+
+Pointf3 GLGizmoFlatten::get_flattening_normal() const {
+ Pointf3 normal = m_normal;
+ normal.rotate(-m_model_object->instances.front()->rotation);
+ m_normal = Pointf3(0.f, 0.f, 0.f);
+ return normal;
+}
+
+
+
} // namespace GUI
} // namespace Slic3r
diff --git a/xs/src/slic3r/GUI/GLGizmo.hpp b/xs/src/slic3r/GUI/GLGizmo.hpp
index 506b3972e..aad31349c 100644
--- a/xs/src/slic3r/GUI/GLGizmo.hpp
+++ b/xs/src/slic3r/GUI/GLGizmo.hpp
@@ -10,6 +10,7 @@ namespace Slic3r {
class BoundingBoxf3;
class Pointf3;
+class ModelObject;
namespace GUI {
@@ -101,7 +102,7 @@ class GLGizmoRotate : public GLGizmoBase
mutable Pointf m_center;
mutable float m_radius;
- mutable bool m_keep_radius;
+ mutable bool m_keep_initial_values;
public:
GLGizmoRotate();
@@ -149,6 +150,56 @@ protected:
virtual void on_render_for_picking(const BoundingBoxf3& box) const;
};
+
+class GLGizmoFlatten : public GLGizmoBase
+{
+// This gizmo does not use grabbers. The m_hover_id relates to polygon managed by the class itself.
+
+private:
+ mutable Pointf3 m_normal;
+
+ struct PlaneData {
+ std::vector<Pointf3> vertices;
+ Pointf3 normal;
+ float area;
+ };
+ struct SourceDataSummary {
+ std::vector<BoundingBoxf3> bounding_boxes; // bounding boxes of convex hulls of individual volumes
+ float scaling_factor;
+ float rotation;
+ Pointf3 mesh_first_point;
+ };
+
+ // This holds information to decide whether recalculation is necessary:
+ SourceDataSummary m_source_data;
+
+ std::vector<PlaneData> m_planes;
+ std::vector<Pointf> m_instances_positions;
+ mutable std::unique_ptr<Pointf3> m_center = nullptr;
+ const ModelObject* m_model_object = nullptr;
+ void update_planes();
+ bool is_plane_update_necessary() const;
+
+public:
+ GLGizmoFlatten();
+
+ void set_flattening_data(const ModelObject* model_object);
+ Pointf3 get_flattening_normal() const;
+
+protected:
+ bool on_init() override;
+ void on_start_dragging() override;
+ void on_update(const Pointf& mouse_pos) override {};
+ void on_render(const BoundingBoxf3& box) const override;
+ void on_render_for_picking(const BoundingBoxf3& box) const override;
+ void on_set_state() override {
+ if (m_state == On && is_plane_update_necessary())
+ update_planes();
+ }
+};
+
+
+
} // namespace GUI
} // namespace Slic3r
diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp
index 8cd7ed776..8555f0b92 100644
--- a/xs/src/slic3r/GUI/GUI.cpp
+++ b/xs/src/slic3r/GUI/GUI.cpp
@@ -604,6 +604,8 @@ void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt
config.set_key_value(opt_key, new ConfigOptionEnum<SupportMaterialPattern>(boost::any_cast<SupportMaterialPattern>(value)));
else if (opt_key.compare("seam_position") == 0)
config.set_key_value(opt_key, new ConfigOptionEnum<SeamPosition>(boost::any_cast<SeamPosition>(value)));
+ else if (opt_key.compare("host_type") == 0)
+ config.set_key_value(opt_key, new ConfigOptionEnum<PrintHostType>(boost::any_cast<PrintHostType>(value)));
}
break;
case coPoints:{
diff --git a/xs/src/slic3r/GUI/OptionsGroup.cpp b/xs/src/slic3r/GUI/OptionsGroup.cpp
index d5cc29e19..a2d6559a9 100644
--- a/xs/src/slic3r/GUI/OptionsGroup.cpp
+++ b/xs/src/slic3r/GUI/OptionsGroup.cpp
@@ -459,8 +459,12 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config
else if (opt_key.compare("support_material_pattern") == 0){
ret = static_cast<int>(config.option<ConfigOptionEnum<SupportMaterialPattern>>(opt_key)->value);
}
- else if (opt_key.compare("seam_position") == 0)
+ else if (opt_key.compare("seam_position") == 0){
ret = static_cast<int>(config.option<ConfigOptionEnum<SeamPosition>>(opt_key)->value);
+ }
+ else if (opt_key.compare("host_type") == 0){
+ ret = static_cast<int>(config.option<ConfigOptionEnum<PrintHostType>>(opt_key)->value);
+ }
}
break;
case coPoints:
diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp
index 9f51f7b97..af2cd8ff6 100644
--- a/xs/src/slic3r/GUI/Preset.cpp
+++ b/xs/src/slic3r/GUI/Preset.cpp
@@ -313,13 +313,12 @@ const std::vector<std::string>& Preset::filament_options()
{
static std::vector<std::string> s_opts {
"filament_colour", "filament_diameter", "filament_type", "filament_soluble", "filament_notes", "filament_max_volumetric_speed",
- "extrusion_multiplier", "filament_density", "filament_cost",
- "filament_loading_speed", "filament_load_time", "filament_unloading_speed", "filament_unload_time", "filament_toolchange_delay",
- "filament_cooling_moves", "filament_cooling_initial_speed", "filament_cooling_final_speed", "filament_ramming_parameters",
- "filament_minimal_purge_on_wipe_tower", "temperature", "first_layer_temperature", "bed_temperature", "first_layer_bed_temperature",
- "fan_always_on", "cooling", "min_fan_speed", "max_fan_speed", "bridge_fan_speed", "disable_fan_first_layers", "fan_below_layer_time",
- "slowdown_below_layer_time", "min_print_speed", "start_filament_gcode", "end_filament_gcode","compatible_printers", "compatible_printers_condition",
- "inherits"
+ "extrusion_multiplier", "filament_density", "filament_cost", "filament_loading_speed", "filament_loading_speed_start", "filament_load_time",
+ "filament_unloading_speed", "filament_unloading_speed_start", "filament_unload_time", "filament_toolchange_delay", "filament_cooling_moves",
+ "filament_cooling_initial_speed", "filament_cooling_final_speed", "filament_ramming_parameters", "filament_minimal_purge_on_wipe_tower",
+ "temperature", "first_layer_temperature", "bed_temperature", "first_layer_bed_temperature", "fan_always_on", "cooling", "min_fan_speed",
+ "max_fan_speed", "bridge_fan_speed", "disable_fan_first_layers", "fan_below_layer_time", "slowdown_below_layer_time", "min_print_speed",
+ "start_filament_gcode", "end_filament_gcode","compatible_printers", "compatible_printers_condition", "inherits"
};
return s_opts;
}
@@ -329,8 +328,8 @@ const std::vector<std::string>& Preset::printer_options()
static std::vector<std::string> s_opts;
if (s_opts.empty()) {
s_opts = {
- "bed_shape", "z_offset", "gcode_flavor", "use_relative_e_distances", "serial_port", "serial_speed",
- "octoprint_host", "octoprint_apikey", "octoprint_cafile", "use_firmware_retraction", "use_volumetric_e", "variable_layer_height",
+ "bed_shape", "z_offset", "gcode_flavor", "use_relative_e_distances", "serial_port", "serial_speed", "host_type",
+ "print_host", "printhost_apikey", "printhost_cafile", "use_firmware_retraction", "use_volumetric_e", "variable_layer_height",
"single_extruder_multi_material", "start_gcode", "end_gcode", "before_layer_gcode", "layer_gcode", "toolchange_gcode",
"between_objects_gcode", "printer_vendor", "printer_model", "printer_variant", "printer_notes", "cooling_tube_retraction",
"cooling_tube_length", "parking_pos_retraction", "extra_loading_move", "max_print_height", "default_print_profile", "inherits",
diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
index 7c4322c5a..6505e1092 100644
--- a/xs/src/slic3r/GUI/Tab.cpp
+++ b/xs/src/slic3r/GUI/Tab.cpp
@@ -5,7 +5,7 @@
#include "../../libslic3r/Utils.hpp"
#include "slic3r/Utils/Http.hpp"
-#include "slic3r/Utils/OctoPrint.hpp"
+#include "slic3r/Utils/PrintHost.hpp"
#include "slic3r/Utils/Serial.hpp"
#include "BonjourDialog.hpp"
#include "WipeTowerDialog.hpp"
@@ -1290,7 +1290,9 @@ void TabFilament::build()
optgroup->append_line(line);
optgroup = page->new_optgroup(_(L("Toolchange parameters with single extruder MM printers")));
- optgroup->append_single_option_line("filament_loading_speed");
+ optgroup->append_single_option_line("filament_loading_speed_start");
+ optgroup->append_single_option_line("filament_loading_speed");
+ optgroup->append_single_option_line("filament_unloading_speed_start");
optgroup->append_single_option_line("filament_unloading_speed");
optgroup->append_single_option_line("filament_load_time");
optgroup->append_single_option_line("filament_unload_time");
@@ -1521,10 +1523,12 @@ void TabPrinter::build()
optgroup->append_line(line);
}
- optgroup = page->new_optgroup(_(L("OctoPrint upload")));
+ optgroup = page->new_optgroup(_(L("Printer Host upload")));
- auto octoprint_host_browse = [this, optgroup] (wxWindow* parent) {
- auto btn = new wxButton(parent, wxID_ANY, _(L(" Browse "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
+ optgroup->append_single_option_line("host_type");
+
+ auto printhost_browse = [this, optgroup] (wxWindow* parent) {
+ auto btn = m_printhost_browse_btn = new wxButton(parent, wxID_ANY, _(L(" Browse "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("zoom.png")), wxBITMAP_TYPE_PNG));
auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(btn);
@@ -1532,47 +1536,50 @@ void TabPrinter::build()
btn->Bind(wxEVT_BUTTON, [this, parent, optgroup](wxCommandEvent e) {
BonjourDialog dialog(parent);
if (dialog.show_and_lookup()) {
- optgroup->set_value("octoprint_host", std::move(dialog.get_selected()), true);
+ optgroup->set_value("print_host", std::move(dialog.get_selected()), true);
}
});
return sizer;
};
- auto octoprint_host_test = [this](wxWindow* parent) {
- auto btn = m_octoprint_host_test_btn = new wxButton(parent, wxID_ANY, _(L("Test")),
+ auto print_host_test = [this](wxWindow* parent) {
+ auto btn = m_print_host_test_btn = new wxButton(parent, wxID_ANY, _(L("Test")),
wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT);
btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("wrench.png")), wxBITMAP_TYPE_PNG));
auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(btn);
btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent e) {
- OctoPrint octoprint(m_config);
+ std::unique_ptr<PrintHost> host(PrintHost::get_print_host(m_config));
+ if (! host) {
+ const auto text = wxString::Format("%s",
+ _(L("Could not get a valid Printer Host reference")));
+ show_error(this, text);
+ return;
+ }
wxString msg;
- if (octoprint.test(msg)) {
- show_info(this, _(L("Connection to OctoPrint works correctly.")), _(L("Success!")));
+ if (host->test(msg)) {
+ show_info(this, host->get_test_ok_msg(), _(L("Success!")));
} else {
- const auto text = wxString::Format("%s: %s\n\n%s",
- _(L("Could not connect to OctoPrint")), msg, _(L("Note: OctoPrint version at least 1.1.0 is required."))
- );
- show_error(this, text);
+ show_error(this, host->get_test_failed_msg(msg));
}
});
return sizer;
};
- Line host_line = optgroup->create_single_option_line("octoprint_host");
- host_line.append_widget(octoprint_host_browse);
- host_line.append_widget(octoprint_host_test);
+ Line host_line = optgroup->create_single_option_line("print_host");
+ host_line.append_widget(printhost_browse);
+ host_line.append_widget(print_host_test);
optgroup->append_line(host_line);
- optgroup->append_single_option_line("octoprint_apikey");
+ optgroup->append_single_option_line("printhost_apikey");
if (Http::ca_file_supported()) {
- Line cafile_line = optgroup->create_single_option_line("octoprint_cafile");
+ Line cafile_line = optgroup->create_single_option_line("printhost_cafile");
- auto octoprint_cafile_browse = [this, optgroup] (wxWindow* parent) {
+ auto printhost_cafile_browse = [this, optgroup] (wxWindow* parent) {
auto btn = new wxButton(parent, wxID_ANY, _(L(" Browse "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("zoom.png")), wxBITMAP_TYPE_PNG));
auto sizer = new wxBoxSizer(wxHORIZONTAL);
@@ -1582,17 +1589,17 @@ void TabPrinter::build()
static const auto filemasks = _(L("Certificate files (*.crt, *.pem)|*.crt;*.pem|All files|*.*"));
wxFileDialog openFileDialog(this, _(L("Open CA certificate file")), "", "", filemasks, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if (openFileDialog.ShowModal() != wxID_CANCEL) {
- optgroup->set_value("octoprint_cafile", std::move(openFileDialog.GetPath()), true);
+ optgroup->set_value("printhost_cafile", std::move(openFileDialog.GetPath()), true);
}
});
return sizer;
};
- cafile_line.append_widget(octoprint_cafile_browse);
+ cafile_line.append_widget(printhost_cafile_browse);
optgroup->append_line(cafile_line);
- auto octoprint_cafile_hint = [this, optgroup] (wxWindow* parent) {
+ auto printhost_cafile_hint = [this, optgroup] (wxWindow* parent) {
auto txt = new wxStaticText(parent, wxID_ANY,
_(L("HTTPS CA file is optional. It is only needed if you use HTTPS with a self-signed certificate.")));
auto sizer = new wxBoxSizer(wxHORIZONTAL);
@@ -1602,7 +1609,7 @@ void TabPrinter::build()
Line cafile_hint { "", "" };
cafile_hint.full_width = 1;
- cafile_hint.widget = std::move(octoprint_cafile_hint);
+ cafile_hint.widget = std::move(printhost_cafile_hint);
optgroup->append_line(cafile_hint);
}
@@ -1897,8 +1904,12 @@ void TabPrinter::update(){
m_serial_test_btn->Disable();
}
- m_octoprint_host_test_btn->Enable(!m_config->opt_string("octoprint_host").empty());
-
+ {
+ std::unique_ptr<PrintHost> host(PrintHost::get_print_host(m_config));
+ m_print_host_test_btn->Enable(!m_config->opt_string("print_host").empty() && host->can_test());
+ m_printhost_browse_btn->Enable(host->has_auto_discovery());
+ }
+
bool have_multiple_extruders = m_extruders_count > 1;
get_field("toolchange_gcode")->toggle(have_multiple_extruders);
get_field("single_extruder_multi_material")->toggle(have_multiple_extruders);
diff --git a/xs/src/slic3r/GUI/Tab.hpp b/xs/src/slic3r/GUI/Tab.hpp
index 8b4eae7de..230fe659e 100644
--- a/xs/src/slic3r/GUI/Tab.hpp
+++ b/xs/src/slic3r/GUI/Tab.hpp
@@ -321,7 +321,8 @@ class TabPrinter : public Tab
bool m_rebuild_kinematics_page = false;
public:
wxButton* m_serial_test_btn;
- wxButton* m_octoprint_host_test_btn;
+ wxButton* m_print_host_test_btn;
+ wxButton* m_printhost_browse_btn;
size_t m_extruders_count;
size_t m_extruders_count_old = 0;
diff --git a/xs/src/slic3r/IProgressIndicator.hpp b/xs/src/slic3r/ProgressIndicator.hpp
index 704931574..1b064077e 100644
--- a/xs/src/slic3r/IProgressIndicator.hpp
+++ b/xs/src/slic3r/ProgressIndicator.hpp
@@ -3,16 +3,15 @@
#include <string>
#include <functional>
-#include "Strings.hpp"
namespace Slic3r {
/**
* @brief Generic progress indication interface.
*/
-class IProgressIndicator {
+class ProgressIndicator {
public:
- using CancelFn = std::function<void(void)>; // Cancel functio signature.
+ using CancelFn = std::function<void(void)>; // Cancel function signature.
private:
float state_ = .0f, max_ = 1.f, step_;
@@ -20,7 +19,7 @@ private:
public:
- inline virtual ~IProgressIndicator() {}
+ inline virtual ~ProgressIndicator() {}
/// Get the maximum of the progress range.
float max() const { return max_; }
@@ -28,14 +27,14 @@ public:
/// Get the current progress state
float state() const { return state_; }
- /// Set the maximum of hte progress range
+ /// Set the maximum of the progress range
virtual void max(float maxval) { max_ = maxval; }
/// Set the current state of the progress.
virtual void state(float val) { state_ = val; }
/**
- * @brief Number of states int the progress. Can be used insted of giving a
+ * @brief Number of states int the progress. Can be used instead of giving a
* maximum value.
*/
virtual void states(unsigned statenum) {
@@ -43,25 +42,19 @@ public:
}
/// Message shown on the next status update.
- virtual void message(const string&) = 0;
+ virtual void message(const std::string&) = 0;
- /// Title of the operaton.
- virtual void title(const string&) = 0;
+ /// Title of the operation.
+ virtual void title(const std::string&) = 0;
- /// Formatted message for the next status update. Works just like sprinf.
- virtual void message_fmt(const string& fmt, ...);
+ /// Formatted message for the next status update. Works just like sprintf.
+ virtual void message_fmt(const std::string& fmt, ...);
/// Set up a cancel callback for the operation if feasible.
- inline void on_cancel(CancelFn func) { cancelfunc_ = func; }
+ virtual void on_cancel(CancelFn func = CancelFn()) { cancelfunc_ = func; }
- /**
- * Explicitly shut down the progress indicator and call the associated
- * callback.
- */
- virtual void cancel() { cancelfunc_(); }
-
- /// Convinience function to call message and status update in one function.
- void update(float st, const string& msg) {
+ /// Convenience function to call message and status update in one function.
+ void update(float st, const std::string& msg) {
message(msg); state(st);
}
};
diff --git a/xs/src/slic3r/Strings.hpp b/xs/src/slic3r/Strings.hpp
deleted file mode 100644
index b267fe064..000000000
--- a/xs/src/slic3r/Strings.hpp
+++ /dev/null
@@ -1,10 +0,0 @@
-#ifndef STRINGS_HPP
-#define STRINGS_HPP
-
-#include "GUI/GUI.hpp"
-
-namespace Slic3r {
-using string = wxString;
-}
-
-#endif // STRINGS_HPP
diff --git a/xs/src/slic3r/Utils/Duet.cpp b/xs/src/slic3r/Utils/Duet.cpp
new file mode 100644
index 000000000..f25327161
--- /dev/null
+++ b/xs/src/slic3r/Utils/Duet.cpp
@@ -0,0 +1,279 @@
+#include "Duet.hpp"
+#include "PrintHostSendDialog.hpp"
+
+#include <algorithm>
+#include <ctime>
+#include <boost/filesystem/path.hpp>
+#include <boost/format.hpp>
+#include <boost/log/trivial.hpp>
+#include <boost/property_tree/ptree.hpp>
+#include <boost/property_tree/json_parser.hpp>
+
+#include <wx/frame.h>
+#include <wx/event.h>
+#include <wx/progdlg.h>
+#include <wx/sizer.h>
+#include <wx/stattext.h>
+#include <wx/textctrl.h>
+#include <wx/checkbox.h>
+
+#include "libslic3r/PrintConfig.hpp"
+#include "slic3r/GUI/GUI.hpp"
+#include "slic3r/GUI/MsgDialog.hpp"
+#include "Http.hpp"
+
+namespace fs = boost::filesystem;
+namespace pt = boost::property_tree;
+
+namespace Slic3r {
+
+Duet::Duet(DynamicPrintConfig *config) :
+ host(config->opt_string("print_host")),
+ password(config->opt_string("printhost_apikey"))
+{}
+
+Duet::~Duet() {}
+
+bool Duet::test(wxString &msg) const
+{
+ bool connected = connect(msg);
+ if (connected) {
+ disconnect();
+ }
+
+ return connected;
+}
+
+wxString Duet::get_test_ok_msg () const
+{
+ return wxString::Format("%s", _(L("Connection to Duet works correctly.")));
+}
+
+wxString Duet::get_test_failed_msg (wxString &msg) const
+{
+ return wxString::Format("%s: %s", _(L("Could not connect to Duet")), msg);
+}
+
+bool Duet::send_gcode(const std::string &filename) const
+{
+ enum { PROGRESS_RANGE = 1000 };
+
+ const auto errortitle = _(L("Error while uploading to the Duet"));
+ fs::path filepath(filename);
+
+ PrintHostSendDialog send_dialog(filepath.filename(), true);
+ if (send_dialog.ShowModal() != wxID_OK) { return false; }
+
+ const bool print = send_dialog.print();
+ const auto upload_filepath = send_dialog.filename();
+ const auto upload_filename = upload_filepath.filename();
+ const auto upload_parent_path = upload_filepath.parent_path();
+
+ wxProgressDialog progress_dialog(
+ _(L("Duet upload")),
+ _(L("Sending G-code file to Duet...")),
+ PROGRESS_RANGE, nullptr, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT);
+ progress_dialog.Pulse();
+
+ wxString connect_msg;
+ if (!connect(connect_msg)) {
+ auto errormsg = wxString::Format("%s: %s", errortitle, connect_msg);
+ GUI::show_error(&progress_dialog, std::move(errormsg));
+ return false;
+ }
+
+ bool res = true;
+
+ auto upload_cmd = get_upload_url(upload_filepath.string());
+ BOOST_LOG_TRIVIAL(info) << boost::format("Duet: Uploading file %1%, filename: %2%, path: %3%, print: %4%, command: %5%")
+ % filepath.string()
+ % upload_filename.string()
+ % upload_parent_path.string()
+ % print
+ % upload_cmd;
+
+ auto http = Http::post(std::move(upload_cmd));
+ http.set_post_body(filename)
+ .on_complete([&](std::string body, unsigned status) {
+ BOOST_LOG_TRIVIAL(debug) << boost::format("Duet: File uploaded: HTTP %1%: %2%") % status % body;
+ progress_dialog.Update(PROGRESS_RANGE);
+
+ int err_code = get_err_code_from_body(body);
+ if (err_code != 0) {
+ auto msg = format_error(body, L("Unknown error occured"), 0);
+ GUI::show_error(&progress_dialog, std::move(msg));
+ res = false;
+ } else if (print) {
+ wxString errormsg;
+ res = start_print(errormsg, upload_filepath.string());
+ if (!res) {
+ GUI::show_error(&progress_dialog, std::move(errormsg));
+ }
+ }
+ })
+ .on_error([&](std::string body, std::string error, unsigned status) {
+ BOOST_LOG_TRIVIAL(error) << boost::format("Duet: Error uploading file: %1%, HTTP %2%, body: `%3%`") % error % status % body;
+ auto errormsg = wxString::Format("%s: %s", errortitle, format_error(body, error, status));
+ GUI::show_error(&progress_dialog, std::move(errormsg));
+ res = false;
+ })
+ .on_progress([&](Http::Progress progress, bool &cancel) {
+ if (cancel) {
+ // Upload was canceled
+ res = false;
+ } else if (progress.ultotal > 0) {
+ int value = PROGRESS_RANGE * progress.ulnow / progress.ultotal;
+ cancel = !progress_dialog.Update(std::min(value, PROGRESS_RANGE - 1)); // Cap the value to prevent premature dialog closing
+ } else {
+ cancel = !progress_dialog.Pulse();
+ }
+ })
+ .perform_sync();
+
+ disconnect();
+
+ return res;
+}
+
+bool Duet::has_auto_discovery() const
+{
+ return false;
+}
+
+bool Duet::can_test() const
+{
+ return true;
+}
+
+bool Duet::connect(wxString &msg) const
+{
+ bool res = false;
+ auto url = get_connect_url();
+
+ auto http = Http::get(std::move(url));
+ http.on_error([&](std::string body, std::string error, unsigned status) {
+ BOOST_LOG_TRIVIAL(error) << boost::format("Duet: Error connecting: %1%, HTTP %2%, body: `%3%`") % error % status % body;
+ msg = format_error(body, error, status);
+ })
+ .on_complete([&](std::string body, unsigned) {
+ BOOST_LOG_TRIVIAL(debug) << boost::format("Duet: Got: %1%") % body;
+
+ int err_code = get_err_code_from_body(body);
+ switch (err_code) {
+ case 0:
+ res = true;
+ break;
+ case 1:
+ msg = format_error(body, L("Wrong password"), 0);
+ break;
+ case 2:
+ msg = format_error(body, L("Could not get resources to create a new connection"), 0);
+ break;
+ default:
+ msg = format_error(body, L("Unknown error occured"), 0);
+ break;
+ }
+
+ })
+ .perform_sync();
+
+ return res;
+}
+
+void Duet::disconnect() const
+{
+ auto url = (boost::format("%1%rr_disconnect")
+ % get_base_url()).str();
+
+ auto http = Http::get(std::move(url));
+ http.on_error([&](std::string body, std::string error, unsigned status) {
+ // we don't care about it, if disconnect is not working Duet will disconnect automatically after some time
+ BOOST_LOG_TRIVIAL(error) << boost::format("Duet: Error disconnecting: %1%, HTTP %2%, body: `%3%`") % error % status % body;
+ })
+ .perform_sync();
+}
+
+std::string Duet::get_upload_url(const std::string &filename) const
+{
+ return (boost::format("%1%rr_upload?name=0:/gcodes/%2%&%3%")
+ % get_base_url()
+ % Http::url_encode(filename)
+ % timestamp_str()).str();
+}
+
+std::string Duet::get_connect_url() const
+{
+ return (boost::format("%1%rr_connect?password=%2%&%3%")
+ % get_base_url()
+ % (password.empty() ? "reprap" : password)
+ % timestamp_str()).str();
+}
+
+std::string Duet::get_base_url() const
+{
+ if (host.find("http://") == 0 || host.find("https://") == 0) {
+ if (host.back() == '/') {
+ return host;
+ } else {
+ return (boost::format("%1%/") % host).str();
+ }
+ } else {
+ return (boost::format("http://%1%/") % host).str();
+ }
+}
+
+std::string Duet::timestamp_str() const
+{
+ enum { BUFFER_SIZE = 32 };
+
+ auto t = std::time(nullptr);
+ auto tm = *std::localtime(&t);
+
+ char buffer[BUFFER_SIZE];
+ std::strftime(buffer, BUFFER_SIZE, "time=%Y-%m-%dT%H:%M:%S", &tm);
+
+ return std::string(buffer);
+}
+
+wxString Duet::format_error(const std::string &body, const std::string &error, unsigned status)
+{
+ if (status != 0) {
+ auto wxbody = wxString::FromUTF8(body.data());
+ return wxString::Format("HTTP %u: %s", status, wxbody);
+ } else {
+ return wxString::FromUTF8(error.data());
+ }
+}
+
+bool Duet::start_print(wxString &msg, const std::string &filename) const
+{
+ bool res = false;
+
+ auto url = (boost::format("%1%rr_gcode?gcode=M32%%20\"%2%\"")
+ % get_base_url()
+ % Http::url_encode(filename)).str();
+
+ auto http = Http::get(std::move(url));
+ http.on_error([&](std::string body, std::string error, unsigned status) {
+ BOOST_LOG_TRIVIAL(error) << boost::format("Duet: Error starting print: %1%, HTTP %2%, body: `%3%`") % error % status % body;
+ msg = format_error(body, error, status);
+ })
+ .on_complete([&](std::string body, unsigned) {
+ BOOST_LOG_TRIVIAL(debug) << boost::format("Duet: Got: %1%") % body;
+ res = true;
+ })
+ .perform_sync();
+
+ return res;
+}
+
+int Duet::get_err_code_from_body(const std::string &body) const
+{
+ pt::ptree root;
+ std::istringstream iss (body); // wrap returned json to istringstream
+ pt::read_json(iss, root);
+
+ return root.get<int>("err", 0);
+}
+
+}
diff --git a/xs/src/slic3r/Utils/Duet.hpp b/xs/src/slic3r/Utils/Duet.hpp
new file mode 100644
index 000000000..bc210d7a4
--- /dev/null
+++ b/xs/src/slic3r/Utils/Duet.hpp
@@ -0,0 +1,47 @@
+#ifndef slic3r_Duet_hpp_
+#define slic3r_Duet_hpp_
+
+#include <string>
+#include <wx/string.h>
+
+#include "PrintHost.hpp"
+
+
+namespace Slic3r {
+
+
+class DynamicPrintConfig;
+class Http;
+
+class Duet : public PrintHost
+{
+public:
+ Duet(DynamicPrintConfig *config);
+ virtual ~Duet();
+
+ bool test(wxString &curl_msg) const;
+ wxString get_test_ok_msg () const;
+ wxString get_test_failed_msg (wxString &msg) const;
+ // Send gcode file to duet, filename is expected to be in UTF-8
+ bool send_gcode(const std::string &filename) const;
+ bool has_auto_discovery() const;
+ bool can_test() const;
+private:
+ std::string host;
+ std::string password;
+
+ std::string get_upload_url(const std::string &filename) const;
+ std::string get_connect_url() const;
+ std::string get_base_url() const;
+ std::string timestamp_str() const;
+ bool connect(wxString &msg) const;
+ void disconnect() const;
+ bool start_print(wxString &msg, const std::string &filename) const;
+ int get_err_code_from_body(const std::string &body) const;
+ static wxString format_error(const std::string &body, const std::string &error, unsigned status);
+};
+
+
+}
+
+#endif
diff --git a/xs/src/slic3r/Utils/Http.cpp b/xs/src/slic3r/Utils/Http.cpp
index 37eb59a00..9b67ceea8 100644
--- a/xs/src/slic3r/Utils/Http.cpp
+++ b/xs/src/slic3r/Utils/Http.cpp
@@ -4,6 +4,7 @@
#include <functional>
#include <thread>
#include <deque>
+#include <sstream>
#include <boost/filesystem/fstream.hpp>
#include <boost/format.hpp>
@@ -42,6 +43,7 @@ struct Http::priv
// Used for storing file streams added as multipart form parts
// Using a deque here because unlike vector it doesn't ivalidate pointers on insertion
std::deque<fs::ifstream> form_files;
+ std::string postfields;
size_t limit;
bool cancel;
@@ -60,6 +62,7 @@ struct Http::priv
static size_t form_file_read_cb(char *buffer, size_t size, size_t nitems, void *userp);
void form_add_file(const char *name, const fs::path &path, const char* filename);
+ void set_post_body(const fs::path &path);
std::string curl_error(CURLcode curlcode);
std::string body_size_error();
@@ -187,6 +190,13 @@ void Http::priv::form_add_file(const char *name, const fs::path &path, const cha
}
}
+void Http::priv::set_post_body(const fs::path &path)
+{
+ std::ifstream file(path.string());
+ std::string file_content { std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>() };
+ postfields = file_content;
+}
+
std::string Http::priv::curl_error(CURLcode curlcode)
{
return (boost::format("%1% (%2%)")
@@ -229,6 +239,11 @@ void Http::priv::http_perform()
::curl_easy_setopt(curl, CURLOPT_HTTPPOST, form);
}
+ if (!postfields.empty()) {
+ ::curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postfields.c_str());
+ ::curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE, postfields.size());
+ }
+
CURLcode res = ::curl_easy_perform(curl);
if (res != CURLE_OK) {
@@ -338,6 +353,12 @@ Http& Http::form_add_file(const std::string &name, const fs::path &path, const s
return *this;
}
+Http& Http::set_post_body(const fs::path &path)
+{
+ if (p) { p->set_post_body(path);}
+ return *this;
+}
+
Http& Http::on_complete(CompleteFn fn)
{
if (p) { p->completefn = std::move(fn); }
@@ -400,6 +421,21 @@ bool Http::ca_file_supported()
return res;
}
+std::string Http::url_encode(const std::string &str)
+{
+ ::CURL *curl = ::curl_easy_init();
+ if (curl == nullptr) {
+ return str;
+ }
+ char *ce = ::curl_easy_escape(curl, str.c_str(), str.length());
+ std::string encoded = std::string(ce);
+
+ ::curl_free(ce);
+ ::curl_easy_cleanup(curl);
+
+ return encoded;
+}
+
std::ostream& operator<<(std::ostream &os, const Http::Progress &progress)
{
os << "Http::Progress("
diff --git a/xs/src/slic3r/Utils/Http.hpp b/xs/src/slic3r/Utils/Http.hpp
index ce4e438ca..44580b7ea 100644
--- a/xs/src/slic3r/Utils/Http.hpp
+++ b/xs/src/slic3r/Utils/Http.hpp
@@ -73,6 +73,11 @@ public:
// Same as above except also override the file's filename with a custom one
Http& form_add_file(const std::string &name, const boost::filesystem::path &path, const std::string &filename);
+ // Set the file contents as a POST request body.
+ // The data is used verbatim, it is not additionally encoded in any way.
+ // This can be used for hosts which do not support multipart requests.
+ Http& set_post_body(const boost::filesystem::path &path);
+
// Callback called on HTTP request complete
Http& on_complete(CompleteFn fn);
// Callback called on an error occuring at any stage of the requests: Url parsing, DNS lookup,
@@ -93,6 +98,9 @@ public:
// Tells whether current backend supports seting up a CA file using ca_file()
static bool ca_file_supported();
+
+ // converts the given string to an url_encoded_string
+ static std::string url_encode(const std::string &str);
private:
Http(const std::string &url);
diff --git a/xs/src/slic3r/Utils/OctoPrint.cpp b/xs/src/slic3r/Utils/OctoPrint.cpp
index 97b4123d4..db86d7697 100644
--- a/xs/src/slic3r/Utils/OctoPrint.cpp
+++ b/xs/src/slic3r/Utils/OctoPrint.cpp
@@ -1,21 +1,11 @@
#include "OctoPrint.hpp"
+#include "PrintHostSendDialog.hpp"
#include <algorithm>
-#include <boost/filesystem/path.hpp>
#include <boost/format.hpp>
#include <boost/log/trivial.hpp>
-#include <wx/frame.h>
-#include <wx/event.h>
-#include <wx/progdlg.h>
-#include <wx/sizer.h>
-#include <wx/stattext.h>
-#include <wx/textctrl.h>
-#include <wx/checkbox.h>
-
#include "libslic3r/PrintConfig.hpp"
-#include "slic3r/GUI/GUI.hpp"
-#include "slic3r/GUI/MsgDialog.hpp"
#include "Http.hpp"
namespace fs = boost::filesystem;
@@ -23,49 +13,14 @@ namespace fs = boost::filesystem;
namespace Slic3r {
-
-struct SendDialog : public GUI::MsgDialog
-{
- wxTextCtrl *txt_filename;
- wxCheckBox *box_print;
-
- SendDialog(const fs::path &path) :
- MsgDialog(nullptr, _(L("Send G-Code to printer")), _(L("Upload to OctoPrint with the following filename:")), wxID_NONE),
- txt_filename(new wxTextCtrl(this, wxID_ANY, path.filename().wstring())),
- box_print(new wxCheckBox(this, wxID_ANY, _(L("Start printing after upload"))))
- {
- auto *label_dir_hint = new wxStaticText(this, wxID_ANY, _(L("Use forward slashes ( / ) as a directory separator if needed.")));
- label_dir_hint->Wrap(CONTENT_WIDTH);
-
- content_sizer->Add(txt_filename, 0, wxEXPAND);
- content_sizer->Add(label_dir_hint);
- content_sizer->AddSpacer(VERT_SPACING);
- content_sizer->Add(box_print, 0, wxBOTTOM, 2*VERT_SPACING);
-
- btn_sizer->Add(CreateStdDialogButtonSizer(wxOK | wxCANCEL));
-
- txt_filename->SetFocus();
- wxString stem(path.stem().wstring());
- txt_filename->SetSelection(0, stem.Length());
-
- Fit();
- }
-
- fs::path filename() const {
- return fs::path(txt_filename->GetValue().wx_str());
- }
-
- bool print() const { return box_print->GetValue(); }
-};
-
-
-
OctoPrint::OctoPrint(DynamicPrintConfig *config) :
- host(config->opt_string("octoprint_host")),
- apikey(config->opt_string("octoprint_apikey")),
- cafile(config->opt_string("octoprint_cafile"))
+ host(config->opt_string("print_host")),
+ apikey(config->opt_string("printhost_apikey")),
+ cafile(config->opt_string("printhost_cafile"))
{}
+OctoPrint::~OctoPrint() {}
+
bool OctoPrint::test(wxString &msg) const
{
// Since the request is performed synchronously here,
@@ -91,6 +46,17 @@ bool OctoPrint::test(wxString &msg) const
return res;
}
+wxString OctoPrint::get_test_ok_msg () const
+{
+ return wxString::Format("%s", _(L("Connection to OctoPrint works correctly.")));
+}
+
+wxString OctoPrint::get_test_failed_msg (wxString &msg) const
+{
+ return wxString::Format("%s: %s\n\n%s",
+ _(L("Could not connect to OctoPrint")), msg, _(L("Note: OctoPrint version at least 1.1.0 is required.")));
+}
+
bool OctoPrint::send_gcode(const std::string &filename) const
{
enum { PROGRESS_RANGE = 1000 };
@@ -98,7 +64,7 @@ bool OctoPrint::send_gcode(const std::string &filename) const
const auto errortitle = _(L("Error while uploading to the OctoPrint server"));
fs::path filepath(filename);
- SendDialog send_dialog(filepath.filename());
+ PrintHostSendDialog send_dialog(filepath.filename(), true);
if (send_dialog.ShowModal() != wxID_OK) { return false; }
const bool print = send_dialog.print();
@@ -161,6 +127,16 @@ bool OctoPrint::send_gcode(const std::string &filename) const
return res;
}
+bool OctoPrint::has_auto_discovery() const
+{
+ return true;
+}
+
+bool OctoPrint::can_test() const
+{
+ return true;
+}
+
void OctoPrint::set_auth(Http &http) const
{
http.header("X-Api-Key", apikey);
diff --git a/xs/src/slic3r/Utils/OctoPrint.hpp b/xs/src/slic3r/Utils/OctoPrint.hpp
index 1e2098ae3..f6c4d58c8 100644
--- a/xs/src/slic3r/Utils/OctoPrint.hpp
+++ b/xs/src/slic3r/Utils/OctoPrint.hpp
@@ -4,6 +4,8 @@
#include <string>
#include <wx/string.h>
+#include "PrintHost.hpp"
+
namespace Slic3r {
@@ -11,14 +13,19 @@ namespace Slic3r {
class DynamicPrintConfig;
class Http;
-class OctoPrint
+class OctoPrint : public PrintHost
{
public:
OctoPrint(DynamicPrintConfig *config);
+ virtual ~OctoPrint();
bool test(wxString &curl_msg) const;
+ wxString get_test_ok_msg () const;
+ wxString get_test_failed_msg (wxString &msg) const;
// Send gcode file to octoprint, filename is expected to be in UTF-8
bool send_gcode(const std::string &filename) const;
+ bool has_auto_discovery() const;
+ bool can_test() const;
private:
std::string host;
std::string apikey;
diff --git a/xs/src/slic3r/Utils/PrintHost.cpp b/xs/src/slic3r/Utils/PrintHost.cpp
new file mode 100644
index 000000000..dd72bae40
--- /dev/null
+++ b/xs/src/slic3r/Utils/PrintHost.cpp
@@ -0,0 +1,23 @@
+#include "OctoPrint.hpp"
+#include "Duet.hpp"
+
+#include "libslic3r/PrintConfig.hpp"
+
+namespace Slic3r {
+
+
+PrintHost::~PrintHost() {}
+
+PrintHost* PrintHost::get_print_host(DynamicPrintConfig *config)
+{
+ PrintHostType kind = config->option<ConfigOptionEnum<PrintHostType>>("host_type")->value;
+ if (kind == htOctoPrint) {
+ return new OctoPrint(config);
+ } else if (kind == htDuet) {
+ return new Duet(config);
+ }
+ return nullptr;
+}
+
+
+}
diff --git a/xs/src/slic3r/Utils/PrintHost.hpp b/xs/src/slic3r/Utils/PrintHost.hpp
new file mode 100644
index 000000000..bc828ea46
--- /dev/null
+++ b/xs/src/slic3r/Utils/PrintHost.hpp
@@ -0,0 +1,35 @@
+#ifndef slic3r_PrintHost_hpp_
+#define slic3r_PrintHost_hpp_
+
+#include <memory>
+#include <string>
+#include <wx/string.h>
+
+
+namespace Slic3r {
+
+
+class DynamicPrintConfig;
+
+class PrintHost
+{
+public:
+ virtual ~PrintHost();
+
+ virtual bool test(wxString &curl_msg) const = 0;
+ virtual wxString get_test_ok_msg () const = 0;
+ virtual wxString get_test_failed_msg (wxString &msg) const = 0;
+ // Send gcode file to print host, filename is expected to be in UTF-8
+ virtual bool send_gcode(const std::string &filename) const = 0;
+ virtual bool has_auto_discovery() const = 0;
+ virtual bool can_test() const = 0;
+
+ static PrintHost* get_print_host(DynamicPrintConfig *config);
+};
+
+
+
+
+}
+
+#endif
diff --git a/xs/src/slic3r/Utils/PrintHostSendDialog.cpp b/xs/src/slic3r/Utils/PrintHostSendDialog.cpp
new file mode 100644
index 000000000..c5d441f87
--- /dev/null
+++ b/xs/src/slic3r/Utils/PrintHostSendDialog.cpp
@@ -0,0 +1,52 @@
+#include "PrintHostSendDialog.hpp"
+
+#include <wx/frame.h>
+#include <wx/event.h>
+#include <wx/progdlg.h>
+#include <wx/sizer.h>
+#include <wx/stattext.h>
+#include <wx/textctrl.h>
+#include <wx/checkbox.h>
+
+#include "slic3r/GUI/GUI.hpp"
+#include "slic3r/GUI/MsgDialog.hpp"
+
+
+namespace fs = boost::filesystem;
+
+namespace Slic3r {
+
+PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, bool can_start_print) :
+ MsgDialog(nullptr, _(L("Send G-Code to printer host")), _(L("Upload to Printer Host with the following filename:")), wxID_NONE),
+ txt_filename(new wxTextCtrl(this, wxID_ANY, path.filename().wstring())),
+ box_print(new wxCheckBox(this, wxID_ANY, _(L("Start printing after upload")))),
+ can_start_print(can_start_print)
+{
+ auto *label_dir_hint = new wxStaticText(this, wxID_ANY, _(L("Use forward slashes ( / ) as a directory separator if needed.")));
+ label_dir_hint->Wrap(CONTENT_WIDTH);
+
+ content_sizer->Add(txt_filename, 0, wxEXPAND);
+ content_sizer->Add(label_dir_hint);
+ content_sizer->AddSpacer(VERT_SPACING);
+ content_sizer->Add(box_print, 0, wxBOTTOM, 2*VERT_SPACING);
+
+ btn_sizer->Add(CreateStdDialogButtonSizer(wxOK | wxCANCEL));
+
+ txt_filename->SetFocus();
+ wxString stem(path.stem().wstring());
+ txt_filename->SetSelection(0, stem.Length());
+
+ box_print->Enable(can_start_print);
+
+ Fit();
+}
+
+fs::path PrintHostSendDialog::filename() const
+{
+ return fs::path(txt_filename->GetValue().wx_str());
+}
+
+bool PrintHostSendDialog::print() const
+{
+ return box_print->GetValue(); }
+}
diff --git a/xs/src/slic3r/Utils/PrintHostSendDialog.hpp b/xs/src/slic3r/Utils/PrintHostSendDialog.hpp
new file mode 100644
index 000000000..dc4a8d6f7
--- /dev/null
+++ b/xs/src/slic3r/Utils/PrintHostSendDialog.hpp
@@ -0,0 +1,38 @@
+#ifndef slic3r_PrintHostSendDialog_hpp_
+#define slic3r_PrintHostSendDialog_hpp_
+
+#include <string>
+
+#include <boost/filesystem/path.hpp>
+
+#include <wx/string.h>
+#include <wx/frame.h>
+#include <wx/event.h>
+#include <wx/progdlg.h>
+#include <wx/sizer.h>
+#include <wx/stattext.h>
+#include <wx/textctrl.h>
+#include <wx/checkbox.h>
+
+#include "slic3r/GUI/GUI.hpp"
+#include "slic3r/GUI/MsgDialog.hpp"
+
+
+namespace Slic3r {
+
+class PrintHostSendDialog : public GUI::MsgDialog
+{
+private:
+ wxTextCtrl *txt_filename;
+ wxCheckBox *box_print;
+ bool can_start_print;
+
+public:
+ PrintHostSendDialog(const boost::filesystem::path &path, bool can_start_print);
+ boost::filesystem::path filename() const;
+ bool print() const;
+};
+
+}
+
+#endif
diff --git a/xs/xsp/AppController.xsp b/xs/xsp/AppController.xsp
index 1b653081d..a578fe0b1 100644
--- a/xs/xsp/AppController.xsp
+++ b/xs/xsp/AppController.xsp
@@ -8,10 +8,7 @@
%}
%name{Slic3r::PrintController} class PrintController {
-
PrintController(Print *print);
-
- void slice();
};
%name{Slic3r::AppController} class AppController {
diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp
index 0e0a03f5e..1a0fc9b9f 100644
--- a/xs/xsp/GUI_3DScene.xsp
+++ b/xs/xsp/GUI_3DScene.xsp
@@ -65,7 +65,6 @@
%};
Clone<BoundingBoxf3> bounding_box() const
%code%{ RETVAL = THIS->bounding_box; %};
- Clone<BoundingBoxf3> transformed_bounding_box() const;
bool empty() const;
bool indexed() const;
diff --git a/xs/xsp/Model.xsp b/xs/xsp/Model.xsp
index 25c26c380..182963257 100644
--- a/xs/xsp/Model.xsp
+++ b/xs/xsp/Model.xsp
@@ -301,7 +301,8 @@ ModelMaterial::attributes()
void translate(double x, double y, double z);
void scale_xyz(Pointf3* versor)
%code{% THIS->scale(*versor); %};
- void rotate(float angle, Axis axis);
+ void rotate(float angle, Pointf3* axis)
+ %code{% THIS->rotate(angle, *axis); %};
void mirror(Axis axis);
Model* cut(double z)
diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp
index 717064916..1dee8a4c4 100644
--- a/xs/xsp/Print.xsp
+++ b/xs/xsp/Print.xsp
@@ -277,6 +277,36 @@ Print::total_cost(...)
}
RETVAL = THIS->total_cost;
OUTPUT:
+ RETVAL
+
+double
+Print::total_wipe_tower_cost(...)
+ CODE:
+ if (items > 1) {
+ THIS->total_wipe_tower_cost = (double)SvNV(ST(1));
+ }
+ RETVAL = THIS->total_wipe_tower_cost;
+ OUTPUT:
+ RETVAL
+
+double
+Print::total_wipe_tower_filament(...)
+ CODE:
+ if (items > 1) {
+ THIS->total_wipe_tower_filament = (double)SvNV(ST(1));
+ }
+ RETVAL = THIS->total_wipe_tower_filament;
+ OUTPUT:
+ RETVAL
+
+int
+Print::m_wipe_tower_number_of_toolchanges(...)
+ CODE:
+ if (items > 1) {
+ THIS->m_wipe_tower_number_of_toolchanges = (double)SvNV(ST(1));
+ }
+ RETVAL = THIS->m_wipe_tower_number_of_toolchanges;
+ OUTPUT:
RETVAL
%}
};
diff --git a/xs/xsp/Utils_OctoPrint.xsp b/xs/xsp/Utils_OctoPrint.xsp
deleted file mode 100644
index 28610cb01..000000000
--- a/xs/xsp/Utils_OctoPrint.xsp
+++ /dev/null
@@ -1,13 +0,0 @@
-%module{Slic3r::XS};
-
-%{
-#include <xsinit.h>
-#include "slic3r/Utils/OctoPrint.hpp"
-%}
-
-%name{Slic3r::OctoPrint} class OctoPrint {
- OctoPrint(DynamicPrintConfig *config);
- ~OctoPrint();
-
- bool send_gcode(std::string filename) const;
-};
diff --git a/xs/xsp/Utils_PrintHost.xsp b/xs/xsp/Utils_PrintHost.xsp
new file mode 100644
index 000000000..59c09c431
--- /dev/null
+++ b/xs/xsp/Utils_PrintHost.xsp
@@ -0,0 +1,12 @@
+%module{Slic3r::XS};
+
+%{
+#include <xsinit.h>
+#include "slic3r/Utils/PrintHost.hpp"
+%}
+
+%name{Slic3r::PrintHost} class PrintHost {
+ bool send_gcode(std::string filename) const;
+
+ static PrintHost* get_print_host(DynamicPrintConfig *config);
+};
diff --git a/xs/xsp/my.map b/xs/xsp/my.map
index 4a14f483f..ba20ee236 100644
--- a/xs/xsp/my.map
+++ b/xs/xsp/my.map
@@ -239,9 +239,7 @@ Ref<TabIface> O_OBJECT_SLIC3R_T
PresetUpdater* O_OBJECT_SLIC3R
Ref<PresetUpdater> O_OBJECT_SLIC3R_T
-OctoPrint* O_OBJECT_SLIC3R
-Ref<OctoPrint> O_OBJECT_SLIC3R_T
-Clone<OctoPrint> O_OBJECT_SLIC3R_T
+PrintHost* O_OBJECT_SLIC3R
Axis T_UV
ExtrusionLoopRole T_UV
diff --git a/xs/xsp/typemap.xspt b/xs/xsp/typemap.xspt
index b576b1373..cee75fe26 100644
--- a/xs/xsp/typemap.xspt
+++ b/xs/xsp/typemap.xspt
@@ -270,3 +270,4 @@
};
%typemap{AppController*};
%typemap{PrintController*};
+%typemap{PrintHost*};