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

github.com/prusa3d/PrusaSlicer.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/xs
diff options
context:
space:
mode:
Diffstat (limited to 'xs')
-rw-r--r--xs/CMakeLists.txt52
-rw-r--r--xs/src/Shiny/ShinyPrereqs.h3
-rw-r--r--xs/src/Shiny/ShinyTools.c5
-rw-r--r--xs/src/admesh/stlinit.cpp2
-rw-r--r--xs/src/avrdude/CMakeLists.txt114
-rw-r--r--xs/src/avrdude/Makefile.standalone54
-rw-r--r--xs/src/avrdude/avrdude-slic3r.cpp118
-rw-r--r--xs/src/avrdude/avrdude-slic3r.hpp24
-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/fileio.c56
-rw-r--r--xs/src/avrdude/lexer.c8
-rw-r--r--xs/src/avrdude/libavrdude.h6
-rw-r--r--xs/src/avrdude/main-standalone.c9
-rw-r--r--xs/src/avrdude/main.c29
-rw-r--r--xs/src/avrdude/pgm.c7
-rw-r--r--xs/src/avrdude/ser_win32.c11
-rw-r--r--xs/src/avrdude/stk500v2.c7
-rw-r--r--xs/src/avrdude/update.c45
-rw-r--r--xs/src/avrdude/wiring.c7
-rw-r--r--xs/src/benchmark.h58
-rw-r--r--xs/src/libnest2d/CMakeLists.txt135
-rw-r--r--xs/src/libnest2d/LICENSE.txt661
-rw-r--r--xs/src/libnest2d/README.md43
-rw-r--r--xs/src/libnest2d/cmake_modules/DownloadNLopt.cmake32
-rw-r--r--xs/src/libnest2d/cmake_modules/DownloadProject.CMakeLists.cmake.in17
-rw-r--r--xs/src/libnest2d/cmake_modules/DownloadProject.cmake182
-rw-r--r--xs/src/libnest2d/cmake_modules/FindClipper.cmake50
-rw-r--r--xs/src/libnest2d/cmake_modules/FindNLopt.cmake125
-rw-r--r--xs/src/libnest2d/cmake_modules/FindTBB.cmake322
-rw-r--r--xs/src/libnest2d/examples/main.cpp218
-rw-r--r--xs/src/libnest2d/libnest2d.h42
-rw-r--r--xs/src/libnest2d/libnest2d/boost_alg.hpp518
-rw-r--r--xs/src/libnest2d/libnest2d/clipper_backend/CMakeLists.txt48
-rw-r--r--xs/src/libnest2d/libnest2d/clipper_backend/clipper_backend.hpp460
-rw-r--r--xs/src/libnest2d/libnest2d/common.hpp202
-rw-r--r--xs/src/libnest2d/libnest2d/geometry_traits.hpp825
-rw-r--r--xs/src/libnest2d/libnest2d/geometry_traits_nfp.hpp558
-rw-r--r--xs/src/libnest2d/libnest2d/libnest2d.hpp980
-rw-r--r--xs/src/libnest2d/libnest2d/metaloop.hpp227
-rw-r--r--xs/src/libnest2d/libnest2d/optimizer.hpp247
-rw-r--r--xs/src/libnest2d/libnest2d/optimizers/genetic.hpp31
-rw-r--r--xs/src/libnest2d/libnest2d/optimizers/nlopt_boilerplate.hpp186
-rw-r--r--xs/src/libnest2d/libnest2d/optimizers/simplex.hpp20
-rw-r--r--xs/src/libnest2d/libnest2d/optimizers/subplex.hpp20
-rw-r--r--xs/src/libnest2d/libnest2d/placers/bottomleftplacer.hpp412
-rw-r--r--xs/src/libnest2d/libnest2d/placers/nfpplacer.hpp1205
-rw-r--r--xs/src/libnest2d/libnest2d/placers/placer_boilerplate.hpp134
-rw-r--r--xs/src/libnest2d/libnest2d/rotfinder.hpp41
-rw-r--r--xs/src/libnest2d/libnest2d/selections/djd_heuristic.hpp717
-rw-r--r--xs/src/libnest2d/libnest2d/selections/filler.hpp80
-rw-r--r--xs/src/libnest2d/libnest2d/selections/firstfit.hpp97
-rw-r--r--xs/src/libnest2d/libnest2d/selections/selection_boilerplate.hpp41
-rw-r--r--xs/src/libnest2d/tests/CMakeLists.txt51
-rw-r--r--xs/src/libnest2d/tests/printer_parts.cpp3175
-rw-r--r--xs/src/libnest2d/tests/printer_parts.h40
-rw-r--r--xs/src/libnest2d/tests/test.cpp847
-rw-r--r--xs/src/libnest2d/tools/benchmark.h58
-rw-r--r--xs/src/libnest2d/tools/libnfpglue.cpp157
-rw-r--r--xs/src/libnest2d/tools/libnfpglue.hpp46
-rw-r--r--xs/src/libnest2d/tools/libnfporb/LICENSE674
-rw-r--r--xs/src/libnest2d/tools/libnfporb/ORIGIN2
-rw-r--r--xs/src/libnest2d/tools/libnfporb/README.md89
-rw-r--r--xs/src/libnest2d/tools/libnfporb/libnfporb.hpp1547
-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.hpp122
-rw-r--r--xs/src/libslic3r/BoundingBox.cpp39
-rw-r--r--xs/src/libslic3r/BoundingBox.hpp6
-rw-r--r--xs/src/libslic3r/Config.cpp30
-rw-r--r--xs/src/libslic3r/Config.hpp5
-rw-r--r--xs/src/libslic3r/EdgeGrid.cpp161
-rw-r--r--xs/src/libslic3r/EdgeGrid.hpp14
-rw-r--r--xs/src/libslic3r/ExPolygonCollection.cpp9
-rw-r--r--xs/src/libslic3r/ExtrusionEntity.hpp38
-rw-r--r--xs/src/libslic3r/ExtrusionEntityCollection.cpp1
-rw-r--r--xs/src/libslic3r/ExtrusionEntityCollection.hpp15
-rw-r--r--xs/src/libslic3r/Fill/FillGyroid.cpp2
-rw-r--r--xs/src/libslic3r/Fill/FillGyroid.hpp2
-rw-r--r--xs/src/libslic3r/Fill/FillHoneycomb.cpp4
-rw-r--r--xs/src/libslic3r/Fill/FillRectilinear3.cpp10
-rw-r--r--xs/src/libslic3r/Flow.cpp9
-rw-r--r--xs/src/libslic3r/Format/3mf.cpp17
-rw-r--r--xs/src/libslic3r/Format/AMF.cpp49
-rw-r--r--xs/src/libslic3r/GCode.cpp610
-rw-r--r--xs/src/libslic3r/GCode.hpp41
-rw-r--r--xs/src/libslic3r/GCode/PreviewData.cpp15
-rw-r--r--xs/src/libslic3r/GCode/PrintExtents.cpp10
-rw-r--r--xs/src/libslic3r/GCode/ToolOrdering.cpp327
-rw-r--r--xs/src/libslic3r/GCode/ToolOrdering.hpp142
-rw-r--r--xs/src/libslic3r/GCode/WipeTower.hpp35
-rw-r--r--xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp261
-rw-r--r--xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp61
-rw-r--r--xs/src/libslic3r/GCodeReader.cpp22
-rw-r--r--xs/src/libslic3r/GCodeReader.hpp1
-rw-r--r--xs/src/libslic3r/GCodeSender.cpp17
-rw-r--r--xs/src/libslic3r/GCodeTimeEstimator.cpp473
-rw-r--r--xs/src/libslic3r/GCodeTimeEstimator.hpp99
-rw-r--r--xs/src/libslic3r/GCodeWriter.cpp10
-rw-r--r--xs/src/libslic3r/GCodeWriter.hpp5
-rw-r--r--xs/src/libslic3r/Geometry.cpp53
-rw-r--r--xs/src/libslic3r/Geometry.hpp2
-rw-r--r--xs/src/libslic3r/I18N.hpp18
-rw-r--r--xs/src/libslic3r/Int128.hpp17
-rw-r--r--xs/src/libslic3r/Layer.hpp54
-rw-r--r--xs/src/libslic3r/LayerRegion.cpp12
-rw-r--r--xs/src/libslic3r/Model.cpp239
-rw-r--r--xs/src/libslic3r/Model.hpp109
-rw-r--r--xs/src/libslic3r/ModelArrange.hpp792
-rw-r--r--xs/src/libslic3r/MultiPoint.hpp21
-rw-r--r--xs/src/libslic3r/Point.cpp23
-rw-r--r--xs/src/libslic3r/Point.hpp32
-rw-r--r--xs/src/libslic3r/Polygon.hpp6
-rw-r--r--xs/src/libslic3r/Polyline.cpp20
-rw-r--r--xs/src/libslic3r/Polyline.hpp6
-rw-r--r--xs/src/libslic3r/Print.cpp227
-rw-r--r--xs/src/libslic3r/Print.hpp31
-rw-r--r--xs/src/libslic3r/PrintConfig.cpp385
-rw-r--r--xs/src/libslic3r/PrintConfig.hpp130
-rw-r--r--xs/src/libslic3r/PrintObject.cpp111
-rw-r--r--xs/src/libslic3r/PrintRegion.cpp5
-rw-r--r--xs/src/libslic3r/Slicing.cpp6
-rw-r--r--xs/src/libslic3r/SupportMaterial.cpp871
-rw-r--r--xs/src/libslic3r/SupportMaterial.hpp16
-rw-r--r--xs/src/libslic3r/SurfaceCollection.hpp5
-rw-r--r--xs/src/libslic3r/TriangleMesh.cpp1091
-rw-r--r--xs/src/libslic3r/TriangleMesh.hpp45
-rw-r--r--xs/src/libslic3r/Utils.hpp11
-rw-r--r--xs/src/libslic3r/libslic3r.h2
-rw-r--r--xs/src/libslic3r/utils.cpp128
-rw-r--r--xs/src/perlglue.cpp4
-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.cpp167
-rw-r--r--xs/src/slic3r/AppController.hpp244
-rw-r--r--xs/src/slic3r/AppControllerWx.cpp307
-rw-r--r--xs/src/slic3r/GUI/3DScene.cpp861
-rw-r--r--xs/src/slic3r/GUI/3DScene.hpp169
-rw-r--r--xs/src/slic3r/GUI/AppConfig.cpp21
-rw-r--r--xs/src/slic3r/GUI/AppConfig.hpp8
-rw-r--r--xs/src/slic3r/GUI/BedShapeDialog.cpp27
-rw-r--r--xs/src/slic3r/GUI/ConfigWizard.cpp63
-rw-r--r--xs/src/slic3r/GUI/ConfigWizard_private.hpp3
-rw-r--r--xs/src/slic3r/GUI/Field.cpp90
-rw-r--r--xs/src/slic3r/GUI/Field.hpp32
-rw-r--r--xs/src/slic3r/GUI/FirmwareDialog.cpp603
-rw-r--r--xs/src/slic3r/GUI/GLCanvas3D.cpp1648
-rw-r--r--xs/src/slic3r/GUI/GLCanvas3D.hpp100
-rw-r--r--xs/src/slic3r/GUI/GLCanvas3DManager.cpp60
-rw-r--r--xs/src/slic3r/GUI/GLCanvas3DManager.hpp12
-rw-r--r--xs/src/slic3r/GUI/GLGizmo.cpp398
-rw-r--r--xs/src/slic3r/GUI/GLGizmo.hpp66
-rw-r--r--xs/src/slic3r/GUI/GLShader.cpp11
-rw-r--r--xs/src/slic3r/GUI/GLShader.hpp1
-rw-r--r--xs/src/slic3r/GUI/GLTexture.cpp28
-rw-r--r--xs/src/slic3r/GUI/GLTexture.hpp9
-rw-r--r--xs/src/slic3r/GUI/GUI.cpp102
-rw-r--r--xs/src/slic3r/GUI/GUI.hpp32
-rw-r--r--xs/src/slic3r/GUI/OptionsGroup.cpp16
-rw-r--r--xs/src/slic3r/GUI/OptionsGroup.hpp12
-rw-r--r--xs/src/slic3r/GUI/Preset.cpp173
-rw-r--r--xs/src/slic3r/GUI/Preset.hpp33
-rw-r--r--xs/src/slic3r/GUI/PresetBundle.cpp177
-rw-r--r--xs/src/slic3r/GUI/Tab.cpp213
-rw-r--r--xs/src/slic3r/GUI/Tab.hpp9
-rw-r--r--xs/src/slic3r/ProgressIndicator.hpp64
-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/HexFile.cpp106
-rw-r--r--xs/src/slic3r/Utils/HexFile.hpp33
-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/PresetUpdater.cpp49
-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/src/slic3r/Utils/Serial.cpp320
-rw-r--r--xs/src/slic3r/Utils/Serial.hpp66
-rw-r--r--xs/src/xsinit.h1
-rw-r--r--xs/t/01_trianglemesh.t8
-rw-r--r--xs/xsp/AppController.xsp24
-rw-r--r--xs/xsp/Config.xsp6
-rw-r--r--xs/xsp/GUI.xsp16
-rw-r--r--xs/xsp/GUI_3DScene.xsp116
-rw-r--r--xs/xsp/Layer.xsp2
-rw-r--r--xs/xsp/Model.xsp19
-rw-r--r--xs/xsp/Print.xsp38
-rw-r--r--xs/xsp/Utils_OctoPrint.xsp13
-rw-r--r--xs/xsp/Utils_PrintHost.xsp12
-rw-r--r--xs/xsp/XS.xsp5
-rw-r--r--xs/xsp/my.map6
-rw-r--r--xs/xsp/typemap.xspt3
431 files changed, 132414 insertions, 3393 deletions
diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt
index f4d0b17ea..d19aad70a 100644
--- a/xs/CMakeLists.txt
+++ b/xs/CMakeLists.txt
@@ -26,7 +26,7 @@ include_directories(${LIBDIR}/libslic3r)
if(WIN32)
# BOOST_ALL_NO_LIB: Avoid the automatic linking of Boost libraries on Windows. Rather rely on explicit linking.
- add_definitions(-D_USE_MATH_DEFINES -D_WIN32 -DBOOST_ALL_NO_LIB)
+ add_definitions(-D_USE_MATH_DEFINES -D_WIN32 -DBOOST_ALL_NO_LIB -DBOOST_USE_WINAPI_VERSION=0x601)
# -D_ITERATOR_DEBUG_LEVEL)
if(WIN10SDK_PATH)
message("Building with Win10 Netfabb STL fixing service support")
@@ -136,6 +136,7 @@ add_library(libslic3r STATIC
${LIBDIR}/libslic3r/Line.hpp
${LIBDIR}/libslic3r/Model.cpp
${LIBDIR}/libslic3r/Model.hpp
+ ${LIBDIR}/libslic3r/ModelArrange.hpp
${LIBDIR}/libslic3r/MotionPlanner.cpp
${LIBDIR}/libslic3r/MotionPlanner.hpp
${LIBDIR}/libslic3r/MultiPoint.cpp
@@ -250,14 +251,26 @@ 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
${LIBDIR}/slic3r/Utils/PresetUpdater.hpp
${LIBDIR}/slic3r/Utils/Time.cpp
${LIBDIR}/slic3r/Utils/Time.hpp
+ ${LIBDIR}/slic3r/Utils/HexFile.cpp
+ ${LIBDIR}/slic3r/Utils/HexFile.hpp
+ ${LIBDIR}/slic3r/ProgressIndicator.hpp
+ ${LIBDIR}/slic3r/AppController.hpp
+ ${LIBDIR}/slic3r/AppController.cpp
+ ${LIBDIR}/slic3r/AppControllerWx.cpp
)
add_library(admesh STATIC
@@ -359,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
)
@@ -403,8 +416,9 @@ 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
)
foreach (file ${XS_XSP_FILES})
@@ -423,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.
@@ -450,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()
@@ -487,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")
@@ -531,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
@@ -713,6 +740,21 @@ add_custom_target(pot
COMMENT "Generate pot file from strings in the source tree"
)
+# ##############################################################################
+# Adding libnest2d project for bin packing...
+# ##############################################################################
+
+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})
+
+message(STATUS "Libnest2D Libraries: ${LIBNEST2D_LIBRARIES}")
+target_link_libraries(libslic3r ${LIBNEST2D_LIBRARIES})
+# ##############################################################################
+
# Installation
install(TARGETS XS DESTINATION ${PERL_VENDORARCH}/auto/Slic3r/XS)
install(FILES lib/Slic3r/XS.pm DESTINATION ${PERL_VENDORLIB}/Slic3r)
diff --git a/xs/src/Shiny/ShinyPrereqs.h b/xs/src/Shiny/ShinyPrereqs.h
index a392515c7..5a3044dbc 100644
--- a/xs/src/Shiny/ShinyPrereqs.h
+++ b/xs/src/Shiny/ShinyPrereqs.h
@@ -25,6 +25,9 @@ THE SOFTWARE.
#ifndef SHINY_PREREQS_H
#define SHINY_PREREQS_H
+
+#include <stdint.h>
+
/*---------------------------------------------------------------------------*/
#ifndef FALSE
diff --git a/xs/src/Shiny/ShinyTools.c b/xs/src/Shiny/ShinyTools.c
index bfc0bcdf5..4058e2285 100644
--- a/xs/src/Shiny/ShinyTools.c
+++ b/xs/src/Shiny/ShinyTools.c
@@ -93,8 +93,11 @@ float ShinyGetTickInvFreq(void) {
#elif SHINY_PLATFORM == SHINY_PLATFORM_POSIX
+//#include <time.h>
+//#include <sys/time.h>
+
void ShinyGetTicks(shinytick_t *p) {
- timeval time;
+ struct timeval time;
gettimeofday(&time, NULL);
*p = time.tv_sec * 1000000 + time.tv_usec;
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/CMakeLists.txt b/xs/src/avrdude/CMakeLists.txt
index 043f8fb7b..d88563368 100644
--- a/xs/src/avrdude/CMakeLists.txt
+++ b/xs/src/avrdude/CMakeLists.txt
@@ -1,3 +1,4 @@
+cmake_minimum_required(VERSION 3.0)
add_definitions(-D_BSD_SOURCE -D_DEFAULT_SOURCE) # To enable various useful macros and functions on Unices
@@ -13,67 +14,74 @@ endif()
set(AVRDUDE_SOURCES
- ${LIBDIR}/avrdude/arduino.c
- ${LIBDIR}/avrdude/avr.c
- # ${LIBDIR}/avrdude/avrftdi.c
- # ${LIBDIR}/avrdude/avrftdi_tpi.c
- ${LIBDIR}/avrdude/avrpart.c
- ${LIBDIR}/avrdude/avr910.c
- ${LIBDIR}/avrdude/bitbang.c
- ${LIBDIR}/avrdude/buspirate.c
- ${LIBDIR}/avrdude/butterfly.c
- ${LIBDIR}/avrdude/config.c
- ${LIBDIR}/avrdude/config_gram.c
- # ${LIBDIR}/avrdude/confwin.c
- ${LIBDIR}/avrdude/crc16.c
- # ${LIBDIR}/avrdude/dfu.c
- ${LIBDIR}/avrdude/fileio.c
- # ${LIBDIR}/avrdude/flip1.c
- # ${LIBDIR}/avrdude/flip2.c
- # ${LIBDIR}/avrdude/ft245r.c
- # ${LIBDIR}/avrdude/jtagmkI.c
- # ${LIBDIR}/avrdude/jtagmkII.c
- # ${LIBDIR}/avrdude/jtag3.c
- ${LIBDIR}/avrdude/lexer.c
- ${LIBDIR}/avrdude/linuxgpio.c
- ${LIBDIR}/avrdude/lists.c
- # ${LIBDIR}/avrdude/par.c
- ${LIBDIR}/avrdude/pgm.c
- ${LIBDIR}/avrdude/pgm_type.c
- ${LIBDIR}/avrdude/pickit2.c
- ${LIBDIR}/avrdude/pindefs.c
- # ${LIBDIR}/avrdude/ppi.c
- # ${LIBDIR}/avrdude/ppiwin.c
- ${LIBDIR}/avrdude/safemode.c
- ${LIBDIR}/avrdude/ser_avrdoper.c
- ${LIBDIR}/avrdude/serbb_posix.c
- ${LIBDIR}/avrdude/serbb_win32.c
- ${LIBDIR}/avrdude/ser_posix.c
- ${LIBDIR}/avrdude/ser_win32.c
- ${LIBDIR}/avrdude/stk500.c
- ${LIBDIR}/avrdude/stk500generic.c
- ${LIBDIR}/avrdude/stk500v2.c
- ${LIBDIR}/avrdude/term.c
- ${LIBDIR}/avrdude/update.c
- # ${LIBDIR}/avrdude/usbasp.c
- # ${LIBDIR}/avrdude/usb_hidapi.c
- # ${LIBDIR}/avrdude/usb_libusb.c
- # ${LIBDIR}/avrdude/usbtiny.c
- ${LIBDIR}/avrdude/wiring.c
+ arduino.c
+ avr.c
+ # avrftdi.c
+ # avrftdi_tpi.c
+ avrpart.c
+ avr910.c
+ bitbang.c
+ buspirate.c
+ butterfly.c
+ config.c
+ config_gram.c
+ # confwin.c
+ crc16.c
+ # dfu.c
+ fileio.c
+ # flip1.c
+ # flip2.c
+ # ft245r.c
+ # jtagmkI.c
+ # jtagmkII.c
+ # jtag3.c
+ lexer.c
+ linuxgpio.c
+ lists.c
+ # par.c
+ pgm.c
+ pgm_type.c
+ pickit2.c
+ pindefs.c
+ # ppi.c
+ # ppiwin.c
+ safemode.c
+ ser_avrdoper.c
+ serbb_posix.c
+ serbb_win32.c
+ ser_posix.c
+ ser_win32.c
+ stk500.c
+ stk500generic.c
+ stk500v2.c
+ term.c
+ update.c
+ # usbasp.c
+ # usb_hidapi.c
+ # usb_libusb.c
+ # usbtiny.c
+ wiring.c
- ${LIBDIR}/avrdude/main.c
- ${LIBDIR}/avrdude/avrdude-slic3r.hpp
- ${LIBDIR}/avrdude/avrdude-slic3r.cpp
+ main.c
+ avrdude-slic3r.hpp
+ avrdude-slic3r.cpp
)
if (WIN32)
set(AVRDUDE_SOURCES ${AVRDUDE_SOURCES}
- ${LIBDIR}/avrdude/windows/unistd.cpp
- ${LIBDIR}/avrdude/windows/getopt.c
+ windows/unistd.cpp
+ windows/getopt.c
)
endif()
add_library(avrdude STATIC ${AVRDUDE_SOURCES})
+set(STANDALONE_SOURCES
+ main-standalone.c
+)
+add_executable(avrdude-slic3r ${STANDALONE_SOURCES})
+target_link_libraries(avrdude-slic3r avrdude)
+set_target_properties(avrdude-slic3r PROPERTIES EXCLUDE_FROM_ALL TRUE)
+
if (WIN32)
target_compile_definitions(avrdude PRIVATE WIN32NATIVE=1)
- target_include_directories(avrdude SYSTEM PRIVATE ${LIBDIR}/avrdude/windows) # So that sources find the getopt.h windows drop-in
+ target_include_directories(avrdude SYSTEM PRIVATE windows) # So that sources find the getopt.h windows drop-in
endif()
diff --git a/xs/src/avrdude/Makefile.standalone b/xs/src/avrdude/Makefile.standalone
new file mode 100644
index 000000000..d9a773771
--- /dev/null
+++ b/xs/src/avrdude/Makefile.standalone
@@ -0,0 +1,54 @@
+
+TARGET = avrdude-slic3r
+
+SOURCES = \
+ arduino.c \
+ avr.c \
+ avrpart.c \
+ avr910.c \
+ bitbang.c \
+ buspirate.c \
+ butterfly.c \
+ config.c \
+ config_gram.c \
+ crc16.c \
+ fileio.c \
+ lexer.c \
+ linuxgpio.c \
+ lists.c \
+ pgm.c \
+ pgm_type.c \
+ pickit2.c \
+ pindefs.c \
+ safemode.c \
+ ser_avrdoper.c \
+ serbb_posix.c \
+ serbb_win32.c \
+ ser_posix.c \
+ ser_win32.c \
+ stk500.c \
+ stk500generic.c \
+ stk500v2.c \
+ term.c \
+ update.c \
+ wiring.c \
+ main.c \
+ main-standalone.c
+
+OBJECTS = $(SOURCES:.c=.o)
+CFLAGS = -std=c99 -Wall -D_BSD_SOURCE -D_DEFAULT_SOURCE -O3 -DNDEBUG -fPIC
+LDFLAGS = -lm
+
+CC = gcc
+RM = rm
+
+all: $(TARGET)
+
+$(TARGET): $(OBJECTS)
+ $(CC) -o ./$@ $(OBJECTS) $(LDFLAGS)
+
+$(OBJECTS): %.o: %.c
+ $(CC) $(CFLAGS) -o $@ -c $<
+
+clean:
+ $(RM) -f $(OBJECTS) $(TARGET)
diff --git a/xs/src/avrdude/avrdude-slic3r.cpp b/xs/src/avrdude/avrdude-slic3r.cpp
index 030353413..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
@@ -35,6 +44,8 @@ struct AvrDude::priv
{
std::string sys_config;
std::deque<std::vector<std::string>> args;
+ bool cancelled = false;
+ int exit_code = 0;
size_t current_args_set = 0;
RunFn run_fn;
MessageFn message_fn;
@@ -45,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 {
@@ -67,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;
}
@@ -132,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()
@@ -141,14 +175,46 @@ AvrDude::Ptr AvrDude::run()
if (self->p) {
auto avrdude_thread = std::thread([self]() {
- if (self->p->run_fn) {
- self->p->run_fn();
- }
-
- auto res = self->p->run();
-
- if (self->p->complete_fn) {
- self->p->complete_fn(res, self->p->current_args_set);
+ 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();
+ }
}
});
@@ -160,7 +226,10 @@ AvrDude::Ptr AvrDude::run()
void AvrDude::cancel()
{
- ::avrdude_cancel();
+ if (p) {
+ p->cancelled = true;
+ ::avrdude_cancel();
+ }
}
void AvrDude::join()
@@ -170,5 +239,20 @@ void AvrDude::join()
}
}
+bool AvrDude::cancelled()
+{
+ return p ? p->cancelled : false;
+}
+
+int AvrDude::exit_code()
+{
+ return p ? p->exit_code : 0;
+}
+
+size_t AvrDude::last_args_set()
+{
+ return p ? p->current_args_set : 0;
+}
+
}
diff --git a/xs/src/avrdude/avrdude-slic3r.hpp b/xs/src/avrdude/avrdude-slic3r.hpp
index 273aa2378..754e1e345 100644
--- a/xs/src/avrdude/avrdude-slic3r.hpp
+++ b/xs/src/avrdude/avrdude-slic3r.hpp
@@ -11,11 +11,16 @@ 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(int /* exit status */, size_t /* args_id */)> CompleteFn;
+ typedef std::function<void()> CompleteFn;
// Main c-tor, sys_config is the location of avrdude's main configuration file
AvrDude(std::string sys_config);
@@ -31,7 +36,8 @@ public:
AvrDude& push_args(std::vector<std::string> args);
// Set a callback to be called just after run() before avrdude is ran
- // This can be used to perform any needed setup tasks from the background thread.
+ // This can be used to perform any needed setup tasks from the background thread,
+ // and, optionally, to cancel by writing true to the `cancel` argument.
// This has no effect when using run_sync().
AvrDude& on_run(RunFn fn);
@@ -48,11 +54,23 @@ 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
+ int exit_code(); // The exit code of the last invocation
+ size_t last_args_set(); // Index of the last argument set that was processsed
private:
struct priv;
std::unique_ptr<priv> p;
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/fileio.c b/xs/src/avrdude/fileio.c
index aa57f5587..708159295 100644
--- a/xs/src/avrdude/fileio.c
+++ b/xs/src/avrdude/fileio.c
@@ -98,11 +98,11 @@ static int fileio_num(struct fioparms * fio,
char * filename, FILE * f, AVRMEM * mem, int size,
FILEFMT fmt);
-static int fmt_autodetect(char * fname, size_t offset);
+static int fmt_autodetect(char * fname, unsigned section);
-static FILE *fopen_and_seek(const char *filename, const char *mode, size_t offset)
+static FILE *fopen_and_seek(const char *filename, const char *mode, unsigned section)
{
FILE *file;
// On Windows we need to convert the filename to UTF-16
@@ -118,16 +118,38 @@ static FILE *fopen_and_seek(const char *filename, const char *mode, size_t offse
file = fopen(filename, mode);
#endif
- if (file != NULL) {
- // Some systems allow seeking past the end of file, so we need check for that first and disallow
- if (fseek(file, 0, SEEK_END) != 0
- || offset >= ftell(file)
- || fseek(file, offset, SEEK_SET) != 0
- ) {
- fclose(file);
- file = NULL;
- errno = EINVAL;
+ if (file == NULL) {
+ return NULL;
+ }
+
+ // Seek to the specified 'section'
+ static const char *hex_terminator = ":00000001FF\r";
+ unsigned terms_seen = 0;
+ char buffer[MAX_LINE_LEN + 1];
+
+ while (terms_seen < section && fgets(buffer, MAX_LINE_LEN, file) != NULL) {
+ size_t len = strlen(buffer);
+
+ if (buffer[len - 1] == '\n') {
+ len--;
+ buffer[len] = 0;
+ }
+ if (buffer[len - 1] != '\r') {
+ buffer[len] = '\r';
+ len++;
+ buffer[len] = 0;
}
+
+ if (strcmp(buffer, hex_terminator) == 0) {
+ // Found a section terminator
+ terms_seen++;
+ }
+ }
+
+ if (feof(file)) {
+ // Section not found
+ fclose(file);
+ return NULL;
}
return file;
@@ -1392,7 +1414,7 @@ int fileio_setparms(int op, struct fioparms * fp,
-static int fmt_autodetect(char * fname, size_t offset)
+static int fmt_autodetect(char * fname, unsigned section)
{
FILE * f;
unsigned char buf[MAX_LINE_LEN];
@@ -1402,9 +1424,9 @@ static int fmt_autodetect(char * fname, size_t offset)
int first = 1;
#if defined(WIN32NATIVE)
- f = fopen_and_seek(fname, "r", offset);
+ f = fopen_and_seek(fname, "r", section);
#else
- f = fopen_and_seek(fname, "rb", offset);
+ f = fopen_and_seek(fname, "rb", section);
#endif
if (f == NULL) {
@@ -1480,7 +1502,7 @@ static int fmt_autodetect(char * fname, size_t offset)
int fileio(int op, char * filename, FILEFMT format,
- struct avrpart * p, char * memtype, int size, size_t offset)
+ struct avrpart * p, char * memtype, int size, unsigned section)
{
int rc;
FILE * f;
@@ -1539,7 +1561,7 @@ int fileio(int op, char * filename, FILEFMT format,
return -1;
}
- format_detect = fmt_autodetect(fname, offset);
+ format_detect = fmt_autodetect(fname, section);
if (format_detect < 0) {
avrdude_message(MSG_INFO, "%s: can't determine file format for %s, specify explicitly\n",
progname, fname);
@@ -1570,7 +1592,7 @@ int fileio(int op, char * filename, FILEFMT format,
if (format != FMT_IMM) {
if (!using_stdio) {
- f = fopen_and_seek(fname, fio.mode, offset);
+ f = fopen_and_seek(fname, fio.mode, section);
if (f == NULL) {
avrdude_message(MSG_INFO, "%s: can't open %s file %s: %s\n",
progname, fio.iodesc, fname, strerror(errno));
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/libavrdude.h b/xs/src/avrdude/libavrdude.h
index 536f1a2f7..aef792476 100644
--- a/xs/src/avrdude/libavrdude.h
+++ b/xs/src/avrdude/libavrdude.h
@@ -821,7 +821,7 @@ extern "C" {
char * fmtstr(FILEFMT format);
int fileio(int op, char * filename, FILEFMT format,
- struct avrpart * p, char * memtype, int size, size_t offset);
+ struct avrpart * p, char * memtype, int size, unsigned section);
#ifdef __cplusplus
}
@@ -870,7 +870,7 @@ enum updateflags {
typedef struct update_t {
char * memtype;
int op;
- size_t offset;
+ unsigned section;
char * filename;
int format;
} UPDATE;
@@ -882,7 +882,7 @@ extern "C" {
extern UPDATE * parse_op(char * s);
extern UPDATE * dup_update(UPDATE * upd);
extern UPDATE * new_update(int op, char * memtype, int filefmt,
- char * filename, size_t offset);
+ char * filename, unsigned section);
extern void free_update(UPDATE * upd);
extern int do_op(PROGRAMMER * pgm, struct avrpart * p, UPDATE * upd,
enum updateflags flags);
diff --git a/xs/src/avrdude/main-standalone.c b/xs/src/avrdude/main-standalone.c
new file mode 100644
index 000000000..359a055ca
--- /dev/null
+++ b/xs/src/avrdude/main-standalone.c
@@ -0,0 +1,9 @@
+#include "avrdude.h"
+
+
+static const char* SYS_CONFIG = "/etc/avrdude-slic3r.conf";
+
+int main(int argc, char *argv[])
+{
+ return avrdude_main(argc, argv, SYS_CONFIG);
+}
diff --git a/xs/src/avrdude/main.c b/xs/src/avrdude/main.c
index d4c34fe44..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;
@@ -194,7 +221,7 @@ static void usage(void)
" -F Override invalid signature check.\n"
" -e Perform a chip erase.\n"
" -O Perform RC oscillator calibration (see AVR053). \n"
- " -U <memtype>:r|w|v:<offset>:<filename>[:format]\n"
+ " -U <memtype>:r|w|v:<section>:<filename>[:format]\n"
" Memory operation specification.\n"
" Multiple -U options are allowed, each request\n"
" is performed in the order specified.\n"
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 20d085d13..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);
@@ -311,8 +312,10 @@ static int ser_open(char * port, union pinfo pinfo, union filedescriptor *fdp)
static void ser_close(union filedescriptor *fd)
{
if (serial_over_ethernet) {
+#ifdef HAVE_LIBWS2_32
closesocket(fd->ifd);
WSACleanup();
+#endif
} else {
HANDLE hComPort=(HANDLE)fd->pfd;
if (hComPort != INVALID_HANDLE_VALUE)
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 e9dd6e325..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);
@@ -101,22 +104,22 @@ UPDATE * parse_op(char * s)
p++;
- // Extension: Parse file contents offset
- size_t offset = 0;
+ // Extension: Parse file section number
+ unsigned section = 0;
for (; *p != ':'; p++) {
if (*p >= '0' && *p <= '9') {
- offset *= 10;
- offset += *p - 0x30;
+ section *= 10;
+ section += *p - 0x30;
} else {
- avrdude_message(MSG_INFO, "%s: invalid update specification: offset is not a number\n", progname);
+ avrdude_message(MSG_INFO, "%s: invalid update specification: <section> is not a number\n", progname);
free(upd->memtype);
free(upd);
return NULL;
}
}
- upd->offset = offset;
+ upd->section = section;
p++;
/*
@@ -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));
@@ -194,21 +198,22 @@ UPDATE * dup_update(UPDATE * upd)
return u;
}
-UPDATE * new_update(int op, char * memtype, int filefmt, char * filename, size_t offset)
+UPDATE * new_update(int op, char * memtype, int filefmt, char * filename, unsigned section)
{
UPDATE * u;
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);
u->filename = strdup(filename);
u->op = op;
u->format = filefmt;
- u->offset = offset;
+ u->section = section;
return u;
}
@@ -286,7 +291,7 @@ int do_op(PROGRAMMER * pgm, struct avrpart * p, UPDATE * upd, enum updateflags f
progname,
strcmp(upd->filename, "-")==0 ? "<stdin>" : upd->filename);
}
- rc = fileio(FIO_READ, upd->filename, upd->format, p, upd->memtype, -1, upd->offset);
+ rc = fileio(FIO_READ, upd->filename, upd->format, p, upd->memtype, -1, upd->section);
if (rc < 0) {
avrdude_message(MSG_INFO, "%s: read from file '%s' failed\n",
progname, upd->filename);
@@ -351,7 +356,7 @@ int do_op(PROGRAMMER * pgm, struct avrpart * p, UPDATE * upd, enum updateflags f
progname, mem->desc, upd->filename);
}
- rc = fileio(FIO_READ, upd->filename, upd->format, p, upd->memtype, -1, upd->offset);
+ rc = fileio(FIO_READ, upd->filename, upd->format, p, upd->memtype, -1, upd->section);
if (rc < 0) {
avrdude_message(MSG_INFO, "%s: read from file '%s' failed\n",
progname, upd->filename);
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/benchmark.h b/xs/src/benchmark.h
new file mode 100644
index 000000000..19870b37b
--- /dev/null
+++ b/xs/src/benchmark.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) Tamás Mészáros
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+#ifndef INCLUDE_BENCHMARK_H_
+#define INCLUDE_BENCHMARK_H_
+
+#include <chrono>
+#include <ratio>
+
+/**
+ * A class for doing benchmarks.
+ */
+class Benchmark {
+ typedef std::chrono::high_resolution_clock Clock;
+ typedef Clock::duration Duration;
+ typedef Clock::time_point TimePoint;
+
+ TimePoint t1, t2;
+ Duration d;
+
+ inline double to_sec(Duration d) {
+ return d.count() * double(Duration::period::num) / Duration::period::den;
+ }
+
+public:
+
+ /**
+ * Measure time from the moment of this call.
+ */
+ void start() { t1 = Clock::now(); }
+
+ /**
+ * Measure time to the moment of this call.
+ */
+ void stop() { t2 = Clock::now(); }
+
+ /**
+ * Get the time elapsed between a start() end a stop() call.
+ * @return Returns the elapsed time in seconds.
+ */
+ double getElapsedSec() { d = t2 - t1; return to_sec(d); }
+};
+
+
+#endif /* INCLUDE_BENCHMARK_H_ */
diff --git a/xs/src/libnest2d/CMakeLists.txt b/xs/src/libnest2d/CMakeLists.txt
new file mode 100644
index 000000000..f81355012
--- /dev/null
+++ b/xs/src/libnest2d/CMakeLists.txt
@@ -0,0 +1,135 @@
+cmake_minimum_required(VERSION 2.8)
+
+project(Libnest2D)
+
+if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
+ # Update if necessary
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-long-long ")
+endif()
+
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD_REQUIRED)
+
+# Add our own cmake module path.
+list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules/)
+
+option(LIBNEST2D_UNITTESTS "If enabled, googletest framework will be downloaded
+ and the provided unit tests will be included in the build." OFF)
+
+option(LIBNEST2D_BUILD_EXAMPLES "If enabled, examples will be built." OFF)
+
+set(LIBNEST2D_GEOMETRIES_BACKEND "clipper" CACHE STRING
+ "Build libnest2d with geometry classes implemented by the chosen backend.")
+
+set(LIBNEST2D_OPTIMIZER_BACKEND "nlopt" CACHE STRING
+ "Build libnest2d with optimization features implemented by the chosen backend.")
+
+set(LIBNEST2D_SRCFILES
+ ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/libnest2d.hpp # Templates only
+ ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d.h # Exports ready made types using template arguments
+ ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/geometry_traits.hpp
+ ${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
+ ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/geometry_traits_nfp.hpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/selections/selection_boilerplate.hpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/selections/filler.hpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/selections/firstfit.hpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/selections/djd_heuristic.hpp
+ )
+
+set(LIBNEST2D_LIBRARIES "")
+
+set(LIBNEST2D_HEADERS ${CMAKE_CURRENT_SOURCE_DIR})
+
+if(LIBNEST2D_GEOMETRIES_BACKEND STREQUAL "clipper")
+
+ # Clipper backend is not enough on its own, it still needs some functions
+ # from Boost geometry
+ if(NOT Boost_INCLUDE_DIRS_FOUND)
+ find_package(Boost 1.58 REQUIRED)
+ # TODO automatic download of boost geometry headers
+ endif()
+
+ add_subdirectory(libnest2d/clipper_backend)
+
+ include_directories(BEFORE ${CLIPPER_INCLUDE_DIRS})
+ include_directories(${Boost_INCLUDE_DIRS})
+
+ list(APPEND LIBNEST2D_SRCFILES ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/clipper_backend/clipper_backend.hpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/boost_alg.hpp)
+ list(APPEND LIBNEST2D_LIBRARIES ${CLIPPER_LIBRARIES})
+ list(APPEND LIBNEST2D_HEADERS ${CLIPPER_INCLUDE_DIRS}
+ ${Boost_INCLUDE_DIRS_FOUND})
+endif()
+
+if(LIBNEST2D_OPTIMIZER_BACKEND STREQUAL "nlopt")
+ find_package(NLopt 1.4)
+ if(NOT NLopt_FOUND)
+ message(STATUS "NLopt not found so downloading "
+ "and automatic build is performed...")
+ include(DownloadNLopt)
+ endif()
+ find_package(Threads REQUIRED)
+
+ list(APPEND LIBNEST2D_SRCFILES ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/optimizers/simplex.hpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/optimizers/subplex.hpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/optimizers/genetic.hpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/optimizers/nlopt_boilerplate.hpp)
+ list(APPEND LIBNEST2D_LIBRARIES ${NLopt_LIBS})
+ list(APPEND LIBNEST2D_HEADERS ${NLopt_INCLUDE_DIR})
+endif()
+
+if(LIBNEST2D_UNITTESTS)
+ enable_testing()
+ add_subdirectory(tests)
+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}
+ )
+ 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})
+endif()
+
+get_directory_property(hasParent PARENT_DIRECTORY)
+if(hasParent)
+ set(LIBNEST2D_INCLUDES ${LIBNEST2D_HEADERS} PARENT_SCOPE)
+ set(LIBNEST2D_LIBRARIES ${LIBNEST2D_LIBRARIES} PARENT_SCOPE)
+endif()
diff --git a/xs/src/libnest2d/LICENSE.txt b/xs/src/libnest2d/LICENSE.txt
new file mode 100644
index 000000000..dba13ed2d
--- /dev/null
+++ b/xs/src/libnest2d/LICENSE.txt
@@ -0,0 +1,661 @@
+ GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3, 19 November 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+our General Public Licenses are intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+ A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate. Many developers of free software are heartened and
+encouraged by the resulting cooperation. However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+ The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community. It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server. Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+ An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals. This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU Affero General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Remote Network Interaction; Use with the GNU General Public License.
+
+ Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software. This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero General Public License from time to time. Such new versions
+will be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU Affero General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU Affero General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source. For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code. There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU AGPL, see
+<http://www.gnu.org/licenses/>.
diff --git a/xs/src/libnest2d/README.md b/xs/src/libnest2d/README.md
new file mode 100644
index 000000000..61a7ac7d0
--- /dev/null
+++ b/xs/src/libnest2d/README.md
@@ -0,0 +1,43 @@
+# Introduction
+
+Libnest2D is a library and framework for the 2D bin packaging problem.
+Inspired from the [SVGNest](svgnest.com) Javascript library the project is
+built from scratch in C++11. The library is written with a policy that it should
+be usable out of the box with a very simple interface but has to be customizable
+to the very core as well. The algorithms are defined in a header only fashion
+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. 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 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. 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)
+- [An effective heuristic for the two-dimensional irregular
+bin packing problem](http://www.cs.stir.ac.uk/~goc/papers/EffectiveHueristic2DAOR2013.pdf)
+- [Complete and robust no-fit polygon generation for the irregular stock cutting problem](https://www.sciencedirect.com/science/article/abs/pii/S0377221706001639)
+- [Applying Meta-Heuristic Algorithms to the Nesting
+Problem Utilising the No Fit Polygon](http://www.graham-kendall.com/papers/k2001.pdf)
+- [A comprehensive and robust procedure for obtaining the nofit polygon
+using Minkowski sums](https://www.sciencedirect.com/science/article/pii/S0305054806000669) \ No newline at end of file
diff --git a/xs/src/libnest2d/cmake_modules/DownloadNLopt.cmake b/xs/src/libnest2d/cmake_modules/DownloadNLopt.cmake
new file mode 100644
index 000000000..0f5392596
--- /dev/null
+++ b/xs/src/libnest2d/cmake_modules/DownloadNLopt.cmake
@@ -0,0 +1,32 @@
+include(DownloadProject)
+
+if (CMAKE_VERSION VERSION_LESS 3.2)
+ set(UPDATE_DISCONNECTED_IF_AVAILABLE "")
+else()
+ set(UPDATE_DISCONNECTED_IF_AVAILABLE "UPDATE_DISCONNECTED 1")
+endif()
+
+# set(NLopt_DIR ${CMAKE_BINARY_DIR}/nlopt)
+include(DownloadProject)
+download_project( PROJ nlopt
+ GIT_REPOSITORY https://github.com/stevengj/nlopt.git
+ GIT_TAG 1fcbcbf2fe8e34234e016cc43a6c41d3e8453e1f #master #nlopt-2.4.2
+ # CMAKE_CACHE_ARGS -DBUILD_SHARED_LIBS:BOOL=OFF -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX=${NLopt_DIR}
+ ${UPDATE_DISCONNECTED_IF_AVAILABLE}
+)
+
+set(SHARED_LIBS_STATE BUILD_SHARED_LIBS)
+set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
+set(NLOPT_PYTHON OFF CACHE BOOL "" FORCE)
+set(NLOPT_OCTAVE OFF CACHE BOOL "" FORCE)
+set(NLOPT_MATLAB OFF CACHE BOOL "" FORCE)
+set(NLOPT_GUILE OFF CACHE BOOL "" FORCE)
+set(NLOPT_SWIG OFF CACHE BOOL "" FORCE)
+set(NLOPT_LINK_PYTHON OFF CACHE BOOL "" FORCE)
+
+add_subdirectory(${nlopt_SOURCE_DIR} ${nlopt_BINARY_DIR})
+
+set(NLopt_LIBS nlopt)
+set(NLopt_INCLUDE_DIR ${nlopt_BINARY_DIR}
+ ${nlopt_BINARY_DIR}/src/api)
+set(SHARED_LIBS_STATE ${SHARED_STATE}) \ No newline at end of file
diff --git a/xs/src/libnest2d/cmake_modules/DownloadProject.CMakeLists.cmake.in b/xs/src/libnest2d/cmake_modules/DownloadProject.CMakeLists.cmake.in
new file mode 100644
index 000000000..d5cf3c1d9
--- /dev/null
+++ b/xs/src/libnest2d/cmake_modules/DownloadProject.CMakeLists.cmake.in
@@ -0,0 +1,17 @@
+# Distributed under the OSI-approved MIT License. See accompanying
+# file LICENSE or https://github.com/Crascit/DownloadProject for details.
+
+cmake_minimum_required(VERSION 2.8.2)
+
+project(${DL_ARGS_PROJ}-download NONE)
+
+include(ExternalProject)
+ExternalProject_Add(${DL_ARGS_PROJ}-download
+ ${DL_ARGS_UNPARSED_ARGUMENTS}
+ SOURCE_DIR "${DL_ARGS_SOURCE_DIR}"
+ BINARY_DIR "${DL_ARGS_BINARY_DIR}"
+ CONFIGURE_COMMAND ""
+ BUILD_COMMAND ""
+ INSTALL_COMMAND ""
+ TEST_COMMAND ""
+) \ No newline at end of file
diff --git a/xs/src/libnest2d/cmake_modules/DownloadProject.cmake b/xs/src/libnest2d/cmake_modules/DownloadProject.cmake
new file mode 100644
index 000000000..1709e09ad
--- /dev/null
+++ b/xs/src/libnest2d/cmake_modules/DownloadProject.cmake
@@ -0,0 +1,182 @@
+# Distributed under the OSI-approved MIT License. See accompanying
+# file LICENSE or https://github.com/Crascit/DownloadProject for details.
+#
+# MODULE: DownloadProject
+#
+# PROVIDES:
+# download_project( PROJ projectName
+# [PREFIX prefixDir]
+# [DOWNLOAD_DIR downloadDir]
+# [SOURCE_DIR srcDir]
+# [BINARY_DIR binDir]
+# [QUIET]
+# ...
+# )
+#
+# Provides the ability to download and unpack a tarball, zip file, git repository,
+# etc. at configure time (i.e. when the cmake command is run). How the downloaded
+# and unpacked contents are used is up to the caller, but the motivating case is
+# to download source code which can then be included directly in the build with
+# add_subdirectory() after the call to download_project(). Source and build
+# directories are set up with this in mind.
+#
+# The PROJ argument is required. The projectName value will be used to construct
+# the following variables upon exit (obviously replace projectName with its actual
+# value):
+#
+# projectName_SOURCE_DIR
+# projectName_BINARY_DIR
+#
+# The SOURCE_DIR and BINARY_DIR arguments are optional and would not typically
+# need to be provided. They can be specified if you want the downloaded source
+# and build directories to be located in a specific place. The contents of
+# projectName_SOURCE_DIR and projectName_BINARY_DIR will be populated with the
+# locations used whether you provide SOURCE_DIR/BINARY_DIR or not.
+#
+# The DOWNLOAD_DIR argument does not normally need to be set. It controls the
+# location of the temporary CMake build used to perform the download.
+#
+# The PREFIX argument can be provided to change the base location of the default
+# values of DOWNLOAD_DIR, SOURCE_DIR and BINARY_DIR. If all of those three arguments
+# are provided, then PREFIX will have no effect. The default value for PREFIX is
+# CMAKE_BINARY_DIR.
+#
+# The QUIET option can be given if you do not want to show the output associated
+# with downloading the specified project.
+#
+# In addition to the above, any other options are passed through unmodified to
+# ExternalProject_Add() to perform the actual download, patch and update steps.
+# The following ExternalProject_Add() options are explicitly prohibited (they
+# are reserved for use by the download_project() command):
+#
+# CONFIGURE_COMMAND
+# BUILD_COMMAND
+# INSTALL_COMMAND
+# TEST_COMMAND
+#
+# Only those ExternalProject_Add() arguments which relate to downloading, patching
+# and updating of the project sources are intended to be used. Also note that at
+# least one set of download-related arguments are required.
+#
+# If using CMake 3.2 or later, the UPDATE_DISCONNECTED option can be used to
+# prevent a check at the remote end for changes every time CMake is run
+# after the first successful download. See the documentation of the ExternalProject
+# module for more information. It is likely you will want to use this option if it
+# is available to you. Note, however, that the ExternalProject implementation contains
+# bugs which result in incorrect handling of the UPDATE_DISCONNECTED option when
+# using the URL download method or when specifying a SOURCE_DIR with no download
+# method. Fixes for these have been created, the last of which is scheduled for
+# inclusion in CMake 3.8.0. Details can be found here:
+#
+# https://gitlab.kitware.com/cmake/cmake/commit/bdca68388bd57f8302d3c1d83d691034b7ffa70c
+# https://gitlab.kitware.com/cmake/cmake/issues/16428
+#
+# If you experience build errors related to the update step, consider avoiding
+# the use of UPDATE_DISCONNECTED.
+#
+# EXAMPLE USAGE:
+#
+# include(DownloadProject)
+# download_project(PROJ googletest
+# GIT_REPOSITORY https://github.com/google/googletest.git
+# GIT_TAG master
+# UPDATE_DISCONNECTED 1
+# QUIET
+# )
+#
+# add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR})
+#
+#========================================================================================
+
+
+set(_DownloadProjectDir "${CMAKE_CURRENT_LIST_DIR}")
+
+include(CMakeParseArguments)
+
+function(download_project)
+
+ set(options QUIET)
+ set(oneValueArgs
+ PROJ
+ PREFIX
+ DOWNLOAD_DIR
+ SOURCE_DIR
+ BINARY_DIR
+ # Prevent the following from being passed through
+ CONFIGURE_COMMAND
+ BUILD_COMMAND
+ INSTALL_COMMAND
+ TEST_COMMAND
+ )
+ set(multiValueArgs "")
+
+ cmake_parse_arguments(DL_ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+ # Hide output if requested
+ if (DL_ARGS_QUIET)
+ set(OUTPUT_QUIET "OUTPUT_QUIET")
+ else()
+ unset(OUTPUT_QUIET)
+ message(STATUS "Downloading/updating ${DL_ARGS_PROJ}")
+ endif()
+
+ # Set up where we will put our temporary CMakeLists.txt file and also
+ # the base point below which the default source and binary dirs will be.
+ # The prefix must always be an absolute path.
+ if (NOT DL_ARGS_PREFIX)
+ set(DL_ARGS_PREFIX "${CMAKE_BINARY_DIR}")
+ else()
+ get_filename_component(DL_ARGS_PREFIX "${DL_ARGS_PREFIX}" ABSOLUTE
+ BASE_DIR "${CMAKE_CURRENT_BINARY_DIR}")
+ endif()
+ if (NOT DL_ARGS_DOWNLOAD_DIR)
+ set(DL_ARGS_DOWNLOAD_DIR "${DL_ARGS_PREFIX}/${DL_ARGS_PROJ}-download")
+ endif()
+
+ # Ensure the caller can know where to find the source and build directories
+ if (NOT DL_ARGS_SOURCE_DIR)
+ set(DL_ARGS_SOURCE_DIR "${DL_ARGS_PREFIX}/${DL_ARGS_PROJ}-src")
+ endif()
+ if (NOT DL_ARGS_BINARY_DIR)
+ set(DL_ARGS_BINARY_DIR "${DL_ARGS_PREFIX}/${DL_ARGS_PROJ}-build")
+ endif()
+ set(${DL_ARGS_PROJ}_SOURCE_DIR "${DL_ARGS_SOURCE_DIR}" PARENT_SCOPE)
+ set(${DL_ARGS_PROJ}_BINARY_DIR "${DL_ARGS_BINARY_DIR}" PARENT_SCOPE)
+
+ # The way that CLion manages multiple configurations, it causes a copy of
+ # the CMakeCache.txt to be copied across due to it not expecting there to
+ # be a project within a project. This causes the hard-coded paths in the
+ # cache to be copied and builds to fail. To mitigate this, we simply
+ # remove the cache if it exists before we configure the new project. It
+ # is safe to do so because it will be re-generated. Since this is only
+ # executed at the configure step, it should not cause additional builds or
+ # downloads.
+ file(REMOVE "${DL_ARGS_DOWNLOAD_DIR}/CMakeCache.txt")
+
+ # Create and build a separate CMake project to carry out the download.
+ # If we've already previously done these steps, they will not cause
+ # anything to be updated, so extra rebuilds of the project won't occur.
+ # Make sure to pass through CMAKE_MAKE_PROGRAM in case the main project
+ # has this set to something not findable on the PATH.
+ configure_file("${_DownloadProjectDir}/DownloadProject.CMakeLists.cmake.in"
+ "${DL_ARGS_DOWNLOAD_DIR}/CMakeLists.txt")
+ execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}"
+ -D "CMAKE_MAKE_PROGRAM:FILE=${CMAKE_MAKE_PROGRAM}"
+ .
+ RESULT_VARIABLE result
+ ${OUTPUT_QUIET}
+ WORKING_DIRECTORY "${DL_ARGS_DOWNLOAD_DIR}"
+ )
+ if(result)
+ message(FATAL_ERROR "CMake step for ${DL_ARGS_PROJ} failed: ${result}")
+ endif()
+ execute_process(COMMAND ${CMAKE_COMMAND} --build .
+ RESULT_VARIABLE result
+ ${OUTPUT_QUIET}
+ WORKING_DIRECTORY "${DL_ARGS_DOWNLOAD_DIR}"
+ )
+ if(result)
+ message(FATAL_ERROR "Build step for ${DL_ARGS_PROJ} failed: ${result}")
+ endif()
+
+endfunction() \ No newline at end of file
diff --git a/xs/src/libnest2d/cmake_modules/FindClipper.cmake b/xs/src/libnest2d/cmake_modules/FindClipper.cmake
new file mode 100644
index 000000000..f6b973440
--- /dev/null
+++ b/xs/src/libnest2d/cmake_modules/FindClipper.cmake
@@ -0,0 +1,50 @@
+# Find Clipper library (http://www.angusj.com/delphi/clipper.php).
+# The following variables are set
+#
+# CLIPPER_FOUND
+# CLIPPER_INCLUDE_DIRS
+# CLIPPER_LIBRARIES
+#
+# It searches the environment variable $CLIPPER_PATH automatically.
+
+FIND_PATH(CLIPPER_INCLUDE_DIRS clipper.hpp
+ $ENV{CLIPPER_PATH}
+ $ENV{CLIPPER_PATH}/cpp/
+ $ENV{CLIPPER_PATH}/include/
+ $ENV{CLIPPER_PATH}/include/polyclipping/
+ ${PROJECT_SOURCE_DIR}/python/pymesh/third_party/include/
+ ${PROJECT_SOURCE_DIR}/python/pymesh/third_party/include/polyclipping/
+ ${CMAKE_PREFIX_PATH}/include/polyclipping
+ ${CMAKE_PREFIX_PATH}/include/
+ /opt/local/include/
+ /opt/local/include/polyclipping/
+ /usr/local/include/
+ /usr/local/include/polyclipping/
+ /usr/include
+ /usr/include/polyclipping/)
+
+FIND_LIBRARY(CLIPPER_LIBRARIES polyclipping
+ $ENV{CLIPPER_PATH}
+ $ENV{CLIPPER_PATH}/cpp/
+ $ENV{CLIPPER_PATH}/cpp/build/
+ $ENV{CLIPPER_PATH}/lib/
+ $ENV{CLIPPER_PATH}/lib/polyclipping/
+ ${PROJECT_SOURCE_DIR}/python/pymesh/third_party/lib/
+ ${PROJECT_SOURCE_DIR}/python/pymesh/third_party/lib/polyclipping/
+ ${CMAKE_PREFIX_PATH}/lib/
+ ${CMAKE_PREFIX_PATH}/lib/polyclipping/
+ /opt/local/lib/
+ /opt/local/lib/polyclipping/
+ /usr/local/lib/
+ /usr/local/lib/polyclipping/
+ /usr/lib/polyclipping)
+
+include(FindPackageHandleStandardArgs)
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(Clipper
+ "Clipper library cannot be found. Consider set CLIPPER_PATH environment variable"
+ CLIPPER_INCLUDE_DIRS
+ CLIPPER_LIBRARIES)
+
+MARK_AS_ADVANCED(
+ CLIPPER_INCLUDE_DIRS
+ CLIPPER_LIBRARIES) \ No newline at end of file
diff --git a/xs/src/libnest2d/cmake_modules/FindNLopt.cmake b/xs/src/libnest2d/cmake_modules/FindNLopt.cmake
new file mode 100644
index 000000000..4b93be7b6
--- /dev/null
+++ b/xs/src/libnest2d/cmake_modules/FindNLopt.cmake
@@ -0,0 +1,125 @@
+#///////////////////////////////////////////////////////////////////////////
+#//-------------------------------------------------------------------------
+#//
+#// Description:
+#// cmake module for finding NLopt installation
+#// NLopt installation location is defined by environment variable $NLOPT
+#//
+#// following variables are defined:
+#// NLopt_DIR - NLopt installation directory
+#// NLopt_INCLUDE_DIR - NLopt header directory
+#// NLopt_LIBRARY_DIR - NLopt library directory
+#// NLopt_LIBS - NLopt library files
+#//
+#// Example usage:
+#// find_package(NLopt 1.4 REQUIRED)
+#//
+#//
+#//-------------------------------------------------------------------------
+
+
+set(NLopt_FOUND FALSE)
+set(NLopt_ERROR_REASON "")
+set(NLopt_DEFINITIONS "")
+set(NLopt_LIBS)
+
+
+set(NLopt_DIR $ENV{NLOPT})
+if(NOT NLopt_DIR)
+
+ set(NLopt_FOUND TRUE)
+
+ set(_NLopt_LIB_NAMES "nlopt")
+ find_library(NLopt_LIBS
+ NAMES ${_NLopt_LIB_NAMES})
+ if(NOT NLopt_LIBS)
+ set(NLopt_FOUND FALSE)
+ set(NLopt_ERROR_REASON "${NLopt_ERROR_REASON} Cannot find NLopt library '${_NLopt_LIB_NAMES}'.")
+ else()
+ get_filename_component(NLopt_DIR ${NLopt_LIBS} PATH)
+ endif()
+ unset(_NLopt_LIB_NAMES)
+
+ set(_NLopt_HEADER_FILE_NAME "nlopt.hpp")
+ find_file(_NLopt_HEADER_FILE
+ NAMES ${_NLopt_HEADER_FILE_NAME})
+ if(NOT _NLopt_HEADER_FILE)
+ set(NLopt_FOUND FALSE)
+ set(NLopt_ERROR_REASON "${NLopt_ERROR_REASON} Cannot find NLopt header file '${_NLopt_HEADER_FILE_NAME}'.")
+ endif()
+ unset(_NLopt_HEADER_FILE_NAME)
+ unset(_NLopt_HEADER_FILE)
+
+ if(NOT NLopt_FOUND)
+ set(NLopt_ERROR_REASON "${NLopt_ERROR_REASON} NLopt not found in system directories (and environment variable NLOPT is not set).")
+ else()
+ get_filename_component(NLopt_INCLUDE_DIR ${_NLopt_HEADER_FILE} DIRECTORY )
+ endif()
+
+
+
+else()
+
+ set(NLopt_FOUND TRUE)
+
+ set(NLopt_INCLUDE_DIR "${NLopt_DIR}/include")
+ if(NOT EXISTS "${NLopt_INCLUDE_DIR}")
+ set(NLopt_FOUND FALSE)
+ set(NLopt_ERROR_REASON "${NLopt_ERROR_REASON} Directory '${NLopt_INCLUDE_DIR}' does not exist.")
+ endif()
+
+ set(NLopt_LIBRARY_DIR "${NLopt_DIR}/lib")
+ if(NOT EXISTS "${NLopt_LIBRARY_DIR}")
+ set(NLopt_FOUND FALSE)
+ set(NLopt_ERROR_REASON "${NLopt_ERROR_REASON} Directory '${NLopt_LIBRARY_DIR}' does not exist.")
+ endif()
+
+ set(_NLopt_LIB_NAMES "nlopt_cxx")
+ find_library(NLopt_LIBS
+ NAMES ${_NLopt_LIB_NAMES}
+ PATHS ${NLopt_LIBRARY_DIR}
+ NO_DEFAULT_PATH)
+ if(NOT NLopt_LIBS)
+ set(NLopt_FOUND FALSE)
+ set(NLopt_ERROR_REASON "${NLopt_ERROR_REASON} Cannot find NLopt library '${_NLopt_LIB_NAMES}' in '${NLopt_LIBRARY_DIR}'.")
+ endif()
+ unset(_NLopt_LIB_NAMES)
+
+ set(_NLopt_HEADER_FILE_NAME "nlopt.hpp")
+ find_file(_NLopt_HEADER_FILE
+ NAMES ${_NLopt_HEADER_FILE_NAME}
+ PATHS ${NLopt_INCLUDE_DIR}
+ NO_DEFAULT_PATH)
+ if(NOT _NLopt_HEADER_FILE)
+ set(NLopt_FOUND FALSE)
+ set(NLopt_ERROR_REASON "${NLopt_ERROR_REASON} Cannot find NLopt header file '${_NLopt_HEADER_FILE_NAME}' in '${NLopt_INCLUDE_DIR}'.")
+ endif()
+ unset(_NLopt_HEADER_FILE_NAME)
+ unset(_NLopt_HEADER_FILE)
+
+endif()
+
+
+# make variables changeable
+mark_as_advanced(
+ NLopt_INCLUDE_DIR
+ NLopt_LIBRARY_DIR
+ NLopt_LIBS
+ NLopt_DEFINITIONS
+ )
+
+
+# report result
+if(NLopt_FOUND)
+ message(STATUS "Found NLopt in '${NLopt_DIR}'.")
+ message(STATUS "Using NLopt include directory '${NLopt_INCLUDE_DIR}'.")
+ message(STATUS "Using NLopt library '${NLopt_LIBS}'.")
+else()
+ if(NLopt_FIND_REQUIRED)
+ message(FATAL_ERROR "Unable to find requested NLopt installation:${NLopt_ERROR_REASON}")
+ else()
+ if(NOT NLopt_FIND_QUIETLY)
+ message(STATUS "NLopt was not found:${NLopt_ERROR_REASON}")
+ endif()
+ endif()
+endif() \ No newline at end of file
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
new file mode 100644
index 000000000..ebc3fb15c
--- /dev/null
+++ b/xs/src/libnest2d/examples/main.cpp
@@ -0,0 +1,218 @@
+#include <iostream>
+#include <string>
+#include <fstream>
+//#define DEBUG_EXPORT_NFP
+
+#include <libnest2d.h>
+
+#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>>;
+
+std::vector<Item>& _parts(std::vector<Item>& ret, const TestData& data)
+{
+ if(ret.empty()) {
+ ret.reserve(data.size());
+ for(auto& inp : data)
+ ret.emplace_back(inp);
+ }
+
+ return ret;
+}
+
+std::vector<Item>& prusaParts() {
+ static std::vector<Item> ret;
+ return _parts(ret, PRINTER_PART_POLYGONS);
+}
+
+std::vector<Item>& stegoParts() {
+ static std::vector<Item> ret;
+ return _parts(ret, STEGOSAUR_POLYGONS);
+}
+
+std::vector<Item>& prusaExParts() {
+ static std::vector<Item> ret;
+ if(ret.empty()) {
+ ret.reserve(PRINTER_PART_POLYGONS_EX.size());
+ for(auto& p : PRINTER_PART_POLYGONS_EX) {
+ ret.emplace_back(p.Contour, p.Holes);
+ }
+ }
+ return ret;
+}
+
+void arrangeRectangles() {
+ using namespace libnest2d;
+
+ const int SCALE = 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> 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());
+
+ Box bin(250*SCALE, 210*SCALE);
+// PolygonImpl bin = {
+// {
+// {25*SCALE, 0},
+// {0, 25*SCALE},
+// {0, 225*SCALE},
+// {25*SCALE, 250*SCALE},
+// {225*SCALE, 250*SCALE},
+// {250*SCALE, 225*SCALE},
+// {250*SCALE, 25*SCALE},
+// {225*SCALE, 0},
+// {25*SCALE, 0}
+// },
+// {}
+// };
+
+// Circle bin({0, 0}, 125*SCALE);
+
+ 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);
+
+ Packer::PlacementConfig pconf;
+ 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.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.01;
+
+ arrange.configure(pconf, sconf);
+
+ arrange.progressIndicator([&](unsigned r){
+ std::cout << "Remaining items: " << r << std::endl;
+ });
+
+// findMinimumBoundingBoxRotations(input.begin(), input.end());
+
+ Benchmark bench;
+
+ bench.start();
+ Packer::ResultType result;
+
+ try {
+ result = arrange.execute(input.begin(), input.end());
+ } catch(GeometryException& ge) {
+ std::cerr << "Geometry error: " << ge.what() << std::endl;
+ return ;
+ } catch(std::exception& e) {
+ std::cerr << "Exception: " << e.what() << std::endl;
+ return ;
+ }
+
+ bench.stop();
+
+ std::vector<double> eff;
+ eff.reserve(result.size());
+
+ 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(); });
+ eff.emplace_back(a/bin_area);
+ };
+
+ std::cout << bench.getElapsedSec() << " bin count: " << result.size()
+ << std::endl;
+
+ std::cout << "Bin efficiency: (";
+ for(double e : eff) std::cout << e*100.0 << "% ";
+ std::cout << ") Average: "
+ << std::accumulate(eff.begin(), eff.end(), 0.0)*100.0/result.size()
+ << " %" << std::endl;
+
+ std::cout << "Bin usage: (";
+ size_t total = 0;
+ for(auto& r : result) { std::cout << r.size() << " "; total += r.size(); }
+ std::cout << ") Total: " << total << 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!"
+ << std::endl;
+
+ using SVGWriter = svg::SVGWriter<PolygonImpl>;
+
+ SVGWriter::Config conf;
+ conf.mm_in_coord_units = SCALE;
+ SVGWriter svgw(conf);
+ svgw.setSize(Box(250*SCALE, 210*SCALE));
+ svgw.writePackGroup(result);
+ svgw.save("out");
+}
+
+int main(void /*int argc, char **argv*/) {
+ arrangeRectangles();
+ return EXIT_SUCCESS;
+}
diff --git a/xs/src/libnest2d/libnest2d.h b/xs/src/libnest2d/libnest2d.h
new file mode 100644
index 000000000..bfd88f4f5
--- /dev/null
+++ b/xs/src/libnest2d/libnest2d.h
@@ -0,0 +1,42 @@
+#ifndef LIBNEST2D_H
+#define LIBNEST2D_H
+
+// The type of backend should be set conditionally by the cmake configuriation
+// for now we set it statically to clipper backend
+#include <libnest2d/clipper_backend/clipper_backend.hpp>
+
+// We include the stock optimizers for local and global optimization
+#include <libnest2d/optimizers/subplex.hpp> // Local subplex for NfpPlacer
+#include <libnest2d/optimizers/genetic.hpp> // Genetic for min. bounding box
+
+#include <libnest2d/libnest2d.hpp>
+#include <libnest2d/placers/bottomleftplacer.hpp>
+#include <libnest2d/placers/nfpplacer.hpp>
+#include <libnest2d/selections/firstfit.hpp>
+#include <libnest2d/selections/filler.hpp>
+#include <libnest2d/selections/djd_heuristic.hpp>
+
+namespace libnest2d {
+
+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>;
+
+using PackGroup = _PackGroup<PolygonImpl>;
+using IndexedPackGroup = _IndexedPackGroup<PolygonImpl>;
+
+using FillerSelection = selections::_FillerSelection<PolygonImpl>;
+using FirstFitSelection = selections::_FirstFitSelection<PolygonImpl>;
+using DJDHeuristic = selections::_DJDHeuristic<PolygonImpl>;
+
+using NfpPlacer = placers::_NofitPolyPlacer<PolygonImpl>;
+using BottomLeftPlacer = placers::_BottomLeftPlacer<PolygonImpl>;
+
+}
+
+#endif // LIBNEST2D_H
diff --git a/xs/src/libnest2d/libnest2d/boost_alg.hpp b/xs/src/libnest2d/libnest2d/boost_alg.hpp
new file mode 100644
index 000000000..bb0403b06
--- /dev/null
+++ b/xs/src/libnest2d/libnest2d/boost_alg.hpp
@@ -0,0 +1,518 @@
+#ifndef BOOST_ALG_HPP
+#define BOOST_ALG_HPP
+
+#ifndef DISABLE_BOOST_SERIALIZE
+ #include <sstream>
+#endif
+
+#ifdef __clang__
+#undef _MSC_EXTENSIONS
+#endif
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable: 4244)
+#pragma warning(disable: 4267)
+#endif
+#include <boost/geometry.hpp>
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+// this should be removed to not confuse the compiler
+// #include <libnest2d.h>
+
+namespace bp2d {
+
+using libnest2d::TCoord;
+using libnest2d::PointImpl;
+using Coord = TCoord<PointImpl>;
+using libnest2d::PolygonImpl;
+using libnest2d::PathImpl;
+using libnest2d::Orientation;
+using libnest2d::OrientationType;
+using libnest2d::getX;
+using libnest2d::getY;
+using libnest2d::setX;
+using libnest2d::setY;
+using Box = libnest2d::_Box<PointImpl>;
+using Segment = libnest2d::_Segment<PointImpl>;
+using Shapes = libnest2d::nfp::Shapes<PolygonImpl>;
+
+}
+
+/**
+ * We have to make all the libnest2d geometry types available to boost. The real
+ * models of the geometries remain the same if a conforming model for libnest2d
+ * was defined by the library client. Boost is used only as an optional
+ * implementer of some algorithms that can be implemented by the model itself
+ * if a faster alternative exists.
+ *
+ * However, boost has its own type traits and we have to define the needed
+ * specializations to be able to use boost::geometry. This can be done with the
+ * already provided model.
+ */
+namespace boost {
+namespace geometry {
+namespace traits {
+
+/* ************************************************************************** */
+/* Point concept adaptaion ************************************************** */
+/* ************************************************************************** */
+
+template<> struct tag<bp2d::PointImpl> {
+ using type = point_tag;
+};
+
+template<> struct coordinate_type<bp2d::PointImpl> {
+ using type = bp2d::Coord;
+};
+
+template<> struct coordinate_system<bp2d::PointImpl> {
+ using type = cs::cartesian;
+};
+
+template<> struct dimension<bp2d::PointImpl>: boost::mpl::int_<2> {};
+
+template<>
+struct access<bp2d::PointImpl, 0 > {
+ static inline bp2d::Coord get(bp2d::PointImpl const& a) {
+ return libnest2d::getX(a);
+ }
+
+ static inline void set(bp2d::PointImpl& a,
+ bp2d::Coord const& value) {
+ libnest2d::setX(a, value);
+ }
+};
+
+template<>
+struct access<bp2d::PointImpl, 1 > {
+ static inline bp2d::Coord get(bp2d::PointImpl const& a) {
+ return libnest2d::getY(a);
+ }
+
+ static inline void set(bp2d::PointImpl& a,
+ bp2d::Coord const& value) {
+ libnest2d::setY(a, value);
+ }
+};
+
+
+/* ************************************************************************** */
+/* Box concept adaptaion **************************************************** */
+/* ************************************************************************** */
+
+template<> struct tag<bp2d::Box> {
+ using type = box_tag;
+};
+
+template<> struct point_type<bp2d::Box> {
+ using type = bp2d::PointImpl;
+};
+
+template<> struct indexed_access<bp2d::Box, min_corner, 0> {
+ static inline bp2d::Coord get(bp2d::Box const& box) {
+ return bp2d::getX(box.minCorner());
+ }
+ static inline void set(bp2d::Box &box, bp2d::Coord const& coord) {
+ bp2d::setX(box.minCorner(), coord);
+ }
+};
+
+template<> struct indexed_access<bp2d::Box, min_corner, 1> {
+ static inline bp2d::Coord get(bp2d::Box const& box) {
+ return bp2d::getY(box.minCorner());
+ }
+ static inline void set(bp2d::Box &box, bp2d::Coord const& coord) {
+ bp2d::setY(box.minCorner(), coord);
+ }
+};
+
+template<> struct indexed_access<bp2d::Box, max_corner, 0> {
+ static inline bp2d::Coord get(bp2d::Box const& box) {
+ return bp2d::getX(box.maxCorner());
+ }
+ static inline void set(bp2d::Box &box, bp2d::Coord const& coord) {
+ bp2d::setX(box.maxCorner(), coord);
+ }
+};
+
+template<> struct indexed_access<bp2d::Box, max_corner, 1> {
+ static inline bp2d::Coord get(bp2d::Box const& box) {
+ return bp2d::getY(box.maxCorner());
+ }
+ static inline void set(bp2d::Box &box, bp2d::Coord const& coord) {
+ bp2d::setY(box.maxCorner(), coord);
+ }
+};
+
+/* ************************************************************************** */
+/* Segment concept adaptaion ************************************************ */
+/* ************************************************************************** */
+
+template<> struct tag<bp2d::Segment> {
+ using type = segment_tag;
+};
+
+template<> struct point_type<bp2d::Segment> {
+ using type = bp2d::PointImpl;
+};
+
+template<> struct indexed_access<bp2d::Segment, 0, 0> {
+ static inline bp2d::Coord get(bp2d::Segment const& seg) {
+ return bp2d::getX(seg.first());
+ }
+ static inline void set(bp2d::Segment &seg, bp2d::Coord const& coord) {
+ auto p = seg.first(); bp2d::setX(p, coord); seg.first(p);
+ }
+};
+
+template<> struct indexed_access<bp2d::Segment, 0, 1> {
+ static inline bp2d::Coord get(bp2d::Segment const& seg) {
+ return bp2d::getY(seg.first());
+ }
+ static inline void set(bp2d::Segment &seg, bp2d::Coord const& coord) {
+ auto p = seg.first(); bp2d::setY(p, coord); seg.first(p);
+ }
+};
+
+template<> struct indexed_access<bp2d::Segment, 1, 0> {
+ static inline bp2d::Coord get(bp2d::Segment const& seg) {
+ return bp2d::getX(seg.second());
+ }
+ static inline void set(bp2d::Segment &seg, bp2d::Coord const& coord) {
+ auto p = seg.second(); bp2d::setX(p, coord); seg.second(p);
+ }
+};
+
+template<> struct indexed_access<bp2d::Segment, 1, 1> {
+ static inline bp2d::Coord get(bp2d::Segment const& seg) {
+ return bp2d::getY(seg.second());
+ }
+ static inline void set(bp2d::Segment &seg, bp2d::Coord const& coord) {
+ auto p = seg.second(); bp2d::setY(p, coord); seg.second(p);
+ }
+};
+
+
+/* ************************************************************************** */
+/* Polygon concept adaptation *********************************************** */
+/* ************************************************************************** */
+
+// Connversion between libnest2d::Orientation and order_selector ///////////////
+
+template<bp2d::Orientation> struct ToBoostOrienation {};
+
+template<>
+struct ToBoostOrienation<bp2d::Orientation::CLOCKWISE> {
+ static const order_selector Value = clockwise;
+};
+
+template<>
+struct ToBoostOrienation<bp2d::Orientation::COUNTER_CLOCKWISE> {
+ static const order_selector Value = counterclockwise;
+};
+
+static const bp2d::Orientation RealOrientation =
+ bp2d::OrientationType<bp2d::PolygonImpl>::Value;
+
+// Ring implementation /////////////////////////////////////////////////////////
+
+// Boost would refer to ClipperLib::Path (alias bp2d::PolygonImpl) as a ring
+template<> struct tag<bp2d::PathImpl> {
+ using type = ring_tag;
+};
+
+template<> struct point_order<bp2d::PathImpl> {
+ static const order_selector value =
+ ToBoostOrienation<RealOrientation>::Value;
+};
+
+// All our Paths should be closed for the bin packing application
+template<> struct closure<bp2d::PathImpl> {
+ static const closure_selector value = closed;
+};
+
+// Polygon implementation //////////////////////////////////////////////////////
+
+template<> struct tag<bp2d::PolygonImpl> {
+ using type = polygon_tag;
+};
+
+template<> struct exterior_ring<bp2d::PolygonImpl> {
+ static inline bp2d::PathImpl& get(bp2d::PolygonImpl& p) {
+ return libnest2d::shapelike::getContour(p);
+ }
+
+ static inline bp2d::PathImpl const& get(bp2d::PolygonImpl const& p) {
+ return libnest2d::shapelike::getContour(p);
+ }
+};
+
+template<> struct ring_const_type<bp2d::PolygonImpl> {
+ using type = const bp2d::PathImpl&;
+};
+
+template<> struct ring_mutable_type<bp2d::PolygonImpl> {
+ using type = bp2d::PathImpl&;
+};
+
+template<> struct interior_const_type<bp2d::PolygonImpl> {
+ using type = const libnest2d::THolesContainer<bp2d::PolygonImpl>&;
+};
+
+template<> struct interior_mutable_type<bp2d::PolygonImpl> {
+ using type = libnest2d::THolesContainer<bp2d::PolygonImpl>&;
+};
+
+template<>
+struct interior_rings<bp2d::PolygonImpl> {
+
+ static inline libnest2d::THolesContainer<bp2d::PolygonImpl>& get(
+ bp2d::PolygonImpl& p)
+ {
+ return libnest2d::shapelike::holes(p);
+ }
+
+ static inline const libnest2d::THolesContainer<bp2d::PolygonImpl>& get(
+ bp2d::PolygonImpl const& p)
+ {
+ return libnest2d::shapelike::holes(p);
+ }
+};
+
+/* ************************************************************************** */
+/* MultiPolygon concept adaptation ****************************************** */
+/* ************************************************************************** */
+
+template<> struct tag<bp2d::Shapes> {
+ using type = multi_polygon_tag;
+};
+
+} // traits
+} // geometry
+
+// This is an addition to the ring implementation of Polygon concept
+template<>
+struct range_value<bp2d::PathImpl> {
+ using type = bp2d::PointImpl;
+};
+
+template<>
+struct range_value<bp2d::Shapes> {
+ using type = bp2d::PolygonImpl;
+};
+
+} // boost
+
+/* ************************************************************************** */
+/* Algorithms *************************************************************** */
+/* ************************************************************************** */
+
+namespace libnest2d { // Now the algorithms that boost can provide...
+
+namespace pointlike {
+template<>
+inline double distance(const PointImpl& p1, const PointImpl& p2 )
+{
+ return boost::geometry::distance(p1, p2);
+}
+
+template<>
+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 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 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 intersects(const bp2d::Segment& s1, const bp2d::Segment& s2)
+{
+ return boost::geometry::intersects(s1, s2);
+}
+
+#ifndef DISABLE_BOOST_AREA
+template<>
+inline double area(const PolygonImpl& shape, const PolygonTag&)
+{
+ return boost::geometry::area(shape);
+}
+#endif
+
+template<>
+inline bool isInside(const PointImpl& point, const PolygonImpl& shape)
+{
+ return boost::geometry::within(point, shape);
+}
+
+template<>
+inline bool isInside(const PolygonImpl& sh1, const PolygonImpl& sh2)
+{
+ return boost::geometry::within(sh1, sh2);
+}
+
+template<>
+inline bool touches(const PolygonImpl& sh1, const PolygonImpl& sh2)
+{
+ return boost::geometry::touches(sh1, sh2);
+}
+
+template<>
+inline bool touches( const PointImpl& point, const PolygonImpl& shape)
+{
+ return boost::geometry::touches(point, shape);
+}
+
+#ifndef DISABLE_BOOST_BOUNDING_BOX
+template<>
+inline bp2d::Box boundingBox(const PolygonImpl& sh, const PolygonTag&)
+{
+ bp2d::Box b;
+ boost::geometry::envelope(sh, b);
+ return b;
+}
+
+template<>
+inline bp2d::Box boundingBox<bp2d::Shapes>(const bp2d::Shapes& shapes,
+ const MultiPolygonTag&)
+{
+ bp2d::Box b;
+ boost::geometry::envelope(shapes, b);
+ return b;
+}
+#endif
+
+#ifndef DISABLE_BOOST_CONVEX_HULL
+template<>
+inline PolygonImpl convexHull(const PolygonImpl& sh, const PolygonTag&)
+{
+ PolygonImpl ret;
+ boost::geometry::convex_hull(sh, ret);
+ return ret;
+}
+
+template<>
+inline PolygonImpl convexHull(const TMultiShape<PolygonImpl>& shapes,
+ const MultiPolygonTag&)
+{
+ PolygonImpl ret;
+ boost::geometry::convex_hull(shapes, ret);
+ return ret;
+}
+#endif
+
+#ifndef DISABLE_BOOST_OFFSET
+template<>
+inline void offset(PolygonImpl& sh, bp2d::Coord distance)
+{
+ PolygonImpl cpy = sh;
+ boost::geometry::buffer(cpy, sh, distance);
+}
+#endif
+
+#ifndef DISABLE_BOOST_SERIALIZE
+template<> inline std::string serialize<libnest2d::Formats::SVG>(
+ const PolygonImpl& sh, double scale)
+{
+ std::stringstream ss;
+ std::string style = "fill: none; stroke: black; stroke-width: 1px;";
+
+ using namespace boost::geometry;
+ using Pointf = model::point<double, 2, cs::cartesian>;
+ using Polygonf = model::polygon<Pointf>;
+
+ Polygonf::ring_type ring;
+ Polygonf::inner_container_type holes;
+ ring.reserve(shapelike::contourVertexCount(sh));
+
+ 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);
+ for(PathImpl& h : H ) {
+ Polygonf::ring_type hf;
+ for(auto it = h.begin(); it != h.end(); it++) {
+ auto& v = *it;
+ hf.emplace_back(getX(v)*scale, getY(v)*scale);
+ };
+ holes.push_back(hf);
+ }
+
+ Polygonf poly;
+ poly.outer() = ring;
+ poly.inners() = holes;
+ auto svg_data = boost::geometry::svg(poly, style);
+
+ ss << svg_data << std::endl;
+
+ return ss.str();
+}
+#endif
+
+#ifndef DISABLE_BOOST_UNSERIALIZE
+template<>
+inline void unserialize<libnest2d::Formats::SVG>(
+ PolygonImpl& sh,
+ const std::string& str)
+{
+}
+#endif
+
+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
+
+}
+
+
+}
+
+
+
+#endif // BOOST_ALG_HPP
diff --git a/xs/src/libnest2d/libnest2d/clipper_backend/CMakeLists.txt b/xs/src/libnest2d/libnest2d/clipper_backend/CMakeLists.txt
new file mode 100644
index 000000000..b6f2de439
--- /dev/null
+++ b/xs/src/libnest2d/libnest2d/clipper_backend/CMakeLists.txt
@@ -0,0 +1,48 @@
+if(NOT TARGET clipper) # If there is a clipper target in the parent project we are good to go.
+
+ find_package(Clipper 6.1)
+
+ if(NOT CLIPPER_FOUND)
+ find_package(Subversion QUIET)
+ if(Subversion_FOUND)
+ message(STATUS "Clipper not found so it will be downloaded.")
+ # Silently download and build the library in the build dir
+
+ if (CMAKE_VERSION VERSION_LESS 3.2)
+ set(UPDATE_DISCONNECTED_IF_AVAILABLE "")
+ else()
+ set(UPDATE_DISCONNECTED_IF_AVAILABLE "UPDATE_DISCONNECTED 1")
+ endif()
+
+ include(DownloadProject)
+ download_project( PROJ clipper_library
+ SVN_REPOSITORY https://svn.code.sf.net/p/polyclipping/code/trunk/cpp
+ SVN_REVISION -r540
+ #SOURCE_SUBDIR cpp
+ INSTALL_COMMAND ""
+ CONFIGURE_COMMAND "" # Not working, I will just add the source files
+ ${UPDATE_DISCONNECTED_IF_AVAILABLE}
+ )
+
+ # This is not working and I dont have time to fix it
+ # add_subdirectory(${clipper_library_SOURCE_DIR}/cpp
+ # ${clipper_library_BINARY_DIR}
+ # )
+
+ add_library(clipper_lib STATIC
+ ${clipper_library_SOURCE_DIR}/clipper.cpp
+ ${clipper_library_SOURCE_DIR}/clipper.hpp)
+
+ set(CLIPPER_INCLUDE_DIRS ${clipper_library_SOURCE_DIR}
+ PARENT_SCOPE)
+
+ set(CLIPPER_LIBRARIES clipper_lib PARENT_SCOPE)
+
+ else()
+ message(FATAL_ERROR "Can't find clipper library and no SVN client found to download.
+ You can download the clipper sources and define a clipper target in your project, that will work for libnest2d.")
+ endif()
+ endif()
+else()
+ set(CLIPPER_LIBRARIES clipper PARENT_SCOPE)
+endif()
diff --git a/xs/src/libnest2d/libnest2d/clipper_backend/clipper_backend.hpp b/xs/src/libnest2d/libnest2d/clipper_backend/clipper_backend.hpp
new file mode 100644
index 000000000..745fd2108
--- /dev/null
+++ b/xs/src/libnest2d/libnest2d/clipper_backend/clipper_backend.hpp
@@ -0,0 +1,460 @@
+#ifndef CLIPPER_BACKEND_HPP
+#define CLIPPER_BACKEND_HPP
+
+#include <sstream>
+#include <unordered_map>
+#include <cassert>
+#include <vector>
+#include <iostream>
+
+#include "../geometry_traits.hpp"
+#include "../geometry_traits_nfp.hpp"
+
+#include <clipper.hpp>
+
+namespace ClipperLib {
+using PointImpl = IntPoint;
+using PathImpl = Path;
+using HoleStore = std::vector<PathImpl>;
+
+struct PolygonImpl {
+ PathImpl Contour;
+ HoleStore Holes;
+
+ inline PolygonImpl() = default;
+
+ inline explicit PolygonImpl(const PathImpl& cont): Contour(cont) {}
+ inline explicit PolygonImpl(const HoleStore& holes):
+ Holes(holes) {}
+ inline PolygonImpl(const Path& cont, const HoleStore& holes):
+ Contour(cont), Holes(holes) {}
+
+ inline explicit PolygonImpl(PathImpl&& cont): Contour(std::move(cont)) {}
+ inline explicit PolygonImpl(HoleStore&& holes): Holes(std::move(holes)) {}
+ inline PolygonImpl(Path&& cont, HoleStore&& holes):
+ Contour(std::move(cont)), Holes(std::move(holes)) {}
+};
+
+inline PointImpl& operator +=(PointImpl& p, const PointImpl& pa ) {
+ // This could be done with SIMD
+ p.X += pa.X;
+ p.Y += pa.Y;
+ return p;
+}
+
+inline PointImpl operator+(const PointImpl& p1, const PointImpl& p2) {
+ PointImpl ret = p1;
+ ret += p2;
+ return ret;
+}
+
+inline PointImpl& operator -=(PointImpl& p, const PointImpl& pa ) {
+ p.X -= pa.X;
+ p.Y -= pa.Y;
+ return p;
+}
+
+inline PointImpl operator -(PointImpl& p ) {
+ PointImpl ret = p;
+ ret.X = -ret.X;
+ ret.Y = -ret.Y;
+ return ret;
+}
+
+inline PointImpl operator-(const PointImpl& p1, const PointImpl& p2) {
+ PointImpl ret = p1;
+ ret -= p2;
+ return ret;
+}
+
+inline PointImpl& operator *=(PointImpl& p, const PointImpl& pa ) {
+ p.X *= pa.X;
+ p.Y *= pa.Y;
+ return p;
+}
+
+inline PointImpl operator*(const PointImpl& p1, const PointImpl& p2) {
+ PointImpl ret = p1;
+ ret *= p2;
+ return ret;
+}
+
+}
+
+namespace libnest2d {
+
+// Aliases for convinience
+using ClipperLib::PointImpl;
+using ClipperLib::PathImpl;
+using ClipperLib::PolygonImpl;
+using ClipperLib::HoleStore;
+
+// Type of coordinate units used by Clipper
+template<> struct CoordType<PointImpl> {
+ using Type = ClipperLib::cInt;
+};
+
+// Type of point used by Clipper
+template<> struct PointType<PolygonImpl> {
+ using Type = PointImpl;
+};
+
+template<> struct PointType<PointImpl> {
+ using Type = PointImpl;
+};
+
+template<> struct CountourType<PolygonImpl> {
+ using Type = PathImpl;
+};
+
+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 libnest2d how to extract the Y coord from a ClipperPoint object
+template<> inline TCoord<PointImpl> y(const PointImpl& p)
+{
+ return p.Y;
+}
+
+// Tell libnest2d how to extract the X coord from a ClipperPoint object
+template<> inline TCoord<PointImpl>& x(PointImpl& p)
+{
+ return p.X;
+}
+
+// Tell libnest2d how to extract the Y coord from a ClipperPoint object
+template<> inline TCoord<PointImpl>& y(PointImpl& p)
+{
+ return p.Y;
+}
+
+}
+
+#define DISABLE_BOOST_AREA
+
+namespace _smartarea {
+template<Orientation o>
+inline double area(const PolygonImpl& /*sh*/) {
+ return std::nan("");
+}
+
+template<>
+inline double area<Orientation::CLOCKWISE>(const PolygonImpl& sh) {
+ double a = 0;
+
+ std::for_each(sh.Holes.begin(), sh.Holes.end(), [&a](const PathImpl& h)
+ {
+ a -= ClipperLib::Area(h);
+ });
+
+ return -ClipperLib::Area(sh.Contour) + a;
+}
+
+template<>
+inline double area<Orientation::COUNTER_CLOCKWISE>(const PolygonImpl& sh) {
+ double a = 0;
+
+ std::for_each(sh.Holes.begin(), sh.Holes.end(), [&a](const PathImpl& h)
+ {
+ a += ClipperLib::Area(h);
+ });
+
+ return ClipperLib::Area(sh.Contour) + a;
+}
+
+}
+
+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 offset(PolygonImpl& sh, TCoord<PointImpl> distance)
+{
+ #define DISABLE_BOOST_OFFSET
+
+ using ClipperLib::ClipperOffset;
+ using ClipperLib::jtMiter;
+ using ClipperLib::etClosedPolygon;
+ using ClipperLib::Paths;
+
+ // If the input is not at least a triangle, we can not do this algorithm
+ if(sh.Contour.size() <= 3 ||
+ std::any_of(sh.Holes.begin(), sh.Holes.end(),
+ [](const PathImpl& p) { return p.size() <= 3; })
+ ) throw GeometryException(GeomErr::OFFSET);
+
+ ClipperOffset offs;
+ Paths result;
+ offs.AddPath(sh.Contour, jtMiter, etClosedPolygon);
+ offs.AddPaths(sh.Holes, jtMiter, etClosedPolygon);
+ offs.Execute(result, static_cast<double>(distance));
+
+ // Offsetting reverts the orientation and also removes the last vertex
+ // so boost will not have a closed polygon.
+
+ bool found_the_contour = false;
+ for(auto& r : result) {
+ if(ClipperLib::Orientation(r)) {
+ // We don't like if the offsetting generates more than one contour
+ // but throwing would be an overkill. Instead, we should warn the
+ // caller about the inability to create correct geometries
+ if(!found_the_contour) {
+ sh.Contour = r;
+ ClipperLib::ReversePath(sh.Contour);
+ sh.Contour.push_back(sh.Contour.front());
+ found_the_contour = true;
+ } else {
+ dout() << "Warning: offsetting result is invalid!";
+ /* TODO warning */
+ }
+ } else {
+ // TODO If there are multiple contours we can't be sure which hole
+ // belongs to the first contour. (But in this case the situation is
+ // bad enough to let it go...)
+ sh.Holes.push_back(r);
+ ClipperLib::ReversePath(sh.Holes.back());
+ sh.Holes.back().push_back(sh.Holes.back().front());
+ }
+ }
+}
+
+// Tell libnest2d how to make string out of a ClipperPolygon object
+template<> inline std::string toString(const PolygonImpl& sh)
+{
+ std::stringstream ss;
+
+ ss << "Contour {\n";
+ for(auto p : sh.Contour) {
+ ss << "\t" << p.X << " " << p.Y << "\n";
+ }
+ ss << "}\n";
+
+ for(auto& h : sh.Holes) {
+ ss << "Holes {\n";
+ for(auto p : h) {
+ ss << "\t{\n";
+ ss << "\t\t" << p.X << " " << p.Y << "\n";
+ ss << "\t}\n";
+ }
+ ss << "}\n";
+ }
+
+ return ss.str();
+}
+
+template<>
+inline PolygonImpl create(const PathImpl& path, const HoleStore& holes)
+{
+ PolygonImpl p;
+ p.Contour = path;
+
+ // Expecting that the coordinate system Y axis is positive in upwards
+ // direction
+ if(ClipperLib::Orientation(p.Contour)) {
+ // Not clockwise then reverse the b*tch
+ ClipperLib::ReversePath(p.Contour);
+ }
+
+ p.Holes = holes;
+ for(auto& h : p.Holes) {
+ if(!ClipperLib::Orientation(h)) {
+ ClipperLib::ReversePath(h);
+ }
+ }
+
+ return p;
+}
+
+template<> inline PolygonImpl create( PathImpl&& path, HoleStore&& holes) {
+ PolygonImpl p;
+ p.Contour.swap(path);
+
+ // Expecting that the coordinate system Y axis is positive in upwards
+ // direction
+ if(ClipperLib::Orientation(p.Contour)) {
+ // Not clockwise then reverse the b*tch
+ ClipperLib::ReversePath(p.Contour);
+ }
+
+ p.Holes.swap(holes);
+
+ for(auto& h : p.Holes) {
+ if(!ClipperLib::Orientation(h)) {
+ ClipperLib::ReversePath(h);
+ }
+ }
+
+ return p;
+}
+
+template<>
+inline const THolesContainer<PolygonImpl>& holes(const PolygonImpl& sh)
+{
+ return sh.Holes;
+}
+
+template<> inline THolesContainer<PolygonImpl>& holes(PolygonImpl& sh)
+{
+ return sh.Holes;
+}
+
+template<>
+inline TContour<PolygonImpl>& getHole(PolygonImpl& sh, unsigned long idx)
+{
+ return sh.Holes[idx];
+}
+
+template<>
+inline const TContour<PolygonImpl>& getHole(const PolygonImpl& sh,
+ unsigned long idx)
+{
+ return sh.Holes[idx];
+}
+
+template<> inline size_t holeCount(const PolygonImpl& sh)
+{
+ return sh.Holes.size();
+}
+
+template<> inline PathImpl& getContour(PolygonImpl& sh)
+{
+ return sh.Contour;
+}
+
+template<>
+inline const PathImpl& getContour(const PolygonImpl& sh)
+{
+ return sh.Contour;
+}
+
+#define DISABLE_BOOST_TRANSLATE
+template<>
+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; }
+}
+
+#define DISABLE_BOOST_ROTATE
+template<>
+inline void rotate(PolygonImpl& sh, const Radians& rads)
+{
+ using Coord = TCoord<PointImpl>;
+
+ auto cosa = rads.cos();
+ auto sina = rads.sin();
+
+ for(auto& p : sh.Contour) {
+ p = {
+ static_cast<Coord>(p.X * cosa - p.Y * sina),
+ static_cast<Coord>(p.X * sina + p.Y * cosa)
+ };
+ }
+ for(auto& hole : sh.Holes) for(auto& p : hole) {
+ p = {
+ static_cast<Coord>(p.X * cosa - p.Y * sina),
+ static_cast<Coord>(p.X * sina + p.Y * cosa)
+ };
+ }
+}
+
+} // namespace shapelike
+
+#define DISABLE_BOOST_NFP_MERGE
+inline std::vector<PolygonImpl> _merge(ClipperLib::Clipper& clipper) {
+ shapelike::Shapes<PolygonImpl> retv;
+
+ ClipperLib::PolyTree result;
+ clipper.Execute(ClipperLib::ctUnion, result, ClipperLib::pftNegative);
+ retv.reserve(static_cast<size_t>(result.Total()));
+
+ std::function<void(ClipperLib::PolyNode*, PolygonImpl&)> processHole;
+
+ auto processPoly = [&retv, &processHole](ClipperLib::PolyNode *pptr) {
+ PolygonImpl poly(pptr->Contour);
+ poly.Contour.push_back(poly.Contour.front());
+ for(auto h : pptr->Childs) { processHole(h, poly); }
+ retv.push_back(poly);
+ };
+
+ processHole = [&processPoly](ClipperLib::PolyNode *pptr, PolygonImpl& poly)
+ {
+ poly.Holes.push_back(pptr->Contour);
+ poly.Holes.back().push_back(poly.Holes.back().front());
+ for(auto c : pptr->Childs) processPoly(c);
+ };
+
+ auto traverse = [&processPoly] (ClipperLib::PolyNode *node)
+ {
+ for(auto ch : node->Childs) {
+ processPoly(ch);
+ }
+ };
+
+ traverse(&result);
+
+ return retv;
+}
+
+namespace nfp {
+
+template<> inline std::vector<PolygonImpl>
+merge(const std::vector<PolygonImpl>& shapes)
+{
+ ClipperLib::Clipper clipper(ClipperLib::ioReverseSolution);
+
+ bool closed = true;
+ bool valid = true;
+
+ for(auto& path : shapes) {
+ valid &= clipper.AddPath(path.Contour, ClipperLib::ptSubject, closed);
+
+ for(auto& hole : path.Holes) {
+ valid &= clipper.AddPath(hole, ClipperLib::ptSubject, closed);
+ }
+ }
+
+ if(!valid) throw GeometryException(GeomErr::MERGE);
+
+ return _merge(clipper);
+}
+
+}
+
+}
+
+//#define DISABLE_BOOST_SERIALIZE
+//#define DISABLE_BOOST_UNSERIALIZE
+
+// All other operators and algorithms are implemented with boost
+#include "../boost_alg.hpp"
+
+#endif // CLIPPER_BACKEND_HPP
diff --git a/xs/src/libnest2d/libnest2d/common.hpp b/xs/src/libnest2d/libnest2d/common.hpp
new file mode 100644
index 000000000..6867f76f3
--- /dev/null
+++ b/xs/src/libnest2d/libnest2d/common.hpp
@@ -0,0 +1,202 @@
+#ifndef LIBNEST2D_CONFIG_HPP
+#define LIBNEST2D_CONFIG_HPP
+
+#ifndef NDEBUG
+#include <iostream>
+#endif
+
+#include <stdexcept>
+#include <string>
+#include <cmath>
+#include <type_traits>
+
+#if defined(_MSC_VER) && _MSC_VER <= 1800 || __cplusplus < 201103L
+ #define BP2D_NOEXCEPT
+ #define BP2D_CONSTEXPR
+ #define BP2D_COMPILER_MSVC12
+#elif __cplusplus >= 201103L
+ #define BP2D_NOEXCEPT noexcept
+ #define BP2D_CONSTEXPR constexpr
+#endif
+
+/*
+ * Debugging output dout and derr definition
+ */
+//#ifndef NDEBUG
+//# define dout std::cout
+//# define derr std::cerr
+//#else
+//# define dout 0 && std::cout
+//# define derr 0 && std::cerr
+//#endif
+
+namespace libnest2d {
+
+struct DOut {
+#ifndef NDEBUG
+ std::ostream& out = std::cout;
+#endif
+};
+
+struct DErr {
+#ifndef NDEBUG
+ std::ostream& out = std::cerr;
+#endif
+};
+
+template<class T>
+inline DOut&& operator<<( DOut&& out, T&& d) {
+#ifndef NDEBUG
+ out.out << d;
+#endif
+ return std::move(out);
+}
+
+template<class T>
+inline DErr&& operator<<( DErr&& out, T&& d) {
+#ifndef NDEBUG
+ out.out << d;
+#endif
+ return std::move(out);
+}
+inline DOut dout() { return DOut(); }
+inline DErr derr() { return DErr(); }
+
+template< class T >
+struct remove_cvref {
+ using type = typename std::remove_cv<
+ typename std::remove_reference<T>::type>::type;
+};
+
+template< class T >
+using remove_cvref_t = typename remove_cvref<T>::type;
+
+template< class T >
+using remove_ref_t = typename std::remove_reference<T>::type;
+
+template<bool B, class T>
+using enable_if_t = typename std::enable_if<B, T>::type;
+
+template<class F, class...Args>
+struct invoke_result {
+ using type = typename std::result_of<F(Args...)>::type;
+};
+
+template<class F, class...Args>
+using invoke_result_t = typename invoke_result<F, Args...>::type;
+
+/**
+ * A useful little tool for triggering static_assert error messages e.g. when
+ * a mandatory template specialization (implementation) is missing.
+ *
+ * \tparam T A template argument that may come from and outer template method.
+ */
+template<class T> struct always_false { enum { value = false }; };
+
+const double BP2D_CONSTEXPR Pi = 3.141592653589793238463; // 2*std::acos(0);
+const double BP2D_CONSTEXPR Pi_2 = 2*Pi;
+
+/**
+ * @brief Only for the Radian and Degrees classes to behave as doubles.
+ */
+class Double {
+protected:
+ double val_;
+public:
+ Double(): val_(double{}) { }
+ Double(double d) : val_(d) { }
+
+ operator double() const BP2D_NOEXCEPT { return val_; }
+ operator double&() BP2D_NOEXCEPT { return val_; }
+};
+
+class Degrees;
+
+/**
+ * @brief Data type representing radians. It supports conversion to degrees.
+ */
+class Radians: public Double {
+ mutable double sin_ = std::nan(""), cos_ = std::nan("");
+public:
+ Radians(double rads = Double() ): Double(rads) {}
+ inline Radians(const Degrees& degs);
+
+ inline operator Degrees();
+ inline double toDegrees();
+
+ inline double sin() const {
+ if(std::isnan(sin_)) {
+ cos_ = std::cos(val_);
+ sin_ = std::sin(val_);
+ }
+ return sin_;
+ }
+
+ inline double cos() const {
+ if(std::isnan(cos_)) {
+ cos_ = std::cos(val_);
+ sin_ = std::sin(val_);
+ }
+ return cos_;
+ }
+};
+
+/**
+ * @brief Data type representing degrees. It supports conversion to radians.
+ */
+class Degrees: public Double {
+public:
+ Degrees(double deg = Double()): Double(deg) {}
+ Degrees(const Radians& rads): Double( rads * 180/Pi ) {}
+ inline double toRadians() { return Radians(*this);}
+};
+
+inline bool operator==(const Degrees& deg, const Radians& rads) {
+ Degrees deg2 = rads;
+ auto diff = std::abs(deg - deg2);
+ return diff < 0.0001;
+}
+
+inline bool operator==(const Radians& rads, const Degrees& deg) {
+ return deg == rads;
+}
+
+inline Radians::operator Degrees() { return *this * 180/Pi; }
+
+inline Radians::Radians(const Degrees &degs): Double( degs * Pi/180) {}
+
+inline double Radians::toDegrees() { return operator Degrees(); }
+
+enum class GeomErr : std::size_t {
+ OFFSET,
+ MERGE,
+ NFP
+};
+
+const std::string ERROR_STR[] = {
+ "Offsetting could not be done! An invalid geometry may have been added.",
+ "Error while merging geometries!",
+ "No fit polygon cannot be calculated."
+};
+
+class GeometryException: public std::exception {
+
+ virtual const std::string& errorstr(GeomErr errcode) const BP2D_NOEXCEPT {
+ return ERROR_STR[static_cast<std::size_t>(errcode)];
+ }
+
+ GeomErr errcode_;
+public:
+
+ GeometryException(GeomErr code): errcode_(code) {}
+
+ GeomErr errcode() const { return errcode_; }
+
+ const char * what() const BP2D_NOEXCEPT override {
+ return errorstr(errcode_).c_str();
+ }
+};
+
+
+}
+#endif // LIBNEST2D_CONFIG_HPP
diff --git a/xs/src/libnest2d/libnest2d/geometry_traits.hpp b/xs/src/libnest2d/libnest2d/geometry_traits.hpp
new file mode 100644
index 000000000..d16257731
--- /dev/null
+++ b/xs/src/libnest2d/libnest2d/geometry_traits.hpp
@@ -0,0 +1,825 @@
+#ifndef GEOMETRY_TRAITS_HPP
+#define GEOMETRY_TRAITS_HPP
+
+#include <string>
+#include <type_traits>
+#include <algorithm>
+#include <array>
+#include <vector>
+#include <numeric>
+#include <limits>
+#include <cmath>
+
+#include "common.hpp"
+
+namespace libnest2d {
+
+/// Getting the coordinate data type for a geometry class.
+template<class GeomClass> struct CoordType { using Type = long; };
+
+/// TCoord<GeomType> as shorthand for typename `CoordType<GeomType>::Type`.
+template<class GeomType>
+using TCoord = typename CoordType<remove_cvref_t<GeomType>>::Type;
+
+/// Getting the type of point structure used by a shape.
+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;
+
+/**
+ * \brief A point pair base class for other point pairs (segment, box, ...).
+ * \tparam RawPoint The actual point type to use.
+ */
+template<class RawPoint>
+struct PointPair {
+ RawPoint p1;
+ 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;
+ */
+template<class RawPoint>
+class _Box: PointPair<RawPoint> {
+ using PointPair<RawPoint>::p1;
+ 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}) {}
+
+ inline _Box(TCoord<RawPoint> width, TCoord<RawPoint> height):
+ _Box(RawPoint{0, 0}, RawPoint{width, height}) {}
+
+ inline const RawPoint& minCorner() const BP2D_NOEXCEPT { return p1; }
+ inline const RawPoint& maxCorner() const BP2D_NOEXCEPT { return p2; }
+
+ inline RawPoint& minCorner() BP2D_NOEXCEPT { return p1; }
+ inline RawPoint& maxCorner() BP2D_NOEXCEPT { return p2; }
+
+ inline TCoord<RawPoint> width() const BP2D_NOEXCEPT;
+ inline TCoord<RawPoint> height() const BP2D_NOEXCEPT;
+
+ inline RawPoint center() const BP2D_NOEXCEPT;
+
+ inline double area() const BP2D_NOEXCEPT {
+ return double(width()*height());
+ }
+};
+
+template<class RawPoint>
+class _Circle {
+ RawPoint center_;
+ double radius_ = 0;
+public:
+
+ using Tag = CircleTag;
+ using PointType = RawPoint;
+
+ _Circle() = default;
+
+ _Circle(const RawPoint& center, double r): center_(center), radius_(r) {}
+
+ inline const RawPoint& center() const BP2D_NOEXCEPT { return center_; }
+ inline const void center(const RawPoint& c) { center_ = c; }
+
+ inline double radius() const BP2D_NOEXCEPT { return radius_; }
+ inline void radius(double r) { radius_ = r; }
+
+ inline double area() const BP2D_NOEXCEPT {
+ return 2.0*Pi*radius_*radius_;
+ }
+};
+
+/**
+ * \brief An abstraction of a directed line segment with two points.
+ */
+template<class RawPoint>
+class _Segment: PointPair<RawPoint> {
+ using PointPair<RawPoint>::p1;
+ using PointPair<RawPoint>::p2;
+ mutable Radians angletox_ = std::nan("");
+public:
+
+ using PointType = RawPoint;
+
+ inline _Segment() = default;
+
+ inline _Segment(const RawPoint& p, const RawPoint& pp):
+ PointPair<RawPoint>({p, pp}) {}
+
+ /**
+ * @brief Get the first point.
+ * @return Returns the starting point.
+ */
+ inline const RawPoint& first() const BP2D_NOEXCEPT { return p1; }
+
+ /**
+ * @brief The end point.
+ * @return Returns the end point of the segment.
+ */
+ inline const RawPoint& second() const BP2D_NOEXCEPT { return p2; }
+
+ inline void first(const RawPoint& p) BP2D_NOEXCEPT
+ {
+ angletox_ = std::nan(""); p1 = p;
+ }
+
+ inline void second(const RawPoint& p) BP2D_NOEXCEPT {
+ angletox_ = std::nan(""); p2 = p;
+ }
+
+ /// Returns the angle measured to the X (horizontal) axis.
+ inline Radians angleToXaxis() const;
+
+ /// The length of the segment in the measure of the coordinate system.
+ inline double length();
+};
+
+// This struct serves almost as a namespace. The only difference is that is can
+// used in friend declarations.
+namespace pointlike {
+
+ template<class RawPoint>
+ inline TCoord<RawPoint> x(const RawPoint& p)
+ {
+ return p.x();
+ }
+
+ template<class RawPoint>
+ inline TCoord<RawPoint> y(const RawPoint& p)
+ {
+ return p.y();
+ }
+
+ template<class RawPoint>
+ inline TCoord<RawPoint>& x(RawPoint& p)
+ {
+ return p.x();
+ }
+
+ template<class RawPoint>
+ inline TCoord<RawPoint>& y(RawPoint& p)
+ {
+ return p.y();
+ }
+
+ template<class RawPoint>
+ inline double distance(const RawPoint& /*p1*/, const RawPoint& /*p2*/)
+ {
+ static_assert(always_false<RawPoint>::value,
+ "PointLike::distance(point, point) unimplemented!");
+ return 0;
+ }
+
+ template<class RawPoint>
+ inline double distance(const RawPoint& /*p1*/,
+ const _Segment<RawPoint>& /*s*/)
+ {
+ static_assert(always_false<RawPoint>::value,
+ "PointLike::distance(point, segment) unimplemented!");
+ return 0;
+ }
+
+ template<class RawPoint>
+ 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());
+
+ TCoord<RawPoint> ret;
+
+ if( (y < y1 && y < y2) || (y > y1 && y > y2) )
+ return {0, false};
+ if ((y == y1 && y == y2) && (x > x1 && x > x2))
+ ret = std::min( x-x1, x -x2);
+ else if( (y == y1 && y == y2) && (x < x1 && x < x2))
+ ret = -std::min(x1 - x, x2 - x);
+ else if(std::abs(y - y1) <= std::numeric_limits<Unit>::epsilon() &&
+ std::abs(y - y2) <= std::numeric_limits<Unit>::epsilon())
+ ret = 0;
+ else
+ ret = x - x1 + (x1 - x2)*(y1 - y)/(y1 - y2);
+
+ return {ret, true};
+ }
+
+ template<class RawPoint>
+ 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());
+
+ TCoord<RawPoint> ret;
+
+ if( (x < x1 && x < x2) || (x > x1 && x > x2) )
+ return {0, false};
+ if ((x == x1 && x == x2) && (y > y1 && y > y2))
+ ret = std::min( y-y1, y -y2);
+ else if( (x == x1 && x == x2) && (y < y1 && y < y2))
+ ret = -std::min(y1 - y, y2 - y);
+ else if(std::abs(x - x1) <= std::numeric_limits<Unit>::epsilon() &&
+ std::abs(x - x2) <= std::numeric_limits<Unit>::epsilon())
+ ret = 0;
+ else
+ ret = y - y1 + (y1 - y2)*(x1 - x)/(x1 - x2);
+
+ return {ret, true};
+ }
+}
+
+template<class RawPoint>
+TCoord<RawPoint> _Box<RawPoint>::width() const BP2D_NOEXCEPT
+{
+ 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());
+}
+
+template<class RawPoint>
+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); }
+
+template<class RawPoint>
+void setX(RawPoint& p, const TCoord<RawPoint>& val)
+{
+ pointlike::x<RawPoint>(p) = val;
+}
+
+template<class RawPoint>
+void setY(RawPoint& p, const TCoord<RawPoint>& val)
+{
+ pointlike::y<RawPoint>(p) = val;
+}
+
+template<class RawPoint>
+inline Radians _Segment<RawPoint>::angleToXaxis() const
+{
+ if(std::isnan(static_cast<double>(angletox_))) {
+ TCoord<RawPoint> dx = getX(second()) - getX(first());
+ TCoord<RawPoint> dy = getY(second()) - getY(first());
+
+ double a = std::atan2(dy, dx);
+ auto s = std::signbit(a);
+
+ if(s) a += Pi_2;
+ angletox_ = a;
+ }
+ return angletox_;
+}
+
+template<class RawPoint>
+inline double _Segment<RawPoint>::length()
+{
+ return pointlike::distance(first(), second());
+}
+
+template<class RawPoint>
+inline RawPoint _Box<RawPoint>::center() const BP2D_NOEXCEPT {
+ auto& minc = minCorner();
+ auto& maxc = maxCorner();
+
+ using Coord = TCoord<RawPoint>;
+
+ 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;
+}
+
+template<class RawShape>
+struct HolesContainer {
+ using Type = std::vector<RawShape>;
+};
+
+template<class RawShape>
+using THolesContainer = typename HolesContainer<remove_cvref_t<RawShape>>::Type;
+
+template<class RawShape>
+struct CountourType {
+ using Type = RawShape;
+};
+
+template<class RawShape>
+using TContour = typename CountourType<remove_cvref_t<RawShape>>::Type;
+
+enum class Orientation {
+ CLOCKWISE,
+ COUNTER_CLOCKWISE
+};
+
+template<class RawShape>
+struct OrientationType {
+
+ // Default Polygon orientation that the library expects
+ static const Orientation Value = Orientation::CLOCKWISE;
+};
+
+enum class Formats {
+ WKT,
+ SVG
+};
+
+// 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.
+namespace shapelike {
+
+ template<class RawShape>
+ using Shapes = TMultiShape<RawShape>;
+
+ template<class RawShape>
+ inline RawShape create(const TContour<RawShape>& contour,
+ const THolesContainer<RawShape>& holes)
+ {
+ return RawShape(contour, holes);
+ }
+
+ template<class RawShape>
+ inline RawShape create(TContour<RawShape>&& contour,
+ THolesContainer<RawShape>&& holes)
+ {
+ return RawShape(contour, holes);
+ }
+
+ template<class RawShape>
+ inline RawShape create(const TContour<RawShape>& contour)
+ {
+ return create<RawShape>(contour, {});
+ }
+
+ template<class RawShape>
+ inline RawShape create(TContour<RawShape>&& contour)
+ {
+ return create<RawShape>(contour, {});
+ }
+
+ template<class RawShape>
+ inline THolesContainer<RawShape>& holes(RawShape& /*sh*/)
+ {
+ static THolesContainer<RawShape> empty;
+ return empty;
+ }
+
+ template<class RawShape>
+ inline const THolesContainer<RawShape>& holes(const RawShape& /*sh*/)
+ {
+ static THolesContainer<RawShape> empty;
+ return empty;
+ }
+
+ template<class RawShape>
+ inline TContour<RawShape>& getHole(RawShape& sh, unsigned long idx)
+ {
+ return holes(sh)[idx];
+ }
+
+ template<class RawShape>
+ inline const TContour<RawShape>& getHole(const RawShape& sh,
+ unsigned long idx)
+ {
+ return holes(sh)[idx];
+ }
+
+ template<class RawShape>
+ inline size_t holeCount(const RawShape& sh)
+ {
+ return holes(sh).size();
+ }
+
+ template<class RawShape>
+ inline TContour<RawShape>& getContour(RawShape& sh)
+ {
+ return sh;
+ }
+
+ template<class RawShape>
+ inline const TContour<RawShape>& getContour(const RawShape& sh)
+ {
+ return sh;
+ }
+
+ // Optional, does nothing by default
+ template<class RawShape>
+ inline void reserve(RawShape& /*sh*/, size_t /*vertex_capacity*/) {}
+
+ template<class RawShape, class...Args>
+ inline void addVertex(RawShape& sh, Args...args)
+ {
+ return getContour(sh).emplace_back(std::forward<Args>(args)...);
+ }
+
+ template<class RawShape>
+ inline typename TContour<RawShape>::iterator begin(RawShape& sh)
+ {
+ return getContour(sh).begin();
+ }
+
+ template<class RawShape>
+ inline typename TContour<RawShape>::iterator end(RawShape& sh)
+ {
+ return getContour(sh).end();
+ }
+
+ template<class RawShape>
+ inline typename TContour<RawShape>::const_iterator
+ cbegin(const RawShape& sh)
+ {
+ return getContour(sh).cbegin();
+ }
+
+ template<class RawShape>
+ inline typename TContour<RawShape>::const_iterator cend(const RawShape& sh)
+ {
+ return getContour(sh).cend();
+ }
+
+ template<class RawShape>
+ inline std::string toString(const RawShape& /*sh*/)
+ {
+ return "";
+ }
+
+ template<Formats, class RawShape>
+ inline std::string serialize(const RawShape& /*sh*/, double /*scale*/=1)
+ {
+ static_assert(always_false<RawShape>::value,
+ "ShapeLike::serialize() unimplemented!");
+ return "";
+ }
+
+ template<Formats, class RawShape>
+ inline void unserialize(RawShape& /*sh*/, const std::string& /*str*/)
+ {
+ static_assert(always_false<RawShape>::value,
+ "ShapeLike::unserialize() unimplemented!");
+ }
+
+ template<class RawShape>
+ inline double area(const RawShape& /*sh*/, const PolygonTag&)
+ {
+ static_assert(always_false<RawShape>::value,
+ "ShapeLike::area() unimplemented!");
+ return 0;
+ }
+
+ template<class RawShape>
+ inline bool intersects(const RawShape& /*sh*/, const RawShape& /*sh*/)
+ {
+ static_assert(always_false<RawShape>::value,
+ "ShapeLike::intersects() unimplemented!");
+ return false;
+ }
+
+ template<class RawShape>
+ inline bool isInside(const TPoint<RawShape>& /*point*/,
+ const RawShape& /*shape*/)
+ {
+ static_assert(always_false<RawShape>::value,
+ "ShapeLike::isInside(point, shape) unimplemented!");
+ return false;
+ }
+
+ template<class RawShape>
+ inline bool isInside(const RawShape& /*shape*/,
+ const RawShape& /*shape*/)
+ {
+ static_assert(always_false<RawShape>::value,
+ "ShapeLike::isInside(shape, shape) unimplemented!");
+ return false;
+ }
+
+ template<class RawShape>
+ inline bool touches( const RawShape& /*shape*/,
+ const RawShape& /*shape*/)
+ {
+ static_assert(always_false<RawShape>::value,
+ "ShapeLike::touches(shape, shape) unimplemented!");
+ return false;
+ }
+
+ template<class RawShape>
+ inline bool touches( const TPoint<RawShape>& /*point*/,
+ const RawShape& /*shape*/)
+ {
+ static_assert(always_false<RawShape>::value,
+ "ShapeLike::touches(point, shape) unimplemented!");
+ return false;
+ }
+
+ template<class RawShape>
+ inline _Box<TPoint<RawShape>> boundingBox(const RawShape& /*sh*/,
+ const PolygonTag&)
+ {
+ static_assert(always_false<RawShape>::value,
+ "ShapeLike::boundingBox(shape) unimplemented!");
+ }
+
+ template<class RawShapes>
+ inline _Box<TPoint<typename RawShapes::value_type>>
+ boundingBox(const RawShapes& /*sh*/, const MultiPolygonTag&)
+ {
+ static_assert(always_false<RawShapes>::value,
+ "ShapeLike::boundingBox(shapes) unimplemented!");
+ }
+
+ template<class RawShape>
+ inline RawShape convexHull(const RawShape& /*sh*/, const PolygonTag&)
+ {
+ static_assert(always_false<RawShape>::value,
+ "ShapeLike::convexHull(shape) unimplemented!");
+ return RawShape();
+ }
+
+ template<class RawShapes>
+ inline typename RawShapes::value_type
+ convexHull(const RawShapes& /*sh*/, const MultiPolygonTag&)
+ {
+ static_assert(always_false<RawShapes>::value,
+ "ShapeLike::convexHull(shapes) unimplemented!");
+ return typename RawShapes::value_type();
+ }
+
+ template<class RawShape>
+ inline void rotate(RawShape& /*sh*/, const Radians& /*rads*/)
+ {
+ static_assert(always_false<RawShape>::value,
+ "ShapeLike::rotate() unimplemented!");
+ }
+
+ template<class RawShape, class RawPoint>
+ inline void translate(RawShape& /*sh*/, const RawPoint& /*offs*/)
+ {
+ static_assert(always_false<RawShape>::value,
+ "ShapeLike::translate() unimplemented!");
+ }
+
+ template<class RawShape>
+ inline void offset(RawShape& /*sh*/, TCoord<TPoint<RawShape>> /*distance*/)
+ {
+ dout() << "The current geometry backend does not support offsetting!\n";
+ }
+
+ template<class RawShape>
+ inline std::pair<bool, std::string> isValid(const RawShape& /*sh*/)
+ {
+ return {false, "ShapeLike::isValid() unimplemented!"};
+ }
+
+ template<class RawShape>
+ inline bool isConvex(const TContour<RawShape>& sh)
+ {
+ using Vertex = TPoint<RawShape>;
+ auto first = sh.begin();
+ auto middle = std::next(first);
+ auto last = std::next(middle);
+ using CVrRef = const Vertex&;
+
+ auto zcrossproduct = [](CVrRef k, CVrRef k1, CVrRef k2) {
+ auto dx1 = getX(k1) - getX(k);
+ auto dy1 = getY(k1) - getY(k);
+ auto dx2 = getX(k2) - getX(k1);
+ auto dy2 = getY(k2) - getY(k1);
+ return dx1*dy2 - dy1*dx2;
+ };
+
+ auto firstprod = zcrossproduct( *(std::prev(std::prev(sh.end()))),
+ *first,
+ *middle );
+
+ bool ret = true;
+ bool frsign = firstprod > 0;
+ while(last != sh.end()) {
+ auto &k = *first, &k1 = *middle, &k2 = *last;
+ auto zc = zcrossproduct(k, k1, k2);
+ ret &= frsign == (zc > 0);
+ ++first; ++middle; ++last;
+ }
+
+ return ret;
+ }
+
+ // *************************************************************************
+ // No need to implement these
+ // *************************************************************************
+
+ template<class Box>
+ inline Box boundingBox(const Box& box, const BoxTag& )
+ {
+ return box;
+ }
+
+ template<class Circle>
+ inline _Box<typename Circle::PointType> boundingBox(
+ const Circle& circ, const CircleTag&)
+ {
+ 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()) };
+
+ Point pmax = {
+ static_cast<Coord>(getX(circ.center()) + circ.radius()),
+ static_cast<Coord>(getY(circ.center()) + circ.radius()) };
+
+ return {pmin, pmax};
+ }
+
+ template<class S> // Dispatch function
+ inline _Box<TPoint<S>> boundingBox(const S& sh)
+ {
+ return boundingBox(sh, Tag<S>() );
+ }
+
+ 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>
+ inline double area(const Shapes<RawShape>& shapes)
+ {
+ return std::accumulate(shapes.begin(), shapes.end(), 0.0,
+ [](double a, const RawShape& b) {
+ return a += area(b);
+ });
+ }
+
+ template<class RawShape>
+ 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();
+ }
+
+ template<class RawShape>
+ inline bool isInside(const TPoint<RawShape>& point,
+ const _Box<TPoint<RawShape>>& box)
+ {
+ auto px = getX(point);
+ auto py = getY(point);
+ auto minx = getX(box.minCorner());
+ auto miny = getY(box.minCorner());
+ auto maxx = getX(box.maxCorner());
+ auto maxy = getY(box.maxCorner());
+
+ return px > minx && px < maxx && py > miny && py < maxy;
+ }
+
+ template<class RawShape>
+ inline bool isInside(const RawShape& sh,
+ const _Circle<TPoint<RawShape>>& circ)
+ {
+ return std::all_of(cbegin(sh), cend(sh),
+ [&circ](const TPoint<RawShape>& p){
+ return isInside<RawShape>(p, circ);
+ });
+ }
+
+ template<class RawShape>
+ inline bool isInside(const _Box<TPoint<RawShape>>& box,
+ const _Circle<TPoint<RawShape>>& circ)
+ {
+ return isInside<RawShape>(box.minCorner(), circ) &&
+ isInside<RawShape>(box.maxCorner(), circ);
+ }
+
+ template<class RawShape>
+ inline bool isInside(const _Box<TPoint<RawShape>>& ibb,
+ const _Box<TPoint<RawShape>>& box)
+ {
+ auto iminX = getX(ibb.minCorner());
+ auto imaxX = getX(ibb.maxCorner());
+ auto iminY = getY(ibb.minCorner());
+ auto imaxY = getY(ibb.maxCorner());
+
+ auto minX = getX(box.minCorner());
+ auto maxX = getX(box.maxCorner());
+ auto minY = getY(box.minCorner());
+ auto maxY = getY(box.maxCorner());
+
+ return iminX > minX && imaxX < maxX && iminY > minY && imaxY < maxY;
+ }
+
+ template<class RawShape> // Potential O(1) implementation may exist
+ inline TPoint<RawShape>& vertex(RawShape& sh, unsigned long idx)
+ {
+ return *(begin(sh) + idx);
+ }
+
+ template<class RawShape> // Potential O(1) implementation may exist
+ inline const TPoint<RawShape>& vertex(const RawShape& sh,
+ unsigned long idx)
+ {
+ return *(cbegin(sh) + idx);
+ }
+
+ template<class RawShape>
+ inline size_t contourVertexCount(const RawShape& sh)
+ {
+ return cend(sh) - cbegin(sh);
+ }
+
+ template<class RawShape, class Fn>
+ inline void foreachContourVertex(RawShape& sh, Fn fn) {
+ for(auto it = begin(sh); it != end(sh); ++it) fn(*it);
+ }
+
+ template<class RawShape, class 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);
+ }
+ }
+
+ template<class RawShape, class 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>
+ 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);
+ }
+ }
+
+ template<class RawShape, class Fn>
+ inline void foreachVertex(RawShape& sh, Fn fn) {
+ foreachContourVertex(sh, fn);
+ foreachHoleVertex(sh, fn);
+ }
+
+ template<class RawShape, class 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>
+
+}
+
+#endif // GEOMETRY_TRAITS_HPP
diff --git a/xs/src/libnest2d/libnest2d/geometry_traits_nfp.hpp b/xs/src/libnest2d/libnest2d/geometry_traits_nfp.hpp
new file mode 100644
index 000000000..2982454cd
--- /dev/null
+++ b/xs/src/libnest2d/libnest2d/geometry_traits_nfp.hpp
@@ -0,0 +1,558 @@
+#ifndef GEOMETRIES_NOFITPOLYGON_HPP
+#define GEOMETRIES_NOFITPOLYGON_HPP
+
+#include "geometry_traits.hpp"
+#include <algorithm>
+#include <functional>
+#include <vector>
+#include <iterator>
+
+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,
+ ONE_CONVEX,
+ BOTH_CONCAVE,
+ ONE_CONVEX_WITH_HOLES,
+ BOTH_CONCAVE_WITH_HOLES
+};
+
+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 = TMultiShape<RawShape>;
+
+/**
+ * Merge a bunch of polygons with the specified additional polygon.
+ *
+ * \tparam RawShape the Polygon data type.
+ * \param shc The pile of polygons that will be unified with sh.
+ * \param sh A single polygon to unify with shc.
+ *
+ * \return A set of polygons that is the union of the input polygons. Note that
+ * 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 RawShapes>
+inline RawShapes merge(const RawShapes& /*shc*/)
+{
+ static_assert(always_false<RawShapes>::value,
+ "Nfp::merge(shapes, shape) unimplemented!");
+}
+
+/**
+ * Merge a bunch of polygons with the specified additional polygon.
+ *
+ * \tparam RawShape the Polygon data type.
+ * \param shc The pile of polygons that will be unified with sh.
+ * \param sh A single polygon to unify with shc.
+ *
+ * \return A set of polygons that is the union of the input polygons. Note that
+ * 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>
+inline TMultiShape<RawShape> merge(const TMultiShape<RawShape>& shc,
+ const RawShape& sh)
+{
+ auto m = nfp::merge(shc);
+ m.push_back(sh);
+ return nfp::merge(m);
+}
+
+/**
+ * Get the vertex of the polygon that is at the lowest values (bottom) in the Y
+ * axis and if there are more than one vertices on the same Y coordinate than
+ * the result will be the leftmost (with the highest X coordinate).
+ */
+template<class RawShape>
+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),
+ __nfp::_vsort<RawShape>);
+
+ return it == shapelike::cend(sh) ? TPoint<RawShape>() : *it;;
+}
+
+/**
+ * Get the vertex of the polygon that is at the highest values (top) in the Y
+ * axis and if there are more than one vertices on the same Y coordinate than
+ * the result will be the rightmost (with the lowest X coordinate).
+ */
+template<class RawShape>
+TPoint<RawShape> rightmostUpVertex(const RawShape& sh)
+{
+
+ // find max x and max y vertex
+ auto it = std::max_element(shapelike::cbegin(sh), shapelike::cend(sh),
+ __nfp::_vsort<RawShape>);
+
+ 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>
+inline TPoint<RawShape> referenceVertex(const RawShape& sh)
+{
+ return rightmostUpVertex(sh);
+}
+
+/**
+ * The "trivial" Cuninghame-Green implementation of NFP for convex polygons.
+ *
+ * You can use this even if you provide implementations for the more complex
+ * cases (Through specializing the the NfpImpl struct). Currently, no other
+ * cases are covered in the library.
+ *
+ * Complexity should be no more than linear in the number of edges of the input
+ * polygons.
+ *
+ * \tparam RawShape the Polygon data type.
+ * \param sh The stationary polygon
+ * \param cother The orbiting polygon
+ * \return Returns a pair of the NFP and its reference vertex of the two input
+ * polygons which have to be strictly convex. The resulting NFP is proven to be
+ * convex as well in this case.
+ *
+ */
+template<class RawShape>
+inline NfpResult<RawShape> nfpConvexOnly(const RawShape& sh,
+ const RawShape& other)
+{
+ using Vertex = TPoint<RawShape>; using Edge = _Segment<Vertex>;
+ namespace sl = shapelike;
+
+ RawShape rsh; // Final nfp placeholder
+ Vertex top_nfp;
+ std::vector<Edge> edgelist;
+
+ auto cap = sl::contourVertexCount(sh) + sl::contourVertexCount(other);
+
+ // Reserve the needed memory
+ edgelist.reserve(cap);
+ sl::reserve(rsh, static_cast<unsigned long>(cap));
+
+ { // place all edges from sh into edgelist
+ auto first = sl::cbegin(sh);
+ auto next = std::next(first);
+
+ while(next != sl::cend(sh)) {
+ edgelist.emplace_back(*(first), *(next));
+ ++first; ++next;
+ }
+ }
+
+ { // place all edges from other into edgelist
+ auto first = sl::cbegin(other);
+ auto next = std::next(first);
+
+ while(next != sl::cend(other)) {
+ edgelist.emplace_back(*(next), *(first));
+ ++first; ++next;
+ }
+ }
+
+ // Sort the edges by angle to X axis.
+ std::sort(edgelist.begin(), edgelist.end(),
+ [](const Edge& e1, const Edge& e2)
+ {
+ return e1.angleToXaxis() > e2.angleToXaxis();
+ });
+
+ // Add the two vertices from the first edge into the final polygon.
+ sl::addVertex(rsh, edgelist.front().first());
+ sl::addVertex(rsh, edgelist.front().second());
+
+ // Sorting function for the nfp reference vertex search
+ auto& cmp = __nfp::_vsort<RawShape>;
+
+ // the reference (rightmost top) vertex so far
+ top_nfp = *std::max_element(sl::cbegin(rsh), sl::cend(rsh), cmp );
+
+ auto tmp = std::next(sl::begin(rsh));
+
+ // Construct final nfp by placing each edge to the end of the previous
+ for(auto eit = std::next(edgelist.begin());
+ eit != edgelist.end();
+ ++eit)
+ {
+ auto d = *tmp - eit->first();
+ Vertex p = eit->second() + d;
+
+ sl::addVertex(rsh, p);
+
+ // Set the new reference vertex
+ if(cmp(top_nfp, p)) top_nfp = p;
+
+ tmp = std::next(tmp);
+ }
+
+ return {rsh, top_nfp};
+}
+
+template<class RawShape>
+NfpResult<RawShape> nfpSimpleSimple(const RawShape& cstationary,
+ const RawShape& cother)
+{
+
+ // Algorithms are from the original algorithm proposed in paper:
+ // https://eprints.soton.ac.uk/36850/1/CORMSIS-05-05.pdf
+
+ // /////////////////////////////////////////////////////////////////////////
+ // Algorithm 1: Obtaining the minkowski sum
+ // /////////////////////////////////////////////////////////////////////////
+
+ // I guess this is not a full minkowski sum of the two input polygons by
+ // definition. This yields a subset that is compatible with the next 2
+ // algorithms.
+
+ using Result = NfpResult<RawShape>;
+ using Vertex = TPoint<RawShape>;
+ using Coord = TCoord<Vertex>;
+ using Edge = _Segment<Vertex>;
+ namespace sl = shapelike;
+ using std::signbit;
+ using std::sort;
+ using std::vector;
+ using std::ref;
+ using std::reference_wrapper;
+
+ // TODO The original algorithms expects the stationary polygon in
+ // counter clockwise and the orbiter in clockwise order.
+ // So for preventing any further complication, I will make the input
+ // the way it should be, than make my way around the orientations.
+
+ // Reverse the stationary contour to counter clockwise
+ auto stcont = sl::getContour(cstationary);
+ std::reverse(stcont.begin(), stcont.end());
+ RawShape stationary;
+ sl::getContour(stationary) = stcont;
+
+ // Reverse the orbiter contour to counter clockwise
+ auto orbcont = sl::getContour(cother);
+
+ std::reverse(orbcont.begin(), orbcont.end());
+
+ // Copy the orbiter (contour only), we will have to work on it
+ RawShape orbiter;
+ sl::getContour(orbiter) = orbcont;
+
+ // Step 1: Make the orbiter reverse oriented
+ for(auto &v : sl::getContour(orbiter)) v = -v;
+
+ // An egde with additional data for marking it
+ struct MarkedEdge {
+ Edge e; Radians turn_angle = 0; bool is_turning_point = false;
+ MarkedEdge() = default;
+ MarkedEdge(const Edge& ed, Radians ta, bool tp):
+ e(ed), turn_angle(ta), is_turning_point(tp) {}
+ };
+
+ // Container for marked edges
+ using EdgeList = vector<MarkedEdge>;
+
+ EdgeList A, B;
+
+ // This is how an edge list is created from the polygons
+ auto fillEdgeList = [](EdgeList& L, const RawShape& poly, int dir) {
+ L.reserve(sl::contourVertexCount(poly));
+
+ auto it = sl::cbegin(poly);
+ auto nextit = std::next(it);
+
+ double turn_angle = 0;
+ bool is_turn_point = false;
+
+ while(nextit != sl::cend(poly)) {
+ L.emplace_back(Edge(*it, *nextit), turn_angle, is_turn_point);
+ it++; nextit++;
+ }
+
+ auto getTurnAngle = [](const Edge& e1, const Edge& e2) {
+ auto phi = e1.angleToXaxis();
+ auto phi_prev = e2.angleToXaxis();
+ auto TwoPi = 2.0*Pi;
+ if(phi > Pi) phi -= TwoPi;
+ if(phi_prev > Pi) phi_prev -= TwoPi;
+ auto turn_angle = phi-phi_prev;
+ if(turn_angle > Pi) turn_angle -= TwoPi;
+ return phi-phi_prev;
+ };
+
+ if(dir > 0) {
+ auto eit = L.begin();
+ auto enext = std::next(eit);
+
+ eit->turn_angle = getTurnAngle(L.front().e, L.back().e);
+
+ while(enext != L.end()) {
+ enext->turn_angle = getTurnAngle( enext->e, eit->e);
+ enext->is_turning_point =
+ signbit(enext->turn_angle) != signbit(eit->turn_angle);
+ ++eit; ++enext;
+ }
+
+ L.front().is_turning_point = signbit(L.front().turn_angle) !=
+ signbit(L.back().turn_angle);
+ } else {
+ std::cout << L.size() << std::endl;
+
+ auto eit = L.rbegin();
+ auto enext = std::next(eit);
+
+ eit->turn_angle = getTurnAngle(L.back().e, L.front().e);
+
+ while(enext != L.rend()) {
+ enext->turn_angle = getTurnAngle(enext->e, eit->e);
+ enext->is_turning_point =
+ signbit(enext->turn_angle) != signbit(eit->turn_angle);
+ std::cout << enext->is_turning_point << " " << enext->turn_angle << std::endl;
+
+ ++eit; ++enext;
+ }
+
+ L.back().is_turning_point = signbit(L.back().turn_angle) !=
+ signbit(L.front().turn_angle);
+ }
+ };
+
+ // Step 2: Fill the edgelists
+ fillEdgeList(A, stationary, 1);
+ fillEdgeList(B, orbiter, -1);
+
+ // A reference to a marked edge that also knows its container
+ struct MarkedEdgeRef {
+ reference_wrapper<MarkedEdge> eref;
+ reference_wrapper<vector<MarkedEdgeRef>> container;
+ Coord dir = 1; // Direction modifier
+
+ inline Radians angleX() const { return eref.get().e.angleToXaxis(); }
+ inline const Edge& edge() const { return eref.get().e; }
+ inline Edge& edge() { return eref.get().e; }
+ inline bool isTurningPoint() const {
+ return eref.get().is_turning_point;
+ }
+ inline bool isFrom(const vector<MarkedEdgeRef>& cont ) {
+ return &(container.get()) == &cont;
+ }
+ inline bool eq(const MarkedEdgeRef& mr) {
+ return &(eref.get()) == &(mr.eref.get());
+ }
+
+ MarkedEdgeRef(reference_wrapper<MarkedEdge> er,
+ reference_wrapper<vector<MarkedEdgeRef>> ec):
+ eref(er), container(ec), dir(1) {}
+
+ MarkedEdgeRef(reference_wrapper<MarkedEdge> er,
+ reference_wrapper<vector<MarkedEdgeRef>> ec,
+ Coord d):
+ eref(er), container(ec), dir(d) {}
+ };
+
+ using EdgeRefList = vector<MarkedEdgeRef>;
+
+ // Comparing two marked edges
+ auto sortfn = [](const MarkedEdgeRef& e1, const MarkedEdgeRef& e2) {
+ return e1.angleX() < e2.angleX();
+ };
+
+ EdgeRefList Aref, Bref; // We create containers for the references
+ Aref.reserve(A.size()); Bref.reserve(B.size());
+
+ // Fill reference container for the stationary polygon
+ std::for_each(A.begin(), A.end(), [&Aref](MarkedEdge& me) {
+ Aref.emplace_back( ref(me), ref(Aref) );
+ });
+
+ // Fill reference container for the orbiting polygon
+ std::for_each(B.begin(), B.end(), [&Bref](MarkedEdge& me) {
+ Bref.emplace_back( ref(me), ref(Bref) );
+ });
+
+ struct EdgeGroup { typename EdgeRefList::const_iterator first, last; };
+
+ auto mink = [sortfn] // the Mink(Q, R, direction) sub-procedure
+ (const EdgeGroup& Q, const EdgeGroup& R, bool positive)
+ {
+
+ // Step 1 "merge sort_list(Q) and sort_list(R) to form merge_list(Q,R)"
+ // Sort the containers of edge references and merge them.
+ // Q could be sorted only once and be reused here but we would still
+ // need to merge it with sorted(R).
+
+ EdgeRefList merged;
+ EdgeRefList S, seq;
+ merged.reserve((Q.last - Q.first) + (R.last - R.first));
+
+ merged.insert(merged.end(), Q.first, Q.last);
+ merged.insert(merged.end(), R.first, R.last);
+ sort(merged.begin(), merged.end(), sortfn);
+
+ // Step 2 "set i = 1, k = 1, direction = 1, s1 = q1"
+ // we dont use i, instead, q is an iterator into Q. k would be an index
+ // into the merged sequence but we use "it" as an iterator for that
+
+ // here we obtain references for the containers for later comparisons
+ const auto& Rcont = R.first->container.get();
+ const auto& Qcont = Q.first->container.get();
+
+ // Set the intial direction
+ Coord dir = positive? 1 : -1;
+
+ // roughly i = 1 (so q = Q.first) and s1 = q1 so S[0] = q;
+ auto q = Q.first;
+ S.push_back(*q++);
+
+ // Roughly step 3
+ while(q != Q.last) {
+ auto it = merged.begin();
+ while(it != merged.end() && !(it->eq(*(Q.first))) ) {
+ if(it->isFrom(Rcont)) {
+ auto s = *it;
+ s.dir = dir;
+ S.push_back(s);
+ }
+ if(it->eq(*q)) {
+ S.push_back(*q);
+ if(it->isTurningPoint()) dir = -dir;
+ if(q != Q.first) it += dir;
+ }
+ else it += dir;
+ }
+ ++q; // "Set i = i + 1"
+ }
+
+ // Step 4:
+
+ // "Let starting edge r1 be in position si in sequence"
+ // whaaat? I guess this means the following:
+ S[0] = *R.first;
+ auto it = S.begin();
+
+ // "Set j = 1, next = 2, direction = 1, seq1 = si"
+ // we dont use j, seq is expanded dynamically.
+ dir = 1; auto next = std::next(R.first);
+
+ // Step 5:
+ // "If all si edges have been allocated to seqj" should mean that
+ // we loop until seq has equal size with S
+ while(seq.size() < S.size()) {
+ ++it; if(it == S.end()) it = S.begin();
+
+ if(it->isFrom(Qcont)) {
+ seq.push_back(*it); // "If si is from Q, j = j + 1, seqj = si"
+
+ // "If si is a turning point in Q,
+ // direction = - direction, next = next + direction"
+ if(it->isTurningPoint()) { dir = -dir; next += dir; }
+ }
+
+ if(it->eq(*next) && dir == next->dir) { // "If si = direction.rnext"
+ // "j = j + 1, seqj = si, next = next + direction"
+ seq.push_back(*it); next += dir;
+ }
+ }
+
+ return seq;
+ };
+
+ EdgeGroup R{ Bref.begin(), Bref.begin() }, Q{ Aref.begin(), Aref.end() };
+ auto it = Bref.begin();
+ bool orientation = true;
+ EdgeRefList seqlist;
+ seqlist.reserve(3*(Aref.size() + Bref.size()));
+
+ while(it != Bref.end()) // This is step 3 and step 4 in one loop
+ if(it->isTurningPoint()) {
+ R = {R.last, it++};
+ auto seq = mink(Q, R, orientation);
+
+ // TODO step 6 (should be 5 shouldn't it?): linking edges from A
+ // I don't get this step
+
+ seqlist.insert(seqlist.end(), seq.begin(), seq.end());
+ orientation = !orientation;
+ } else ++it;
+
+ if(seqlist.empty()) seqlist = mink(Q, {Bref.begin(), Bref.end()}, true);
+
+ // /////////////////////////////////////////////////////////////////////////
+ // Algorithm 2: breaking Minkowski sums into track line trips
+ // /////////////////////////////////////////////////////////////////////////
+
+
+ // /////////////////////////////////////////////////////////////////////////
+ // Algorithm 3: finding the boundary of the NFP from track line trips
+ // /////////////////////////////////////////////////////////////////////////
+
+
+
+ return Result(stationary, Vertex());
+}
+
+// Specializable NFP implementation class. Specialize it if you have a faster
+// or better NFP implementation
+template<class RawShape, NfpLevel nfptype>
+struct NfpImpl {
+ NfpResult<RawShape> operator()(const RawShape& sh, const RawShape& other)
+ {
+ static_assert(nfptype == NfpLevel::CONVEX_ONLY,
+ "Nfp::noFitPolygon() unimplemented!");
+
+ // Libnest2D has a default implementation for convex polygons and will
+ // use it if feasible.
+ return nfpConvexOnly(sh, other);
+ }
+};
+
+/// Helper function to get the NFP
+template<NfpLevel nfptype, class RawShape>
+inline NfpResult<RawShape> noFitPolygon(const RawShape& sh,
+ const RawShape& other)
+{
+ NfpImpl<RawShape, nfptype> nfps;
+ return nfps(sh, other);
+}
+
+}
+
+}
+
+#endif // GEOMETRIES_NOFITPOLYGON_HPP
diff --git a/xs/src/libnest2d/libnest2d/libnest2d.hpp b/xs/src/libnest2d/libnest2d/libnest2d.hpp
new file mode 100644
index 000000000..4d1e62f99
--- /dev/null
+++ b/xs/src/libnest2d/libnest2d/libnest2d.hpp
@@ -0,0 +1,980 @@
+#ifndef LIBNEST2D_HPP
+#define LIBNEST2D_HPP
+
+#include <memory>
+#include <vector>
+#include <map>
+#include <array>
+#include <algorithm>
+#include <functional>
+
+#include "geometry_traits.hpp"
+
+namespace libnest2d {
+
+namespace sl = shapelike;
+namespace pl = pointlike;
+
+/**
+ * \brief An item to be placed on a bin.
+ *
+ * It holds a copy of the original shape object but supports move construction
+ * from the shape objects if its an rvalue reference. This way we can construct
+ * the items without the cost of copying a potentially large amount of input.
+ *
+ * The results of some calculations are cached for maintaining fast run times.
+ * For this reason, memory demands are much higher but this should pay off.
+ */
+template<class RawShape>
+class _Item {
+ using Coord = TCoord<TPoint<RawShape>>;
+ using Vertex = TPoint<RawShape>;
+ using Box = _Box<Vertex>;
+
+ using VertexConstIterator = typename TContour<RawShape>::const_iterator;
+
+ // The original shape that gets encapsulated.
+ RawShape sh_;
+
+ // Transformation data
+ Vertex translation_;
+ Radians rotation_;
+ Coord offset_distance_;
+
+ // 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;
+
+ // For caching the calculations as they can get pretty expensive.
+ mutable RawShape tr_cache_;
+ mutable bool tr_cache_valid_ = false;
+ mutable double area_cache_ = 0;
+ mutable bool area_cache_valid_ = false;
+ mutable RawShape offset_cache_;
+ mutable bool offset_cache_valid_ = false;
+
+ enum class Convexity: char {
+ UNCHECKED,
+ C_TRUE,
+ C_FALSE
+ };
+
+ mutable Convexity convexity_ = Convexity::UNCHECKED;
+ 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;
+ BBCache(): valid(false) {}
+ } bb_cache_;
+
+public:
+
+ /// The type of the shape which was handed over as the template argument.
+ using ShapeType = RawShape;
+
+ /**
+ * \brief Iterator type for the outer vertices.
+ *
+ * Only const iterators can be used. The _Item type is not intended to
+ * modify the carried shapes from the outside. The main purpose of this type
+ * is to cache the calculation results from the various operators it
+ * supports. Giving out a non const iterator would make it impossible to
+ * perform correct cache invalidation.
+ */
+ using Iterator = VertexConstIterator;
+
+ /**
+ * @brief Get the orientation of the polygon.
+ *
+ * The orientation have to be specified as a specialization of the
+ * OrientationType struct which has a Value constant.
+ *
+ * @return The orientation type identifier for the _Item type.
+ */
+ static BP2D_CONSTEXPR Orientation orientation() {
+ return OrientationType<RawShape>::Value;
+ }
+
+ /**
+ * @brief Constructing an _Item form an existing raw shape. The shape will
+ * be copied into the _Item object.
+ * @param sh The original shape object.
+ */
+ explicit inline _Item(const RawShape& sh): sh_(sh) {}
+
+ /**
+ * @brief Construction of an item by moving the content of the raw shape,
+ * assuming that it supports move semantics.
+ * @param sh The original shape object.
+ */
+ explicit inline _Item(RawShape&& sh): sh_(std::move(sh)) {}
+
+ /**
+ * @brief Create an item from an initializer list.
+ * @param il The initializer list of vertices.
+ */
+ inline _Item(const std::initializer_list< Vertex >& il):
+ sh_(sl::create<RawShape>(il)) {}
+
+ inline _Item(const TContour<RawShape>& contour,
+ const THolesContainer<RawShape>& holes = {}):
+ sh_(sl::create<RawShape>(contour, holes)) {}
+
+ inline _Item(TContour<RawShape>&& contour,
+ THolesContainer<RawShape>&& holes):
+ sh_(sl::create<RawShape>(std::move(contour),
+ std::move(holes))) {}
+
+ /**
+ * @brief Convert the polygon to string representation. The format depends
+ * on the implementation of the polygon.
+ * @return
+ */
+ inline std::string toString() const
+ {
+ return sl::toString(sh_);
+ }
+
+ /// Iterator tho the first contour vertex in the polygon.
+ inline Iterator begin() const
+ {
+ return sl::cbegin(sh_);
+ }
+
+ /// Alias to begin()
+ inline Iterator cbegin() const
+ {
+ return sl::cbegin(sh_);
+ }
+
+ /// Iterator to the last contour vertex.
+ inline Iterator end() const
+ {
+ return sl::cend(sh_);
+ }
+
+ /// Alias to end()
+ inline Iterator cend() const
+ {
+ return sl::cend(sh_);
+ }
+
+ /**
+ * @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
+ * applied to the shape in this call.
+ *
+ * @param idx The index of the requested vertex.
+ * @return A copy of the requested vertex.
+ */
+ inline Vertex vertex(unsigned long idx) const
+ {
+ return sl::vertex(sh_, idx);
+ }
+
+ /**
+ * @brief Modify a vertex.
+ *
+ * Note that this method will invalidate every cached calculation result
+ * including polygon offset and transformations.
+ *
+ * @param idx The index of the requested vertex.
+ * @param v The new vertex data.
+ */
+ inline void setVertex(unsigned long idx, const Vertex& v )
+ {
+ invalidateCache();
+ sl::vertex(sh_, idx) = v;
+ }
+
+ /**
+ * @brief Calculate the shape area.
+ *
+ * The method returns absolute value and does not reflect polygon
+ * orientation. The result is cached, subsequent calls will have very little
+ * cost.
+ * @return The shape area in floating point double precision.
+ */
+ inline double area() const {
+ double ret ;
+ if(area_cache_valid_) ret = area_cache_;
+ else {
+ ret = sl::area(offsettedShape());
+ area_cache_ = ret;
+ area_cache_valid_ = true;
+ }
+ return ret;
+ }
+
+ inline bool isContourConvex() const {
+ bool ret = false;
+
+ switch(convexity_) {
+ case Convexity::UNCHECKED:
+ ret = sl::isConvex<RawShape>(sl::getContour(transformedShape()));
+ convexity_ = ret? Convexity::C_TRUE : Convexity::C_FALSE;
+ break;
+ case Convexity::C_TRUE: ret = true; break;
+ case Convexity::C_FALSE:;
+ }
+
+ return ret;
+ }
+
+ inline bool isHoleConvex(unsigned /*holeidx*/) const {
+ return false;
+ }
+
+ inline bool areHolesConvex() const {
+ return false;
+ }
+
+ /// The number of the outer ring vertices.
+ inline size_t vertexCount() const {
+ return sl::contourVertexCount(sh_);
+ }
+
+ inline size_t holeCount() const {
+ return sl::holeCount(sh_);
+ }
+
+ /**
+ * @brief isPointInside
+ * @param p
+ * @return
+ */
+ inline bool isInside(const Vertex& p) const
+ {
+ return sl::isInside(p, transformedShape());
+ }
+
+ inline bool isInside(const _Item& sh) const
+ {
+ return sl::isInside(transformedShape(), sh.transformedShape());
+ }
+
+ inline bool isInside(const RawShape& sh) const
+ {
+ return sl::isInside(transformedShape(), sh);
+ }
+
+ inline bool isInside(const _Box<TPoint<RawShape>>& box) const;
+ inline bool isInside(const _Circle<TPoint<RawShape>>& box) const;
+
+ inline void translate(const Vertex& d) BP2D_NOEXCEPT
+ {
+ translation(translation() + d);
+ }
+
+ inline void rotate(const Radians& rads) BP2D_NOEXCEPT
+ {
+ rotation(rotation() + rads);
+ }
+
+ inline void addOffset(Coord distance) BP2D_NOEXCEPT
+ {
+ offset_distance_ = distance;
+ has_offset_ = true;
+ invalidateCache();
+ }
+
+ inline void removeOffset() BP2D_NOEXCEPT {
+ has_offset_ = false;
+ invalidateCache();
+ }
+
+ inline Radians rotation() const BP2D_NOEXCEPT
+ {
+ return rotation_;
+ }
+
+ inline TPoint<RawShape> translation() const BP2D_NOEXCEPT
+ {
+ return translation_;
+ }
+
+ inline void rotation(Radians rot) BP2D_NOEXCEPT
+ {
+ if(rotation_ != rot) {
+ rotation_ = rot; has_rotation_ = true; tr_cache_valid_ = false;
+ rmt_valid_ = false; lmb_valid_ = false;
+ bb_cache_.valid = false;
+ }
+ }
+
+ inline void translation(const TPoint<RawShape>& tr) BP2D_NOEXCEPT
+ {
+ if(translation_ != tr) {
+ translation_ = tr; has_translation_ = true; tr_cache_valid_ = false;
+ //bb_cache_.valid = false;
+ }
+ }
+
+ inline const RawShape& transformedShape() const
+ {
+ if(tr_cache_valid_) return tr_cache_;
+
+ RawShape cpy = offsettedShape();
+ if(has_rotation_) sl::rotate(cpy, rotation_);
+ if(has_translation_) sl::translate(cpy, translation_);
+ tr_cache_ = cpy; tr_cache_valid_ = true;
+ rmt_valid_ = false; lmb_valid_ = false;
+
+ return tr_cache_;
+ }
+
+ inline operator RawShape() const
+ {
+ return transformedShape();
+ }
+
+ inline const RawShape& rawShape() const BP2D_NOEXCEPT
+ {
+ return sh_;
+ }
+
+ inline void resetTransformation() BP2D_NOEXCEPT
+ {
+ has_translation_ = false; has_rotation_ = false; has_offset_ = false;
+ invalidateCache();
+ }
+
+ inline Box boundingBox() const {
+ if(!bb_cache_.valid) {
+ 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 = translation_;
+ return {bb.minCorner() + tr, bb.maxCorner() + tr };
+ }
+
+ inline Vertex referenceVertex() const {
+ return rightmostTopVertex();
+ }
+
+ inline Vertex rightmostTopVertex() const {
+ if(!rmt_valid_ || !tr_cache_valid_) { // find max x and max y vertex
+ auto& tsh = transformedShape();
+ rmt_ = std::max_element(sl::cbegin(tsh), sl::cend(tsh), vsort);
+ rmt_valid_ = true;
+ }
+ return *rmt_;
+ }
+
+ inline Vertex leftmostBottomVertex() const {
+ if(!lmb_valid_ || !tr_cache_valid_) { // find min x and min y vertex
+ auto& tsh = transformedShape();
+ lmb_ = std::min_element(sl::cbegin(tsh), sl::cend(tsh), vsort);
+ lmb_valid_ = true;
+ }
+ return *lmb_;
+ }
+
+ //Static methods:
+
+ inline static bool intersects(const _Item& sh1, const _Item& sh2)
+ {
+ return sl::intersects(sh1.transformedShape(),
+ sh2.transformedShape());
+ }
+
+ inline static bool touches(const _Item& sh1, const _Item& sh2)
+ {
+ return sl::touches(sh1.transformedShape(),
+ sh2.transformedShape());
+ }
+
+private:
+
+ inline const RawShape& offsettedShape() const {
+ if(has_offset_ ) {
+ if(offset_cache_valid_) return offset_cache_;
+
+ offset_cache_ = sh_;
+ sl::offset(offset_cache_, offset_distance_);
+ offset_cache_valid_ = true;
+ return offset_cache_;
+ }
+ return sh_;
+ }
+
+ inline void invalidateCache() const BP2D_NOEXCEPT
+ {
+ tr_cache_valid_ = false;
+ lmb_valid_ = false; rmt_valid_ = false;
+ area_cache_valid_ = false;
+ offset_cache_valid_ = false;
+ bb_cache_.valid = false;
+ convexity_ = Convexity::UNCHECKED;
+ }
+
+ static inline bool vsort(const Vertex& v1, const Vertex& v2)
+ {
+ Coord &&x1 = getX(v1), &&x2 = getX(v2);
+ Coord &&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;
+ }
+};
+
+/**
+ * \brief Subclass of _Item for regular rectangle items.
+ */
+template<class RawShape>
+class _Rectangle: public _Item<RawShape> {
+ using _Item<RawShape>::vertex;
+ using TO = Orientation;
+public:
+
+ using Unit = TCoord<TPoint<RawShape>>;
+
+ template<TO o = OrientationType<RawShape>::Value>
+ inline _Rectangle(Unit width, Unit height,
+ // disable this ctor if o != CLOCKWISE
+ enable_if_t< o == TO::CLOCKWISE, int> = 0 ):
+ _Item<RawShape>( sl::create<RawShape>( {
+ {0, 0},
+ {0, height},
+ {width, height},
+ {width, 0},
+ {0, 0}
+ } ))
+ {
+ }
+
+ template<TO o = OrientationType<RawShape>::Value>
+ inline _Rectangle(Unit width, Unit height,
+ // disable this ctor if o != COUNTER_CLOCKWISE
+ enable_if_t< o == TO::COUNTER_CLOCKWISE, int> = 0 ):
+ _Item<RawShape>( sl::create<RawShape>( {
+ {0, 0},
+ {width, 0},
+ {width, height},
+ {0, height},
+ {0, 0}
+ } ))
+ {
+ }
+
+ inline Unit width() const BP2D_NOEXCEPT {
+ return getX(vertex(2));
+ }
+
+ inline Unit height() const BP2D_NOEXCEPT {
+ return getY(vertex(2));
+ }
+};
+
+template<class RawShape>
+inline bool _Item<RawShape>::isInside(const _Box<TPoint<RawShape>>& box) const {
+ return sl::isInside<RawShape>(boundingBox(), box);
+}
+
+template<class RawShape> inline bool
+_Item<RawShape>::isInside(const _Circle<TPoint<RawShape>>& circ) const {
+ 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 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
+ * and it will work out of the box.
+ */
+template<class PlacementStrategy>
+class PlacementStrategyLike {
+ PlacementStrategy impl_;
+public:
+
+ /// The item type that the placer works with.
+ using Item = typename PlacementStrategy::Item;
+
+ /// The placer's config type. Should be a simple struct but can be anything.
+ using Config = typename PlacementStrategy::Config;
+
+ /**
+ * \brief The type of the bin that the placer works with.
+ *
+ * Can be a box or an arbitrary shape or just a width or height without a
+ * second dimension if an infinite bin is considered.
+ */
+ using BinType = typename PlacementStrategy::BinType;
+
+ /**
+ * \brief Pack result that can be used to accept or discard it. See trypack
+ * method.
+ */
+ using PackResult = typename PlacementStrategy::PackResult;
+
+ using ItemRef = _ItemRef<Item>;
+ using ItemGroup = _ItemGroup<Item>;
+ using DefaultIterator = typename ItemGroup::const_iterator;
+
+ /**
+ * @brief Constructor taking the bin and an optional configuration.
+ * @param bin The bin object whose type is defined by the placement strategy.
+ * @param config The configuration for the particular placer.
+ */
+ explicit PlacementStrategyLike(const BinType& bin,
+ const Config& config = Config()):
+ impl_(bin)
+ {
+ configure(config);
+ }
+
+ /**
+ * @brief Provide a different configuration for the placer.
+ *
+ * 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 strategy.
+ */
+ inline void configure(const Config& config) { impl_.configure(config); }
+
+ /**
+ * Try to pack an item with a result object that contains the packing
+ * information for later accepting it.
+ *
+ * \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.
+ */
+ 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 (or items).
+ *
+ * If the pack result is a failure the method should ignore it.
+ * @param r The result of a previous trypack call.
+ */
+ inline void accept(PackResult& r) { impl_.accept(r); }
+
+ /**
+ * @brief pack Try to pack and immediately accept it on success.
+ *
+ * A default implementation would be to call
+ * { 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 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.
+ */
+ 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(); }
+
+ /// Get the bin object.
+ inline const BinType& bin() const { return impl_.bin(); }
+
+ /// Set a new bin object.
+ inline void bin(const BinType& bin) { impl_.bin(bin); }
+
+ /// Get the packed items.
+ inline ItemGroup getItems() { return impl_.getItems(); }
+
+ /// Clear the packed items so a new session can be started.
+ inline void clearItems() { impl_.clearItems(); }
+
+ inline double filledArea() const { return impl_.filledArea(); }
+
+};
+
+// The progress function will be called with the number of placed items
+using ProgressFunction = std::function<void(unsigned)>;
+
+/**
+ * A wrapper interface (trait) class for any selections strategy provider.
+ */
+template<class SelectionStrategy>
+class SelectionStrategyLike {
+ SelectionStrategy impl_;
+public:
+ using Item = typename SelectionStrategy::Item;
+ using Config = typename SelectionStrategy::Config;
+
+ using ItemRef = std::reference_wrapper<Item>;
+ using ItemGroup = std::vector<ItemRef>;
+
+ /**
+ * @brief Provide a different configuration for the selection strategy.
+ *
+ * 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 strategy.
+ */
+ inline void configure(const Config& config) {
+ impl_.configure(config);
+ }
+
+ /**
+ * @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.
+ */
+ void progressIndicator(ProgressFunction fn) { impl_.progressIndicator(fn); }
+
+ /**
+ * \brief A method to start the calculation on the input sequence.
+ *
+ * \tparam TPlacer The only mandatory template parameter is the type of
+ * 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 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.
+ */
+ template<class TPlacer, class TIterator,
+ class TBin = typename PlacementStrategyLike<TPlacer>::BinType,
+ class PConfig = typename PlacementStrategyLike<TPlacer>::Config>
+ inline void packItems(
+ TIterator first,
+ TIterator last,
+ TBin&& bin,
+ PConfig&& config = PConfig() )
+ {
+ impl_.template packItems<TPlacer>(first, last,
+ std::forward<TBin>(bin),
+ std::forward<PConfig>(config));
+ }
+
+ /**
+ * \brief Get the number of bins opened by the selection algorithm.
+ *
+ * Initially it is zero and after the call to packItems it will return
+ * the number of bins opened by the packing procedure.
+ *
+ * \return The number of bins opened.
+ */
+ inline size_t binCount() const { return impl_.binCount(); }
+
+ /**
+ * @brief Get the items for a particular bin.
+ * @param binIndex The index of 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);
+ }
+
+ /// Same as itemsForBin but for a const context.
+ inline const ItemGroup itemsForBin(size_t binIndex) const {
+ return impl_.itemsForBin(binIndex);
+ }
+};
+
+
+/**
+ * \brief A list of packed item vectors. Each vector represents a bin.
+ */
+template<class RawShape>
+using _PackGroup = std::vector<
+ std::vector<
+ std::reference_wrapper<_Item<RawShape>>
+ >
+ >;
+
+/**
+ * \brief A list of packed (index, item) pair vectors. Each vector represents a
+ * bin.
+ *
+ * The index is points to the position of the item in the original input
+ * sequence. This way the caller can use the items as a transformation data
+ * carrier and transform the original objects manually.
+ */
+template<class RawShape>
+using _IndexedPackGroup = std::vector<
+ std::vector<
+ std::pair<
+ unsigned,
+ std::reference_wrapper<_Item<RawShape>>
+ >
+ >
+ >;
+
+/**
+ * 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 Nester {
+ using TSel = SelectionStrategyLike<SelectionStrategy>;
+ TSel selector_;
+public:
+ using Item = typename PlacementStrategy::Item;
+ using ItemRef = std::reference_wrapper<Item>;
+ using TPlacer = PlacementStrategyLike<PlacementStrategy>;
+ using BinType = typename TPlacer::BinType;
+ using PlacementConfig = typename TPlacer::Config;
+ using SelectionConfig = typename TSel::Config;
+
+ using Unit = TCoord<TPoint<typename Item::ShapeType>>;
+
+ using IndexedPackGroup = _IndexedPackGroup<typename Item::ShapeType>;
+ using PackGroup = _PackGroup<typename Item::ShapeType>;
+ using ResultType = PackGroup;
+ using ResultTypeIndexed = IndexedPackGroup;
+
+private:
+ BinType bin_;
+ PlacementConfig pconfig_;
+ Unit min_obj_distance_;
+
+ using SItem = typename SelectionStrategy::Item;
+ using TPItem = remove_cvref_t<Item>;
+ using TSItem = remove_cvref_t<SItem>;
+
+ std::vector<TPItem> item_cache_;
+
+public:
+
+ /**
+ * \brief Constructor taking the bin as the only mandatory parameter.
+ *
+ * \param bin The bin shape that will be used by the placers. The type
+ * of the bin should be one that is supported by the placer type.
+ */
+ template<class TBinType = BinType,
+ class PConf = PlacementConfig,
+ class SConf = SelectionConfig>
+ Nester( TBinType&& bin,
+ Unit min_obj_distance = 0,
+ PConf&& pconfig = PConf(),
+ SConf&& sconfig = SConf()):
+ bin_(std::forward<TBinType>(bin)),
+ pconfig_(std::forward<PlacementConfig>(pconfig)),
+ min_obj_distance_(min_obj_distance)
+ {
+ static_assert( std::is_same<TPItem, TSItem>::value,
+ "Incompatible placement and selection strategy!");
+
+ selector_.configure(std::forward<SelectionConfig>(sconfig));
+ }
+
+ void configure(const PlacementConfig& pconf) { pconfig_ = pconf; }
+ void configure(const SelectionConfig& sconf) { selector_.configure(sconf); }
+ void configure(const PlacementConfig& pconf, const SelectionConfig& sconf) {
+ pconfig_ = pconf;
+ selector_.configure(sconf);
+ }
+ void configure(const SelectionConfig& sconf, const PlacementConfig& pconf) {
+ pconfig_ = pconf;
+ selector_.configure(sconf);
+ }
+
+ /**
+ * \brief Arrange an input sequence and return a PackGroup object with
+ * the packed groups corresponding to the bins.
+ *
+ * The number of groups in the pack group is the number of bins opened by
+ * the selection algorithm.
+ */
+ template<class TIterator>
+ inline PackGroup execute(TIterator from, TIterator to)
+ {
+ return _execute(from, to);
+ }
+
+ /**
+ * A version of the arrange method returning an IndexedPackGroup with
+ * the item indexes into the original input sequence.
+ *
+ * Takes a little longer to collect the indices. Scales linearly with the
+ * input sequence size.
+ */
+ template<class TIterator>
+ inline IndexedPackGroup executeIndexed(TIterator from, TIterator to)
+ {
+ return _executeIndexed(from, to);
+ }
+
+ /// Shorthand to normal arrange method.
+ template<class TIterator>
+ inline PackGroup operator() (TIterator from, TIterator to)
+ {
+ return _execute(from, to);
+ }
+
+ /// Set a progress indicator function object for the selector.
+ inline Nester& progressIndicator(ProgressFunction func)
+ {
+ selector_.progressIndicator(func); return *this;
+ }
+
+ inline PackGroup lastResult() {
+ PackGroup ret;
+ for(size_t i = 0; i < selector_.binCount(); i++) {
+ auto items = selector_.itemsForBin(i);
+ ret.push_back(items);
+ }
+ return ret;
+ }
+
+private:
+
+ template<class TIterator,
+ class IT = remove_cvref_t<typename TIterator::value_type>,
+
+ // 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 _execute(TIterator from, TIterator to, bool = false)
+ {
+ __execute(from, to);
+ return lastResult();
+ }
+
+ template<class TIterator,
+ class IT = remove_cvref_t<typename TIterator::value_type>,
+ class T = enable_if_t<!std::is_convertible<IT, TPItem>::value, IT>
+ >
+ inline PackGroup _execute(TIterator from, TIterator to, int = false)
+ {
+ item_cache_ = {from, to};
+
+ __execute(item_cache_.begin(), item_cache_.end());
+ return lastResult();
+ }
+
+ template<class TIterator,
+ class IT = remove_cvref_t<typename TIterator::value_type>,
+
+ // 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 _executeIndexed(TIterator from,
+ TIterator to,
+ bool = false)
+ {
+ __execute(from, to);
+ return createIndexedPackGroup(from, to, selector_);
+ }
+
+ template<class TIterator,
+ class IT = remove_cvref_t<typename TIterator::value_type>,
+ class T = enable_if_t<!std::is_convertible<IT, TPItem>::value, IT>
+ >
+ inline IndexedPackGroup _executeIndexed(TIterator from,
+ TIterator to,
+ int = false)
+ {
+ item_cache_ = {from, to};
+ __execute(item_cache_.begin(), item_cache_.end());
+ return createIndexedPackGroup(from, to, selector_);
+ }
+
+ template<class TIterator>
+ static IndexedPackGroup createIndexedPackGroup(TIterator from,
+ TIterator to,
+ TSel& selector)
+ {
+ IndexedPackGroup pg;
+ pg.reserve(selector.binCount());
+
+ for(size_t i = 0; i < selector.binCount(); i++) {
+ auto items = selector.itemsForBin(i);
+ pg.push_back({});
+ pg[i].reserve(items.size());
+
+ for(Item& itemA : items) {
+ auto it = from;
+ unsigned idx = 0;
+ while(it != to) {
+ Item& itemB = *it;
+ if(&itemB == &itemA) break;
+ it++; idx++;
+ }
+ pg[i].emplace_back(idx, itemA);
+ }
+ }
+
+ return pg;
+ }
+
+ 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)));
+ });
+
+ selector_.template packItems<PlacementStrategy>(
+ from, to, bin_, pconfig_);
+
+ if(min_obj_distance_ > 0) std::for_each(from, to, [](Item& item) {
+ item.removeOffset();
+ });
+ }
+};
+
+}
+
+#endif // LIBNEST2D_HPP
diff --git a/xs/src/libnest2d/libnest2d/metaloop.hpp b/xs/src/libnest2d/libnest2d/metaloop.hpp
new file mode 100644
index 000000000..d88988ba1
--- /dev/null
+++ b/xs/src/libnest2d/libnest2d/metaloop.hpp
@@ -0,0 +1,227 @@
+#ifndef METALOOP_HPP
+#define METALOOP_HPP
+
+#include "common.hpp"
+#include <tuple>
+#include <functional>
+
+namespace libnest2d {
+
+/* ************************************************************************** */
+/* C++14 std::index_sequence implementation: */
+/* ************************************************************************** */
+
+/**
+ * \brief C++11 conformant implementation of the index_sequence type from C++14
+ */
+template<size_t...Ints> struct index_sequence {
+ using value_type = size_t;
+ BP2D_CONSTEXPR value_type size() const { return sizeof...(Ints); }
+};
+
+// A Help structure to generate the integer list
+template<size_t...Nseq> struct genSeq;
+
+// Recursive template to generate the list
+template<size_t I, size_t...Nseq> struct genSeq<I, Nseq...> {
+ // Type will contain a genSeq with Nseq appended by one element
+ using Type = typename genSeq< I - 1, I - 1, Nseq...>::Type;
+};
+
+// Terminating recursion
+template <size_t ... Nseq> struct genSeq<0, Nseq...> {
+ // If I is zero, Type will contain index_sequence with the fuly generated
+ // integer list.
+ using Type = index_sequence<Nseq...>;
+};
+
+/// Helper alias to make an index sequence from 0 to N
+template<size_t N> using make_index_sequence = typename genSeq<N>::Type;
+
+/// Helper alias to make an index sequence for a parameter pack
+template<class...Args>
+using index_sequence_for = make_index_sequence<sizeof...(Args)>;
+
+
+/* ************************************************************************** */
+
+namespace opt {
+
+using std::forward;
+using std::tuple;
+using std::get;
+using std::tuple_element;
+
+/**
+ * @brief Helper class to be able to loop over a parameter pack's elements.
+ */
+class metaloop {
+
+// The implementation is based on partial struct template specializations.
+// Basically we need a template type that is callable and takes an integer
+// non-type template parameter which can be used to implement recursive calls.
+//
+// C++11 will not allow the usage of a plain template function that is why we
+// use struct with overloaded call operator. At the same time C++11 prohibits
+// partial template specialization with a non type parameter such as int. We
+// need to wrap that in a type (see metaloop::Int).
+
+/*
+ * 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 necessary for this trick.
+ */
+template<int N> using Int = std::integral_constant<int, N>;
+
+/*
+ * Helper class to implement in-place functors.
+ *
+ * We want to be able to use inline functors like a lambda to keep the code
+ * as clear as possible.
+ */
+template<int N, class Fn> class MapFn {
+ Fn&& fn_;
+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 lambda parameter list.
+ inline MapFn(Fn&& fn): fn_(forward<Fn>(fn)) {}
+
+ template<class T> void operator ()(T&& pack_element) {
+ // We provide the index as the first parameter and the pack (or tuple)
+ // element as the second parameter to the functor.
+ fn_(N, forward<T>(pack_element));
+ }
+};
+
+/*
+ * Implementation of the template loop trick.
+ * We create a mechanism for looping over a parameter pack in compile time.
+ * \tparam Idx is the loop index which will be decremented at each recursion.
+ * \tparam Args The parameter pack that will be processed.
+ *
+ */
+template <typename Idx, class...Args>
+class _MetaLoop {};
+
+// Implementation for the first element of Args...
+template <class...Args>
+class _MetaLoop<Int<0>, Args...> {
+public:
+
+ const static BP2D_CONSTEXPR int N = 0;
+ const static BP2D_CONSTEXPR int ARGNUM = sizeof...(Args)-1;
+
+ template<class Tup, class Fn>
+ void run( Tup&& valtup, Fn&& fn) {
+ MapFn<ARGNUM-N, Fn> {forward<Fn>(fn)} (get<ARGNUM-N>(valtup));
+ }
+};
+
+// Implementation for the N-th element of Args...
+template <int N, class...Args>
+class _MetaLoop<Int<N>, Args...> {
+public:
+
+ const static BP2D_CONSTEXPR int ARGNUM = sizeof...(Args)-1;
+
+ template<class Tup, class Fn>
+ void run(Tup&& valtup, Fn&& fn) {
+ MapFn<ARGNUM-N, Fn> {forward<Fn>(fn)} (std::get<ARGNUM-N>(valtup));
+
+ // Recursive call to process the next element of Args
+ _MetaLoop<Int<N-1>, Args...> ().run(forward<Tup>(valtup),
+ forward<Fn>(fn));
+ }
+};
+
+/*
+ * Instantiation: We must instantiate the template with the last index because
+ * the generalized version calls the decremented instantiations recursively.
+ * Once the instantiation with the first index is called, the terminating
+ * 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 meta-programming pattern.
+ */
+template<class...Args>
+using MetaLoop = _MetaLoop<Int<sizeof...(Args)-1>, Args...>;
+
+public:
+
+/**
+ * \brief The final usable function template.
+ *
+ * This is similar to what varags was on C but in compile time C++11.
+ * You can call:
+ * apply(<the mapping function>, <arbitrary number of arguments of any type>);
+ * For example:
+ *
+ * struct mapfunc {
+ * template<class T> void operator()(int N, T&& element) {
+ * std::cout << "The value of the parameter "<< N <<": "
+ * << element << std::endl;
+ * }
+ * };
+ *
+ * apply(mapfunc(), 'a', 10, 151.545);
+ *
+ * C++14:
+ * apply([](int N, auto&& element){
+ * std::cout << "The value of the parameter "<< N <<": "
+ * << element << std::endl;
+ * }, 'a', 10, 151.545);
+ *
+ * This yields the output:
+ * The value of the parameter 0: a
+ * The value of the parameter 1: 10
+ * The value of the parameter 2: 151.545
+ *
+ * As an addition, the function can be called with a tuple as the second
+ * parameter holding the arguments instead of a parameter pack.
+ *
+ */
+template<class...Args, class Fn>
+inline static void apply(Fn&& fn, Args&&...args) {
+ MetaLoop<Args...>().run(tuple<Args&&...>(forward<Args>(args)...),
+ forward<Fn>(fn));
+}
+
+/// The version of apply with a tuple rvalue reference.
+template<class...Args, class Fn>
+inline static void apply(Fn&& fn, tuple<Args...>&& tup) {
+ MetaLoop<Args...>().run(std::move(tup), forward<Fn>(fn));
+}
+
+/// The version of apply with a tuple lvalue reference.
+template<class...Args, class Fn>
+inline static void apply(Fn&& fn, tuple<Args...>& tup) {
+ MetaLoop<Args...>().run(tup, forward<Fn>(fn));
+}
+
+/// The version of apply with a tuple const reference.
+template<class...Args, class Fn>
+inline static void apply(Fn&& fn, const tuple<Args...>& tup) {
+ MetaLoop<Args...>().run(tup, forward<Fn>(fn));
+}
+
+/**
+ * Call a function with its arguments encapsualted in a tuple.
+ */
+template<class Fn, class Tup, std::size_t...Is>
+inline static auto
+callFunWithTuple(Fn&& fn, Tup&& tup, index_sequence<Is...>) ->
+ decltype(fn(std::get<Is>(tup)...))
+{
+ return fn(std::get<Is>(tup)...);
+}
+
+};
+}
+}
+
+#endif // METALOOP_HPP
diff --git a/xs/src/libnest2d/libnest2d/optimizer.hpp b/xs/src/libnest2d/libnest2d/optimizer.hpp
new file mode 100644
index 000000000..90d2f2ff9
--- /dev/null
+++ b/xs/src/libnest2d/libnest2d/optimizer.hpp
@@ -0,0 +1,247 @@
+#ifndef OPTIMIZER_HPP
+#define OPTIMIZER_HPP
+
+#include <tuple>
+#include <functional>
+#include <limits>
+#include "common.hpp"
+
+namespace libnest2d { namespace opt {
+
+using std::forward;
+using std::tuple;
+using std::make_tuple;
+
+/// A Type trait for upper and lower limit of a numeric type.
+template<class T, class B = void >
+struct limits {
+ inline static T min() { return std::numeric_limits<T>::min(); }
+ inline static T max() { return std::numeric_limits<T>::max(); }
+};
+
+template<class T>
+struct limits<T, enable_if_t<std::numeric_limits<T>::has_infinity, void>> {
+ inline static T min() { return -std::numeric_limits<T>::infinity(); }
+ inline static T max() { return std::numeric_limits<T>::infinity(); }
+};
+
+/// An interval of possible input values for optimization
+template<class T>
+class Bound {
+ T min_;
+ T max_;
+public:
+ Bound(const T& min = limits<T>::min(),
+ const T& max = limits<T>::max()): min_(min), max_(max) {}
+ inline const T min() const BP2D_NOEXCEPT { return min_; }
+ inline const T max() const BP2D_NOEXCEPT { return max_; }
+};
+
+/**
+ * Helper function to make a Bound object with its type deduced automatically.
+ */
+template<class T>
+inline Bound<T> bound(const T& min, const T& max) { return Bound<T>(min, max); }
+
+/**
+ * This is the type of an input tuple for the object function. It holds the
+ * values and their type in each dimension.
+ */
+template<class...Args> using Input = tuple<Args...>;
+
+template<class...Args>
+inline tuple<Args...> initvals(Args...args) { return make_tuple(args...); }
+
+/**
+ * @brief Specific optimization methods for which a default optimizer
+ * implementation can be instantiated.
+ */
+enum class Method {
+ L_SIMPLEX,
+ L_SUBPLEX,
+ G_GENETIC,
+ //...
+};
+
+/**
+ * @brief Info about result of an optimization. These codes are exactly the same
+ * as the nlopt codes for convinience.
+ */
+enum ResultCodes {
+ FAILURE = -1, /* generic failure code */
+ INVALID_ARGS = -2,
+ OUT_OF_MEMORY = -3,
+ ROUNDOFF_LIMITED = -4,
+ FORCED_STOP = -5,
+ SUCCESS = 1, /* generic success code */
+ STOPVAL_REACHED = 2,
+ FTOL_REACHED = 3,
+ XTOL_REACHED = 4,
+ MAXEVAL_REACHED = 5,
+ MAXTIME_REACHED = 6
+};
+
+/**
+ * \brief A type to hold the complete result of the optimization.
+ */
+template<class...Args>
+struct Result {
+ ResultCodes resultcode;
+ tuple<Args...> optimum;
+ double score;
+};
+
+/**
+ * @brief A type for specifying the stop criteria.
+ */
+struct StopCriteria {
+
+ /// If the absolute value difference between two scores.
+ double absolute_score_difference = std::nan("");
+
+ /// 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;
+};
+
+/**
+ * \brief The Optimizer base class with CRTP pattern.
+ */
+template<class Subclass>
+class Optimizer {
+protected:
+ enum class OptDir{
+ MIN,
+ MAX
+ } dir_;
+
+ StopCriteria stopcr_;
+
+public:
+
+ inline explicit Optimizer(const StopCriteria& scr = {}): stopcr_(scr) {}
+
+ /**
+ * \brief Optimize for minimum value of the provided objectfunction.
+ * \param objectfunction The function that will be searched for the minimum
+ * return value.
+ * \param initvals A tuple with the initial values for the search
+ * \param bounds A parameter pack with the bounds for each dimension.
+ * \return Returns a Result<Args...> structure.
+ * An example call would be:
+ * auto result = opt.optimize_min(
+ * [](tuple<double> x) // object function
+ * {
+ * return std::pow(std::get<0>(x), 2);
+ * },
+ * make_tuple(-0.5), // initial value
+ * {-1.0, 1.0} // search space bounds
+ * );
+ */
+ template<class Func, class...Args>
+ inline Result<Args...> optimize_min(Func&& objectfunction,
+ Input<Args...> initvals,
+ Bound<Args>... bounds)
+ {
+ dir_ = OptDir::MIN;
+ return static_cast<Subclass*>(this)->template optimize<Func, Args...>(
+ forward<Func>(objectfunction), initvals, bounds... );
+ }
+
+ template<class Func, class...Args>
+ inline Result<Args...> optimize_min(Func&& objectfunction,
+ Input<Args...> initvals)
+ {
+ dir_ = OptDir::MIN;
+ return static_cast<Subclass*>(this)->template optimize<Func, Args...>(
+ objectfunction, initvals, Bound<Args>()... );
+ }
+
+ template<class...Args, class Func>
+ inline Result<Args...> optimize_min(Func&& objectfunction)
+ {
+ dir_ = OptDir::MIN;
+ return static_cast<Subclass*>(this)->template optimize<Func, Args...>(
+ objectfunction,
+ Input<Args...>(),
+ Bound<Args>()... );
+ }
+
+ /// Same as optimize_min but optimizes for maximum function value.
+ template<class Func, class...Args>
+ inline Result<Args...> optimize_max(Func&& objectfunction,
+ Input<Args...> initvals,
+ Bound<Args>... bounds)
+ {
+ dir_ = OptDir::MAX;
+ return static_cast<Subclass*>(this)->template optimize<Func, Args...>(
+ objectfunction, initvals, bounds... );
+ }
+
+ template<class Func, class...Args>
+ inline Result<Args...> optimize_max(Func&& objectfunction,
+ Input<Args...> initvals)
+ {
+ dir_ = OptDir::MAX;
+ return static_cast<Subclass*>(this)->template optimize<Func, Args...>(
+ objectfunction, initvals, Bound<Args>()... );
+ }
+
+ template<class...Args, class Func>
+ inline Result<Args...> optimize_max(Func&& objectfunction)
+ {
+ dir_ = OptDir::MAX;
+ return static_cast<Subclass*>(this)->template optimize<Func, Args...>(
+ objectfunction,
+ Input<Args...>(),
+ Bound<Args>()... );
+ }
+
+};
+
+// Just to be able to instantiate an unimplemented method and generate compile
+// error.
+template<class T = void>
+class DummyOptimizer : public Optimizer<DummyOptimizer<T>> {
+ friend class Optimizer<DummyOptimizer<T>>;
+
+public:
+ DummyOptimizer() {
+ static_assert(always_false<T>::value, "Optimizer unimplemented!");
+ }
+
+ DummyOptimizer(const StopCriteria&) {
+ static_assert(always_false<T>::value, "Optimizer unimplemented!");
+ }
+
+ template<class Func, class...Args>
+ Result<Args...> optimize(Func&& /*func*/,
+ tuple<Args...> /*initvals*/,
+ Bound<Args>... /*args*/)
+ {
+ return Result<Args...>();
+ }
+};
+
+// Specializing this struct will tell what kind of optimizer to generate for
+// a given method
+template<Method m> struct OptimizerSubclass { using Type = DummyOptimizer<>; };
+
+/// Optimizer type based on the method provided in parameter m.
+template<Method m> using TOptimizer = typename OptimizerSubclass<m>::Type;
+
+/// Global optimizer with an explicitly specified local method.
+template<Method m>
+inline TOptimizer<m> GlobalOptimizer(Method, const StopCriteria& scr = {})
+{ // Need to be specialized in order to do anything useful.
+ return TOptimizer<m>(scr);
+}
+
+}
+}
+
+#endif // OPTIMIZER_HPP
diff --git a/xs/src/libnest2d/libnest2d/optimizers/genetic.hpp b/xs/src/libnest2d/libnest2d/optimizers/genetic.hpp
new file mode 100644
index 000000000..276854a12
--- /dev/null
+++ b/xs/src/libnest2d/libnest2d/optimizers/genetic.hpp
@@ -0,0 +1,31 @@
+#ifndef GENETIC_HPP
+#define GENETIC_HPP
+
+#include "nlopt_boilerplate.hpp"
+
+namespace libnest2d { namespace opt {
+
+class GeneticOptimizer: public NloptOptimizer {
+public:
+ inline explicit GeneticOptimizer(const StopCriteria& scr = {}):
+ NloptOptimizer(method2nloptAlg(Method::G_GENETIC), scr) {}
+
+ inline GeneticOptimizer& localMethod(Method m) {
+ localmethod_ = m;
+ return *this;
+ }
+};
+
+template<>
+struct OptimizerSubclass<Method::G_GENETIC> { using Type = GeneticOptimizer; };
+
+template<> TOptimizer<Method::G_GENETIC> GlobalOptimizer<Method::G_GENETIC>(
+ Method localm, const StopCriteria& scr )
+{
+ return GeneticOptimizer (scr).localMethod(localm);
+}
+
+}
+}
+
+#endif // GENETIC_HPP
diff --git a/xs/src/libnest2d/libnest2d/optimizers/nlopt_boilerplate.hpp b/xs/src/libnest2d/libnest2d/optimizers/nlopt_boilerplate.hpp
new file mode 100644
index 000000000..1a0f06e02
--- /dev/null
+++ b/xs/src/libnest2d/libnest2d/optimizers/nlopt_boilerplate.hpp
@@ -0,0 +1,186 @@
+#ifndef NLOPT_BOILERPLATE_HPP
+#define NLOPT_BOILERPLATE_HPP
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable: 4244)
+#pragma warning(disable: 4267)
+#endif
+#include <nlopt.hpp>
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+#include <libnest2d/optimizer.hpp>
+#include <cassert>
+#include "libnest2d/metaloop.hpp"
+
+#include <utility>
+
+namespace libnest2d { namespace opt {
+
+inline nlopt::algorithm method2nloptAlg(Method m) {
+
+ switch(m) {
+ case Method::L_SIMPLEX: return nlopt::LN_NELDERMEAD;
+ case Method::L_SUBPLEX: return nlopt::LN_SBPLX;
+ case Method::G_GENETIC: return nlopt::GN_ESCH;
+ default: assert(false); throw(m);
+ }
+}
+
+/**
+ * Optimizer based on NLopt.
+ *
+ * All the optimized types have to be convertible to double.
+ */
+class NloptOptimizer: public Optimizer<NloptOptimizer> {
+protected:
+ nlopt::opt opt_;
+ std::vector<double> lower_bounds_;
+ std::vector<double> upper_bounds_;
+ std::vector<double> initvals_;
+ nlopt::algorithm alg_;
+ Method localmethod_;
+
+ using Base = Optimizer<NloptOptimizer>;
+
+ friend Base;
+
+ // ********************************************************************** */
+
+ // TODO: CHANGE FOR LAMBDAS WHEN WE WILL MOVE TO C++14
+
+ struct BoundsFunc {
+ NloptOptimizer& self;
+ inline explicit BoundsFunc(NloptOptimizer& o): self(o) {}
+
+ template<class T> void operator()(int N, T& bounds)
+ {
+ self.lower_bounds_[N] = bounds.min();
+ self.upper_bounds_[N] = bounds.max();
+ }
+ };
+
+ struct InitValFunc {
+ NloptOptimizer& self;
+ inline explicit InitValFunc(NloptOptimizer& o): self(o) {}
+
+ template<class T> void operator()(int N, T& initval)
+ {
+ self.initvals_[N] = initval;
+ }
+ };
+
+ struct ResultCopyFunc {
+ NloptOptimizer& self;
+ inline explicit ResultCopyFunc(NloptOptimizer& o): self(o) {}
+
+ template<class T> void operator()(int N, T& resultval)
+ {
+ resultval = self.initvals_[N];
+ }
+ };
+
+ struct FunvalCopyFunc {
+ using D = const std::vector<double>;
+ D& params;
+ inline explicit FunvalCopyFunc(D& p): params(p) {}
+
+ template<class T> void operator()(int N, T& resultval)
+ {
+ resultval = params[N];
+ }
+ };
+
+ /* ********************************************************************** */
+
+ template<class Fn, class...Args>
+ static double optfunc(const std::vector<double>& params,
+ std::vector<double>& /*grad*/,
+ void *data)
+ {
+ auto fnptr = static_cast<remove_ref_t<Fn>*>(data);
+ auto funval = std::tuple<Args...>();
+
+ // copy the obtained objectfunction arguments to the funval tuple.
+ metaloop::apply(FunvalCopyFunc(params), funval);
+
+ auto ret = metaloop::callFunWithTuple(*fnptr, funval,
+ index_sequence_for<Args...>());
+
+ return ret;
+ }
+
+ template<class Func, class...Args>
+ Result<Args...> optimize(Func&& func,
+ std::tuple<Args...> initvals,
+ Bound<Args>... args)
+ {
+ lower_bounds_.resize(sizeof...(Args));
+ upper_bounds_.resize(sizeof...(Args));
+ initvals_.resize(sizeof...(Args));
+
+ opt_ = nlopt::opt(alg_, sizeof...(Args) );
+
+ // Copy the bounds which is obtained as a parameter pack in args into
+ // lower_bounds_ and upper_bounds_
+ metaloop::apply(BoundsFunc(*this), args...);
+
+ opt_.set_lower_bounds(lower_bounds_);
+ opt_.set_upper_bounds(upper_bounds_);
+
+ nlopt::opt localopt;
+ switch(opt_.get_algorithm()) {
+ case nlopt::GN_MLSL:
+ case nlopt::GN_MLSL_LDS:
+ localopt = nlopt::opt(method2nloptAlg(localmethod_),
+ sizeof...(Args));
+ localopt.set_lower_bounds(lower_bounds_);
+ localopt.set_upper_bounds(upper_bounds_);
+ opt_.set_local_optimizer(localopt);
+ default: ;
+ }
+
+ 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 );
+
+ // Take care of the initial values, copy them to initvals_
+ metaloop::apply(InitValFunc(*this), initvals);
+
+ switch(dir_) {
+ case OptDir::MIN:
+ opt_.set_min_objective(optfunc<Func, Args...>, &func); break;
+ case OptDir::MAX:
+ opt_.set_max_objective(optfunc<Func, Args...>, &func); break;
+ }
+
+ Result<Args...> result;
+
+ auto rescode = opt_.optimize(initvals_, result.score);
+ result.resultcode = static_cast<ResultCodes>(rescode);
+
+ metaloop::apply(ResultCopyFunc(*this), result.optimum);
+
+ return result;
+ }
+
+public:
+ inline explicit NloptOptimizer(nlopt::algorithm alg,
+ StopCriteria stopcr = {}):
+ Base(stopcr), alg_(alg), localmethod_(Method::L_SIMPLEX) {}
+
+};
+
+}
+}
+
+
+#endif // NLOPT_BOILERPLATE_HPP
diff --git a/xs/src/libnest2d/libnest2d/optimizers/simplex.hpp b/xs/src/libnest2d/libnest2d/optimizers/simplex.hpp
new file mode 100644
index 000000000..78b09b89a
--- /dev/null
+++ b/xs/src/libnest2d/libnest2d/optimizers/simplex.hpp
@@ -0,0 +1,20 @@
+#ifndef SIMPLEX_HPP
+#define SIMPLEX_HPP
+
+#include "nlopt_boilerplate.hpp"
+
+namespace libnest2d { namespace opt {
+
+class SimplexOptimizer: public NloptOptimizer {
+public:
+ inline explicit SimplexOptimizer(const StopCriteria& scr = {}):
+ NloptOptimizer(method2nloptAlg(Method::L_SIMPLEX), scr) {}
+};
+
+template<>
+struct OptimizerSubclass<Method::L_SIMPLEX> { using Type = SimplexOptimizer; };
+
+}
+}
+
+#endif // SIMPLEX_HPP
diff --git a/xs/src/libnest2d/libnest2d/optimizers/subplex.hpp b/xs/src/libnest2d/libnest2d/optimizers/subplex.hpp
new file mode 100644
index 000000000..841b04057
--- /dev/null
+++ b/xs/src/libnest2d/libnest2d/optimizers/subplex.hpp
@@ -0,0 +1,20 @@
+#ifndef SUBPLEX_HPP
+#define SUBPLEX_HPP
+
+#include "nlopt_boilerplate.hpp"
+
+namespace libnest2d { namespace opt {
+
+class SubplexOptimizer: public NloptOptimizer {
+public:
+ inline explicit SubplexOptimizer(const StopCriteria& scr = {}):
+ NloptOptimizer(method2nloptAlg(Method::L_SUBPLEX), scr) {}
+};
+
+template<>
+struct OptimizerSubclass<Method::L_SUBPLEX> { using Type = SubplexOptimizer; };
+
+}
+}
+
+#endif // SUBPLEX_HPP
diff --git a/xs/src/libnest2d/libnest2d/placers/bottomleftplacer.hpp b/xs/src/libnest2d/libnest2d/placers/bottomleftplacer.hpp
new file mode 100644
index 000000000..18c27c40c
--- /dev/null
+++ b/xs/src/libnest2d/libnest2d/placers/bottomleftplacer.hpp
@@ -0,0 +1,412 @@
+#ifndef BOTTOMLEFT_HPP
+#define BOTTOMLEFT_HPP
+
+#include <limits>
+
+#include "placer_boilerplate.hpp"
+
+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 {
+ DECLARE_MAIN_TYPES(RawShape);
+
+ Coord min_obj_distance = 0;
+ Coord epsilon = Epsilon<Coord>::Value;
+ bool allow_rotations = false;
+};
+
+template<class RawShape>
+class _BottomLeftPlacer: public PlacerBoilerplate<
+ _BottomLeftPlacer<RawShape>,
+ RawShape, _Box<TPoint<RawShape>>,
+ BLConfig<RawShape> >
+{
+ using Base = PlacerBoilerplate<_BottomLeftPlacer<RawShape>, RawShape,
+ _Box<TPoint<RawShape>>, BLConfig<RawShape>>;
+ DECLARE_PLACER(Base)
+
+public:
+
+ explicit _BottomLeftPlacer(const BinType& bin): Base(bin) {}
+
+ 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);
+ }
+ return r;
+ }
+
+ enum class Dir {
+ LEFT,
+ DOWN
+ };
+
+ inline RawShape leftPoly(const Item& item) const {
+ return toWallPoly(item, Dir::LEFT);
+ }
+
+ inline RawShape downPoly(const Item& item) const {
+ return toWallPoly(item, Dir::DOWN);
+ }
+
+ inline Unit availableSpaceLeft(const Item& item) {
+ return availableSpace(item, Dir::LEFT);
+ }
+
+ inline Unit availableSpaceDown(const Item& item) {
+ return availableSpace(item, Dir::DOWN);
+ }
+
+protected:
+
+ PackResult _trypack(Item& item) {
+
+ // Get initial position for item in the top right corner
+ setInitialPosition(item);
+
+ Unit d = availableSpaceDown(item);
+ 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+eps});
+ d = availableSpaceLeft(item);
+ can_move = d > eps;
+ left = false;
+ } else { // write previous left move and go down
+ item.translate({-d+eps, 0});
+ d = availableSpaceDown(item);
+ can_move = d > eps;
+ left = true;
+ }
+ }
+
+ if(can_be_packed) {
+ Item trsh(item.transformedShape());
+ for(auto& v : trsh) can_be_packed = can_be_packed &&
+ getX(v) < bin_.width() &&
+ getY(v) < bin_.height();
+ }
+
+ return can_be_packed? PackResult(item) : PackResult();
+ }
+
+ void setInitialPosition(Item& item) {
+ auto bb = item.boundingBox();
+
+ Vertex v = { getX(bb.maxCorner()), getY(bb.minCorner()) };
+
+
+ Coord dx = getX(bin_.maxCorner()) - getX(v);
+ Coord dy = getY(bin_.maxCorner()) - getY(v);
+
+ item.translate({dx, dy});
+ }
+
+ template<class C = Coord>
+ static enable_if_t<std::is_floating_point<C>::value, bool>
+ isInTheWayOf( const Item& item,
+ const Item& other,
+ const RawShape& scanpoly)
+ {
+ auto tsh = other.transformedShape();
+ return ( sl::intersects(tsh, scanpoly) ||
+ sl::isInside(tsh, scanpoly) ) &&
+ ( !sl::intersects(tsh, item.rawShape()) &&
+ !sl::isInside(tsh, item.rawShape()) );
+ }
+
+ template<class C = Coord>
+ static enable_if_t<std::is_integral<C>::value, bool>
+ isInTheWayOf( const Item& item,
+ const Item& other,
+ const RawShape& scanpoly)
+ {
+ auto tsh = other.transformedShape();
+
+ 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 ||
+ sl::isInside(tsh, scanpoly)) &&
+ ( !inters_item &&
+ !sl::isInside(tsh, item.rawShape())
+ );
+ }
+
+ 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);
+
+ 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
+ auto predicate = [&scanpoly, &item](const Item& it) {
+ return isInTheWayOf(item, it, scanpoly);
+ };
+
+ // Get the items that are in the way for the left (or down) movement
+ std::copy_if(items_.begin(), items_.end(),
+ std::back_inserter(ret), predicate);
+
+ return ret;
+ }
+
+ Unit availableSpace(const Item& _item, const Dir dir) {
+
+ Item item (_item.transformedShape());
+
+
+ std::function<Coord(const Vertex&)> getCoord;
+ std::function< std::pair<Coord, bool>(const Segment&, const Vertex&) >
+ availableDistanceSV;
+
+ std::function< std::pair<Coord, bool>(const Vertex&, const Segment&) >
+ availableDistance;
+
+ if(dir == Dir::LEFT) {
+ getCoord = [](const Vertex& v) { return getX(v); };
+ availableDistance = pointlike::horizontalDistance<Vertex>;
+ availableDistanceSV = [](const Segment& s, const Vertex& v) {
+ 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>;
+ availableDistanceSV = [](const Segment& s, const Vertex& v) {
+ auto ret = pointlike::verticalDistance<Vertex>(v, s);
+ if(ret.second) ret.first = -ret.first;
+ return ret;
+ };
+ }
+
+ auto&& items_in_the_way = itemsInTheWayOf(item, dir);
+
+ // Comparison function for finding min vertex
+ auto cmp = [&getCoord](const Vertex& v1, const Vertex& v2) {
+ return getCoord(v1) < getCoord(v2);
+ };
+
+ // find minimum left or down coordinate of item
+ auto minvertex_it = std::min_element(item.begin(),
+ item.end(),
+ cmp);
+
+ // Get the initial distance in floating point
+ Unit m = getCoord(*minvertex_it);
+
+ // Check available distance for every vertex of item to the objects
+ // in the way for the nearest intersection
+ if(!items_in_the_way.empty()) { // This is crazy, should be optimized...
+ for(Item& pleft : items_in_the_way) {
+ // For all segments in items_to_left
+
+ assert(pleft.vertexCount() > 0);
+
+ auto trpleft = pleft.transformedShape();
+ auto first = sl::begin(trpleft);
+ auto next = first + 1;
+ auto endit = sl::end(trpleft);
+
+ while(next != endit) {
+ Segment seg(*(first++), *(next++));
+ for(auto& v : item) { // For all vertices in item
+
+ auto d = availableDistance(v, seg);
+
+ if(d.second && d.first < m) m = d.first;
+ }
+ }
+ }
+
+ auto first = item.begin();
+ auto next = first + 1;
+ auto endit = item.end();
+
+ // For all edges in item:
+ while(next != endit) {
+ Segment seg(*(first++), *(next++));
+
+ // for all shapes in items_to_left
+ for(Item& sh : items_in_the_way) {
+ assert(sh.vertexCount() > 0);
+
+ Item tsh(sh.transformedShape());
+ for(auto& v : tsh) { // For all vertices in item
+
+ auto d = availableDistanceSV(seg, v);
+
+ if(d.second && d.first < m) m = d.first;
+ }
+ }
+ }
+ }
+
+ return m;
+ }
+
+ /**
+ * Implementation of the left (and down) polygon as described by
+ * [López-Camacho et al. 2013]\
+ * (http://www.cs.stir.ac.uk/~goc/papers/EffectiveHueristic2DAOR2013.pdf)
+ * see algorithm 8 for details...
+ */
+ RawShape toWallPoly(const Item& _item, const Dir dir) const {
+ // The variable names reflect the case of left polygon calculation.
+ //
+ // We will iterate through the item's vertices and search for the top
+ // and bottom vertices (or right and left if dir==Dir::DOWN).
+ // Save the relevant vertices and their indices into `bottom` and
+ // `top` vectors. In case of left polygon construction these will
+ // contain the top and bottom polygons which have the same vertical
+ // coordinates (in case there is more of them).
+ //
+ // We get the leftmost (or downmost) vertex from the `bottom` and `top`
+ // vectors and construct the final polygon.
+
+ Item item (_item.transformedShape());
+
+ auto getCoord = [dir](const Vertex& v) {
+ return dir == Dir::LEFT? getY(v) : getX(v);
+ };
+
+ Coord max_y = std::numeric_limits<Coord>::min();
+ Coord min_y = std::numeric_limits<Coord>::max();
+
+ using El = std::pair<size_t, std::reference_wrapper<const Vertex>>;
+
+ std::function<bool(const El&, const El&)> cmp;
+
+ if(dir == Dir::LEFT)
+ cmp = [](const El& e1, const El& e2) {
+ return getX(e1.second.get()) < getX(e2.second.get());
+ };
+ else
+ cmp = [](const El& e1, const El& e2) {
+ return getY(e1.second.get()) < getY(e2.second.get());
+ };
+
+ std::vector< El > top;
+ std::vector< El > bottom;
+
+ size_t idx = 0;
+ for(auto& v : item) { // Find the bottom and top vertices and save them
+ auto vref = std::cref(v);
+ auto vy = getCoord(v);
+
+ if( vy > max_y ) {
+ max_y = vy;
+ top.clear();
+ top.emplace_back(idx, vref);
+ }
+ else if(vy == max_y) { top.emplace_back(idx, vref); }
+
+ if(vy < min_y) {
+ min_y = vy;
+ bottom.clear();
+ bottom.emplace_back(idx, vref);
+ }
+ else if(vy == min_y) { bottom.emplace_back(idx, vref); }
+
+ idx++;
+ }
+
+ // Get the top and bottom leftmost vertices, or the right and left
+ // downmost vertices (if dir == Dir::DOWN)
+ auto topleft_it = std::min_element(top.begin(), top.end(), cmp);
+ auto bottomleft_it =
+ std::min_element(bottom.begin(), bottom.end(), cmp);
+
+ auto& topleft_vertex = topleft_it->second.get();
+ auto& bottomleft_vertex = bottomleft_it->second.get();
+
+ // Start and finish positions for the vertices that will be part of the
+ // new polygon
+ auto start = std::min(topleft_it->first, bottomleft_it->first);
+ auto finish = std::max(topleft_it->first, bottomleft_it->first);
+
+ // the return shape
+ RawShape rsh;
+
+ // reserve for all vertices plus 2 for the left horizontal wall, 2 for
+ // the additional vertices for maintaning min object distance
+ sl::reserve(rsh, finish-start+4);
+
+ /*auto addOthers = [&rsh, finish, start, &item](){
+ for(size_t i = start+1; i < finish; i++)
+ sl::addVertex(rsh, item.vertex(i));
+ };*/
+
+ auto reverseAddOthers = [&rsh, finish, start, &item](){
+ for(auto i = finish-1; i > start; i--)
+ sl::addVertex(rsh, item.vertex(
+ static_cast<unsigned long>(i)));
+ };
+
+ // Final polygon construction...
+
+ static_assert(OrientationType<RawShape>::Value ==
+ Orientation::CLOCKWISE,
+ "Counter clockwise toWallPoly() Unimplemented!");
+
+ // Clockwise polygon construction
+
+ sl::addVertex(rsh, topleft_vertex);
+
+ if(dir == Dir::LEFT) reverseAddOthers();
+ else {
+ sl::addVertex(rsh, getX(topleft_vertex), 0);
+ sl::addVertex(rsh, getX(bottomleft_vertex), 0);
+ }
+
+ sl::addVertex(rsh, bottomleft_vertex);
+
+ if(dir == Dir::LEFT) {
+ sl::addVertex(rsh, 0, getY(bottomleft_vertex));
+ sl::addVertex(rsh, 0, getY(topleft_vertex));
+ }
+ else reverseAddOthers();
+
+
+ // Close the polygon
+ sl::addVertex(rsh, topleft_vertex);
+
+ return rsh;
+ }
+
+};
+
+}
+}
+
+#endif //BOTTOMLEFT_HPP
diff --git a/xs/src/libnest2d/libnest2d/placers/nfpplacer.hpp b/xs/src/libnest2d/libnest2d/placers/nfpplacer.hpp
new file mode 100644
index 000000000..c86fb507e
--- /dev/null
+++ b/xs/src/libnest2d/libnest2d/placers/nfpplacer.hpp
@@ -0,0 +1,1205 @@
+#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 "tools/svgtools.hpp"
+
+#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,
+ BOTTOM_RIGHT,
+ TOP_LEFT,
+ TOP_RIGHT,
+ };
+
+ /// Which angles to try out for better results.
+ std::vector<Radians> rotations;
+
+ /// Where to align the resulting packed pile.
+ Alignment alignment;
+
+ /// Where to start putting objects in the bin.
+ Alignment starting_point;
+
+ /**
+ * @brief A function object representing the fitting function in the
+ * placement optimization process. (Optional)
+ *
+ * This is the most versatile tool to configure the placer. The fitting
+ * function is evaluated many times when a new item is being placed into the
+ * bin. The output should be a rated score of the new item's position.
+ *
+ * This is not a mandatory option as there is a default fitting function
+ * 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 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(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 = 0.65f;
+
+ /**
+ * @brief If you want to see items inside other item's holes, you have to
+ * turn this switch on.
+ *
+ * This will only work if a suitable nfp implementation is provided.
+ * The library has no such implementation right now.
+ */
+ 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) {}
+};
+
+/**
+ * A class for getting a point on the circumference of the polygon (in log time)
+ *
+ * This is a transformation of the provided polygon to be able to pinpoint
+ * locations on the circumference. The optimizer will pass a floating point
+ * value e.g. within <0,1> and we have to transform this value quickly into a
+ * coordinate on the circumference. By definition 0 should yield the first
+ * vertex and 1.0 would be the last (which should coincide with first).
+ *
+ * We also have to make this work for the holes of the captured polygon.
+ */
+template<class RawShape> class EdgeCache {
+ using Vertex = TPoint<RawShape>;
+ using Coord = TCoord<Vertex>;
+ using Edge = _Segment<Vertex>;
+
+ struct ContourCache {
+ mutable std::vector<double> corners;
+ std::vector<Edge> emap;
+ std::vector<double> distances;
+ double full_distance = 0;
+ } contour_;
+
+ std::vector<ContourCache> holes_;
+
+ double accuracy_ = 1.0;
+
+ void createCache(const RawShape& sh) {
+ { // For the contour
+ auto first = shapelike::cbegin(sh);
+ auto next = std::next(first);
+ auto endit = shapelike::cend(sh);
+
+ contour_.distances.reserve(shapelike::contourVertexCount(sh));
+
+ while(next != endit) {
+ contour_.emap.emplace_back(*(first++), *(next++));
+ contour_.full_distance += contour_.emap.back().length();
+ contour_.distances.push_back(contour_.full_distance);
+ }
+ }
+
+ for(auto& h : shapelike::holes(sh)) { // For the holes
+ auto first = h.begin();
+ auto next = std::next(first);
+ auto endit = h.end();
+
+ ContourCache hc;
+ hc.distances.reserve(endit - first);
+
+ while(next != endit) {
+ hc.emap.emplace_back(*(first++), *(next++));
+ hc.full_distance += hc.emap.back().length();
+ hc.distances.push_back(hc.full_distance);
+ }
+
+ holes_.push_back(hc);
+ }
+ }
+
+ size_t stride(const size_t N) const {
+ using std::round;
+ using std::pow;
+
+ return static_cast<Coord>(
+ round(N/pow(N, pow(accuracy_, 1.0/3.0)))
+ );
+ }
+
+ void fetchCorners() const {
+ if(!contour_.corners.empty()) return;
+
+ const auto N = contour_.distances.size();
+ 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) {
+ contour_.corners.emplace_back(
+ contour_.distances.at(i) / contour_.full_distance);
+ }
+ }
+
+ void fetchHoleCorners(unsigned hidx) const {
+ auto& hc = holes_[hidx];
+ if(!hc.corners.empty()) return;
+
+ const auto N = hc.distances.size();
+ 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) {
+ hc.corners.emplace_back(
+ hc.distances.at(i) / hc.full_distance);
+ }
+ }
+
+ inline Vertex coords(const ContourCache& cache, double distance) const {
+ assert(distance >= .0 && distance <= 1.0);
+
+ // distance is from 0.0 to 1.0, we scale it up to the full length of
+ // the circumference
+ double d = distance*cache.full_distance;
+
+ auto& distances = cache.distances;
+
+ // Magic: we find the right edge in log time
+ auto it = std::lower_bound(distances.begin(), distances.end(), d);
+ auto idx = it - distances.begin(); // get the index of the edge
+ auto edge = cache.emap[idx]; // extrac the edge
+
+ // Get the remaining distance on the target edge
+ auto ed = d - (idx > 0 ? *std::prev(it) : 0 );
+ auto angle = edge.angleToXaxis();
+ Vertex ret = edge.first();
+
+ // Get the point on the edge which lies in ed distance from the start
+ ret += { static_cast<Coord>(std::round(ed*std::cos(angle))),
+ static_cast<Coord>(std::round(ed*std::sin(angle))) };
+
+ return ret;
+ }
+
+public:
+
+ using iterator = std::vector<double>::iterator;
+ using const_iterator = std::vector<double>::const_iterator;
+
+ inline EdgeCache() = default;
+
+ inline EdgeCache(const _Item<RawShape>& item)
+ {
+ createCache(item.transformedShape());
+ }
+
+ inline EdgeCache(const RawShape& sh)
+ {
+ createCache(sh);
+ }
+
+ /// Resolution of returned corners. The stride is derived from this value.
+ void accuracy(double a /* within <0.0, 1.0>*/) { accuracy_ = a; }
+
+ /**
+ * @brief Get a point on the circumference of a polygon.
+ * @param distance A relative distance from the starting point to the end.
+ * Can be from 0.0 to 1.0 where 0.0 is the starting point and 1.0 is the
+ * closing point (which should be eqvivalent with the starting point with
+ * closed polygons).
+ * @return Returns the coordinates of the point lying on the polygon
+ * circumference.
+ */
+ inline Vertex coords(double distance) const {
+ return coords(contour_, distance);
+ }
+
+ inline Vertex coords(unsigned hidx, double distance) const {
+ assert(hidx < holes_.size());
+ return coords(holes_[hidx], distance);
+ }
+
+ inline double circumference() const BP2D_NOEXCEPT {
+ return contour_.full_distance;
+ }
+
+ inline double circumference(unsigned hidx) const BP2D_NOEXCEPT {
+ return holes_[hidx].full_distance;
+ }
+
+ /// Get the normalized distance values for each vertex
+ inline const std::vector<double>& corners() const BP2D_NOEXCEPT {
+ fetchCorners();
+ return contour_.corners;
+ }
+
+ /// corners for a specific hole
+ inline const std::vector<double>&
+ corners(unsigned holeidx) const BP2D_NOEXCEPT {
+ fetchHoleCorners(holeidx);
+ return holes_[holeidx].corners;
+ }
+
+ /// The number of holes in the abstracted polygon
+ inline size_t holeCount() const BP2D_NOEXCEPT { return holes_.size(); }
+
+};
+
+template<nfp::NfpLevel lvl>
+struct Lvl { static const nfp::NfpLevel value = lvl; };
+
+template<class RawShape>
+inline void correctNfpPosition(nfp::NfpResult<RawShape>& nfp,
+ const _Item<RawShape>& stationary,
+ const _Item<RawShape>& orbiter)
+{
+ // The provided nfp is somewhere in the dark. We need to get it
+ // to the right position around the stationary shape.
+ // This is done by choosing the leftmost lowest vertex of the
+ // orbiting polygon to be touched with the rightmost upper
+ // vertex of the stationary polygon. In this configuration, the
+ // reference vertex of the orbiting polygon (which can be dragged around
+ // the nfp) will be its rightmost upper vertex that coincides with the
+ // rightmost upper vertex of the nfp. No proof provided other than Jonas
+ // Lindmark's reasoning about the reference vertex of nfp in his thesis
+ // ("No fit polygon problem" - section 2.1.9)
+
+ auto touch_sh = stationary.rightmostTopVertex();
+ auto touch_other = orbiter.leftmostBottomVertex();
+ 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);
+}
+
+template<class RawShape>
+inline void correctNfpPosition(nfp::NfpResult<RawShape>& nfp,
+ const RawShape& stationary,
+ const _Item<RawShape>& orbiter)
+{
+ 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 Circle = _Circle<TPoint<RawShape>> >
+Circle minimizeCircle(const RawShape& sh) {
+ using Point = TPoint<RawShape>;
+ using Coord = TCoord<Point>;
+
+ auto& ctr = sl::getContour(sh);
+ if(ctr.empty()) return {{0, 0}, 0};
+
+ auto bb = sl::boundingBox(sh);
+ auto capprx = bb.center();
+ auto rapprx = pl::distance(bb.minCorner(), bb.maxCorner());
+
+
+ opt::StopCriteria stopcr;
+ stopcr.max_iterations = 30;
+ stopcr.relative_score_difference = 1e-3;
+ opt::TOptimizer<opt::Method::L_SUBPLEX> solver(stopcr);
+
+ 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) );
+
+ Point centr(xt, yt);
+
+ unsigned i = 0;
+ for(auto v : ctr) {
+ dists[i++] = pl::distance(v, centr);
+ }
+
+ auto mit = std::max_element(dists.begin(), dists.end());
+
+ assert(mit != dists.end());
+
+ 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) );
+
+ Point cc(xt, yt);
+ auto r = result.score;
+
+ return {cc, r};
+}
+
+template<class RawShape>
+_Circle<TPoint<RawShape>> boundingCircle(const RawShape& sh) {
+ return minimizeCircle(sh);
+}
+
+template<class RawShape, class TBin = _Box<TPoint<RawShape>>>
+class _NofitPolyPlacer: public PlacerBoilerplate<_NofitPolyPlacer<RawShape, TBin>,
+ RawShape, TBin, NfpPConfig<RawShape>> {
+
+ using Base = PlacerBoilerplate<_NofitPolyPlacer<RawShape, TBin>,
+ RawShape, TBin, NfpPConfig<RawShape>>;
+
+ DECLARE_PLACER(Base)
+
+ 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_;
+
+ // Caching calculated nfps
+ __itemhash::Hash<RawShape> nfpcache_;
+
+ // Storing item hash keys
+ ItemKeys item_keys_;
+
+public:
+
+ using Pile = nfp::Shapes<RawShape>;
+
+ inline explicit _NofitPolyPlacer(const BinType& bin):
+ Base(bin),
+ norm_(std::sqrt(sl::area(bin))) {}
+
+ _NofitPolyPlacer(const _NofitPolyPlacer&) = default;
+ _NofitPolyPlacer& operator=(const _NofitPolyPlacer&) = default;
+
+#ifndef BP2D_COMPILER_MSVC12 // MSVC2013 does not support default move ctors
+ _NofitPolyPlacer(_NofitPolyPlacer&&) BP2D_NOEXCEPT = default;
+ _NofitPolyPlacer& operator=(_NofitPolyPlacer&&) BP2D_NOEXCEPT = default;
+#endif
+
+ 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(rect.transformedShape(), bin) ? -1.0 : 1;
+ }
+
+ 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(chullcpy, bin) ? -1.0 : 1.0;
+ }
+
+ static inline double overfit(const RawShape& chull, const Box& bin)
+ {
+ auto bbch = sl::boundingBox(chull);
+ return overfit(bbch, bin);
+ }
+
+ static inline double overfit(const Box& bb, const Box& bin)
+ {
+ 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;
+ }
+
+ static inline double overfit(const Box& bb, const _Circle<Vertex>& bin)
+ {
+ double boxr = 0.5*pl::distance(bb.minCorner(), bb.maxCorner());
+ double diff = boxr - bin.radius();
+ return diff;
+ }
+
+ static inline double overfit(const RawShape& chull,
+ const _Circle<Vertex>& 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);
+ }
+
+ 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);
+ best_overfit = overfit(item.transformedShape(), bin_);
+ can_pack = best_overfit <= 0;
+ } else {
+
+ 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;
+ 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 disjunct from the current merged pile
+ placeOutsideOfBin(item);
+
+ nfps = calcnfp({item, itemhash}, Lvl<MaxNfpLevel::value>());
+
+ auto iv = item.referenceVertex();
+
+ auto startpos = item.translation();
+
+ std::vector<Edges> ecache;
+ ecache.reserve(nfps.size());
+
+ for(auto& nfp : nfps ) {
+ ecache.emplace_back(nfp);
+ ecache.back().accuracy(config_.accuracy);
+ }
+
+ Shapes pile;
+ pile.reserve(items_.size()+1);
+ // double pile_area = 0;
+ for(Item& mitem : items_) {
+ pile.emplace_back(mitem.transformedShape());
+ // pile_area += mitem.area();
+ }
+
+ 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 :
+ [norm, bin, binbb, pbb](const Item& item)
+ {
+ auto ibb = item.boundingBox();
+ auto fullbb = boundingBox(pbb, ibb);
+
+ double score = pl::distance(ibb.center(), binbb.center());
+ 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 =
+ [_objfunc, iv, startpos] (Vertex v, Item& itm)
+ {
+ auto d = v - iv;
+ d += startpos;
+ itm.translation(d);
+ return _objfunc(itm);
+ };
+
+ 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 =
+ [&merged_pile, &getNfpPoint, &item, &bin, &iv, &startpos]
+ (const Optimum& o)
+ {
+ auto v = getNfpPoint(o);
+ auto d = v - iv;
+ d += startpos;
+ item.translation(d);
+
+ merged_pile.emplace_back(item.transformedShape());
+ auto chull = sl::convexHull(merged_pile);
+ merged_pile.pop_back();
+
+ return overfit(chull, bin);
+ };
+
+ Optimum optimum(0, 0);
+ 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];
+
+ OptResults results(cache.corners().size());
+
+ auto& rofn = rawobjfunc;
+ auto& nfpoint = getNfpPoint;
+
+ __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 {
+ results[n] = solver.optimize_min(contour_ofn,
+ opt::initvals<double>(pos),
+ opt::bound<double>(0, 1.0)
+ );
+ } 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) {
+ results.clear();
+ results.resize(cache.corners(hidx).size());
+
+ // TODO : use parallel for
+ __parallel::enumerate(cache.corners(hidx).begin(),
+ cache.corners(hidx).end(),
+ [&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 {
+ results[n] = solver.optimize_min(hole_ofn,
+ opt::initvals<double>(pos),
+ opt::bound<double>(0, 1.0)
+ );
+
+ } 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);
+ }
+ }
+ }
+ }
+
+ if( best_score < global_score ) {
+ auto d = getNfpPoint(optimum) - iv;
+ d += startpos;
+ final_tr = d;
+ final_rot = initial_rot + rot;
+ can_pack = true;
+ global_score = best_score;
+ }
+ }
+
+ item.translation(final_tr);
+ item.rotation(final_rot);
+ }
+
+ if(can_pack) {
+ ret = PackResult(item);
+ item_keys_.emplace_back(itemhash);
+ } else {
+ ret = PackResult(best_overfit);
+ }
+
+ return ret;
+ }
+
+ inline void finalAlign(const RawShape& pbin) {
+ auto bbin = sl::boundingBox(pbin);
+ finalAlign(bbin);
+ }
+
+ 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(m);
+
+ Vertex ci, cb;
+
+ switch(config_.alignment) {
+ case Config::Alignment::CENTER: {
+ ci = bb.center();
+ cb = bbin.center();
+ break;
+ }
+ case Config::Alignment::BOTTOM_LEFT: {
+ ci = bb.minCorner();
+ cb = bbin.minCorner();
+ break;
+ }
+ case Config::Alignment::BOTTOM_RIGHT: {
+ ci = {getX(bb.maxCorner()), getY(bb.minCorner())};
+ cb = {getX(bbin.maxCorner()), getY(bbin.minCorner())};
+ break;
+ }
+ case Config::Alignment::TOP_LEFT: {
+ ci = {getX(bb.minCorner()), getY(bb.maxCorner())};
+ cb = {getX(bbin.minCorner()), getY(bbin.maxCorner())};
+ break;
+ }
+ case Config::Alignment::TOP_RIGHT: {
+ ci = bb.maxCorner();
+ cb = bbin.maxCorner();
+ break;
+ }
+ }
+
+ auto d = cb - ci;
+ for(Item& item : items_) item.translate(d);
+ }
+
+ void setInitialPosition(Item& item) {
+ Box&& bb = item.boundingBox();
+ Vertex ci, cb;
+ auto bbin = sl::boundingBox(bin_);
+
+ switch(config_.starting_point) {
+ case Config::Alignment::CENTER: {
+ ci = bb.center();
+ cb = bbin.center();
+ break;
+ }
+ case Config::Alignment::BOTTOM_LEFT: {
+ ci = bb.minCorner();
+ cb = bbin.minCorner();
+ break;
+ }
+ case Config::Alignment::BOTTOM_RIGHT: {
+ ci = {getX(bb.maxCorner()), getY(bb.minCorner())};
+ cb = {getX(bbin.maxCorner()), getY(bbin.minCorner())};
+ break;
+ }
+ case Config::Alignment::TOP_LEFT: {
+ ci = {getX(bb.minCorner()), getY(bb.maxCorner())};
+ cb = {getX(bbin.minCorner()), getY(bbin.maxCorner())};
+ break;
+ }
+ case Config::Alignment::TOP_RIGHT: {
+ ci = bb.maxCorner();
+ cb = bbin.maxCorner();
+ break;
+ }
+ }
+
+ auto d = cb - ci;
+ item.translate(d);
+ }
+
+ void placeOutsideOfBin(Item& item) {
+ auto&& bb = item.boundingBox();
+ Box binbb = sl::boundingBox(bin_);
+
+ Vertex v = { getX(bb.maxCorner()), getY(bb.minCorner()) };
+
+ Coord dx = getX(binbb.maxCorner()) - getX(v);
+ Coord dy = getY(binbb.maxCorner()) - getY(v);
+
+ item.translate({dx, dy});
+ }
+
+};
+
+
+}
+}
+
+#endif // NOFITPOLY_H
diff --git a/xs/src/libnest2d/libnest2d/placers/placer_boilerplate.hpp b/xs/src/libnest2d/libnest2d/placers/placer_boilerplate.hpp
new file mode 100644
index 000000000..0df1b8c91
--- /dev/null
+++ b/xs/src/libnest2d/libnest2d/placers/placer_boilerplate.hpp
@@ -0,0 +1,134 @@
+#ifndef PLACER_BOILERPLATE_HPP
+#define PLACER_BOILERPLATE_HPP
+
+#include "../libnest2d.hpp"
+
+namespace libnest2d { namespace placers {
+
+struct EmptyConfig {};
+
+template<class Subclass, class RawShape, class TBin, class Cfg = EmptyConfig>
+class PlacerBoilerplate {
+ mutable bool farea_valid_ = false;
+ mutable double farea_ = 0.0;
+public:
+ using Item = _Item<RawShape>;
+ using Vertex = TPoint<RawShape>;
+ using Segment = _Segment<Vertex>;
+ using BinType = TBin;
+ using Coord = TCoord<Vertex>;
+ using Unit = Coord;
+ using Config = Cfg;
+ 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(double overfit = 1.0):
+ item_ptr_(nullptr), overfit_(overfit) {}
+
+ public:
+ operator bool() { return item_ptr_ != nullptr; }
+ double overfit() const { return overfit_; }
+ };
+
+ inline PlacerBoilerplate(const BinType& bin, unsigned cap = 50): bin_(bin)
+ {
+ items_.reserve(cap);
+ }
+
+ inline const BinType& bin() const BP2D_NOEXCEPT { return bin_; }
+
+ template<class TB> inline void bin(TB&& b) {
+ bin_ = std::forward<BinType>(b);
+ }
+
+ inline void configure(const Config& config) BP2D_NOEXCEPT {
+ config_ = config;
+ }
+
+ 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;
+ }
+ return r;
+ }
+
+ void accept(PackResult& r) {
+ if(r) {
+ r.item_ptr_->translation(r.move_);
+ r.item_ptr_->rotation(r.rot_);
+ items_.push_back(*(r.item_ptr_));
+ farea_valid_ = false;
+ }
+ }
+
+ void unpackLast() {
+ items_.pop_back();
+ farea_valid_ = false;
+ }
+
+ inline const ItemGroup& getItems() const { return items_; }
+
+ inline void clearItems() {
+ items_.clear();
+ farea_valid_ = false;
+ }
+
+ inline double filledArea() const {
+ if(farea_valid_) return farea_;
+ else {
+ farea_ = .0;
+ std::for_each(items_.begin(), items_.end(),
+ [this] (Item& item) {
+ farea_ += item.area();
+ });
+ farea_valid_ = true;
+ }
+
+ return farea_;
+ }
+
+protected:
+
+ BinType bin_;
+ ItemGroup items_;
+ Cfg config_;
+};
+
+
+#define DECLARE_PLACER(Base) \
+using Base::bin_; \
+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; \
+using typename Base::Segment; \
+using typename Base::PackResult; \
+using typename Base::Coord; \
+using typename Base::Unit; \
+private:
+
+}
+}
+
+#endif // PLACER_BOILERPLATE_HPP
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
new file mode 100644
index 000000000..ee93d0592
--- /dev/null
+++ b/xs/src/libnest2d/libnest2d/selections/djd_heuristic.hpp
@@ -0,0 +1,717 @@
+#ifndef DJD_HEURISTIC_HPP
+#define DJD_HEURISTIC_HPP
+
+#include <list>
+#include <future>
+#include <atomic>
+#include <functional>
+
+#include "selection_boilerplate.hpp"
+
+namespace libnest2d { namespace selections {
+
+/**
+ * Selection heuristic based on [López-Camacho]\
+ * (http://www.cs.stir.ac.uk/~goc/papers/EffectiveHueristic2DAOR2013.pdf)
+ */
+template<class RawShape>
+class _DJDHeuristic: public SelectionBoilerplate<RawShape> {
+ using Base = SelectionBoilerplate<RawShape>;
+
+ class SpinLock {
+ std::atomic_flag& lck_;
+ public:
+
+ inline SpinLock(std::atomic_flag& flg): lck_(flg) {}
+
+ inline void lock() {
+ while(lck_.test_and_set(std::memory_order_acquire)) {}
+ }
+
+ inline void unlock() { lck_.clear(std::memory_order_release); }
+ };
+
+public:
+ using typename Base::Item;
+ using typename Base::ItemRef;
+
+ /**
+ * @brief The Config for DJD heuristic.
+ */
+ struct Config {
+
+ /**
+ * If true, the algorithm will try to place pair and triplets in all
+ * possible order. It will have a hugely negative impact on performance.
+ */
+ bool try_reverse_order = true;
+
+ /**
+ * @brief try_pairs Whether to try pairs of items to pack. It will add
+ * a quadratic component to the complexity.
+ */
+ bool try_pairs = true;
+
+ /**
+ * @brief Whether to try groups of 3 items to pack. This could be very
+ * slow for large number of items (>100) as it adds a cubic component
+ * to the complexity.
+ */
+ bool try_triplets = false;
+
+ /**
+ * The initial fill proportion of the bin area that will be filled before
+ * trying items one by one, or pairs or triplets.
+ *
+ * The initial fill proportion suggested by
+ * [López-Camacho]\
+ * (http://www.cs.stir.ac.uk/~goc/papers/EffectiveHueristic2DAOR2013.pdf)
+ * is one third of the area of bin.
+ */
+ double initial_fill_proportion = 1.0/3.0;
+
+ /**
+ * @brief How much is the acceptable waste incremented at each iteration
+ */
+ double waste_increment = 0.1;
+
+ /**
+ * @brief Allow parallel jobs for filling multiple bins.
+ *
+ * This will decrease the soution quality but can greatly boost up
+ * performance for large number of items.
+ */
+ bool allow_parallel = true;
+
+ /**
+ * @brief Always use parallel processing if the items don't fit into
+ * one bin.
+ */
+ bool force_parallel = false;
+ };
+
+private:
+ using Base::packed_bins_;
+ using ItemGroup = typename Base::ItemGroup;
+
+ using Container = ItemGroup;
+ Container store_;
+ Config config_;
+
+ static const unsigned MAX_ITEMS_SEQUENTIALLY = 30;
+ static const unsigned MAX_VERTICES_SEQUENTIALLY = MAX_ITEMS_SEQUENTIALLY*20;
+
+public:
+
+ inline void configure(const Config& config) {
+ config_ = config;
+ }
+
+ template<class TPlacer, class TIterator,
+ class TBin = typename PlacementStrategyLike<TPlacer>::BinType,
+ class PConfig = typename PlacementStrategyLike<TPlacer>::Config>
+ void packItems( TIterator first,
+ TIterator last,
+ const TBin& bin,
+ PConfig&& pconfig = PConfig() )
+ {
+ using Placer = PlacementStrategyLike<TPlacer>;
+ using ItemList = std::list<ItemRef>;
+
+ const double bin_area = sl::area(bin);
+ const double w = bin_area * config_.waste_increment;
+
+ const double INITIAL_FILL_PROPORTION = config_.initial_fill_proportion;
+ const double INITIAL_FILL_AREA = bin_area*INITIAL_FILL_PROPORTION;
+
+ store_.clear();
+ store_.reserve(last-first);
+ packed_bins_.clear();
+
+ std::copy(first, last, std::back_inserter(store_));
+
+ std::sort(store_.begin(), store_.end(), [](Item& i1, Item& i2) {
+ return i1.area() > i2.area();
+ });
+
+ size_t glob_vertex_count = 0;
+ std::for_each(store_.begin(), store_.end(),
+ [&glob_vertex_count](const Item& item) {
+ glob_vertex_count += item.vertexCount();
+ });
+
+ std::vector<Placer> placers;
+
+ bool try_reverse = config_.try_reverse_order;
+
+ // Will use a subroutine to add a new bin
+ auto addBin = [this, &placers, &bin, &pconfig]()
+ {
+ placers.emplace_back(bin);
+ packed_bins_.emplace_back();
+ placers.back().configure(pconfig);
+ };
+
+ // Types for pairs and triplets
+ using TPair = std::tuple<ItemRef, ItemRef>;
+ using TTriplet = std::tuple<ItemRef, ItemRef, ItemRef>;
+
+
+ // Method for checking a pair whether it was a pack failure.
+ auto check_pair = [](const std::vector<TPair>& wrong_pairs,
+ ItemRef i1, ItemRef i2)
+ {
+ return std::any_of(wrong_pairs.begin(), wrong_pairs.end(),
+ [&i1, &i2](const TPair& pair)
+ {
+ Item& pi1 = std::get<0>(pair), &pi2 = std::get<1>(pair);
+ Item& ri1 = i1, &ri2 = i2;
+ return (&pi1 == &ri1 && &pi2 == &ri2) ||
+ (&pi1 == &ri2 && &pi2 == &ri1);
+ });
+ };
+
+ // Method for checking if a triplet was a pack failure
+ auto check_triplet = [](
+ const std::vector<TTriplet>& wrong_triplets,
+ ItemRef i1,
+ ItemRef i2,
+ ItemRef i3)
+ {
+ return std::any_of(wrong_triplets.begin(),
+ wrong_triplets.end(),
+ [&i1, &i2, &i3](const TTriplet& tripl)
+ {
+ Item& pi1 = std::get<0>(tripl);
+ Item& pi2 = std::get<1>(tripl);
+ Item& pi3 = std::get<2>(tripl);
+ Item& ri1 = i1, &ri2 = i2, &ri3 = i3;
+ return (&pi1 == &ri1 && &pi2 == &ri2 && &pi3 == &ri3) ||
+ (&pi1 == &ri1 && &pi2 == &ri3 && &pi3 == &ri2) ||
+ (&pi1 == &ri2 && &pi2 == &ri1 && &pi3 == &ri3) ||
+ (&pi1 == &ri3 && &pi2 == &ri2 && &pi3 == &ri1);
+ });
+ };
+
+ using ItemListIt = typename ItemList::iterator;
+
+ auto largestPiece = [](ItemListIt it, ItemList& not_packed) {
+ return it == not_packed.begin()? std::next(it) : not_packed.begin();
+ };
+
+ auto secondLargestPiece = [&largestPiece](ItemListIt it,
+ ItemList& not_packed) {
+ auto ret = std::next(largestPiece(it, not_packed));
+ return ret == it? std::next(ret) : ret;
+ };
+
+ auto smallestPiece = [](ItemListIt it, ItemList& not_packed) {
+ auto last = std::prev(not_packed.end());
+ return it == last? std::prev(it) : last;
+ };
+
+ auto secondSmallestPiece = [&smallestPiece](ItemListIt it,
+ ItemList& not_packed) {
+ auto ret = std::prev(smallestPiece(it, not_packed));
+ return ret == it? std::prev(ret) : ret;
+ };
+
+ auto tryOneByOne = // Subroutine to try adding items one by one.
+ [&bin_area]
+ (Placer& placer, ItemList& not_packed,
+ double waste,
+ double& free_area,
+ double& filled_area)
+ {
+ double item_area = 0;
+ 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 && pack(it) ) {
+ free_area -= item_area;
+ filled_area = bin_area - free_area;
+ ret = true;
+ } else
+ it++;
+ }
+
+ if(ret) not_packed.erase(it);
+
+ return ret;
+ };
+
+ auto tryGroupsOfTwo = // Try adding groups of two items into the bin.
+ [&bin_area, &check_pair, &largestPiece, &smallestPiece,
+ try_reverse]
+ (Placer& placer, ItemList& not_packed,
+ double waste,
+ double& free_area,
+ double& filled_area)
+ {
+ double item_area = 0;
+ const auto endit = not_packed.end();
+
+ if(not_packed.size() < 2)
+ return false; // No group of two items
+
+ double largest_area = not_packed.front().get().area();
+ auto itmp = not_packed.begin(); itmp++;
+ double second_largest = itmp->get().area();
+ if( free_area - second_largest - largest_area > waste)
+ return false; // If even the largest two items do not fill
+ // the bin to the desired waste than we can end here.
+
+
+ bool ret = false;
+ auto it = not_packed.begin();
+ 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()) -
+ largestPiece(it, not_packed)->get().area() <= waste)
+ {
+ if(item_area + smallestPiece(it, not_packed)->get().area() >
+ free_area ) { it++; continue; }
+
+ auto pr = trypack(it);
+
+ // First would fit
+ it2 = not_packed.begin();
+ double item2_area = 0;
+ while(it2 != endit && pr && !ret && free_area -
+ (item2_area = it2->get().area()) - item_area <= waste)
+ {
+ double area_sum = item_area + item2_area;
+
+ if(it == it2 || area_sum > free_area ||
+ check_pair(wrong_pairs, *it, *it2)) {
+ it2++; continue;
+ }
+
+ placer.accept(pr);
+ auto pr2 = trypack(it2);
+ if(!pr2) {
+ placer.unpackLast(); // remove first
+ if(try_reverse) {
+ pr2 = trypack(it2);
+ if(pr2) {
+ placer.accept(pr2);
+ auto pr12 = trypack(it);
+ if(pr12) {
+ placer.accept(pr12);
+ ret = true;
+ } else {
+ placer.unpackLast();
+ }
+ }
+ }
+ } else {
+ placer.accept(pr2); ret = true;
+ }
+
+ if(ret)
+ { // Second fits as well
+ free_area -= area_sum;
+ filled_area = bin_area - free_area;
+ } else {
+ wrong_pairs.emplace_back(*it, *it2);
+ it2++;
+ }
+ }
+
+ if(!ret) it++;
+ }
+
+ if(ret) { not_packed.erase(it); not_packed.erase(it2); }
+
+ return ret;
+ };
+
+ auto tryGroupsOfThree = // Try adding groups of three items.
+ [&bin_area,
+ &smallestPiece, &largestPiece,
+ &secondSmallestPiece, &secondLargestPiece,
+ &check_pair, &check_triplet, try_reverse]
+ (Placer& placer, ItemList& not_packed,
+ double waste,
+ double& free_area,
+ double& filled_area)
+ {
+ auto np_size = not_packed.size();
+ if(np_size < 3) return false;
+
+ auto it = not_packed.begin(); // from
+ const auto endit = not_packed.end(); // to
+ auto it2 = it, it3 = it;
+
+ // Containers for pairs and triplets that were tried before and
+ // do not work.
+ std::vector<TPair> wrong_pairs;
+ std::vector<TTriplet> wrong_triplets;
+
+ auto cap = np_size*np_size / 2 ;
+ wrong_pairs.reserve(cap);
+ wrong_triplets.reserve(cap);
+
+ // Will be true if a succesfull pack can be made.
+ bool ret = false;
+
+ auto area = [](const ItemListIt& it) {
+ 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
+ // largest, smallest and second smallest item in terms of area.
+
+ Item& largest = *largestPiece(it, not_packed);
+ Item& second_largest = *secondLargestPiece(it, not_packed);
+
+ double area_of_two_largest =
+ largest.area() + second_largest.area();
+
+ // Check if there is enough free area for the item and the two
+ // largest item
+ if(free_area - area(it) - area_of_two_largest > waste)
+ break;
+
+ // Determine the area of the two smallest item.
+ Item& smallest = *smallestPiece(it, not_packed);
+ Item& second_smallest = *secondSmallestPiece(it, not_packed);
+
+ // Check if there is enough free area for the item and the two
+ // smallest item.
+ double area_of_two_smallest =
+ smallest.area() + second_smallest.area();
+
+ if(area(it) + area_of_two_smallest > free_area) {
+ it++; continue;
+ }
+
+ auto pr = trypack(it);
+
+ // Check for free area and try to pack the 1st item...
+ if(!pr) { it++; continue; }
+
+ it2 = not_packed.begin();
+ double rem2_area = free_area - largest.area();
+ double a2_sum = 0;
+
+ while(it2 != endit && !ret &&
+ rem2_area - (a2_sum = area(it) + area(it2)) <= waste) {
+ // Drill down level 2
+
+ if(a2_sum != area(it) + area(it2)) throw -1;
+
+ if(it == it2 || check_pair(wrong_pairs, *it, *it2)) {
+ it2++; continue;
+ }
+
+ if(a2_sum + smallest.area() > free_area) {
+ it2++; continue;
+ }
+
+ bool can_pack2 = false;
+
+ placer.accept(pr);
+ auto pr2 = trypack(it2);
+ auto pr12 = pr;
+ if(!pr2) {
+ placer.unpackLast(); // remove first
+ if(try_reverse) {
+ pr2 = trypack(it2);
+ if(pr2) {
+ placer.accept(pr2);
+ pr12 = trypack(it);
+ if(pr12) can_pack2 = true;
+ placer.unpackLast();
+ }
+ }
+ } else {
+ placer.unpackLast();
+ can_pack2 = true;
+ }
+
+ if(!can_pack2) {
+ wrong_pairs.emplace_back(*it, *it2);
+ it2++;
+ continue;
+ }
+
+ // Now we have packed a group of 2 items.
+ // The 'smallest' variable now could be identical with
+ // it2 but we don't bother with that
+
+ it3 = not_packed.begin();
+
+ double a3_sum = 0;
+
+ while(it3 != endit && !ret &&
+ free_area - (a3_sum = a2_sum + area(it3)) <= waste) {
+ // 3rd level
+
+ if(it3 == it || it3 == it2 ||
+ check_triplet(wrong_triplets, *it, *it2, *it3))
+ { it3++; continue; }
+
+ if(a3_sum > free_area) { it3++; continue; }
+
+ placer.accept(pr12); placer.accept(pr2);
+ bool can_pack3 = pack(it3);
+
+ if(!can_pack3) {
+ placer.unpackLast();
+ placer.unpackLast();
+ }
+
+ if(!can_pack3 && try_reverse) {
+
+ std::array<size_t, 3> indices = {0, 1, 2};
+ std::array<typename ItemList::iterator, 3>
+ candidates = {it, it2, it3};
+
+ auto tryPack = [&placer, &candidates, &pack](
+ const decltype(indices)& idx)
+ {
+ std::array<bool, 3> packed = {false};
+
+ for(auto id : idx) packed.at(id) =
+ pack(candidates[id]);
+
+ bool check =
+ std::all_of(packed.begin(),
+ packed.end(),
+ [](bool b) { return b; });
+
+ if(!check) for(bool b : packed) if(b)
+ placer.unpackLast();
+
+ return check;
+ };
+
+ while (!can_pack3 && std::next_permutation(
+ indices.begin(),
+ indices.end())){
+ can_pack3 = tryPack(indices);
+ };
+ }
+
+ if(can_pack3) {
+ // finishit
+ free_area -= a3_sum;
+ filled_area = bin_area - free_area;
+ ret = true;
+ } else {
+ wrong_triplets.emplace_back(*it, *it2, *it3);
+ it3++;
+ }
+
+ } // 3rd while
+
+ if(!ret) it2++;
+
+ } // Second while
+
+ if(!ret) it++;
+
+ } // First while
+
+ if(ret) { // If we eventually succeeded, remove all the packed ones.
+ not_packed.erase(it);
+ not_packed.erase(it2);
+ not_packed.erase(it3);
+ }
+
+ return ret;
+ };
+
+ // Safety test: try to pack each item into an empty bin. If it fails
+ // then it should be removed from the not_packed list
+ { auto it = store_.begin();
+ while (it != store_.end()) {
+ Placer p(bin); p.configure(pconfig);
+ if(!p.pack(*it, rem(it, store_))) {
+ it = store_.erase(it);
+ } else it++;
+ }
+ }
+
+ int acounter = int(store_.size());
+ std::atomic_flag flg = ATOMIC_FLAG_INIT;
+ SpinLock slock(flg);
+
+ auto makeProgress = [this, &acounter, &slock]
+ (Placer& placer, size_t idx, int packednum)
+ {
+
+ packed_bins_[idx] = placer.getItems();
+
+ // TODO here should be a spinlock
+ slock.lock();
+ acounter -= packednum;
+ this->progress_(acounter);
+ slock.unlock();
+ };
+
+ double items_area = 0;
+ for(Item& item : store_) items_area += item.area();
+
+ // Number of bins that will definitely be needed
+ auto bincount_guess = unsigned(std::ceil(items_area / bin_area));
+
+ // Do parallel if feasible
+ bool do_parallel = config_.allow_parallel && bincount_guess > 1 &&
+ ((glob_vertex_count > MAX_VERTICES_SEQUENTIALLY ||
+ store_.size() > MAX_ITEMS_SEQUENTIALLY) ||
+ config_.force_parallel);
+
+ if(do_parallel) dout() << "Parallel execution..." << "\n";
+
+ bool do_pairs = config_.try_pairs;
+ bool do_triplets = config_.try_triplets;
+
+ // The DJD heuristic algorithm itself:
+ auto packjob = [INITIAL_FILL_AREA, bin_area, w, do_triplets, do_pairs,
+ &tryOneByOne,
+ &tryGroupsOfTwo,
+ &tryGroupsOfThree,
+ &makeProgress]
+ (Placer& placer, ItemList& not_packed, size_t idx)
+ {
+ double filled_area = placer.filledArea();
+ double free_area = bin_area - filled_area;
+ double waste = .0;
+ bool lasttry = false;
+
+ while(!not_packed.empty()) {
+
+ {// Fill the bin up to INITIAL_FILL_PROPORTION of its capacity
+ auto it = not_packed.begin();
+
+ while(it != not_packed.end() &&
+ filled_area < INITIAL_FILL_AREA)
+ {
+ if(placer.pack(*it, rem(it, not_packed))) {
+ filled_area += it->get().area();
+ free_area = bin_area - filled_area;
+ it = not_packed.erase(it);
+ makeProgress(placer, idx, 1);
+ } else it++;
+ }
+ }
+
+ // try pieses one by one
+ while(tryOneByOne(placer, not_packed, waste, free_area,
+ filled_area)) {
+ waste = 0; lasttry = false;
+ makeProgress(placer, idx, 1);
+ }
+
+ // try groups of 2 pieses
+ while(do_pairs &&
+ tryGroupsOfTwo(placer, not_packed, waste, free_area,
+ filled_area)) {
+ waste = 0; lasttry = false;
+ makeProgress(placer, idx, 2);
+ }
+
+ // try groups of 3 pieses
+ while(do_triplets &&
+ tryGroupsOfThree(placer, not_packed, waste, free_area,
+ filled_area)) {
+ waste = 0; lasttry = false;
+ makeProgress(placer, idx, 3);
+ }
+
+ waste += w;
+ if(!lasttry && waste > free_area) lasttry = true;
+ else if(lasttry) break;
+ }
+ };
+
+ size_t idx = 0;
+ ItemList remaining;
+
+ if(do_parallel) {
+ std::vector<ItemList> not_packeds(bincount_guess);
+
+ // Preallocating the bins
+ for(unsigned b = 0; b < bincount_guess; b++) {
+ addBin();
+ ItemList& not_packed = not_packeds[b];
+ for(unsigned idx = b; idx < store_.size(); idx+=bincount_guess) {
+ not_packed.push_back(store_[idx]);
+ }
+ }
+
+ // The parallel job
+ auto job = [&placers, &not_packeds, &packjob](unsigned idx) {
+ Placer& placer = placers[idx];
+ ItemList& not_packed = not_packeds[idx];
+ return packjob(placer, not_packed, idx);
+ };
+
+ // We will create jobs for each bin
+ std::vector<std::future<void>> rets(bincount_guess);
+
+ for(unsigned b = 0; b < bincount_guess; b++) { // launch the jobs
+ rets[b] = std::async(std::launch::async, job, b);
+ }
+
+ for(unsigned fi = 0; fi < rets.size(); ++fi) {
+ rets[fi].wait();
+
+ // Collect remaining items while waiting for the running jobs
+ remaining.merge( not_packeds[fi], [](Item& i1, Item& i2) {
+ return i1.area() > i2.area();
+ });
+
+ }
+
+ idx = placers.size();
+
+ // Try to put the remaining items into one of the packed bins
+ if(remaining.size() <= placers.size())
+ for(size_t j = 0; j < idx && !remaining.empty(); j++) {
+ packjob(placers[j], remaining, j);
+ }
+
+ } else {
+ remaining = ItemList(store_.begin(), store_.end());
+ }
+
+ while(!remaining.empty()) {
+ addBin();
+ packjob(placers[idx], remaining, idx); idx++;
+ }
+
+ }
+};
+
+}
+}
+
+#endif // DJD_HEURISTIC_HPP
diff --git a/xs/src/libnest2d/libnest2d/selections/filler.hpp b/xs/src/libnest2d/libnest2d/selections/filler.hpp
new file mode 100644
index 000000000..0da7220a1
--- /dev/null
+++ b/xs/src/libnest2d/libnest2d/selections/filler.hpp
@@ -0,0 +1,80 @@
+#ifndef FILLER_HPP
+#define FILLER_HPP
+
+#include "selection_boilerplate.hpp"
+
+namespace libnest2d { namespace selections {
+
+template<class RawShape>
+class _FillerSelection: public SelectionBoilerplate<RawShape> {
+ using Base = SelectionBoilerplate<RawShape>;
+public:
+ using typename Base::Item;
+ using Config = int; //dummy
+
+private:
+ using Base::packed_bins_;
+ using typename Base::ItemGroup;
+ using Container = ItemGroup;
+ Container store_;
+
+public:
+
+ void configure(const Config& /*config*/) { }
+
+ template<class TPlacer, class TIterator,
+ class TBin = typename PlacementStrategyLike<TPlacer>::BinType,
+ class PConfig = typename PlacementStrategyLike<TPlacer>::Config>
+ void packItems(TIterator first,
+ TIterator last,
+ TBin&& bin,
+ PConfig&& pconfig = PConfig())
+ {
+
+ store_.clear();
+ auto total = last-first;
+ store_.reserve(total);
+ packed_bins_.emplace_back();
+
+ auto makeProgress = [this, &total](
+ PlacementStrategyLike<TPlacer>& placer)
+ {
+ packed_bins_.back() = placer.getItems();
+#ifndef NDEBUG
+ packed_bins_.back().insert(packed_bins_.back().end(),
+ placer.getDebugItems().begin(),
+ placer.getDebugItems().end());
+#endif
+ this->progress_(--total);
+ };
+
+ std::copy(first, last, std::back_inserter(store_));
+
+ auto sortfunc = [](Item& i1, Item& i2) {
+ return i1.area() > i2.area();
+ };
+
+ std::sort(store_.begin(), store_.end(), sortfunc);
+
+ PlacementStrategyLike<TPlacer> placer(bin);
+ placer.configure(pconfig);
+
+ auto it = store_.begin();
+ while(it != store_.end()) {
+ if(!placer.pack(*it, {std::next(it), store_.end()})) {
+ if(packed_bins_.back().empty()) ++it;
+ placer.clearItems();
+ packed_bins_.emplace_back();
+ } else {
+ makeProgress(placer);
+ ++it;
+ }
+ }
+
+ }
+};
+
+}
+}
+
+#endif //BOTTOMLEFT_HPP
diff --git a/xs/src/libnest2d/libnest2d/selections/firstfit.hpp b/xs/src/libnest2d/libnest2d/selections/firstfit.hpp
new file mode 100644
index 000000000..bca7497db
--- /dev/null
+++ b/xs/src/libnest2d/libnest2d/selections/firstfit.hpp
@@ -0,0 +1,97 @@
+#ifndef FIRSTFIT_HPP
+#define FIRSTFIT_HPP
+
+#include "../libnest2d.hpp"
+#include "selection_boilerplate.hpp"
+
+namespace libnest2d { namespace selections {
+
+template<class RawShape>
+class _FirstFitSelection: public SelectionBoilerplate<RawShape> {
+ using Base = SelectionBoilerplate<RawShape>;
+public:
+ using typename Base::Item;
+ using Config = int; //dummy
+
+private:
+ using Base::packed_bins_;
+ using typename Base::ItemGroup;
+ using Container = ItemGroup;//typename std::vector<_Item<RawShape>>;
+
+ Container store_;
+
+public:
+
+ void configure(const Config& /*config*/) { }
+
+ template<class TPlacer, class TIterator,
+ class TBin = typename PlacementStrategyLike<TPlacer>::BinType,
+ class PConfig = typename PlacementStrategyLike<TPlacer>::Config>
+ void packItems(TIterator first,
+ TIterator last,
+ TBin&& bin,
+ PConfig&& pconfig = PConfig())
+ {
+
+ using Placer = PlacementStrategyLike<TPlacer>;
+
+ store_.clear();
+ store_.reserve(last-first);
+ packed_bins_.clear();
+
+ std::vector<Placer> placers;
+ placers.reserve(last-first);
+
+ std::copy(first, last, std::back_inserter(store_));
+
+ auto sortfunc = [](Item& i1, Item& i2) {
+ return i1.area() > i2.area();
+ };
+
+ std::sort(store_.begin(), store_.end(), sortfunc);
+
+ auto total = last-first;
+ auto makeProgress = [this, &total](Placer& placer, size_t idx) {
+ packed_bins_[idx] = placer.getItems();
+ this->progress_(static_cast<unsigned>(--total));
+ };
+
+ // Safety test: try to pack each item into an empty bin. If it fails
+ // then it should be removed from the list
+ { auto it = store_.begin();
+ while (it != store_.end()) {
+ Placer p(bin); p.configure(pconfig);
+ if(!p.pack(*it)) {
+ it = store_.erase(it);
+ } else it++;
+ }
+ }
+
+ auto it = store_.begin();
+
+ while(it != store_.end()) {
+ bool was_packed = false;
+ size_t j = 0;
+ while(!was_packed) {
+ 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;
+ }
+ }
+
+};
+
+}
+}
+
+#endif // FIRSTFIT_HPP
diff --git a/xs/src/libnest2d/libnest2d/selections/selection_boilerplate.hpp b/xs/src/libnest2d/libnest2d/selections/selection_boilerplate.hpp
new file mode 100644
index 000000000..05bbae658
--- /dev/null
+++ b/xs/src/libnest2d/libnest2d/selections/selection_boilerplate.hpp
@@ -0,0 +1,41 @@
+#ifndef SELECTION_BOILERPLATE_HPP
+#define SELECTION_BOILERPLATE_HPP
+
+#include "../libnest2d.hpp"
+
+namespace libnest2d { namespace selections {
+
+template<class RawShape>
+class SelectionBoilerplate {
+public:
+ using Item = _Item<RawShape>;
+ using ItemRef = std::reference_wrapper<Item>;
+ using ItemGroup = std::vector<ItemRef>;
+ using PackGroup = std::vector<ItemGroup>;
+
+ size_t binCount() const { return packed_bins_.size(); }
+
+ ItemGroup itemsForBin(size_t binIndex) {
+ assert(binIndex < packed_bins_.size());
+ return packed_bins_[binIndex];
+ }
+
+ inline const ItemGroup itemsForBin(size_t binIndex) const {
+ assert(binIndex < packed_bins_.size());
+ return packed_bins_[binIndex];
+ }
+
+ inline void progressIndicator(ProgressFunction fn) {
+ progress_ = fn;
+ }
+
+protected:
+
+ PackGroup packed_bins_;
+ ProgressFunction progress_ = [](unsigned){};
+};
+
+}
+}
+
+#endif // SELECTION_BOILERPLATE_HPP
diff --git a/xs/src/libnest2d/tests/CMakeLists.txt b/xs/src/libnest2d/tests/CMakeLists.txt
new file mode 100644
index 000000000..3777f3c56
--- /dev/null
+++ b/xs/src/libnest2d/tests/CMakeLists.txt
@@ -0,0 +1,51 @@
+
+# Try to find existing GTest installation
+find_package(GTest 1.7)
+
+if(NOT GTEST_FOUND)
+ message(STATUS "GTest not found so downloading...")
+ # Go and download google test framework, integrate it with the build
+ set(GTEST_LIBS_TO_LINK gtest gtest_main)
+
+ if (CMAKE_VERSION VERSION_LESS 3.2)
+ set(UPDATE_DISCONNECTED_IF_AVAILABLE "")
+ else()
+ set(UPDATE_DISCONNECTED_IF_AVAILABLE "UPDATE_DISCONNECTED 1")
+ endif()
+
+ include(DownloadProject)
+ download_project(PROJ googletest
+ GIT_REPOSITORY https://github.com/google/googletest.git
+ GIT_TAG release-1.7.0
+ ${UPDATE_DISCONNECTED_IF_AVAILABLE}
+ )
+
+ # Prevent GoogleTest from overriding our compiler/linker options
+ # when building with Visual Studio
+ set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
+
+ add_subdirectory(${googletest_SOURCE_DIR}
+ ${googletest_BINARY_DIR}
+ )
+
+ set(GTEST_INCLUDE_DIRS ${googletest_SOURCE_DIR}/include)
+
+else()
+ find_package(Threads REQUIRED)
+ set(GTEST_LIBS_TO_LINK ${GTEST_BOTH_LIBRARIES} Threads::Threads)
+endif()
+
+add_executable(bp2d_tests test.cpp
+ ../tools/svgtools.hpp
+# ../tools/libnfpglue.hpp
+# ../tools/libnfpglue.cpp
+ printer_parts.h
+ printer_parts.cpp
+ ${LIBNEST2D_SRCFILES}
+ )
+target_link_libraries(bp2d_tests ${LIBNEST2D_LIBRARIES} ${GTEST_LIBS_TO_LINK} )
+
+target_include_directories(bp2d_tests PRIVATE BEFORE ${LIBNEST2D_HEADERS}
+ ${GTEST_INCLUDE_DIRS})
+
+add_test(libnest2d_tests bp2d_tests)
diff --git a/xs/src/libnest2d/tests/printer_parts.cpp b/xs/src/libnest2d/tests/printer_parts.cpp
new file mode 100644
index 000000000..bdc2a3d43
--- /dev/null
+++ b/xs/src/libnest2d/tests/printer_parts.cpp
@@ -0,0 +1,3175 @@
+#include "printer_parts.h"
+
+const TestData PRINTER_PART_POLYGONS =
+{
+ {
+ {-5000000, 8954050},
+ {5000000, 8954050},
+ {5000000, -45949},
+ {4972609, -568550},
+ {3500000, -8954050},
+ {-3500000, -8954050},
+ {-4972609, -568550},
+ {-5000000, -45949},
+ {-5000000, 8954050},
+ },
+ {
+ {-63750000, -8000000},
+ {-54750000, 46000000},
+ {50750000, 46000000},
+ {63750000, 33000000},
+ {63750000, -46000000},
+ {-54750000, -46000000},
+ {-63750000, -28000000},
+ {-63750000, -8000000},
+ },
+ {
+ {-52750000, 41512348},
+ {-31250000, 45987651},
+ {52750000, 45987651},
+ {52750000, -45987651},
+ {-52750000, -45987651},
+ {-52750000, 41512348},
+ },
+ {
+ {-3900000, 14000000},
+ {-2167950, 14000000},
+ {1721454, 7263400},
+ {3828529, 3613790},
+ {3838809, 3582149},
+ {3871560, 3270569},
+ {3900000, 3000000},
+ {3500000, -3000000},
+ {3471560, -3270565},
+ {3447549, -3498986},
+ {3292510, -3976167},
+ {3099999, -4512949},
+ {2530129, -5500000},
+ {807565, -8483570},
+ {-2377349, -14000000},
+ {-3900000, -14000000},
+ {-3900000, 14000000},
+ },
+ {
+ {-31750000, -1000000},
+ {-25250000, 40500000},
+ {-18250000, 47500000},
+ {10750000, 47500000},
+ {16750000, 41500000},
+ {31750000, -37000000},
+ {31750000, -43857898},
+ {28107900, -47500000},
+ {18392099, -47500000},
+ {-20750000, -46500000},
+ {-31750000, -4000000},
+ {-31750000, -1000000},
+ },
+ {
+ {-34625000, -14265399},
+ {-10924999, 24875000},
+ {33325000, 24875000},
+ {37575000, 20625000},
+ {37575000, 17625000},
+ {26575000, -24875000},
+ {-8924999, -24875000},
+ {-34625000, -24484600},
+ {-37575000, -19375000},
+ {-34625000, -14265399},
+ },
+ {
+ {-14000000, 9000000},
+ {-11000000, 17000000},
+ {14000000, 17000000},
+ {14000000, -17000000},
+ {-11000000, -17000000},
+ {-14000000, -8000000},
+ {-14000000, 9000000},
+ },
+ {
+ {-5300000, 2227401},
+ {-237800, 5150001},
+ {5299999, 5150001},
+ {5299999, 650001},
+ {4699999, -5149997},
+ {-5300000, -5149997},
+ {-5300000, 2227401},
+ },
+ {
+ {-12000000, 18000000},
+ {12000000, 18000000},
+ {12000000, -18000000},
+ {-12000000, -18000000},
+ {-12000000, 18000000},
+ },
+ {
+ {-18000000, -1000000},
+ {-15000000, 22000000},
+ {-11000000, 26000000},
+ {11000000, 26000000},
+ {15000000, 22000000},
+ {18000000, -1000000},
+ {18000000, -26000000},
+ {-18000000, -26000000},
+ {-18000000, -1000000},
+ },
+ {
+ {-77500000, 30000000},
+ {-72500000, 35000000},
+ {72500000, 35000000},
+ {77500000, 30000000},
+ {77500000, -32928901},
+ {75428901, -35000000},
+ {-75428901, -35000000},
+ {-77500000, -32928901},
+ {-77500000, 30000000},
+ },
+ {
+ {-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},
+ },
+ {
+ {-34192394, -5192389},
+ {-31499996, 39000000},
+ {-8183795, 47668998},
+ {-6769596, 47668998},
+ {-4648197, 45547698},
+ {34192394, 6707109},
+ {34192394, 5192389},
+ {31500003, -39000000},
+ {8183803, -47668998},
+ {6769603, -47668998},
+ {4648202, -45547698},
+ {-32474895, -8424619},
+ {-34192394, -6707109},
+ {-34192394, -5192389},
+ },
+ {
+ {-23475500, -11910099},
+ {-18000000, 8217699},
+ {-11139699, 20100000},
+ {-10271400, 20899999},
+ {9532010, 20899999},
+ {11199999, 20100000},
+ {18500000, 8600000},
+ {23475500, -11910099},
+ {23799999, -14899999},
+ {23706600, -15788900},
+ {23668899, -16147499},
+ {23281299, -17340400},
+ {22654100, -18426700},
+ {21814800, -19358900},
+ {20799999, -20096199},
+ {19654100, -20606300},
+ {18427200, -20867099},
+ {17799999, -20899999},
+ {-17799999, -20899999},
+ {-18427200, -20867099},
+ {-19654100, -20606300},
+ {-20799999, -20096199},
+ {-21814800, -19358900},
+ {-22654100, -18426700},
+ {-23281299, -17340400},
+ {-23668899, -16147499},
+ {-23799999, -14899999},
+ {-23475500, -11910099},
+ },
+ {
+ {-32000000, 10000000},
+ {-31934440, 10623733},
+ {-31740640, 11220210},
+ {-31427049, 11763360},
+ {-31007389, 12229430},
+ {-30500000, 12598079},
+ {-29927051, 12853170},
+ {-29313585, 12983570},
+ {16000000, 16000000},
+ {26000000, 16000000},
+ {31007400, 12229430},
+ {31427101, 11763360},
+ {31740600, 11220210},
+ {31934398, 10623733},
+ {32000000, 10000000},
+ {32000000, -13000000},
+ {31934398, -13623699},
+ {31740600, -14220199},
+ {31427101, -14763399},
+ {31007400, -15229400},
+ {30500000, -15598100},
+ {29927101, -15853200},
+ {29313598, -15983600},
+ {29000000, -16000000},
+ {-28000000, -16000000},
+ {-29313585, -15983600},
+ {-29927051, -15853200},
+ {-30500000, -15598100},
+ {-31007389, -15229400},
+ {-31427049, -14763399},
+ {-31740640, -14220199},
+ {-31934440, -13623699},
+ {-32000000, -13000000},
+ {-32000000, 10000000},
+ },
+ {
+ {-36133789, -46431022},
+ {-36040100, -46171817},
+ {-35852722, -45653411},
+ {2200073, 59616485},
+ {12112792, 87039184},
+ {14274505, 93019332},
+ {14382049, 93291641},
+ {14508483, 93563430},
+ {14573425, 93688369},
+ {14654052, 93832443},
+ {14818634, 94096328},
+ {14982757, 94327621},
+ {15001708, 94352630},
+ {15202392, 94598999},
+ {15419342, 94833160},
+ {15497497, 94910552},
+ {15650848, 95053039},
+ {15894866, 95256866},
+ {16104309, 95412185},
+ {16149047, 95443206},
+ {16410888, 95611038},
+ {16677795, 95759750},
+ {16782348, 95812332},
+ {16947143, 95889144},
+ {17216400, 95999465},
+ {17483123, 96091293},
+ {17505554, 96098251},
+ {17745178, 96165542},
+ {18000671, 96223373},
+ {18245880, 96265884},
+ {18484039, 96295257},
+ {18976715, 96319580},
+ {31135131, 96319580},
+ {31697082, 96287902},
+ {31746368, 96282104},
+ {32263000, 96190719},
+ {32338623, 96172576},
+ {32821411, 96026641},
+ {32906188, 95995391},
+ {33360565, 95797012},
+ {33443420, 95754882},
+ {33869171, 95505874},
+ {33900756, 95485122},
+ {34136413, 95318618},
+ {34337127, 95159790},
+ {34377288, 95125930},
+ {34619628, 94905410},
+ {34756286, 94767364},
+ {34859008, 94656143},
+ {35090606, 94378067},
+ {35120849, 94338546},
+ {35309295, 94072113},
+ {35434875, 93871475},
+ {35510070, 93740310},
+ {35688232, 93385772},
+ {35699096, 93361679},
+ {35839782, 93012557},
+ {35905487, 92817459},
+ {35961578, 92625488},
+ {36048004, 92249023},
+ {36051574, 92229934},
+ {36108856, 91831405},
+ {36122985, 91667816},
+ {36133789, 91435317},
+ {36129669, 91085830},
+ {36127685, 91046661},
+ {36092742, 90669830},
+ {36069946, 90514739},
+ {36031829, 90308425},
+ {35948211, 89965225},
+ {34482635, 84756820},
+ {27911407, 61403976},
+ {-5872558, -58657440},
+ {-14243621, -88406509},
+ {-14576812, -89590599},
+ {-15421997, -92594200},
+ {-15657684, -93431732},
+ {-16038940, -93720520},
+ {-16420196, -94009307},
+ {-17182708, -94586875},
+ {-18834838, -95838272},
+ {-19470275, -96319580},
+ {-21368133, -96319580},
+ {-22763854, -96319534},
+ {-29742462, -96319274},
+ {-32533935, -96319168},
+ {-36133789, -54619018},
+ {-36133789, -46431022},
+ },
+ {
+ {-26000000, 25500000},
+ {-6500000, 45000000},
+ {17499998, 45000000},
+ {23966310, 38533699},
+ {26000000, 36500000},
+ {26000000, -19000000},
+ {25950000, -24500000},
+ {17000000, -42214698},
+ {14300000, -45000000},
+ {-14299999, -45000000},
+ {-17500000, -41714698},
+ {-23400001, -24500000},
+ {-26000000, -10464000},
+ {-26000000, 25500000},
+ },
+ {
+ {-26000000, 16636100},
+ {-25072200, 18777799},
+ {-16500000, 35299999},
+ {-15050000, 36750000},
+ {13550000, 36750000},
+ {15000000, 35299999},
+ {26000000, 16045200},
+ {26000000, -2750000},
+ {16500000, -34507900},
+ {14840600, -36167301},
+ {14257900, -36750000},
+ {-14257900, -36750000},
+ {-16500000, -34507900},
+ {-26000000, -2750000},
+ {-26000000, 16636100},
+ },
+ {
+ {-18062349, 18950099},
+ {4644938, 20049900},
+ {6230361, 20049900},
+ {7803279, 19851200},
+ {9338899, 19456899},
+ {10812990, 18873300},
+ {12202310, 18109500},
+ {13484951, 17177600},
+ {14640670, 16092300},
+ {15651250, 14870700},
+ {16500749, 13532100},
+ {17175849, 12097599},
+ {17665750, 10589700},
+ {17962850, 9032400},
+ {18062349, 7450099},
+ {17962850, 5867799},
+ {15810750, -11007740},
+ {15683750, -11727769},
+ {15506849, -12437200},
+ {15280929, -13132559},
+ {15007040, -13810470},
+ {14686531, -14467609},
+ {14320949, -15100799},
+ {13912099, -15706950},
+ {13461959, -16283100},
+ {12972730, -16826450},
+ {12446790, -17334339},
+ {11886699, -17804309},
+ {11295190, -18234069},
+ {10675149, -18621520},
+ {10029590, -18964771},
+ {9361650, -19262149},
+ {8674600, -19512220},
+ {7971780, -19713699},
+ {7256609, -19865798},
+ {6532589, -19967498},
+ {5803222, -20018501},
+ {5437650, -20024900},
+ {-1062349, -20049900},
+ {-16562349, -20049900},
+ {-18062349, -18549900},
+ {-18062349, 18950099},
+ },
+ {
+ {-18062349, 41299900},
+ {-1062349, 41299900},
+ {15280929, -8117440},
+ {15506849, -8812799},
+ {15683750, -9522230},
+ {15810750, -10242259},
+ {17962850, -27117799},
+ {18062349, -28700099},
+ {17962850, -30282400},
+ {17665750, -31839700},
+ {17175849, -33347599},
+ {16500749, -34782100},
+ {15651250, -36120700},
+ {14640670, -37342300},
+ {13484951, -38427600},
+ {12202310, -39359500},
+ {10812990, -40123298},
+ {9338899, -40706901},
+ {7803279, -41101200},
+ {6230361, -41299900},
+ {4644938, -41299900},
+ {-18062349, -40200099},
+ {-18062349, 41299900},
+ },
+ {
+ {-11750000, 13057900},
+ {-9807860, 15000000},
+ {4392139, 24000000},
+ {11750000, 24000000},
+ {11750000, -24000000},
+ {4392139, -24000000},
+ {-9807860, -15000000},
+ {-11750000, -13057900},
+ {-11750000, 13057900},
+ },
+ {
+ {-12500000, 17500000},
+ {12500000, 17500000},
+ {12500000, -17500000},
+ {-12500000, -17500000},
+ {-12500000, 17500000},
+ },
+ {
+ {-23500000, 11500000},
+ {-13857859, 21000000},
+ {-11000000, 21000000},
+ {18500000, 500000},
+ {23500000, -4500000},
+ {23500000, -19500000},
+ {22000000, -21000000},
+ {-23500000, -21000000},
+ {-23500000, 11500000},
+ },
+ {
+ {-13000000, 5250000},
+ {-4000000, 6750000},
+ {4000000, 6750000},
+ {13000000, 5250000},
+ {13000000, 838459},
+ {11376299, -1973939},
+ {10350899, -3750000},
+ {8618800, -6750000},
+ {-8498290, -6750000},
+ {-13000000, 1047180},
+ {-13000000, 5250000},
+ },
+ {
+ {-25000000, 50500000},
+ {-21500000, 54000000},
+ {18286800, 54000000},
+ {25000000, 47286800},
+ {25000000, -47286800},
+ {18286800, -54000000},
+ {-21500000, -54000000},
+ {-25000000, -50500000},
+ {-25000000, 50500000},
+ },
+ {
+ {-19000000, 46000000},
+ {-16799999, 46000000},
+ {14000000, 34000000},
+ {19000000, 29000000},
+ {19000000, -29000000},
+ {14000000, -34000000},
+ {-16799999, -46000000},
+ {-19000000, -46000000},
+ {-19000000, 46000000},
+ },
+ {
+ {-7956170, 836226},
+ {-7825180, 1663290},
+ {-7767529, 1914530},
+ {-7608449, 2472140},
+ {-7308360, 3253890},
+ {-7083650, 3717780},
+ {-6928199, 4000000},
+ {-6472139, 4702280},
+ {-5988090, 5304979},
+ {-5945159, 5353040},
+ {-5353040, 5945159},
+ {-4702280, 6472139},
+ {-4544519, 6583869},
+ {-4000000, 6928199},
+ {-3253890, 7308360},
+ {-2836839, 7480130},
+ {-2472140, 7608449},
+ {-1663290, 7825180},
+ {-964293, 7941669},
+ {-836226, 7956170},
+ {0, 8000000},
+ {836226, 7956170},
+ {964293, 7941669},
+ {1663290, 7825180},
+ {2472140, 7608449},
+ {2836839, 7480130},
+ {3253890, 7308360},
+ {4000000, 6928199},
+ {4544519, 6583869},
+ {4702280, 6472139},
+ {5353040, 5945159},
+ {5945159, 5353040},
+ {5988090, 5304979},
+ {6472139, 4702280},
+ {6928199, 4000000},
+ {7083650, 3717780},
+ {7308360, 3253890},
+ {7608449, 2472140},
+ {7767529, 1914530},
+ {7825180, 1663290},
+ {7956170, 836226},
+ {8000000, 0},
+ {7956170, -836226},
+ {7825180, -1663290},
+ {7767529, -1914530},
+ {7608449, -2472140},
+ {7308360, -3253890},
+ {7083650, -3717780},
+ {6928199, -4000000},
+ {6472139, -4702280},
+ {5988090, -5304979},
+ {5945159, -5353040},
+ {5353040, -5945159},
+ {4702280, -6472139},
+ {4544519, -6583869},
+ {4000000, -6928199},
+ {3253890, -7308360},
+ {2836839, -7480130},
+ {2472140, -7608449},
+ {1663290, -7825180},
+ {964293, -7941669},
+ {836226, -7956170},
+ {0, -8000000},
+ {-836226, -7956170},
+ {-964293, -7941669},
+ {-1663290, -7825180},
+ {-2472140, -7608449},
+ {-2836839, -7480130},
+ {-3253890, -7308360},
+ {-4000000, -6928199},
+ {-4544519, -6583869},
+ {-4702280, -6472139},
+ {-5353040, -5945159},
+ {-5945159, -5353040},
+ {-5988090, -5304979},
+ {-6472139, -4702280},
+ {-6928199, -4000000},
+ {-7083650, -3717780},
+ {-7308360, -3253890},
+ {-7608449, -2472140},
+ {-7767529, -1914530},
+ {-7825180, -1663290},
+ {-7956170, -836226},
+ {-8000000, 0},
+ {-7956170, 836226},
+ },
+};
+
+const TestData STEGOSAUR_POLYGONS =
+{
+ {
+ {113210205, 107034095},
+ {113561798, 109153793},
+ {113750099, 109914001},
+ {114396499, 111040199},
+ {114599197, 111321998},
+ {115570404, 112657096},
+ {116920097, 114166595},
+ {117630599, 114609390},
+ {119703704, 115583900},
+ {120559494, 115811996},
+ {121045410, 115754493},
+ {122698097, 115526496},
+ {123373001, 115370193},
+ {123482406, 115315689},
+ {125664199, 114129798},
+ {125920303, 113968193},
+ {128551208, 111866195},
+ {129075592, 111443199},
+ {135044692, 106572608},
+ {135254898, 106347694},
+ {135415100, 106102897},
+ {136121704, 103779891},
+ {136325103, 103086303},
+ {136690093, 101284896},
+ {136798309, 97568496},
+ {136798309, 97470397},
+ {136787399, 97375297},
+ {136753295, 97272102},
+ {136687988, 97158699},
+ {136539794, 96946899},
+ {135526702, 95550994},
+ {135388488, 95382293},
+ {135272491, 95279098},
+ {135214904, 95250595},
+ {135122894, 95218002},
+ {134966705, 95165191},
+ {131753997, 94380798},
+ {131226806, 94331001},
+ {129603393, 94193893},
+ {129224197, 94188003},
+ {127874107, 94215103},
+ {126812797, 94690200},
+ {126558197, 94813896},
+ {118361801, 99824195},
+ {116550796, 101078796},
+ {116189704, 101380493},
+ {114634002, 103027999},
+ {114118103, 103820297},
+ {113399200, 105568000},
+ {113201705, 106093597},
+ {113210205, 107034095},
+ },
+ {
+ {77917999, 130563003},
+ {77926300, 131300903},
+ {77990196, 132392700},
+ {78144195, 133328002},
+ {78170593, 133427093},
+ {78235900, 133657592},
+ {78799598, 135466705},
+ {78933296, 135832397},
+ {79112899, 136247604},
+ {79336303, 136670898},
+ {79585197, 137080596},
+ {79726303, 137309005},
+ {79820297, 137431900},
+ {79942199, 137549407},
+ {90329193, 145990203},
+ {90460197, 146094390},
+ {90606399, 146184509},
+ {90715194, 146230010},
+ {90919601, 146267211},
+ {142335296, 153077697},
+ {143460296, 153153594},
+ {143976593, 153182189},
+ {145403991, 153148605},
+ {145562301, 153131195},
+ {145705993, 153102905},
+ {145938796, 153053192},
+ {146134094, 153010101},
+ {146483184, 152920196},
+ {146904693, 152806396},
+ {147180099, 152670196},
+ {147357788, 152581695},
+ {147615295, 152423095},
+ {147782287, 152294708},
+ {149281799, 150908386},
+ {149405303, 150784912},
+ {166569305, 126952499},
+ {166784301, 126638099},
+ {166938491, 126393699},
+ {167030899, 126245101},
+ {167173004, 126015899},
+ {167415298, 125607200},
+ {167468292, 125504699},
+ {167553100, 125320899},
+ {167584594, 125250694},
+ {167684997, 125004394},
+ {167807098, 124672401},
+ {167938995, 124255203},
+ {168052307, 123694000},
+ {170094100, 112846900},
+ {170118408, 112684204},
+ {172079101, 88437797},
+ {172082000, 88294403},
+ {171916290, 82827606},
+ {171911590, 82705703},
+ {171874893, 82641906},
+ {169867004, 79529907},
+ {155996795, 58147998},
+ {155904998, 58066299},
+ {155864791, 58054199},
+ {134315704, 56830902},
+ {134086486, 56817901},
+ {98200096, 56817798},
+ {97838195, 56818599},
+ {79401695, 56865097},
+ {79291297, 56865501},
+ {79180694, 56869499},
+ {79058799, 56885097},
+ {78937301, 56965301},
+ {78324691, 57374599},
+ {77932998, 57638401},
+ {77917999, 57764297},
+ {77917999, 130563003},
+ },
+ {
+ {75566848, 109289947},
+ {75592651, 109421951},
+ {75644248, 109534446},
+ {95210548, 141223846},
+ {95262649, 141307449},
+ {95487854, 141401443},
+ {95910850, 141511642},
+ {96105651, 141550338},
+ {106015045, 142803451},
+ {106142852, 142815155},
+ {166897460, 139500244},
+ {167019348, 139484741},
+ {168008239, 138823043},
+ {168137542, 138735153},
+ {168156250, 138616851},
+ {173160751, 98882049},
+ {174381546, 87916046},
+ {174412246, 87579048},
+ {174429443, 86988746},
+ {174436141, 86297348},
+ {174438949, 84912048},
+ {174262939, 80999145},
+ {174172546, 80477546},
+ {173847549, 79140846},
+ {173623840, 78294349},
+ {173120239, 76485046},
+ {173067138, 76300544},
+ {173017852, 76137542},
+ {172941543, 75903045},
+ {172892547, 75753143},
+ {172813537, 75533348},
+ {172758453, 75387046},
+ {172307556, 74196746},
+ {171926544, 73192848},
+ {171891448, 73100448},
+ {171672546, 72524147},
+ {171502441, 72085144},
+ {171414459, 71859146},
+ {171294250, 71552352},
+ {171080139, 71019744},
+ {171039245, 70928146},
+ {170970550, 70813346},
+ {170904235, 70704040},
+ {170786254, 70524353},
+ {168063247, 67259048},
+ {167989547, 67184844},
+ {83427947, 67184844},
+ {78360847, 67201248},
+ {78238845, 67220550},
+ {78151550, 67350547},
+ {77574554, 68220550},
+ {77494949, 68342651},
+ {77479949, 68464546},
+ {75648345, 106513351},
+ {75561050, 109165740},
+ {75566848, 109289947},
+ },
+ {
+ {75619415, 108041595},
+ {83609863, 134885772},
+ {83806945, 135450820},
+ {83943908, 135727371},
+ {84799934, 137289794},
+ {86547897, 140033782},
+ {86674118, 140192962},
+ {86810661, 140364715},
+ {87045211, 140619918},
+ {88187042, 141853240},
+ {93924575, 147393783},
+ {94058013, 147454803},
+ {111640083, 153754562},
+ {111762550, 153787933},
+ {111975250, 153835311},
+ {112127426, 153842803},
+ {116797996, 154005157},
+ {116969688, 154010681},
+ {117141731, 154005935},
+ {117333145, 153988037},
+ {118007507, 153919952},
+ {118159675, 153902130},
+ {118931480, 153771942},
+ {120878150, 153379089},
+ {121172164, 153319259},
+ {122074508, 153034362},
+ {122260681, 152970367},
+ {122313438, 152949584},
+ {130755096, 149423736},
+ {130996063, 149316818},
+ {138893524, 144469665},
+ {138896423, 144466918},
+ {169883666, 97686134},
+ {170115036, 96518981},
+ {170144317, 96365257},
+ {174395645, 67672065},
+ {174396560, 67664222},
+ {174288452, 66839241},
+ {174170364, 66096923},
+ {174112731, 65952033},
+ {174021377, 65823486},
+ {173948608, 65743225},
+ {173863830, 65654769},
+ {170408340, 63627494},
+ {170004867, 63394714},
+ {169585632, 63194389},
+ {169441162, 63137046},
+ {168944274, 62952133},
+ {160605072, 60214218},
+ {160331573, 60126396},
+ {159674743, 59916877},
+ {150337249, 56943778},
+ {150267730, 56922073},
+ {150080139, 56864868},
+ {149435333, 56676422},
+ {149310241, 56640579},
+ {148055419, 56285041},
+ {147828796, 56230949},
+ {147598205, 56181800},
+ {147149963, 56093917},
+ {146834457, 56044700},
+ {146727966, 56028717},
+ {146519729, 56004882},
+ {146328521, 55989326},
+ {146170684, 55990036},
+ {146151321, 55990745},
+ {145800170, 56003616},
+ {145639526, 56017753},
+ {145599426, 56022491},
+ {145481338, 56039184},
+ {145389556, 56052757},
+ {145325134, 56062591},
+ {145176574, 56086135},
+ {145017272, 56113922},
+ {107163085, 63504539},
+ {101013870, 65454101},
+ {100921798, 65535285},
+ {95362182, 74174079},
+ {75652366, 107803443},
+ {75635391, 107834983},
+ {75628814, 107853294},
+ {75603431, 107933692},
+ {75619415, 108041595},
+ },
+ {
+ {83617141, 120264900},
+ {84617370, 126416427},
+ {84648635, 126601341},
+ {84693695, 126816085},
+ {84762496, 127082641},
+ {84772140, 127117034},
+ {84860748, 127391693},
+ {84927398, 127550239},
+ {85072967, 127789642},
+ {85155151, 127908851},
+ {86745422, 130042907},
+ {86982666, 130317489},
+ {89975143, 133230743},
+ {90091384, 133338500},
+ {96260833, 138719818},
+ {96713928, 139103668},
+ {98139297, 140307388},
+ {102104766, 143511505},
+ {102142089, 143536468},
+ {102457626, 143735107},
+ {103386764, 144312988},
+ {103845001, 144579177},
+ {104139175, 144737136},
+ {104551254, 144932250},
+ {104690155, 144985778},
+ {104844238, 145010009},
+ {105020034, 145010375},
+ {128999633, 144082305},
+ {129096542, 144076141},
+ {133932327, 143370178},
+ {134130615, 143326751},
+ {134281250, 143289520},
+ {135247116, 142993438},
+ {150774948, 137828704},
+ {150893478, 137786178},
+ {151350921, 137608901},
+ {159797760, 134318115},
+ {159979827, 134244384},
+ {159988128, 134240997},
+ {160035186, 134221633},
+ {160054962, 134211486},
+ {160168762, 134132736},
+ {160181228, 134121047},
+ {160336425, 133961502},
+ {160689147, 133564331},
+ {161446258, 132710739},
+ {163306427, 130611648},
+ {164845474, 128873855},
+ {165270233, 128393600},
+ {165281478, 128380706},
+ {165300598, 128358673},
+ {165303497, 128355194},
+ {166411590, 122772674},
+ {166423767, 122708648},
+ {164745605, 66237312},
+ {164740341, 66193061},
+ {164721755, 66082092},
+ {164721160, 66078750},
+ {164688476, 65914146},
+ {164668426, 65859436},
+ {164563110, 65765937},
+ {164431152, 65715034},
+ {163997619, 65550788},
+ {163946426, 65531440},
+ {162998107, 65173629},
+ {162664978, 65049140},
+ {162482696, 64991668},
+ {162464660, 64989639},
+ {148029083, 66896141},
+ {147862396, 66932853},
+ {130087829, 73341102},
+ {129791564, 73469726},
+ {100590927, 90307685},
+ {100483535, 90373847},
+ {100364990, 90458930},
+ {96447448, 93276664},
+ {95179656, 94189010},
+ {93692718, 95260208},
+ {87904327, 99430885},
+ {87663711, 99606147},
+ {87576202, 99683990},
+ {87498199, 99801719},
+ {85740264, 104173728},
+ {85538925, 104710494},
+ {84786132, 107265830},
+ {84635955, 107801383},
+ {84619506, 107868064},
+ {84518463, 108287200},
+ {84456848, 108613471},
+ {84419158, 108826194},
+ {84375244, 109093818},
+ {84329818, 109435180},
+ {84249862, 110179664},
+ {84218429, 110572166},
+ {83630020, 117995208},
+ {83595535, 118787673},
+ {83576217, 119290679},
+ {83617141, 120264900},
+ },
+ {
+ {91735549, 117640846},
+ {91748252, 117958145},
+ {91823547, 118515449},
+ {92088752, 119477249},
+ {97995346, 140538452},
+ {98031051, 140660446},
+ {98154449, 141060241},
+ {98179855, 141133758},
+ {98217056, 141232849},
+ {98217147, 141233047},
+ {98269256, 141337051},
+ {98298950, 141387954},
+ {98337753, 141445755},
+ {99455047, 142984451},
+ {99656250, 143247344},
+ {102567855, 146783752},
+ {102685150, 146906845},
+ {102828948, 147031250},
+ {102972457, 147120452},
+ {103676147, 147539642},
+ {103758956, 147586151},
+ {103956756, 147682144},
+ {104479949, 147931457},
+ {104744453, 148044143},
+ {104994750, 148123443},
+ {105375648, 148158645},
+ {109266250, 148178253},
+ {109447753, 148169052},
+ {109693649, 148129150},
+ {113729949, 147337448},
+ {113884552, 147303054},
+ {115155349, 146956146},
+ {117637145, 146174346},
+ {154694046, 134048049},
+ {156979949, 133128555},
+ {157076843, 133059356},
+ {157125045, 133001449},
+ {157561340, 132300750},
+ {157865753, 131795959},
+ {157923156, 131667358},
+ {158007049, 131297653},
+ {158112747, 130777053},
+ {158116653, 130640853},
+ {158268951, 119981643},
+ {158260040, 119824752},
+ {158229949, 119563751},
+ {149914047, 73458648},
+ {149877548, 73331748},
+ {144460754, 66413558},
+ {144230545, 66153152},
+ {144128051, 66075057},
+ {143974853, 65973152},
+ {142812744, 65353149},
+ {141810943, 64837249},
+ {141683349, 64805152},
+ {141505157, 64784652},
+ {108214355, 61896251},
+ {107826354, 61866352},
+ {107072151, 61821750},
+ {106938850, 61873550},
+ {106584251, 62055152},
+ {106419952, 62147548},
+ {100459152, 65546951},
+ {100343849, 65615150},
+ {100198852, 65716949},
+ {99825149, 65979751},
+ {94619247, 70330352},
+ {94492355, 70480850},
+ {94445846, 70547355},
+ {94425354, 70588752},
+ {94379753, 70687652},
+ {94110252, 71443450},
+ {94095252, 71569053},
+ {91737251, 117308746},
+ {91731048, 117430946},
+ {91735549, 117640846},
+ },
+ {
+ {108231399, 111763748},
+ {108335403, 111927955},
+ {108865203, 112754745},
+ {109206703, 113283851},
+ {127117500, 125545951},
+ {127212097, 125560951},
+ {127358497, 125563652},
+ {131348007, 125551147},
+ {131412002, 125550849},
+ {131509506, 125535446},
+ {131579391, 125431343},
+ {132041000, 124735656},
+ {132104690, 124637847},
+ {144108505, 100950546},
+ {144120605, 100853042},
+ {144123291, 100764648},
+ {144122695, 100475143},
+ {144086898, 85637748},
+ {144083602, 85549346},
+ {144071105, 85451843},
+ {144007003, 85354545},
+ {143679595, 84864547},
+ {143468597, 84551048},
+ {143367889, 84539146},
+ {109847702, 84436347},
+ {109684700, 84458953},
+ {105946502, 89406143},
+ {105915901, 91160446},
+ {105880905, 93187744},
+ {105876701, 93441345},
+ {108231399, 111763748},
+ },
+ {
+ {102614700, 117684249},
+ {102675102, 118074157},
+ {102888999, 118743148},
+ {103199707, 119517555},
+ {103446800, 120099655},
+ {103488204, 120193450},
+ {104063903, 121373947},
+ {104535499, 122192245},
+ {104595802, 122295249},
+ {104663002, 122402854},
+ {104945701, 122854858},
+ {105740501, 124038848},
+ {106809700, 125479354},
+ {107564399, 126380050},
+ {108116203, 126975646},
+ {123724700, 142516540},
+ {124938400, 143705444},
+ {127919601, 146599243},
+ {128150894, 146821456},
+ {128251602, 146917251},
+ {128383605, 147041839},
+ {128527709, 147176147},
+ {128685699, 147321456},
+ {128861007, 147481246},
+ {132825103, 151046661},
+ {133005493, 151205657},
+ {133389007, 151488143},
+ {133896499, 151858062},
+ {134172302, 151991546},
+ {134375000, 152063140},
+ {135316101, 152300949},
+ {136056304, 152220947},
+ {136242706, 152186843},
+ {136622207, 152016448},
+ {136805404, 151908355},
+ {147099594, 145766845},
+ {147246704, 144900756},
+ {147387603, 144048461},
+ {144353698, 99345855},
+ {144333801, 99232254},
+ {144244598, 98812850},
+ {144228698, 98757858},
+ {144174606, 98616455},
+ {133010101, 72396743},
+ {132018905, 70280853},
+ {130667404, 67536949},
+ {129167297, 64854446},
+ {128569198, 64098350},
+ {124458503, 59135948},
+ {124260597, 58946949},
+ {123908706, 58658851},
+ {123460098, 58327850},
+ {122674499, 57840648},
+ {122041801, 57712150},
+ {121613403, 57699047},
+ {121359901, 57749351},
+ {121123199, 57826450},
+ {120953498, 57882247},
+ {120431701, 58198547},
+ {120099205, 58599349},
+ {119892303, 58903049},
+ {102835296, 115179351},
+ {102686599, 115817245},
+ {102612396, 116540557},
+ {102614700, 117684249},
+ },
+ {
+ {98163757, 71203430},
+ {98212463, 73314544},
+ {98326538, 74432693},
+ {98402908, 75169799},
+ {98524154, 76328353},
+ {99088806, 79911361},
+ {99304885, 80947769},
+ {100106689, 84244186},
+ {100358123, 85080337},
+ {101715545, 89252807},
+ {101969528, 89987213},
+ {107989440, 106391418},
+ {126299575, 140277343},
+ {127061813, 141486663},
+ {127405746, 141872253},
+ {127846908, 142318450},
+ {130818496, 145301574},
+ {134366424, 148100921},
+ {135308380, 148798828},
+ {135745666, 149117523},
+ {136033020, 149251800},
+ {136500579, 149387725},
+ {136662719, 149418395},
+ {136973922, 149474822},
+ {137184890, 149484375},
+ {137623748, 149434356},
+ {137830810, 149355072},
+ {138681732, 148971343},
+ {139374465, 148463409},
+ {139589187, 148264312},
+ {139809707, 148010711},
+ {139985610, 147685028},
+ {140196029, 147284973},
+ {140355834, 146978668},
+ {142079666, 142575622},
+ {146702194, 129469726},
+ {151285888, 113275238},
+ {151543731, 112046264},
+ {151701629, 110884704},
+ {151837020, 108986206},
+ {151837097, 107724029},
+ {151760101, 106529205},
+ {151581970, 105441925},
+ {151577301, 105413757},
+ {151495269, 105014709},
+ {151393142, 104551513},
+ {151058502, 103296112},
+ {150705520, 102477264},
+ {150137725, 101686370},
+ {149427032, 100938537},
+ {102979965, 60772064},
+ {101930953, 60515609},
+ {101276748, 60634414},
+ {100717803, 60918136},
+ {100125732, 61584625},
+ {99618148, 62413436},
+ {99457214, 62709442},
+ {99368347, 62914794},
+ {99166992, 63728332},
+ {98313827, 69634780},
+ {98176910, 70615707},
+ {98162902, 70798233},
+ {98163757, 71203430},
+ },
+ {
+ {79090698, 116426399},
+ {80959800, 137087692},
+ {81030303, 137762298},
+ {81190704, 138903503},
+ {81253700, 139084197},
+ {81479301, 139544998},
+ {81952003, 140118896},
+ {82319900, 140523895},
+ {82967803, 140993896},
+ {83022903, 141032104},
+ {83777900, 141493606},
+ {84722099, 141849899},
+ {84944396, 141887207},
+ {86144699, 141915893},
+ {87643997, 141938095},
+ {88277503, 141887695},
+ {88582099, 141840606},
+ {89395401, 141712203},
+ {90531204, 141528396},
+ {91014801, 141438400},
+ {92097595, 141190093},
+ {123348297, 132876998},
+ {123399505, 132860000},
+ {123452804, 132841506},
+ {123515502, 132818908},
+ {123543800, 132806198},
+ {124299598, 132437393},
+ {124975502, 132042098},
+ {125047500, 131992202},
+ {125119506, 131930603},
+ {166848800, 86317703},
+ {168976409, 83524902},
+ {169359603, 82932701},
+ {169852600, 81917800},
+ {170686904, 79771202},
+ {170829406, 79245597},
+ {170885498, 78796295},
+ {170909301, 78531898},
+ {170899703, 78238700},
+ {170842803, 77553199},
+ {170701293, 76723495},
+ {170302307, 75753898},
+ {169924301, 75067398},
+ {169359802, 74578796},
+ {168148605, 73757499},
+ {163261596, 71124702},
+ {162986007, 70977798},
+ {162248703, 70599098},
+ {158193405, 68923995},
+ {157514297, 68667495},
+ {156892700, 68495201},
+ {156607299, 68432998},
+ {154301895, 68061904},
+ {93440299, 68061904},
+ {88732002, 68255996},
+ {88627304, 68298500},
+ {88111396, 68541900},
+ {86393898, 69555404},
+ {86138298, 69706695},
+ {85871704, 69913200},
+ {85387199, 70393402},
+ {79854499, 76783203},
+ {79209701, 77649398},
+ {79108505, 78072502},
+ {79090698, 78472198},
+ {79090698, 116426399},
+ },
+ {
+ {90956314, 84639938},
+ {91073814, 85141891},
+ {91185752, 85505371},
+ {109815368, 137196487},
+ {110342590, 138349899},
+ {110388549, 138447540},
+ {110652862, 138971343},
+ {110918045, 139341140},
+ {114380859, 143159042},
+ {114446723, 143220352},
+ {114652198, 143392166},
+ {114712196, 143437301},
+ {114782165, 143476028},
+ {114873054, 143514923},
+ {115217086, 143660934},
+ {115306060, 143695526},
+ {115344009, 143707580},
+ {115444541, 143737747},
+ {115589378, 143779937},
+ {115751358, 143823989},
+ {115802780, 143825820},
+ {116872810, 143753616},
+ {116927055, 143744644},
+ {154690734, 133504180},
+ {155009704, 133371856},
+ {155029907, 133360061},
+ {155089141, 133323181},
+ {155342315, 133163360},
+ {155602294, 132941406},
+ {155669158, 132880294},
+ {155821624, 132737884},
+ {155898986, 132656890},
+ {155934936, 132608932},
+ {155968627, 132562713},
+ {156062896, 132431808},
+ {156111694, 132363174},
+ {156148147, 132297180},
+ {158738342, 127281066},
+ {159026672, 126378631},
+ {159073699, 125806335},
+ {159048522, 125299743},
+ {159040313, 125192901},
+ {158898300, 123934677},
+ {149829376, 70241508},
+ {149763031, 69910629},
+ {149684692, 69628723},
+ {149557800, 69206214},
+ {149366485, 68864326},
+ {149137390, 68578514},
+ {148637466, 68048767},
+ {147027725, 66632934},
+ {146228607, 66257507},
+ {146061309, 66184646},
+ {146017929, 66174186},
+ {145236465, 66269500},
+ {144802490, 66345039},
+ {144673995, 66376220},
+ {93732284, 79649864},
+ {93345336, 79785865},
+ {93208084, 79840286},
+ {92814521, 79997779},
+ {92591087, 80098968},
+ {92567016, 80110511},
+ {92032684, 80860725},
+ {91988853, 80930152},
+ {91471725, 82210029},
+ {91142349, 83076683},
+ {90969284, 83653182},
+ {90929664, 84043212},
+ {90926315, 84325256},
+ {90956314, 84639938},
+ },
+ {
+ {114758499, 88719909},
+ {114771591, 88860549},
+ {115515533, 94195907},
+ {115559539, 94383651},
+ {119882980, 109502059},
+ {120660522, 111909683},
+ {126147735, 124949630},
+ {127127212, 127107215},
+ {129976379, 132117279},
+ {130754470, 133257080},
+ {130820968, 133340835},
+ {130889312, 133423858},
+ {131094787, 133652832},
+ {131257629, 133828247},
+ {131678619, 134164276},
+ {131791107, 134248901},
+ {131969482, 134335189},
+ {132054107, 134373718},
+ {132927368, 134701141},
+ {133077072, 134749313},
+ {133196075, 134785705},
+ {133345230, 134804351},
+ {133498809, 134809051},
+ {133611541, 134797607},
+ {134621170, 134565322},
+ {134741165, 134527511},
+ {134892089, 134465240},
+ {135071212, 134353820},
+ {135252029, 134185821},
+ {135384979, 134003631},
+ {135615585, 133576675},
+ {135793029, 132859008},
+ {135890228, 131382904},
+ {135880828, 131261657},
+ {135837570, 130787963},
+ {135380661, 127428909},
+ {132830596, 109495368},
+ {132815826, 109411666},
+ {132765869, 109199302},
+ {132724380, 109068161},
+ {127490066, 93353515},
+ {125330810, 87852828},
+ {125248336, 87647026},
+ {125002182, 87088424},
+ {124894592, 86872482},
+ {121007278, 80019584},
+ {120962829, 79941261},
+ {120886489, 79833923},
+ {120154983, 78949615},
+ {119366561, 78111709},
+ {119014755, 77776794},
+ {116728790, 75636238},
+ {116660522, 75593933},
+ {116428192, 75458541},
+ {116355255, 75416870},
+ {116264663, 75372528},
+ {115952728, 75233367},
+ {115865554, 75205482},
+ {115756835, 75190956},
+ {115564163, 75197830},
+ {115481170, 75202087},
+ {115417144, 75230400},
+ {115226959, 75337806},
+ {115203842, 75351448},
+ {114722015, 75746932},
+ {114672103, 75795661},
+ {114594619, 75891891},
+ {114565811, 75973831},
+ {114478256, 76240814},
+ {114178039, 77252197},
+ {114137664, 77769668},
+ {114109771, 78154464},
+ {114758499, 88719909},
+ },
+ {
+ {108135070, 109828002},
+ {108200347, 110091529},
+ {108319419, 110298500},
+ {108439025, 110488388},
+ {108663574, 110766731},
+ {108812957, 110935768},
+ {109321914, 111398925},
+ {109368087, 111430320},
+ {109421295, 111466331},
+ {110058998, 111849746},
+ {127160308, 120588981},
+ {127350692, 120683456},
+ {128052749, 120997207},
+ {128326919, 121113449},
+ {131669586, 122213058},
+ {131754745, 122240592},
+ {131854583, 122264770},
+ {132662048, 122449813},
+ {132782669, 122449897},
+ {132909118, 122443687},
+ {133013442, 122436058},
+ {140561035, 121609939},
+ {140786346, 121583320},
+ {140876144, 121570228},
+ {140962356, 121547996},
+ {141052612, 121517837},
+ {141231292, 121442184},
+ {141309371, 121390007},
+ {141370132, 121327003},
+ {141456008, 121219932},
+ {141591598, 121045005},
+ {141905761, 120634796},
+ {141894607, 120305725},
+ {141881881, 120110855},
+ {141840881, 119885009},
+ {141685043, 119238922},
+ {141617416, 118962882},
+ {141570434, 118858856},
+ {131617462, 100598548},
+ {131542846, 100487213},
+ {131229385, 100089019},
+ {131091476, 99928108},
+ {119824127, 90297180},
+ {119636337, 90142387},
+ {119507492, 90037765},
+ {119436744, 89983657},
+ {119423942, 89974159},
+ {119207366, 89822471},
+ {119117149, 89767097},
+ {119039489, 89726867},
+ {116322929, 88522857},
+ {114817031, 87882110},
+ {114683975, 87826751},
+ {114306411, 87728507},
+ {113876434, 87646003},
+ {113792106, 87629974},
+ {113658988, 87615974},
+ {113574333, 87609275},
+ {112813575, 87550102},
+ {112578567, 87560157},
+ {112439880, 87571647},
+ {112306922, 87599395},
+ {112225082, 87622535},
+ {112132568, 87667175},
+ {112103477, 87682830},
+ {110795242, 88511634},
+ {110373565, 88847793},
+ {110286537, 88934989},
+ {109730873, 89531501},
+ {109648735, 89628883},
+ {109552581, 89768859},
+ {109514228, 89838470},
+ {109501640, 89877586},
+ {109480964, 89941864},
+ {109461761, 90032417},
+ {109457778, 90055458},
+ {108105194, 109452575},
+ {108094238, 109620979},
+ {108135070, 109828002},
+ },
+ {
+ {108764694, 108910400},
+ {108965499, 112306495},
+ {109598602, 120388298},
+ {110573898, 128289596},
+ {110597801, 128427795},
+ {113786201, 137983795},
+ {113840301, 138134704},
+ {113937202, 138326904},
+ {114046005, 138520401},
+ {114150802, 138696792},
+ {114164703, 138717895},
+ {114381896, 139021194},
+ {114701004, 139425292},
+ {114997398, 139747497},
+ {115065597, 139805191},
+ {115134498, 139850891},
+ {115167098, 139871704},
+ {115473396, 139992797},
+ {115537498, 139995101},
+ {116762596, 139832000},
+ {116897499, 139808593},
+ {118401802, 139225585},
+ {118437500, 139209594},
+ {118488204, 139182189},
+ {118740097, 139033996},
+ {118815795, 138967285},
+ {134401000, 116395492},
+ {134451507, 116309997},
+ {135488098, 113593597},
+ {137738006, 106775695},
+ {140936492, 97033889},
+ {140960006, 96948997},
+ {141026504, 96660995},
+ {141067291, 96467094},
+ {141124893, 95771896},
+ {141511795, 90171600},
+ {141499801, 90026000},
+ {141479598, 89907798},
+ {141276794, 88844596},
+ {141243804, 88707397},
+ {140778305, 87031593},
+ {140733306, 86871696},
+ {140697204, 86789993},
+ {140619796, 86708190},
+ {140398391, 86487396},
+ {125798797, 72806198},
+ {125415802, 72454498},
+ {123150398, 70566093},
+ {123038803, 70503997},
+ {122681198, 70305397},
+ {121919204, 70104797},
+ {121533699, 70008094},
+ {121273696, 70004898},
+ {121130599, 70020797},
+ {121045097, 70033294},
+ {120847099, 70082298},
+ {120481895, 70278999},
+ {120367004, 70379692},
+ {120272796, 70475097},
+ {119862098, 71004791},
+ {119745101, 71167297},
+ {119447799, 71726997},
+ {119396499, 71825798},
+ {119348701, 71944496},
+ {109508796, 98298797},
+ {109368598, 98700897},
+ {109298400, 98926391},
+ {108506301, 102750991},
+ {108488197, 102879898},
+ {108764694, 108910400},
+ },
+ {
+ {106666252, 87231246},
+ {106673248, 87358055},
+ {107734146, 101975646},
+ {107762649, 102357955},
+ {108702445, 111208351},
+ {108749450, 111345153},
+ {108848350, 111542648},
+ {110270645, 114264358},
+ {110389648, 114445144},
+ {138794845, 143461151},
+ {139048355, 143648956},
+ {139376144, 143885345},
+ {139594451, 144022644},
+ {139754043, 144110046},
+ {139923950, 144185852},
+ {140058242, 144234451},
+ {140185653, 144259552},
+ {140427551, 144292648},
+ {141130950, 144281448},
+ {141157653, 144278152},
+ {141214355, 144266555},
+ {141347457, 144223449},
+ {141625350, 144098953},
+ {141755142, 144040145},
+ {141878143, 143971557},
+ {142011444, 143858154},
+ {142076843, 143796356},
+ {142160644, 143691055},
+ {142224456, 143560852},
+ {142925842, 142090850},
+ {142935653, 142065353},
+ {142995956, 141899154},
+ {143042556, 141719757},
+ {143102951, 141436157},
+ {143129257, 141230453},
+ {143316055, 139447250},
+ {143342544, 133704650},
+ {143307556, 130890960},
+ {142461257, 124025558},
+ {141916046, 120671051},
+ {141890457, 120526153},
+ {140002349, 113455749},
+ {139909149, 113144149},
+ {139853454, 112974456},
+ {137303756, 105228057},
+ {134700546, 98161254},
+ {134617950, 97961547},
+ {133823547, 96118057},
+ {133688751, 95837356},
+ {133481353, 95448059},
+ {133205444, 94948150},
+ {131178955, 91529853},
+ {131144744, 91482055},
+ {113942047, 67481246},
+ {113837051, 67360549},
+ {113048950, 66601745},
+ {112305549, 66002746},
+ {112030853, 65790351},
+ {111970649, 65767547},
+ {111912445, 65755249},
+ {111854248, 65743453},
+ {111657447, 65716354},
+ {111576950, 65707351},
+ {111509750, 65708549},
+ {111443550, 65718551},
+ {111397247, 65737449},
+ {111338546, 65764648},
+ {111129547, 65863349},
+ {111112449, 65871551},
+ {110995254, 65927856},
+ {110968849, 65946151},
+ {110941444, 65966751},
+ {110836448, 66057853},
+ {110490447, 66445449},
+ {110404144, 66576751},
+ {106802055, 73202148},
+ {106741950, 73384948},
+ {106715454, 73469650},
+ {106678054, 73627151},
+ {106657455, 75433448},
+ {106666252, 87231246},
+ },
+ {
+ {101852752, 106261352},
+ {101868949, 106406051},
+ {102347549, 108974250},
+ {112286750, 152027954},
+ {112305648, 152106536},
+ {112325752, 152175857},
+ {112391448, 152290863},
+ {113558250, 154187454},
+ {113592048, 154226745},
+ {113694351, 154313156},
+ {113736549, 154335647},
+ {113818145, 154367462},
+ {114284454, 154490951},
+ {114415847, 154504547},
+ {114520751, 154489151},
+ {114571350, 154478057},
+ {114594551, 154472854},
+ {114630546, 154463958},
+ {114715148, 154429443},
+ {146873657, 136143051},
+ {146941741, 136074249},
+ {147190155, 135763549},
+ {147262649, 135654937},
+ {147309951, 135557159},
+ {147702255, 133903945},
+ {147934143, 131616348},
+ {147967041, 131273864},
+ {148185852, 127892250},
+ {148195648, 127669754},
+ {148179656, 126409851},
+ {148119552, 126182151},
+ {147874053, 125334152},
+ {147818954, 125150352},
+ {146958557, 122656646},
+ {139070251, 101025955},
+ {139002655, 100879051},
+ {119028450, 63067649},
+ {118846649, 62740753},
+ {115676048, 57814651},
+ {115550453, 57629852},
+ {115330352, 57319751},
+ {115094749, 56998352},
+ {114978347, 56847454},
+ {114853050, 56740550},
+ {114695053, 56609550},
+ {114582252, 56528148},
+ {114210449, 56375953},
+ {113636245, 56214950},
+ {113470352, 56171649},
+ {109580749, 55503551},
+ {109491645, 55495452},
+ {109238754, 55511550},
+ {109080352, 55534049},
+ {108027748, 55687351},
+ {107839950, 55732349},
+ {107614456, 55834953},
+ {107488143, 55925952},
+ {107302551, 56062553},
+ {107218353, 56145751},
+ {107199447, 56167251},
+ {107052749, 56354850},
+ {106978652, 56476348},
+ {106869644, 56710754},
+ {104541351, 62448753},
+ {104454551, 62672554},
+ {104441253, 62707351},
+ {104231750, 63366348},
+ {104222648, 63419952},
+ {104155746, 63922649},
+ {104127349, 64147552},
+ {104110847, 64299957},
+ {102235450, 92366752},
+ {101804351, 102877655},
+ {101852752, 106261352},
+ },
+ {
+ {106808700, 120885696},
+ {106818695, 120923103},
+ {106873901, 121057098},
+ {115123603, 133614700},
+ {115128799, 133619598},
+ {115182197, 133661804},
+ {115330101, 133740707},
+ {115455398, 133799407},
+ {115595001, 133836807},
+ {115651000, 133851806},
+ {116413604, 134055206},
+ {116654495, 134097900},
+ {116887603, 134075210},
+ {117071098, 134040405},
+ {117458801, 133904891},
+ {118057998, 133572601},
+ {118546997, 133261001},
+ {118578498, 133239395},
+ {118818603, 133011596},
+ {121109695, 130501495},
+ {122661598, 128760101},
+ {142458190, 102765197},
+ {142789001, 102099601},
+ {143105010, 101386505},
+ {143154800, 101239700},
+ {143193908, 100825500},
+ {143160507, 100282501},
+ {143133499, 100083602},
+ {143092697, 99880500},
+ {143050689, 99766700},
+ {142657501, 98974502},
+ {142580307, 98855201},
+ {122267196, 76269897},
+ {122036399, 76105003},
+ {121832000, 76028305},
+ {121688796, 75983108},
+ {121591598, 75955001},
+ {121119697, 75902099},
+ {120789596, 75953498},
+ {120487495, 76041900},
+ {120042701, 76365798},
+ {119886695, 76507301},
+ {119774200, 76635299},
+ {119739097, 76686904},
+ {119685195, 76798202},
+ {119456199, 77320098},
+ {106877601, 119561401},
+ {106854797, 119645103},
+ {106849098, 119668807},
+ {106847099, 119699005},
+ {106840400, 119801406},
+ {106807800, 120719299},
+ {106806098, 120862808},
+ {106808700, 120885696},
+ },
+ {
+ {99663352, 105328948},
+ {99690048, 105797050},
+ {99714050, 105921447},
+ {99867248, 106439949},
+ {100111557, 107256546},
+ {104924850, 120873649},
+ {105106155, 121284049},
+ {105519149, 122184753},
+ {105586051, 122292655},
+ {105665054, 122400154},
+ {106064147, 122838455},
+ {106755355, 123453453},
+ {106929054, 123577651},
+ {107230346, 123771949},
+ {107760650, 123930648},
+ {108875854, 124205154},
+ {108978752, 124228050},
+ {131962051, 123738754},
+ {135636047, 123513954},
+ {135837249, 123500747},
+ {136357345, 123442749},
+ {136577346, 123394454},
+ {136686645, 123367752},
+ {137399353, 123185050},
+ {137733947, 123063156},
+ {137895355, 122997154},
+ {138275650, 122829154},
+ {138394256, 122767753},
+ {138516845, 122670150},
+ {139987045, 121111251},
+ {149171646, 108517349},
+ {149274353, 108372848},
+ {149314758, 108314247},
+ {149428848, 108140846},
+ {149648651, 107650550},
+ {149779541, 107290252},
+ {149833343, 107115249},
+ {149891357, 106920051},
+ {150246353, 105630249},
+ {150285842, 105423454},
+ {150320953, 105233749},
+ {150336639, 104981552},
+ {150298049, 104374053},
+ {150287948, 104271850},
+ {150026153, 103481147},
+ {149945449, 103301651},
+ {149888946, 103213455},
+ {149800949, 103103851},
+ {149781143, 103079650},
+ {149714141, 103005447},
+ {149589950, 102914146},
+ {149206054, 102698951},
+ {128843856, 91378150},
+ {128641754, 91283050},
+ {119699851, 87248046},
+ {117503555, 86311950},
+ {117145851, 86178054},
+ {116323654, 85925048},
+ {115982551, 85834045},
+ {115853050, 85819252},
+ {115222549, 85771949},
+ {107169357, 85771949},
+ {107122650, 85776451},
+ {106637145, 85831550},
+ {105095046, 86423950},
+ {104507850, 86703750},
+ {104384155, 86763153},
+ {104332351, 86790145},
+ {104198257, 86882644},
+ {103913757, 87109451},
+ {103592346, 87388450},
+ {103272651, 87666748},
+ {103198051, 87779052},
+ {101698654, 90600952},
+ {101523551, 90958450},
+ {101360054, 91347450},
+ {101295349, 91542144},
+ {99774551, 98278152},
+ {99746749, 98417755},
+ {99704055, 98675453},
+ {99663352, 99022949},
+ {99663352, 105328948},
+ },
+ {
+ {95036499, 101778106},
+ {95479103, 102521301},
+ {95587295, 102700103},
+ {98306503, 106984901},
+ {98573303, 107377700},
+ {100622406, 110221702},
+ {101252304, 111089599},
+ {104669502, 115750198},
+ {121838500, 131804107},
+ {122000503, 131943695},
+ {122176803, 132023406},
+ {122474105, 132025390},
+ {122703804, 132023101},
+ {123278808, 131878112},
+ {124072998, 131509109},
+ {124466506, 131102508},
+ {152779296, 101350906},
+ {153016510, 101090606},
+ {153269699, 100809097},
+ {153731994, 100214096},
+ {153927902, 99939796},
+ {154641098, 98858100},
+ {154864303, 98517601},
+ {155056594, 97816604},
+ {155083511, 97645599},
+ {155084899, 97462097},
+ {154682601, 94386100},
+ {154376007, 92992599},
+ {154198593, 92432403},
+ {153830505, 91861701},
+ {153686904, 91678695},
+ {151907104, 90314605},
+ {151368896, 89957603},
+ {146983306, 87632202},
+ {139082397, 84273605},
+ {128947692, 80411399},
+ {121179000, 78631301},
+ {120264701, 78458198},
+ {119279510, 78304603},
+ {116913101, 77994102},
+ {116151504, 77974601},
+ {115435104, 78171401},
+ {113544105, 78709106},
+ {113231002, 78879898},
+ {112726303, 79163604},
+ {112310501, 79411102},
+ {96169998, 97040802},
+ {95196304, 98364402},
+ {95167800, 98409599},
+ {95083503, 98570701},
+ {94986999, 99022201},
+ {94915100, 100413299},
+ {95036499, 101778106},
+ },
+ {
+ {82601348, 96004745},
+ {83443847, 128861953},
+ {84173248, 136147354},
+ {104268249, 141388839},
+ {104373649, 141395355},
+ {105686950, 141389541},
+ {149002243, 140435653},
+ {159095748, 133388244},
+ {159488143, 133112655},
+ {159661849, 132894653},
+ {163034149, 128290847},
+ {164801849, 124684249},
+ {167405746, 72553245},
+ {167330444, 71960746},
+ {167255050, 71791847},
+ {167147155, 71572044},
+ {166999557, 71341545},
+ {166723937, 70961448},
+ {166238250, 70611541},
+ {165782348, 70359649},
+ {165649444, 70286849},
+ {165332946, 70122344},
+ {165164154, 70062248},
+ {164879150, 69967544},
+ {164744949, 69928947},
+ {164691452, 69915245},
+ {164669448, 69910247},
+ {159249938, 68738952},
+ {158528259, 68704742},
+ {147564254, 68604644},
+ {116196655, 68982742},
+ {115364944, 69005050},
+ {115193145, 69013549},
+ {101701248, 70984146},
+ {93918449, 72233047},
+ {93789749, 72285247},
+ {93777046, 72292648},
+ {93586044, 72444046},
+ {93366348, 72662345},
+ {93301147, 72745452},
+ {93260345, 72816345},
+ {83523948, 92593849},
+ {83430145, 92810241},
+ {82815048, 94665542},
+ {82755554, 94858551},
+ {82722953, 95014350},
+ {82594253, 95682350},
+ {82601348, 96004745},
+ },
+ {
+ {110371345, 125796493},
+ {110411544, 126159599},
+ {110445251, 126362899},
+ {111201950, 127863800},
+ {112030052, 129270492},
+ {112367050, 129799301},
+ {113088348, 130525604},
+ {113418144, 130853698},
+ {117363449, 134705505},
+ {118131149, 135444793},
+ {118307449, 135607299},
+ {119102546, 136297195},
+ {119385047, 136531906},
+ {120080848, 137094390},
+ {120794845, 137645401},
+ {121150344, 137896392},
+ {121528945, 138162506},
+ {121644546, 138242095},
+ {122142349, 138506408},
+ {127540847, 141363006},
+ {127933448, 141516204},
+ {128728256, 141766799},
+ {129877151, 141989898},
+ {130626052, 142113891},
+ {130912246, 142135192},
+ {131246841, 142109100},
+ {131496047, 142027404},
+ {131596252, 141957794},
+ {131696350, 141873504},
+ {131741043, 141803405},
+ {138788452, 128037704},
+ {139628646, 125946197},
+ {138319351, 112395401},
+ {130035354, 78066703},
+ {124174049, 69908798},
+ {123970649, 69676895},
+ {123874252, 69571899},
+ {123246643, 68961303},
+ {123193954, 68924400},
+ {121952049, 68110000},
+ {121787345, 68021896},
+ {121661544, 67970306},
+ {121313446, 67877502},
+ {121010650, 67864799},
+ {120995346, 67869705},
+ {120583747, 68122207},
+ {120509750, 68170600},
+ {120485847, 68189102},
+ {112160148, 77252403},
+ {111128646, 78690704},
+ {110969650, 78939407},
+ {110512550, 79663406},
+ {110397247, 79958206},
+ {110371345, 80038299},
+ {110371345, 125796493},
+ },
+ {
+ {112163948, 137752700},
+ {112171150, 137837997},
+ {112203048, 137955993},
+ {112240150, 138008209},
+ {112343246, 138111099},
+ {112556243, 138223205},
+ {112937149, 138307998},
+ {113318748, 138331909},
+ {126076446, 138428298},
+ {126165245, 138428695},
+ {126312446, 138417907},
+ {134075546, 136054504},
+ {134322753, 135949401},
+ {134649948, 135791198},
+ {135234954, 135493408},
+ {135290145, 135464691},
+ {135326248, 135443695},
+ {135920043, 135032592},
+ {135993850, 134975799},
+ {136244247, 134761199},
+ {136649444, 134378692},
+ {137067153, 133964294},
+ {137188156, 133839096},
+ {137298049, 133704498},
+ {137318954, 133677795},
+ {137413543, 133522201},
+ {137687347, 133043792},
+ {137816055, 132660705},
+ {137836044, 131747695},
+ {137807144, 131318603},
+ {136279342, 119078704},
+ {136249053, 118945800},
+ {127306152, 81348602},
+ {127114852, 81065505},
+ {127034248, 80951400},
+ {126971649, 80893707},
+ {125093551, 79178001},
+ {124935745, 79036003},
+ {115573745, 71767601},
+ {115411148, 71701805},
+ {115191947, 71621002},
+ {115017051, 71571304},
+ {114870147, 71572898},
+ {113869552, 71653900},
+ {112863349, 72976104},
+ {112756347, 73223899},
+ {112498947, 73832206},
+ {112429351, 73998504},
+ {112366050, 74168098},
+ {112273246, 74487098},
+ {112239250, 74605400},
+ {112195549, 74899902},
+ {112163948, 75280700},
+ {112163948, 137752700},
+ },
+ {
+ {78562347, 141451843},
+ {79335624, 142828186},
+ {79610343, 143188140},
+ {79845077, 143445724},
+ {81379173, 145126678},
+ {81826751, 145577178},
+ {82519126, 146209472},
+ {83964973, 147280502},
+ {85471343, 148377868},
+ {86115539, 148760803},
+ {88839988, 150281188},
+ {89021247, 150382217},
+ {90775917, 151320526},
+ {91711380, 151767288},
+ {92757591, 152134277},
+ {93241058, 152201766},
+ {113402145, 153091995},
+ {122065994, 146802825},
+ {164111053, 91685104},
+ {164812759, 90470565},
+ {165640182, 89037384},
+ {171027435, 66211853},
+ {171450805, 64406951},
+ {171463150, 64349624},
+ {171469787, 64317184},
+ {171475585, 64282028},
+ {171479812, 64253036},
+ {171483596, 64210433},
+ {171484405, 64153488},
+ {171483001, 64140785},
+ {171481719, 64132751},
+ {171478668, 64115478},
+ {171472702, 64092437},
+ {171462768, 64075408},
+ {171448089, 64061347},
+ {171060333, 63854789},
+ {169640502, 63197738},
+ {169342147, 63086711},
+ {166413101, 62215766},
+ {151881774, 58826736},
+ {146010574, 57613151},
+ {141776962, 56908004},
+ {140982940, 57030628},
+ {139246154, 57540817},
+ {139209609, 57566974},
+ {127545310, 66015594},
+ {127476654, 66104812},
+ {105799087, 98784980},
+ {85531921, 129338897},
+ {79319717, 138704513},
+ {78548156, 140188079},
+ {78530448, 140530456},
+ {78515594, 141299987},
+ {78562347, 141451843},
+ },
+ {
+ {77755004, 128712387},
+ {78073547, 130552612},
+ {78433593, 132017822},
+ {79752693, 136839645},
+ {80479461, 138929260},
+ {80903221, 140119674},
+ {81789848, 141978454},
+ {82447387, 143105575},
+ {83288436, 144264328},
+ {84593582, 145846542},
+ {84971939, 146242813},
+ {86905578, 147321304},
+ {87874191, 147594131},
+ {89249092, 147245132},
+ {89541542, 147169052},
+ {98759140, 144071609},
+ {98894233, 144024261},
+ {113607818, 137992843},
+ {128324356, 131649307},
+ {139610076, 126210189},
+ {146999572, 122112884},
+ {147119415, 122036041},
+ {148717330, 120934616},
+ {149114776, 120652725},
+ {171640289, 92086624},
+ {171677917, 92036224},
+ {171721191, 91973869},
+ {171851608, 91721557},
+ {171927795, 91507644},
+ {172398696, 89846351},
+ {172436752, 89559959},
+ {169361663, 64753852},
+ {169349029, 64687164},
+ {169115127, 63616458},
+ {168965728, 63218254},
+ {168911788, 63121219},
+ {168901611, 63106807},
+ {168896896, 63100486},
+ {168890686, 63092460},
+ {168876586, 63081058},
+ {168855529, 63067909},
+ {168808746, 63046024},
+ {167251068, 62405864},
+ {164291717, 63716899},
+ {152661651, 69910156},
+ {142312393, 75421356},
+ {78778053, 111143295},
+ {77887222, 113905914},
+ {77591979, 124378433},
+ {77563247, 126586669},
+ {77755004, 128712387},
+ },
+ {
+ {105954101, 131182754},
+ {105959197, 131275848},
+ {105972801, 131473556},
+ {105981498, 131571044},
+ {106077903, 132298553},
+ {106134094, 132715255},
+ {106155700, 132832351},
+ {106180099, 132942657},
+ {106326797, 133590347},
+ {106375099, 133719345},
+ {106417602, 133829345},
+ {106471000, 133930343},
+ {106707901, 134308654},
+ {106728401, 134340545},
+ {106778198, 134417556},
+ {106832397, 134491851},
+ {106891296, 134562957},
+ {106981300, 134667358},
+ {107044204, 134736557},
+ {107111000, 134802658},
+ {107180999, 134865661},
+ {107291099, 134961349},
+ {107362998, 135020355},
+ {107485397, 135112854},
+ {107558998, 135166946},
+ {107690399, 135256256},
+ {107765098, 135305252},
+ {107903594, 135390548},
+ {108183898, 135561843},
+ {108459503, 135727951},
+ {108532501, 135771850},
+ {108796096, 135920059},
+ {108944099, 135972549},
+ {109102401, 136010757},
+ {109660598, 136071044},
+ {109971595, 136100250},
+ {110209594, 136116851},
+ {110752799, 136122344},
+ {111059906, 136105758},
+ {111152900, 136100357},
+ {111237197, 136091354},
+ {111316101, 136075057},
+ {111402000, 136050949},
+ {111475296, 136026657},
+ {143546600, 123535949},
+ {143899002, 122454353},
+ {143917404, 122394348},
+ {143929199, 122354652},
+ {143944793, 122295753},
+ {143956207, 122250953},
+ {143969497, 122192253},
+ {143980102, 122143249},
+ {143991302, 122083053},
+ {144000396, 122031753},
+ {144009796, 121970954},
+ {144017303, 121917655},
+ {144025405, 121850250},
+ {144030609, 121801452},
+ {144036804, 121727455},
+ {144040008, 121683456},
+ {144043502, 121600952},
+ {144044708, 121565048},
+ {144045700, 121470352},
+ {144045898, 121446952},
+ {144041503, 121108657},
+ {144037506, 121023452},
+ {143733795, 118731750},
+ {140461395, 95238647},
+ {140461105, 95236755},
+ {140433807, 95115249},
+ {140392608, 95011650},
+ {134840805, 84668952},
+ {134824996, 84642456},
+ {134781494, 84572952},
+ {134716796, 84480850},
+ {127473899, 74425453},
+ {127467002, 74417152},
+ {127431701, 74381652},
+ {127402603, 74357147},
+ {127375503, 74334457},
+ {127294906, 74276649},
+ {127181900, 74207649},
+ {127177597, 74205451},
+ {127123901, 74178451},
+ {127078903, 74155853},
+ {127028999, 74133148},
+ {126870803, 74070953},
+ {126442901, 73917648},
+ {126432403, 73914955},
+ {126326004, 73889846},
+ {126262405, 73880645},
+ {126128097, 73878456},
+ {125998199, 73877655},
+ {108701095, 74516647},
+ {108644599, 74519348},
+ {108495201, 74528953},
+ {108311302, 74556457},
+ {108252799, 74569458},
+ {108079002, 74612152},
+ {107981399, 74638954},
+ {107921295, 74657951},
+ {107862197, 74685951},
+ {107601303, 74828948},
+ {107546997, 74863449},
+ {107192794, 75098846},
+ {107131202, 75151153},
+ {106260002, 76066146},
+ {106195098, 76221145},
+ {106168502, 76328453},
+ {106144699, 76437454},
+ {106124496, 76538452},
+ {106103698, 76649650},
+ {106084197, 76761650},
+ {106066299, 76874450},
+ {106049903, 76987457},
+ {106034797, 77101150},
+ {106020904, 77214950},
+ {106008201, 77328948},
+ {105996902, 77443145},
+ {105986099, 77565849},
+ {105977005, 77679649},
+ {105969299, 77793151},
+ {105963096, 77906349},
+ {105958297, 78019149},
+ {105955299, 78131454},
+ {105954101, 78242950},
+ {105954101, 131182754},
+ },
+ {
+ {91355499, 77889205},
+ {114834197, 120804504},
+ {114840301, 120815200},
+ {124701507, 132324798},
+ {124798805, 132436706},
+ {124901504, 132548309},
+ {125126602, 132788909},
+ {125235000, 132901901},
+ {125337707, 133005401},
+ {125546302, 133184707},
+ {125751602, 133358703},
+ {126133300, 133673004},
+ {126263900, 133775604},
+ {126367401, 133855499},
+ {126471908, 133935104},
+ {126596008, 134027496},
+ {127119308, 134397094},
+ {127135101, 134408203},
+ {127433609, 134614303},
+ {127554107, 134695709},
+ {128155395, 135070907},
+ {128274505, 135141799},
+ {129132003, 135573211},
+ {129438003, 135713195},
+ {129556106, 135767196},
+ {131512695, 136648498},
+ {132294509, 136966598},
+ {132798400, 137158798},
+ {133203796, 137294494},
+ {133377410, 137350799},
+ {133522399, 137396606},
+ {133804397, 137480697},
+ {134017807, 137542205},
+ {134288696, 137618408},
+ {134564208, 137680099},
+ {134844696, 137740097},
+ {135202606, 137807098},
+ {135489105, 137849807},
+ {135626800, 137864898},
+ {135766906, 137878692},
+ {135972808, 137895797},
+ {136110107, 137905502},
+ {136235000, 137913101},
+ {136485809, 137907196},
+ {139194305, 136979202},
+ {140318298, 136536209},
+ {140380004, 136505004},
+ {140668197, 136340499},
+ {140724304, 136298904},
+ {140808197, 136228210},
+ {140861801, 136180603},
+ {140917404, 136129104},
+ {140979202, 136045104},
+ {141022903, 135984207},
+ {147591094, 126486999},
+ {147661315, 126356101},
+ {147706100, 126261901},
+ {147749099, 126166000},
+ {147817108, 126007507},
+ {147859100, 125908599},
+ {153693206, 111901100},
+ {153731109, 111807800},
+ {153760894, 111698806},
+ {158641998, 92419303},
+ {158644500, 92263702},
+ {158539703, 92013504},
+ {158499603, 91918899},
+ {158335510, 91626800},
+ {158264007, 91516304},
+ {158216308, 91449203},
+ {158178314, 91397506},
+ {158094299, 91283203},
+ {157396408, 90368202},
+ {157285491, 90224700},
+ {157169906, 90079200},
+ {157050003, 89931304},
+ {156290603, 89006805},
+ {156221099, 88922897},
+ {156087707, 88771003},
+ {155947906, 88620498},
+ {155348602, 88004203},
+ {155113204, 87772796},
+ {154947296, 87609703},
+ {154776306, 87448204},
+ {154588806, 87284301},
+ {153886306, 86716400},
+ {153682403, 86560501},
+ {152966705, 86032402},
+ {152687805, 85828704},
+ {152484313, 85683204},
+ {152278808, 85539001},
+ {150878204, 84561401},
+ {150683013, 84426498},
+ {150599395, 84372703},
+ {150395599, 84243202},
+ {149988906, 83989395},
+ {149782897, 83864501},
+ {149568908, 83739799},
+ {148872100, 83365303},
+ {148625396, 83242202},
+ {128079010, 73079605},
+ {127980506, 73031005},
+ {126701103, 72407104},
+ {126501701, 72312202},
+ {126431503, 72280601},
+ {126311706, 72230606},
+ {126260101, 72210899},
+ {126191902, 72187599},
+ {126140106, 72170303},
+ {126088203, 72155303},
+ {126036102, 72142700},
+ {125965904, 72126899},
+ {125913009, 72116600},
+ {125859603, 72108505},
+ {125788101, 72100296},
+ {125733505, 72094398},
+ {125678100, 72090400},
+ {125621398, 72088302},
+ {125548805, 72087303},
+ {125490707, 72086898},
+ {125430908, 72088203},
+ {125369804, 72091094},
+ {125306900, 72095306},
+ {125233505, 72100997},
+ {125168609, 72106506},
+ {125102203, 72113601},
+ {125034103, 72122207},
+ {124964309, 72132095},
+ {124890701, 72143707},
+ {124819305, 72155105},
+ {91355499, 77889099},
+ {91355499, 77889205},
+ },
+ {
+ {84531845, 127391708},
+ {84916946, 130417510},
+ {86133247, 131166900},
+ {86338447, 131292892},
+ {86748847, 131544799},
+ {102193946, 136599502},
+ {103090942, 136796798},
+ {103247146, 136822509},
+ {104083549, 136911499},
+ {106119346, 137109802},
+ {106265853, 137122207},
+ {106480247, 137139205},
+ {110257850, 137133605},
+ {116917747, 136131408},
+ {117054946, 136106704},
+ {119043945, 135244293},
+ {119249046, 135154708},
+ {136220947, 126833007},
+ {165896347, 91517105},
+ {166032546, 91314697},
+ {166055435, 91204902},
+ {166056152, 91176803},
+ {166047256, 91100006},
+ {166039733, 91063705},
+ {165814849, 90080802},
+ {165736450, 89837707},
+ {165677246, 89732101},
+ {165676956, 89731803},
+ {165560241, 89629302},
+ {154419952, 82608505},
+ {153822143, 82239700},
+ {137942749, 74046104},
+ {137095245, 73845504},
+ {135751342, 73537704},
+ {134225952, 73208602},
+ {132484344, 72860801},
+ {124730346, 73902000},
+ {120736549, 74464401},
+ {100401245, 78685401},
+ {90574645, 90625701},
+ {90475944, 90748809},
+ {90430747, 90808700},
+ {90321548, 90958305},
+ {90254852, 91077903},
+ {90165641, 91244003},
+ {90134941, 91302398},
+ {84474647, 103745697},
+ {84328048, 104137901},
+ {84288543, 104327606},
+ {84038047, 106164604},
+ {84013351, 106368698},
+ {83943847, 110643203},
+ {84531845, 127391708},
+ },
+};
+
+const TestDataEx PRINTER_PART_POLYGONS_EX =
+{
+ {
+ {
+ {533726562, 142141690},
+ {532359712, 143386134},
+ {530141290, 142155145},
+ {528649729, 160091460},
+ {533659500, 157607547},
+ {538669739, 160091454},
+ {537178168, 142155145},
+ {534959534, 143386102},
+ {533726562, 142141690},
+ },
+ {
+ },
+ },
+ {
+ {
+ {118305840, 11603332},
+ {118311095, 26616786},
+ {113311095, 26611146},
+ {109311095, 29604752},
+ {109300760, 44608489},
+ {109311095, 49631801},
+ {113300790, 52636806},
+ {118311095, 52636806},
+ {118308782, 103636810},
+ {223830940, 103636981},
+ {236845321, 90642174},
+ {236832882, 11630488},
+ {232825251, 11616786},
+ {210149075, 11616786},
+ {211308596, 13625149},
+ {209315325, 17080886},
+ {205326885, 17080886},
+ {203334352, 13629720},
+ {204493136, 11616786},
+ {118305840, 11603332},
+ },
+ {
+ },
+ },
+ {
+ {
+ {365619370, 111280336},
+ {365609100, 198818091},
+ {387109100, 198804367},
+ {387109100, 203279701},
+ {471129120, 203279688},
+ {471128689, 111283937},
+ {365619370, 111280336},
+ },
+ {
+ },
+ },
+ {
+ {
+ {479997525, 19177632},
+ {477473010, 21975778},
+ {475272613, 21969219},
+ {475267479, 32995796},
+ {477026388, 32995796},
+ {483041428, 22582411},
+ {482560272, 20318630},
+ {479997525, 19177632},
+ },
+ {
+ },
+ },
+ {
+ {
+ {476809080, 4972372},
+ {475267479, 4975778},
+ {475272613, 16002357},
+ {481018177, 18281994},
+ {482638044, 15466085},
+ {476809080, 4972372},
+ },
+ {
+ },
+ },
+ {
+ {
+ {424866064, 10276075},
+ {415113411, 10277960},
+ {411723180, 13685293},
+ {410473354, 18784347},
+ {382490868, 18784008},
+ {380996185, 17286945},
+ {380996185, 11278161},
+ {375976165, 11284347},
+ {375976165, 56389754},
+ {375169018, 57784347},
+ {371996185, 57784347},
+ {371996185, 53779177},
+ {364976165, 53784347},
+ {364969637, 56791976},
+ {369214608, 61054367},
+ {371474507, 61054367},
+ {371473155, 98298160},
+ {378476349, 105317193},
+ {407491306, 105307497},
+ {413509785, 99284903},
+ {413496185, 48304367},
+ {419496173, 48315719},
+ {422501887, 45292801},
+ {422500504, 39363184},
+ {420425079, 37284347},
+ {419476165, 43284347},
+ {413496185, 43284347},
+ {413497261, 30797428},
+ {418986175, 25308513},
+ {424005230, 25315076},
+ {428496185, 20815924},
+ {428512720, 13948847},
+ {424866064, 10276075},
+ },
+ {
+ },
+ },
+ {
+ {
+ {723893066, 37354349},
+ {717673034, 37370791},
+ {717673034, 44872138},
+ {715673034, 44867768},
+ {715673034, 46055353},
+ {699219526, 40066777},
+ {697880758, 37748547},
+ {691985477, 37748293},
+ {689014018, 42869257},
+ {691985477, 48016003},
+ {697575093, 48003007},
+ {715671494, 54589493},
+ {715656800, 87142158},
+ {759954611, 87142158},
+ {764193054, 82897328},
+ {764193054, 79872138},
+ {757173034, 79866968},
+ {757173034, 83872138},
+ {754419422, 83869509},
+ {753193054, 81739327},
+ {753193054, 37360571},
+ {723893066, 37354349},
+ },
+ {
+ },
+ },
+ {
+ {
+ {85607478, 4227596},
+ {61739211, 4230337},
+ {61739211, 13231393},
+ {58725066, 13231405},
+ {58721589, 27731406},
+ {58738375, 30262521},
+ {61739211, 30251413},
+ {61736212, 38251411},
+ {70759231, 38254724},
+ {70905600, 33317391},
+ {73749222, 31251468},
+ {76592843, 33317393},
+ {76739211, 38254516},
+ {86765007, 38251411},
+ {86759599, 4231393},
+ {85607478, 4227596},
+ },
+ {
+ },
+ },
+ {
+ {
+ {534839721, 53437770},
+ {534839721, 60849059},
+ {539898273, 63773857},
+ {545461140, 63757881},
+ {544859741, 53447836},
+ {541839721, 53437862},
+ {541710836, 56353878},
+ {540193984, 57229659},
+ {538859741, 53437862},
+ {534839721, 53437770},
+ },
+ {
+ },
+ },
+ {
+ {
+ {756086230, 136598477},
+ {732054387, 136605752},
+ {732052489, 172629505},
+ {756091994, 172627853},
+ {756086230, 136598477},
+ },
+ {
+ },
+ },
+ {
+ {
+ {100337034, 79731391},
+ {70296833, 79731391},
+ {70311095, 92263567},
+ {74329808, 96264260},
+ {96344976, 96257215},
+ {100344419, 92232243},
+ {100337034, 79731391},
+ },
+ {
+ },
+ },
+ {
+ {
+ {102331115, 44216643},
+ {67311095, 44217252},
+ {67311095, 69250964},
+ {74329808, 76264260},
+ {96334594, 76251411},
+ {103335261, 69241401},
+ {103345839, 44231404},
+ {102331115, 44216643},
+ },
+ {
+ },
+ },
+ {
+ {
+ {93849749, 109613798},
+ {91771666, 111698636},
+ {91772404, 174626800},
+ {96782902, 179645338},
+ {241790509, 179645349},
+ {246800716, 174626800},
+ {246802574, 111699755},
+ {243934250, 109616385},
+ {93849749, 109613798},
+ },
+ {
+ },
+ },
+ {
+ {
+ {15856630, 87966835},
+ {8414359, 91273170},
+ {5891847, 99010553},
+ {8403012, 104668172},
+ {13739106, 107763252},
+ {13739106, 116209175},
+ {17959116, 116219127},
+ {17959127, 107763252},
+ {23952579, 103855773},
+ {25806388, 96944174},
+ {22553953, 90543787},
+ {15856630, 87966835},
+ },
+ {
+ },
+ },
+ {
+ {
+ {503922805, 110421794},
+ {491110107, 123244292},
+ {479598157, 123244304},
+ {479601067, 149264312},
+ {494260327, 149265241},
+ {502929782, 157948320},
+ {506490250, 155806171},
+ {502950518, 155094962},
+ {507193172, 150852294},
+ {504364680, 148023895},
+ {535816833, 116571757},
+ {538656617, 119411542},
+ {542887886, 115157558},
+ {543594970, 118693080},
+ {545330008, 116966050},
+ {540309189, 110425901},
+ {503922805, 110421794},
+ },
+ {
+ },
+ },
+ {
+ {
+ {519310433, 62560296},
+ {515749982, 64702434},
+ {519289696, 65413661},
+ {515047062, 69656303},
+ {517875553, 72484703},
+ {486423431, 103936848},
+ {483595031, 101108448},
+ {479352325, 105351055},
+ {478645233, 101815525},
+ {476917724, 103520870},
+ {481923478, 110077233},
+ {518337308, 110084297},
+ {531130127, 97264312},
+ {542630127, 97281049},
+ {542639167, 71244292},
+ {527979906, 71243363},
+ {519310433, 62560296},
+ },
+ {
+ },
+ },
+ {
+ {
+ {528658425, 14775300},
+ {525975568, 24475413},
+ {522556814, 29181341},
+ {517517474, 32090757},
+ {511736147, 32698600},
+ {506200465, 30901018},
+ {501879743, 27011092},
+ {497782491, 14775300},
+ {492372374, 15588397},
+ {489384268, 20795320},
+ {491253082, 28537271},
+ {495185363, 34469052},
+ {495178475, 43927542},
+ {502032399, 55796416},
+ {524402581, 55807400},
+ {531706434, 44295318},
+ {531205383, 34469052},
+ {536679415, 23789946},
+ {535868173, 17264403},
+ {532873348, 15073849},
+ {528658425, 14775300},
+ },
+ {
+ },
+ },
+ {
+ {
+ {481122222, 166062916},
+ {478115710, 166824472},
+ {477103577, 169063247},
+ {477106058, 192070670},
+ {478623652, 194687013},
+ {525109130, 195083267},
+ {525117792, 198086965},
+ {535129140, 198091624},
+ {535129150, 195083267},
+ {539038502, 194940807},
+ {540865280, 193308821},
+ {541132038, 169100183},
+ {539614599, 166459484},
+ {481122222, 166062916},
+ },
+ {
+ },
+ },
+ {
+ {
+ {23771404, 13005453},
+ {24774973, 19182457},
+ {31971050, 18727127},
+ {32556286, 58337520},
+ {25390683, 58337566},
+ {25063762, 54707065},
+ {20168811, 54707252},
+ {20171550, 62917175},
+ {70810377, 202895528},
+ {74314421, 205588631},
+ {88674817, 205515176},
+ {91837376, 203083756},
+ {92280287, 199307207},
+ {40674807, 15904975},
+ {36849630, 13006690},
+ {23771404, 13005453},
+ },
+ {
+ },
+ },
+ {
+ {
+ {336421201, 2986256},
+ {331176570, 6498191},
+ {327552287, 5825511},
+ {324913825, 2988891},
+ {316226154, 2989990},
+ {313040282, 6275291},
+ {313040282, 23489990},
+ {307126391, 23490002},
+ {307140289, 25510010},
+ {313040282, 25510010},
+ {313040282, 28989990},
+ {307126391, 28990002},
+ {307140289, 31015515},
+ {313040282, 31010010},
+ {313040282, 35989990},
+ {304534809, 37529785},
+ {304524991, 73488855},
+ {308554680, 77518546},
+ {324040282, 77510010},
+ {324040295, 93025333},
+ {334574441, 93010010},
+ {334574441, 90989990},
+ {332560302, 90989990},
+ {332560302, 85010010},
+ {334560302, 85010010},
+ {334561237, 82010010},
+ {338540282, 82010010},
+ {339540282, 83760010},
+ {338540293, 93020012},
+ {348060655, 93014679},
+ {356564448, 84500000},
+ {356560555, 28989990},
+ {347334198, 29039989},
+ {347334198, 25510010},
+ {356510304, 25521084},
+ {356510315, 23478922},
+ {347560302, 23489990},
+ {347560302, 5775291},
+ {344874443, 2989990},
+ {336421201, 2986256},
+ },
+ {
+ },
+ },
+ {
+ {
+ {465152221, 31684687},
+ {457606880, 31688302},
+ {452659362, 35508617},
+ {449044605, 34734089},
+ {446478972, 31692751},
+ {437784814, 31692957},
+ {435521210, 33956565},
+ {435532195, 65697616},
+ {426028494, 65691361},
+ {426025938, 85049712},
+ {435532195, 95717636},
+ {435524445, 103754026},
+ {436995898, 105225463},
+ {447552204, 105226323},
+ {447552215, 103197497},
+ {444552215, 103197616},
+ {444552215, 99217636},
+ {452032195, 99217636},
+ {452032195, 105221758},
+ {465588513, 105225463},
+ {467059965, 103754026},
+ {467052215, 95717636},
+ {478053039, 84511285},
+ {478056214, 65697616},
+ {468552215, 65697616},
+ {468563959, 33957323},
+ {465152221, 31684687},
+ },
+ {
+ },
+ },
+ {
+ {
+ {764927063, 92658416},
+ {762115426, 94171595},
+ {762122741, 131696443},
+ {786415417, 132779578},
+ {793690904, 129904572},
+ {797383202, 124822853},
+ {798269157, 120142660},
+ {796710161, 114090278},
+ {793387498, 110215980},
+ {796094093, 103892242},
+ {794107594, 96994001},
+ {787445494, 92840355},
+ {764927063, 92658416},
+ },
+ {
+ },
+ },
+ {
+ {
+ {27496331, 123147467},
+ {3202195, 124246400},
+ {3203433, 205768600},
+ {20223453, 205775606},
+ {20223644, 163243606},
+ {31297341, 162189074},
+ {36789517, 155659691},
+ {36967183, 150566416},
+ {34468182, 145711036},
+ {38465496, 140400171},
+ {38952460, 132613091},
+ {34771593, 126022444},
+ {27496331, 123147467},
+ },
+ {
+ },
+ },
+ {
+ {
+ {797556553, 39197820},
+ {791313598, 39199767},
+ {789506233, 39864015},
+ {789522521, 48199767},
+ {775974570, 48195721},
+ {774022521, 50129235},
+ {774008720, 76258022},
+ {775974570, 78223833},
+ {789522521, 78219787},
+ {789522521, 86576919},
+ {797556547, 87221747},
+ {797556553, 39197820},
+ },
+ {
+ },
+ },
+ {
+ {
+ {676593113, 129820144},
+ {676565322, 164844636},
+ {701599609, 164858650},
+ {701599609, 129823260},
+ {676593113, 129820144},
+ },
+ {
+ },
+ },
+ {
+ {
+ {727646871, 93121321},
+ {709122741, 93122138},
+ {709122741, 125656310},
+ {718769809, 135145243},
+ {721622937, 135156111},
+ {724152429, 132626619},
+ {723734126, 112688301},
+ {725837154, 107378546},
+ {728976138, 104430846},
+ {735847924, 102664848},
+ {741289364, 104430846},
+ {745202882, 108599767},
+ {746590596, 114642158},
+ {751137173, 114644887},
+ {756151199, 109641674},
+ {756149037, 94634278},
+ {754642761, 93122138},
+ {727646871, 93121321},
+ },
+ {
+ },
+ },
+ {
+ {
+ {135915724, 185598906},
+ {131396265, 193419009},
+ {131399444, 197643260},
+ {140399444, 197636810},
+ {140399444, 199138818},
+ {157419464, 197643916},
+ {157422805, 193210743},
+ {153046747, 185604789},
+ {149044579, 185614655},
+ {147324399, 189850396},
+ {144168954, 191108901},
+ {141187892, 189479768},
+ {139917659, 185615382},
+ {135915724, 185598906},
+ },
+ {
+ },
+ },
+ {
+ {
+ {312619110, 154485844},
+ {309601817, 157488332},
+ {309599764, 203494810},
+ {313109244, 207010010},
+ {352900849, 207019221},
+ {359629120, 200302405},
+ {359638705, 159501827},
+ {354621096, 154487830},
+ {312619110, 154485844},
+ },
+ {
+ },
+ },
+ {
+ {
+ {313120315, 98984639},
+ {309609100, 102486971},
+ {309596977, 148492024},
+ {312591195, 151510010},
+ {354608772, 151524494},
+ {359629120, 146515788},
+ {359638123, 105715491},
+ {352907860, 98987790},
+ {313120315, 98984639},
+ },
+ {
+ },
+ },
+ {
+ {
+ {657746643, 86246732},
+ {651722477, 92270881},
+ {651720052, 131280884},
+ {653947196, 131280884},
+ {659746643, 125487816},
+ {659746643, 119273826},
+ {663742413, 112352691},
+ {671726623, 112352691},
+ {675733721, 119283349},
+ {684745297, 119298573},
+ {689758503, 114263168},
+ {689752066, 91272158},
+ {684746643, 86260871},
+ {657746643, 86246732},
+ },
+ {
+ },
+ },
+ {
+ {
+ {653940791, 39260871},
+ {651720052, 39260871},
+ {651726623, 78280611},
+ {657746631, 84295035},
+ {684746643, 84280891},
+ {689752066, 79269604},
+ {689746643, 56247942},
+ {684745283, 51243184},
+ {675733721, 51258413},
+ {671726623, 58189071},
+ {663742413, 58189071},
+ {659746643, 51267936},
+ {659746643, 45053950},
+ {653940791, 39260871},
+ },
+ {
+ },
+ },
+ {
+ {
+ {442365208, 3053303},
+ {436408500, 5694021},
+ {434342552, 11072741},
+ {436986326, 17009033},
+ {442365367, 19073360},
+ {448299202, 16431441},
+ {450365150, 11052721},
+ {448299202, 5694021},
+ {442365208, 3053303},
+ },
+ {
+ },
+ },
+};
diff --git a/xs/src/libnest2d/tests/printer_parts.h b/xs/src/libnest2d/tests/printer_parts.h
new file mode 100644
index 000000000..b9a4eb8fa
--- /dev/null
+++ b/xs/src/libnest2d/tests/printer_parts.h
@@ -0,0 +1,40 @@
+#ifndef PRINTER_PARTS_H
+#define PRINTER_PARTS_H
+
+#include <vector>
+#include <clipper.hpp>
+
+#ifndef CLIPPER_BACKEND_HPP
+namespace ClipperLib {
+using PointImpl = IntPoint;
+using PathImpl = Path;
+using HoleStore = std::vector<PathImpl>;
+
+struct PolygonImpl {
+ PathImpl Contour;
+ HoleStore Holes;
+
+ inline PolygonImpl() {}
+
+ inline explicit PolygonImpl(const PathImpl& cont): Contour(cont) {}
+ inline explicit PolygonImpl(const HoleStore& holes):
+ Holes(holes) {}
+ inline PolygonImpl(const Path& cont, const HoleStore& holes):
+ Contour(cont), Holes(holes) {}
+
+ inline explicit PolygonImpl(PathImpl&& cont): Contour(std::move(cont)) {}
+ inline explicit PolygonImpl(HoleStore&& holes): Holes(std::move(holes)) {}
+ inline PolygonImpl(Path&& cont, HoleStore&& holes):
+ Contour(std::move(cont)), Holes(std::move(holes)) {}
+};
+}
+#endif
+
+using TestData = std::vector<ClipperLib::Path>;
+using TestDataEx = std::vector<ClipperLib::PolygonImpl>;
+
+extern const TestData PRINTER_PART_POLYGONS;
+extern const TestData STEGOSAUR_POLYGONS;
+extern const TestDataEx PRINTER_PART_POLYGONS_EX;
+
+#endif // PRINTER_PARTS_H
diff --git a/xs/src/libnest2d/tests/test.cpp b/xs/src/libnest2d/tests/test.cpp
new file mode 100644
index 000000000..323fb8d31
--- /dev/null
+++ b/xs/src/libnest2d/tests/test.cpp
@@ -0,0 +1,847 @@
+#include <gtest/gtest.h>
+#include <fstream>
+
+#include <libnest2d.h>
+#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;
+
+ if(ret.empty()) {
+ ret.reserve(PRINTER_PART_POLYGONS.size());
+ for(auto& inp : PRINTER_PART_POLYGONS) ret.emplace_back(inp);
+ }
+
+ return ret;
+}
+
+TEST(BasicFunctionality, Angles)
+{
+
+ using namespace libnest2d;
+
+ Degrees deg(180);
+ Radians rad(deg);
+ Degrees deg2(rad);
+
+ ASSERT_DOUBLE_EQ(rad, Pi);
+ ASSERT_DOUBLE_EQ(deg, 180);
+ ASSERT_DOUBLE_EQ(deg2, 180);
+ ASSERT_DOUBLE_EQ(rad, (Radians) deg);
+ ASSERT_DOUBLE_EQ( (Degrees) rad, deg);
+
+ ASSERT_TRUE(rad == deg);
+
+ Segment seg = {{0, 0}, {12, -10}};
+
+ ASSERT_TRUE(Degrees(seg.angleToXaxis()) > 270 &&
+ Degrees(seg.angleToXaxis()) < 360);
+
+ seg = {{0, 0}, {12, 10}};
+
+ ASSERT_TRUE(Degrees(seg.angleToXaxis()) > 0 &&
+ Degrees(seg.angleToXaxis()) < 90);
+
+ seg = {{0, 0}, {-12, 10}};
+
+ ASSERT_TRUE(Degrees(seg.angleToXaxis()) > 90 &&
+ Degrees(seg.angleToXaxis()) < 180);
+
+ seg = {{0, 0}, {-12, -10}};
+
+ ASSERT_TRUE(Degrees(seg.angleToXaxis()) > 180 &&
+ Degrees(seg.angleToXaxis()) < 270);
+
+ seg = {{0, 0}, {1, 0}};
+
+ ASSERT_DOUBLE_EQ(Degrees(seg.angleToXaxis()), 0);
+
+ seg = {{0, 0}, {0, 1}};
+
+ ASSERT_DOUBLE_EQ(Degrees(seg.angleToXaxis()), 90);
+
+
+ seg = {{0, 0}, {-1, 0}};
+
+ ASSERT_DOUBLE_EQ(Degrees(seg.angleToXaxis()), 180);
+
+
+ seg = {{0, 0}, {0, -1}};
+
+ ASSERT_DOUBLE_EQ(Degrees(seg.angleToXaxis()), 270);
+
+}
+
+// Simple test, does not use gmock
+TEST(BasicFunctionality, creationAndDestruction)
+{
+ using namespace libnest2d;
+
+ Item sh = { {0, 0}, {1, 0}, {1, 1}, {0, 1} };
+
+ ASSERT_EQ(sh.vertexCount(), 4u);
+
+ Item sh2 ({ {0, 0}, {1, 0}, {1, 1}, {0, 1} });
+
+ ASSERT_EQ(sh2.vertexCount(), 4u);
+
+ // copy
+ Item sh3 = sh2;
+
+ ASSERT_EQ(sh3.vertexCount(), 4u);
+
+ sh2 = {};
+
+ ASSERT_EQ(sh2.vertexCount(), 0u);
+ ASSERT_EQ(sh3.vertexCount(), 4u);
+
+}
+
+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;
+
+ Point p1 = {0, 0};
+
+ 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));
+
+ Segment seg(p1, p3);
+
+ ASSERT_DOUBLE_EQ(pointlike::distance(p2, seg), 7.0710678118654755);
+
+ auto result = pointlike::horizontalDistance(p2, seg);
+
+ auto check = [](Coord val, Coord expected) {
+ if(std::is_floating_point<Coord>::value)
+ ASSERT_DOUBLE_EQ(static_cast<double>(val),
+ static_cast<double>(expected));
+ else
+ ASSERT_EQ(val, expected);
+ };
+
+ ASSERT_TRUE(result.second);
+ check(result.first, 10);
+
+ result = pointlike::verticalDistance(p2, seg);
+ ASSERT_TRUE(result.second);
+ check(result.first, -10);
+
+ result = pointlike::verticalDistance(Point{10, 20}, seg);
+ ASSERT_TRUE(result.second);
+ check(result.first, 10);
+
+
+ Point p4 = {80, 0};
+ Segment seg2 = { {0, 0}, {0, 40} };
+
+ result = pointlike::horizontalDistance(p4, seg2);
+
+ ASSERT_TRUE(result.second);
+ check(result.first, 80);
+
+ result = pointlike::verticalDistance(p4, seg2);
+ // Point should not be related to the segment
+ ASSERT_FALSE(result.second);
+
+}
+
+TEST(GeometryAlgorithms, Area) {
+ using namespace libnest2d;
+
+ Rectangle rect(10, 10);
+
+ ASSERT_EQ(rect.area(), 100);
+
+ Rectangle rect2 = {100, 100};
+
+ ASSERT_EQ(rect2.area(), 10000);
+
+ Item item = {
+ {61, 97},
+ {70, 151},
+ {176, 151},
+ {189, 138},
+ {189, 59},
+ {70, 59},
+ {61, 77},
+ {61, 97}
+ };
+
+ ASSERT_TRUE(shapelike::area(item.transformedShape()) > 0 );
+}
+
+TEST(GeometryAlgorithms, IsPointInsidePolygon) {
+ using namespace libnest2d;
+
+ Rectangle rect(10, 10);
+
+ Point p = {1, 1};
+
+ ASSERT_TRUE(rect.isInside(p));
+
+ p = {11, 11};
+
+ ASSERT_FALSE(rect.isInside(p));
+
+
+ p = {11, 12};
+
+ ASSERT_FALSE(rect.isInside(p));
+
+
+ p = {3, 3};
+
+ ASSERT_TRUE(rect.isInside(p));
+
+}
+
+//TEST(GeometryAlgorithms, Intersections) {
+// using namespace binpack2d;
+
+// Rectangle rect(70, 30);
+
+// rect.translate({80, 60});
+
+// Rectangle rect2(80, 60);
+// rect2.translate({80, 0});
+
+//// ASSERT_FALSE(Item::intersects(rect, rect2));
+
+// Segment s1({0, 0}, {10, 10});
+// Segment s2({1, 1}, {11, 11});
+// ASSERT_FALSE(ShapeLike::intersects(s1, s1));
+// ASSERT_FALSE(ShapeLike::intersects(s1, s2));
+//}
+
+// Simple test, does not use gmock
+TEST(GeometryAlgorithms, LeftAndDownPolygon)
+{
+ using namespace libnest2d;
+ using namespace libnest2d;
+
+ Box bin(100, 100);
+ BottomLeftPlacer placer(bin);
+
+ Item item = {{70, 75}, {88, 60}, {65, 50}, {60, 30}, {80, 20}, {42, 20},
+ {35, 35}, {35, 55}, {40, 75}, {70, 75}};
+
+ Item leftControl = { {40, 75},
+ {35, 55},
+ {35, 35},
+ {42, 20},
+ {0, 20},
+ {0, 75},
+ {40, 75}};
+
+ Item downControl = {{88, 60},
+ {88, 0},
+ {35, 0},
+ {35, 35},
+ {42, 20},
+ {80, 20},
+ {60, 30},
+ {65, 50},
+ {88, 60}};
+
+ Item leftp(placer.leftPoly(item));
+
+ ASSERT_TRUE(shapelike::isValid(leftp.rawShape()).first);
+ ASSERT_EQ(leftp.vertexCount(), leftControl.vertexCount());
+
+ for(unsigned long i = 0; i < leftControl.vertexCount(); i++) {
+ ASSERT_EQ(getX(leftp.vertex(i)), getX(leftControl.vertex(i)));
+ ASSERT_EQ(getY(leftp.vertex(i)), getY(leftControl.vertex(i)));
+ }
+
+ Item downp(placer.downPoly(item));
+
+ ASSERT_TRUE(shapelike::isValid(downp.rawShape()).first);
+ ASSERT_EQ(downp.vertexCount(), downControl.vertexCount());
+
+ for(unsigned long i = 0; i < downControl.vertexCount(); i++) {
+ ASSERT_EQ(getX(downp.vertex(i)), getX(downControl.vertex(i)));
+ ASSERT_EQ(getY(downp.vertex(i)), getY(downControl.vertex(i)));
+ }
+}
+
+// Simple test, does not use gmock
+TEST(GeometryAlgorithms, ArrangeRectanglesTight)
+{
+ using namespace libnest2d;
+
+ std::vector<Rectangle> rects = {
+ {80, 80},
+ {60, 90},
+ {70, 30},
+ {80, 60},
+ {60, 60},
+ {60, 40},
+ {40, 40},
+ {10, 10},
+ {10, 10},
+ {10, 10},
+ {10, 10},
+ {10, 10},
+ {5, 5},
+ {5, 5},
+ {5, 5},
+ {5, 5},
+ {5, 5},
+ {5, 5},
+ {5, 5},
+ {20, 20} };
+
+
+ Nester<BottomLeftPlacer, DJDHeuristic> arrange(Box(210, 250));
+
+ auto groups = arrange(rects.begin(), rects.end());
+
+ ASSERT_EQ(groups.size(), 1u);
+ ASSERT_EQ(groups[0].size(), rects.size());
+
+ // check for no intersections, no containment:
+
+ for(auto result : groups) {
+ bool valid = true;
+ for(Item& r1 : result) {
+ for(Item& r2 : result) {
+ if(&r1 != &r2 ) {
+ valid = !Item::intersects(r1, r2) || Item::touches(r1, r2);
+ valid = (valid && !r1.isInside(r2) && !r2.isInside(r1));
+ ASSERT_TRUE(valid);
+ }
+ }
+ }
+ }
+
+}
+
+TEST(GeometryAlgorithms, ArrangeRectanglesLoose)
+{
+ using namespace libnest2d;
+
+// std::vector<Rectangle> rects = { {40, 40}, {10, 10}, {20, 20} };
+ std::vector<Rectangle> rects = {
+ {80, 80},
+ {60, 90},
+ {70, 30},
+ {80, 60},
+ {60, 60},
+ {60, 40},
+ {40, 40},
+ {10, 10},
+ {10, 10},
+ {10, 10},
+ {10, 10},
+ {10, 10},
+ {5, 5},
+ {5, 5},
+ {5, 5},
+ {5, 5},
+ {5, 5},
+ {5, 5},
+ {5, 5},
+ {20, 20} };
+
+ Coord min_obj_distance = 5;
+
+ Nester<BottomLeftPlacer, DJDHeuristic> arrange(Box(210, 250),
+ min_obj_distance);
+
+ auto groups = arrange(rects.begin(), rects.end());
+
+ ASSERT_EQ(groups.size(), 1u);
+ ASSERT_EQ(groups[0].size(), rects.size());
+
+ // check for no intersections, no containment:
+ auto result = groups[0];
+ bool valid = true;
+ for(Item& r1 : result) {
+ for(Item& r2 : result) {
+ if(&r1 != &r2 ) {
+ valid = !Item::intersects(r1, r2);
+ valid = (valid && !r1.isInside(r2) && !r2.isInside(r1));
+ ASSERT_TRUE(valid);
+ }
+ }
+ }
+
+}
+
+namespace {
+using namespace libnest2d;
+
+template<unsigned long SCALE = 1, class Bin>
+void exportSVG(std::vector<std::reference_wrapper<Item>>& result, const Bin& bin, int idx = 0) {
+
+
+ std::string loc = "out";
+
+ static std::string svg_header =
+R"raw(<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<svg height="500" width="500" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+)raw";
+
+ int i = idx;
+ auto r = result;
+// for(auto r : result) {
+ std::fstream out(loc + std::to_string(i) + ".svg", std::fstream::out);
+ if(out.is_open()) {
+ out << svg_header;
+ Item rbin( Rectangle(bin.width(), bin.height()) );
+ for(unsigned i = 0; i < rbin.vertexCount(); i++) {
+ auto v = rbin.vertex(i);
+ setY(v, -getY(v)/SCALE + 500 );
+ setX(v, getX(v)/SCALE);
+ rbin.setVertex(i, v);
+ }
+ 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++) {
+ auto v = tsh.vertex(i);
+ setY(v, -getY(v)/SCALE + 500);
+ setX(v, getX(v)/SCALE);
+ tsh.setVertex(i, v);
+ }
+ out << shapelike::serialize<Formats::SVG>(tsh.rawShape()) << std::endl;
+ }
+ out << "\n</svg>" << std::endl;
+ }
+ out.close();
+
+// i++;
+// }
+}
+}
+
+TEST(GeometryAlgorithms, BottomLeftStressTest) {
+ using namespace libnest2d;
+
+ const Coord SCALE = 1000000;
+ auto& input = prusaParts();
+
+ Box bin(210*SCALE, 250*SCALE);
+ BottomLeftPlacer placer(bin);
+
+ auto it = input.begin();
+ auto next = it;
+ int i = 0;
+ while(it != input.end() && ++next != input.end()) {
+ placer.pack(*it);
+ placer.pack(*next);
+
+ auto result = placer.getItems();
+ bool valid = true;
+
+ if(result.size() == 2) {
+ Item& r1 = result[0];
+ Item& r2 = result[1];
+ valid = !Item::intersects(r1, r2) || Item::touches(r1, r2);
+ valid = (valid && !r1.isInside(r2) && !r2.isInside(r1));
+ if(!valid) {
+ std::cout << "error index: " << i << std::endl;
+ exportSVG(result, bin, i);
+ }
+ ASSERT_TRUE(valid);
+ } else {
+ std::cout << "something went terribly wrong!" << std::endl;
+ FAIL();
+ }
+
+ placer.clearItems();
+ it++;
+ i++;
+ }
+}
+
+namespace {
+
+struct ItemPair {
+ Item orbiter;
+ Item stationary;
+};
+
+std::vector<ItemPair> nfp_testdata = {
+ {
+ {
+ {80, 50},
+ {100, 70},
+ {120, 50},
+ {80, 50}
+ },
+ {
+ {10, 10},
+ {10, 40},
+ {40, 40},
+ {40, 10},
+ {10, 10}
+ }
+ },
+ {
+ {
+ {80, 50},
+ {60, 70},
+ {80, 90},
+ {120, 90},
+ {140, 70},
+ {120, 50},
+ {80, 50}
+ },
+ {
+ {10, 10},
+ {10, 40},
+ {40, 40},
+ {40, 10},
+ {10, 10}
+ }
+ },
+ {
+ {
+ {40, 10},
+ {30, 10},
+ {20, 20},
+ {20, 30},
+ {30, 40},
+ {40, 40},
+ {50, 30},
+ {50, 20},
+ {40, 10}
+ },
+ {
+ {80, 0},
+ {80, 30},
+ {110, 30},
+ {110, 0},
+ {80, 0}
+ }
+ },
+ {
+ {
+ {117, 107},
+ {118, 109},
+ {120, 112},
+ {122, 113},
+ {128, 113},
+ {130, 112},
+ {132, 109},
+ {133, 107},
+ {133, 103},
+ {132, 101},
+ {130, 98},
+ {128, 97},
+ {122, 97},
+ {120, 98},
+ {118, 101},
+ {117, 103},
+ {117, 107}
+ },
+ {
+ {102, 116},
+ {111, 126},
+ {114, 126},
+ {144, 106},
+ {148, 100},
+ {148, 85},
+ {147, 84},
+ {102, 84},
+ {102, 116},
+ }
+ },
+ {
+ {
+ {99, 122},
+ {108, 140},
+ {110, 142},
+ {139, 142},
+ {151, 122},
+ {151, 102},
+ {142, 70},
+ {139, 68},
+ {111, 68},
+ {108, 70},
+ {99, 102},
+ {99, 122},
+ },
+ {
+ {107, 124},
+ {128, 125},
+ {133, 125},
+ {136, 124},
+ {140, 121},
+ {142, 119},
+ {143, 116},
+ {143, 109},
+ {141, 93},
+ {139, 89},
+ {136, 86},
+ {134, 85},
+ {108, 85},
+ {107, 86},
+ {107, 124},
+ }
+ },
+ {
+ {
+ {91, 100},
+ {94, 144},
+ {117, 153},
+ {118, 153},
+ {159, 112},
+ {159, 110},
+ {156, 66},
+ {133, 57},
+ {132, 57},
+ {91, 98},
+ {91, 100},
+ },
+ {
+ {101, 90},
+ {103, 98},
+ {107, 113},
+ {114, 125},
+ {115, 126},
+ {135, 126},
+ {136, 125},
+ {144, 114},
+ {149, 90},
+ {149, 89},
+ {148, 87},
+ {145, 84},
+ {105, 84},
+ {102, 87},
+ {101, 89},
+ {101, 90},
+ }
+ }
+};
+
+std::vector<ItemPair> nfp_concave_testdata = {
+ { // ItemPair
+ {
+ {
+ {533726, 142141},
+ {532359, 143386},
+ {530141, 142155},
+ {528649, 160091},
+ {533659, 157607},
+ {538669, 160091},
+ {537178, 142155},
+ {534959, 143386},
+ {533726, 142141},
+ }
+ },
+ {
+ {
+ {118305, 11603},
+ {118311, 26616},
+ {113311, 26611},
+ {109311, 29604},
+ {109300, 44608},
+ {109311, 49631},
+ {113300, 52636},
+ {118311, 52636},
+ {118308, 103636},
+ {223830, 103636},
+ {236845, 90642},
+ {236832, 11630},
+ {232825, 11616},
+ {210149, 11616},
+ {211308, 13625},
+ {209315, 17080},
+ {205326, 17080},
+ {203334, 13629},
+ {204493, 11616},
+ {118305, 11603},
+ }
+ },
+ }
+};
+
+template<nfp::NfpLevel lvl, Coord SCALE>
+void testNfp(const std::vector<ItemPair>& testdata) {
+ using namespace libnest2d;
+
+ Box bin(210*SCALE, 250*SCALE);
+
+ int testcase = 0;
+
+ auto& exportfun = exportSVG<SCALE, Box>;
+
+ auto onetest = [&](Item& orbiter, Item& stationary, unsigned testidx){
+ testcase++;
+
+ orbiter.translate({210*SCALE, 0});
+
+ auto&& nfp = nfp::noFitPolygon<lvl>(stationary.rawShape(),
+ orbiter.transformedShape());
+
+ placers::correctNfpPosition(nfp, stationary, orbiter);
+
+ auto valid = shapelike::isValid(nfp.first);
+
+ /*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(valid.first);
+
+ Item infp(nfp.first);
+
+ int i = 0;
+ auto rorbiter = orbiter.transformedShape();
+ auto vo = nfp::referenceVertex(rorbiter);
+
+ ASSERT_TRUE(stationary.isInside(infp));
+
+ for(auto v : infp) {
+ auto dx = getX(v) - getX(vo);
+ auto dy = getY(v) - getY(vo);
+
+ Item tmp = orbiter;
+
+ tmp.translate({dx, dy});
+
+ bool touching = Item::touches(tmp, stationary);
+
+ if(!touching || !valid.first) {
+ std::vector<std::reference_wrapper<Item>> inp = {
+ std::ref(stationary), std::ref(tmp), std::ref(infp)
+ };
+
+ exportfun(inp, bin, testcase*i++);
+ }
+
+ ASSERT_TRUE(touching);
+ }
+ };
+
+ unsigned tidx = 0;
+ for(auto& td : testdata) {
+ auto orbiter = td.orbiter;
+ auto stationary = td.stationary;
+ onetest(orbiter, stationary, tidx++);
+ }
+
+ tidx = 0;
+ for(auto& td : testdata) {
+ auto orbiter = td.stationary;
+ auto stationary = td.orbiter;
+ onetest(orbiter, stationary, tidx++);
+ }
+}
+}
+
+TEST(GeometryAlgorithms, nfpConvexConvex) {
+ testNfp<nfp::NfpLevel::CONVEX_ONLY, 1>(nfp_testdata);
+}
+
+//TEST(GeometryAlgorithms, nfpConcaveConcave) {
+// testNfp<NfpLevel::BOTH_CONCAVE, 1000>(nfp_concave_testdata);
+//}
+
+TEST(GeometryAlgorithms, nfpConcaveConcave) {
+ using namespace libnest2d;
+
+// Rectangle r1(10, 10);
+// Rectangle r2(20, 20);
+// auto result = Nfp::nfpSimpleSimple(r1.transformedShape(),
+// r2.transformedShape());
+}
+
+TEST(GeometryAlgorithms, pointOnPolygonContour) {
+ using namespace libnest2d;
+
+ Rectangle input(10, 10);
+
+ placers::EdgeCache<PolygonImpl> ecache(input);
+
+ auto first = *input.begin();
+ ASSERT_TRUE(getX(first) == getX(ecache.coords(0)));
+ ASSERT_TRUE(getY(first) == getY(ecache.coords(0)));
+
+ auto last = *std::prev(input.end());
+ ASSERT_TRUE(getX(last) == getX(ecache.coords(1.0)));
+ ASSERT_TRUE(getY(last) == getY(ecache.coords(1.0)));
+
+ for(int i = 0; i <= 100; i++) {
+ auto v = ecache.coords(i*(0.01));
+ ASSERT_TRUE(shapelike::touches(v, input.transformedShape()));
+ }
+}
+
+TEST(GeometryAlgorithms, mergePileWithPolygon) {
+ using namespace libnest2d;
+
+ Rectangle rect1(10, 15);
+ Rectangle rect2(15, 15);
+ Rectangle rect3(20, 15);
+
+ rect2.translate({10, 0});
+ rect3.translate({25, 0});
+
+ shapelike::Shapes<PolygonImpl> pile;
+ pile.push_back(rect1.transformedShape());
+ pile.push_back(rect2.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());
+}
+
+int main(int argc, char **argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/xs/src/libnest2d/tools/benchmark.h b/xs/src/libnest2d/tools/benchmark.h
new file mode 100644
index 000000000..19870b37b
--- /dev/null
+++ b/xs/src/libnest2d/tools/benchmark.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) Tamás Mészáros
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+#ifndef INCLUDE_BENCHMARK_H_
+#define INCLUDE_BENCHMARK_H_
+
+#include <chrono>
+#include <ratio>
+
+/**
+ * A class for doing benchmarks.
+ */
+class Benchmark {
+ typedef std::chrono::high_resolution_clock Clock;
+ typedef Clock::duration Duration;
+ typedef Clock::time_point TimePoint;
+
+ TimePoint t1, t2;
+ Duration d;
+
+ inline double to_sec(Duration d) {
+ return d.count() * double(Duration::period::num) / Duration::period::den;
+ }
+
+public:
+
+ /**
+ * Measure time from the moment of this call.
+ */
+ void start() { t1 = Clock::now(); }
+
+ /**
+ * Measure time to the moment of this call.
+ */
+ void stop() { t2 = Clock::now(); }
+
+ /**
+ * Get the time elapsed between a start() end a stop() call.
+ * @return Returns the elapsed time in seconds.
+ */
+ double getElapsedSec() { d = t2 - t1; return to_sec(d); }
+};
+
+
+#endif /* INCLUDE_BENCHMARK_H_ */
diff --git a/xs/src/libnest2d/tools/libnfpglue.cpp b/xs/src/libnest2d/tools/libnfpglue.cpp
new file mode 100644
index 000000000..31733acf9
--- /dev/null
+++ b/xs/src/libnest2d/tools/libnfpglue.cpp
@@ -0,0 +1,157 @@
+//#ifndef NDEBUG
+//#define NFP_DEBUG
+//#endif
+
+#include "libnfpglue.hpp"
+#include "tools/libnfporb/libnfporb.hpp"
+
+namespace libnest2d {
+
+namespace {
+inline bool vsort(const libnfporb::point_t& v1, const libnfporb::point_t& v2)
+{
+ using Coord = libnfporb::coord_t;
+ Coord x1 = v1.x_, x2 = v2.x_, y1 = v1.y_, y2 = v2.y_;
+ auto diff = y1 - y2;
+#ifdef LIBNFP_USE_RATIONAL
+ long double diffv = diff.convert_to<long double>();
+#else
+ long double diffv = diff.val();
+#endif
+ if(std::abs(diffv) <=
+ std::numeric_limits<Coord>::epsilon())
+ return x1 < x2;
+
+ return diff < 0;
+}
+
+TCoord<PointImpl> getX(const libnfporb::point_t& p) {
+#ifdef LIBNFP_USE_RATIONAL
+ return p.x_.convert_to<TCoord<PointImpl>>();
+#else
+ return static_cast<TCoord<PointImpl>>(std::round(p.x_.val()));
+#endif
+}
+
+TCoord<PointImpl> getY(const libnfporb::point_t& p) {
+#ifdef LIBNFP_USE_RATIONAL
+ return p.y_.convert_to<TCoord<PointImpl>>();
+#else
+ return static_cast<TCoord<PointImpl>>(std::round(p.y_.val()));
+#endif
+}
+
+libnfporb::point_t scale(const libnfporb::point_t& p, long double factor) {
+#ifdef LIBNFP_USE_RATIONAL
+ auto px = p.x_.convert_to<long double>();
+ auto py = p.y_.convert_to<long double>();
+#else
+ long double px = p.x_.val();
+ long double py = p.y_.val();
+#endif
+ return {px*factor, py*factor};
+}
+
+}
+
+NfpR _nfp(const PolygonImpl &sh, const PolygonImpl &cother)
+{
+ namespace sl = shapelike;
+
+ NfpR ret;
+
+ try {
+ libnfporb::polygon_t pstat, porb;
+
+ boost::geometry::convert(sh, pstat);
+ boost::geometry::convert(cother, porb);
+
+ long double factor = 0.0000001;//libnfporb::NFP_EPSILON;
+ long double refactor = 1.0/factor;
+
+ for(auto& v : pstat.outer()) v = scale(v, factor);
+// std::string message;
+// boost::geometry::is_valid(pstat, message);
+// std::cout << message << std::endl;
+ for(auto& h : pstat.inners()) for(auto& v : h) v = scale(v, factor);
+
+ for(auto& v : porb.outer()) v = scale(v, factor);
+// message;
+// boost::geometry::is_valid(porb, message);
+// std::cout << message << std::endl;
+ for(auto& h : porb.inners()) for(auto& v : h) v = scale(v, factor);
+
+
+ // this can throw
+ auto nfp = libnfporb::generateNFP(pstat, porb, true);
+
+ auto &ct = sl::getContour(ret.first);
+ ct.reserve(nfp.front().size()+1);
+ for(auto v : nfp.front()) {
+ v = scale(v, refactor);
+ ct.emplace_back(getX(v), getY(v));
+ }
+ ct.push_back(ct.front());
+ std::reverse(ct.begin(), ct.end());
+
+ auto &rholes = sl::holes(ret.first);
+ for(size_t hidx = 1; hidx < nfp.size(); ++hidx) {
+ if(nfp[hidx].size() >= 3) {
+ rholes.emplace_back();
+ auto& h = rholes.back();
+ h.reserve(nfp[hidx].size()+1);
+
+ for(auto& v : nfp[hidx]) {
+ v = scale(v, refactor);
+ h.emplace_back(getX(v), getY(v));
+ }
+ h.push_back(h.front());
+ std::reverse(h.begin(), h.end());
+ }
+ }
+
+ 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);
+ }
+
+ return ret;
+}
+
+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, nfp::NfpLevel::ONE_CONVEX>::operator()(
+ const PolygonImpl &sh, const ClipperLib::PolygonImpl &cother)
+{
+ return _nfp(sh, cother);
+}
+
+NfpR nfp::NfpImpl<PolygonImpl, nfp::NfpLevel::BOTH_CONCAVE>::operator()(
+ const PolygonImpl &sh, const ClipperLib::PolygonImpl &cother)
+{
+ return _nfp(sh, cother);
+}
+
+//PolygonImpl
+//Nfp::NfpImpl<PolygonImpl, NfpLevel::ONE_CONVEX_WITH_HOLES>::operator()(
+// const PolygonImpl &sh, const ClipperLib::PolygonImpl &cother)
+//{
+// return _nfp(sh, cother);
+//}
+
+//PolygonImpl
+//Nfp::NfpImpl<PolygonImpl, NfpLevel::BOTH_CONCAVE_WITH_HOLES>::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
new file mode 100644
index 000000000..1ff033cb9
--- /dev/null
+++ b/xs/src/libnest2d/tools/libnfpglue.hpp
@@ -0,0 +1,46 @@
+#ifndef LIBNFPGLUE_HPP
+#define LIBNFPGLUE_HPP
+
+#include <libnest2d/clipper_backend/clipper_backend.hpp>
+
+namespace libnest2d {
+
+using NfpR = nfp::NfpResult<PolygonImpl>;
+
+NfpR _nfp(const PolygonImpl& sh, const PolygonImpl& cother);
+
+template<>
+struct nfp::NfpImpl<PolygonImpl, nfp::NfpLevel::CONVEX_ONLY> {
+ NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother);
+};
+
+template<>
+struct nfp::NfpImpl<PolygonImpl, nfp::NfpLevel::ONE_CONVEX> {
+ NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother);
+};
+
+template<>
+struct nfp::NfpImpl<PolygonImpl, nfp::NfpLevel::BOTH_CONCAVE> {
+ NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother);
+};
+
+//template<>
+//struct Nfp::NfpImpl<PolygonImpl, NfpLevel::ONE_CONVEX_WITH_HOLES> {
+// NfpResult operator()(const PolygonImpl& sh, const PolygonImpl& cother);
+//};
+
+//template<>
+//struct Nfp::NfpImpl<PolygonImpl, NfpLevel::BOTH_CONCAVE_WITH_HOLES> {
+// NfpResult operator()(const PolygonImpl& sh, const PolygonImpl& cother);
+//};
+
+template<> struct nfp::MaxNfpLevel<PolygonImpl> {
+ static const BP2D_CONSTEXPR NfpLevel value =
+// NfpLevel::CONVEX_ONLY;
+ NfpLevel::BOTH_CONCAVE;
+};
+
+}
+
+
+#endif // LIBNFPGLUE_HPP
diff --git a/xs/src/libnest2d/tools/libnfporb/LICENSE b/xs/src/libnest2d/tools/libnfporb/LICENSE
new file mode 100644
index 000000000..94a9ed024
--- /dev/null
+++ b/xs/src/libnest2d/tools/libnfporb/LICENSE
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/xs/src/libnest2d/tools/libnfporb/ORIGIN b/xs/src/libnest2d/tools/libnfporb/ORIGIN
new file mode 100644
index 000000000..788bfd9af
--- /dev/null
+++ b/xs/src/libnest2d/tools/libnfporb/ORIGIN
@@ -0,0 +1,2 @@
+https://github.com/kallaballa/libnfp.git
+commit hash a5cf9f6a76ddab95567fccf629d4d099b60237d7 \ No newline at end of file
diff --git a/xs/src/libnest2d/tools/libnfporb/README.md b/xs/src/libnest2d/tools/libnfporb/README.md
new file mode 100644
index 000000000..9698972be
--- /dev/null
+++ b/xs/src/libnest2d/tools/libnfporb/README.md
@@ -0,0 +1,89 @@
+[![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0.en.html)
+##### If you give me a real good reason i might be willing to give you permission to use it under a different license for a specific application. Real good reasons include the following (non-exhausive): the greater good, educational purpose and money :)
+
+# libnfporb
+Implementation of a robust no-fit polygon generation in a C++ library using an orbiting approach.
+
+__Please note:__ The paper this implementation is based it on has several bad assumptions that required me to "improvise". That means the code doesn't reflect the paper anymore and is running way slower than expected. At the moment I'm working on implementing a new approach based on this paper (using minkowski sums): https://eprints.soton.ac.uk/36850/1/CORMSIS-05-05.pdf
+
+## Description
+
+The no-fit polygon optimization makes it possible to check for overlap (or non-overlapping touch) of two polygons with only 1 point in polygon check (by providing the set of non-overlapping placements).
+This library implements the orbiting approach to generate the no-fit polygon: Given two polygons A and B, A is the stationary one and B the orbiting one, B is slid as tightly as possibly around the edges of polygon A. During the orbiting a chosen reference point is tracked. By tracking the movement of the reference point a third polygon can be generated: the no-fit polygon.
+
+Once the no-fit polygon has been generated it can be used to test for overlap by only checking if the reference point is inside the NFP (overlap) outside the NFP (no overlap) or exactly on the edge of the NFP (touch).
+
+### Examples:
+
+The polygons:
+
+![Start of NFP](/images/start.png?raw=true)
+
+Orbiting:
+
+![State 1](/images/next0.png?raw=true)
+![State 2](/images/next1.png?raw=true)
+![State 3](/images/next2.png?raw=true)
+![State 4](/images/next3.png?raw=true)
+
+![State 5](/images/next4.png?raw=true)
+![State 6](/images/next5.png?raw=true)
+![State 7](/images/next6.png?raw=true)
+![State 8](/images/next7.png?raw=true)
+
+![State 9](/images/next8.png?raw=true)
+
+The resulting NFP is red:
+
+![nfp](/images/nfp.png?raw=true)
+
+Polygons can have concavities, holes, interlocks or might fit perfectly:
+
+![concavities](/images/concavities.png?raw=true)
+![hole](/images/hole.png?raw=true)
+![interlock](/images/interlock.png?raw=true)
+![jigsaw](/images/jigsaw.png?raw=true)
+
+## The Approach
+The approch of this library is highly inspired by the scientific paper [Complete and robust no-fit polygon generation
+for the irregular stock cutting problem](https://pdfs.semanticscholar.org/e698/0dd78306ba7d5bb349d20c6d8f2e0aa61062.pdf) and by [Svgnest](http://svgnest.com)
+
+Note that is wasn't completely possible to implement it as suggested in the paper because it had several shortcomings that prevent complete NFP generation on some of my test cases. Especially the termination criteria (reference point returns to first point of NFP) proved to be wrong (see: test-case rect). Also tracking of used edges can't be performed as suggested in the paper since there might be situations where no edge of A is traversed (see: test-case doublecon).
+
+By default the library is using floating point as coordinate type but by defining the flag "LIBNFP_USE_RATIONAL" the library can be instructed to use infinite precision.
+
+## Build
+The library has two dependencies: [Boost Geometry](http://www.boost.org/doc/libs/1_65_1/libs/geometry/doc/html/index.html) and [libgmp](https://gmplib.org). You need to install those first before building. Note that building is only required for the examples. The library itself is header-only.
+
+ git clone https://github.com/kallaballa/libnfp.git
+ cd libnfp
+ make
+ sudo make install
+
+## Code Example
+
+```c++
+//uncomment next line to use infinite precision (slow)
+//#define LIBNFP_USE_RATIONAL
+#include "../src/libnfp.hpp"
+
+int main(int argc, char** argv) {
+ using namespace libnfp;
+ polygon_t pA;
+ polygon_t pB;
+ //read polygons from wkt files
+ read_wkt_polygon(argv[1], pA);
+ read_wkt_polygon(argv[2], pB);
+
+ //generate NFP of polygon A and polygon B and check the polygons for validity.
+ //When the third parameters is false validity check is skipped for a little performance increase
+ nfp_t nfp = generateNFP(pA, pB, true);
+
+ //write a svg containing pA, pB and NFP
+ write_svg("nfp.svg",{pA,pB},nfp);
+ return 0;
+}
+```
+Run the example program:
+
+ examples/nfp data/crossing/A.wkt data/crossing/B.wkt
diff --git a/xs/src/libnest2d/tools/libnfporb/libnfporb.hpp b/xs/src/libnest2d/tools/libnfporb/libnfporb.hpp
new file mode 100644
index 000000000..8cb34567e
--- /dev/null
+++ b/xs/src/libnest2d/tools/libnfporb/libnfporb.hpp
@@ -0,0 +1,1547 @@
+#ifndef NFP_HPP_
+#define NFP_HPP_
+
+#include <iostream>
+#include <list>
+#include <string>
+#include <fstream>
+#include <streambuf>
+#include <vector>
+#include <set>
+#include <exception>
+#include <random>
+#include <limits>
+
+#if defined(_MSC_VER) && _MSC_VER <= 1800 || __cplusplus < 201103L
+ #define LIBNFP_NOEXCEPT
+ #define LIBNFP_CONSTEXPR
+#elif __cplusplus >= 201103L
+ #define LIBNFP_NOEXCEPT noexcept
+ #define LIBNFP_CONSTEXPR constexpr
+#endif
+
+#ifdef LIBNFP_USE_RATIONAL
+#include <boost/multiprecision/gmp.hpp>
+#include <boost/multiprecision/number.hpp>
+#endif
+#include <boost/geometry.hpp>
+#include <boost/geometry/util/math.hpp>
+#include <boost/geometry/geometries/point_xy.hpp>
+#include <boost/geometry/geometries/polygon.hpp>
+#include <boost/geometry/geometries/linestring.hpp>
+#include <boost/geometry/io/svg/svg_mapper.hpp>
+#include <boost/geometry/algorithms/intersects.hpp>
+#include <boost/geometry/geometries/register/point.hpp>
+
+#ifdef LIBNFP_USE_RATIONAL
+namespace bm = boost::multiprecision;
+#endif
+namespace bg = boost::geometry;
+namespace trans = boost::geometry::strategy::transform;
+
+
+namespace libnfporb {
+#ifdef NFP_DEBUG
+#define DEBUG_VAL(x) std::cerr << x << std::endl;
+#define DEBUG_MSG(title, value) std::cerr << title << ":" << value << std::endl;
+#else
+#define DEBUG_VAL(x)
+#define DEBUG_MSG(title, value)
+#endif
+
+using std::string;
+
+static LIBNFP_CONSTEXPR long double NFP_EPSILON=0.00000001;
+
+class LongDouble {
+private:
+ long double val_;
+public:
+ LongDouble() : val_(0) {
+ }
+
+ LongDouble(const long double& val) : val_(val) {
+ }
+
+ void setVal(const long double& v) {
+ val_ = v;
+ }
+
+ long double val() const {
+ return val_;
+ }
+
+ LongDouble operator/(const LongDouble& other) const {
+ return this->val_ / other.val_;
+ }
+
+ LongDouble operator*(const LongDouble& other) const {
+ return this->val_ * other.val_;
+ }
+
+ LongDouble operator-(const LongDouble& other) const {
+ return this->val_ - other.val_;
+ }
+
+ LongDouble operator-() const {
+ return this->val_ * -1;
+ }
+
+ LongDouble operator+(const LongDouble& other) const {
+ return this->val_ + other.val_;
+ }
+
+ void operator/=(const LongDouble& other) {
+ this->val_ = this->val_ / other.val_;
+ }
+
+ void operator*=(const LongDouble& other) {
+ this->val_ = this->val_ * other.val_;
+ }
+
+ void operator-=(const LongDouble& other) {
+ this->val_ = this->val_ - other.val_;
+ }
+
+ void operator+=(const LongDouble& other) {
+ this->val_ = this->val_ + other.val_;
+ }
+
+ bool operator==(const int& other) const {
+ return this->operator ==(static_cast<long double>(other));
+ }
+
+ bool operator==(const LongDouble& other) const {
+ return this->operator ==(other.val());
+ }
+
+ bool operator==(const long double& other) const {
+ return this->val() == other;
+ }
+
+ bool operator!=(const int& other) const {
+ return !this->operator ==(other);
+ }
+
+ bool operator!=(const LongDouble& other) const {
+ return !this->operator ==(other);
+ }
+
+ bool operator!=(const long double& other) const {
+ return !this->operator ==(other);
+ }
+
+ bool operator<(const int& other) const {
+ return this->operator <(static_cast<long double>(other));
+ }
+
+ bool operator<(const LongDouble& other) const {
+ return this->operator <(other.val());
+ }
+
+ bool operator<(const long double& other) const {
+ return this->val() < other;
+ }
+
+ bool operator>(const int& other) const {
+ return this->operator >(static_cast<long double>(other));
+ }
+
+ bool operator>(const LongDouble& other) const {
+ return this->operator >(other.val());
+ }
+
+ bool operator>(const long double& other) const {
+ return this->val() > other;
+ }
+
+ bool operator>=(const int& other) const {
+ return this->operator >=(static_cast<long double>(other));
+ }
+
+ bool operator>=(const LongDouble& other) const {
+ return this->operator >=(other.val());
+ }
+
+ bool operator>=(const long double& other) const {
+ return this->val() >= other;
+ }
+
+ bool operator<=(const int& other) const {
+ return this->operator <=(static_cast<long double>(other));
+ }
+
+ bool operator<=(const LongDouble& other) const {
+ return this->operator <=(other.val());
+ }
+
+ bool operator<=(const long double& other) const {
+ return this->val() <= other;
+ }
+};
+}
+
+
+namespace std {
+template<>
+ struct numeric_limits<libnfporb::LongDouble>
+ {
+ static const LIBNFP_CONSTEXPR bool is_specialized = true;
+
+ static const LIBNFP_CONSTEXPR long double
+ min() LIBNFP_NOEXCEPT { return std::numeric_limits<long double>::min(); }
+
+ static LIBNFP_CONSTEXPR long double
+ max() LIBNFP_NOEXCEPT { return std::numeric_limits<long double>::max(); }
+
+#if __cplusplus >= 201103L
+ static LIBNFP_CONSTEXPR long double
+ lowest() LIBNFP_NOEXCEPT { return -std::numeric_limits<long double>::lowest(); }
+#endif
+
+ static const LIBNFP_CONSTEXPR int digits = std::numeric_limits<long double>::digits;
+ static const LIBNFP_CONSTEXPR int digits10 = std::numeric_limits<long double>::digits10;
+#if __cplusplus >= 201103L
+ static const LIBNFP_CONSTEXPR int max_digits10
+ = std::numeric_limits<long double>::max_digits10;
+#endif
+ static const LIBNFP_CONSTEXPR bool is_signed = true;
+ static const LIBNFP_CONSTEXPR bool is_integer = false;
+ static const LIBNFP_CONSTEXPR bool is_exact = false;
+ static const LIBNFP_CONSTEXPR int radix = std::numeric_limits<long double>::radix;
+
+ static const LIBNFP_CONSTEXPR long double
+ epsilon() LIBNFP_NOEXCEPT { return libnfporb::NFP_EPSILON; }
+
+ static const LIBNFP_CONSTEXPR long double
+ round_error() LIBNFP_NOEXCEPT { return 0.5L; }
+
+ static const LIBNFP_CONSTEXPR int min_exponent = std::numeric_limits<long double>::min_exponent;
+ static const LIBNFP_CONSTEXPR int min_exponent10 = std::numeric_limits<long double>::min_exponent10;
+ static const LIBNFP_CONSTEXPR int max_exponent = std::numeric_limits<long double>::max_exponent;
+ static const LIBNFP_CONSTEXPR int max_exponent10 = std::numeric_limits<long double>::max_exponent10;
+
+
+ static const LIBNFP_CONSTEXPR bool has_infinity = std::numeric_limits<long double>::has_infinity;
+ static const LIBNFP_CONSTEXPR bool has_quiet_NaN = std::numeric_limits<long double>::has_quiet_NaN;
+ static const LIBNFP_CONSTEXPR bool has_signaling_NaN = has_quiet_NaN;
+ static const LIBNFP_CONSTEXPR float_denorm_style has_denorm
+ = std::numeric_limits<long double>::has_denorm;
+ static const LIBNFP_CONSTEXPR bool has_denorm_loss
+ = std::numeric_limits<long double>::has_denorm_loss;
+
+
+ static const LIBNFP_CONSTEXPR long double
+ infinity() LIBNFP_NOEXCEPT { return std::numeric_limits<long double>::infinity(); }
+
+ static const LIBNFP_CONSTEXPR long double
+ quiet_NaN() LIBNFP_NOEXCEPT { return std::numeric_limits<long double>::quiet_NaN(); }
+
+ static const LIBNFP_CONSTEXPR long double
+ signaling_NaN() LIBNFP_NOEXCEPT { return std::numeric_limits<long double>::signaling_NaN(); }
+
+
+ static const LIBNFP_CONSTEXPR long double
+ denorm_min() LIBNFP_NOEXCEPT { return std::numeric_limits<long double>::denorm_min(); }
+
+ static const LIBNFP_CONSTEXPR bool is_iec559
+ = has_infinity && has_quiet_NaN && has_denorm == denorm_present;
+
+ static const LIBNFP_CONSTEXPR bool is_bounded = true;
+ static const LIBNFP_CONSTEXPR bool is_modulo = false;
+
+ static const LIBNFP_CONSTEXPR bool traps = std::numeric_limits<long double>::traps;
+ static const LIBNFP_CONSTEXPR bool tinyness_before =
+ std::numeric_limits<long double>::tinyness_before;
+ static const LIBNFP_CONSTEXPR float_round_style round_style =
+ round_to_nearest;
+ };
+}
+
+namespace boost {
+namespace numeric {
+ template<>
+ struct raw_converter<boost::numeric::conversion_traits<double, libnfporb::LongDouble>>
+ {
+ typedef boost::numeric::conversion_traits<double, libnfporb::LongDouble>::result_type result_type ;
+ typedef boost::numeric::conversion_traits<double, libnfporb::LongDouble>::argument_type argument_type ;
+
+ static result_type low_level_convert ( argument_type s ) { return s.val() ; }
+ } ;
+}
+}
+
+namespace libnfporb {
+
+#ifndef LIBNFP_USE_RATIONAL
+typedef LongDouble coord_t;
+#else
+typedef bm::number<bm::gmp_rational, bm::et_off> rational_t;
+typedef rational_t coord_t;
+#endif
+
+bool equals(const LongDouble& lhs, const LongDouble& rhs);
+#ifdef LIBNFP_USE_RATIONAL
+bool equals(const rational_t& lhs, const rational_t& rhs);
+#endif
+bool equals(const long double& lhs, const long double& rhs);
+
+const coord_t MAX_COORD = 999999999999999999.0;
+const coord_t MIN_COORD = std::numeric_limits<coord_t>::min();
+
+class point_t {
+public:
+ point_t() : x_(0), y_(0) {
+ }
+ point_t(coord_t x, coord_t y) : x_(x), y_(y) {
+ }
+ bool marked_ = false;
+ coord_t x_;
+ coord_t y_;
+
+ point_t operator-(const point_t& other) const {
+ point_t result = *this;
+ bg::subtract_point(result, other);
+ return result;
+ }
+
+ point_t operator+(const point_t& other) const {
+ point_t result = *this;
+ bg::add_point(result, other);
+ return result;
+ }
+
+ bool operator==(const point_t& other) const {
+ return bg::equals(this, other);
+ }
+
+ bool operator!=(const point_t& other) const {
+ return !this->operator ==(other);
+ }
+
+ bool operator<(const point_t& other) const {
+ return boost::geometry::math::smaller(this->x_, other.x_) || (equals(this->x_, other.x_) && boost::geometry::math::smaller(this->y_, other.y_));
+ }
+};
+
+
+
+
+inline long double toLongDouble(const LongDouble& c) {
+ return c.val();
+}
+
+#ifdef LIBNFP_USE_RATIONAL
+inline long double toLongDouble(const rational_t& c) {
+ return bm::numerator(c).convert_to<long double>() / bm::denominator(c).convert_to<long double>();
+}
+#endif
+
+std::ostream& operator<<(std::ostream& os, const coord_t& p) {
+ os << toLongDouble(p);
+ return os;
+}
+
+std::istream& operator>>(std::istream& is, LongDouble& c) {
+ long double val;
+ is >> val;
+ c.setVal(val);
+ return is;
+}
+
+std::ostream& operator<<(std::ostream& os, const point_t& p) {
+ os << "{" << toLongDouble(p.x_) << "," << toLongDouble(p.y_) << "}";
+ return os;
+}
+const point_t INVALID_POINT = {MAX_COORD, MAX_COORD};
+
+typedef bg::model::segment<point_t> segment_t;
+}
+
+#ifdef LIBNFP_USE_RATIONAL
+inline long double acos(const libnfporb::rational_t& r) {
+ return acos(libnfporb::toLongDouble(r));
+}
+#endif
+
+inline long double acos(const libnfporb::LongDouble& ld) {
+ return acos(libnfporb::toLongDouble(ld));
+}
+
+#ifdef LIBNFP_USE_RATIONAL
+inline long double sqrt(const libnfporb::rational_t& r) {
+ return sqrt(libnfporb::toLongDouble(r));
+}
+#endif
+
+inline long double sqrt(const libnfporb::LongDouble& ld) {
+ return sqrt(libnfporb::toLongDouble(ld));
+}
+
+BOOST_GEOMETRY_REGISTER_POINT_2D(libnfporb::point_t, libnfporb::coord_t, cs::cartesian, x_, y_)
+
+
+namespace boost {
+namespace geometry {
+namespace math {
+namespace detail {
+
+template <>
+struct square_root<libnfporb::LongDouble>
+{
+ typedef libnfporb::LongDouble return_type;
+
+ static inline libnfporb::LongDouble apply(libnfporb::LongDouble const& a)
+ {
+ return std::sqrt(a.val());
+ }
+};
+
+#ifdef LIBNFP_USE_RATIONAL
+template <>
+struct square_root<libnfporb::rational_t>
+{
+ typedef libnfporb::rational_t return_type;
+
+ static inline libnfporb::rational_t apply(libnfporb::rational_t const& a)
+ {
+ return std::sqrt(libnfporb::toLongDouble(a));
+ }
+};
+#endif
+
+template<>
+struct abs<libnfporb::LongDouble>
+ {
+ static libnfporb::LongDouble apply(libnfporb::LongDouble const& value)
+ {
+ libnfporb::LongDouble const zero = libnfporb::LongDouble();
+ return value.val() < zero.val() ? -value.val() : value.val();
+ }
+ };
+
+template <>
+struct equals<libnfporb::LongDouble, false>
+{
+ template<typename Policy>
+ static inline bool apply(libnfporb::LongDouble const& lhs, libnfporb::LongDouble const& rhs, Policy const& policy)
+ {
+ if(lhs.val() == rhs.val())
+ return true;
+
+ return bg::math::detail::abs<libnfporb::LongDouble>::apply(lhs.val() - rhs.val()) <= policy.apply(lhs.val(), rhs.val()) * libnfporb::NFP_EPSILON;
+ }
+};
+
+template <>
+struct smaller<libnfporb::LongDouble>
+{
+ static inline bool apply(libnfporb::LongDouble const& lhs, libnfporb::LongDouble const& rhs)
+ {
+ if(lhs.val() == rhs.val() || bg::math::detail::abs<libnfporb::LongDouble>::apply(lhs.val() - rhs.val()) <= libnfporb::NFP_EPSILON * std::max(lhs.val(), rhs.val()))
+ return false;
+
+ return lhs < rhs;
+ }
+};
+}
+}
+}
+}
+
+namespace libnfporb {
+inline bool smaller(const LongDouble& lhs, const LongDouble& rhs) {
+ return boost::geometry::math::detail::smaller<LongDouble>::apply(lhs, rhs);
+}
+
+inline bool larger(const LongDouble& lhs, const LongDouble& rhs) {
+ return smaller(rhs, lhs);
+}
+
+bool equals(const LongDouble& lhs, const LongDouble& rhs) {
+ if(lhs.val() == rhs.val())
+ return true;
+
+ return bg::math::detail::abs<libnfporb::LongDouble>::apply(lhs.val() - rhs.val()) <= libnfporb::NFP_EPSILON * std::max(lhs.val(), rhs.val());
+}
+
+#ifdef LIBNFP_USE_RATIONAL
+inline bool smaller(const rational_t& lhs, const rational_t& rhs) {
+ return lhs < rhs;
+}
+
+inline bool larger(const rational_t& lhs, const rational_t& rhs) {
+ return smaller(rhs, lhs);
+}
+
+bool equals(const rational_t& lhs, const rational_t& rhs) {
+ return lhs == rhs;
+}
+#endif
+
+inline bool smaller(const long double& lhs, const long double& rhs) {
+ return lhs < rhs;
+}
+
+inline bool larger(const long double& lhs, const long double& rhs) {
+ return smaller(rhs, lhs);
+}
+
+
+bool equals(const long double& lhs, const long double& rhs) {
+ return lhs == rhs;
+}
+
+typedef bg::model::polygon<point_t, false, true> polygon_t;
+typedef std::vector<polygon_t::ring_type> nfp_t;
+typedef bg::model::linestring<point_t> linestring_t;
+
+typedef polygon_t::ring_type::size_type psize_t;
+
+typedef bg::model::d2::point_xy<long double> pointf_t;
+typedef bg::model::segment<pointf_t> segmentf_t;
+typedef bg::model::polygon<pointf_t, false, true> polygonf_t;
+
+polygonf_t::ring_type convert(const polygon_t::ring_type& r) {
+ polygonf_t::ring_type rf;
+ for(const auto& pt : r) {
+ rf.push_back(pointf_t(toLongDouble(pt.x_), toLongDouble(pt.y_)));
+ }
+ return rf;
+}
+
+polygonf_t convert(polygon_t p) {
+ polygonf_t pf;
+ pf.outer() = convert(p.outer());
+
+ for(const auto& r : p.inners()) {
+ pf.inners().push_back(convert(r));
+ }
+
+ return pf;
+}
+
+polygon_t nfpRingsToNfpPoly(const nfp_t& nfp) {
+ polygon_t nfppoly;
+ for (const auto& pt : nfp.front()) {
+ nfppoly.outer().push_back(pt);
+ }
+
+ for (size_t i = 1; i < nfp.size(); ++i) {
+ nfppoly.inners().push_back({});
+ for (const auto& pt : nfp[i]) {
+ nfppoly.inners().back().push_back(pt);
+ }
+ }
+
+ return nfppoly;
+}
+
+void write_svg(std::string const& filename,const std::vector<segment_t>& segments) {
+ std::ofstream svg(filename.c_str());
+
+ boost::geometry::svg_mapper<pointf_t> mapper(svg, 100, 100, "width=\"200mm\" height=\"200mm\" viewBox=\"-250 -250 500 500\"");
+ for(const auto& seg : segments) {
+ segmentf_t segf({toLongDouble(seg.first.x_), toLongDouble(seg.first.y_)}, {toLongDouble(seg.second.x_), toLongDouble(seg.second.y_)});
+ mapper.add(segf);
+ mapper.map(segf, "fill-opacity:0.5;fill:rgb(153,204,0);stroke:rgb(153,204,0);stroke-width:2");
+ }
+}
+
+void write_svg(std::string const& filename, const polygon_t& p, const polygon_t::ring_type& ring) {
+ std::ofstream svg(filename.c_str());
+
+ boost::geometry::svg_mapper<pointf_t> mapper(svg, 100, 100, "width=\"200mm\" height=\"200mm\" viewBox=\"-250 -250 500 500\"");
+ auto pf = convert(p);
+ auto rf = convert(ring);
+
+ mapper.add(pf);
+ mapper.map(pf, "fill-opacity:0.5;fill:rgb(153,204,0);stroke:rgb(153,204,0);stroke-width:2");
+ mapper.add(rf);
+ mapper.map(rf, "fill-opacity:0.5;fill:rgb(153,204,0);stroke:rgb(153,204,0);stroke-width:2");
+}
+
+void write_svg(std::string const& filename, std::vector<polygon_t> const& polygons) {
+ std::ofstream svg(filename.c_str());
+
+ boost::geometry::svg_mapper<pointf_t> mapper(svg, 100, 100, "width=\"200mm\" height=\"200mm\" viewBox=\"-250 -250 500 500\"");
+ for (auto p : polygons) {
+ auto pf = convert(p);
+ mapper.add(pf);
+ mapper.map(pf, "fill-opacity:0.5;fill:rgb(153,204,0);stroke:rgb(153,204,0);stroke-width:2");
+ }
+}
+
+void write_svg(std::string const& filename, std::vector<polygon_t> const& polygons, const nfp_t& nfp) {
+ polygon_t nfppoly;
+ for (const auto& pt : nfp.front()) {
+ nfppoly.outer().push_back(pt);
+ }
+
+ for (size_t i = 1; i < nfp.size(); ++i) {
+ nfppoly.inners().push_back({});
+ for (const auto& pt : nfp[i]) {
+ nfppoly.inners().back().push_back(pt);
+ }
+ }
+ std::ofstream svg(filename.c_str());
+
+ boost::geometry::svg_mapper<pointf_t> mapper(svg, 100, 100, "width=\"200mm\" height=\"200mm\" viewBox=\"-250 -250 500 500\"");
+ for (auto p : polygons) {
+ auto pf = convert(p);
+ mapper.add(pf);
+ mapper.map(pf, "fill-opacity:0.5;fill:rgb(153,204,0);stroke:rgb(153,204,0);stroke-width:2");
+ }
+ bg::correct(nfppoly);
+ auto nfpf = convert(nfppoly);
+ mapper.add(nfpf);
+ mapper.map(nfpf, "fill-opacity:0.5;fill:rgb(204,153,0);stroke:rgb(204,153,0);stroke-width:2");
+
+ for(auto& r: nfpf.inners()) {
+ if(r.size() == 1) {
+ mapper.add(r.front());
+ mapper.map(r.front(), "fill-opacity:0.5;fill:rgb(204,153,0);stroke:rgb(204,153,0);stroke-width:2");
+ } else if(r.size() == 2) {
+ segmentf_t seg(r.front(), *(r.begin()+1));
+ mapper.add(seg);
+ mapper.map(seg, "fill-opacity:0.5;fill:rgb(204,153,0);stroke:rgb(204,153,0);stroke-width:2");
+ }
+ }
+}
+
+std::ostream& operator<<(std::ostream& os, const segment_t& seg) {
+ os << "{" << seg.first << "," << seg.second << "}";
+ return os;
+}
+
+bool operator<(const segment_t& lhs, const segment_t& rhs) {
+ return lhs.first < rhs.first || ((lhs.first == rhs.first) && (lhs.second < rhs.second));
+}
+
+bool operator==(const segment_t& lhs, const segment_t& rhs) {
+ return (lhs.first == rhs.first && lhs.second == rhs.second) || (lhs.first == rhs.second && lhs.second == rhs.first);
+}
+
+bool operator!=(const segment_t& lhs, const segment_t& rhs) {
+ return !operator==(lhs,rhs);
+}
+
+enum Alignment {
+ LEFT,
+ RIGHT,
+ ON
+};
+
+point_t normalize(const point_t& pt) {
+ point_t norm = pt;
+ coord_t len = bg::length(segment_t{{0,0},pt});
+
+ if(len == 0.0L)
+ return {0,0};
+
+ norm.x_ /= len;
+ norm.y_ /= len;
+
+ return norm;
+}
+
+Alignment get_alignment(const segment_t& seg, const point_t& pt){
+ coord_t res = ((seg.second.x_ - seg.first.x_)*(pt.y_ - seg.first.y_)
+ - (seg.second.y_ - seg.first.y_)*(pt.x_ - seg.first.x_));
+
+ if(equals(res, 0)) {
+ return ON;
+ } else if(larger(res,0)) {
+ return LEFT;
+ } else {
+ return RIGHT;
+ }
+}
+
+long double get_inner_angle(const point_t& joint, const point_t& end1, const point_t& end2) {
+ coord_t dx21 = end1.x_-joint.x_;
+ coord_t dx31 = end2.x_-joint.x_;
+ coord_t dy21 = end1.y_-joint.y_;
+ coord_t dy31 = end2.y_-joint.y_;
+ coord_t m12 = sqrt((dx21*dx21 + dy21*dy21));
+ coord_t m13 = sqrt((dx31*dx31 + dy31*dy31));
+ if(m12 == 0.0L || m13 == 0.0L)
+ return 0;
+ return acos( (dx21*dx31 + dy21*dy31) / (m12 * m13) );
+}
+
+struct TouchingPoint {
+ enum Type {
+ VERTEX,
+ A_ON_B,
+ B_ON_A
+ };
+ Type type_;
+ psize_t A_;
+ psize_t B_;
+};
+
+struct TranslationVector {
+ point_t vector_;
+ segment_t edge_;
+ bool fromA_;
+ string name_;
+
+ bool operator<(const TranslationVector& other) const {
+ return this->vector_ < other.vector_ || ((this->vector_ == other.vector_) && (this->edge_ < other.edge_));
+ }
+};
+
+std::ostream& operator<<(std::ostream& os, const TranslationVector& tv) {
+ os << "{" << tv.edge_ << " -> " << tv.vector_ << "} = " << tv.name_;
+ return os;
+}
+
+
+void read_wkt_polygon(const string& filename, polygon_t& p) {
+ std::ifstream t(filename);
+
+ std::string str;
+ t.seekg(0, std::ios::end);
+ str.reserve(t.tellg());
+ t.seekg(0, std::ios::beg);
+
+ str.assign((std::istreambuf_iterator<char>(t)),
+ std::istreambuf_iterator<char>());
+
+ str.pop_back();
+ bg::read_wkt(str, p);
+ bg::correct(p);
+}
+
+std::vector<psize_t> find_minimum_y(const polygon_t& p) {
+ std::vector<psize_t> result;
+ coord_t min = MAX_COORD;
+ auto& po = p.outer();
+ for(psize_t i = 0; i < p.outer().size() - 1; ++i) {
+ if(smaller(po[i].y_, min)) {
+ result.clear();
+ min = po[i].y_;
+ result.push_back(i);
+ } else if (equals(po[i].y_, min)) {
+ result.push_back(i);
+ }
+ }
+ return result;
+}
+
+std::vector<psize_t> find_maximum_y(const polygon_t& p) {
+ std::vector<psize_t> result;
+ coord_t max = MIN_COORD;
+ auto& po = p.outer();
+ for(psize_t i = 0; i < p.outer().size() - 1; ++i) {
+ if(larger(po[i].y_, max)) {
+ result.clear();
+ max = po[i].y_;
+ result.push_back(i);
+ } else if (equals(po[i].y_, max)) {
+ result.push_back(i);
+ }
+ }
+ return result;
+}
+
+psize_t find_point(const polygon_t::ring_type& ring, const point_t& pt) {
+ for(psize_t i = 0; i < ring.size(); ++i) {
+ if(ring[i] == pt)
+ return i;
+ }
+ return std::numeric_limits<psize_t>::max();
+}
+
+std::vector<TouchingPoint> findTouchingPoints(const polygon_t::ring_type& ringA, const polygon_t::ring_type& ringB) {
+ std::vector<TouchingPoint> touchers;
+ for(psize_t i = 0; i < ringA.size() - 1; i++) {
+ psize_t nextI = i+1;
+ for(psize_t j = 0; j < ringB.size() - 1; j++) {
+ psize_t nextJ = j+1;
+ if(ringA[i] == ringB[j]) {
+ touchers.push_back({TouchingPoint::VERTEX, i, j});
+ } else if (ringA[nextI] != ringB[j] && bg::intersects(segment_t(ringA[i],ringA[nextI]), ringB[j])) {
+ touchers.push_back({TouchingPoint::B_ON_A, nextI, j});
+ } else if (ringB[nextJ] != ringA[i] && bg::intersects(segment_t(ringB[j],ringB[nextJ]), ringA[i])) {
+ touchers.push_back({TouchingPoint::A_ON_B, i, nextJ});
+ }
+ }
+ }
+ return touchers;
+}
+
+//TODO deduplicate code
+TranslationVector trimVector(const polygon_t::ring_type& rA, const polygon_t::ring_type& rB, const TranslationVector& tv) {
+ coord_t shortest = bg::length(tv.edge_);
+ TranslationVector trimmed = tv;
+ for(const auto& ptA : rA) {
+ point_t translated;
+ //for polygon A we invert the translation
+ trans::translate_transformer<coord_t, 2, 2> translate(-tv.vector_.x_, -tv.vector_.y_);
+ boost::geometry::transform(ptA, translated, translate);
+ linestring_t projection;
+ segment_t segproj(ptA, translated);
+ projection.push_back(ptA);
+ projection.push_back(translated);
+ std::vector<point_t> intersections;
+ bg::intersection(rB, projection, intersections);
+ if(bg::touches(projection, rB) && intersections.size() < 2) {
+ continue;
+ }
+
+ //find shortest intersection
+ coord_t len;
+ segment_t segi;
+ for(const auto& pti : intersections) {
+ segi = segment_t(ptA,pti);
+ len = bg::length(segi);
+ if(smaller(len, shortest)) {
+ trimmed.vector_ = ptA - pti;
+ trimmed.edge_ = segi;
+ shortest = len;
+ }
+ }
+ }
+
+ for(const auto& ptB : rB) {
+ point_t translated;
+
+ trans::translate_transformer<coord_t, 2, 2> translate(tv.vector_.x_, tv.vector_.y_);
+ boost::geometry::transform(ptB, translated, translate);
+ linestring_t projection;
+ segment_t segproj(ptB, translated);
+ projection.push_back(ptB);
+ projection.push_back(translated);
+ std::vector<point_t> intersections;
+ bg::intersection(rA, projection, intersections);
+ if(bg::touches(projection, rA) && intersections.size() < 2) {
+ continue;
+ }
+
+ //find shortest intersection
+ coord_t len;
+ segment_t segi;
+ for(const auto& pti : intersections) {
+
+ segi = segment_t(ptB,pti);
+ len = bg::length(segi);
+ if(smaller(len, shortest)) {
+ trimmed.vector_ = pti - ptB;
+ trimmed.edge_ = segi;
+ shortest = len;
+ }
+ }
+ }
+ return trimmed;
+}
+
+std::vector<TranslationVector> findFeasibleTranslationVectors(polygon_t::ring_type& ringA, polygon_t::ring_type& ringB, const std::vector<TouchingPoint>& touchers) {
+ //use a set to automatically filter duplicate vectors
+ std::vector<TranslationVector> potentialVectors;
+ std::vector<std::pair<segment_t,segment_t>> touchEdges;
+
+ for (psize_t i = 0; i < touchers.size(); i++) {
+ point_t& vertexA = ringA[touchers[i].A_];
+ vertexA.marked_ = true;
+
+ // adjacent A vertices
+ auto prevAindex = static_cast<signed long>(touchers[i].A_ - 1);
+ auto nextAindex = static_cast<signed long>(touchers[i].A_ + 1);
+
+ prevAindex = (prevAindex < 0) ? static_cast<signed long>(ringA.size() - 2) : prevAindex; // loop
+ nextAindex = (static_cast<psize_t>(nextAindex) >= ringA.size()) ? 1 : nextAindex; // loop
+
+ point_t& prevA = ringA[prevAindex];
+ point_t& nextA = ringA[nextAindex];
+
+ // adjacent B vertices
+ point_t& vertexB = ringB[touchers[i].B_];
+
+ auto prevBindex = static_cast<signed long>(touchers[i].B_ - 1);
+ auto nextBindex = static_cast<signed long>(touchers[i].B_ + 1);
+
+ prevBindex = (prevBindex < 0) ? static_cast<signed long>(ringB.size() - 2) : prevBindex; // loop
+ nextBindex = (static_cast<psize_t>(nextBindex) >= ringB.size()) ? 1 : nextBindex; // loop
+
+ point_t& prevB = ringB[prevBindex];
+ point_t& nextB = ringB[nextBindex];
+
+ if (touchers[i].type_ == TouchingPoint::VERTEX) {
+ segment_t a1 = { vertexA, nextA };
+ segment_t a2 = { vertexA, prevA };
+ segment_t b1 = { vertexB, nextB };
+ segment_t b2 = { vertexB, prevB };
+
+ //swap the segment elements so that always the first point is the touching point
+ //also make the second segment always a segment of ringB
+ touchEdges.push_back({a1, b1});
+ touchEdges.push_back({a1, b2});
+ touchEdges.push_back({a2, b1});
+ touchEdges.push_back({a2, b2});
+#ifdef NFP_DEBUG
+ write_svg("touchersV" + std::to_string(i) + ".svg", {a1,a2,b1,b2});
+#endif
+
+ //TODO test parallel edges for floating point stability
+ Alignment al;
+ //a1 and b1 meet at start vertex
+ al = get_alignment(a1, b1.second);
+ if(al == LEFT) {
+ potentialVectors.push_back({b1.first - b1.second, b1, false, "vertex1"});
+ } else if(al == RIGHT) {
+ potentialVectors.push_back({a1.second - a1.first, a1, true, "vertex2"});
+ } else {
+ potentialVectors.push_back({a1.second - a1.first, a1, true, "vertex3"});
+ }
+
+ //a1 and b2 meet at start and end
+ al = get_alignment(a1, b2.second);
+ if(al == LEFT) {
+ //no feasible translation
+ } else if(al == RIGHT) {
+ potentialVectors.push_back({a1.second - a1.first, a1, true, "vertex4"});
+ } else {
+ potentialVectors.push_back({a1.second - a1.first, a1, true, "vertex5"});
+ }
+
+ //a2 and b1 meet at end and start
+ al = get_alignment(a2, b1.second);
+ if(al == LEFT) {
+ //no feasible translation
+ } else if(al == RIGHT) {
+ potentialVectors.push_back({b1.first - b1.second, b1, false, "vertex6"});
+ } else {
+ potentialVectors.push_back({b1.first - b1.second, b1, false, "vertex7"});
+ }
+ } else if (touchers[i].type_ == TouchingPoint::B_ON_A) {
+ segment_t a1 = {vertexB, vertexA};
+ segment_t a2 = {vertexB, prevA};
+ segment_t b1 = {vertexB, prevB};
+ segment_t b2 = {vertexB, nextB};
+
+ touchEdges.push_back({a1, b1});
+ touchEdges.push_back({a1, b2});
+ touchEdges.push_back({a2, b1});
+ touchEdges.push_back({a2, b2});
+#ifdef NFP_DEBUG
+ write_svg("touchersB" + std::to_string(i) + ".svg", {a1,a2,b1,b2});
+#endif
+ potentialVectors.push_back({vertexA - vertexB, {vertexB, vertexA}, true, "bona"});
+ } else if (touchers[i].type_ == TouchingPoint::A_ON_B) {
+ //TODO testme
+ segment_t a1 = {vertexA, prevA};
+ segment_t a2 = {vertexA, nextA};
+ segment_t b1 = {vertexA, vertexB};
+ segment_t b2 = {vertexA, prevB};
+#ifdef NFP_DEBUG
+ write_svg("touchersA" + std::to_string(i) + ".svg", {a1,a2,b1,b2});
+#endif
+ touchEdges.push_back({a1, b1});
+ touchEdges.push_back({a2, b1});
+ touchEdges.push_back({a1, b2});
+ touchEdges.push_back({a2, b2});
+ potentialVectors.push_back({vertexA - vertexB, {vertexA, vertexB}, false, "aonb"});
+ }
+ }
+
+ //discard immediately intersecting translations
+ std::vector<TranslationVector> vectors;
+ for(const auto& v : potentialVectors) {
+ bool discarded = false;
+ for(const auto& sp : touchEdges) {
+ point_t normEdge = normalize(v.edge_.second - v.edge_.first);
+ point_t normFirst = normalize(sp.first.second - sp.first.first);
+ point_t normSecond = normalize(sp.second.second - sp.second.first);
+
+ Alignment a1 = get_alignment({{0,0},normEdge}, normFirst);
+ Alignment a2 = get_alignment({{0,0},normEdge}, normSecond);
+
+ if(a1 == a2 && a1 != ON) {
+ long double df = get_inner_angle({0,0},normEdge, normFirst);
+ long double ds = get_inner_angle({0,0},normEdge, normSecond);
+
+ point_t normIn = normalize(v.edge_.second - v.edge_.first);
+ if (equals(df, ds)) {
+ TranslationVector trimmed = trimVector(ringA,ringB, v);
+ polygon_t::ring_type translated;
+ trans::translate_transformer<coord_t, 2, 2> translate(trimmed.vector_.x_, trimmed.vector_.y_);
+ boost::geometry::transform(ringB, translated, translate);
+ if (!(bg::intersects(translated, ringA) && !bg::overlaps(translated, ringA) && !bg::covered_by(translated, ringA) && !bg::covered_by(ringA, translated))) {
+ discarded = true;
+ break;
+ }
+ } else {
+
+ if (normIn == normalize(v.vector_)) {
+ if (larger(ds, df)) {
+ discarded = true;
+ break;
+ }
+ } else {
+ if (smaller(ds, df)) {
+ discarded = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+ if(!discarded)
+ vectors.push_back(v);
+ }
+ return vectors;
+}
+
+bool find(const std::vector<TranslationVector>& h, const TranslationVector& tv) {
+ for(const auto& htv : h) {
+ if(htv.vector_ == tv.vector_)
+ return true;
+ }
+ return false;
+}
+
+TranslationVector getLongest(const std::vector<TranslationVector>& tvs) {
+ coord_t len;
+ coord_t maxLen = MIN_COORD;
+ TranslationVector longest;
+ longest.vector_ = INVALID_POINT;
+
+ for(auto& tv : tvs) {
+ len = bg::length(segment_t{{0,0},tv.vector_});
+ if(larger(len, maxLen)) {
+ maxLen = len;
+ longest = tv;
+ }
+ }
+ return longest;
+}
+
+TranslationVector selectNextTranslationVector(const polygon_t& pA, const polygon_t::ring_type& rA, const polygon_t::ring_type& rB, const std::vector<TranslationVector>& tvs, const std::vector<TranslationVector>& history) {
+ if(!history.empty()) {
+ TranslationVector last = history.back();
+ std::vector<TranslationVector> historyCopy = history;
+ if(historyCopy.size() >= 2) {
+ historyCopy.erase(historyCopy.end() - 1);
+ historyCopy.erase(historyCopy.end() - 1);
+ if(historyCopy.size() > 4) {
+ historyCopy.erase(historyCopy.begin(), historyCopy.end() - 4);
+ }
+
+ } else {
+ historyCopy.clear();
+ }
+ DEBUG_MSG("last", last);
+
+ psize_t laterI = std::numeric_limits<psize_t>::max();
+ point_t previous = rA[0];
+ point_t next;
+
+ if(last.fromA_) {
+ for (psize_t i = 1; i < rA.size() + 1; ++i) {
+ if (i >= rA.size())
+ next = rA[i % rA.size()];
+ else
+ next = rA[i];
+
+ segment_t candidate( previous, next );
+ if(candidate == last.edge_) {
+ laterI = i;
+ break;
+ }
+ previous = next;
+ }
+
+ if (laterI == std::numeric_limits<psize_t>::max()) {
+ point_t later;
+ if (last.vector_ == (last.edge_.second - last.edge_.first)) {
+ later = last.edge_.second;
+ } else {
+ later = last.edge_.first;
+ }
+
+ laterI = find_point(rA, later);
+ }
+ } else {
+ point_t later;
+ if (last.vector_ == (last.edge_.second - last.edge_.first)) {
+ later = last.edge_.second;
+ } else {
+ later = last.edge_.first;
+ }
+
+ laterI = find_point(rA, later);
+ }
+
+ if (laterI == std::numeric_limits<psize_t>::max()) {
+ throw std::runtime_error(
+ "Internal error: Can't find later point of last edge");
+ }
+
+ std::vector<segment_t> viableEdges;
+ previous = rA[laterI];
+ for(psize_t i = laterI + 1; i < rA.size() + laterI + 1; ++i) {
+ if(i >= rA.size())
+ next = rA[i % rA.size()];
+ else
+ next = rA[i];
+
+ viableEdges.push_back({previous, next});
+ previous = next;
+ }
+
+// auto rng = std::default_random_engine {};
+// std::shuffle(std::begin(viableEdges), std::end(viableEdges), rng);
+
+ //search with consulting the history to prevent oscillation
+ std::vector<TranslationVector> viableTrans;
+ for(const auto& ve: viableEdges) {
+ for(const auto& tv : tvs) {
+ if((tv.fromA_ && (normalize(tv.vector_) == normalize(ve.second - ve.first))) && (tv.edge_ != last.edge_ || tv.vector_.x_ != -last.vector_.x_ || tv.vector_.y_ != -last.vector_.y_) && !find(historyCopy, tv)) {
+ viableTrans.push_back(tv);
+ }
+ }
+ for (const auto& tv : tvs) {
+ if (!tv.fromA_) {
+ point_t later;
+ if (tv.vector_ == (tv.edge_.second - tv.edge_.first) && (tv.edge_ != last.edge_ || tv.vector_.x_ != -last.vector_.x_ || tv.vector_.y_ != -last.vector_.y_) && !find(historyCopy, tv)) {
+ later = tv.edge_.second;
+ } else if (tv.vector_ == (tv.edge_.first - tv.edge_.second)) {
+ later = tv.edge_.first;
+ } else
+ continue;
+
+ if (later == ve.first || later == ve.second) {
+ viableTrans.push_back(tv);
+ }
+ }
+ }
+ }
+
+ if(!viableTrans.empty())
+ return getLongest(viableTrans);
+
+ //search again without the history
+ for(const auto& ve: viableEdges) {
+ for(const auto& tv : tvs) {
+ if((tv.fromA_ && (normalize(tv.vector_) == normalize(ve.second - ve.first))) && (tv.edge_ != last.edge_ || tv.vector_.x_ != -last.vector_.x_ || tv.vector_.y_ != -last.vector_.y_)) {
+ viableTrans.push_back(tv);
+ }
+ }
+ for (const auto& tv : tvs) {
+ if (!tv.fromA_) {
+ point_t later;
+ if (tv.vector_ == (tv.edge_.second - tv.edge_.first) && (tv.edge_ != last.edge_ || tv.vector_.x_ != -last.vector_.x_ || tv.vector_.y_ != -last.vector_.y_)) {
+ later = tv.edge_.second;
+ } else if (tv.vector_ == (tv.edge_.first - tv.edge_.second)) {
+ later = tv.edge_.first;
+ } else
+ continue;
+
+ if (later == ve.first || later == ve.second) {
+ viableTrans.push_back(tv);
+ }
+ }
+ }
+ }
+ if(!viableTrans.empty())
+ return getLongest(viableTrans);
+
+ /*
+ //search again without the history and without checking last edge
+ for(const auto& ve: viableEdges) {
+ for(const auto& tv : tvs) {
+ if((tv.fromA_ && (normalize(tv.vector_) == normalize(ve.second - ve.first)))) {
+ return tv;
+ }
+ }
+ for (const auto& tv : tvs) {
+ if (!tv.fromA_) {
+ point_t later;
+ if (tv.vector_ == (tv.edge_.second - tv.edge_.first)) {
+ later = tv.edge_.second;
+ } else if (tv.vector_ == (tv.edge_.first - tv.edge_.second)) {
+ later = tv.edge_.first;
+ } else
+ continue;
+
+ if (later == ve.first || later == ve.second) {
+ return tv;
+ }
+ }
+ }
+ }*/
+
+ if(tvs.size() == 1)
+ return *tvs.begin();
+
+ TranslationVector tv;
+ tv.vector_ = INVALID_POINT;
+ return tv;
+ } else {
+ return getLongest(tvs);
+ }
+}
+
+bool inNfp(const point_t& pt, const nfp_t& nfp) {
+ for(const auto& r : nfp) {
+ if(bg::touches(pt, r))
+ return true;
+ }
+
+ return false;
+}
+
+enum SearchStartResult {
+ FIT,
+ FOUND,
+ NOT_FOUND
+};
+
+SearchStartResult searchStartTranslation(polygon_t::ring_type& rA, const polygon_t::ring_type& rB, const nfp_t& nfp,const bool& inside, point_t& result) {
+ for(psize_t i = 0; i < rA.size() - 1; i++) {
+ psize_t index;
+ if (i >= rA.size())
+ index = i % rA.size() + 1;
+ else
+ index = i;
+
+ auto& ptA = rA[index];
+
+ if(ptA.marked_)
+ continue;
+
+ ptA.marked_ = true;
+
+ for(const auto& ptB: rB) {
+ point_t testTranslation = ptA - ptB;
+ polygon_t::ring_type translated;
+ boost::geometry::transform(rB, translated, trans::translate_transformer<coord_t, 2, 2>(testTranslation.x_, testTranslation.y_));
+
+ //check if the translated rB is identical to rA
+ bool identical = false;
+ for(const auto& ptT: translated) {
+ identical = false;
+ for(const auto& ptA: rA) {
+ if(ptT == ptA) {
+ identical = true;
+ break;
+ }
+ }
+ if(!identical)
+ break;
+ }
+
+ if(identical) {
+ result = testTranslation;
+ return FIT;
+ }
+
+ bool bInside = false;
+ for(const auto& ptT: translated) {
+ if(bg::within(ptT, rA)) {
+ bInside = true;
+ break;
+ } else if(!bg::touches(ptT, rA)) {
+ bInside = false;
+ break;
+ }
+ }
+
+ if(((bInside && inside) || (!bInside && !inside)) && (!bg::overlaps(translated, rA) && !bg::covered_by(translated, rA) && !bg::covered_by(rA, translated)) && !inNfp(translated.front(), nfp)){
+ result = testTranslation;
+ return FOUND;
+ }
+
+ point_t nextPtA = rA[index + 1];
+ TranslationVector slideVector;
+ slideVector.vector_ = nextPtA - ptA;
+ slideVector.edge_ = {ptA, nextPtA};
+ slideVector.fromA_ = true;
+ TranslationVector trimmed = trimVector(rA, translated, slideVector);
+ polygon_t::ring_type translated2;
+ trans::translate_transformer<coord_t, 2, 2> trans(trimmed.vector_.x_, trimmed.vector_.y_);
+ boost::geometry::transform(translated, translated2, trans);
+
+ //check if the translated rB is identical to rA
+ identical = false;
+ for(const auto& ptT: translated) {
+ identical = false;
+ for(const auto& ptA: rA) {
+ if(ptT == ptA) {
+ identical = true;
+ break;
+ }
+ }
+ if(!identical)
+ break;
+ }
+
+ if(identical) {
+ result = trimmed.vector_ + testTranslation;
+ return FIT;
+ }
+
+ bInside = false;
+ for(const auto& ptT: translated2) {
+ if(bg::within(ptT, rA)) {
+ bInside = true;
+ break;
+ } else if(!bg::touches(ptT, rA)) {
+ bInside = false;
+ break;
+ }
+ }
+
+ if(((bInside && inside) || (!bInside && !inside)) && (!bg::overlaps(translated2, rA) && !bg::covered_by(translated2, rA) && !bg::covered_by(rA, translated2)) && !inNfp(translated2.front(), nfp)){
+ result = trimmed.vector_ + testTranslation;
+ return FOUND;
+ }
+ }
+ }
+ return NOT_FOUND;
+}
+
+enum SlideResult {
+ LOOP,
+ NO_LOOP,
+ NO_TRANSLATION
+};
+
+SlideResult slide(polygon_t& pA, polygon_t::ring_type& rA, polygon_t::ring_type& rB, nfp_t& nfp, const point_t& transB, bool inside) {
+ polygon_t::ring_type rifsB;
+ boost::geometry::transform(rB, rifsB, trans::translate_transformer<coord_t, 2, 2>(transB.x_, transB.y_));
+ rB = std::move(rifsB);
+
+#ifdef NFP_DEBUG
+ write_svg("ifs.svg", pA, rB);
+#endif
+
+ bool startAvailable = true;
+ psize_t cnt = 0;
+ point_t referenceStart = rB.front();
+ std::vector<TranslationVector> history;
+
+ //generate the nfp for the ring
+ while(startAvailable) {
+ DEBUG_VAL(cnt);
+ //use first point of rB as reference
+ nfp.back().push_back(rB.front());
+ if(cnt == 15)
+ std::cerr << "";
+
+ std::vector<TouchingPoint> touchers = findTouchingPoints(rA, rB);
+
+#ifdef NFP_DEBUG
+ DEBUG_MSG("touchers", touchers.size());
+ for(auto t : touchers) {
+ DEBUG_VAL(t.type_);
+ }
+#endif
+ if(touchers.empty()) {
+ throw std::runtime_error("Internal error: No touching points found");
+ }
+ std::vector<TranslationVector> transVectors = findFeasibleTranslationVectors(rA, rB, touchers);
+
+#ifdef NFP_DEBUG
+ DEBUG_MSG("collected vectors", transVectors.size());
+ for(auto pt : transVectors) {
+ DEBUG_VAL(pt);
+ }
+#endif
+
+ if(transVectors.empty()) {
+ return NO_LOOP;
+ }
+
+ TranslationVector next = selectNextTranslationVector(pA, rA, rB, transVectors, history);
+
+ if(next.vector_ == INVALID_POINT)
+ return NO_TRANSLATION;
+
+ DEBUG_MSG("next", next);
+
+ TranslationVector trimmed = trimVector(rA, rB, next);
+ DEBUG_MSG("trimmed", trimmed);
+
+ history.push_back(next);
+
+ polygon_t::ring_type nextRB;
+ boost::geometry::transform(rB, nextRB, trans::translate_transformer<coord_t, 2, 2>(trimmed.vector_.x_, trimmed.vector_.y_));
+ rB = std::move(nextRB);
+
+#ifdef NFP_DEBUG
+ write_svg("next" + std::to_string(cnt) + ".svg", pA,rB);
+#endif
+
+ ++cnt;
+ if(referenceStart == rB.front() || (inside && bg::touches(rB.front(), nfp.front()))) {
+ startAvailable = false;
+ }
+ }
+ return LOOP;
+}
+
+void removeCoLinear(polygon_t::ring_type& r) {
+ assert(r.size() > 2);
+ psize_t nextI;
+ psize_t prevI = 0;
+ segment_t segment(r[r.size() - 2], r[0]);
+ polygon_t::ring_type newR;
+
+ for (psize_t i = 1; i < r.size() + 1; ++i) {
+ if (i >= r.size())
+ nextI = i % r.size() + 1;
+ else
+ nextI = i;
+
+ if (get_alignment(segment, r[nextI]) != ON) {
+ newR.push_back(r[prevI]);
+ }
+ segment = {segment.second, r[nextI]};
+ prevI = nextI;
+ }
+
+ r = newR;
+}
+
+void removeCoLinear(polygon_t& p) {
+ removeCoLinear(p.outer());
+ for (auto& r : p.inners())
+ removeCoLinear(r);
+}
+
+nfp_t generateNFP(polygon_t& pA, polygon_t& pB, const bool checkValidity = true) {
+ removeCoLinear(pA);
+ removeCoLinear(pB);
+
+ if(checkValidity) {
+ std::string reason;
+ if(!bg::is_valid(pA, reason))
+ throw std::runtime_error("Polygon A is invalid: " + reason);
+
+ if(!bg::is_valid(pB, reason))
+ throw std::runtime_error("Polygon B is invalid: " + reason);
+ }
+
+ nfp_t nfp;
+
+#ifdef NFP_DEBUG
+ write_svg("start.svg", {pA, pB});
+#endif
+
+ DEBUG_VAL(bg::wkt(pA))
+ DEBUG_VAL(bg::wkt(pB));
+
+ //prevent double vertex connections at start because we might come back the same way we go which would end the nfp prematurely
+ std::vector<psize_t> ptyaminI = find_minimum_y(pA);
+ std::vector<psize_t> ptybmaxI = find_maximum_y(pB);
+
+ point_t pAstart;
+ point_t pBstart;
+
+ if(ptyaminI.size() > 1 || ptybmaxI.size() > 1) {
+ //find right-most of A and left-most of B to prevent double connection at start
+ coord_t maxX = MIN_COORD;
+ psize_t iRightMost = 0;
+ for(psize_t& ia : ptyaminI) {
+ const point_t& candidateA = pA.outer()[ia];
+ if(larger(candidateA.x_, maxX)) {
+ maxX = candidateA.x_;
+ iRightMost = ia;
+ }
+ }
+
+ coord_t minX = MAX_COORD;
+ psize_t iLeftMost = 0;
+ for(psize_t& ib : ptybmaxI) {
+ const point_t& candidateB = pB.outer()[ib];
+ if(smaller(candidateB.x_, minX)) {
+ minX = candidateB.x_;
+ iLeftMost = ib;
+ }
+ }
+ pAstart = pA.outer()[iRightMost];
+ pBstart = pB.outer()[iLeftMost];
+ } else {
+ pAstart = pA.outer()[ptyaminI.front()];
+ pBstart = pB.outer()[ptybmaxI.front()];
+ }
+
+ nfp.push_back({});
+ point_t transB = {pAstart - pBstart};
+
+ if(slide(pA, pA.outer(), pB.outer(), nfp, transB, false) != LOOP) {
+ throw std::runtime_error("Unable to complete outer nfp loop");
+ }
+
+ DEBUG_VAL("##### outer #####");
+ point_t startTrans;
+ while(true) {
+ SearchStartResult res = searchStartTranslation(pA.outer(), pB.outer(), nfp, false, startTrans);
+ if(res == FOUND) {
+ nfp.push_back({});
+ DEBUG_VAL("##### interlock start #####")
+ polygon_t::ring_type rifsB;
+ boost::geometry::transform(pB.outer(), rifsB, trans::translate_transformer<coord_t, 2, 2>(startTrans.x_, startTrans.y_));
+ if(inNfp(rifsB.front(), nfp)) {
+ continue;
+ }
+ SlideResult sres = slide(pA, pA.outer(), pB.outer(), nfp, startTrans, true);
+ if(sres != LOOP) {
+ if(sres == NO_TRANSLATION) {
+ //no initial slide found -> jiggsaw
+ if(!inNfp(pB.outer().front(),nfp)) {
+ nfp.push_back({});
+ nfp.back().push_back(pB.outer().front());
+ }
+ }
+ }
+ DEBUG_VAL("##### interlock end #####");
+ } else if(res == FIT) {
+ point_t reference = pB.outer().front();
+ point_t translated;
+ trans::translate_transformer<coord_t, 2, 2> translate(startTrans.x_, startTrans.y_);
+ boost::geometry::transform(reference, translated, translate);
+ if(!inNfp(translated,nfp)) {
+ nfp.push_back({});
+ nfp.back().push_back(translated);
+ }
+ break;
+ } else
+ break;
+ }
+
+
+ for(auto& rA : pA.inners()) {
+ while(true) {
+ SearchStartResult res = searchStartTranslation(rA, pB.outer(), nfp, true, startTrans);
+ if(res == FOUND) {
+ nfp.push_back({});
+ DEBUG_VAL("##### hole start #####");
+ slide(pA, rA, pB.outer(), nfp, startTrans, true);
+ DEBUG_VAL("##### hole end #####");
+ } else if(res == FIT) {
+ point_t reference = pB.outer().front();
+ point_t translated;
+ trans::translate_transformer<coord_t, 2, 2> translate(startTrans.x_, startTrans.y_);
+ boost::geometry::transform(reference, translated, translate);
+ if(!inNfp(translated,nfp)) {
+ nfp.push_back({});
+ nfp.back().push_back(translated);
+ }
+ break;
+ } else
+ break;
+ }
+ }
+
+#ifdef NFP_DEBUG
+ write_svg("nfp.svg", {pA,pB}, nfp);
+#endif
+
+ return nfp;
+}
+}
+#endif
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
new file mode 100644
index 000000000..776dd5a1a
--- /dev/null
+++ b/xs/src/libnest2d/tools/svgtools.hpp
@@ -0,0 +1,122 @@
+#ifndef SVGTOOLS_HPP
+#define SVGTOOLS_HPP
+
+#include <iostream>
+#include <fstream>
+#include <string>
+
+#include <libnest2d/libnest2d.hpp>
+
+namespace libnest2d { namespace svg {
+
+template<class RawShape>
+class SVGWriter {
+ using Item = _Item<RawShape>;
+ using Coord = TCoord<TPoint<RawShape>>;
+ using Box = _Box<TPoint<RawShape>>;
+ using PackGroup = _PackGroup<RawShape>;
+
+public:
+
+ enum OrigoLocation {
+ TOPLEFT,
+ BOTTOMLEFT
+ };
+
+ struct Config {
+ OrigoLocation origo_location;
+ Coord mm_in_coord_units;
+ double width, height;
+ Config():
+ origo_location(BOTTOMLEFT), mm_in_coord_units(1000000),
+ width(500), height(500) {}
+
+ };
+
+private:
+ Config conf_;
+ std::vector<std::string> svg_layers_;
+ bool finished_ = false;
+public:
+
+ SVGWriter(const Config& conf = Config()):
+ conf_(conf) {}
+
+ void setSize(const Box& box) {
+ conf_.height = static_cast<double>(box.height()) /
+ conf_.mm_in_coord_units;
+ conf_.width = static_cast<double>(box.width()) /
+ conf_.mm_in_coord_units;
+ }
+
+ void writeItem(const Item& item) {
+ if(svg_layers_.empty()) addLayer();
+ auto tsh = item.transformedShape();
+ if(conf_.origo_location == BOTTOMLEFT) {
+ auto d = static_cast<Coord>(
+ std::round(conf_.height*conf_.mm_in_coord_units) );
+
+ auto& contour = shapelike::getContour(tsh);
+ for(auto& v : contour) setY(v, -getY(v) + d);
+
+ auto& holes = shapelike::holes(tsh);
+ for(auto& h : holes) for(auto& v : h) setY(v, -getY(v) + d);
+
+ }
+ currentLayer() += shapelike::serialize<Formats::SVG>(tsh,
+ 1.0/conf_.mm_in_coord_units) + "\n";
+ }
+
+ void writePackGroup(const PackGroup& result) {
+ for(auto r : result) {
+ addLayer();
+ for(Item& sh : r) {
+ writeItem(sh);
+ }
+ finishLayer();
+ }
+ }
+
+ void addLayer() {
+ svg_layers_.emplace_back(header());
+ finished_ = false;
+ }
+
+ void finishLayer() {
+ currentLayer() += "\n</svg>\n";
+ finished_ = true;
+ }
+
+ void save(const std::string& filepath) {
+ size_t lyrc = svg_layers_.size() > 1? 1 : 0;
+ size_t last = svg_layers_.size() > 1? svg_layers_.size() : 0;
+
+ for(auto& lyr : svg_layers_) {
+ std::fstream out(filepath + (lyrc > 0? std::to_string(lyrc) : "") +
+ ".svg", std::fstream::out);
+ if(out.is_open()) out << lyr;
+ if(lyrc == last && !finished_) out << "\n</svg>\n";
+ out.flush(); out.close(); lyrc++;
+ };
+ }
+
+private:
+
+ std::string& currentLayer() { return svg_layers_.back(); }
+
+ const std::string header() const {
+ std::string svg_header =
+R"raw(<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<svg height=")raw";
+ svg_header += std::to_string(conf_.height) + "\" width=\"" + std::to_string(conf_.width) + "\" ";
+ svg_header += R"raw(xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">)raw";
+ return svg_header;
+ }
+
+};
+
+}
+}
+
+#endif // SVGTOOLS_HPP
diff --git a/xs/src/libslic3r/BoundingBox.cpp b/xs/src/libslic3r/BoundingBox.cpp
index ceb968a50..4355cd61b 100644
--- a/xs/src/libslic3r/BoundingBox.cpp
+++ b/xs/src/libslic3r/BoundingBox.cpp
@@ -2,6 +2,8 @@
#include <algorithm>
#include <assert.h>
+#include <Eigen/Dense>
+
namespace Slic3r {
template BoundingBoxBase<Point>::BoundingBoxBase(const std::vector<Point> &points);
@@ -251,4 +253,41 @@ void BoundingBox::align_to_grid(const coord_t cell_size)
}
}
+BoundingBoxf3 BoundingBoxf3::transformed(const std::vector<float>& matrix) const
+{
+ Eigen::Matrix<float, 3, 8> vertices;
+
+ vertices(0, 0) = (float)min.x; vertices(1, 0) = (float)min.y; vertices(2, 0) = (float)min.z;
+ vertices(0, 1) = (float)max.x; vertices(1, 1) = (float)min.y; vertices(2, 1) = (float)min.z;
+ vertices(0, 2) = (float)max.x; vertices(1, 2) = (float)max.y; vertices(2, 2) = (float)min.z;
+ vertices(0, 3) = (float)min.x; vertices(1, 3) = (float)max.y; vertices(2, 3) = (float)min.z;
+ vertices(0, 4) = (float)min.x; vertices(1, 4) = (float)min.y; vertices(2, 4) = (float)max.z;
+ vertices(0, 5) = (float)max.x; vertices(1, 5) = (float)min.y; vertices(2, 5) = (float)max.z;
+ vertices(0, 6) = (float)max.x; vertices(1, 6) = (float)max.y; vertices(2, 6) = (float)max.z;
+ vertices(0, 7) = (float)min.x; vertices(1, 7) = (float)max.y; vertices(2, 7) = (float)max.z;
+
+ Eigen::Transform<float, 3, Eigen::Affine> m;
+ ::memcpy((void*)m.data(), (const void*)matrix.data(), 16 * sizeof(float));
+ Eigen::Matrix<float, 3, 8> transf_vertices = m * vertices.colwise().homogeneous();
+
+ float min_x = transf_vertices(0, 0);
+ float max_x = transf_vertices(0, 0);
+ float min_y = transf_vertices(1, 0);
+ float max_y = transf_vertices(1, 0);
+ float min_z = transf_vertices(2, 0);
+ float max_z = transf_vertices(2, 0);
+
+ for (int i = 1; i < 8; ++i)
+ {
+ min_x = std::min(min_x, transf_vertices(0, i));
+ max_x = std::max(max_x, transf_vertices(0, i));
+ min_y = std::min(min_y, transf_vertices(1, i));
+ max_y = std::max(max_y, transf_vertices(1, i));
+ min_z = std::min(min_z, transf_vertices(2, i));
+ max_z = std::max(max_z, transf_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));
+}
+
}
diff --git a/xs/src/libslic3r/BoundingBox.hpp b/xs/src/libslic3r/BoundingBox.hpp
index 5de94aa9c..5324dbe3b 100644
--- a/xs/src/libslic3r/BoundingBox.hpp
+++ b/xs/src/libslic3r/BoundingBox.hpp
@@ -103,6 +103,10 @@ public:
bool contains(const BoundingBox3Base<PointClass>& other) const {
return contains(other.min) && contains(other.max);
}
+
+ bool intersects(const BoundingBox3Base<PointClass>& other) const {
+ return (this->min.x < other.max.x) && (this->max.x > other.min.x) && (this->min.y < other.max.y) && (this->max.y > other.min.y) && (this->min.z < other.max.z) && (this->max.z > other.min.z);
+ }
};
class BoundingBox : public BoundingBoxBase<Point>
@@ -148,6 +152,8 @@ public:
BoundingBoxf3() : BoundingBox3Base<Pointf3>() {};
BoundingBoxf3(const Pointf3 &pmin, const Pointf3 &pmax) : BoundingBox3Base<Pointf3>(pmin, pmax) {};
BoundingBoxf3(const std::vector<Pointf3> &points) : BoundingBox3Base<Pointf3>(points) {};
+
+ BoundingBoxf3 transformed(const std::vector<float>& matrix) const;
};
template<typename VT>
diff --git a/xs/src/libslic3r/Config.cpp b/xs/src/libslic3r/Config.cpp
index 8c1349e08..5db093c5c 100644
--- a/xs/src/libslic3r/Config.cpp
+++ b/xs/src/libslic3r/Config.cpp
@@ -20,6 +20,7 @@
namespace Slic3r {
+// Escape \n, \r and backslash
std::string escape_string_cstyle(const std::string &str)
{
// Allocate a buffer twice the input string length,
@@ -28,9 +29,15 @@ std::string escape_string_cstyle(const std::string &str)
char *outptr = out.data();
for (size_t i = 0; i < str.size(); ++ i) {
char c = str[i];
- if (c == '\n' || c == '\r') {
+ if (c == '\r') {
+ (*outptr ++) = '\\';
+ (*outptr ++) = 'r';
+ } else if (c == '\n') {
(*outptr ++) = '\\';
(*outptr ++) = 'n';
+ } else if (c == '\\') {
+ (*outptr ++) = '\\';
+ (*outptr ++) = '\\';
} else
(*outptr ++) = c;
}
@@ -69,7 +76,10 @@ std::string escape_strings_cstyle(const std::vector<std::string> &strs)
if (c == '\\' || c == '"') {
(*outptr ++) = '\\';
(*outptr ++) = c;
- } else if (c == '\n' || c == '\r') {
+ } else if (c == '\r') {
+ (*outptr ++) = '\\';
+ (*outptr ++) = 'r';
+ } else if (c == '\n') {
(*outptr ++) = '\\';
(*outptr ++) = 'n';
} else
@@ -84,6 +94,7 @@ std::string escape_strings_cstyle(const std::vector<std::string> &strs)
return std::string(out.data(), outptr - out.data());
}
+// Unescape \n, \r and backslash
bool unescape_string_cstyle(const std::string &str, std::string &str_out)
{
std::vector<char> out(str.size(), 0);
@@ -94,8 +105,12 @@ bool unescape_string_cstyle(const std::string &str, std::string &str_out)
if (++ i == str.size())
return false;
c = str[i];
- if (c == 'n')
+ if (c == 'r')
+ (*outptr ++) = '\r';
+ else if (c == 'n')
(*outptr ++) = '\n';
+ else
+ (*outptr ++) = c;
} else
(*outptr ++) = c;
}
@@ -134,7 +149,9 @@ bool unescape_strings_cstyle(const std::string &str, std::vector<std::string> &o
if (++ i == str.size())
return false;
c = str[i];
- if (c == 'n')
+ if (c == 'r')
+ c = '\r';
+ else if (c == 'n')
c = '\n';
}
buf.push_back(c);
@@ -188,7 +205,10 @@ void ConfigBase::apply_only(const ConfigBase &other, const t_config_option_keys
throw UnknownOptionException(opt_key);
}
const ConfigOption *other_opt = other.option(opt_key);
- if (other_opt != nullptr)
+ if (other_opt == nullptr) {
+ // The key was not found in the source config, therefore it will not be initialized!
+// printf("Not found, therefore not initialized: %s\n", opt_key.c_str());
+ } else
my_opt->set(other_opt);
}
}
diff --git a/xs/src/libslic3r/Config.hpp b/xs/src/libslic3r/Config.hpp
index bde1eb651..2d995551c 100644
--- a/xs/src/libslic3r/Config.hpp
+++ b/xs/src/libslic3r/Config.hpp
@@ -291,6 +291,8 @@ public:
ConfigOptionFloats() : ConfigOptionVector<double>() {}
explicit ConfigOptionFloats(size_t n, double value) : ConfigOptionVector<double>(n, value) {}
explicit ConfigOptionFloats(std::initializer_list<double> il) : ConfigOptionVector<double>(std::move(il)) {}
+ explicit ConfigOptionFloats(const std::vector<double> &vec) : ConfigOptionVector<double>(vec) {}
+ explicit ConfigOptionFloats(std::vector<double> &&vec) : ConfigOptionVector<double>(std::move(vec)) {}
static ConfigOptionType static_type() { return coFloats; }
ConfigOptionType type() const override { return static_type(); }
@@ -1169,6 +1171,7 @@ public:
// Allow DynamicConfig to be instantiated on ints own without a definition.
// If the definition is not defined, the method requiring the definition will throw NoDefinitionException.
const ConfigDef* def() const override { return nullptr; };
+ bool has(const t_config_option_key &opt_key) const { return this->options.find(opt_key) != this->options.end(); }
template<class T> T* opt(const t_config_option_key &opt_key, bool create = false)
{ return dynamic_cast<T*>(this->option(opt_key, create)); }
template<class T> const T* opt(const t_config_option_key &opt_key) const
@@ -1212,7 +1215,7 @@ public:
bool opt_bool(const t_config_option_key &opt_key) const { return this->option<ConfigOptionBool>(opt_key)->value != 0; }
bool opt_bool(const t_config_option_key &opt_key, unsigned int idx) const { return this->option<ConfigOptionBools>(opt_key)->get_at(idx) != 0; }
-private:
+protected:
typedef std::map<t_config_option_key,ConfigOption*> t_options_map;
t_options_map options;
};
diff --git a/xs/src/libslic3r/EdgeGrid.cpp b/xs/src/libslic3r/EdgeGrid.cpp
index 733ff2ad7..9249d0ff1 100644
--- a/xs/src/libslic3r/EdgeGrid.cpp
+++ b/xs/src/libslic3r/EdgeGrid.cpp
@@ -9,7 +9,9 @@
#endif /* SLIC3R_GUI */
#include "libslic3r.h"
+#include "ClipperUtils.hpp"
#include "EdgeGrid.hpp"
+#include "SVG.hpp"
#if 0
// Enable debugging and assert in this file.
@@ -756,8 +758,8 @@ void EdgeGrid::Grid::calculate_sdf()
float search_radius = float(m_resolution<<1);
m_signed_distance_field.assign(nrows * ncols, search_radius);
// For each cell:
- for (size_t r = 0; r < m_rows; ++ r) {
- for (size_t c = 0; c < m_cols; ++ c) {
+ for (int r = 0; r < (int)m_rows; ++ r) {
+ for (int c = 0; c < (int)m_cols; ++ c) {
const Cell &cell = m_cells[r * m_cols + c];
// For each segment in the cell:
for (size_t i = cell.begin; i != cell.end; ++ i) {
@@ -842,6 +844,8 @@ void EdgeGrid::Grid::calculate_sdf()
#if 0
static int iRun = 0;
++ iRun;
+ if (wxImage::FindHandler(wxBITMAP_TYPE_PNG) == nullptr)
+ wxImage::AddHandler(new wxPNGHandler);
//#ifdef SLIC3R_GUI
{
wxImage img(ncols, nrows);
@@ -1225,8 +1229,10 @@ bool EdgeGrid::Grid::signed_distance(const Point &pt, coord_t search_radius, coo
return true;
}
-Polygons EdgeGrid::Grid::contours_simplified(coord_t offset) const
+Polygons EdgeGrid::Grid::contours_simplified(coord_t offset, bool fill_holes) const
{
+ assert(std::abs(2 * offset) < m_resolution);
+
typedef std::unordered_multimap<Point, int, PointHash> EndPointMapType;
// 0) Prepare a binary grid.
size_t cell_rows = m_rows + 2;
@@ -1237,7 +1243,7 @@ Polygons EdgeGrid::Grid::contours_simplified(coord_t offset) const
cell_inside[r * cell_cols + c] = cell_inside_or_crossing(r - 1, c - 1);
// Fill in empty cells, which have a left / right neighbor filled.
// Fill in empty cells, which have the top / bottom neighbor filled.
- {
+ if (fill_holes) {
std::vector<char> cell_inside2(cell_inside);
for (int r = 1; r + 1 < int(cell_rows); ++ r) {
for (int c = 1; c + 1 < int(cell_cols); ++ c) {
@@ -1354,9 +1360,101 @@ Polygons EdgeGrid::Grid::contours_simplified(coord_t offset) const
return out;
}
+inline int segments_could_intersect(
+ const Slic3r::Point &ip1, const Slic3r::Point &ip2,
+ const Slic3r::Point &jp1, const Slic3r::Point &jp2)
+{
+ Slic3r::Point iv = ip1.vector_to(ip2);
+ Slic3r::Point vij1 = ip1.vector_to(jp1);
+ Slic3r::Point vij2 = ip1.vector_to(jp2);
+ int64_t tij1 = int64_t(iv.x) * int64_t(vij1.y) - int64_t(iv.y) * int64_t(vij1.x); // cross(iv, vij1)
+ int64_t tij2 = int64_t(iv.x) * int64_t(vij2.y) - int64_t(iv.y) * int64_t(vij2.x); // cross(iv, vij2)
+ int sij1 = (tij1 > 0) ? 1 : ((tij1 < 0) ? -1 : 0); // signum
+ int sij2 = (tij2 > 0) ? 1 : ((tij2 < 0) ? -1 : 0);
+ return sij1 * sij2;
+}
+
+inline bool segments_intersect(
+ const Slic3r::Point &ip1, const Slic3r::Point &ip2,
+ const Slic3r::Point &jp1, const Slic3r::Point &jp2)
+{
+ return segments_could_intersect(ip1, ip2, jp1, jp2) <= 0 &&
+ segments_could_intersect(jp1, jp2, ip1, ip2) <= 0;
+}
+
+std::vector<std::pair<EdgeGrid::Grid::ContourEdge, EdgeGrid::Grid::ContourEdge>> EdgeGrid::Grid::intersecting_edges() const
+{
+ std::vector<std::pair<ContourEdge, ContourEdge>> out;
+ // For each cell:
+ for (int r = 0; r < (int)m_rows; ++ r) {
+ for (int c = 0; c < (int)m_cols; ++ c) {
+ const Cell &cell = m_cells[r * m_cols + c];
+ // For each pair of segments in the cell:
+ for (size_t i = cell.begin; i != cell.end; ++ i) {
+ const Slic3r::Points &ipts = *m_contours[m_cell_data[i].first];
+ size_t ipt = m_cell_data[i].second;
+ // End points of the line segment and their vector.
+ const Slic3r::Point &ip1 = ipts[ipt];
+ const Slic3r::Point &ip2 = ipts[(ipt + 1 == ipts.size()) ? 0 : ipt + 1];
+ for (size_t j = i + 1; j != cell.end; ++ j) {
+ const Slic3r::Points &jpts = *m_contours[m_cell_data[j].first];
+ size_t jpt = m_cell_data[j].second;
+ // End points of the line segment and their vector.
+ const Slic3r::Point &jp1 = jpts[jpt];
+ const Slic3r::Point &jp2 = jpts[(jpt + 1 == jpts.size()) ? 0 : jpt + 1];
+ if (&ipts == &jpts && (&ip1 == &jp2 || &jp1 == &ip2))
+ // Segments of the same contour share a common vertex.
+ continue;
+ if (segments_intersect(ip1, ip2, jp1, jp2)) {
+ // The two segments intersect. Add them to the output.
+ int jfirst = (&jpts < &ipts) || (&jpts == &ipts && jpt < ipt);
+ out.emplace_back(jfirst ?
+ std::make_pair(std::make_pair(&ipts, ipt), std::make_pair(&jpts, jpt)) :
+ std::make_pair(std::make_pair(&ipts, ipt), std::make_pair(&jpts, jpt)));
+ }
+ }
+ }
+ }
+ }
+ Slic3r::sort_remove_duplicates(out);
+ return out;
+}
+
+bool EdgeGrid::Grid::has_intersecting_edges() const
+{
+ // For each cell:
+ for (int r = 0; r < (int)m_rows; ++ r) {
+ for (int c = 0; c < (int)m_cols; ++ c) {
+ const Cell &cell = m_cells[r * m_cols + c];
+ // For each pair of segments in the cell:
+ for (size_t i = cell.begin; i != cell.end; ++ i) {
+ const Slic3r::Points &ipts = *m_contours[m_cell_data[i].first];
+ size_t ipt = m_cell_data[i].second;
+ // End points of the line segment and their vector.
+ const Slic3r::Point &ip1 = ipts[ipt];
+ const Slic3r::Point &ip2 = ipts[(ipt + 1 == ipts.size()) ? 0 : ipt + 1];
+ for (size_t j = i + 1; j != cell.end; ++ j) {
+ const Slic3r::Points &jpts = *m_contours[m_cell_data[j].first];
+ size_t jpt = m_cell_data[j].second;
+ // End points of the line segment and their vector.
+ const Slic3r::Point &jp1 = jpts[jpt];
+ const Slic3r::Point &jp2 = jpts[(jpt + 1 == jpts.size()) ? 0 : jpt + 1];
+ if (! (&ipts == &jpts && (&ip1 == &jp2 || &jp1 == &ip2)) &&
+ segments_intersect(ip1, ip2, jp1, jp2))
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
#if 0
void EdgeGrid::save_png(const EdgeGrid::Grid &grid, const BoundingBox &bbox, coord_t resolution, const char *path)
{
+ if (wxImage::FindHandler(wxBITMAP_TYPE_PNG) == nullptr)
+ wxImage::AddHandler(new wxPNGHandler);
+
unsigned int w = (bbox.max.x - bbox.min.x + resolution - 1) / resolution;
unsigned int h = (bbox.max.y - bbox.min.y + resolution - 1) / resolution;
wxImage img(w, h);
@@ -1448,4 +1546,59 @@ void EdgeGrid::save_png(const EdgeGrid::Grid &grid, const BoundingBox &bbox, coo
}
#endif /* SLIC3R_GUI */
+// Find all pairs of intersectiong edges from the set of polygons.
+std::vector<std::pair<EdgeGrid::Grid::ContourEdge, EdgeGrid::Grid::ContourEdge>> intersecting_edges(const Polygons &polygons)
+{
+ double len = 0;
+ size_t cnt = 0;
+ BoundingBox bbox;
+ for (const Polygon &poly : polygons) {
+ if (poly.points.size() < 2)
+ continue;
+ for (size_t i = 0; i < poly.points.size(); ++ i) {
+ bbox.merge(poly.points[i]);
+ size_t j = (i == 0) ? (poly.points.size() - 1) : i - 1;
+ len += poly.points[i].distance_to(poly.points[j]);
+ ++ cnt;
+ }
+ }
+ len /= double(cnt);
+ bbox.offset(20);
+ EdgeGrid::Grid grid;
+ grid.set_bbox(bbox);
+ grid.create(polygons, len);
+ return grid.intersecting_edges();
+}
+
+// Find all pairs of intersectiong edges from the set of polygons, highlight them in an SVG.
+void export_intersections_to_svg(const std::string &filename, const Polygons &polygons)
+{
+ std::vector<std::pair<EdgeGrid::Grid::ContourEdge, EdgeGrid::Grid::ContourEdge>> intersections = intersecting_edges(polygons);
+ BoundingBox bbox = get_extents(polygons);
+ SVG svg(filename.c_str(), bbox);
+ svg.draw(union_ex(polygons), "gray", 0.25f);
+ svg.draw_outline(polygons, "black");
+ std::set<const Points*> intersecting_contours;
+ for (const std::pair<EdgeGrid::Grid::ContourEdge, EdgeGrid::Grid::ContourEdge> &ie : intersections) {
+ intersecting_contours.insert(ie.first.first);
+ intersecting_contours.insert(ie.second.first);
+ }
+ // Highlight the contours with intersections.
+ coord_t line_width = coord_t(scale_(0.01));
+ for (const Points *ic : intersecting_contours) {
+ svg.draw_outline(Polygon(*ic), "green");
+ svg.draw_outline(Polygon(*ic), "black", line_width);
+ }
+ // Paint the intersections.
+ for (const std::pair<EdgeGrid::Grid::ContourEdge, EdgeGrid::Grid::ContourEdge> &intersecting_edges : intersections) {
+ auto edge = [](const EdgeGrid::Grid::ContourEdge &e) {
+ return Line(e.first->at(e.second),
+ e.first->at((e.second + 1 == e.first->size()) ? 0 : e.second + 1));
+ };
+ svg.draw(edge(intersecting_edges.first), "red", line_width);
+ svg.draw(edge(intersecting_edges.second), "red", line_width);
+ }
+ svg.Close();
+}
+
} // namespace Slic3r
diff --git a/xs/src/libslic3r/EdgeGrid.hpp b/xs/src/libslic3r/EdgeGrid.hpp
index 3eb741865..f99ab30c4 100644
--- a/xs/src/libslic3r/EdgeGrid.hpp
+++ b/xs/src/libslic3r/EdgeGrid.hpp
@@ -58,7 +58,12 @@ public:
const size_t cols() const { return m_cols; }
// For supports: Contours enclosing the rasterized edges.
- Polygons contours_simplified(coord_t offset) const;
+ Polygons contours_simplified(coord_t offset, bool fill_holes) const;
+
+ typedef std::pair<const Slic3r::Points*, size_t> ContourPoint;
+ typedef std::pair<const Slic3r::Points*, size_t> ContourEdge;
+ std::vector<std::pair<ContourEdge, ContourEdge>> intersecting_edges() const;
+ bool has_intersecting_edges() const;
protected:
struct Cell {
@@ -113,6 +118,13 @@ extern void save_png(const Grid &grid, const BoundingBox &bbox, coord_t resoluti
#endif /* SLIC3R_GUI */
} // namespace EdgeGrid
+
+// Find all pairs of intersectiong edges from the set of polygons.
+extern std::vector<std::pair<EdgeGrid::Grid::ContourEdge, EdgeGrid::Grid::ContourEdge>> intersecting_edges(const Polygons &polygons);
+
+// Find all pairs of intersectiong edges from the set of polygons, highlight them in an SVG.
+extern void export_intersections_to_svg(const std::string &filename, const Polygons &polygons);
+
} // namespace Slic3r
#endif /* slic3r_EdgeGrid_hpp_ */
diff --git a/xs/src/libslic3r/ExPolygonCollection.cpp b/xs/src/libslic3r/ExPolygonCollection.cpp
index e52498ecb..6933544b6 100644
--- a/xs/src/libslic3r/ExPolygonCollection.cpp
+++ b/xs/src/libslic3r/ExPolygonCollection.cpp
@@ -61,12 +61,11 @@ ExPolygonCollection::rotate(double angle, const Point &center)
}
template <class T>
-bool
-ExPolygonCollection::contains(const T &item) const
+bool ExPolygonCollection::contains(const T &item) const
{
- for (ExPolygons::const_iterator it = this->expolygons.begin(); it != this->expolygons.end(); ++it) {
- if (it->contains(item)) return true;
- }
+ for (const ExPolygon &poly : this->expolygons)
+ if (poly.contains(item))
+ return true;
return false;
}
template bool ExPolygonCollection::contains<Point>(const Point &item) const;
diff --git a/xs/src/libslic3r/ExtrusionEntity.hpp b/xs/src/libslic3r/ExtrusionEntity.hpp
index 16ef51c1f..504d264fe 100644
--- a/xs/src/libslic3r/ExtrusionEntity.hpp
+++ b/xs/src/libslic3r/ExtrusionEntity.hpp
@@ -91,7 +91,10 @@ public:
// Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
virtual double min_mm3_per_mm() const = 0;
virtual Polyline as_polyline() const = 0;
+ virtual void collect_polylines(Polylines &dst) const = 0;
+ virtual Polylines as_polylines() const { Polylines dst; this->collect_polylines(dst); return dst; }
virtual double length() const = 0;
+ virtual double total_volume() const = 0;
};
typedef std::vector<ExtrusionEntity*> ExtrusionEntitiesPtr;
@@ -122,8 +125,11 @@ public:
ExtrusionPath* clone() const { return new ExtrusionPath (*this); }
void reverse() { this->polyline.reverse(); }
- Point first_point() const { return this->polyline.points.front(); }
- Point last_point() const { return this->polyline.points.back(); }
+ Point first_point() const override { return this->polyline.points.front(); }
+ Point last_point() const override { return this->polyline.points.back(); }
+ size_t size() const { return this->polyline.size(); }
+ bool empty() const { return this->polyline.empty(); }
+ bool is_closed() const { return ! this->empty() && this->polyline.points.front() == this->polyline.points.back(); }
// Produce a list of extrusion paths into retval by clipping this path by ExPolygonCollection.
// Currently not used.
void intersect_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const;
@@ -132,8 +138,8 @@ public:
void subtract_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const;
void clip_end(double distance);
void simplify(double tolerance);
- virtual double length() const;
- virtual ExtrusionRole role() const { return m_role; }
+ double length() const override;
+ ExtrusionRole role() const override { return m_role; }
// Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
// Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const;
@@ -148,6 +154,8 @@ public:
// Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
double min_mm3_per_mm() const { return this->mm3_per_mm; }
Polyline as_polyline() const { return this->polyline; }
+ void collect_polylines(Polylines &dst) const override { if (! this->polyline.empty()) dst.emplace_back(this->polyline); }
+ double total_volume() const override { return mm3_per_mm * unscale(length()); }
private:
void _inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const;
@@ -176,10 +184,10 @@ public:
bool can_reverse() const { return true; }
ExtrusionMultiPath* clone() const { return new ExtrusionMultiPath(*this); }
void reverse();
- Point first_point() const { return this->paths.front().polyline.points.front(); }
- Point last_point() const { return this->paths.back().polyline.points.back(); }
- virtual double length() const;
- virtual ExtrusionRole role() const { return this->paths.empty() ? erNone : this->paths.front().role(); }
+ Point first_point() const override { return this->paths.front().polyline.points.front(); }
+ Point last_point() const override { return this->paths.back().polyline.points.back(); }
+ double length() const override;
+ ExtrusionRole role() const override { return this->paths.empty() ? erNone : this->paths.front().role(); }
// Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
// Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const;
@@ -194,6 +202,8 @@ public:
// Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
double min_mm3_per_mm() const;
Polyline as_polyline() const;
+ void collect_polylines(Polylines &dst) const override { Polyline pl = this->as_polyline(); if (! pl.empty()) dst.emplace_back(std::move(pl)); }
+ double total_volume() const override { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; }
};
// Single continuous extrusion loop, possibly with varying extrusion thickness, extrusion height or bridging / non bridging.
@@ -215,18 +225,18 @@ public:
bool make_clockwise();
bool make_counter_clockwise();
void reverse();
- Point first_point() const { return this->paths.front().polyline.points.front(); }
- Point last_point() const { assert(first_point() == this->paths.back().polyline.points.back()); return first_point(); }
+ Point first_point() const override { return this->paths.front().polyline.points.front(); }
+ Point last_point() const override { assert(first_point() == this->paths.back().polyline.points.back()); return first_point(); }
Polygon polygon() const;
- virtual double length() const;
+ double length() const override;
bool split_at_vertex(const Point &point);
void split_at(const Point &point, bool prefer_non_overhang);
void clip_end(double distance, ExtrusionPaths* paths) const;
// Test, whether the point is extruded by a bridging flow.
// This used to be used to avoid placing seams on overhangs, but now the EdgeGrid is used instead.
bool has_overhang_point(const Point &point) const;
- virtual ExtrusionRole role() const { return this->paths.empty() ? erNone : this->paths.front().role(); }
- ExtrusionLoopRole loop_role() const { return m_loop_role; }
+ ExtrusionRole role() const override { return this->paths.empty() ? erNone : this->paths.front().role(); }
+ ExtrusionLoopRole loop_role() const { return m_loop_role; }
// Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
// Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const;
@@ -241,6 +251,8 @@ public:
// Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
double min_mm3_per_mm() const;
Polyline as_polyline() const { return this->polygon().split_at_first_point(); }
+ void collect_polylines(Polylines &dst) const override { Polyline pl = this->as_polyline(); if (! pl.empty()) dst.emplace_back(std::move(pl)); }
+ double total_volume() const override { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; }
private:
ExtrusionLoopRole m_loop_role;
diff --git a/xs/src/libslic3r/ExtrusionEntityCollection.cpp b/xs/src/libslic3r/ExtrusionEntityCollection.cpp
index 4513139e2..7a086bcbf 100644
--- a/xs/src/libslic3r/ExtrusionEntityCollection.cpp
+++ b/xs/src/libslic3r/ExtrusionEntityCollection.cpp
@@ -125,6 +125,7 @@ void ExtrusionEntityCollection::chained_path_from(Point start_near, ExtrusionEnt
continue;
}
}
+
ExtrusionEntity* entity = (*it)->clone();
my_paths.push_back(entity);
if (orig_indices != NULL) indices_map[entity] = it - this->entities.begin();
diff --git a/xs/src/libslic3r/ExtrusionEntityCollection.hpp b/xs/src/libslic3r/ExtrusionEntityCollection.hpp
index 03bd2ba97..81582a94d 100644
--- a/xs/src/libslic3r/ExtrusionEntityCollection.hpp
+++ b/xs/src/libslic3r/ExtrusionEntityCollection.hpp
@@ -24,7 +24,7 @@ public:
explicit operator ExtrusionPaths() const;
bool is_collection() const { return true; };
- virtual ExtrusionRole role() const {
+ ExtrusionRole role() const override {
ExtrusionRole out = erNone;
for (const ExtrusionEntity *ee : entities) {
ExtrusionRole er = ee->role();
@@ -66,11 +66,11 @@ public:
Point last_point() const { return this->entities.back()->last_point(); }
// Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
// Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
- virtual void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const;
+ void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const override;
// Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion spacing.
// Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
// Useful to calculate area of an infill, which has been really filled in by a 100% rectilinear infill.
- virtual void polygons_covered_by_spacing(Polygons &out, const float scaled_epsilon) const;
+ void polygons_covered_by_spacing(Polygons &out, const float scaled_epsilon) const override;
Polygons polygons_covered_by_width(const float scaled_epsilon = 0.f) const
{ Polygons out; this->polygons_covered_by_width(out, scaled_epsilon); return out; }
Polygons polygons_covered_by_spacing(const float scaled_epsilon = 0.f) const
@@ -79,13 +79,20 @@ public:
void flatten(ExtrusionEntityCollection* retval) const;
ExtrusionEntityCollection flatten() const;
double min_mm3_per_mm() const;
+ double total_volume() const override { double volume=0.; for (const auto& ent : entities) volume+=ent->total_volume(); return volume; }
// Following methods shall never be called on an ExtrusionEntityCollection.
Polyline as_polyline() const {
CONFESS("Calling as_polyline() on a ExtrusionEntityCollection");
return Polyline();
};
- virtual double length() const {
+
+ void collect_polylines(Polylines &dst) const override {
+ for (ExtrusionEntity* extrusion_entity : this->entities)
+ extrusion_entity->collect_polylines(dst);
+ }
+
+ double length() const override {
CONFESS("Calling length() on a ExtrusionEntityCollection");
return 0.;
}
diff --git a/xs/src/libslic3r/Fill/FillGyroid.cpp b/xs/src/libslic3r/Fill/FillGyroid.cpp
index 89d5d231e..46d6382f7 100644
--- a/xs/src/libslic3r/Fill/FillGyroid.cpp
+++ b/xs/src/libslic3r/Fill/FillGyroid.cpp
@@ -131,7 +131,7 @@ void FillGyroid::_fill_surface_single(
// no rotation is supported for this infill pattern (yet)
BoundingBox bb = expolygon.contour.bounding_box();
// Density adjusted to have a good %of weight.
- double density_adjusted = std::max(0., params.density * 2.);
+ double density_adjusted = std::max(0., params.density * 2.44);
// Distance between the gyroid waves in scaled coordinates.
coord_t distance = coord_t(scale_(this->spacing) / density_adjusted);
diff --git a/xs/src/libslic3r/Fill/FillGyroid.hpp b/xs/src/libslic3r/Fill/FillGyroid.hpp
index 17924b5ab..9c3cef940 100644
--- a/xs/src/libslic3r/Fill/FillGyroid.hpp
+++ b/xs/src/libslic3r/Fill/FillGyroid.hpp
@@ -14,7 +14,7 @@ public:
virtual Fill* clone() const { return new FillGyroid(*this); }
// require bridge flow since most of this pattern hangs in air
- virtual bool use_bridge_flow() const { return true; }
+ virtual bool use_bridge_flow() const { return false; }
protected:
virtual void _fill_surface_single(
diff --git a/xs/src/libslic3r/Fill/FillHoneycomb.cpp b/xs/src/libslic3r/Fill/FillHoneycomb.cpp
index aa0e0f6b0..aa52856ae 100644
--- a/xs/src/libslic3r/Fill/FillHoneycomb.cpp
+++ b/xs/src/libslic3r/Fill/FillHoneycomb.cpp
@@ -86,8 +86,8 @@ void FillHoneycomb::_fill_surface_single(
Polylines paths;
{
Polylines p;
- for (Polygons::iterator it = polygons.begin(); it != polygons.end(); ++ it)
- p.push_back((Polyline)(*it));
+ for (Polygon &poly : polygons)
+ p.emplace_back(poly.points);
paths = intersection_pl(p, to_polygons(expolygon));
}
diff --git a/xs/src/libslic3r/Fill/FillRectilinear3.cpp b/xs/src/libslic3r/Fill/FillRectilinear3.cpp
index 1e7e3a1cd..cccea3030 100644
--- a/xs/src/libslic3r/Fill/FillRectilinear3.cpp
+++ b/xs/src/libslic3r/Fill/FillRectilinear3.cpp
@@ -470,9 +470,9 @@ static bool prepare_infill_hatching_segments(
int ir = std::min<int>(int(out.segs.size()) - 1, (r - x0) / line_spacing);
// The previous tests were done with floating point arithmetics over an epsilon-extended interval.
// Now do the same tests with exact arithmetics over the exact interval.
- while (il <= ir && Int128::orient(out.segs[il].pos, out.segs[il].pos + out.direction, *pl) < 0)
+ while (il <= ir && int128::orient(out.segs[il].pos, out.segs[il].pos + out.direction, *pl) < 0)
++ il;
- while (il <= ir && Int128::orient(out.segs[ir].pos, out.segs[ir].pos + out.direction, *pr) > 0)
+ while (il <= ir && int128::orient(out.segs[ir].pos, out.segs[ir].pos + out.direction, *pr) > 0)
-- ir;
// Here it is ensured, that
// 1) out.seg is not parallel to (pl, pr)
@@ -489,8 +489,8 @@ static bool prepare_infill_hatching_segments(
is.iSegment = iSegment;
// Test whether the calculated intersection point falls into the bounding box of the input segment.
// +-1 to take rounding into account.
- assert(Int128::orient(out.segs[i].pos, out.segs[i].pos + out.direction, *pl) >= 0);
- assert(Int128::orient(out.segs[i].pos, out.segs[i].pos + out.direction, *pr) <= 0);
+ assert(int128::orient(out.segs[i].pos, out.segs[i].pos + out.direction, *pl) >= 0);
+ assert(int128::orient(out.segs[i].pos, out.segs[i].pos + out.direction, *pr) <= 0);
assert(is.pos().x + 1 >= std::min(pl->x, pr->x));
assert(is.pos().y + 1 >= std::min(pl->y, pr->y));
assert(is.pos().x <= std::max(pl->x, pr->x) + 1);
@@ -527,7 +527,7 @@ static bool prepare_infill_hatching_segments(
const Points &contour = poly_with_offset.contour(iContour).points;
size_t iSegment = sil.intersections[i].iSegment;
size_t iPrev = ((iSegment == 0) ? contour.size() : iSegment) - 1;
- int dir = Int128::cross(contour[iSegment] - contour[iPrev], sil.dir);
+ int dir = int128::cross(contour[iSegment] - contour[iPrev], sil.dir);
bool low = dir > 0;
sil.intersections[i].type = poly_with_offset.is_contour_outer(iContour) ?
(low ? SegmentIntersection::OUTER_LOW : SegmentIntersection::OUTER_HIGH) :
diff --git a/xs/src/libslic3r/Flow.cpp b/xs/src/libslic3r/Flow.cpp
index b60e26dcc..e92674a17 100644
--- a/xs/src/libslic3r/Flow.cpp
+++ b/xs/src/libslic3r/Flow.cpp
@@ -115,7 +115,8 @@ Flow support_material_flow(const PrintObject *object, float layer_height)
// if object->config.support_material_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component.
float(object->print()->config.nozzle_diameter.get_at(object->config.support_material_extruder-1)),
(layer_height > 0.f) ? layer_height : float(object->config.layer_height.value),
- false);
+ // bridge_flow_ratio
+ 0.f);
}
Flow support_material_1st_layer_flow(const PrintObject *object, float layer_height)
@@ -127,7 +128,8 @@ Flow support_material_1st_layer_flow(const PrintObject *object, float layer_heig
(width.value > 0) ? width : object->config.extrusion_width,
float(object->print()->config.nozzle_diameter.get_at(object->config.support_material_extruder-1)),
(layer_height > 0.f) ? layer_height : float(object->config.first_layer_height.get_abs_value(object->config.layer_height.value)),
- false);
+ // bridge_flow_ratio
+ 0.f);
}
Flow support_material_interface_flow(const PrintObject *object, float layer_height)
@@ -139,7 +141,8 @@ Flow support_material_interface_flow(const PrintObject *object, float layer_heig
// if object->config.support_material_interface_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component.
float(object->print()->config.nozzle_diameter.get_at(object->config.support_material_interface_extruder-1)),
(layer_height > 0.f) ? layer_height : float(object->config.layer_height.value),
- false);
+ // bridge_flow_ratio
+ 0.f);
}
}
diff --git a/xs/src/libslic3r/Format/3mf.cpp b/xs/src/libslic3r/Format/3mf.cpp
index 2c32db1a6..5f6a48cf8 100644
--- a/xs/src/libslic3r/Format/3mf.cpp
+++ b/xs/src/libslic3r/Format/3mf.cpp
@@ -71,6 +71,7 @@ const char* VOLUME_TYPE = "volume";
const char* NAME_KEY = "name";
const char* MODIFIER_KEY = "modifier";
+const char* VOLUME_TYPE_KEY = "volume_type";
const unsigned int VALID_OBJECT_TYPES_COUNT = 1;
const char* VALID_OBJECT_TYPES[] =
@@ -1491,6 +1492,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)
@@ -1498,7 +1500,9 @@ namespace Slic3r {
if (metadata.key == NAME_KEY)
volume->name = metadata.value;
else if ((metadata.key == MODIFIER_KEY) && (metadata.value == "1"))
- volume->modifier = true;
+ volume->set_type(ModelVolume::PARAMETER_MODIFIER);
+ else if (metadata.key == VOLUME_TYPE_KEY)
+ volume->set_type(ModelVolume::type_from_string(metadata.value));
else
volume->config.set_deserialize(metadata.key, metadata.value);
}
@@ -1989,7 +1993,7 @@ namespace Slic3r {
// stores object's name
if (!obj->name.empty())
- stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << OBJECT_TYPE << "\" " << KEY_ATTR << "=\"name\" " << VALUE_ATTR << "=\"" << obj->name << "\"/>\n";
+ stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << OBJECT_TYPE << "\" " << KEY_ATTR << "=\"name\" " << VALUE_ATTR << "=\"" << xml_escape(obj->name) << "\"/>\n";
// stores object's config data
for (const std::string& key : obj->config.keys())
@@ -2012,11 +2016,14 @@ namespace Slic3r {
// stores volume's name
if (!volume->name.empty())
- stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << NAME_KEY << "\" " << VALUE_ATTR << "=\"" << volume->name << "\"/>\n";
+ stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << NAME_KEY << "\" " << VALUE_ATTR << "=\"" << xml_escape(volume->name) << "\"/>\n";
- // stores volume's modifier field
- if (volume->modifier)
+ // stores volume's modifier field (legacy, to support old slicers)
+ if (volume->is_modifier())
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << MODIFIER_KEY << "\" " << VALUE_ATTR << "=\"1\"/>\n";
+ // stores volume's type (overrides the modifier field above)
+ stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << VOLUME_TYPE_KEY << "\" " <<
+ VALUE_ATTR << "=\"" << ModelVolume::type_to_string(volume->type()) << "\"/>\n";
// stores volume's config data
for (const std::string& key : volume->config.keys())
diff --git a/xs/src/libslic3r/Format/AMF.cpp b/xs/src/libslic3r/Format/AMF.cpp
index 263363756..5aa922f62 100644
--- a/xs/src/libslic3r/Format/AMF.cpp
+++ b/xs/src/libslic3r/Format/AMF.cpp
@@ -8,14 +8,13 @@
#include "../libslic3r.h"
#include "../Model.hpp"
#include "../GCode.hpp"
+#include "../Utils.hpp"
#include "../slic3r/GUI/PresetBundle.hpp"
#include "AMF.hpp"
#include <boost/filesystem/operations.hpp>
#include <boost/algorithm/string.hpp>
-//############################################################################################################################################
#include <boost/nowide/fstream.hpp>
-//############################################################################################################################################
#include <miniz/miniz_zip.h>
#if 0
@@ -407,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;
@@ -458,9 +458,14 @@ void AMFParserContext::endElement(const char * /* name */)
p = end + 1;
}
m_object->layer_height_profile_valid = true;
- } else if (m_path.size() == 5 && m_path[3] == NODE_TYPE_VOLUME && m_volume && strcmp(opt_key, "modifier") == 0) {
- // Is this volume a modifier volume?
- m_volume->modifier = atoi(m_value[1].c_str()) == 1;
+ } else if (m_path.size() == 5 && m_path[3] == NODE_TYPE_VOLUME && m_volume) {
+ if (strcmp(opt_key, "modifier") == 0) {
+ // Is this volume a modifier volume?
+ // "modifier" flag comes first in the XML file, so it may be later overwritten by the "type" flag.
+ m_volume->set_type((atoi(m_value[1].c_str()) == 1) ? ModelVolume::PARAMETER_MODIFIER : ModelVolume::MODEL_PART);
+ } else if (strcmp(opt_key, "volume_type") == 0) {
+ m_volume->set_type(ModelVolume::type_from_string(m_value[1]));
+ }
}
} else if (m_path.size() == 3) {
if (m_path[1] == NODE_TYPE_MATERIAL) {
@@ -688,33 +693,6 @@ bool load_amf(const char *path, PresetBundle* bundle, Model *model)
return false;
}
-std::string xml_escape(std::string text)
-{
- std::string::size_type pos = 0;
- for (;;)
- {
- pos = text.find_first_of("\"\'&<>", pos);
- if (pos == std::string::npos)
- break;
-
- std::string replacement;
- switch (text[pos])
- {
- case '\"': replacement = "&quot;"; break;
- case '\'': replacement = "&apos;"; break;
- case '&': replacement = "&amp;"; break;
- case '<': replacement = "&lt;"; break;
- case '>': replacement = "&gt;"; break;
- default: break;
- }
-
- text.replace(pos, 1, replacement);
- pos += replacement.size();
- }
-
- return text;
-}
-
bool store_amf(const char *path, Model *model, Print* print, bool export_print_config)
{
if ((path == nullptr) || (model == nullptr) || (print == nullptr))
@@ -763,7 +741,7 @@ bool store_amf(const char *path, Model *model, Print* print, bool export_print_c
for (const std::string &key : object->config.keys())
stream << " <metadata type=\"slic3r." << key << "\">" << object->config.serialize(key) << "</metadata>\n";
if (!object->name.empty())
- stream << " <metadata type=\"name\">" << object->name << "</metadata>\n";
+ stream << " <metadata type=\"name\">" << xml_escape(object->name) << "</metadata>\n";
std::vector<double> layer_height_profile = object->layer_height_profile_valid ? object->layer_height_profile : std::vector<double>();
if (layer_height_profile.size() >= 4 && (layer_height_profile.size() % 2) == 0) {
// Store the layer height profile as a single semicolon separated list.
@@ -807,9 +785,10 @@ bool store_amf(const char *path, Model *model, Print* print, bool export_print_c
for (const std::string &key : volume->config.keys())
stream << " <metadata type=\"slic3r." << key << "\">" << volume->config.serialize(key) << "</metadata>\n";
if (!volume->name.empty())
- stream << " <metadata type=\"name\">" << volume->name << "</metadata>\n";
- if (volume->modifier)
+ stream << " <metadata type=\"name\">" << xml_escape(volume->name) << "</metadata>\n";
+ if (volume->is_modifier())
stream << " <metadata type=\"slic3r.modifier\">1</metadata>\n";
+ stream << " <metadata type=\"slic3r.volume_type\">" << ModelVolume::type_to_string(volume->type()) << "</metadata>\n";
for (int i = 0; i < volume->mesh.stl.stats.number_of_facets; ++i) {
stream << " <triangle>\n";
for (int j = 0; j < 3; ++j)
diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp
index b581b3e76..2254c2154 100644
--- a/xs/src/libslic3r/GCode.cpp
+++ b/xs/src/libslic3r/GCode.cpp
@@ -167,6 +167,18 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T
{
std::string gcode;
+ // Toolchangeresult.gcode assumes the wipe tower corner is at the origin
+ // We want to rotate and shift all extrusions (gcode postprocessing) and starting and ending position
+ float alpha = m_wipe_tower_rotation/180.f * M_PI;
+ WipeTower::xy start_pos = tcr.start_pos;
+ WipeTower::xy end_pos = tcr.end_pos;
+ start_pos.rotate(alpha);
+ start_pos.translate(m_wipe_tower_pos);
+ end_pos.rotate(alpha);
+ end_pos.translate(m_wipe_tower_pos);
+ std::string tcr_rotated_gcode = rotate_wipe_tower_moves(tcr.gcode, tcr.start_pos, m_wipe_tower_pos, alpha);
+
+
// Disable linear advance for the wipe tower operations.
gcode += "M900 K0\n";
// Move over the wipe tower.
@@ -174,14 +186,14 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T
gcode += gcodegen.retract(true);
gcodegen.m_avoid_crossing_perimeters.use_external_mp_once = true;
gcode += gcodegen.travel_to(
- wipe_tower_point_to_object_point(gcodegen, tcr.start_pos),
+ wipe_tower_point_to_object_point(gcodegen, start_pos),
erMixed,
"Travel to a Wipe Tower");
gcode += gcodegen.unretract();
// Let the tool change be executed by the wipe tower class.
// Inform the G-code writer about the changes done behind its back.
- gcode += tcr.gcode;
+ gcode += tcr_rotated_gcode;
// Let the m_writer know the current extruder_id, but ignore the generated G-code.
if (new_extruder_id >= 0 && gcodegen.writer().need_toolchange(new_extruder_id))
gcodegen.writer().toolchange(new_extruder_id);
@@ -195,18 +207,18 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T
check_add_eol(gcode);
}
// A phony move to the end position at the wipe tower.
- gcodegen.writer().travel_to_xy(Pointf(tcr.end_pos.x, tcr.end_pos.y));
- gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, tcr.end_pos));
+ gcodegen.writer().travel_to_xy(Pointf(end_pos.x, end_pos.y));
+ gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, end_pos));
// Prepare a future wipe.
gcodegen.m_wipe.path.points.clear();
if (new_extruder_id >= 0) {
// Start the wipe at the current position.
- gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, tcr.end_pos));
+ gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, end_pos));
// Wipe end point: Wipe direction away from the closer tower edge to the further tower edge.
gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen,
- WipeTower::xy((std::abs(m_left - tcr.end_pos.x) < std::abs(m_right - tcr.end_pos.x)) ? m_right : m_left,
- tcr.end_pos.y)));
+ WipeTower::xy((std::abs(m_left - end_pos.x) < std::abs(m_right - end_pos.x)) ? m_right : m_left,
+ end_pos.y)));
}
// Let the planner know we are traveling between objects.
@@ -214,6 +226,56 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T
return gcode;
}
+// This function postprocesses gcode_original, rotates and moves all G1 extrusions and returns resulting gcode
+// Starting position has to be supplied explicitely (otherwise it would fail in case first G1 command only contained one coordinate)
+std::string WipeTowerIntegration::rotate_wipe_tower_moves(const std::string& gcode_original, const WipeTower::xy& start_pos, const WipeTower::xy& translation, float angle) const
+{
+ std::istringstream gcode_str(gcode_original);
+ std::string gcode_out;
+ std::string line;
+ WipeTower::xy pos = start_pos;
+ WipeTower::xy transformed_pos;
+ WipeTower::xy old_pos(-1000.1f, -1000.1f);
+
+ while (gcode_str) {
+ std::getline(gcode_str, line); // we read the gcode line by line
+ if (line.find("G1 ") == 0) {
+ std::ostringstream line_out;
+ std::istringstream line_str(line);
+ line_str >> std::noskipws; // don't skip whitespace
+ char ch = 0;
+ while (line_str >> ch) {
+ if (ch == 'X')
+ line_str >> pos.x;
+ else
+ if (ch == 'Y')
+ line_str >> pos.y;
+ else
+ line_out << ch;
+ }
+
+ transformed_pos = pos;
+ transformed_pos.rotate(angle);
+ transformed_pos.translate(translation);
+
+ if (transformed_pos != old_pos) {
+ line = line_out.str();
+ char buf[2048] = "G1";
+ if (transformed_pos.x != old_pos.x)
+ sprintf(buf + strlen(buf), " X%.3f", transformed_pos.x);
+ if (transformed_pos.y != old_pos.y)
+ sprintf(buf + strlen(buf), " Y%.3f", transformed_pos.y);
+
+ line.replace(line.find("G1 "), 3, buf);
+ old_pos = transformed_pos;
+ }
+ }
+ gcode_out += line + "\n";
+ }
+ return gcode_out;
+}
+
+
std::string WipeTowerIntegration::prime(GCode &gcodegen)
{
assert(m_layer_idx == 0);
@@ -309,10 +371,12 @@ std::vector<std::pair<coordf_t, std::vector<GCode::LayerToPrint>>> GCode::collec
size_t object_idx;
size_t layer_idx;
};
- std::vector<std::vector<LayerToPrint>> per_object(print.objects.size(), std::vector<LayerToPrint>());
+
+ PrintObjectPtrs printable_objects = print.get_printable_objects();
+ std::vector<std::vector<LayerToPrint>> per_object(printable_objects.size(), std::vector<LayerToPrint>());
std::vector<OrderingItem> ordering;
- for (size_t i = 0; i < print.objects.size(); ++ i) {
- per_object[i] = collect_layers_to_print(*print.objects[i]);
+ for (size_t i = 0; i < printable_objects.size(); ++i) {
+ per_object[i] = collect_layers_to_print(*printable_objects[i]);
OrderingItem ordering_item;
ordering_item.object_idx = i;
ordering.reserve(ordering.size() + per_object[i].size());
@@ -337,8 +401,8 @@ std::vector<std::pair<coordf_t, std::vector<GCode::LayerToPrint>>> GCode::collec
std::pair<coordf_t, std::vector<LayerToPrint>> merged;
// Assign an average print_z to the set of layers with nearly equal print_z.
merged.first = 0.5 * (ordering[i].print_z + ordering[j-1].print_z);
- merged.second.assign(print.objects.size(), LayerToPrint());
- for (; i < j; ++ i) {
+ merged.second.assign(printable_objects.size(), LayerToPrint());
+ for (; i < j; ++i) {
const OrderingItem &oi = ordering[i];
assert(merged.second[oi.object_idx].layer() == nullptr);
merged.second[oi.object_idx] = std::move(per_object[oi.object_idx][oi.layer_idx]);
@@ -374,6 +438,14 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_
throw std::runtime_error(std::string("G-code export to ") + path + " failed\nIs the disk full?\n");
}
fclose(file);
+
+ if (print->config.remaining_times.value)
+ {
+ m_normal_time_estimator.post_process_remaining_times(path_tmp, 60.0f);
+ if (m_silent_time_estimator_enabled)
+ m_silent_time_estimator.post_process_remaining_times(path_tmp, 60.0f);
+ }
+
if (! this->m_placeholder_parser_failed_templates.empty()) {
// G-code export proceeded, but some of the PlaceholderParser substitutions failed.
std::string msg = std::string("G-code export to ") + path + " failed due to invalid custom G-code sections:\n\n";
@@ -403,9 +475,69 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
{
PROFILE_FUNC();
- // resets time estimator
- m_time_estimator.reset();
- m_time_estimator.set_dialect(print.config.gcode_flavor);
+ // resets time estimators
+ m_normal_time_estimator.reset();
+ m_normal_time_estimator.set_dialect(print.config.gcode_flavor);
+ m_silent_time_estimator_enabled = (print.config.gcode_flavor == gcfMarlin) && print.config.silent_mode;
+
+ // Until we have a UI support for the other firmwares than the Marlin, use the hardcoded default values
+ // and let the user to enter the G-code limits into the start G-code.
+ // If the following block is enabled for other firmwares than the Marlin, then the function
+ // this->print_machine_envelope(file, print);
+ // shall be adjusted as well to produce a G-code block compatible with the particular firmware flavor.
+ if (print.config.gcode_flavor.value == gcfMarlin) {
+ m_normal_time_estimator.set_max_acceleration(print.config.machine_max_acceleration_extruding.values[0]);
+ m_normal_time_estimator.set_retract_acceleration(print.config.machine_max_acceleration_retracting.values[0]);
+ m_normal_time_estimator.set_minimum_feedrate(print.config.machine_min_extruding_rate.values[0]);
+ m_normal_time_estimator.set_minimum_travel_feedrate(print.config.machine_min_travel_rate.values[0]);
+ m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, print.config.machine_max_acceleration_x.values[0]);
+ m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, print.config.machine_max_acceleration_y.values[0]);
+ m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, print.config.machine_max_acceleration_z.values[0]);
+ m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, print.config.machine_max_acceleration_e.values[0]);
+ m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, print.config.machine_max_feedrate_x.values[0]);
+ m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, print.config.machine_max_feedrate_y.values[0]);
+ m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, print.config.machine_max_feedrate_z.values[0]);
+ m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, print.config.machine_max_feedrate_e.values[0]);
+ m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, print.config.machine_max_jerk_x.values[0]);
+ m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, print.config.machine_max_jerk_y.values[0]);
+ m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, print.config.machine_max_jerk_z.values[0]);
+ m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, print.config.machine_max_jerk_e.values[0]);
+
+ if (m_silent_time_estimator_enabled)
+ {
+ m_silent_time_estimator.reset();
+ m_silent_time_estimator.set_dialect(print.config.gcode_flavor);
+ m_silent_time_estimator.set_max_acceleration(print.config.machine_max_acceleration_extruding.values[1]);
+ m_silent_time_estimator.set_retract_acceleration(print.config.machine_max_acceleration_retracting.values[1]);
+ m_silent_time_estimator.set_minimum_feedrate(print.config.machine_min_extruding_rate.values[1]);
+ m_silent_time_estimator.set_minimum_travel_feedrate(print.config.machine_min_travel_rate.values[1]);
+ m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, print.config.machine_max_acceleration_x.values[1]);
+ m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, print.config.machine_max_acceleration_y.values[1]);
+ m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, print.config.machine_max_acceleration_z.values[1]);
+ m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, print.config.machine_max_acceleration_e.values[1]);
+ m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, print.config.machine_max_feedrate_x.values[1]);
+ m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, print.config.machine_max_feedrate_y.values[1]);
+ m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, print.config.machine_max_feedrate_z.values[1]);
+ m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, print.config.machine_max_feedrate_e.values[1]);
+ m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, print.config.machine_max_jerk_x.values[1]);
+ m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, print.config.machine_max_jerk_y.values[1]);
+ m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, print.config.machine_max_jerk_z.values[1]);
+ m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, print.config.machine_max_jerk_e.values[1]);
+ if (print.config.single_extruder_multi_material) {
+ // As of now the fields are shown at the UI dialog in the same combo box as the ramming values, so they
+ // are considered to be active for the single extruder multi-material printers only.
+ m_silent_time_estimator.set_filament_load_times(print.config.filament_load_time.values);
+ m_silent_time_estimator.set_filament_unload_times(print.config.filament_unload_time.values);
+ }
+ }
+ }
+ // Filament load / unload times are not specific to a firmware flavor. Let anybody use it if they find it useful.
+ if (print.config.single_extruder_multi_material) {
+ // As of now the fields are shown at the UI dialog in the same combo box as the ramming values, so they
+ // are considered to be active for the single extruder multi-material printers only.
+ m_normal_time_estimator.set_filament_load_times(print.config.filament_load_time.values);
+ m_normal_time_estimator.set_filament_unload_times(print.config.filament_unload_time.values);
+ }
// resets analyzer
m_analyzer.reset();
@@ -419,9 +551,10 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
// How many times will be change_layer() called?
// change_layer() in turn increments the progress bar status.
m_layer_count = 0;
+ PrintObjectPtrs printable_objects = print.get_printable_objects();
if (print.config.complete_objects.value) {
// Add each of the object's layers separately.
- for (auto object : print.objects) {
+ for (auto object : printable_objects) {
std::vector<coordf_t> zs;
zs.reserve(object->layers.size() + object->support_layers.size());
for (auto layer : object->layers)
@@ -434,7 +567,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
} else {
// Print all objects with the same print_z together.
std::vector<coordf_t> zs;
- for (auto object : print.objects) {
+ for (auto object : printable_objects) {
zs.reserve(zs.size() + object->layers.size() + object->support_layers.size());
for (auto layer : object->layers)
zs.push_back(layer->print_z);
@@ -453,8 +586,8 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
{
// get the minimum cross-section used in the print
std::vector<double> mm3_per_mm;
- for (auto object : print.objects) {
- for (size_t region_id = 0; region_id < print.regions.size(); ++ region_id) {
+ for (auto object : printable_objects) {
+ for (size_t region_id = 0; region_id < print.regions.size(); ++region_id) {
auto region = print.regions[region_id];
for (auto layer : object->layers) {
auto layerm = layer->regions[region_id];
@@ -514,7 +647,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
_write(file, "\n");
}
// Write some terse information on the slicing parameters.
- const PrintObject *first_object = print.objects.front();
+ const PrintObject *first_object = printable_objects.front();
const double layer_height = first_object->config.layer_height.value;
const double first_layer_height = first_object->config.first_layer_height.get_abs_value(layer_height);
for (size_t region_id = 0; region_id < print.regions.size(); ++ region_id) {
@@ -531,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();
@@ -543,20 +684,24 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
size_t initial_print_object_id = 0;
bool has_wipe_tower = false;
if (print.config.complete_objects.value) {
- // Find the 1st printing object, find its tool ordering and the initial extruder ID.
- for (; initial_print_object_id < print.objects.size(); ++initial_print_object_id) {
- tool_ordering = ToolOrdering(*print.objects[initial_print_object_id], initial_extruder_id);
- if ((initial_extruder_id = tool_ordering.first_extruder()) != (unsigned int)-1)
- break;
- }
- } else {
+ // Find the 1st printing object, find its tool ordering and the initial extruder ID.
+ for (; initial_print_object_id < printable_objects.size(); ++initial_print_object_id) {
+ tool_ordering = ToolOrdering(*printable_objects[initial_print_object_id], initial_extruder_id);
+ if ((initial_extruder_id = tool_ordering.first_extruder()) != (unsigned int)-1)
+ break;
+ }
+ } else {
// Find tool ordering for all the objects at once, and the initial extruder ID.
// If the tool ordering has been pre-calculated by Print class for wipe tower already, reuse it.
tool_ordering = print.m_tool_ordering.empty() ?
ToolOrdering(print, initial_extruder_id) :
print.m_tool_ordering;
- initial_extruder_id = tool_ordering.first_extruder();
has_wipe_tower = print.has_wipe_tower() && tool_ordering.has_wipe_tower();
+ initial_extruder_id = (has_wipe_tower && ! print.config.single_extruder_multi_material_priming) ?
+ // The priming towers will be skipped.
+ tool_ordering.all_extruders().back() :
+ // Don't skip the priming towers.
+ tool_ordering.first_extruder();
}
if (initial_extruder_id == (unsigned int)-1) {
// Nothing to print!
@@ -569,6 +714,9 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
m_cooling_buffer->set_current_extruder(initial_extruder_id);
+ // Emit machine envelope limits for the Marlin firmware.
+ this->print_machine_envelope(file, print);
+
// Disable fan.
if (! print.config.cooling.get_at(initial_extruder_id) || print.config.disable_fan_first_layers.get_at(initial_extruder_id))
_write(file, m_writer.set_fan(0, true));
@@ -581,8 +729,8 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
m_placeholder_parser.set("current_object_idx", 0);
// For the start / end G-code to do the priming and final filament pull in case there is no wipe tower provided.
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.
@@ -620,7 +768,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
// Collect outer contours of all objects over all layers.
// Discard objects only containing thin walls (offset would fail on an empty polygon).
Polygons islands;
- for (const PrintObject *object : print.objects)
+ for (const PrintObject *object : printable_objects)
for (const Layer *layer : object->layers)
for (const ExPolygon &expoly : layer->slices.expolygons)
for (const Point &copy : object->_shifted_copies) {
@@ -661,14 +809,17 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
}
}
- // Set initial extruder only after custom start G-code.
- _write(file, this->set_extruder(initial_extruder_id));
+ if (! (has_wipe_tower && print.config.single_extruder_multi_material_priming)) {
+ // Set initial extruder only after custom start G-code.
+ // Ugly hack: Do not set the initial extruder if the extruder is primed using the MMU priming towers at the edge of the print bed.
+ _write(file, this->set_extruder(initial_extruder_id));
+ }
// Do all objects for each layer.
if (print.config.complete_objects.value) {
// Print objects from the smallest to the tallest to avoid collisions
// when moving onto next object starting point.
- std::vector<PrintObject*> objects(print.objects);
+ std::vector<PrintObject*> objects(printable_objects);
std::sort(objects.begin(), objects.end(), [](const PrintObject* po1, const PrintObject* po2) { return po1->size.z < po2->size.z; });
size_t finished_objects = 0;
for (size_t object_id = initial_print_object_id; object_id < objects.size(); ++ object_id) {
@@ -729,7 +880,8 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
// Order objects using a nearest neighbor search.
std::vector<size_t> object_indices;
Points object_reference_points;
- for (PrintObject *object : print.objects)
+ PrintObjectPtrs printable_objects = print.get_printable_objects();
+ for (PrintObject *object : printable_objects)
object_reference_points.push_back(object->_shifted_copies.front());
Slic3r::Geometry::chained_path(object_reference_points, object_indices);
// Sort layers by Z.
@@ -739,32 +891,34 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
if (has_wipe_tower && ! layers_to_print.empty()) {
m_wipe_tower.reset(new WipeTowerIntegration(print.config, *print.m_wipe_tower_priming.get(), print.m_wipe_tower_tool_changes, *print.m_wipe_tower_final_purge.get()));
_write(file, m_writer.travel_to_z(first_layer_height + m_config.z_offset.value, "Move to the first layer height"));
- _write(file, m_wipe_tower->prime(*this));
- // Verify, whether the print overaps the priming extrusions.
- BoundingBoxf bbox_print(get_print_extrusions_extents(print));
- coordf_t twolayers_printz = ((layers_to_print.size() == 1) ? layers_to_print.front() : layers_to_print[1]).first + EPSILON;
- for (const PrintObject *print_object : print.objects)
- bbox_print.merge(get_print_object_extrusions_extents(*print_object, twolayers_printz));
- bbox_print.merge(get_wipe_tower_extrusions_extents(print, twolayers_printz));
- BoundingBoxf bbox_prime(get_wipe_tower_priming_extrusions_extents(print));
- bbox_prime.offset(0.5f);
- // Beep for 500ms, tone 800Hz. Yet better, play some Morse.
- _write(file, this->retract());
- _write(file, "M300 S800 P500\n");
- if (bbox_prime.overlap(bbox_print)) {
- // Wait for the user to remove the priming extrusions, otherwise they would
- // get covered by the print.
- _write(file, "M1 Remove priming towers and click button.\n");
- }
- else {
- // Just wait for a bit to let the user check, that the priming succeeded.
- //TODO Add a message explaining what the printer is waiting for. This needs a firmware fix.
- _write(file, "M1 S10\n");
+ if (print.config.single_extruder_multi_material_priming) {
+ _write(file, m_wipe_tower->prime(*this));
+ // Verify, whether the print overaps the priming extrusions.
+ BoundingBoxf bbox_print(get_print_extrusions_extents(print));
+ coordf_t twolayers_printz = ((layers_to_print.size() == 1) ? layers_to_print.front() : layers_to_print[1]).first + EPSILON;
+ for (const PrintObject *print_object : printable_objects)
+ bbox_print.merge(get_print_object_extrusions_extents(*print_object, twolayers_printz));
+ bbox_print.merge(get_wipe_tower_extrusions_extents(print, twolayers_printz));
+ BoundingBoxf bbox_prime(get_wipe_tower_priming_extrusions_extents(print));
+ bbox_prime.offset(0.5f);
+ // Beep for 500ms, tone 800Hz. Yet better, play some Morse.
+ _write(file, this->retract());
+ _write(file, "M300 S800 P500\n");
+ if (bbox_prime.overlap(bbox_print)) {
+ // Wait for the user to remove the priming extrusions, otherwise they would
+ // get covered by the print.
+ _write(file, "M1 Remove priming towers and click button.\n");
+ }
+ else {
+ // Just wait for a bit to let the user check, that the priming succeeded.
+ //TODO Add a message explaining what the printer is waiting for. This needs a firmware fix.
+ _write(file, "M1 S10\n");
+ }
}
}
// Extrude the layers.
for (auto &layer : layers_to_print) {
- const ToolOrdering::LayerTools &layer_tools = tool_ordering.tools_for_layer(layer.first);
+ const LayerTools &layer_tools = tool_ordering.tools_for_layer(layer.first);
if (m_wipe_tower && layer_tools.has_wipe_tower)
m_wipe_tower->next_layer();
this->process_layer(file, print, layer.second, layer_tools, size_t(-1));
@@ -806,21 +960,27 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
_write(file, m_writer.postamble());
// calculates estimated printing time
- m_time_estimator.calculate_time();
+ m_normal_time_estimator.calculate_time(false);
+ if (m_silent_time_estimator_enabled)
+ m_silent_time_estimator.calculate_time(false);
// 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.estimated_print_time = m_time_estimator.get_time_hms();
+ 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(), used_filament));
+
+ 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.) {
print.total_weight = print.total_weight + filament_weight;
@@ -830,11 +990,15 @@ 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 = %s\n", m_time_estimator.get_time_hms().c_str());
+ _write_format(file, "; estimated printing time (normal mode) = %s\n", m_normal_time_estimator.get_time_dhms().c_str());
+ if (m_silent_time_estimator_enabled)
+ _write_format(file, "; estimated printing time (silent mode) = %s\n", m_silent_time_estimator.get_time_dhms().c_str());
// Append full config.
_write(file, "\n");
@@ -919,6 +1083,36 @@ static bool custom_gcode_sets_temperature(const std::string &gcode, const int mc
return temp_set_by_gcode;
}
+// Print the machine envelope G-code for the Marlin firmware based on the "machine_max_xxx" parameters.
+// Do not process this piece of G-code by the time estimator, it already knows the values through another sources.
+void GCode::print_machine_envelope(FILE *file, Print &print)
+{
+ if (print.config.gcode_flavor.value == gcfMarlin) {
+ fprintf(file, "M201 X%d Y%d Z%d E%d ; sets maximum accelerations, mm/sec^2\n",
+ int(print.config.machine_max_acceleration_x.values.front() + 0.5),
+ int(print.config.machine_max_acceleration_y.values.front() + 0.5),
+ int(print.config.machine_max_acceleration_z.values.front() + 0.5),
+ int(print.config.machine_max_acceleration_e.values.front() + 0.5));
+ fprintf(file, "M203 X%d Y%d Z%d E%d ; sets maximum feedrates, mm/sec\n",
+ int(print.config.machine_max_feedrate_x.values.front() + 0.5),
+ int(print.config.machine_max_feedrate_y.values.front() + 0.5),
+ int(print.config.machine_max_feedrate_z.values.front() + 0.5),
+ int(print.config.machine_max_feedrate_e.values.front() + 0.5));
+ fprintf(file, "M204 P%d R%d T%d ; sets acceleration (P, T) and retract acceleration (R), mm/sec^2\n",
+ int(print.config.machine_max_acceleration_extruding.values.front() + 0.5),
+ int(print.config.machine_max_acceleration_retracting.values.front() + 0.5),
+ int(print.config.machine_max_acceleration_extruding.values.front() + 0.5));
+ fprintf(file, "M205 X%.2lf Y%.2lf Z%.2lf E%.2lf ; sets the jerk limits, mm/sec\n",
+ print.config.machine_max_jerk_x.values.front(),
+ print.config.machine_max_jerk_y.values.front(),
+ print.config.machine_max_jerk_z.values.front(),
+ print.config.machine_max_jerk_e.values.front());
+ fprintf(file, "M205 S%d T%d ; sets the minimum extruding and travel feed rate, mm/sec\n",
+ int(print.config.machine_min_extruding_rate.values.front() + 0.5),
+ int(print.config.machine_min_travel_rate.values.front() + 0.5));
+ }
+}
+
// Write 1st layer bed temperatures into the G-code.
// Only do that if the start G-code does not already contain any M-code controlling an extruder temperature.
// M140 - Set Extruder Temperature
@@ -1009,7 +1203,7 @@ void GCode::process_layer(
const Print &print,
// Set of object & print layers of the same PrintObject and with the same print_z.
const std::vector<LayerToPrint> &layers,
- const ToolOrdering::LayerTools &layer_tools,
+ const LayerTools &layer_tools,
// If set to size_t(-1), then print all copies of all objects.
// Otherwise print a single copy of a single object.
const size_t single_object_idx)
@@ -1147,7 +1341,6 @@ void GCode::process_layer(
// Group extrusions by an extruder, then by an object, an island and a region.
std::map<unsigned int, std::vector<ObjectByExtruder>> by_extruder;
-
for (const LayerToPrint &layer_to_print : layers) {
if (layer_to_print.support_layer != nullptr) {
const SupportLayer &support_layer = *layer_to_print.support_layer;
@@ -1224,70 +1417,66 @@ void GCode::process_layer(
if (layerm == nullptr)
continue;
const PrintRegion &region = *print.regions[region_id];
-
- // process perimeters
- for (const ExtrusionEntity *ee : layerm->perimeters.entities) {
- // perimeter_coll represents perimeter extrusions of a single island.
- const auto *perimeter_coll = dynamic_cast<const ExtrusionEntityCollection*>(ee);
- if (perimeter_coll->entities.empty())
- // This shouldn't happen but first_point() would fail.
- continue;
- // Init by_extruder item only if we actually use the extruder.
- std::vector<ObjectByExtruder::Island> &islands = object_islands_by_extruder(
- by_extruder,
- std::max<int>(region.config.perimeter_extruder.value - 1, 0),
- &layer_to_print - layers.data(),
- layers.size(), n_slices+1);
- for (size_t i = 0; i <= n_slices; ++ i)
- if (// perimeter_coll->first_point does not fit inside any slice
- i == n_slices ||
- // perimeter_coll->first_point fits inside ith slice
- point_inside_surface(i, perimeter_coll->first_point())) {
- if (islands[i].by_region.empty())
- islands[i].by_region.assign(print.regions.size(), ObjectByExtruder::Island::Region());
- islands[i].by_region[region_id].perimeters.append(perimeter_coll->entities);
- break;
- }
- }
-
- // process infill
- // layerm->fills is a collection of Slic3r::ExtrusionPath::Collection objects (C++ class ExtrusionEntityCollection),
- // each one containing the ExtrusionPath objects of a certain infill "group" (also called "surface"
- // throughout the code). We can redefine the order of such Collections but we have to
- // do each one completely at once.
- for (const ExtrusionEntity *ee : layerm->fills.entities) {
- // fill represents infill extrusions of a single island.
- const auto *fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
- if (fill->entities.empty())
- // This shouldn't happen but first_point() would fail.
- continue;
- // init by_extruder item only if we actually use the extruder
- int extruder_id = std::max<int>(0, (is_solid_infill(fill->entities.front()->role()) ? region.config.solid_infill_extruder : region.config.infill_extruder) - 1);
- // Init by_extruder item only if we actually use the extruder.
- std::vector<ObjectByExtruder::Island> &islands = object_islands_by_extruder(
- by_extruder,
- extruder_id,
- &layer_to_print - layers.data(),
- layers.size(), n_slices+1);
- for (size_t i = 0; i <= n_slices; ++i)
- if (// fill->first_point does not fit inside any slice
- i == n_slices ||
- // fill->first_point fits inside ith slice
- point_inside_surface(i, fill->first_point())) {
- if (islands[i].by_region.empty())
- islands[i].by_region.assign(print.regions.size(), ObjectByExtruder::Island::Region());
- islands[i].by_region[region_id].infills.append(fill->entities);
- break;
+
+
+ // Now we must process perimeters and infills and create islands of extrusions in by_region std::map.
+ // It is also necessary to save which extrusions are part of MM wiping and which are not.
+ // The process is almost the same for perimeters and infills - we will do it in a cycle that repeats twice:
+ for (std::string entity_type("infills") ; entity_type != "done" ; entity_type = entity_type=="infills" ? "perimeters" : "done") {
+
+ const ExtrusionEntitiesPtr& source_entities = entity_type=="infills" ? layerm->fills.entities : layerm->perimeters.entities;
+
+ for (const ExtrusionEntity *ee : source_entities) {
+ // fill represents infill extrusions of a single island.
+ const auto *fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
+ if (fill->entities.empty()) // This shouldn't happen but first_point() would fail.
+ continue;
+
+ // This extrusion is part of certain Region, which tells us which extruder should be used for it:
+ int correct_extruder_id = Print::get_extruder(*fill, region); entity_type=="infills" ? std::max<int>(0, (is_solid_infill(fill->entities.front()->role()) ? region.config.solid_infill_extruder : region.config.infill_extruder) - 1) :
+ std::max<int>(region.config.perimeter_extruder.value - 1, 0);
+
+ // Let's recover vector of extruder overrides:
+ const ExtruderPerCopy* entity_overrides = const_cast<LayerTools&>(layer_tools).wiping_extrusions().get_extruder_overrides(fill, correct_extruder_id, layer_to_print.object()->_shifted_copies.size());
+
+ // Now we must add this extrusion into the by_extruder map, once for each extruder that will print it:
+ for (unsigned int extruder : layer_tools.extruders)
+ {
+ // Init by_extruder item only if we actually use the extruder:
+ if (std::find(entity_overrides->begin(), entity_overrides->end(), extruder) != entity_overrides->end() || // at least one copy is overridden to use this extruder
+ std::find(entity_overrides->begin(), entity_overrides->end(), -extruder-1) != entity_overrides->end() || // at least one copy would normally be printed with this extruder (see get_extruder_overrides function for explanation)
+ (std::find(layer_tools.extruders.begin(), layer_tools.extruders.end(), correct_extruder_id) == layer_tools.extruders.end() && extruder == layer_tools.extruders.back())) // this entity is not overridden, but its extruder is not in layer_tools - we'll print it
+ //by last extruder on this layer (could happen e.g. when a wiping object is taller than others - dontcare extruders are eradicated from layer_tools)
+ {
+ std::vector<ObjectByExtruder::Island> &islands = object_islands_by_extruder(
+ by_extruder,
+ extruder,
+ &layer_to_print - layers.data(),
+ layers.size(), n_slices+1);
+ for (size_t i = 0; i <= n_slices; ++i)
+ if (// fill->first_point does not fit inside any slice
+ i == n_slices ||
+ // fill->first_point fits inside ith slice
+ point_inside_surface(i, fill->first_point())) {
+ if (islands[i].by_region.empty())
+ islands[i].by_region.assign(print.regions.size(), ObjectByExtruder::Island::Region());
+ islands[i].by_region[region_id].append(entity_type, fill, entity_overrides, layer_to_print.object()->_shifted_copies.size());
+ break;
+ }
+ }
}
+ }
}
} // for regions
}
} // for objects
+
+
// Extrude the skirt, brim, support, perimeters, infill ordered by the extruders.
std::vector<std::unique_ptr<EdgeGrid::Grid>> lower_layer_edge_grids(layers.size());
for (unsigned int extruder_id : layer_tools.extruders)
- {
+ {
gcode += (layer_tools.has_wipe_tower && m_wipe_tower) ?
m_wipe_tower->tool_change(*this, extruder_id, extruder_id == layer_tools.extruders.back()) :
this->set_extruder(extruder_id);
@@ -1312,7 +1501,7 @@ void GCode::process_layer(
for (ExtrusionPath &path : loop.paths) {
path.height = (float)layer.height;
path.mm3_per_mm = mm3_per_mm;
- }
+ }
gcode += this->extrude_loop(loop, "skirt", m_config.support_material_speed.value);
}
m_avoid_crossing_perimeters.use_external_mp = false;
@@ -1321,7 +1510,7 @@ void GCode::process_layer(
m_avoid_crossing_perimeters.disable_once = true;
}
}
-
+
// Extrude brim with the extruder of the 1st region.
if (! m_brim_done) {
this->set_origin(0., 0.);
@@ -1334,49 +1523,61 @@ void GCode::process_layer(
m_avoid_crossing_perimeters.disable_once = true;
}
+
auto objects_by_extruder_it = by_extruder.find(extruder_id);
if (objects_by_extruder_it == by_extruder.end())
continue;
- for (const ObjectByExtruder &object_by_extruder : objects_by_extruder_it->second) {
- const size_t layer_id = &object_by_extruder - objects_by_extruder_it->second.data();
- const PrintObject *print_object = layers[layer_id].object();
- if (print_object == nullptr)
- // This layer is empty for this particular object, it has neither object extrusions nor support extrusions at this print_z.
- continue;
-
- m_config.apply(print_object->config, true);
- m_layer = layers[layer_id].layer();
- if (m_config.avoid_crossing_perimeters)
- m_avoid_crossing_perimeters.init_layer_mp(union_ex(m_layer->slices, true));
- Points copies;
- if (single_object_idx == size_t(-1))
- copies = print_object->_shifted_copies;
- else
- copies.push_back(print_object->_shifted_copies[single_object_idx]);
- // Sort the copies by the closest point starting with the current print position.
-
- for (const Point &copy : copies) {
- // When starting a new object, use the external motion planner for the first travel move.
- std::pair<const PrintObject*, Point> this_object_copy(print_object, copy);
- if (m_last_obj_copy != this_object_copy)
- m_avoid_crossing_perimeters.use_external_mp_once = true;
- m_last_obj_copy = this_object_copy;
- this->set_origin(unscale(copy.x), unscale(copy.y));
- if (object_by_extruder.support != nullptr) {
- m_layer = layers[layer_id].support_layer;
- gcode += this->extrude_support(
- // support_extrusion_role is erSupportMaterial, erSupportMaterialInterface or erMixed for all extrusion paths.
- object_by_extruder.support->chained_path_from(m_last_pos, false, object_by_extruder.support_extrusion_role));
- m_layer = layers[layer_id].layer();
- }
- for (const ObjectByExtruder::Island &island : object_by_extruder.islands) {
- if (print.config.infill_first) {
- gcode += this->extrude_infill(print, island.by_region);
- gcode += this->extrude_perimeters(print, island.by_region, lower_layer_edge_grids[layer_id]);
- } else {
- gcode += this->extrude_perimeters(print, island.by_region, lower_layer_edge_grids[layer_id]);
- gcode += this->extrude_infill(print, island.by_region);
+
+ // We are almost ready to print. However, we must go through all the objects twice to print the the overridden extrusions first (infill/perimeter wiping feature):
+ for (int print_wipe_extrusions=const_cast<LayerTools&>(layer_tools).wiping_extrusions().is_anything_overridden(); print_wipe_extrusions>=0; --print_wipe_extrusions) {
+ if (print_wipe_extrusions == 0)
+ gcode+="; PURGING FINISHED\n";
+
+ for (ObjectByExtruder &object_by_extruder : objects_by_extruder_it->second) {
+ const size_t layer_id = &object_by_extruder - objects_by_extruder_it->second.data();
+ const PrintObject *print_object = layers[layer_id].object();
+ if (print_object == nullptr)
+ // This layer is empty for this particular object, it has neither object extrusions nor support extrusions at this print_z.
+ continue;
+
+ m_config.apply(print_object->config, true);
+ m_layer = layers[layer_id].layer();
+ if (m_config.avoid_crossing_perimeters)
+ m_avoid_crossing_perimeters.init_layer_mp(union_ex(m_layer->slices, true));
+ Points copies;
+ if (single_object_idx == size_t(-1))
+ copies = print_object->_shifted_copies;
+ else
+ copies.push_back(print_object->_shifted_copies[single_object_idx]);
+ // Sort the copies by the closest point starting with the current print position.
+
+ unsigned int copy_id = 0;
+ for (const Point &copy : copies) {
+ // When starting a new object, use the external motion planner for the first travel move.
+ std::pair<const PrintObject*, Point> this_object_copy(print_object, copy);
+ if (m_last_obj_copy != this_object_copy)
+ m_avoid_crossing_perimeters.use_external_mp_once = true;
+ m_last_obj_copy = this_object_copy;
+ this->set_origin(unscale(copy.x), unscale(copy.y));
+ if (object_by_extruder.support != nullptr && !print_wipe_extrusions) {
+ m_layer = layers[layer_id].support_layer;
+ gcode += this->extrude_support(
+ // support_extrusion_role is erSupportMaterial, erSupportMaterialInterface or erMixed for all extrusion paths.
+ object_by_extruder.support->chained_path_from(m_last_pos, false, object_by_extruder.support_extrusion_role));
+ m_layer = layers[layer_id].layer();
+ }
+ for (ObjectByExtruder::Island &island : object_by_extruder.islands) {
+ const auto& by_region_specific = const_cast<LayerTools&>(layer_tools).wiping_extrusions().is_anything_overridden() ? island.by_region_per_copy(copy_id, extruder_id, print_wipe_extrusions) : island.by_region;
+
+ if (print.config.infill_first) {
+ gcode += this->extrude_infill(print, by_region_specific);
+ gcode += this->extrude_perimeters(print, by_region_specific, lower_layer_edge_grids[layer_id]);
+ } else {
+ gcode += this->extrude_perimeters(print, by_region_specific, lower_layer_edge_grids[layer_id]);
+ gcode += this->extrude_infill(print,by_region_specific);
+ }
}
+ ++copy_id;
}
}
}
@@ -1399,7 +1600,7 @@ void GCode::process_layer(
if (m_pressure_equalizer)
gcode = m_pressure_equalizer->process(gcode.c_str(), false);
// printf("G-code after filter:\n%s\n", out.c_str());
-
+
_write(file, gcode);
}
@@ -1411,15 +1612,22 @@ void GCode::apply_print_config(const PrintConfig &print_config)
void GCode::append_full_config(const Print& print, std::string& str)
{
- const StaticPrintConfig *configs[] = { &print.config, &print.default_object_config, &print.default_region_config };
+ const StaticPrintConfig *configs[] = { static_cast<const GCodeConfig*>(&print.config), &print.default_object_config, &print.default_region_config };
for (size_t i = 0; i < sizeof(configs) / sizeof(configs[0]); ++i) {
const StaticPrintConfig *cfg = configs[i];
for (const std::string &key : cfg->keys())
- {
if (key != "compatible_printers")
str += "; " + key + " = " + cfg->serialize(key) + "\n";
- }
}
+ const DynamicConfig &full_config = print.placeholder_parser.config();
+ for (const char *key : {
+ "print_settings_id", "filament_settings_id", "printer_settings_id",
+ "printer_model", "printer_variant", "default_print_profile", "default_filament_profile",
+ "compatible_printers_condition_cummulative", "inherits_cummulative" }) {
+ const ConfigOption *opt = full_config.option(key);
+ if (opt != nullptr)
+ str += std::string("; ") + key + " = " + opt->serialize() + "\n";
+ }
}
void GCode::set_extruders(const std::vector<unsigned int> &extruder_ids)
@@ -2059,7 +2267,9 @@ void GCode::_write(FILE* file, const char *what)
// writes string to file
fwrite(gcode, 1, ::strlen(gcode), file);
// updates time estimator and gcode lines vector
- m_time_estimator.add_gcode_block(gcode);
+ m_normal_time_estimator.add_gcode_block(gcode);
+ if (m_silent_time_estimator_enabled)
+ m_silent_time_estimator.add_gcode_block(gcode);
}
}
@@ -2438,4 +2648,62 @@ Point GCode::gcode_to_point(const Pointf &point) const
scale_(point.y - m_origin.y + extruder_offset.y));
}
+
+// Goes through by_region std::vector and returns reference to a subvector of entities, that are to be printed
+// during infill/perimeter wiping, or normally (depends on wiping_entities parameter)
+// Returns a reference to member to avoid copying.
+const std::vector<GCode::ObjectByExtruder::Island::Region>& GCode::ObjectByExtruder::Island::by_region_per_copy(unsigned int copy, int extruder, bool wiping_entities)
+{
+ by_region_per_copy_cache.clear();
+
+ for (const auto& reg : by_region) {
+ by_region_per_copy_cache.push_back(ObjectByExtruder::Island::Region()); // creates a region in the newly created Island
+
+ // Now we are going to iterate through perimeters and infills and pick ones that are supposed to be printed
+ // References are used so that we don't have to repeat the same code
+ for (int iter = 0; iter < 2; ++iter) {
+ const ExtrusionEntitiesPtr& entities = (iter ? reg.infills.entities : reg.perimeters.entities);
+ ExtrusionEntityCollection& target_eec = (iter ? by_region_per_copy_cache.back().infills : by_region_per_copy_cache.back().perimeters);
+ const std::vector<const ExtruderPerCopy*>& overrides = (iter ? reg.infills_overrides : reg.perimeters_overrides);
+
+ // Now the most important thing - which extrusion should we print.
+ // See function ToolOrdering::get_extruder_overrides for details about the negative numbers hack.
+ int this_extruder_mark = wiping_entities ? extruder : -extruder-1;
+
+ for (unsigned int i=0;i<entities.size();++i)
+ if (overrides[i]->at(copy) == this_extruder_mark) // this copy should be printed with this extruder
+ target_eec.append((*entities[i]));
+ }
+ }
+ return by_region_per_copy_cache;
}
+
+
+
+// This function takes the eec and appends its entities to either perimeters or infills of this Region (depending on the first parameter)
+// It also saves pointer to ExtruderPerCopy struct (for each entity), that holds information about which extruders should be used for which copy.
+void GCode::ObjectByExtruder::Island::Region::append(const std::string& type, const ExtrusionEntityCollection* eec, const ExtruderPerCopy* copies_extruder, unsigned int object_copies_num)
+{
+ // We are going to manipulate either perimeters or infills, exactly in the same way. Let's create pointers to the proper structure to not repeat ourselves:
+ ExtrusionEntityCollection* perimeters_or_infills = &infills;
+ std::vector<const ExtruderPerCopy*>* perimeters_or_infills_overrides = &infills_overrides;
+
+ if (type == "perimeters") {
+ perimeters_or_infills = &perimeters;
+ perimeters_or_infills_overrides = &perimeters_overrides;
+ }
+ else
+ if (type != "infills") {
+ CONFESS("Unknown parameter!");
+ return;
+ }
+
+
+ // First we append the entities, there are eec->entities.size() of them:
+ perimeters_or_infills->append(eec->entities);
+
+ for (unsigned int i=0;i<eec->entities.size();++i)
+ perimeters_or_infills_overrides->push_back(copies_extruder);
+}
+
+} // namespace Slic3r
diff --git a/xs/src/libslic3r/GCode.hpp b/xs/src/libslic3r/GCode.hpp
index d028e90aa..d319bee01 100644
--- a/xs/src/libslic3r/GCode.hpp
+++ b/xs/src/libslic3r/GCode.hpp
@@ -83,8 +83,10 @@ public:
const WipeTower::ToolChangeResult &priming,
const std::vector<std::vector<WipeTower::ToolChangeResult>> &tool_changes,
const WipeTower::ToolChangeResult &final_purge) :
- m_left(float(print_config.wipe_tower_x.value)),
- m_right(float(print_config.wipe_tower_x.value + print_config.wipe_tower_width.value)),
+ m_left(/*float(print_config.wipe_tower_x.value)*/ 0.f),
+ m_right(float(/*print_config.wipe_tower_x.value +*/ print_config.wipe_tower_width.value)),
+ m_wipe_tower_pos(float(print_config.wipe_tower_x.value), float(print_config.wipe_tower_y.value)),
+ m_wipe_tower_rotation(float(print_config.wipe_tower_rotation_angle)),
m_priming(priming),
m_tool_changes(tool_changes),
m_final_purge(final_purge),
@@ -96,14 +98,20 @@ 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&);
std::string append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id) const;
+ // Postprocesses gcode: rotates and moves all G1 extrusions and returns result
+ std::string rotate_wipe_tower_moves(const std::string& gcode_original, const WipeTower::xy& start_pos, const WipeTower::xy& translation, float angle) const;
+
// Left / right edges of the wipe tower, for the planning of wipe moves.
const float m_left;
const float m_right;
+ const WipeTower::xy m_wipe_tower_pos;
+ const float m_wipe_tower_rotation;
// Reference to cached values at the Printer class.
const WipeTower::ToolChangeResult &m_priming;
const std::vector<std::vector<WipeTower::ToolChangeResult>> &m_tool_changes;
@@ -112,6 +120,7 @@ private:
int m_layer_idx;
int m_tool_change_idx;
bool m_brim_done;
+ bool i_have_brim = false;
};
class GCode {
@@ -133,6 +142,9 @@ public:
m_last_height(GCodeAnalyzer::Default_Height),
m_brim_done(false),
m_second_layer_things_done(false),
+ m_normal_time_estimator(GCodeTimeEstimator::Normal),
+ m_silent_time_estimator(GCodeTimeEstimator::Silent),
+ m_silent_time_estimator_enabled(false),
m_last_obj_copy(nullptr, Point(std::numeric_limits<coord_t>::max(), std::numeric_limits<coord_t>::max()))
{}
~GCode() {}
@@ -185,7 +197,7 @@ protected:
const Print &print,
// Set of object & print layers of the same PrintObject and with the same print_z.
const std::vector<LayerToPrint> &layers,
- const ToolOrdering::LayerTools &layer_tools,
+ const LayerTools &layer_tools,
// If set to size_t(-1), then print all copies of all objects.
// Otherwise print a single copy of a single object.
const size_t single_object_idx = size_t(-1));
@@ -200,6 +212,7 @@ protected:
std::string extrude_multi_path(ExtrusionMultiPath multipath, std::string description = "", double speed = -1.);
std::string extrude_path(ExtrusionPath path, std::string description = "", double speed = -1.);
+ typedef std::vector<int> ExtruderPerCopy;
// Extruding multiple objects with soluble / non-soluble / combined supports
// on a multi-material printer, trying to minimize tool switches.
// Following structures sort extrusions by the extruder ID, by an order of objects and object islands.
@@ -215,11 +228,24 @@ protected:
struct Region {
ExtrusionEntityCollection perimeters;
ExtrusionEntityCollection infills;
+
+ std::vector<const ExtruderPerCopy*> infills_overrides;
+ std::vector<const ExtruderPerCopy*> perimeters_overrides;
+
+ // Appends perimeter/infill entities and writes don't indices of those that are not to be extruder as part of perimeter/infill wiping
+ void append(const std::string& type, const ExtrusionEntityCollection* eec, const ExtruderPerCopy* copy_extruders, unsigned int object_copies_num);
};
- std::vector<Region> by_region;
+
+ std::vector<Region> by_region; // all extrusions for this island, grouped by regions
+ const std::vector<Region>& by_region_per_copy(unsigned int copy, int extruder, bool wiping_entities = false); // returns reference to subvector of by_region
+
+ private:
+ std::vector<Region> by_region_per_copy_cache; // caches vector generated by function above to avoid copying and recalculating
};
std::vector<Island> islands;
};
+
+
std::string extrude_perimeters(const Print &print, const std::vector<ObjectByExtruder::Island::Region> &by_region, std::unique_ptr<EdgeGrid::Grid> &lower_layer_edge_grid);
std::string extrude_infill(const Print &print, const std::vector<ObjectByExtruder::Island::Region> &by_region);
std::string extrude_support(const ExtrusionEntityCollection &support_fills);
@@ -289,8 +315,10 @@ protected:
// Index of a last object copy extruded.
std::pair<const PrintObject*, Point> m_last_obj_copy;
- // Time estimator
- GCodeTimeEstimator m_time_estimator;
+ // Time estimators
+ GCodeTimeEstimator m_normal_time_estimator;
+ GCodeTimeEstimator m_silent_time_estimator;
+ bool m_silent_time_estimator_enabled;
// Analyzer
GCodeAnalyzer m_analyzer;
@@ -308,6 +336,7 @@ protected:
void _write_format(FILE* file, const char* format, ...);
std::string _extrude(const ExtrusionPath &path, std::string description = "", double speed = -1);
+ void print_machine_envelope(FILE *file, Print &print);
void _print_first_layer_bed_temperature(FILE *file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait);
void _print_first_layer_extruder_temperatures(FILE *file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait);
// this flag triggers first layer speeds
diff --git a/xs/src/libslic3r/GCode/PreviewData.cpp b/xs/src/libslic3r/GCode/PreviewData.cpp
index 40f0747b2..3833bca06 100644
--- a/xs/src/libslic3r/GCode/PreviewData.cpp
+++ b/xs/src/libslic3r/GCode/PreviewData.cpp
@@ -2,7 +2,12 @@
#include "PreviewData.hpp"
#include <float.h>
#include <wx/intl.h>
-#include "slic3r/GUI/GUI.hpp"
+#include <I18N.hpp>
+
+#include <boost/format.hpp>
+
+//! macro used to mark string used at localization,
+#define L(s) (s)
namespace Slic3r {
@@ -405,7 +410,7 @@ GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std::
items.reserve(last_valid - first_valid + 1);
for (unsigned int i = (unsigned int)first_valid; i <= (unsigned int)last_valid; ++i)
{
- items.emplace_back(_CHB(extrusion.role_names[i].c_str()).data(), extrusion.role_colors[i]);
+ items.emplace_back(Slic3r::I18N::translate(extrusion.role_names[i]), extrusion.role_colors[i]);
}
break;
@@ -436,13 +441,9 @@ GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std::
items.reserve(tools_colors_count);
for (unsigned int i = 0; i < tools_colors_count; ++i)
{
- char buf[MIN_BUF_LENGTH_FOR_L];
- sprintf(buf, _CHB(L("Extruder %d")), i + 1);
-
GCodePreviewData::Color color;
::memcpy((void*)color.rgba, (const void*)(tool_colors.data() + i * 4), 4 * sizeof(float));
-
- items.emplace_back(buf, color);
+ items.emplace_back((boost::format(Slic3r::I18N::translate(L("Extruder %d"))) % (i + 1)).str(), color);
}
break;
diff --git a/xs/src/libslic3r/GCode/PrintExtents.cpp b/xs/src/libslic3r/GCode/PrintExtents.cpp
index 3c3f0f8d5..37b79f343 100644
--- a/xs/src/libslic3r/GCode/PrintExtents.cpp
+++ b/xs/src/libslic3r/GCode/PrintExtents.cpp
@@ -134,6 +134,11 @@ BoundingBoxf get_print_object_extrusions_extents(const PrintObject &print_object
// The projection does not contain the priming regions.
BoundingBoxf get_wipe_tower_extrusions_extents(const Print &print, const coordf_t max_print_z)
{
+ // Wipe tower extrusions are saved as if the tower was at the origin with no rotation
+ // We need to get position and angle of the wipe tower to transform them to actual position.
+ Pointf wipe_tower_pos(print.config.wipe_tower_x.value, print.config.wipe_tower_y.value);
+ float wipe_tower_angle = print.config.wipe_tower_rotation_angle.value;
+
BoundingBoxf bbox;
for (const std::vector<WipeTower::ToolChangeResult> &tool_changes : print.m_wipe_tower_tool_changes) {
if (! tool_changes.empty() && tool_changes.front().print_z > max_print_z)
@@ -144,6 +149,11 @@ BoundingBoxf get_wipe_tower_extrusions_extents(const Print &print, const coordf_
if (e.width > 0) {
Pointf p1((&e - 1)->pos.x, (&e - 1)->pos.y);
Pointf p2(e.pos.x, e.pos.y);
+ p1.rotate(wipe_tower_angle);
+ p1.translate(wipe_tower_pos);
+ p2.rotate(wipe_tower_angle);
+ p2.translate(wipe_tower_pos);
+
bbox.merge(p1);
coordf_t radius = 0.5 * e.width;
bbox.min.x = std::min(bbox.min.x, std::min(p1.x, p2.x) - radius);
diff --git a/xs/src/libslic3r/GCode/ToolOrdering.cpp b/xs/src/libslic3r/GCode/ToolOrdering.cpp
index 271b75ef3..189a94d49 100644
--- a/xs/src/libslic3r/GCode/ToolOrdering.cpp
+++ b/xs/src/libslic3r/GCode/ToolOrdering.cpp
@@ -15,6 +15,24 @@
namespace Slic3r {
+
+// Returns true in case that extruder a comes before b (b does not have to be present). False otherwise.
+bool LayerTools::is_extruder_order(unsigned int a, unsigned int b) const
+{
+ if (a==b)
+ return false;
+
+ for (auto extruder : extruders) {
+ if (extruder == a)
+ return true;
+ if (extruder == b)
+ return false;
+ }
+
+ return false;
+}
+
+
// For the use case when each object is printed separately
// (print.config.complete_objects is true).
ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extruder, bool prime_multi_material)
@@ -48,11 +66,14 @@ ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extrude
// (print.config.complete_objects is false).
ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool prime_multi_material)
{
+ m_print_config_ptr = &print.config;
+
+ PrintObjectPtrs objects = print.get_printable_objects();
// Initialize the print layers for all objects and all layers.
coordf_t object_bottom_z = 0.;
{
std::vector<coordf_t> zs;
- for (auto object : print.objects) {
+ for (auto object : objects) {
zs.reserve(zs.size() + object->layers.size() + object->support_layers.size());
for (auto layer : object->layers)
zs.emplace_back(layer->print_z);
@@ -65,7 +86,7 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool
}
// Collect extruders reuqired to print the layers.
- for (auto object : print.objects)
+ for (auto object : objects)
this->collect_extruders(*object);
// Reorder the extruders to minimize tool switches.
@@ -76,9 +97,10 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool
this->collect_extruder_statistics(prime_multi_material);
}
-ToolOrdering::LayerTools& ToolOrdering::tools_for_layer(coordf_t print_z)
+
+LayerTools& ToolOrdering::tools_for_layer(coordf_t print_z)
{
- auto it_layer_tools = std::lower_bound(m_layer_tools.begin(), m_layer_tools.end(), ToolOrdering::LayerTools(print_z - EPSILON));
+ auto it_layer_tools = std::lower_bound(m_layer_tools.begin(), m_layer_tools.end(), LayerTools(print_z - EPSILON));
assert(it_layer_tools != m_layer_tools.end());
coordf_t dist_min = std::abs(it_layer_tools->print_z - print_z);
for (++ it_layer_tools; it_layer_tools != m_layer_tools.end(); ++it_layer_tools) {
@@ -102,7 +124,7 @@ void ToolOrdering::initialize_layers(std::vector<coordf_t> &zs)
coordf_t zmax = zs[i] + EPSILON;
for (; j < zs.size() && zs[j] <= zmax; ++ j) ;
// Assign an average print_z to the set of layers with nearly equal print_z.
- m_layer_tools.emplace_back(LayerTools(0.5 * (zs[i] + zs[j-1])));
+ m_layer_tools.emplace_back(LayerTools(0.5 * (zs[i] + zs[j-1]), m_print_config_ptr));
i = j;
}
}
@@ -134,12 +156,29 @@ void ToolOrdering::collect_extruders(const PrintObject &object)
if (layerm == nullptr)
continue;
const PrintRegion &region = *object.print()->regions[region_id];
+
if (! layerm->perimeters.entities.empty()) {
- layer_tools.extruders.push_back(region.config.perimeter_extruder.value);
+ bool something_nonoverriddable = true;
+
+ if (m_print_config_ptr) { // in this case complete_objects is false (see ToolOrdering constructors)
+ something_nonoverriddable = false;
+ for (const auto& eec : layerm->perimeters.entities) // let's check if there are nonoverriddable entities
+ if (!layer_tools.wiping_extrusions().is_overriddable(dynamic_cast<const ExtrusionEntityCollection&>(*eec), *m_print_config_ptr, object, region)) {
+ something_nonoverriddable = true;
+ break;
+ }
+ }
+
+ if (something_nonoverriddable)
+ layer_tools.extruders.push_back(region.config.perimeter_extruder.value);
+
layer_tools.has_object = true;
}
+
+
bool has_infill = false;
bool has_solid_infill = false;
+ bool something_nonoverriddable = false;
for (const ExtrusionEntity *ee : layerm->fills.entities) {
// fill represents infill extrusions of a single island.
const auto *fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
@@ -148,19 +187,33 @@ void ToolOrdering::collect_extruders(const PrintObject &object)
has_solid_infill = true;
else if (role != erNone)
has_infill = true;
+
+ if (m_print_config_ptr) {
+ if (!something_nonoverriddable && !layer_tools.wiping_extrusions().is_overriddable(*fill, *m_print_config_ptr, object, region))
+ something_nonoverriddable = true;
+ }
+ }
+
+ if (something_nonoverriddable || !m_print_config_ptr)
+ {
+ if (has_solid_infill)
+ layer_tools.extruders.push_back(region.config.solid_infill_extruder);
+ if (has_infill)
+ layer_tools.extruders.push_back(region.config.infill_extruder);
}
- if (has_solid_infill)
- layer_tools.extruders.push_back(region.config.solid_infill_extruder);
- if (has_infill)
- layer_tools.extruders.push_back(region.config.infill_extruder);
if (has_solid_infill || has_infill)
layer_tools.has_object = true;
}
}
- // Sort and remove duplicates
- for (LayerTools &lt : m_layer_tools)
- sort_remove_duplicates(lt.extruders);
+ for (auto& layer : m_layer_tools) {
+ // Sort and remove duplicates
+ sort_remove_duplicates(layer.extruders);
+
+ // make sure that there are some tools for each object layer (e.g. tall wiping object will result in empty extruders vector)
+ if (layer.extruders.empty() && layer.has_object)
+ layer.extruders.push_back(0); // 0="dontcare" extruder - it will be taken care of in reorder_extruders
+ }
}
// Reorder extruders to minimize layer changes.
@@ -217,6 +270,8 @@ void ToolOrdering::reorder_extruders(unsigned int last_extruder_id)
}
}
+
+
void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z)
{
if (m_layer_tools.empty())
@@ -327,4 +382,250 @@ void ToolOrdering::collect_extruder_statistics(bool prime_multi_material)
}
}
+
+
+// This function is called from Print::mark_wiping_extrusions and sets extruder this entity should be printed with (-1 .. as usual)
+void WipingExtrusions::set_extruder_override(const ExtrusionEntity* entity, unsigned int copy_id, int extruder, unsigned int num_of_copies)
+{
+ something_overridden = true;
+
+ auto entity_map_it = (entity_map.insert(std::make_pair(entity, std::vector<int>()))).first; // (add and) return iterator
+ auto& copies_vector = entity_map_it->second;
+ if (copies_vector.size() < num_of_copies)
+ copies_vector.resize(num_of_copies, -1);
+
+ if (copies_vector[copy_id] != -1)
+ std::cout << "ERROR: Entity extruder overriden multiple times!!!\n"; // A debugging message - this must never happen.
+
+ copies_vector[copy_id] = extruder;
+}
+
+
+// Finds first non-soluble extruder on the layer
+int WipingExtrusions::first_nonsoluble_extruder_on_layer(const PrintConfig& print_config) const
+{
+ const LayerTools& lt = *m_layer_tools;
+ for (auto extruders_it = lt.extruders.begin(); extruders_it != lt.extruders.end(); ++extruders_it)
+ if (!print_config.filament_soluble.get_at(*extruders_it))
+ return (*extruders_it);
+
+ return (-1);
+}
+
+// Finds last non-soluble extruder on the layer
+int WipingExtrusions::last_nonsoluble_extruder_on_layer(const PrintConfig& print_config) const
+{
+ const LayerTools& lt = *m_layer_tools;
+ for (auto extruders_it = lt.extruders.rbegin(); extruders_it != lt.extruders.rend(); ++extruders_it)
+ if (!print_config.filament_soluble.get_at(*extruders_it))
+ return (*extruders_it);
+
+ return (-1);
+}
+
+
+// Decides whether this entity could be overridden
+bool WipingExtrusions::is_overriddable(const ExtrusionEntityCollection& eec, const PrintConfig& print_config, const PrintObject& object, const PrintRegion& region) const
+{
+ if (print_config.filament_soluble.get_at(Print::get_extruder(eec, region)))
+ return false;
+
+ if (object.config.wipe_into_objects)
+ return true;
+
+ if (!region.config.wipe_into_infill || eec.role() != erInternalInfill)
+ return false;
+
+ return true;
+}
+
+
+// Following function iterates through all extrusions on the layer, remembers those that could be used for wiping after toolchange
+// and returns volume that is left to be wiped on the wipe tower.
+float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int old_extruder, unsigned int new_extruder, float volume_to_wipe)
+{
+ const LayerTools& lt = *m_layer_tools;
+ const float min_infill_volume = 0.f; // ignore infill with smaller volume than this
+
+ if (print.config.filament_soluble.get_at(old_extruder) || print.config.filament_soluble.get_at(new_extruder))
+ return volume_to_wipe; // Soluble filament cannot be wiped in a random infill, neither the filament after it
+
+ // we will sort objects so that dedicated for wiping are at the beginning:
+ PrintObjectPtrs object_list = print.get_printable_objects();
+ std::sort(object_list.begin(), object_list.end(), [](const PrintObject* a, const PrintObject* b) { return a->config.wipe_into_objects; });
+
+ // We will now iterate through
+ // - first the dedicated objects to mark perimeters or infills (depending on infill_first)
+ // - second through the dedicated ones again to mark infills or perimeters (depending on infill_first)
+ // - then all the others to mark infills (in case that !infill_first, we must also check that the perimeter is finished already
+ // this is controlled by the following variable:
+ bool perimeters_done = false;
+
+ for (int i=0 ; i<(int)object_list.size() + (perimeters_done ? 0 : 1); ++i) {
+ if (!perimeters_done && (i==(int)object_list.size() || !object_list[i]->config.wipe_into_objects)) { // we passed the last dedicated object in list
+ perimeters_done = true;
+ i=-1; // let's go from the start again
+ continue;
+ }
+
+ const auto& object = object_list[i];
+
+ // Finds this layer:
+ auto this_layer_it = std::find_if(object->layers.begin(), object->layers.end(), [&lt](const Layer* lay) { return std::abs(lt.print_z - lay->print_z)<EPSILON; });
+ if (this_layer_it == object->layers.end())
+ continue;
+ const Layer* this_layer = *this_layer_it;
+ unsigned int num_of_copies = object->_shifted_copies.size();
+
+ for (unsigned int copy = 0; copy < num_of_copies; ++copy) { // iterate through copies first, so that we mark neighbouring infills to minimize travel moves
+
+ for (size_t region_id = 0; region_id < object->print()->regions.size(); ++ region_id) {
+ const auto& region = *object->print()->regions[region_id];
+
+ if (!region.config.wipe_into_infill && !object->config.wipe_into_objects)
+ continue;
+
+
+ if ((!print.config.infill_first ? perimeters_done : !perimeters_done) || (!object->config.wipe_into_objects && region.config.wipe_into_infill)) {
+ for (const ExtrusionEntity* ee : this_layer->regions[region_id]->fills.entities) { // iterate through all infill Collections
+ auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
+
+ if (!is_overriddable(*fill, print.config, *object, region))
+ continue;
+
+ // What extruder would this normally be printed with?
+ unsigned int correct_extruder = Print::get_extruder(*fill, region);
+
+ if (volume_to_wipe<=0)
+ continue;
+
+ if (!object->config.wipe_into_objects && !print.config.infill_first && region.config.wipe_into_infill)
+ // In this case we must check that the original extruder is used on this layer before the one we are overridding
+ // (and the perimeters will be finished before the infill is printed):
+ if (!lt.is_extruder_order(region.config.perimeter_extruder - 1, new_extruder))
+ continue;
+
+ if ((!is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume)) { // this infill will be used to wipe this extruder
+ set_extruder_override(fill, copy, new_extruder, num_of_copies);
+ volume_to_wipe -= fill->total_volume();
+ }
+ }
+ }
+
+ // Now the same for perimeters - see comments above for explanation:
+ if (object->config.wipe_into_objects && (print.config.infill_first ? perimeters_done : !perimeters_done))
+ {
+ for (const ExtrusionEntity* ee : this_layer->regions[region_id]->perimeters.entities) {
+ auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
+ if (!is_overriddable(*fill, print.config, *object, region))
+ continue;
+
+ if (volume_to_wipe<=0)
+ continue;
+
+ if ((!is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume)) {
+ set_extruder_override(fill, copy, new_extruder, num_of_copies);
+ volume_to_wipe -= fill->total_volume();
+ }
+ }
+ }
+ }
+ }
+ }
+ return std::max(0.f, volume_to_wipe);
+}
+
+
+
+// Called after all toolchanges on a layer were mark_infill_overridden. There might still be overridable entities,
+// that were not actually overridden. If they are part of a dedicated object, printing them with the extruder
+// they were initially assigned to might mean violating the perimeter-infill order. We will therefore go through
+// them again and make sure we override it.
+void WipingExtrusions::ensure_perimeters_infills_order(const Print& print)
+{
+ const LayerTools& lt = *m_layer_tools;
+ unsigned int first_nonsoluble_extruder = first_nonsoluble_extruder_on_layer(print.config);
+ unsigned int last_nonsoluble_extruder = last_nonsoluble_extruder_on_layer(print.config);
+
+ PrintObjectPtrs printable_objects = print.get_printable_objects();
+ for (const PrintObject* object : printable_objects) {
+ // Finds this layer:
+ auto this_layer_it = std::find_if(object->layers.begin(), object->layers.end(), [&lt](const Layer* lay) { return std::abs(lt.print_z - lay->print_z)<EPSILON; });
+ if (this_layer_it == object->layers.end())
+ continue;
+ const Layer* this_layer = *this_layer_it;
+ unsigned int num_of_copies = object->_shifted_copies.size();
+
+ for (unsigned int copy = 0; copy < num_of_copies; ++copy) { // iterate through copies first, so that we mark neighbouring infills to minimize travel moves
+ for (size_t region_id = 0; region_id < object->print()->regions.size(); ++ region_id) {
+ const auto& region = *object->print()->regions[region_id];
+
+ if (!region.config.wipe_into_infill && !object->config.wipe_into_objects)
+ continue;
+
+ for (const ExtrusionEntity* ee : this_layer->regions[region_id]->fills.entities) { // iterate through all infill Collections
+ auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
+
+ if (!is_overriddable(*fill, print.config, *object, region)
+ || is_entity_overridden(fill, copy) )
+ continue;
+
+ // This infill could have been overridden but was not - unless we do something, it could be
+ // printed before its perimeter, or not be printed at all (in case its original extruder has
+ // not been added to LayerTools
+ // Either way, we will now force-override it with something suitable:
+ if (print.config.infill_first
+ || object->config.wipe_into_objects // in this case the perimeter is overridden, so we can override by the last one safely
+ || lt.is_extruder_order(region.config.perimeter_extruder - 1, last_nonsoluble_extruder // !infill_first, but perimeter is already printed when last extruder prints
+ || std::find(lt.extruders.begin(), lt.extruders.end(), region.config.infill_extruder - 1) == lt.extruders.end()) // we have to force override - this could violate infill_first (FIXME)
+ )
+ set_extruder_override(fill, copy, (print.config.infill_first ? first_nonsoluble_extruder : last_nonsoluble_extruder), num_of_copies);
+ else {
+ // In this case we can (and should) leave it to be printed normally.
+ // Force overriding would mean it gets printed before its perimeter.
+ }
+ }
+
+ // Now the same for perimeters - see comments above for explanation:
+ for (const ExtrusionEntity* ee : this_layer->regions[region_id]->perimeters.entities) { // iterate through all perimeter Collections
+ auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
+ if (!is_overriddable(*fill, print.config, *object, region)
+ || is_entity_overridden(fill, copy) )
+ continue;
+
+ set_extruder_override(fill, copy, (print.config.infill_first ? last_nonsoluble_extruder : first_nonsoluble_extruder), num_of_copies);
+ }
+ }
+ }
+ }
+}
+
+
+
+
+
+
+
+// Following function is called from process_layer and returns pointer to vector with information about which extruders should be used for given copy of this entity.
+// It first makes sure the pointer is valid (creates the vector if it does not exist) and contains a record for each copy
+// It also modifies the vector in place and changes all -1 to correct_extruder_id (at the time the overrides were created, correct extruders were not known,
+// so -1 was used as "print as usual".
+// The resulting vector has to keep track of which extrusions are the ones that were overridden and which were not. In the extruder is used as overridden,
+// its number is saved as it is (zero-based index). Usual extrusions are saved as -number-1 (unfortunately there is no negative zero).
+const std::vector<int>* WipingExtrusions::get_extruder_overrides(const ExtrusionEntity* entity, int correct_extruder_id, int num_of_copies)
+{
+ auto entity_map_it = entity_map.find(entity);
+ if (entity_map_it == entity_map.end())
+ entity_map_it = (entity_map.insert(std::make_pair(entity, std::vector<int>()))).first;
+
+ // Now the entity_map_it should be valid, let's make sure the vector is long enough:
+ entity_map_it->second.resize(num_of_copies, -1);
+
+ // Each -1 now means "print as usual" - we will replace it with actual extruder id (shifted it so we don't lose that information):
+ std::replace(entity_map_it->second.begin(), entity_map_it->second.end(), -1, -correct_extruder_id-1);
+
+ return &(entity_map_it->second);
+}
+
+
} // namespace Slic3r
diff --git a/xs/src/libslic3r/GCode/ToolOrdering.hpp b/xs/src/libslic3r/GCode/ToolOrdering.hpp
index c92806b19..4dcf6516a 100644
--- a/xs/src/libslic3r/GCode/ToolOrdering.hpp
+++ b/xs/src/libslic3r/GCode/ToolOrdering.hpp
@@ -9,38 +9,99 @@ namespace Slic3r {
class Print;
class PrintObject;
+class LayerTools;
-class ToolOrdering
+
+
+// Object of this class holds information about whether an extrusion is printed immediately
+// after a toolchange (as part of infill/perimeter wiping) or not. One extrusion can be a part
+// of several copies - this has to be taken into account.
+class WipingExtrusions
+{
+public:
+ bool is_anything_overridden() const { // if there are no overrides, all the agenda can be skipped - this function can tell us if that's the case
+ return something_overridden;
+ }
+
+ // This is called from GCode::process_layer - see implementation for further comments:
+ const std::vector<int>* get_extruder_overrides(const ExtrusionEntity* entity, int correct_extruder_id, int num_of_copies);
+
+ // This function goes through all infill entities, decides which ones will be used for wiping and
+ // marks them by the extruder id. Returns volume that remains to be wiped on the wipe tower:
+ float mark_wiping_extrusions(const Print& print, unsigned int old_extruder, unsigned int new_extruder, float volume_to_wipe);
+
+ void ensure_perimeters_infills_order(const Print& print);
+
+ bool is_overriddable(const ExtrusionEntityCollection& ee, const PrintConfig& print_config, const PrintObject& object, const PrintRegion& region) const;
+
+ void set_layer_tools_ptr(const LayerTools* lt) { m_layer_tools = lt; }
+
+private:
+ int first_nonsoluble_extruder_on_layer(const PrintConfig& print_config) const;
+ int last_nonsoluble_extruder_on_layer(const PrintConfig& print_config) const;
+
+ // This function is called from mark_wiping_extrusions and sets extruder that it should be printed with (-1 .. as usual)
+ void set_extruder_override(const ExtrusionEntity* entity, unsigned int copy_id, int extruder, unsigned int num_of_copies);
+
+ // Returns true in case that entity is not printed with its usual extruder for a given copy:
+ bool is_entity_overridden(const ExtrusionEntity* entity, int copy_id) const {
+ return (entity_map.find(entity) == entity_map.end() ? false : entity_map.at(entity).at(copy_id) != -1);
+ }
+
+ std::map<const ExtrusionEntity*, std::vector<int>> entity_map; // to keep track of who prints what
+ bool something_overridden = false;
+ const LayerTools* m_layer_tools; // so we know which LayerTools object this belongs to
+};
+
+
+
+class LayerTools
{
public:
- struct LayerTools
- {
- LayerTools(const coordf_t z) :
- print_z(z),
- has_object(false),
- has_support(false),
- has_wipe_tower(false),
- wipe_tower_partitions(0),
- wipe_tower_layer_height(0.) {}
-
- bool operator< (const LayerTools &rhs) const { return print_z < rhs.print_z; }
- bool operator==(const LayerTools &rhs) const { return print_z == rhs.print_z; }
-
- coordf_t print_z;
- bool has_object;
- bool has_support;
- // Zero based extruder IDs, ordered to minimize tool switches.
- std::vector<unsigned int> extruders;
- // Will there be anything extruded on this layer for the wipe tower?
- // Due to the support layers possibly interleaving the object layers,
- // wipe tower will be disabled for some support only layers.
- bool has_wipe_tower;
- // Number of wipe tower partitions to support the required number of tool switches
- // and to support the wipe tower partitions above this one.
- size_t wipe_tower_partitions;
- coordf_t wipe_tower_layer_height;
- };
+ LayerTools(const coordf_t z, const PrintConfig* print_config_ptr = nullptr) :
+ print_z(z),
+ has_object(false),
+ has_support(false),
+ has_wipe_tower(false),
+ wipe_tower_partitions(0),
+ wipe_tower_layer_height(0.) {}
+
+ // Changing these operators to epsilon version can make a problem in cases where support and object layers get close to each other.
+ // In case someone tries to do it, make sure you know what you're doing and test it properly (slice multiple objects at once with supports).
+ bool operator< (const LayerTools &rhs) const { return print_z < rhs.print_z; }
+ bool operator==(const LayerTools &rhs) const { return print_z == rhs.print_z; }
+
+ bool is_extruder_order(unsigned int a, unsigned int b) const;
+
+ coordf_t print_z;
+ bool has_object;
+ bool has_support;
+ // Zero based extruder IDs, ordered to minimize tool switches.
+ std::vector<unsigned int> extruders;
+ // Will there be anything extruded on this layer for the wipe tower?
+ // Due to the support layers possibly interleaving the object layers,
+ // wipe tower will be disabled for some support only layers.
+ bool has_wipe_tower;
+ // Number of wipe tower partitions to support the required number of tool switches
+ // and to support the wipe tower partitions above this one.
+ size_t wipe_tower_partitions;
+ coordf_t wipe_tower_layer_height;
+
+ WipingExtrusions& wiping_extrusions() {
+ m_wiping_extrusions.set_layer_tools_ptr(this);
+ return m_wiping_extrusions;
+ }
+
+private:
+ // This object holds list of extrusion that will be used for extruder wiping
+ WipingExtrusions m_wiping_extrusions;
+};
+
+
+class ToolOrdering
+{
+public:
ToolOrdering() {}
// For the use case when each object is printed separately
@@ -72,7 +133,7 @@ public:
std::vector<LayerTools>::const_iterator begin() const { return m_layer_tools.begin(); }
std::vector<LayerTools>::const_iterator end() const { return m_layer_tools.end(); }
bool empty() const { return m_layer_tools.empty(); }
- const std::vector<LayerTools>& layer_tools() const { return m_layer_tools; }
+ std::vector<LayerTools>& layer_tools() { return m_layer_tools; }
bool has_wipe_tower() const { return ! m_layer_tools.empty() && m_first_printing_extruder != (unsigned int)-1 && m_layer_tools.front().wipe_tower_partitions > 0; }
private:
@@ -80,17 +141,22 @@ private:
void collect_extruders(const PrintObject &object);
void reorder_extruders(unsigned int last_extruder_id);
void fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z);
- void collect_extruder_statistics(bool prime_multi_material);
-
- std::vector<LayerTools> m_layer_tools;
- // First printing extruder, including the multi-material priming sequence.
- unsigned int m_first_printing_extruder = (unsigned int)-1;
- // Final printing extruder.
- unsigned int m_last_printing_extruder = (unsigned int)-1;
- // All extruders, which extrude some material over m_layer_tools.
- std::vector<unsigned int> m_all_printing_extruders;
+ void collect_extruder_statistics(bool prime_multi_material);
+
+ std::vector<LayerTools> m_layer_tools;
+ // First printing extruder, including the multi-material priming sequence.
+ unsigned int m_first_printing_extruder = (unsigned int)-1;
+ // Final printing extruder.
+ unsigned int m_last_printing_extruder = (unsigned int)-1;
+ // All extruders, which extrude some material over m_layer_tools.
+ std::vector<unsigned int> m_all_printing_extruders;
+
+
+ const PrintConfig* m_print_config_ptr = nullptr;
};
+
+
} // namespace SLic3r
#endif /* slic3r_ToolOrdering_hpp_ */
diff --git a/xs/src/libslic3r/GCode/WipeTower.hpp b/xs/src/libslic3r/GCode/WipeTower.hpp
index 36cebeb84..21c10969a 100644
--- a/xs/src/libslic3r/GCode/WipeTower.hpp
+++ b/xs/src/libslic3r/GCode/WipeTower.hpp
@@ -25,18 +25,30 @@ public:
bool operator==(const xy &rhs) const { return x == rhs.x && y == rhs.y; }
bool operator!=(const xy &rhs) const { return x != rhs.x || y != rhs.y; }
- // Rotate the point around given point about given angle (in degrees)
- // shifts the result so that point of rotation is in the middle of the tower
- xy rotate(const xy& origin, float width, float depth, float angle) const {
+ // Rotate the point around center of the wipe tower about given angle (in degrees)
+ xy rotate(float width, float depth, float angle) const {
xy out(0,0);
float temp_x = x - width / 2.f;
float temp_y = y - depth / 2.f;
angle *= M_PI/180.;
- out.x += (temp_x - origin.x) * cos(angle) - (temp_y - origin.y) * sin(angle);
- out.y += (temp_x - origin.x) * sin(angle) + (temp_y - origin.y) * cos(angle);
- return out + origin;
+ out.x += temp_x * cos(angle) - temp_y * sin(angle) + width / 2.f;
+ out.y += temp_x * sin(angle) + temp_y * cos(angle) + depth / 2.f;
+
+ return out;
}
-
+
+ // Rotate the point around origin about given angle in degrees
+ void rotate(float angle) {
+ float temp_x = x * cos(angle) - y * sin(angle);
+ y = x * sin(angle) + y * cos(angle);
+ x = temp_x;
+ }
+
+ void translate(const xy& vect) {
+ x += vect.x;
+ y += vect.y;
+ }
+
float x;
float y;
};
@@ -104,6 +116,9 @@ public:
// This is useful not only for the print time estimation, but also for the control of layer cooling.
float elapsed_time;
+ // Is this a priming extrusion? (If so, the wipe tower rotation & translation will not be applied later)
+ bool priming;
+
// Sum the total length of the extrusion.
float total_extrusion_length_in_plane() {
float e_length = 0.f;
@@ -140,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 5aa6470a2..3ef141829 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -5,7 +5,7 @@ TODO LIST
1. cooling moves - DONE
2. account for perimeter and finish_layer extrusions and subtract it from last wipe - DONE
-3. priming extrusions (last wipe must clear the color)
+3. priming extrusions (last wipe must clear the color) - DONE
4. Peter's wipe tower - layer's are not exactly square
5. Peter's wipe tower - variable width for higher levels
6. Peter's wipe tower - make sure it is not too sparse (apply max_bridge_distance and make last wipe longer)
@@ -17,11 +17,9 @@ TODO LIST
#include <assert.h>
#include <math.h>
-#include <fstream>
#include <iostream>
#include <vector>
#include <numeric>
-#include <algorithm>
#include "Analyzer.hpp"
@@ -69,8 +67,11 @@ public:
return *this;
}
- Writer& set_initial_position(const WipeTower::xy &pos) {
- m_start_pos = WipeTower::xy(pos,0.f,m_y_shift).rotate(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_angle_deg);
+ Writer& set_initial_position(const WipeTower::xy &pos, float width = 0.f, float depth = 0.f, float internal_angle = 0.f) {
+ m_wipe_tower_width = width;
+ m_wipe_tower_depth = depth;
+ m_internal_angle = internal_angle;
+ m_start_pos = WipeTower::xy(pos,0.f,m_y_shift).rotate(m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle);
m_current_pos = pos;
return *this;
}
@@ -82,9 +83,6 @@ public:
Writer& set_extrusion_flow(float flow)
{ m_extrusion_flow = flow; return *this; }
-
- Writer& set_rotation(WipeTower::xy& pos, float width, float depth, float angle)
- { m_wipe_tower_pos = pos; m_wipe_tower_width = width; m_wipe_tower_depth=depth; m_angle_deg = angle; return (*this); }
Writer& set_y_shift(float shift) {
m_current_pos.y -= shift-m_y_shift;
@@ -111,11 +109,12 @@ public:
float y() const { return m_current_pos.y; }
const WipeTower::xy& pos() const { return m_current_pos; }
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_pos, m_wipe_tower_width, m_wipe_tower_depth, m_angle_deg); }
+ 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.
@@ -124,11 +123,13 @@ 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;
- // For rotated wipe tower, transform position to printer coordinates
- WipeTower::xy rotated_current_pos(WipeTower::xy(m_current_pos,0.f,m_y_shift).rotate(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_angle_deg)); // this is where we are
- WipeTower::xy rot(WipeTower::xy(x,y+m_y_shift).rotate(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_angle_deg)); // this is where we want to go
+ // Now do the "internal rotation" with respect to the wipe tower center
+ WipeTower::xy rotated_current_pos(WipeTower::xy(m_current_pos,0.f,m_y_shift).rotate(m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle)); // this is where we are
+ WipeTower::xy rot(WipeTower::xy(x,y+m_y_shift).rotate(m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle)); // this is where we want to go
if (! m_preview_suppressed && e > 0.f && len > 0.) {
// Width of a squished extrusion, corrected for the roundings of the squished extrusions.
@@ -138,7 +139,7 @@ public:
width += m_layer_height * float(1. - M_PI / 4.);
if (m_extrusions.empty() || m_extrusions.back().pos != rotated_current_pos)
m_extrusions.emplace_back(WipeTower::Extrusion(rotated_current_pos, 0, m_current_tool));
- m_extrusions.emplace_back(WipeTower::Extrusion(WipeTower::xy(rot.x, rot.y), width, m_current_tool));
+ m_extrusions.emplace_back(WipeTower::Extrusion(WipeTower::xy(rot.x, rot.y), width, m_current_tool));
}
m_gcode += "G1";
@@ -148,6 +149,7 @@ public:
if (std::abs(rot.y - rotated_current_pos.y) > EPSILON)
m_gcode += set_format_Y(rot.y);
+
if (e != 0.f)
m_gcode += set_format_E(e);
@@ -163,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)
@@ -178,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)
@@ -231,6 +233,17 @@ public:
Writer& retract(float e, float f = 0.f)
{ return load(-e, f); }
+// Loads filament while also moving towards given points in x-axis (x feedrate is limited by cutting the distance short if necessary)
+ Writer& load_move_x_advanced(float farthest_x, float loading_dist, float loading_speed, float max_x_speed = 50.f)
+ {
+ float time = std::abs(loading_dist / loading_speed);
+ float x_speed = std::min(max_x_speed, std::abs(farthest_x - x()) / time);
+ float feedrate = 60.f * std::hypot(x_speed, loading_speed);
+
+ float end_point = x() + (farthest_x > x() ? 1.f : -1.f) * x_speed * time;
+ return extrude_explicit(end_point, y(), loading_dist, feedrate);
+ }
+
// Elevate the extruder head above the current print_z position.
Writer& z_hop(float hop, float f = 0.f)
{
@@ -249,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;
}
@@ -276,12 +289,9 @@ public:
// Set extruder temperature, don't wait by default.
Writer& set_extruder_temp(int temperature, bool wait = false)
{
- if (temperature != current_temp) {
- char buf[128];
- sprintf(buf, "M%d S%d\n", wait ? 109 : 104, temperature);
- m_gcode += buf;
- current_temp = temperature;
- }
+ char buf[128];
+ sprintf(buf, "M%d S%d\n", wait ? 109 : 104, temperature);
+ m_gcode += buf;
return *this;
};
@@ -390,17 +400,16 @@ private:
std::string m_gcode;
std::vector<WipeTower::Extrusion> m_extrusions;
float m_elapsed_time;
- float m_angle_deg = 0.f;
+ float m_internal_angle = 0.f;
float m_y_shift = 0.f;
- WipeTower::xy m_wipe_tower_pos;
float m_wipe_tower_width = 0.f;
float m_wipe_tower_depth = 0.f;
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)
+ std::string set_format_X(float x)
{
char buf[64];
sprintf(buf, " X%.3f", x);
@@ -475,7 +484,6 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
// If false, the last priming are will be large enough to wipe the last extruder sufficiently.
bool last_wipe_inside_wipe_tower)
{
-
this->set_layer(first_layer_height, first_layer_height, tools.size(), true, false);
this->m_current_tool = tools.front();
@@ -485,7 +493,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
// box_coordinates cleaning_box(xy(0.5f, - 1.5f), m_wipe_tower_width, wipe_area);
const float prime_section_width = std::min(240.f / tools.size(), 60.f);
- box_coordinates cleaning_box(xy(5.f, 0.f), prime_section_width, 100.f);
+ box_coordinates cleaning_box(xy(5.f, 0.01f + m_perimeter_width/2.f), prime_section_width, 100.f);
PrusaMultiMaterial::Writer writer(m_layer_height, m_perimeter_width);
writer.set_extrusion_flow(m_extrusion_flow)
@@ -521,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)
@@ -533,7 +544,11 @@ 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;
result.layer_height = this->m_layer_height;
result.gcode = writer.gcode();
@@ -558,7 +573,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
{
for (const auto &b : m_layer_info->tool_changes)
if ( b.new_tool == tool ) {
- wipe_volume = wipe_volumes[b.old_tool][b.new_tool];
+ wipe_volume = b.wipe_volume;
if (tool == m_layer_info->tool_changes.back().new_tool)
last_change_in_layer = true;
wipe_area = b.required_depth * m_layer_info->extra_spacing;
@@ -570,7 +585,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
}
box_coordinates cleaning_box(
- m_wipe_tower_pos + xy(m_perimeter_width / 2.f, m_perimeter_width / 2.f),
+ xy(m_perimeter_width / 2.f, m_perimeter_width / 2.f),
m_wipe_tower_width - m_perimeter_width,
(tool != (unsigned int)(-1) ? /*m_layer_info->depth*/wipe_area+m_depth_traversed-0.5*m_perimeter_width
: m_wipe_tower_depth-m_perimeter_width));
@@ -579,7 +594,6 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
writer.set_extrusion_flow(m_extrusion_flow)
.set_z(m_z_pos)
.set_initial_tool(m_current_tool)
- .set_rotation(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_wipe_tower_rotation_angle)
.set_y_shift(m_y_shift + (tool!=(unsigned int)(-1) && (m_current_shape == SHAPE_REVERSED && !m_peters_wipe_tower) ? m_layer_info->depth - m_layer_info->toolchanges_depth(): 0.f))
.append(";--------------------\n"
"; CP TOOLCHANGE START\n")
@@ -589,7 +603,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
.speed_override(100);
xy initial_position = cleaning_box.ld + WipeTower::xy(0.f,m_depth_traversed);
- writer.set_initial_position(initial_position);
+ writer.set_initial_position(initial_position, m_wipe_tower_width, m_wipe_tower_depth, m_internal_rotation);
// Increase the extruder driver current to allow fast ramming.
writer.set_extruder_trimpot(750);
@@ -602,20 +616,20 @@ 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
writer.set_y_shift(m_y_shift);
if (m_peters_wipe_tower)
- writer.rectangle(m_wipe_tower_pos,m_layer_info->depth + 3*m_perimeter_width,m_wipe_tower_depth);
+ writer.rectangle(WipeTower::xy(0.f, 0.f),m_layer_info->depth + 3*m_perimeter_width,m_wipe_tower_depth);
else {
- writer.rectangle(m_wipe_tower_pos,m_wipe_tower_width, m_layer_info->depth + m_perimeter_width);
+ writer.rectangle(WipeTower::xy(0.f, 0.f),m_wipe_tower_width, m_layer_info->depth + m_perimeter_width);
if (layer_finished()) { // no finish_layer will be called, we must wipe the nozzle
- writer.travel(m_wipe_tower_pos.x + (writer.x()> (m_wipe_tower_pos.x + m_wipe_tower_width) / 2.f ? 0.f : m_wipe_tower_width), writer.y());
+ writer.travel(writer.x()> m_wipe_tower_width / 2.f ? 0.f : m_wipe_tower_width, writer.y());
}
}
}
@@ -628,7 +642,12 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
";------------------\n"
"\n\n");
+ // Ask our writer about how much material was consumed:
+ if (m_current_tool < m_used_filament_length.size())
+ 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;
result.layer_height = this->m_layer_height;
result.gcode = writer.gcode();
@@ -642,7 +661,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(bool sideOnly, float y_offset)
{
const box_coordinates wipeTower_box(
- m_wipe_tower_pos,
+ WipeTower::xy(0.f, 0.f),
m_wipe_tower_width,
m_wipe_tower_depth);
@@ -650,12 +669,11 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(bool sideOnly, flo
writer.set_extrusion_flow(m_extrusion_flow * 1.1f)
.set_z(m_z_pos) // Let the writer know the current Z position as a base for Z-hop.
.set_initial_tool(m_current_tool)
- .set_rotation(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_wipe_tower_rotation_angle)
.append(";-------------------------------------\n"
"; CP WIPE TOWER FIRST LAYER BRIM START\n");
xy initial_position = wipeTower_box.lu - xy(m_perimeter_width * 6.f, 0);
- writer.set_initial_position(initial_position);
+ writer.set_initial_position(initial_position, m_wipe_tower_width, m_wipe_tower_depth, m_internal_rotation);
writer.extrude_explicit(wipeTower_box.ld - xy(m_perimeter_width * 6.f, 0), // Prime the extruder left of the wipe tower.
1.5f * m_extrusion_flow * (wipeTower_box.lu.y - wipeTower_box.ld.y), 2400);
@@ -679,7 +697,11 @@ 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;
result.layer_height = this->m_layer_height;
result.gcode = writer.gcode();
@@ -719,7 +741,7 @@ void WipeTowerPrusaMM::toolchange_Unload(
if (m_layer_info > m_plan.begin() && m_layer_info < m_plan.end() && (m_layer_info-1!=m_plan.begin() || !m_adhesion )) {
// this is y of the center of previous sparse infill border
- float sparse_beginning_y = m_wipe_tower_pos.y;
+ float sparse_beginning_y = 0.f;
if (m_current_shape == SHAPE_REVERSED)
sparse_beginning_y += ((m_layer_info-1)->depth - (m_layer_info-1)->toolchanges_depth())
- ((m_layer_info)->depth-(m_layer_info)->toolchanges_depth()) ;
@@ -737,7 +759,7 @@ void WipeTowerPrusaMM::toolchange_Unload(
for (const auto& tch : m_layer_info->tool_changes) { // let's find this toolchange
if (tch.old_tool == m_current_tool) {
sum_of_depths += tch.ramming_depth;
- float ramming_end_y = m_wipe_tower_pos.y + sum_of_depths;
+ float ramming_end_y = sum_of_depths;
ramming_end_y -= (y_step/m_extra_spacing-m_perimeter_width) / 2.f; // center of final ramming line
// debugging:
@@ -783,51 +805,50 @@ void WipeTowerPrusaMM::toolchange_Unload(
WipeTower::xy end_of_ramming(writer.x(),writer.y());
writer.change_analyzer_line_width(m_perimeter_width); // so the next lines are not affected by ramming_line_width_multiplier
- // Pull the filament end to the BEGINNING of the cooling tube while still moving the print head
- float oldx = writer.x();
- float turning_point = (!m_left_to_right ? std::max(xl,oldx-15.f) : std::min(xr,oldx+15.f) ); // so it's not too far
- float xdist = std::abs(oldx-turning_point);
- float edist = -(m_cooling_tube_retraction+m_cooling_tube_length/2.f-42);
-
+ // Retraction:
+ float old_x = writer.x();
+ 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(turning_point,-15 , 60.f * std::hypot(xdist,15)/15 * 83 ) // fixed speed after ramming
- .load_move_x(oldx ,edist , 60.f * std::hypot(xdist,edist)/std::abs(edist) * m_filpar[m_current_tool].unloading_speed )
- .load_move_x(turning_point,-15 , 60.f * std::hypot(xdist,15)/15 * m_filpar[m_current_tool].unloading_speed*0.55f )
- .load_move_x(oldx ,-12 , 60.f * std::hypot(xdist,12)/12 * m_filpar[m_current_tool].unloading_speed*0.35f )
+ .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*/
.resume_preview();
-
- if (new_temperature != 0) // 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;
+ }
-// cooling:
- writer.suppress_preview();
- writer.travel(writer.x(), writer.y() + y_step);
- const float start_x = writer.x();
- turning_point = ( xr-start_x > start_x-xl ? xr : xl );
- const float max_x_dist = 2*std::abs(start_x-turning_point);
- const unsigned int N = 4 + std::max(0.f, (m_filpar[m_current_tool].cooling_time-14)/3);
- float time = m_filpar[m_current_tool].cooling_time / float(N);
-
- i = 0;
- while (i<N) {
- const float speed = std::min(3.4,2.2 + i*0.3 + (i==0 ? 0 : 0.3)); // mm per second: 2.2, 2.8, 3.1, 3.4, 3.4, 3.4, ...
- const float e_dist = std::min(speed * time,2*m_cooling_tube_length); // distance to travel
-
- // this move is the last one at this speed or someone set tube_length to zero
- if (speed * time < 2*m_cooling_tube_length || m_cooling_tube_length<WT_EPSILON) {
- ++i;
- time = m_filpar[m_current_tool].cooling_time / float(N);
- }
- else
- time -= e_dist / speed; // subtract time this part will really take
-
- // as for x, we will make sure the feedrate is at most 2000
- float x_dist = (turning_point - WT_EPSILON < xl ? -1.f : 1.f) * std::min(e_dist * (float)sqrt(pow(2000 / (60 * speed), 2) - 1),max_x_dist);
- const float feedrate = std::hypot(e_dist, x_dist) / ((e_dist / speed) / 60.f);
- writer.cool(start_x+x_dist/2.f,start_x,e_dist/2.f,-e_dist/2.f, feedrate);
- }
+ // Cooling:
+ const int& number_of_moves = m_filpar[m_current_tool].cooling_moves;
+ if (number_of_moves > 0) {
+ const float& initial_speed = m_filpar[m_current_tool].cooling_initial_speed;
+ const float& final_speed = m_filpar[m_current_tool].cooling_final_speed;
+
+ float speed_inc = (final_speed - initial_speed) / (2.f * number_of_moves - 1.f);
+
+ writer.suppress_preview()
+ .travel(writer.x(), writer.y() + y_step);
+ old_x = writer.x();
+ turning_point = xr-old_x > old_x-xl ? xr : xl;
+ for (int i=0; i<number_of_moves; ++i) {
+ float speed = initial_speed + speed_inc * 2*i;
+ writer.load_move_x_advanced(turning_point, m_cooling_tube_length, speed);
+ speed += speed_inc;
+ writer.load_move_x_advanced(old_x, -m_cooling_tube_length, speed);
+ }
+ }
- // let's wait is necessary
+ // let's wait is necessary:
writer.wait(m_filpar[m_current_tool].delay);
// we should be at the beginning of the cooling tube again - let's move to parking position:
writer.retract(-m_cooling_tube_length/2.f+m_parking_pos_retraction-m_cooling_tube_retraction, 2000);
@@ -846,6 +867,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) {
@@ -871,16 +895,21 @@ void WipeTowerPrusaMM::toolchange_Load(
float oldx = writer.x(); // the nozzle is in place to do the first wiping moves, we will remember the position
// Load the filament while moving left / right, so the excess material will not create a blob at a single position.
- float loading_speed = m_filpar[m_current_tool].loading_speed; // mm/s in e axis
float turning_point = ( oldx-xl < xr-oldx ? xr : xl );
- float dist = std::abs(oldx-turning_point);
- float edist = m_parking_pos_retraction-50-2; // loading is 2mm shorter that previous retraction, 50mm reserved for acceleration/deceleration
- writer.append("; CP TOOLCHANGE LOAD\n")
+ float edist = m_parking_pos_retraction+m_extra_loading_move;
+
+ writer.append("; CP TOOLCHANGE LOAD\n")
.suppress_preview()
- .load_move_x(turning_point, 20, 60*std::hypot(dist,20.f)/20.f * loading_speed*0.3f) // Acceleration
- .load_move_x(oldx,edist,60*std::hypot(dist,edist)/edist * loading_speed) // Fast phase
- .load_move_x(turning_point, 20, 60*std::hypot(dist,20.f)/20.f * loading_speed*0.3f) // Slowing down
- .load_move_x(oldx, 10, 60*std::hypot(dist,10.f)/10.f * loading_speed*0.1f) // Super slow
+ /*.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(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();
// Reset the extruder current to the normal value.
@@ -903,7 +932,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.
@@ -918,7 +946,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) {
@@ -927,7 +954,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);
@@ -952,7 +979,7 @@ void WipeTowerPrusaMM::toolchange_Wipe(
if (m_layer_info != m_plan.end() && m_current_tool != m_layer_info->tool_changes.back().new_tool) {
m_left_to_right = !m_left_to_right;
writer.travel(writer.x(), writer.y() - dy)
- .travel(m_wipe_tower_pos.x + (m_left_to_right ? m_wipe_tower_width : 0.f), writer.y());
+ .travel(m_left_to_right ? m_wipe_tower_width : 0.f, writer.y());
}
writer.set_extrusion_flow(m_extrusion_flow); // Reset the extrusion flow.
@@ -971,7 +998,6 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer()
writer.set_extrusion_flow(m_extrusion_flow)
.set_z(m_z_pos)
.set_initial_tool(m_current_tool)
- .set_rotation(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_wipe_tower_rotation_angle)
.set_y_shift(m_y_shift - (m_current_shape == SHAPE_REVERSED && !m_peters_wipe_tower ? m_layer_info->toolchanges_depth() : 0.f))
.append(";--------------------\n"
"; CP EMPTY GRID START\n")
@@ -980,14 +1006,12 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer()
// Slow down on the 1st layer.
float speed_factor = m_is_first_layer ? 0.5f : 1.f;
float current_depth = m_layer_info->depth - m_layer_info->toolchanges_depth();
- box_coordinates fill_box(m_wipe_tower_pos + xy(m_perimeter_width, m_depth_traversed + m_perimeter_width),
+ box_coordinates fill_box(xy(m_perimeter_width, m_depth_traversed + m_perimeter_width),
m_wipe_tower_width - 2 * m_perimeter_width, current_depth-m_perimeter_width);
- if (m_left_to_right) // so there is never a diagonal travel
- writer.set_initial_position(fill_box.ru);
- else
- writer.set_initial_position(fill_box.lu);
+ writer.set_initial_position((m_left_to_right ? fill_box.ru : fill_box.lu), // so there is never a diagonal travel
+ m_wipe_tower_width, m_wipe_tower_depth, m_internal_rotation);
box_coordinates box = fill_box;
for (int i=0;i<2;++i) {
@@ -1045,7 +1069,12 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer()
m_depth_traversed = m_wipe_tower_depth-m_perimeter_width;
+ // Ask our writer about how much material was consumed.
+ if (m_current_tool < m_used_filament_length.size())
+ 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;
result.layer_height = this->m_layer_height;
result.gcode = writer.gcode();
@@ -1057,7 +1086,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer()
}
// Appends a toolchange into m_plan and calculates neccessary depth of the corresponding box
-void WipeTowerPrusaMM::plan_toolchange(float z_par, float layer_height_par, unsigned int old_tool, unsigned int new_tool,bool brim)
+void WipeTowerPrusaMM::plan_toolchange(float z_par, float layer_height_par, unsigned int old_tool, unsigned int new_tool, bool brim, float wipe_volume)
{
assert(m_plan.back().z <= z_par + WT_EPSILON ); // refuses to add a layer below the last one
@@ -1082,13 +1111,13 @@ void WipeTowerPrusaMM::plan_toolchange(float z_par, float layer_height_par, unsi
float ramming_depth = depth;
length_to_extrude = width*((length_to_extrude / width)-int(length_to_extrude / width)) - width;
float first_wipe_line = -length_to_extrude;
- length_to_extrude += volume_to_length(wipe_volumes[old_tool][new_tool], m_perimeter_width, layer_height_par);
+ length_to_extrude += volume_to_length(wipe_volume, m_perimeter_width, layer_height_par);
length_to_extrude = std::max(length_to_extrude,0.f);
depth += (int(length_to_extrude / width) + 1) * m_perimeter_width;
depth *= m_extra_spacing;
- m_plan.back().tool_changes.push_back(WipeTowerInfo::ToolChange(old_tool, new_tool, depth, ramming_depth,first_wipe_line));
+ m_plan.back().tool_changes.push_back(WipeTowerInfo::ToolChange(old_tool, new_tool, depth, ramming_depth, first_wipe_line, wipe_volume));
}
@@ -1128,7 +1157,7 @@ void WipeTowerPrusaMM::save_on_last_wipe()
float width = m_wipe_tower_width - 3*m_perimeter_width; // width we draw into
float length_to_save = 2*(m_wipe_tower_width+m_wipe_tower_depth) + (!layer_finished() ? finish_layer().total_extrusion_length_in_plane() : 0.f);
- float length_to_wipe = volume_to_length(wipe_volumes[m_layer_info->tool_changes.back().old_tool][m_layer_info->tool_changes.back().new_tool],
+ float length_to_wipe = volume_to_length(m_layer_info->tool_changes.back().wipe_volume,
m_perimeter_width,m_layer_info->height) - m_layer_info->tool_changes.back().first_wipe_line - length_to_save;
length_to_wipe = std::max(length_to_wipe,0.f);
@@ -1139,13 +1168,13 @@ void WipeTowerPrusaMM::save_on_last_wipe()
}
}
-
// Processes vector m_plan and calls respective functions to generate G-code for the wipe tower
// Resulting ToolChangeResults are appended into vector "result"
void WipeTowerPrusaMM::generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &result)
{
if (m_plan.empty())
- return;
+
+ return;
m_extra_spacing = 1.f;
@@ -1160,17 +1189,17 @@ 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)
{
set_layer(layer.z,layer.height,0,layer.z == m_plan.front().z,layer.z == m_plan.back().z);
-
-
if (m_peters_wipe_tower)
- m_wipe_tower_rotation_angle += 90.f;
+ m_internal_rotation += 90.f;
else
- m_wipe_tower_rotation_angle += 180.f;
+ m_internal_rotation += 180.f;
if (!m_peters_wipe_tower && m_layer_info->depth < m_wipe_tower_depth - m_perimeter_width)
m_y_shift = (m_wipe_tower_depth-m_layer_info->depth-m_perimeter_width)/2.f;
@@ -1191,7 +1220,7 @@ void WipeTowerPrusaMM::generate(std::vector<std::vector<WipeTower::ToolChangeRes
last_toolchange.gcode += buf;
}
last_toolchange.gcode += finish_layer_toolchange.gcode;
- last_toolchange.extrusions.insert(last_toolchange.extrusions.end(),finish_layer_toolchange.extrusions.begin(),finish_layer_toolchange.extrusions.end());
+ last_toolchange.extrusions.insert(last_toolchange.extrusions.end(), finish_layer_toolchange.extrusions.begin(), finish_layer_toolchange.extrusions.end());
last_toolchange.end_pos = finish_layer_toolchange.end_pos;
}
else
@@ -1203,9 +1232,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;
@@ -1229,9 +1255,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();
-
}
-
-
}; // namespace Slic3r
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
index b7c721128..06625d189 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
@@ -5,6 +5,7 @@
#include <string>
#include <sstream>
#include <utility>
+#include <algorithm>
#include "WipeTower.hpp"
@@ -43,9 +44,9 @@ public:
// width -- width of wipe tower in mm ( default 60 mm - leave as it is )
// wipe_area -- space available for one toolchange in mm
WipeTowerPrusaMM(float x, float y, float width, float rotation_angle, float cooling_tube_retraction,
- float cooling_tube_length, float parking_pos_retraction, float bridging, const std::vector<float>& wiping_matrix,
- unsigned int initial_tool) :
- m_wipe_tower_pos(x, y),
+ 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_width(width),
m_wipe_tower_rotation_angle(rotation_angle),
m_y_shift(0.f),
@@ -54,20 +55,19 @@ public:
m_cooling_tube_retraction(cooling_tube_retraction),
m_cooling_tube_length(cooling_tube_length),
m_parking_pos_retraction(parking_pos_retraction),
+ m_extra_loading_move(extra_loading_move),
m_bridging(bridging),
- m_current_tool(initial_tool)
- {
- unsigned int number_of_extruders = (unsigned int)(sqrt(wiping_matrix.size())+WT_EPSILON);
- for (unsigned int i = 0; i<number_of_extruders; ++i)
- wipe_volumes.push_back(std::vector<float>(wiping_matrix.begin()+i*number_of_extruders,wiping_matrix.begin()+(i+1)*number_of_extruders));
- }
+ m_current_tool(initial_tool),
+ wipe_volumes(wiping_matrix)
+ {}
virtual ~WipeTowerPrusaMM() {}
// 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, 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,9 +76,13 @@ 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_time = 14.f; // let's fix it for now, cooling moves will be reworked for 1.41 anyway
+ m_filpar[idx].cooling_moves = cooling_moves;
+ m_filpar[idx].cooling_initial_speed = cooling_initial_speed;
+ m_filpar[idx].cooling_final_speed = cooling_final_speed;
m_filpar[idx].nozzle_diameter = nozzle_diameter; // to be used in future with (non-single) multiextruder MM
m_perimeter_width = nozzle_diameter * Width_To_Nozzle_Ratio; // all extruders are now assumed to have the same diameter
@@ -90,16 +94,20 @@ 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
}
// Appends into internal structure m_plan containing info about the future wipe tower
// to be used before building begins. The entries must be added ordered in z.
- void plan_toolchange(float z_par, float layer_height_par, unsigned int old_tool, unsigned int new_tool, bool brim);
+ void plan_toolchange(float z_par, float layer_height_par, unsigned int old_tool, unsigned int new_tool, bool brim, float wipe_volume = 0.f);
// Iterates through prepared m_plan, generates ToolChangeResults and appends them to "result"
void generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &result);
+ float get_depth() const { return m_wipe_tower_depth; }
+
// Switch to a next layer.
@@ -166,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();
@@ -187,16 +198,19 @@ private:
float m_wipe_tower_width; // Width of the wipe tower.
float m_wipe_tower_depth = 0.f; // Depth of the wipe tower
float m_wipe_tower_rotation_angle = 0.f; // Wipe tower rotation angle in degrees (with respect to x axis)
+ float m_internal_rotation = 0.f;
float m_y_shift = 0.f; // y shift passed to writer
float m_z_pos = 0.f; // Current Z position.
float m_layer_height = 0.f; // Current layer height.
size_t m_max_color_changes = 0; // Maximum number of color changes per layer.
bool m_is_first_layer = false;// Is this the 1st layer of the print? If so, print the brim around the waste tower.
+ int m_old_temperature = -1; // To keep track of what was the last temp that we set (so we don't issue the command when not neccessary)
// G-code generator parameters.
float m_cooling_tube_retraction = 0.f;
float m_cooling_tube_length = 0.f;
float m_parking_pos_retraction = 0.f;
+ float m_extra_loading_move = 0.f;
float m_bridging = 0.f;
bool m_adhesion = true;
@@ -209,9 +223,13 @@ 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 ;
- float cooling_time = 0.f;
+ int cooling_moves = 0;
+ float cooling_initial_speed = 0.f;
+ float cooling_final_speed = 0.f;
float ramming_line_width_multiplicator = 0.f;
float ramming_step_multiplicator = 0.f;
std::vector<float> ramming_speed;
@@ -229,14 +247,13 @@ private:
bool m_print_brim = true;
// A fill-in direction (positive Y, negative Y) alternates with each layer.
wipe_shape m_current_shape = SHAPE_NORMAL;
- unsigned int m_current_tool;
- std::vector<std::vector<float>> wipe_volumes;
+ unsigned int m_current_tool = 0;
+ const std::vector<std::vector<float>> wipe_volumes;
float m_depth_traversed = 0.f; // Current y position at the wipe tower.
bool m_left_to_right = true;
float m_extra_spacing = 1.f;
-
// Calculates extrusion flow needed to produce required line width for given layer height
float extrusion_flow(float layer_height = -1.f) const // negative layer_height - return current m_extrusion_flow
{
@@ -247,7 +264,7 @@ private:
// Calculates length of extrusion line to extrude given volume
float volume_to_length(float volume, float line_width, float layer_height) const {
- return volume / (layer_height * (line_width - layer_height * (1. - M_PI / 4.)));
+ return std::max(0., volume / (layer_height * (line_width - layer_height * (1. - M_PI / 4.))));
}
// Calculates depth for all layers and propagates them downwards
@@ -300,8 +317,9 @@ private:
float required_depth;
float ramming_depth;
float first_wipe_line;
- ToolChange(unsigned int old,unsigned int newtool,float depth=0.f,float ramming_depth=0.f,float fwl=0.f)
- : old_tool{old}, new_tool{newtool}, required_depth{depth}, ramming_depth{ramming_depth},first_wipe_line{fwl} {}
+ float wipe_volume;
+ ToolChange(unsigned int old, unsigned int newtool, float depth=0.f, float ramming_depth=0.f, float fwl=0.f, float wv=0.f)
+ : old_tool{old}, new_tool{newtool}, required_depth{depth}, ramming_depth{ramming_depth}, first_wipe_line{fwl}, wipe_volume{wv} {}
};
float z; // z position of the layer
float height; // layer height
@@ -318,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/GCodeReader.cpp b/xs/src/libslic3r/GCodeReader.cpp
index 965b7ef8e..79b6ed970 100644
--- a/xs/src/libslic3r/GCodeReader.cpp
+++ b/xs/src/libslic3r/GCodeReader.cpp
@@ -114,6 +114,28 @@ void GCodeReader::parse_file(const std::string &file, callback_t callback)
this->parse_line(line, callback);
}
+bool GCodeReader::GCodeLine::has(char axis) const
+{
+ const char *c = m_raw.c_str();
+ // Skip the whitespaces.
+ c = skip_whitespaces(c);
+ // Skip the command.
+ c = skip_word(c);
+ // Up to the end of line or comment.
+ while (! is_end_of_gcode_line(*c)) {
+ // Skip whitespaces.
+ c = skip_whitespaces(c);
+ if (is_end_of_gcode_line(*c))
+ break;
+ // Check the name of the axis.
+ if (*c == axis)
+ return true;
+ // Skip the rest of the word.
+ c = skip_word(c);
+ }
+ return false;
+}
+
bool GCodeReader::GCodeLine::has_value(char axis, float &value) const
{
const char *c = m_raw.c_str();
diff --git a/xs/src/libslic3r/GCodeReader.hpp b/xs/src/libslic3r/GCodeReader.hpp
index 102cbd27a..84ed89a7c 100644
--- a/xs/src/libslic3r/GCodeReader.hpp
+++ b/xs/src/libslic3r/GCodeReader.hpp
@@ -27,6 +27,7 @@ public:
bool has(Axis axis) const { return (m_mask & (1 << int(axis))) != 0; }
float value(Axis axis) const { return m_axis[axis]; }
+ bool has(char axis) const;
bool has_value(char axis, float &value) const;
float new_Z(const GCodeReader &reader) const { return this->has(Z) ? this->z() : reader.z(); }
float new_E(const GCodeReader &reader) const { return this->has(E) ? this->e() : reader.e(); }
diff --git a/xs/src/libslic3r/GCodeSender.cpp b/xs/src/libslic3r/GCodeSender.cpp
index c3530e00f..0988091ce 100644
--- a/xs/src/libslic3r/GCodeSender.cpp
+++ b/xs/src/libslic3r/GCodeSender.cpp
@@ -2,6 +2,7 @@
#include <iostream>
#include <istream>
#include <string>
+#include <thread>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/trim.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
@@ -568,16 +569,12 @@ GCodeSender::set_DTR(bool on)
void
GCodeSender::reset()
{
- this->set_DTR(false);
- boost::this_thread::sleep(boost::posix_time::milliseconds(200));
- this->set_DTR(true);
- boost::this_thread::sleep(boost::posix_time::milliseconds(200));
- this->set_DTR(false);
- boost::this_thread::sleep(boost::posix_time::milliseconds(1000));
- {
- boost::lock_guard<boost::mutex> l(this->queue_mutex);
- this->can_send = true;
- }
+ set_DTR(false);
+ std::this_thread::sleep_for(std::chrono::milliseconds(200));
+ set_DTR(true);
+ std::this_thread::sleep_for(std::chrono::milliseconds(200));
+ set_DTR(false);
+ std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
} // namespace Slic3r
diff --git a/xs/src/libslic3r/GCodeTimeEstimator.cpp b/xs/src/libslic3r/GCodeTimeEstimator.cpp
index 176159ff5..7471367fe 100644
--- a/xs/src/libslic3r/GCodeTimeEstimator.cpp
+++ b/xs/src/libslic3r/GCodeTimeEstimator.cpp
@@ -4,15 +4,20 @@
#include <Shiny/Shiny.h>
+#include <boost/nowide/fstream.hpp>
+#include <boost/nowide/cstdio.hpp>
+#include <boost/algorithm/string/predicate.hpp>
+
static const float MMMIN_TO_MMSEC = 1.0f / 60.0f;
static const float MILLISEC_TO_SEC = 0.001f;
static const float INCHES_TO_MM = 25.4f;
+
static const float DEFAULT_FEEDRATE = 1500.0f; // from Prusa Firmware (Marlin_main.cpp)
static const float DEFAULT_ACCELERATION = 1500.0f; // Prusa Firmware 1_75mm_MK2
static const float DEFAULT_RETRACT_ACCELERATION = 1500.0f; // Prusa Firmware 1_75mm_MK2
static const float DEFAULT_AXIS_MAX_FEEDRATE[] = { 500.0f, 500.0f, 12.0f, 120.0f }; // Prusa Firmware 1_75mm_MK2
static const float DEFAULT_AXIS_MAX_ACCELERATION[] = { 9000.0f, 9000.0f, 500.0f, 10000.0f }; // Prusa Firmware 1_75mm_MK2
-static const float DEFAULT_AXIS_MAX_JERK[] = { 10.0f, 10.0f, 0.2f, 2.5f }; // from Prusa Firmware (Configuration.h)
+static const float DEFAULT_AXIS_MAX_JERK[] = { 10.0f, 10.0f, 0.4f, 2.5f }; // from Prusa Firmware (Configuration.h)
static const float DEFAULT_MINIMUM_FEEDRATE = 0.0f; // from Prusa Firmware (Configuration_adv.h)
static const float DEFAULT_MINIMUM_TRAVEL_FEEDRATE = 0.0f; // from Prusa Firmware (Configuration_adv.h)
static const float DEFAULT_EXTRUDE_FACTOR_OVERRIDE_PERCENTAGE = 1.0f; // 100 percent
@@ -73,6 +78,10 @@ namespace Slic3r {
return ::sqrt(value);
}
+ GCodeTimeEstimator::Block::Block()
+ {
+ }
+
float GCodeTimeEstimator::Block::move_length() const
{
float length = ::sqrt(sqr(delta_pos[X]) + sqr(delta_pos[Y]) + sqr(delta_pos[Z]));
@@ -159,12 +168,51 @@ namespace Slic3r {
}
#endif // ENABLE_MOVE_STATS
- GCodeTimeEstimator::GCodeTimeEstimator()
+ 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)
{
reset();
set_default();
}
+ void GCodeTimeEstimator::add_gcode_line(const std::string& gcode_line)
+ {
+ PROFILE_FUNC();
+ _parser.parse_line(gcode_line,
+ [this](GCodeReader &reader, const GCodeReader::GCodeLine &line)
+ { this->_process_gcode_line(reader, line); });
+ }
+
+ void GCodeTimeEstimator::add_gcode_block(const char *ptr)
+ {
+ PROFILE_FUNC();
+ GCodeReader::GCodeLine gline;
+ auto action = [this](GCodeReader &reader, const GCodeReader::GCodeLine &line)
+ { this->_process_gcode_line(reader, line); };
+ for (; *ptr != 0;) {
+ gline.reset();
+ ptr = _parser.parse_line(ptr, gline, action);
+ }
+ }
+
+ void GCodeTimeEstimator::calculate_time(bool start_from_beginning)
+ {
+ PROFILE_FUNC();
+ if (start_from_beginning)
+ {
+ _reset_time();
+ _last_st_synchronized_block_id = -1;
+ }
+ _calculate_time();
+
+#if ENABLE_MOVE_STATS
+ _log_moves_stats();
+#endif // ENABLE_MOVE_STATS
+ }
+
void GCodeTimeEstimator::calculate_time_from_text(const std::string& gcode)
{
reset();
@@ -178,9 +226,6 @@ namespace Slic3r {
#if ENABLE_MOVE_STATS
_log_moves_stats();
#endif // ENABLE_MOVE_STATS
-
- _reset_blocks();
- _reset();
}
void GCodeTimeEstimator::calculate_time_from_file(const std::string& file)
@@ -193,9 +238,6 @@ namespace Slic3r {
#if ENABLE_MOVE_STATS
_log_moves_stats();
#endif // ENABLE_MOVE_STATS
-
- _reset_blocks();
- _reset();
}
void GCodeTimeEstimator::calculate_time_from_lines(const std::vector<std::string>& gcode_lines)
@@ -211,42 +253,126 @@ namespace Slic3r {
#if ENABLE_MOVE_STATS
_log_moves_stats();
#endif // ENABLE_MOVE_STATS
-
- _reset_blocks();
- _reset();
}
- void GCodeTimeEstimator::add_gcode_line(const std::string& gcode_line)
+ bool GCodeTimeEstimator::post_process_remaining_times(const std::string& filename, float interval)
{
- PROFILE_FUNC();
- _parser.parse_line(gcode_line,
- [this](GCodeReader &reader, const GCodeReader::GCodeLine &line)
- { this->_process_gcode_line(reader, line); });
- }
+ boost::nowide::ifstream in(filename);
+ if (!in.good())
+ throw std::runtime_error(std::string("Remaining times export failed.\nCannot open file for reading.\n"));
- void GCodeTimeEstimator::add_gcode_block(const char *ptr)
- {
- PROFILE_FUNC();
- GCodeReader::GCodeLine gline;
- auto action = [this](GCodeReader &reader, const GCodeReader::GCodeLine &line)
- { this->_process_gcode_line(reader, line); };
- for (; *ptr != 0;) {
- gline.reset();
- ptr = _parser.parse_line(ptr, gline, action);
+ std::string path_tmp = filename + ".times";
+
+ FILE* out = boost::nowide::fopen(path_tmp.c_str(), "wb");
+ if (out == nullptr)
+ throw std::runtime_error(std::string("Remaining times export failed.\nCannot open file for writing.\n"));
+
+ std::string time_mask;
+ switch (_mode)
+ {
+ default:
+ case Normal:
+ {
+ time_mask = "M73 P%s R%s\n";
+ break;
+ }
+ case Silent:
+ {
+ time_mask = "M73 Q%s S%s\n";
+ break;
+ }
}
- }
- void GCodeTimeEstimator::calculate_time()
- {
- PROFILE_FUNC();
- _calculate_time();
+ unsigned int g1_lines_count = 0;
+ float last_recorded_time = 0.0f;
+ std::string gcode_line;
+ // buffer line to export only when greater than 64K to reduce writing calls
+ std::string export_line;
+ char time_line[64];
+ while (std::getline(in, gcode_line))
+ {
+ if (!in.good())
+ {
+ fclose(out);
+ throw std::runtime_error(std::string("Remaining times export failed.\nError while reading from file.\n"));
+ }
-#if ENABLE_MOVE_STATS
- _log_moves_stats();
-#endif // ENABLE_MOVE_STATS
+ // 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";
- _reset_blocks();
- _reset();
+ // add remaining time lines where needed
+ _parser.parse_line(gcode_line,
+ [this, &g1_lines_count, &last_recorded_time, &time_line, &gcode_line, time_mask, interval](GCodeReader& reader, const GCodeReader::GCodeLine& line)
+ {
+ if (line.cmd_is("G1"))
+ {
+ ++g1_lines_count;
+
+ if (!line.has_e())
+ return;
+
+ G1LineIdToBlockIdMap::const_iterator it = _g1_line_ids.find(g1_lines_count);
+ if ((it != _g1_line_ids.end()) && (it->second < (unsigned int)_blocks.size()))
+ {
+ const Block& block = _blocks[it->second];
+ if (block.elapsed_time != -1.0f)
+ {
+ float block_remaining_time = _time - block.elapsed_time;
+ if (std::abs(last_recorded_time - block_remaining_time) > interval)
+ {
+ sprintf(time_line, time_mask.c_str(), std::to_string((int)(100.0f * block.elapsed_time / _time)).c_str(), _get_time_minutes(block_remaining_time).c_str());
+ gcode_line += time_line;
+
+ last_recorded_time = block_remaining_time;
+ }
+ }
+ }
+ }
+ });
+
+ export_line += gcode_line;
+ if (export_line.length() > 65535)
+ {
+ fwrite((const void*)export_line.c_str(), 1, export_line.length(), out);
+ if (ferror(out))
+ {
+ in.close();
+ fclose(out);
+ boost::nowide::remove(path_tmp.c_str());
+ throw std::runtime_error(std::string("Remaining times export failed.\nIs the disk full?\n"));
+ }
+ export_line.clear();
+ }
+ }
+
+ if (export_line.length() > 0)
+ {
+ fwrite((const void*)export_line.c_str(), 1, export_line.length(), out);
+ if (ferror(out))
+ {
+ in.close();
+ fclose(out);
+ boost::nowide::remove(path_tmp.c_str());
+ throw std::runtime_error(std::string("Remaining times export failed.\nIs the disk full?\n"));
+ }
+ }
+
+ fclose(out);
+ in.close();
+
+ boost::nowide::remove(filename.c_str());
+ if (boost::nowide::rename(path_tmp.c_str(), filename.c_str()) != 0)
+ throw std::runtime_error(std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + filename + '\n' +
+ "Is " + path_tmp + " locked?" + '\n');
+
+ return true;
}
void GCodeTimeEstimator::set_axis_position(EAxis axis, float position)
@@ -301,7 +427,10 @@ namespace Slic3r {
void GCodeTimeEstimator::set_acceleration(float acceleration_mm_sec2)
{
- _state.acceleration = acceleration_mm_sec2;
+ _state.acceleration = (_state.max_acceleration == 0) ?
+ acceleration_mm_sec2 :
+ // Clamp the acceleration with the maximum.
+ std::min(_state.max_acceleration, acceleration_mm_sec2);
}
float GCodeTimeEstimator::get_acceleration() const
@@ -309,6 +438,18 @@ namespace Slic3r {
return _state.acceleration;
}
+ void GCodeTimeEstimator::set_max_acceleration(float acceleration_mm_sec2)
+ {
+ _state.max_acceleration = acceleration_mm_sec2;
+ if (acceleration_mm_sec2 > 0)
+ _state.acceleration = acceleration_mm_sec2;
+ }
+
+ float GCodeTimeEstimator::get_max_acceleration() const
+ {
+ return _state.max_acceleration;
+ }
+
void GCodeTimeEstimator::set_retract_acceleration(float acceleration_mm_sec2)
{
_state.retract_acceleration = acceleration_mm_sec2;
@@ -339,6 +480,40 @@ namespace Slic3r {
return _state.minimum_travel_feedrate;
}
+ void GCodeTimeEstimator::set_filament_load_times(const std::vector<double> &filament_load_times)
+ {
+ _state.filament_load_times.clear();
+ for (double t : filament_load_times)
+ _state.filament_load_times.push_back(t);
+ }
+
+ void GCodeTimeEstimator::set_filament_unload_times(const std::vector<double> &filament_unload_times)
+ {
+ _state.filament_unload_times.clear();
+ for (double t : filament_unload_times)
+ _state.filament_unload_times.push_back(t);
+ }
+
+ float GCodeTimeEstimator::get_filament_load_time(unsigned int id_extruder)
+ {
+ return
+ (_state.filament_load_times.empty() || id_extruder == _state.extruder_id_unloaded) ?
+ 0 :
+ (_state.filament_load_times.size() <= id_extruder) ?
+ _state.filament_load_times.front() :
+ _state.filament_load_times[id_extruder];
+ }
+
+ float GCodeTimeEstimator::get_filament_unload_time(unsigned int id_extruder)
+ {
+ return
+ (_state.filament_unload_times.empty() || id_extruder == _state.extruder_id_unloaded) ?
+ 0 :
+ (_state.filament_unload_times.size() <= id_extruder) ?
+ _state.filament_unload_times.front() :
+ _state.filament_unload_times[id_extruder];
+ }
+
void GCodeTimeEstimator::set_extrude_factor_override_percentage(float percentage)
{
_state.extrude_factor_override_percentage = percentage;
@@ -356,6 +531,7 @@ namespace Slic3r {
GCodeFlavor GCodeTimeEstimator::get_dialect() const
{
+ PROFILE_FUNC();
return _state.dialect;
}
@@ -389,8 +565,41 @@ namespace Slic3r {
return _state.e_local_positioning_type;
}
+ int GCodeTimeEstimator::get_g1_line_id() const
+ {
+ return _state.g1_line_id;
+ }
+
+ void GCodeTimeEstimator::increment_g1_line_id()
+ {
+ ++_state.g1_line_id;
+ }
+
+ void GCodeTimeEstimator::reset_g1_line_id()
+ {
+ _state.g1_line_id = 0;
+ }
+
+ void GCodeTimeEstimator::set_extruder_id(unsigned int id)
+ {
+ _state.extruder_id = id;
+ }
+
+ unsigned int GCodeTimeEstimator::get_extruder_id() const
+ {
+ return _state.extruder_id;
+ }
+
+ void GCodeTimeEstimator::reset_extruder_id()
+ {
+ // Set the initial extruder ID to unknown. For the multi-material setup it means
+ // that all the filaments are parked in the MMU and no filament is loaded yet.
+ _state.extruder_id = _state.extruder_id_unloaded;
+ }
+
void GCodeTimeEstimator::add_additional_time(float timeSec)
{
+ PROFILE_FUNC();
_state.additional_time += timeSec;
}
@@ -412,12 +621,15 @@ namespace Slic3r {
set_e_local_positioning_type(Absolute);
set_feedrate(DEFAULT_FEEDRATE);
+ // Setting the maximum acceleration to zero means that the there is no limit and the G-code
+ // is allowed to set excessive values.
+ set_max_acceleration(0);
set_acceleration(DEFAULT_ACCELERATION);
set_retract_acceleration(DEFAULT_RETRACT_ACCELERATION);
set_minimum_feedrate(DEFAULT_MINIMUM_FEEDRATE);
set_minimum_travel_feedrate(DEFAULT_MINIMUM_TRAVEL_FEEDRATE);
set_extrude_factor_override_percentage(DEFAULT_EXTRUDE_FACTOR_OVERRIDE_PERCENTAGE);
-
+
for (unsigned char a = X; a < Num_Axis; ++a)
{
EAxis axis = (EAxis)a;
@@ -425,11 +637,14 @@ namespace Slic3r {
set_axis_max_acceleration(axis, DEFAULT_AXIS_MAX_ACCELERATION[a]);
set_axis_max_jerk(axis, DEFAULT_AXIS_MAX_JERK[a]);
}
+
+ _state.filament_load_times.clear();
+ _state.filament_unload_times.clear();
}
void GCodeTimeEstimator::reset()
{
- _time = 0.0f;
+ _reset_time();
#if ENABLE_MOVE_STATS
_moves_stats.clear();
#endif // ENABLE_MOVE_STATS
@@ -442,23 +657,14 @@ namespace Slic3r {
return _time;
}
- std::string GCodeTimeEstimator::get_time_hms() const
+ std::string GCodeTimeEstimator::get_time_dhms() const
{
- float timeinsecs = get_time();
- int hours = (int)(timeinsecs / 3600.0f);
- timeinsecs -= (float)hours * 3600.0f;
- int minutes = (int)(timeinsecs / 60.0f);
- timeinsecs -= (float)minutes * 60.0f;
-
- char buffer[64];
- if (hours > 0)
- ::sprintf(buffer, "%dh %dm %ds", hours, minutes, (int)timeinsecs);
- else if (minutes > 0)
- ::sprintf(buffer, "%dm %ds", minutes, (int)timeinsecs);
- else
- ::sprintf(buffer, "%ds", (int)timeinsecs);
+ return _get_time_dhms(get_time());
+ }
- return buffer;
+ std::string GCodeTimeEstimator::get_time_minutes() const
+ {
+ return _get_time_minutes(get_time());
}
void GCodeTimeEstimator::_reset()
@@ -471,6 +677,17 @@ namespace Slic3r {
set_axis_position(Z, 0.0f);
set_additional_time(0.0f);
+
+ reset_extruder_id();
+ reset_g1_line_id();
+ _g1_line_ids.clear();
+
+ _last_st_synchronized_block_id = -1;
+ }
+
+ void GCodeTimeEstimator::_reset_time()
+ {
+ _time = 0.0f;
}
void GCodeTimeEstimator::_reset_blocks()
@@ -478,22 +695,27 @@ namespace Slic3r {
_blocks.clear();
}
+
void GCodeTimeEstimator::_calculate_time()
{
+ PROFILE_FUNC();
_forward_pass();
_reverse_pass();
_recalculate_trapezoids();
_time += get_additional_time();
- for (const Block& block : _blocks)
+ for (int i = _last_st_synchronized_block_id + 1; i < (int)_blocks.size(); ++i)
{
+ Block& block = _blocks[i];
+
#if ENABLE_MOVE_STATS
float block_time = 0.0f;
block_time += block.acceleration_time();
block_time += block.cruise_time();
block_time += block.deceleration_time();
_time += block_time;
+ block.elapsed_time = _time;
MovesStatsMap::iterator it = _moves_stats.find(block.move_type);
if (it == _moves_stats.end())
@@ -505,8 +727,13 @@ namespace Slic3r {
_time += block.acceleration_time();
_time += block.cruise_time();
_time += block.deceleration_time();
+ block.elapsed_time = _time;
#endif // ENABLE_MOVE_STATS
}
+
+ _last_st_synchronized_block_id = _blocks.size() - 1;
+ // The additional time has been consumed (added to the total time), reset it to zero.
+ set_additional_time(0.);
}
void GCodeTimeEstimator::_process_gcode_line(GCodeReader&, const GCodeReader::GCodeLine& line)
@@ -619,10 +846,20 @@ namespace Slic3r {
_processM566(line);
break;
}
+ case 702: // MK3 MMU2: Process the final filament unload.
+ {
+ _processM702(line);
+ break;
+ }
}
break;
}
+ case 'T': // Select Tools
+ {
+ _processT(line);
+ break;
+ }
}
}
}
@@ -642,6 +879,9 @@ namespace Slic3r {
void GCodeTimeEstimator::_processG1(const GCodeReader::GCodeLine& line)
{
+ PROFILE_FUNC();
+ increment_g1_line_id();
+
// updates axes positions from line
EUnits units = get_units();
float new_pos[Num_Axis];
@@ -690,13 +930,16 @@ namespace Slic3r {
if (_curr.abs_axis_feedrate[a] > 0.0f)
min_feedrate_factor = std::min(min_feedrate_factor, get_axis_max_feedrate((EAxis)a) / _curr.abs_axis_feedrate[a]);
}
-
+
block.feedrate.cruise = min_feedrate_factor * _curr.feedrate;
- for (unsigned char a = X; a < Num_Axis; ++a)
+ if (min_feedrate_factor < 1.0f)
{
- _curr.axis_feedrate[a] *= min_feedrate_factor;
- _curr.abs_axis_feedrate[a] *= min_feedrate_factor;
+ for (unsigned char a = X; a < Num_Axis; ++a)
+ {
+ _curr.axis_feedrate[a] *= min_feedrate_factor;
+ _curr.abs_axis_feedrate[a] *= min_feedrate_factor;
+ }
}
// calculates block acceleration
@@ -829,10 +1072,12 @@ namespace Slic3r {
// adds block to blocks list
_blocks.emplace_back(block);
+ _g1_line_ids.insert(G1LineIdToBlockIdMap::value_type(get_g1_line_id(), (unsigned int)_blocks.size() - 1));
}
void GCodeTimeEstimator::_processG4(const GCodeReader::GCodeLine& line)
{
+ PROFILE_FUNC();
GCodeFlavor dialect = get_dialect();
float value;
@@ -854,31 +1099,37 @@ namespace Slic3r {
void GCodeTimeEstimator::_processG20(const GCodeReader::GCodeLine& line)
{
+ PROFILE_FUNC();
set_units(Inches);
}
void GCodeTimeEstimator::_processG21(const GCodeReader::GCodeLine& line)
{
+ PROFILE_FUNC();
set_units(Millimeters);
}
void GCodeTimeEstimator::_processG28(const GCodeReader::GCodeLine& line)
{
+ PROFILE_FUNC();
// TODO
}
void GCodeTimeEstimator::_processG90(const GCodeReader::GCodeLine& line)
{
+ PROFILE_FUNC();
set_global_positioning_type(Absolute);
}
void GCodeTimeEstimator::_processG91(const GCodeReader::GCodeLine& line)
{
+ PROFILE_FUNC();
set_global_positioning_type(Relative);
}
void GCodeTimeEstimator::_processG92(const GCodeReader::GCodeLine& line)
{
+ PROFILE_FUNC();
float lengthsScaleFactor = (get_units() == Inches) ? INCHES_TO_MM : 1.0f;
bool anyFound = false;
@@ -919,26 +1170,31 @@ namespace Slic3r {
void GCodeTimeEstimator::_processM1(const GCodeReader::GCodeLine& line)
{
+ PROFILE_FUNC();
_simulate_st_synchronize();
}
void GCodeTimeEstimator::_processM82(const GCodeReader::GCodeLine& line)
{
+ PROFILE_FUNC();
set_e_local_positioning_type(Absolute);
}
void GCodeTimeEstimator::_processM83(const GCodeReader::GCodeLine& line)
{
+ PROFILE_FUNC();
set_e_local_positioning_type(Relative);
}
void GCodeTimeEstimator::_processM109(const GCodeReader::GCodeLine& line)
{
+ PROFILE_FUNC();
// TODO
}
void GCodeTimeEstimator::_processM201(const GCodeReader::GCodeLine& line)
{
+ PROFILE_FUNC();
GCodeFlavor dialect = get_dialect();
// see http://reprap.org/wiki/G-code#M201:_Set_max_printing_acceleration
@@ -959,6 +1215,7 @@ namespace Slic3r {
void GCodeTimeEstimator::_processM203(const GCodeReader::GCodeLine& line)
{
+ PROFILE_FUNC();
GCodeFlavor dialect = get_dialect();
// see http://reprap.org/wiki/G-code#M203:_Set_maximum_feedrate
@@ -983,16 +1240,32 @@ namespace Slic3r {
void GCodeTimeEstimator::_processM204(const GCodeReader::GCodeLine& line)
{
+ PROFILE_FUNC();
float value;
- if (line.has_value('S', value))
+ if (line.has_value('S', value)) {
+ // Legacy acceleration format. This format is used by the legacy Marlin, MK2 or MK3 firmware,
+ // and it is also generated by Slic3r to control acceleration per extrusion type
+ // (there is a separate acceleration settings in Slicer for perimeter, first layer etc).
set_acceleration(value);
-
- if (line.has_value('T', value))
- set_retract_acceleration(value);
+ if (line.has_value('T', value))
+ set_retract_acceleration(value);
+ } else {
+ // New acceleration format, compatible with the upstream Marlin.
+ if (line.has_value('P', value))
+ set_acceleration(value);
+ if (line.has_value('R', value))
+ set_retract_acceleration(value);
+ if (line.has_value('T', value)) {
+ // Interpret the T value as the travel acceleration in the new Marlin format.
+ //FIXME Prusa3D firmware currently does not support travel acceleration value independent from the extruding acceleration value.
+ // set_travel_acceleration(value);
+ }
+ }
}
void GCodeTimeEstimator::_processM205(const GCodeReader::GCodeLine& line)
{
+ PROFILE_FUNC();
if (line.has_x())
{
float max_jerk = line.x();
@@ -1019,6 +1292,7 @@ namespace Slic3r {
void GCodeTimeEstimator::_processM221(const GCodeReader::GCodeLine& line)
{
+ PROFILE_FUNC();
float value_s;
float value_t;
if (line.has_value('S', value_s) && !line.has_value('T', value_t))
@@ -1027,6 +1301,7 @@ namespace Slic3r {
void GCodeTimeEstimator::_processM566(const GCodeReader::GCodeLine& line)
{
+ PROFILE_FUNC();
if (line.has_x())
set_axis_max_jerk(X, line.x() * MMMIN_TO_MMSEC);
@@ -1040,17 +1315,49 @@ namespace Slic3r {
set_axis_max_jerk(E, line.e() * MMMIN_TO_MMSEC);
}
+ void GCodeTimeEstimator::_processM702(const GCodeReader::GCodeLine& line)
+ {
+ PROFILE_FUNC();
+ if (line.has('C')) {
+ // MK3 MMU2 specific M code:
+ // M702 C is expected to be sent by the custom end G-code when finalizing a print.
+ // The MK3 unit shall unload and park the active filament into the MMU2 unit.
+ add_additional_time(get_filament_unload_time(get_extruder_id()));
+ reset_extruder_id();
+ _simulate_st_synchronize();
+ }
+ }
+
+ void GCodeTimeEstimator::_processT(const GCodeReader::GCodeLine& line)
+ {
+ std::string cmd = line.cmd();
+ if (cmd.length() > 1)
+ {
+ unsigned int id = (unsigned int)::strtol(cmd.substr(1).c_str(), nullptr, 10);
+ if (get_extruder_id() != id)
+ {
+ // Specific to the MK3 MMU2: The initial extruder ID is set to -1 indicating
+ // that the filament is parked in the MMU2 unit and there is nothing to be unloaded yet.
+ add_additional_time(get_filament_unload_time(get_extruder_id()));
+ set_extruder_id(id);
+ add_additional_time(get_filament_load_time(get_extruder_id()));
+ _simulate_st_synchronize();
+ }
+ }
+ }
+
void GCodeTimeEstimator::_simulate_st_synchronize()
{
+ PROFILE_FUNC();
_calculate_time();
- _reset_blocks();
}
void GCodeTimeEstimator::_forward_pass()
{
+ PROFILE_FUNC();
if (_blocks.size() > 1)
{
- for (unsigned int i = 0; i < (unsigned int)_blocks.size() - 1; ++i)
+ for (int i = _last_st_synchronized_block_id + 1; i < (int)_blocks.size() - 1; ++i)
{
_planner_forward_pass_kernel(_blocks[i], _blocks[i + 1]);
}
@@ -1059,9 +1366,10 @@ namespace Slic3r {
void GCodeTimeEstimator::_reverse_pass()
{
+ PROFILE_FUNC();
if (_blocks.size() > 1)
{
- for (int i = (int)_blocks.size() - 1; i >= 1; --i)
+ for (int i = (int)_blocks.size() - 1; i >= _last_st_synchronized_block_id + 2; --i)
{
_planner_reverse_pass_kernel(_blocks[i - 1], _blocks[i]);
}
@@ -1070,6 +1378,7 @@ namespace Slic3r {
void GCodeTimeEstimator::_planner_forward_pass_kernel(Block& prev, Block& curr)
{
+ PROFILE_FUNC();
// If the previous block is an acceleration block, but it is not long enough to complete the
// full speed change within the block, we need to adjust the entry speed accordingly. Entry
// speeds have already been reset, maximized, and reverse planned by reverse planner.
@@ -1110,11 +1419,14 @@ namespace Slic3r {
void GCodeTimeEstimator::_recalculate_trapezoids()
{
+ PROFILE_FUNC();
Block* curr = nullptr;
Block* next = nullptr;
- for (Block& b : _blocks)
+ for (int i = _last_st_synchronized_block_id + 1; i < (int)_blocks.size(); ++i)
{
+ Block& b = _blocks[i];
+
curr = next;
next = &b;
@@ -1144,6 +1456,33 @@ namespace Slic3r {
}
}
+ std::string GCodeTimeEstimator::_get_time_dhms(float time_in_secs)
+ {
+ int days = (int)(time_in_secs / 86400.0f);
+ time_in_secs -= (float)days * 86400.0f;
+ int hours = (int)(time_in_secs / 3600.0f);
+ time_in_secs -= (float)hours * 3600.0f;
+ int minutes = (int)(time_in_secs / 60.0f);
+ time_in_secs -= (float)minutes * 60.0f;
+
+ char buffer[64];
+ if (days > 0)
+ ::sprintf(buffer, "%dd %dh %dm %ds", days, hours, minutes, (int)time_in_secs);
+ else if (hours > 0)
+ ::sprintf(buffer, "%dh %dm %ds", hours, minutes, (int)time_in_secs);
+ else if (minutes > 0)
+ ::sprintf(buffer, "%dm %ds", minutes, (int)time_in_secs);
+ else
+ ::sprintf(buffer, "%ds", (int)time_in_secs);
+
+ return buffer;
+ }
+
+ std::string GCodeTimeEstimator::_get_time_minutes(float time_in_secs)
+ {
+ return std::to_string((int)(::roundf(time_in_secs / 60.0f)));
+ }
+
#if ENABLE_MOVE_STATS
void GCodeTimeEstimator::_log_moves_stats() const
{
diff --git a/xs/src/libslic3r/GCodeTimeEstimator.hpp b/xs/src/libslic3r/GCodeTimeEstimator.hpp
index 8f948abd1..e9da584c3 100644
--- a/xs/src/libslic3r/GCodeTimeEstimator.hpp
+++ b/xs/src/libslic3r/GCodeTimeEstimator.hpp
@@ -17,6 +17,15 @@ 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,
+ Silent
+ };
+
enum EUnits : unsigned char
{
Millimeters,
@@ -66,11 +75,22 @@ namespace Slic3r {
Axis axis[Num_Axis];
float feedrate; // mm/s
float acceleration; // mm/s^2
+ // hard limit for the acceleration, to which the firmware will clamp.
+ float max_acceleration; // mm/s^2
float retract_acceleration; // mm/s^2
float additional_time; // s
float minimum_feedrate; // mm/s
float minimum_travel_feedrate; // mm/s
- float extrude_factor_override_percentage;
+ float extrude_factor_override_percentage;
+ // Additional load / unload times for a filament exchange sequence.
+ std::vector<float> filament_load_times;
+ std::vector<float> filament_unload_times;
+ unsigned int g1_line_id;
+ // extruder_id is currently used to correctly calculate filament load / unload times
+ // into the total print time. This is currently only really used by the MK3 MMU2:
+ // Extruder id (-1) means no filament is loaded yet, all the filaments are parked in the MK3 MMU2 unit.
+ static const unsigned int extruder_id_unloaded = (unsigned int)-1;
+ unsigned int extruder_id;
};
public:
@@ -121,7 +141,6 @@ namespace Slic3r {
bool nominal_length;
};
-
#if ENABLE_MOVE_STATS
EMoveType move_type;
#endif // ENABLE_MOVE_STATS
@@ -134,6 +153,9 @@ namespace Slic3r {
FeedrateProfile feedrate;
Trapezoid trapezoid;
+ float elapsed_time;
+
+ Block();
// Returns the length of the move covered by this block, in mm
float move_length() const;
@@ -187,19 +209,39 @@ namespace Slic3r {
typedef std::map<Block::EMoveType, MoveStats> MovesStatsMap;
#endif // ENABLE_MOVE_STATS
+ typedef std::map<unsigned int, unsigned int> G1LineIdToBlockIdMap;
+
private:
+ EMode _mode;
GCodeReader _parser;
State _state;
Feedrates _curr;
Feedrates _prev;
BlocksList _blocks;
+ // Map between g1 line id and blocks id, used to speed up export of remaining times
+ G1LineIdToBlockIdMap _g1_line_ids;
+ // Index of the last block already st_synchronized
+ int _last_st_synchronized_block_id;
float _time; // s
+
#if ENABLE_MOVE_STATS
MovesStatsMap _moves_stats;
#endif // ENABLE_MOVE_STATS
public:
- GCodeTimeEstimator();
+ explicit GCodeTimeEstimator(EMode mode);
+
+ // Adds the given gcode line
+ void add_gcode_line(const std::string& gcode_line);
+
+ void add_gcode_block(const char *ptr);
+ void add_gcode_block(const std::string &str) { this->add_gcode_block(str.c_str()); }
+
+ // Calculates the time estimate from the gcode lines added using add_gcode_line() or add_gcode_block()
+ // start_from_beginning:
+ // if set to true all blocks will be used to calculate the time estimate,
+ // if set to false only the blocks not yet processed will be used and the calculated time will be added to the current calculated time
+ void calculate_time(bool start_from_beginning);
// Calculates the time estimate from the given gcode in string format
void calculate_time_from_text(const std::string& gcode);
@@ -210,14 +252,12 @@ namespace Slic3r {
// Calculates the time estimate from the gcode contained in given list of gcode lines
void calculate_time_from_lines(const std::vector<std::string>& gcode_lines);
- // Adds the given gcode line
- void add_gcode_line(const std::string& gcode_line);
-
- void add_gcode_block(const char *ptr);
- void add_gcode_block(const std::string &str) { this->add_gcode_block(str.c_str()); }
-
- // Calculates the time estimate from the gcode lines added using add_gcode_line()
- void calculate_time();
+ // Process the gcode contained in the file with the given filename,
+ // placing in it new lines (M73) containing the remaining time, at the given interval in seconds
+ // and saving the result back in the same file
+ // This time estimator should have been already used to calculate the time estimate for the gcode
+ // contained in the given file before to call this method
+ bool post_process_remaining_times(const std::string& filename, float interval_sec);
// Set current position on the given axis with the given value
void set_axis_position(EAxis axis, float position);
@@ -239,6 +279,10 @@ namespace Slic3r {
void set_acceleration(float acceleration_mm_sec2);
float get_acceleration() const;
+ // Maximum acceleration for the machine. The firmware simulator will clamp the M204 Sxxx to this maximum.
+ void set_max_acceleration(float acceleration_mm_sec2);
+ float get_max_acceleration() const;
+
void set_retract_acceleration(float acceleration_mm_sec2);
float get_retract_acceleration() const;
@@ -248,6 +292,11 @@ namespace Slic3r {
void set_minimum_travel_feedrate(float feedrate_mm_sec);
float get_minimum_travel_feedrate() const;
+ void set_filament_load_times(const std::vector<double> &filament_load_times);
+ void set_filament_unload_times(const std::vector<double> &filament_unload_times);
+ float get_filament_load_time(unsigned int id_extruder);
+ float get_filament_unload_time(unsigned int id_extruder);
+
void set_extrude_factor_override_percentage(float percentage);
float get_extrude_factor_override_percentage() const;
@@ -263,6 +312,14 @@ namespace Slic3r {
void set_e_local_positioning_type(EPositioningType type);
EPositioningType get_e_local_positioning_type() const;
+ int get_g1_line_id() const;
+ void increment_g1_line_id();
+ void reset_g1_line_id();
+
+ void set_extruder_id(unsigned int id);
+ unsigned int get_extruder_id() const;
+ void reset_extruder_id();
+
void add_additional_time(float timeSec);
void set_additional_time(float timeSec);
float get_additional_time() const;
@@ -275,11 +332,15 @@ namespace Slic3r {
// Returns the estimated time, in seconds
float get_time() const;
- // Returns the estimated time, in format HHh MMm SSs
- std::string get_time_hms() const;
+ // Returns the estimated time, in format DDd HHh MMm SSs
+ std::string get_time_dhms() const;
+
+ // Returns the estimated time, in minutes (integer)
+ std::string get_time_minutes() const;
private:
void _reset();
+ void _reset_time();
void _reset_blocks();
// Calculates the time estimate
@@ -342,6 +403,12 @@ namespace Slic3r {
// Set allowable instantaneous speed change
void _processM566(const GCodeReader::GCodeLine& line);
+ // Unload the current filament into the MK3 MMU2 unit at the end of print.
+ void _processM702(const GCodeReader::GCodeLine& line);
+
+ // Processes T line (Select Tool)
+ void _processT(const GCodeReader::GCodeLine& line);
+
// Simulates firmware st_synchronize() call
void _simulate_st_synchronize();
@@ -353,6 +420,12 @@ namespace Slic3r {
void _recalculate_trapezoids();
+ // Returns the given time is seconds in format DDd HHh MMm SSs
+ static std::string _get_time_dhms(float time_in_secs);
+
+ // Returns the given, in minutes (integer)
+ static std::string _get_time_minutes(float time_in_secs);
+
#if ENABLE_MOVE_STATS
void _log_moves_stats() const;
#endif // ENABLE_MOVE_STATS
diff --git a/xs/src/libslic3r/GCodeWriter.cpp b/xs/src/libslic3r/GCodeWriter.cpp
index cbe94f317..34e6b7ec3 100644
--- a/xs/src/libslic3r/GCodeWriter.cpp
+++ b/xs/src/libslic3r/GCodeWriter.cpp
@@ -18,7 +18,9 @@ void GCodeWriter::apply_print_config(const PrintConfig &print_config)
{
this->config.apply(print_config, true);
m_extrusion_axis = this->config.get_extrusion_axis();
- this->m_single_extruder_multi_material = print_config.single_extruder_multi_material.value;
+ m_single_extruder_multi_material = print_config.single_extruder_multi_material.value;
+ m_max_acceleration = (print_config.gcode_flavor.value == gcfMarlin) ?
+ print_config.machine_max_acceleration_extruding.values.front() : 0;
}
void GCodeWriter::set_extruders(const std::vector<unsigned int> &extruder_ids)
@@ -85,7 +87,7 @@ std::string GCodeWriter::set_temperature(unsigned int temperature, bool wait, in
}
gcode << temperature;
if (tool != -1 &&
- ( (this->multiple_extruders && ! this->m_single_extruder_multi_material) ||
+ ( (this->multiple_extruders && ! m_single_extruder_multi_material) ||
FLAVOR_IS(gcfMakerWare) || FLAVOR_IS(gcfSailfish)) ) {
gcode << " T" << tool;
}
@@ -170,6 +172,10 @@ std::string GCodeWriter::set_fan(unsigned int speed, bool dont_save)
std::string GCodeWriter::set_acceleration(unsigned int acceleration)
{
+ // Clamp the acceleration to the allowed maximum.
+ if (m_max_acceleration > 0 && acceleration > m_max_acceleration)
+ acceleration = m_max_acceleration;
+
if (acceleration == 0 || acceleration == m_last_acceleration)
return std::string();
diff --git a/xs/src/libslic3r/GCodeWriter.hpp b/xs/src/libslic3r/GCodeWriter.hpp
index 78e9c9323..f706b8768 100644
--- a/xs/src/libslic3r/GCodeWriter.hpp
+++ b/xs/src/libslic3r/GCodeWriter.hpp
@@ -18,7 +18,7 @@ public:
GCodeWriter() :
multiple_extruders(false), m_extrusion_axis("E"), m_extruder(nullptr),
m_single_extruder_multi_material(false),
- m_last_acceleration(0), m_last_fan_speed(0),
+ m_last_acceleration(0), m_max_acceleration(0), m_last_fan_speed(0),
m_last_bed_temperature(0), m_last_bed_temperature_reached(true),
m_lifted(0)
{}
@@ -74,6 +74,9 @@ private:
bool m_single_extruder_multi_material;
Extruder* m_extruder;
unsigned int m_last_acceleration;
+ // Limit for setting the acceleration, to respect the machine limits set for the Marlin firmware.
+ // If set to zero, the limit is not in action.
+ unsigned int m_max_acceleration;
unsigned int m_last_fan_speed;
unsigned int m_last_bed_temperature;
bool m_last_bed_temperature_reached;
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/I18N.hpp b/xs/src/libslic3r/I18N.hpp
new file mode 100644
index 000000000..db4fd22df
--- /dev/null
+++ b/xs/src/libslic3r/I18N.hpp
@@ -0,0 +1,18 @@
+#ifndef slic3r_I18N_hpp_
+#define slic3r_I18N_hpp_
+
+#include <string>
+
+namespace Slic3r {
+
+namespace I18N {
+ typedef std::string (*translate_fn_type)(const char*);
+ extern translate_fn_type translate_fn;
+ inline void set_translate_callback(translate_fn_type fn) { translate_fn = fn; }
+ inline std::string translate(const std::string &s) { return (translate_fn == nullptr) ? s : (*translate_fn)(s.c_str()); }
+ inline std::string translate(const char *ptr) { return (translate_fn == nullptr) ? std::string(ptr) : (*translate_fn)(ptr); }
+} // namespace I18N
+
+} // namespace Slic3r
+
+#endif /* slic3r_I18N_hpp_ */
diff --git a/xs/src/libslic3r/Int128.hpp b/xs/src/libslic3r/Int128.hpp
index 7bf0c87b4..d54b75342 100644
--- a/xs/src/libslic3r/Int128.hpp
+++ b/xs/src/libslic3r/Int128.hpp
@@ -48,7 +48,6 @@
#endif
#include <cassert>
-#include "Point.hpp"
#if ! defined(_MSC_VER) && defined(__SIZEOF_INT128__)
#define HAS_INTRINSIC_128_TYPE
@@ -288,20 +287,4 @@ public:
}
return sign_determinant_2x2(p1, q1, p2, q2) * invert;
}
-
- // Exact orientation predicate,
- // returns +1: CCW, 0: collinear, -1: CW.
- static int orient(const Slic3r::Point &p1, const Slic3r::Point &p2, const Slic3r::Point &p3)
- {
- Slic3r::Vector v1(p2 - p1);
- Slic3r::Vector v2(p3 - p1);
- return sign_determinant_2x2_filtered(v1.x, v1.y, v2.x, v2.y);
- }
-
- // Exact orientation predicate,
- // returns +1: CCW, 0: collinear, -1: CW.
- static int cross(const Slic3r::Point &v1, const Slic3r::Point &v2)
- {
- return sign_determinant_2x2_filtered(v1.x, v1.y, v2.x, v2.y);
- }
};
diff --git a/xs/src/libslic3r/Layer.hpp b/xs/src/libslic3r/Layer.hpp
index f3b460443..620583128 100644
--- a/xs/src/libslic3r/Layer.hpp
+++ b/xs/src/libslic3r/Layer.hpp
@@ -21,45 +21,37 @@ class LayerRegion
friend class Layer;
public:
- Layer* layer() { return this->_layer; }
- const Layer* layer() const { return this->_layer; }
- PrintRegion* region() { return this->_region; }
- const PrintRegion* region() const { return this->_region; }
+ Layer* layer() { return this->_layer; }
+ const Layer* layer() const { return this->_layer; }
+ PrintRegion* region() { return this->_region; }
+ const PrintRegion* region() const { return this->_region; }
- // collection of surfaces generated by slicing the original geometry
- // divided by type top/bottom/internal
- SurfaceCollection slices;
-
- // collection of extrusion paths/loops filling gaps
- // These fills are generated by the perimeter generator.
- // They are not printed on their own, but they are copied to this->fills during infill generation.
- ExtrusionEntityCollection thin_fills;
+ // Collection of surfaces generated by slicing the original geometry, divided by type top/bottom/internal.
+ SurfaceCollection slices;
// Unspecified fill polygons, used for overhang detection ("ensure vertical wall thickness feature")
// and for re-starting of infills.
- ExPolygons fill_expolygons;
+ ExPolygons fill_expolygons;
// collection of surfaces for infill generation
- SurfaceCollection fill_surfaces;
-
- // Collection of perimeter surfaces. This is a cached result of diff(slices, fill_surfaces).
- // While not necessary, the memory consumption is meager and it speeds up calculation.
- // The perimeter_surfaces keep the IDs of the slices (top/bottom/)
- SurfaceCollection perimeter_surfaces;
+ SurfaceCollection fill_surfaces;
+ // Collection of extrusion paths/loops filling gaps.
+ // These fills are generated by the perimeter generator.
+ // They are not printed on their own, but they are copied to this->fills during infill generation.
+ ExtrusionEntityCollection thin_fills;
- // collection of expolygons representing the bridged areas (thus not
- // needing support material)
- Polygons bridged;
+ // Collection of expolygons representing the bridged areas (thus not needing support material).
+ //FIXME Not used as of now.
+ Polygons bridged;
// collection of polylines representing the unsupported bridge edges
- PolylineCollection unsupported_bridge_edges;
-
- // ordered collection of extrusion paths/loops to build all perimeters
- // (this collection contains only ExtrusionEntityCollection objects)
- ExtrusionEntityCollection perimeters;
-
- // ordered collection of extrusion paths to fill surfaces
- // (this collection contains only ExtrusionEntityCollection objects)
- ExtrusionEntityCollection fills;
+ PolylineCollection unsupported_bridge_edges;
+
+ // Ordered collection of extrusion paths/loops to build all perimeters.
+ // This collection contains only ExtrusionEntityCollection objects.
+ ExtrusionEntityCollection perimeters;
+ // Ordered collection of extrusion paths to fill surfaces.
+ // This collection contains only ExtrusionEntityCollection objects.
+ ExtrusionEntityCollection fills;
Flow flow(FlowRole role, bool bridge = false, double width = -1) const;
void slices_to_fill_surfaces_clipped();
diff --git a/xs/src/libslic3r/LayerRegion.cpp b/xs/src/libslic3r/LayerRegion.cpp
index 68e17407e..12526f2ec 100644
--- a/xs/src/libslic3r/LayerRegion.cpp
+++ b/xs/src/libslic3r/LayerRegion.cpp
@@ -15,8 +15,7 @@
namespace Slic3r {
-Flow
-LayerRegion::flow(FlowRole role, bool bridge, double width) const
+Flow LayerRegion::flow(FlowRole role, bool bridge, double width) const
{
return this->_region->flow(
role,
@@ -51,8 +50,7 @@ void LayerRegion::slices_to_fill_surfaces_clipped()
}
}
-void
-LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces)
+void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces)
{
this->perimeters.clear();
this->thin_fills.clear();
@@ -340,8 +338,7 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer)
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
}
-void
-LayerRegion::prepare_fill_surfaces()
+void LayerRegion::prepare_fill_surfaces()
{
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
export_region_slices_to_svg_debug("2_prepare_fill_surfaces-initial");
@@ -382,8 +379,7 @@ LayerRegion::prepare_fill_surfaces()
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
}
-double
-LayerRegion::infill_area_threshold() const
+double LayerRegion::infill_area_threshold() const
{
double ss = this->flow(frSolidInfill).scaled_spacing();
return ss*ss;
diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp
index 147353abd..9fe855aa1 100644
--- a/xs/src/libslic3r/Model.cpp
+++ b/xs/src/libslic3r/Model.cpp
@@ -14,6 +14,14 @@
#include <boost/nowide/iostream.hpp>
#include <boost/algorithm/string/replace.hpp>
+#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;
@@ -232,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();
@@ -315,15 +315,16 @@ bool Model::arrange_objects(coordf_t dist, const BoundingBoxf* bb)
Pointfs positions;
if (! _arrange(instance_sizes, dist, bb, positions))
return false;
-
- size_t idx = 0;
- for (ModelObject *o : this->objects) {
+
+ size_t idx = 0;
+ for (ModelObject *o : this->objects) {
for (ModelInstance *i : o->instances) {
i->offset = positions[idx] - instance_centers[idx];
++ idx;
}
o->invalidate_bounding_box();
}
+
return true;
}
@@ -454,10 +455,14 @@ void Model::adjust_min_z()
unsigned int Model::get_auto_extruder_id(unsigned int max_extruders)
{
unsigned int id = s_auto_extruder_id;
-
- if (++s_auto_extruder_id > max_extruders)
+ if (id > max_extruders) {
+ // The current counter is invalid, likely due to switching the printer profiles
+ // to a profile with a lower number of extruders.
reset_auto_extruder_id();
-
+ id = s_auto_extruder_id;
+ } else if (++s_auto_extruder_id > max_extruders) {
+ reset_auto_extruder_id();
+ }
return id;
}
@@ -603,15 +608,13 @@ void ModelObject::clear_instances()
// Returns the bounding box of the transformed instances.
// This bounding box is approximate and not snug.
-//========================================================================================================
const BoundingBoxf3& ModelObject::bounding_box() const
-//const BoundingBoxf3& ModelObject::bounding_box()
-//========================================================================================================
{
if (! m_bounding_box_valid) {
BoundingBoxf3 raw_bbox;
for (const ModelVolume *v : this->volumes)
- if (! v->modifier)
+ if (v->is_model_part())
+ // mesh.bounding_box() returns a cached value.
raw_bbox.merge(v->mesh.bounding_box());
BoundingBoxf3 bb;
for (const ModelInstance *i : this->instances)
@@ -622,54 +625,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
{
@@ -690,7 +645,7 @@ TriangleMesh ModelObject::raw_mesh() const
{
TriangleMesh mesh;
for (const ModelVolume *v : this->volumes)
- if (! v->modifier)
+ if (v->is_model_part())
mesh.merge(v->mesh);
return mesh;
}
@@ -701,7 +656,7 @@ BoundingBoxf3 ModelObject::raw_bounding_box() const
{
BoundingBoxf3 bb;
for (const ModelVolume *v : this->volumes)
- if (! v->modifier) {
+ if (v->is_model_part()) {
if (this->instances.empty()) CONFESS("Can't call raw_bounding_box() with no instances");
bb.merge(this->instances.front()->transform_mesh_bounding_box(&v->mesh, true));
}
@@ -713,7 +668,7 @@ BoundingBoxf3 ModelObject::instance_bounding_box(size_t instance_idx, bool dont_
{
BoundingBoxf3 bb;
for (ModelVolume *v : this->volumes)
- if (! v->modifier)
+ if (v->is_model_part())
bb.merge(this->instances[instance_idx]->transform_mesh_bounding_box(&v->mesh, dont_translate));
return bb;
}
@@ -724,7 +679,7 @@ void ModelObject::center_around_origin()
// center this object around the origin
BoundingBoxf3 bb;
for (ModelVolume *v : this->volumes)
- if (! v->modifier)
+ if (v->is_model_part())
bb.merge(v->mesh.bounding_box());
// first align to origin on XYZ
@@ -754,25 +709,38 @@ 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)
{
for (ModelVolume *v : this->volumes)
+ {
v->mesh.rotate(angle, axis);
- this->origin_translation = Pointf3(0,0,0);
+ v->m_convex_hull.rotate(angle, axis);
+ }
+
+ center_around_origin();
+
+ this->origin_translation = Pointf3(0, 0, 0);
this->invalidate_bounding_box();
}
@@ -784,6 +752,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);
@@ -793,8 +762,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();
}
@@ -810,7 +783,7 @@ size_t ModelObject::facets_count() const
{
size_t num = 0;
for (const ModelVolume *v : this->volumes)
- if (! v->modifier)
+ if (v->is_model_part())
num += v->mesh.stl.stats.number_of_facets;
return num;
}
@@ -818,7 +791,7 @@ size_t ModelObject::facets_count() const
bool ModelObject::needed_repair() const
{
for (const ModelVolume *v : this->volumes)
- if (! v->modifier && v->mesh.needed_repair())
+ if (v->is_model_part() && v->mesh.needed_repair())
return true;
return false;
}
@@ -834,7 +807,7 @@ void ModelObject::cut(coordf_t z, Model* model) const
lower->input_file = "";
for (ModelVolume *volume : this->volumes) {
- if (volume->modifier) {
+ if (! volume->is_model_part()) {
// don't cut modifiers
upper->add_volume(*volume);
lower->add_volume(*volume);
@@ -886,7 +859,7 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
ModelVolume* new_volume = new_object->add_volume(*mesh);
new_volume->name = volume->name;
new_volume->config = volume->config;
- new_volume->modifier = volume->modifier;
+ new_volume->set_type(volume->type());
new_volume->material_id(volume->material_id());
new_objects->push_back(new_object);
@@ -896,6 +869,34 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
return;
}
+void ModelObject::check_instances_print_volume_state(const BoundingBoxf3& print_volume)
+{
+ for (const ModelVolume* vol : this->volumes)
+ {
+ if (vol->is_model_part())
+ {
+ for (ModelInstance* inst : this->instances)
+ {
+ 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));
+
+ BoundingBoxf3 bb = vol->get_convex_hull().transformed_bounding_box(world_mat);
+
+ if (print_volume.contains(bb))
+ inst->print_volume_state = ModelInstance::PVS_Inside;
+ else if (print_volume.intersects(bb))
+ inst->print_volume_state = ModelInstance::PVS_Partly_Outside;
+ else
+ inst->print_volume_state = ModelInstance::PVS_Fully_Outside;
+ }
+ }
+ }
+}
+
void ModelObject::print_info() const
{
using namespace std;
@@ -966,6 +967,52 @@ 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;
+}
+
+TriangleMesh& ModelVolume::get_convex_hull()
+{
+ return m_convex_hull;
+}
+
+ModelVolume::Type ModelVolume::type_from_string(const std::string &s)
+{
+ // Legacy support
+ if (s == "0")
+ return MODEL_PART;
+ if (s == "1")
+ return PARAMETER_MODIFIER;
+ // New type (supporting the support enforcers & blockers)
+ if (s == "ModelPart")
+ return MODEL_PART;
+ if (s == "ParameterModifier")
+ return PARAMETER_MODIFIER;
+ if (s == "SupportEnforcer")
+ return SUPPORT_ENFORCER;
+ if (s == "SupportBlocker")
+ return SUPPORT_BLOCKER;
+}
+
+std::string ModelVolume::type_to_string(const Type t)
+{
+ switch (t) {
+ case MODEL_PART: return "ModelPart";
+ case PARAMETER_MODIFIER: return "ParameterModifier";
+ case SUPPORT_ENFORCER: return "SupportEnforcer";
+ case SUPPORT_BLOCKER: return "SupportBlocker";
+ default:
+ assert(false);
+ return "ModelPart";
+ }
+}
+
// 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.
@@ -1048,32 +1095,16 @@ BoundingBoxf3 ModelInstance::transform_mesh_bounding_box(const TriangleMesh* mes
BoundingBoxf3 ModelInstance::transform_bounding_box(const BoundingBoxf3 &bbox, bool dont_translate) const
{
- // rotate around mesh origin
- double c = cos(this->rotation);
- double s = sin(this->rotation);
- Pointf3 pts[4] = {
- bbox.min,
- bbox.max,
- Pointf3(bbox.min.x, bbox.max.y, bbox.min.z),
- Pointf3(bbox.max.x, bbox.min.y, bbox.max.z)
- };
- BoundingBoxf3 out;
- for (int i = 0; i < 4; ++ i) {
- Pointf3 &v = pts[i];
- double xold = v.x;
- double yold = v.y;
- v.x = float(c * xold - s * yold);
- v.y = float(s * xold + c * yold);
- v.x *= this->scaling_factor;
- v.y *= this->scaling_factor;
- v.z *= this->scaling_factor;
- if (! dont_translate) {
- v.x += this->offset.x;
- v.y += this->offset.y;
- }
- out.merge(v);
- }
- return out;
+ Eigen::Transform<float, 3, Eigen::Affine> matrix = Eigen::Transform<float, 3, Eigen::Affine>::Identity();
+ if (!dont_translate)
+ matrix.translate(Eigen::Vector3f((float)offset.x, (float)offset.y, 0.0f));
+
+ matrix.rotate(Eigen::AngleAxisf(rotation, Eigen::Vector3f::UnitZ()));
+ matrix.scale(scaling_factor);
+
+ std::vector<float> m(16, 0.0f);
+ ::memcpy((void*)m.data(), (const void*)matrix.data(), 16 * sizeof(float));
+ return bbox.transformed(m);
}
void ModelInstance::transform_polygon(Polygon* polygon) const
diff --git a/xs/src/libslic3r/Model.hpp b/xs/src/libslic3r/Model.hpp
index b148ec29d..353dc5ccc 100644
--- a/xs/src/libslic3r/Model.hpp
+++ b/xs/src/libslic3r/Model.hpp
@@ -103,14 +103,8 @@ public:
// Returns the bounding box of the transformed instances.
// This bounding box is approximate and not snug.
// This bounding box is being cached.
-//========================================================================================================
const BoundingBoxf3& bounding_box() const;
-// const BoundingBoxf3& bounding_box();
-//========================================================================================================
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;
@@ -126,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;
@@ -135,6 +129,8 @@ public:
void cut(coordf_t z, Model* model) const;
void split(ModelObjectPtrs* new_objects);
+ void check_instances_print_volume_state(const BoundingBoxf3& print_volume);
+
// Print object statistics to console.
void print_info() const;
@@ -148,10 +144,9 @@ private:
// Parent object, owning this ModelObject.
Model *m_model;
// Bounding box, cached.
-//========================================================================================================
+
mutable BoundingBoxf3 m_bounding_box;
mutable bool m_bounding_box_valid;
-//========================================================================================================
};
// An object STL, or a modifier volume, over which a different set of parameters shall be applied.
@@ -159,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.
@@ -166,15 +165,27 @@ public:
// Configuration parameters specific to an object model geometry or a modifier volume,
// overriding the global Slic3r settings and the ModelObject settings.
DynamicPrintConfig config;
- // Is it an object to be printed, or a modifier volume?
- bool modifier;
-
+
+ enum Type {
+ MODEL_TYPE_INVALID = -1,
+ MODEL_PART = 0,
+ PARAMETER_MODIFIER,
+ SUPPORT_ENFORCER,
+ SUPPORT_BLOCKER,
+ };
+
// A parent object owning this modifier volume.
- ModelObject* get_object() const { return this->object; };
+ ModelObject* get_object() const { return this->object; };
+ Type type() const { return m_type; }
+ void set_type(const Type t) { m_type = t; }
+ bool is_model_part() const { return m_type == MODEL_PART; }
+ bool is_modifier() const { return m_type == PARAMETER_MODIFIER; }
+ bool is_support_enforcer() const { return m_type == SUPPORT_ENFORCER; }
+ bool is_support_blocker() const { return m_type == SUPPORT_BLOCKER; }
t_model_material_id material_id() const { return this->_material_id; }
- void material_id(t_model_material_id material_id);
- ModelMaterial* material() const;
- void set_material(t_model_material_id material_id, const ModelMaterial &material);
+ void material_id(t_model_material_id material_id);
+ ModelMaterial* material() const;
+ void set_material(t_model_material_id material_id, const ModelMaterial &material);
// 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.
@@ -182,32 +193,64 @@ public:
ModelMaterial* assign_unique_material();
+ void calculate_convex_hull();
+ const TriangleMesh& get_convex_hull() const;
+ TriangleMesh& get_convex_hull();
+
+ // Helpers for loading / storing into AMF / 3MF files.
+ static Type type_from_string(const std::string &s);
+ static std::string type_to_string(const Type t);
+
private:
// Parent object owning this ModelVolume.
- ModelObject* object;
- t_model_material_id _material_id;
+ ModelObject* object;
+ // Is it an object to be printed, or a modifier volume?
+ Type m_type;
+ 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) :
- name(other.name), mesh(std::move(mesh)), config(other.config), modifier(other.modifier), object(object)
- { this->material_id(other.material_id()); }
+ ModelVolume(ModelObject *object, const TriangleMesh &mesh) : mesh(mesh), m_type(MODEL_PART), 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)), m_type(MODEL_PART), object(object) {}
+ ModelVolume(ModelObject *object, const ModelVolume &other) :
+ name(other.name), mesh(other.mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), 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), m_type(other.m_type), object(object)
+ {
+ this->material_id(other.material_id());
+ if (mesh.stl.stats.number_of_facets > 1)
+ calculate_convex_hull();
+ }
};
// A single instance of a ModelObject.
// Knows the affine transformation of an object.
class ModelInstance
{
- friend class ModelObject;
public:
+ enum EPrintVolumeState : unsigned char
+ {
+ PVS_Inside,
+ PVS_Partly_Outside,
+ PVS_Fully_Outside,
+ Num_BedStates
+ };
+
+ friend class ModelObject;
+
double rotation; // Rotation around the Z axis, in radians around mesh center point
double scaling_factor;
Pointf offset; // in unscaled coordinates
- ModelObject* get_object() const { return this->object; };
+ // flag showing the position of this instance with respect to the print volume (set by Print::validate() using ModelObject::check_instances_print_volume_state())
+ EPrintVolumeState print_volume_state;
+
+ ModelObject* get_object() const { return this->object; }
// To be called on an external mesh
void transform_mesh(TriangleMesh* mesh, bool dont_translate = false) const;
@@ -217,14 +260,16 @@ public:
BoundingBoxf3 transform_bounding_box(const BoundingBoxf3 &bbox, bool dont_translate = false) const;
// To be called on an external polygon. It does not translate the polygon, only rotates and scales.
void transform_polygon(Polygon* polygon) const;
-
+
+ bool is_printable() const { return print_volume_state == PVS_Inside; }
+
private:
// Parent object, owning this instance.
ModelObject* object;
- ModelInstance(ModelObject *object) : rotation(0), scaling_factor(1), object(object) {}
+ ModelInstance(ModelObject *object) : rotation(0), scaling_factor(1), object(object), print_volume_state(PVS_Inside) {}
ModelInstance(ModelObject *object, const ModelInstance &other) :
- rotation(other.rotation), scaling_factor(other.scaling_factor), offset(other.offset), object(object) {}
+ rotation(other.rotation), scaling_factor(other.scaling_factor), offset(other.offset), object(object), print_volume_state(PVS_Inside) {}
};
@@ -273,8 +318,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
new file mode 100644
index 000000000..b1ccf4d13
--- /dev/null
+++ b/xs/src/libslic3r/ModelArrange.hpp
@@ -0,0 +1,792 @@
+#ifndef MODELARRANGE_HPP
+#define MODELARRANGE_HPP
+
+#include "Model.hpp"
+#include "SVG.hpp"
+#include <libnest2d.h>
+
+#include <numeric>
+#include <ClipperUtils.hpp>
+
+#include <boost/geometry/index/rtree.hpp>
+
+namespace Slic3r {
+namespace arr {
+
+using namespace libnest2d;
+
+std::string toString(const Model& model, bool holes = true) {
+ std::stringstream ss;
+
+ ss << "{\n";
+
+ for(auto objptr : model.objects) {
+ if(!objptr) continue;
+
+ auto rmesh = objptr->raw_mesh();
+
+ for(auto objinst : objptr->instances) {
+ if(!objinst) continue;
+
+ Slic3r::TriangleMesh tmpmesh = rmesh;
+ tmpmesh.scale(objinst->scaling_factor);
+ objinst->transform_mesh(&tmpmesh);
+ ExPolygons expolys = tmpmesh.horizontal_projection();
+ for(auto& expoly_complex : expolys) {
+
+ auto tmp = expoly_complex.simplify(1.0/SCALING_FACTOR);
+ if(tmp.empty()) continue;
+ auto expoly = tmp.front();
+ expoly.contour.make_clockwise();
+ for(auto& h : expoly.holes) h.make_counter_clockwise();
+
+ ss << "\t{\n";
+ ss << "\t\t{\n";
+
+ for(auto v : expoly.contour.points) ss << "\t\t\t{"
+ << v.x << ", "
+ << v.y << "},\n";
+ {
+ auto v = expoly.contour.points.front();
+ ss << "\t\t\t{" << v.x << ", " << v.y << "},\n";
+ }
+ ss << "\t\t},\n";
+
+ // Holes:
+ ss << "\t\t{\n";
+ if(holes) for(auto h : expoly.holes) {
+ ss << "\t\t\t{\n";
+ for(auto v : h.points) ss << "\t\t\t\t{"
+ << v.x << ", "
+ << v.y << "},\n";
+ {
+ auto v = h.points.front();
+ ss << "\t\t\t\t{" << v.x << ", " << v.y << "},\n";
+ }
+ ss << "\t\t\t},\n";
+ }
+ ss << "\t\t},\n";
+
+ ss << "\t},\n";
+ }
+ }
+ }
+
+ ss << "}\n";
+
+ return ss.str();
+}
+
+void toSVG(SVG& svg, const Model& model) {
+ for(auto objptr : model.objects) {
+ if(!objptr) continue;
+
+ auto rmesh = objptr->raw_mesh();
+
+ for(auto objinst : objptr->instances) {
+ if(!objinst) continue;
+
+ Slic3r::TriangleMesh tmpmesh = rmesh;
+ tmpmesh.scale(objinst->scaling_factor);
+ objinst->transform_mesh(&tmpmesh);
+ ExPolygons expolys = tmpmesh.horizontal_projection();
+ svg.draw(expolys);
+ }
+ }
+}
+
+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,
+ 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
+ // a spatial index to quickly get neighbors of the candidate item
+ const SpatIndex& spatindex,
+ const SpatIndex& smalls_spatindex,
+ const ItemGroup& remaining
+ )
+{
+ using Coord = TCoord<PointImpl>;
+
+ 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 isBig = [bin_area](double a) {
+ return a/bin_area > BIG_ITEM_TRESHOLD ;
+ };
+
+ // Candidate item bounding box
+ auto ibb = sl::boundingBox(item.transformedShape());
+
+ // Calculate the full bounding box of the pile with the candidate item
+ auto fullbb = boundingBox(pilebb, ibb);
+
+ // The bounding box of the big items (they will accumulate in the center
+ // of the pile
+ Box bigbb;
+ if(spatindex.empty()) bigbb = fullbb;
+ else {
+ auto boostbb = spatindex.bounds();
+ boost::geometry::convert(boostbb, bigbb);
+ }
+
+ // Will hold the resulting score
+ double score = 0;
+
+ if(isBig(item.area()) || spatindex.empty()) {
+ // This branch is for the bigger items..
+
+ 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)};
+
+ // Now the distance of the gravity center will be calculated to the
+ // five anchor points and the smallest will be chosen.
+ std::array<double, 5> dists;
+ auto cc = fullbb.center(); // The gravity center
+ 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);
+
+ // 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
+ 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));
+ }
+
+ 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;
+
+ 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;
+ }
+ } 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;
+ }
+
+ return std::make_tuple(score, fullbb);
+}
+
+template<class PConf>
+void fillConfig(PConf& pcfg) {
+
+ // Align the arranged pile into the center of the bin
+ pcfg.alignment = PConf::Alignment::CENTER;
+
+ // Start placing the items from the center of the print bed
+ pcfg.starting_point = PConf::Alignment::CENTER;
+
+ // TODO cannot use rotations until multiple objects of same geometry can
+ // handle different rotations
+ // arranger.useMinimumBoundigBoxRotation();
+ pcfg.rotations = { 0.0 };
+
+ // The accuracy of optimization.
+ // Goes from 0.0 to 1.0 and scales performance as well
+ pcfg.accuracy = 0.65f;
+
+ pcfg.parallel = true;
+}
+
+template<class TBin>
+class AutoArranger {};
+
+template<class TBin>
+class _ArrBase {
+protected:
+
+ using Placer = TPacker<TBin>;
+ using Selector = FirstFitSelection;
+ using Packer = Nester<Placer, Selector>;
+ using PConfig = typename Packer::PlacementConfig;
+ using Distance = TCoord<PointImpl>;
+ using Pile = sl::Shapes<PolygonImpl>;
+
+ Packer pck_;
+ PConfig pconf_; // Placement configuration
+ double bin_area_;
+ 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_(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) {
+ rtree_.clear();
+ return pck_.executeIndexed(std::forward<Args>(args)...);
+ }
+};
+
+template<>
+class AutoArranger<Box>: public _ArrBase<Box> {
+public:
+
+ AutoArranger(const Box& bin, Distance dist,
+ std::function<void(unsigned)> progressind):
+ _ArrBase<Box>(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& fullbb = std::get<1>(result);
+
+ 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;
+ };
+
+ pck_.configure(pconf_);
+ }
+};
+
+template<>
+class AutoArranger<PolygonImpl>: public _ArrBase<PolygonImpl> {
+public:
+ AutoArranger(const PolygonImpl& bin, Distance dist,
+ std::function<void(unsigned)> progressind):
+ _ArrBase<PolygonImpl>(bin, dist, progressind)
+ {
+ 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);
+
+ return score;
+ };
+
+ pck_.configure(pconf_);
+ }
+};
+
+template<> // Specialization with no bin
+class AutoArranger<bool>: public _ArrBase<Box> {
+public:
+
+ AutoArranger(Distance dist, std::function<void(unsigned)> progressind):
+ _ArrBase<Box>(Box(0, 0), dist, progressind)
+ {
+ 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);
+ };
+
+ this->pck_.configure(pconf_);
+ }
+};
+
+// A container which stores a pointer to the 3D object and its projected
+// 2D shape from top view.
+using ShapeData2D =
+ std::vector<std::pair<Slic3r::ModelInstance*, Item>>;
+
+ShapeData2D projectModelFromTop(const Slic3r::Model &model) {
+ ShapeData2D ret;
+
+ auto s = std::accumulate(model.objects.begin(), model.objects.end(), 0,
+ [](size_t s, ModelObject* o){
+ return s + o->instances.size();
+ });
+
+ ret.reserve(s);
+
+ for(auto objptr : model.objects) {
+ if(objptr) {
+
+ auto rmesh = objptr->raw_mesh();
+
+ for(auto objinst : objptr->instances) {
+ if(objinst) {
+ Slic3r::TriangleMesh tmpmesh = rmesh;
+ ClipperLib::PolygonImpl pn;
+
+ tmpmesh.scale(objinst->scaling_factor);
+
+ // TODO export the exact 2D projection
+ auto p = tmpmesh.convex_hull();
+
+ p.make_clockwise();
+ p.append(p.first_point());
+ pn.Contour = Slic3rMultiPoint_to_ClipperPath( p );
+
+ // Efficient conversion to item.
+ Item item(std::move(pn));
+
+ // Invalid geometries would throw exceptions when arranging
+ if(item.vertexCount() > 3) {
+ item.rotation(objinst->rotation);
+ item.translation( {
+ ClipperLib::cInt(objinst->offset.x/SCALING_FACTOR),
+ ClipperLib::cInt(objinst->offset.y/SCALING_FACTOR)
+ });
+ ret.emplace_back(objinst, item);
+ }
+ }
+ }
+ }
+ }
+
+ return ret;
+}
+
+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
+};
+
+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 ret;
+}
+
+void applyResult(
+ IndexedPackGroup::value_type& group,
+ Coord batch_offset,
+ ShapeData2D& shapemap)
+{
+ for(auto& r : group) {
+ auto idx = r.first; // get the original item index
+ Item& item = r.second; // get the item itself
+
+ // Get the model instance from the shapemap using the index
+ ModelInstance *inst_ptr = shapemap[idx].first;
+
+ // Get the tranformation data from the item object and scale it
+ // appropriately
+ auto off = item.translation();
+ Radians rot = item.rotation();
+ Pointf foff(off.X*SCALING_FACTOR + batch_offset,
+ off.Y*SCALING_FACTOR);
+
+ // write the tranformation data into the model instance
+ inst_ptr->rotation = rot;
+ inst_ptr->offset = foff;
+ }
+}
+
+
+/**
+ * \brief Arranges the model objects on the screen.
+ *
+ * The arrangement considers multiple bins (aka. print beds) for placing all
+ * the items provided in the model argument. If the items don't fit on one
+ * print bed, the remaining will be placed onto newly created print beds.
+ * The first_bin_only parameter, if set to true, disables this behaviour and
+ * makes sure that only one print bed is filled and the remaining items will be
+ * untouched. When set to false, the items which could not fit onto the
+ * print bed will be placed next to the print bed so the user should see a
+ * pile of items on the print bed and some other piles outside the print
+ * area that can be dragged later onto the print bed as a group.
+ *
+ * \param model The model object with the 3D content.
+ * \param dist The minimum distance which is allowed for any pair of items
+ * on the print bed in any direction.
+ * \param bb The bounding box of the print bed. It corresponds to the 'bin'
+ * for bin packing.
+ * \param first_bin_only This parameter controls whether to place the
+ * remaining items which do not fit onto the print area next to the print
+ * bed or leave them untouched (let the user arrange them by hand or remove
+ * them).
+ */
+bool arrange(Model &model, coordf_t min_obj_distance,
+ const Slic3r::Polyline& bed,
+ BedShapeHint bedhint,
+ bool first_bin_only,
+ std::function<void(unsigned)> progressind)
+{
+ using ArrangeResult = _IndexedPackGroup<PolygonImpl>;
+
+ bool ret = true;
+
+ // Get the 2D projected shapes with their 3D model instance pointers
+ auto shapemap = arr::projectModelFromTop(model);
+
+ // Copy the references for the shapes only as the arranger expects a
+ // sequence of objects convertible to Item or ClipperPolygon
+ std::vector<std::reference_wrapper<Item>> shapes;
+ shapes.reserve(shapemap.size());
+ std::for_each(shapemap.begin(), shapemap.end(),
+ [&shapes] (ShapeData2D::value_type& it)
+ {
+ shapes.push_back(std::ref(it.second));
+ });
+
+ IndexedPackGroup result;
+
+ if(bedhint.type == BedShapeType::WHO_KNOWS) bedhint = bedShape(bed);
+
+ BoundingBox bbb(bed);
+
+ auto binbb = Box({
+ static_cast<libnest2d::Coord>(bbb.min.x),
+ static_cast<libnest2d::Coord>(bbb.min.y)
+ },
+ {
+ static_cast<libnest2d::Coord>(bbb.max.x),
+ static_cast<libnest2d::Coord>(bbb.max.y)
+ });
+
+ switch(bedhint.type) {
+ case BedShapeType::BOX: {
+
+ // Create the arranger for the box shaped bed
+ AutoArranger<Box> arrange(binbb, min_obj_distance, progressind);
+
+ // Arrange and return the items with their respective indices within the
+ // input sequence.
+ result = arrange(shapes.begin(), shapes.end());
+ break;
+ }
+ 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 BedShapeType::IRREGULAR:
+ case BedShapeType::WHO_KNOWS: {
+
+ using P = libnest2d::PolygonImpl;
+
+ auto ctour = Slic3rMultiPoint_to_ClipperPath(bed);
+ P irrbed = sl::create<PolygonImpl>(std::move(ctour));
+
+ AutoArranger<P> arrange(irrbed, min_obj_distance, progressind);
+
+ // Arrange and return the items with their respective indices within the
+ // input sequence.
+ result = arrange(shapes.begin(), shapes.end());
+ break;
+ }
+ };
+
+ if(result.empty()) return false;
+
+ if(first_bin_only) {
+ applyResult(result.front(), 0, shapemap);
+ } else {
+
+ const auto STRIDE_PADDING = 1.2;
+
+ Coord stride = static_cast<Coord>(STRIDE_PADDING*
+ binbb.width()*SCALING_FACTOR);
+ Coord batch_offset = 0;
+
+ for(auto& group : result) {
+ applyResult(group, batch_offset, shapemap);
+
+ // Only the first pack group can be placed onto the print bed. The
+ // other objects which could not fit will be placed next to the
+ // print bed
+ batch_offset += stride;
+ }
+ }
+
+ for(auto objptr : model.objects) objptr->invalidate_bounding_box();
+
+ return ret && result.size() == 1;
+}
+
+}
+}
+#endif // MODELARRANGE_HPP
diff --git a/xs/src/libslic3r/MultiPoint.hpp b/xs/src/libslic3r/MultiPoint.hpp
index 0970e9a67..02086ded8 100644
--- a/xs/src/libslic3r/MultiPoint.hpp
+++ b/xs/src/libslic3r/MultiPoint.hpp
@@ -34,8 +34,10 @@ public:
Point first_point() const;
virtual Point last_point() const = 0;
virtual Lines lines() const = 0;
+ size_t size() const { return points.size(); }
+ bool empty() const { return points.empty(); }
double length() const;
- bool is_valid() const { return this->points.size() >= 2; }
+ bool is_valid() const { return this->points.size() >= 2; }
int find_point(const Point &point) const;
bool has_boundary_point(const Point &point) const;
@@ -103,6 +105,23 @@ extern BoundingBox get_extents(const MultiPoint &mp);
extern BoundingBox get_extents_rotated(const std::vector<Point> &points, double angle);
extern BoundingBox get_extents_rotated(const MultiPoint &mp, double angle);
+inline double length(const Points &pts) {
+ double total = 0;
+ if (! pts.empty()) {
+ auto it = pts.begin();
+ for (auto it_prev = it ++; it != pts.end(); ++ it, ++ it_prev)
+ total += it->distance_to(*it_prev);
+ }
+ return total;
+}
+
+inline double area(const Points &polygon) {
+ double area = 0.;
+ for (size_t i = 0, j = polygon.size() - 1; i < polygon.size(); j = i ++)
+ area += double(polygon[j].x + polygon[i].x) * double(polygon[i].y - polygon[j].y);
+ return area;
+}
+
} // namespace Slic3r
#endif
diff --git a/xs/src/libslic3r/Point.cpp b/xs/src/libslic3r/Point.cpp
index 7c1dc91f5..349b00bb6 100644
--- a/xs/src/libslic3r/Point.cpp
+++ b/xs/src/libslic3r/Point.cpp
@@ -1,6 +1,7 @@
#include "Point.hpp"
#include "Line.hpp"
#include "MultiPoint.hpp"
+#include "Int128.hpp"
#include <algorithm>
#include <cmath>
@@ -262,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
{
@@ -375,4 +382,20 @@ Pointf3::vector_to(const Pointf3 &point) const
return Vectorf3(point.x - this->x, point.y - this->y, point.z - this->z);
}
+namespace int128 {
+
+int orient(const Point &p1, const Point &p2, const Point &p3)
+{
+ Slic3r::Vector v1(p2 - p1);
+ Slic3r::Vector v2(p3 - p1);
+ return Int128::sign_determinant_2x2_filtered(v1.x, v1.y, v2.x, v2.y);
+}
+
+int cross(const Point &v1, const Point &v2)
+{
+ return Int128::sign_determinant_2x2_filtered(v1.x, v1.y, v2.x, v2.y);
+}
+
+}
+
}
diff --git a/xs/src/libslic3r/Point.hpp b/xs/src/libslic3r/Point.hpp
index a52cdceb6..a8c732fd7 100644
--- a/xs/src/libslic3r/Point.hpp
+++ b/xs/src/libslic3r/Point.hpp
@@ -81,6 +81,17 @@ inline Point operator*(double scalar, const Point& point2) { return Point(scalar
inline int64_t cross(const Point &v1, const Point &v2) { return int64_t(v1.x) * int64_t(v2.y) - int64_t(v1.y) * int64_t(v2.x); }
inline int64_t dot(const Point &v1, const Point &v2) { return int64_t(v1.x) * int64_t(v2.x) + int64_t(v1.y) * int64_t(v2.y); }
+namespace int128 {
+
+// Exact orientation predicate,
+// returns +1: CCW, 0: collinear, -1: CW.
+int orient(const Point &p1, const Point &p2, const Point &p3);
+
+// Exact orientation predicate,
+// returns +1: CCW, 0: collinear, -1: CW.
+int cross(const Point &v1, const Slic3r::Point &v2);
+}
+
// To be used by std::unordered_map, std::unordered_multimap and friends.
struct PointHash {
size_t operator()(const Point &pt) const {
@@ -139,6 +150,24 @@ public:
m_map.emplace(std::make_pair(Point(pt->x>>m_grid_log2, pt->y>>m_grid_log2), std::move(value)));
}
+ // Erase a data point equal to value. (ValueType has to declare the operator==).
+ // Returns true if the data point equal to value was found and removed.
+ bool erase(const ValueType &value) {
+ const Point *pt = m_point_accessor(value);
+ if (pt != nullptr) {
+ // Range of fragment starts around grid_corner, close to pt.
+ auto range = m_map.equal_range(Point(pt->x>>m_grid_log2, pt->y>>m_grid_log2));
+ // Remove the first item.
+ for (auto it = range.first; it != range.second; ++ it) {
+ if (it->second == value) {
+ m_map.erase(it);
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
// Return a pair of <ValueType*, distance_squared>
std::pair<const ValueType*, double> find(const Point &pt) {
// Iterate over 4 closest grid cells around pt,
@@ -166,7 +195,7 @@ public:
}
}
}
- return (value_min != nullptr && dist_min < coordf_t(m_search_radius * m_search_radius)) ?
+ return (value_min != nullptr && dist_min < coordf_t(m_search_radius) * coordf_t(m_search_radius)) ?
std::make_pair(value_min, dist_min) :
std::make_pair(nullptr, std::numeric_limits<double>::max());
}
@@ -210,6 +239,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/Polygon.hpp b/xs/src/libslic3r/Polygon.hpp
index 1a02d78b7..2d624e71a 100644
--- a/xs/src/libslic3r/Polygon.hpp
+++ b/xs/src/libslic3r/Polygon.hpp
@@ -103,6 +103,12 @@ inline void polygons_rotate(Polygons &polys, double angle)
p.rotate(cos_angle, sin_angle);
}
+inline void polygons_reverse(Polygons &polys)
+{
+ for (Polygon &p : polys)
+ p.reverse();
+}
+
inline Points to_points(const Polygon &poly)
{
return poly.points;
diff --git a/xs/src/libslic3r/Polyline.cpp b/xs/src/libslic3r/Polyline.cpp
index 3432506c6..05bd8c7fb 100644
--- a/xs/src/libslic3r/Polyline.cpp
+++ b/xs/src/libslic3r/Polyline.cpp
@@ -193,23 +193,19 @@ Polyline::split_at(const Point &point, Polyline* p1, Polyline* p2) const
}
}
-bool
-Polyline::is_straight() const
+bool Polyline::is_straight() const
{
- /* Check that each segment's direction is equal to the line connecting
- first point and last point. (Checking each line against the previous
- one would cause the error to accumulate.) */
+ // Check that each segment's direction is equal to the line connecting
+ // first point and last point. (Checking each line against the previous
+ // one would cause the error to accumulate.)
double dir = Line(this->first_point(), this->last_point()).direction();
-
- Lines lines = this->lines();
- for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line) {
- if (!line->parallel_to(dir)) return false;
- }
+ for (const auto &line: this->lines())
+ if (! line.parallel_to(dir))
+ return false;
return true;
}
-std::string
-Polyline::wkt() const
+std::string Polyline::wkt() const
{
std::ostringstream wkt;
wkt << "LINESTRING((";
diff --git a/xs/src/libslic3r/Polyline.hpp b/xs/src/libslic3r/Polyline.hpp
index b64743d84..774af3fab 100644
--- a/xs/src/libslic3r/Polyline.hpp
+++ b/xs/src/libslic3r/Polyline.hpp
@@ -19,6 +19,8 @@ public:
Polyline() {};
Polyline(const Polyline &other) : MultiPoint(other.points) {}
Polyline(Polyline &&other) : MultiPoint(std::move(other.points)) {}
+ explicit Polyline(const Points &points) : MultiPoint(points) {}
+ explicit Polyline(Points &&points) : MultiPoint(std::move(points)) {}
Polyline& operator=(const Polyline &other) { points = other.points; return *this; }
Polyline& operator=(Polyline &&other) { points = std::move(other.points); return *this; }
static Polyline new_scale(std::vector<Pointf> points) {
@@ -79,8 +81,8 @@ extern BoundingBox get_extents(const Polylines &polylines);
inline double total_length(const Polylines &polylines) {
double total = 0;
- for (Polylines::const_iterator it = polylines.begin(); it != polylines.end(); ++it)
- total += it->length();
+ for (const Polyline &pl : polylines)
+ total += pl.length();
return total;
}
diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index 08802139d..c2fac891d 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -4,6 +4,7 @@
#include "Extruder.hpp"
#include "Flow.hpp"
#include "Geometry.hpp"
+#include "I18N.hpp"
#include "SupportMaterial.hpp"
#include "GCode/WipeTowerPrusaMM.hpp"
#include <algorithm>
@@ -11,6 +12,10 @@
#include <boost/filesystem.hpp>
#include <boost/lexical_cast.hpp>
+//! macro used to mark string used at localization,
+//! return same string
+#define L(s) Slic3r::I18N::translate(s)
+
namespace Slic3r {
template class PrintState<PrintStep, psCount>;
@@ -66,6 +71,13 @@ bool Print::reload_model_instances()
return invalidated;
}
+PrintObjectPtrs Print::get_printable_objects() const
+{
+ PrintObjectPtrs printable_objects(this->objects);
+ printable_objects.erase(std::remove_if(printable_objects.begin(), printable_objects.end(), [](PrintObject* o) { return !o->is_printable(); }), printable_objects.end());
+ return printable_objects;
+}
+
PrintRegion* Print::add_region()
{
regions.push_back(new PrintRegion(this));
@@ -116,7 +128,6 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
"gcode_comments",
"gcode_flavor",
"infill_acceleration",
- "infill_first",
"layer_gcode",
"min_fan_speed",
"max_fan_speed",
@@ -143,6 +154,7 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
"retract_restart_extra",
"retract_restart_extra_toolchange",
"retract_speed",
+ "single_extruder_multi_material_priming",
"slowdown_below_layer_time",
"standby_temperature_delta",
"start_gcode",
@@ -154,12 +166,16 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
"use_relative_e_distances",
"use_volumetric_e",
"variable_layer_height",
- "wipe"
+ "wipe",
+ "wipe_tower_x",
+ "wipe_tower_y",
+ "wipe_tower_rotation_angle"
};
std::vector<PrintStep> steps;
std::vector<PrintObjectStep> osteps;
bool invalidated = false;
+
for (const t_config_option_key &opt_key : opt_keys) {
if (steps_ignore.find(opt_key) != steps_ignore.end()) {
// These options only affect G-code export or they are just notes without influence on the generated G-code,
@@ -184,23 +200,28 @@ 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"
+ || opt_key == "filament_cooling_initial_speed"
+ || opt_key == "filament_cooling_final_speed"
|| opt_key == "filament_ramming_parameters"
|| opt_key == "gcode_flavor"
+ || opt_key == "infill_first"
|| opt_key == "single_extruder_multi_material"
|| opt_key == "spiral_vase"
|| opt_key == "temperature"
|| opt_key == "wipe_tower"
- || opt_key == "wipe_tower_x"
- || opt_key == "wipe_tower_y"
|| opt_key == "wipe_tower_width"
- || opt_key == "wipe_tower_rotation_angle"
|| opt_key == "wipe_tower_bridging"
|| opt_key == "wiping_volumes_matrix"
|| opt_key == "parking_pos_retraction"
|| opt_key == "cooling_tube_retraction"
|| opt_key == "cooling_tube_length"
+ || opt_key == "extra_loading_move"
|| opt_key == "z_offset") {
steps.emplace_back(psWipeTower);
} else if (
@@ -212,7 +233,6 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
osteps.emplace_back(posSupportMaterial);
steps.emplace_back(psSkirt);
steps.emplace_back(psBrim);
- steps.emplace_back(psWipeTower);
} else {
// for legacy, if we can't handle this option let's invalidate all steps
//FIXME invalidate all steps of all objects as well?
@@ -342,9 +362,12 @@ void Print::add_model_object(ModelObject* model_object, int idx)
// Invalidate all print steps.
this->invalidate_all_steps();
- for (size_t volume_id = 0; volume_id < model_object->volumes.size(); ++ volume_id) {
+ size_t volume_id = 0;
+ for (const ModelVolume *volume : model_object->volumes) {
+ if (! volume->is_model_part() && ! volume->is_modifier())
+ continue;
// Get the config applied to this volume.
- PrintRegionConfig config = this->_region_config_from_model_volume(*model_object->volumes[volume_id]);
+ PrintRegionConfig config = this->_region_config_from_model_volume(*volume);
// Find an existing print region with the same config.
size_t region_id = size_t(-1);
for (size_t i = 0; i < this->regions.size(); ++ i)
@@ -359,6 +382,7 @@ void Print::add_model_object(ModelObject* model_object, int idx)
}
// Assign volume to a region.
object->add_region_volume(region_id, volume_id);
+ ++ volume_id;
}
// Apply config to print object.
@@ -444,7 +468,7 @@ bool Print::apply_config(DynamicPrintConfig config)
const ModelVolume &volume = *object->model_object()->volumes[volume_id];
if (this_region_config_set) {
// If the new config for this volume differs from the other
- // volume configs currently associated to this region, it means
+ // volume configs currently associated to this region, it means
// the region subdivision does not make sense anymore.
if (! this_region_config.equals(this->_region_config_from_model_volume(volume))) {
rearrange_regions = true;
@@ -521,11 +545,17 @@ std::string Print::validate() const
BoundingBoxf3 print_volume(Pointf3(unscale(bed_box_2D.min.x), unscale(bed_box_2D.min.y), 0.0), Pointf3(unscale(bed_box_2D.max.x), unscale(bed_box_2D.max.y), config.max_print_height));
// Allow the objects to protrude below the print bed, only the part of the object above the print bed will be sliced.
print_volume.min.z = -1e10;
+ unsigned int printable_count = 0;
for (PrintObject *po : this->objects) {
- if (!print_volume.contains(po->model_object()->tight_bounding_box(false)))
- return "Some objects are outside of the print volume.";
+ po->model_object()->check_instances_print_volume_state(print_volume);
+ po->reload_model_instances();
+ if (po->is_printable())
+ ++printable_count;
}
+ if (printable_count == 0)
+ return L("All objects are outside of the print volume.");
+
if (this->config.complete_objects) {
// Check horizontal clearance.
{
@@ -550,7 +580,7 @@ std::string Print::validate() const
Polygon p = convex_hull;
p.translate(copy);
if (! intersection(convex_hulls_other, p).empty())
- return "Some objects are too close; your extruder will collide with them.";
+ return L("Some objects are too close; your extruder will collide with them.");
polygons_append(convex_hulls_other, p);
}
}
@@ -565,7 +595,7 @@ std::string Print::validate() const
// it will be printed as last one so its height doesn't matter.
object_height.pop_back();
if (! object_height.empty() && object_height.back() > scale_(this->config.extruder_clearance_height.value))
- return "Some objects are too tall and cannot be printed without extruder collisions.";
+ return L("Some objects are too tall and cannot be printed without extruder collisions.");
}
} // end if (this->config.complete_objects)
@@ -575,27 +605,22 @@ std::string Print::validate() const
total_copies_count += object->copies().size();
// #4043
if (total_copies_count > 1 && ! this->config.complete_objects.value)
- return "The Spiral Vase option can only be used when printing a single object.";
+ return L("The Spiral Vase option can only be used when printing a single object.");
if (this->regions.size() > 1)
- return "The Spiral Vase option can only be used when printing single material objects.";
+ return L("The Spiral Vase option can only be used when printing single material objects.");
}
if (this->config.single_extruder_multi_material) {
for (size_t i=1; i<this->config.nozzle_diameter.values.size(); ++i)
if (this->config.nozzle_diameter.values[i] != this->config.nozzle_diameter.values[i-1])
- return "All extruders must have the same diameter for single extruder multimaterial printer.";
+ return L("All extruders must have the same diameter for single extruder multimaterial printer.");
}
if (this->has_wipe_tower() && ! this->objects.empty()) {
- #if 0
- for (auto dmr : this->config.nozzle_diameter.values)
- if (std::abs(dmr - 0.4) > EPSILON)
- return "The Wipe Tower is currently only supported for the 0.4mm nozzle diameter.";
- #endif
if (this->config.gcode_flavor != gcfRepRap && this->config.gcode_flavor != gcfMarlin)
- return "The Wipe Tower is currently only supported for the Marlin and RepRap/Sprinter G-code flavors.";
+ return L("The Wipe Tower is currently only supported for the Marlin and RepRap/Sprinter G-code flavors.");
if (! this->config.use_relative_e_distances)
- return "The Wipe Tower is currently only supported with the relative extruder addressing (use_relative_e_distances=1).";
+ return L("The Wipe Tower is currently only supported with the relative extruder addressing (use_relative_e_distances=1).");
SlicingParameters slicing_params0 = this->objects.front()->slicing_parameters();
const PrintObject* tallest_object = this->objects.front(); // let's find the tallest object
@@ -607,13 +632,13 @@ std::string Print::validate() const
SlicingParameters slicing_params = object->slicing_parameters();
if (std::abs(slicing_params.first_print_layer_height - slicing_params0.first_print_layer_height) > EPSILON ||
std::abs(slicing_params.layer_height - slicing_params0.layer_height ) > EPSILON)
- return "The Wipe Tower is only supported for multiple objects if they have equal layer heigths";
+ return L("The Wipe Tower is only supported for multiple objects if they have equal layer heigths");
if (slicing_params.raft_layers() != slicing_params0.raft_layers())
- return "The Wipe Tower is only supported for multiple objects if they are printed over an equal number of raft layers";
+ return L("The Wipe Tower is only supported for multiple objects if they are printed over an equal number of raft layers");
if (object->config.support_material_contact_distance != this->objects.front()->config.support_material_contact_distance)
- return "The Wipe Tower is only supported for multiple objects if they are printed with the same support_material_contact_distance";
+ return L("The Wipe Tower is only supported for multiple objects if they are printed with the same support_material_contact_distance");
if (! equal_layering(slicing_params, slicing_params0))
- return "The Wipe Tower is only supported for multiple objects if they are sliced equally.";
+ return L("The Wipe Tower is only supported for multiple objects if they are sliced equally.");
bool was_layer_height_profile_valid = object->layer_height_profile_valid;
object->update_layer_height_profile();
object->layer_height_profile_valid = was_layer_height_profile_valid;
@@ -637,13 +662,8 @@ std::string Print::validate() const
failed = true;
if (failed)
- return "The Wipe tower is only supported if all objects have the same layer height profile";
+ return L("The Wipe tower is only supported if all objects have the same layer height profile");
}
-
- /*for (size_t i = 5; i < object->layer_height_profile.size(); i += 2)
- if (object->layer_height_profile[i-1] > slicing_params.object_print_z_min + EPSILON &&
- std::abs(object->layer_height_profile[i] - object->config.layer_height) > EPSILON)
- return "The Wipe Tower is currently only supported with constant Z layer spacing. Layer editing is not allowed.";*/
}
}
@@ -651,7 +671,7 @@ std::string Print::validate() const
// find the smallest nozzle diameter
std::vector<unsigned int> extruders = this->extruders();
if (extruders.empty())
- return "The supplied settings will cause an empty print.";
+ return L("The supplied settings will cause an empty print.");
std::vector<double> nozzle_diameters;
for (unsigned int extruder_id : extruders)
@@ -661,7 +681,7 @@ std::string Print::validate() const
unsigned int total_extruders_count = this->config.nozzle_diameter.size();
for (const auto& extruder_idx : extruders)
if ( extruder_idx >= total_extruders_count )
- return "One or more object were assigned an extruder that the printer does not have.";
+ return L("One or more object were assigned an extruder that the printer does not have.");
for (PrintObject *object : this->objects) {
if ((object->config.support_material_extruder == -1 || object->config.support_material_interface_extruder == -1) &&
@@ -670,13 +690,13 @@ std::string Print::validate() const
// will be printed with the current tool without a forced tool change. Play safe, assert that all object nozzles
// are of the same diameter.
if (nozzle_diameters.size() > 1)
- return "Printing with multiple extruders of differing nozzle diameters. "
+ return L("Printing with multiple extruders of differing nozzle diameters. "
"If support is to be printed with the current extruder (support_material_extruder == 0 or support_material_interface_extruder == 0), "
- "all nozzles have to be of the same diameter.";
+ "all nozzles have to be of the same diameter.");
}
// validate first_layer_height
- double first_layer_height = object->config.get_abs_value("first_layer_height");
+ double first_layer_height = object->config.get_abs_value(L("first_layer_height"));
double first_layer_min_nozzle_diameter;
if (object->config.raft_layers > 0) {
// if we have raft layers, only support material extruder is used on first layer
@@ -691,11 +711,11 @@ std::string Print::validate() const
first_layer_min_nozzle_diameter = min_nozzle_diameter;
}
if (first_layer_height > first_layer_min_nozzle_diameter)
- return "First layer height can't be greater than nozzle diameter";
+ return L("First layer height can't be greater than nozzle diameter");
// validate layer_height
if (object->config.layer_height.value > min_nozzle_diameter)
- return "Layer height can't be greater than nozzle diameter";
+ return L("Layer height can't be greater than nozzle diameter");
}
}
@@ -837,7 +857,7 @@ void Print::auto_assign_extruders(ModelObject* model_object) const
for (size_t volume_id = 0; volume_id < model_object->volumes.size(); ++ volume_id) {
ModelVolume *volume = model_object->volumes[volume_id];
//FIXME Vojtech: This assigns an extruder ID even to a modifier volume, if it has a material assigned.
- if (! volume->material_id().empty() && ! volume->config.has("extruder"))
+ if ((volume->is_model_part() || volume->is_modifier()) && ! volume->material_id().empty() && ! volume->config.has("extruder"))
volume->config.opt<ConfigOptionInt>("extruder", true)->value = int(volume_id + 1);
}
}
@@ -855,8 +875,9 @@ void Print::_make_skirt()
// prepended to the first 'n' layers (with 'n' = skirt_height).
// $skirt_height_z in this case is the highest possible skirt height for safety.
coordf_t skirt_height_z = 0.;
- for (const PrintObject *object : this->objects) {
- size_t skirt_layers = this->has_infinite_skirt() ?
+ PrintObjectPtrs printable_objects = get_printable_objects();
+ for (const PrintObject *object : printable_objects) {
+ size_t skirt_layers = this->has_infinite_skirt() ?
object->layer_count() :
std::min(size_t(this->config.skirt_height.value), object->layer_count());
skirt_height_z = std::max(skirt_height_z, object->layers[skirt_layers-1]->print_z);
@@ -864,7 +885,7 @@ void Print::_make_skirt()
// Collect points from all layers contained in skirt height.
Points points;
- for (const PrintObject *object : this->objects) {
+ for (const PrintObject *object : printable_objects) {
Points object_points;
// Get object layers up to skirt_height_z.
for (const Layer *layer : object->layers) {
@@ -977,7 +998,8 @@ void Print::_make_brim()
// Brim is only printed on first layer and uses perimeter extruder.
Flow flow = this->brim_flow();
Polygons islands;
- for (PrintObject *object : this->objects) {
+ PrintObjectPtrs printable_objects = get_printable_objects();
+ for (PrintObject *object : printable_objects) {
Polygons object_islands;
for (ExPolygon &expoly : object->layers.front()->slices.expolygons)
object_islands.push_back(expoly.contour);
@@ -1033,6 +1055,16 @@ void Print::_make_wipe_tower()
if (! this->has_wipe_tower())
return;
+ m_wipe_tower_depth = 0.f;
+
+ // Get wiping matrix to get number of extruders and convert vector<double> to vector<float>:
+ std::vector<float> wiping_matrix((this->config.wiping_volumes_matrix.values).begin(),(this->config.wiping_volumes_matrix.values).end());
+ // Extract purging volumes for each extruder pair:
+ std::vector<std::vector<float>> wipe_volumes;
+ const unsigned int number_of_extruders = (unsigned int)(sqrt(wiping_matrix.size())+EPSILON);
+ for (unsigned int i = 0; i<number_of_extruders; ++i)
+ wipe_volumes.push_back(std::vector<float>(wiping_matrix.begin()+i*number_of_extruders, wiping_matrix.begin()+(i+1)*number_of_extruders));
+
// Let the ToolOrdering class know there will be initial priming extrusions at the start of the print.
m_tool_ordering = ToolOrdering(*this, (unsigned int)-1, true);
if (! m_tool_ordering.has_wipe_tower())
@@ -1048,7 +1080,7 @@ void Print::_make_wipe_tower()
size_t idx_end = m_tool_ordering.layer_tools().size();
// Find the first wipe tower layer, which does not have a counterpart in an object or a support layer.
for (size_t i = 0; i < idx_end; ++ i) {
- const ToolOrdering::LayerTools &lt = m_tool_ordering.layer_tools()[i];
+ const LayerTools &lt = m_tool_ordering.layer_tools()[i];
if (lt.has_wipe_tower && ! lt.has_object && ! lt.has_support) {
idx_begin = i;
break;
@@ -1062,7 +1094,7 @@ void Print::_make_wipe_tower()
for (; it_layer != it_end && (*it_layer)->print_z - EPSILON < wipe_tower_new_layer_print_z_first; ++ it_layer);
// Find the stopper of the sequence of wipe tower layers, which do not have a counterpart in an object or a support layer.
for (size_t i = idx_begin; i < idx_end; ++ i) {
- ToolOrdering::LayerTools &lt = const_cast<ToolOrdering::LayerTools&>(m_tool_ordering.layer_tools()[i]);
+ LayerTools &lt = const_cast<LayerTools&>(m_tool_ordering.layer_tools()[i]);
if (! (lt.has_wipe_tower && ! lt.has_object && ! lt.has_support))
break;
lt.has_support = true;
@@ -1077,115 +1109,76 @@ void Print::_make_wipe_tower()
}
}
- // Get wiping matrix to get number of extruders and convert vector<double> to vector<float>:
- std::vector<float> wiping_volumes((this->config.wiping_volumes_matrix.values).begin(),(this->config.wiping_volumes_matrix.values).end());
-
// Initialize the wipe tower.
WipeTowerPrusaMM wipe_tower(
float(this->config.wipe_tower_x.value), float(this->config.wipe_tower_y.value),
float(this->config.wipe_tower_width.value),
float(this->config.wipe_tower_rotation_angle.value), float(this->config.cooling_tube_retraction.value),
float(this->config.cooling_tube_length.value), float(this->config.parking_pos_retraction.value),
- float(this->config.wipe_tower_bridging), wiping_volumes, m_tool_ordering.first_extruder());
+ float(this->config.extra_loading_move.value), float(this->config.wipe_tower_bridging), wipe_volumes,
+ m_tool_ordering.first_extruder());
//wipe_tower.set_retract();
//wipe_tower.set_zhop();
// Set the extruder & material properties at the wipe tower object.
- for (size_t i = 0; i < (int)(sqrt(wiping_volumes.size())+EPSILON); ++ i)
+ for (size_t i = 0; i < number_of_extruders; ++ i)
wipe_tower.set_extruder(
i,
WipeTowerPrusaMM::parse_material(this->config.filament_type.get_at(i).c_str()),
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),
+ this->config.filament_cooling_final_speed.get_at(i),
this->config.filament_ramming_parameters.get_at(i),
this->config.nozzle_diameter.get_at(i));
- // When printing the first layer's wipe tower, the first extruder is expected to be active and primed.
- // Therefore the number of wipe sections at the wipe tower will be (m_tool_ordering.front().extruders-1) at the 1st layer.
- // The following variable is true if the last priming section cannot be squeezed inside the wipe tower.
- bool last_priming_wipe_full = m_tool_ordering.front().extruders.size() > m_tool_ordering.front().wipe_tower_partitions;
-
m_wipe_tower_priming = Slic3r::make_unique<WipeTower::ToolChangeResult>(
- wipe_tower.prime(this->skirt_first_layer_height(), m_tool_ordering.all_extruders(), ! last_priming_wipe_full));
-
+ wipe_tower.prime(this->skirt_first_layer_height(), m_tool_ordering.all_extruders(), false));
// Lets go through the wipe tower layers and determine pairs of extruder changes for each
// to pass to wipe_tower (so that it can use it for planning the layout of the tower)
{
unsigned int current_extruder_id = m_tool_ordering.all_extruders().back();
- for (const auto &layer_tools : m_tool_ordering.layer_tools()) { // for all layers
+ for (auto &layer_tools : m_tool_ordering.layer_tools()) { // for all layers
if (!layer_tools.has_wipe_tower) continue;
bool first_layer = &layer_tools == &m_tool_ordering.front();
wipe_tower.plan_toolchange(layer_tools.print_z, layer_tools.wipe_tower_layer_height, current_extruder_id, current_extruder_id,false);
for (const auto extruder_id : layer_tools.extruders) {
if ((first_layer && extruder_id == m_tool_ordering.all_extruders().back()) || extruder_id != current_extruder_id) {
- wipe_tower.plan_toolchange(layer_tools.print_z, layer_tools.wipe_tower_layer_height, current_extruder_id, extruder_id, first_layer && extruder_id == m_tool_ordering.all_extruders().back());
+ float volume_to_wipe = wipe_volumes[current_extruder_id][extruder_id]; // total volume to wipe after this toolchange
+ // Not all of that can be used for infill purging:
+ volume_to_wipe -= config.filament_minimal_purge_on_wipe_tower.get_at(extruder_id);
+
+ // try to assign some infills/objects for the wiping:
+ volume_to_wipe = layer_tools.wiping_extrusions().mark_wiping_extrusions(*this, current_extruder_id, extruder_id, volume_to_wipe);
+
+ // add back the minimal amount toforce on the wipe tower:
+ volume_to_wipe += config.filament_minimal_purge_on_wipe_tower.get_at(extruder_id);
+
+ // request a toolchange at the wipe tower with at least volume_to_wipe purging amount
+ wipe_tower.plan_toolchange(layer_tools.print_z, layer_tools.wipe_tower_layer_height, current_extruder_id, extruder_id,
+ first_layer && extruder_id == m_tool_ordering.all_extruders().back(), volume_to_wipe);
current_extruder_id = extruder_id;
}
}
+ layer_tools.wiping_extrusions().ensure_perimeters_infills_order(*this);
if (&layer_tools == &m_tool_ordering.back() || (&layer_tools + 1)->wipe_tower_partitions == 0)
break;
}
}
-
-
// Generate the wipe tower layers.
m_wipe_tower_tool_changes.reserve(m_tool_ordering.layer_tools().size());
wipe_tower.generate(m_wipe_tower_tool_changes);
-
- // Set current_extruder_id to the last extruder primed.
- /*unsigned int current_extruder_id = m_tool_ordering.all_extruders().back();
+ m_wipe_tower_depth = wipe_tower.get_depth();
- for (const ToolOrdering::LayerTools &layer_tools : m_tool_ordering.layer_tools()) {
- if (! layer_tools.has_wipe_tower)
- // This is a support only layer, or the wipe tower does not reach to this height.
- continue;
- bool first_layer = &layer_tools == &m_tool_ordering.front();
- bool last_layer = &layer_tools == &m_tool_ordering.back() || (&layer_tools + 1)->wipe_tower_partitions == 0;
- wipe_tower.set_layer(
- float(layer_tools.print_z),
- float(layer_tools.wipe_tower_layer_height),
- layer_tools.wipe_tower_partitions,
- first_layer,
- last_layer);
- std::vector<WipeTower::ToolChangeResult> tool_changes;
- for (unsigned int extruder_id : layer_tools.extruders)
- // Call the wipe_tower.tool_change() at the first layer for the initial extruder
- // to extrude the wipe tower brim,
- if ((first_layer && extruder_id == m_tool_ordering.all_extruders().back()) ||
- // or when an extruder shall be switched.
- extruder_id != current_extruder_id) {
- tool_changes.emplace_back(wipe_tower.tool_change(extruder_id, extruder_id == layer_tools.extruders.back(), WipeTower::PURPOSE_EXTRUDE));
- current_extruder_id = extruder_id;
- }
- if (! wipe_tower.layer_finished()) {
- tool_changes.emplace_back(wipe_tower.finish_layer(WipeTower::PURPOSE_EXTRUDE));
- if (tool_changes.size() > 1) {
- // Merge the two last tool changes into one.
- WipeTower::ToolChangeResult &tc1 = tool_changes[tool_changes.size() - 2];
- WipeTower::ToolChangeResult &tc2 = tool_changes.back();
- if (tc1.end_pos != tc2.start_pos) {
- // Add a travel move from tc1.end_pos to tc2.start_pos.
- char buf[2048];
- sprintf(buf, "G1 X%.3f Y%.3f F7200\n", tc2.start_pos.x, tc2.start_pos.y);
- tc1.gcode += buf;
- }
- tc1.gcode += tc2.gcode;
- append(tc1.extrusions, tc2.extrusions);
- tc1.end_pos = tc2.end_pos;
- tool_changes.pop_back();
- }
- }
- m_wipe_tower_tool_changes.emplace_back(std::move(tool_changes));
- if (last_layer)
- break;
- }*/
-
// Unload the current filament over the purge tower.
coordf_t layer_height = this->objects.front()->config.layer_height.value;
if (m_tool_ordering.back().wipe_tower_partitions > 0) {
@@ -1204,6 +1197,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()
@@ -1212,7 +1208,7 @@ std::string Print::output_filename()
try {
return this->placeholder_parser.process(this->config.output_filename_format.value, 0);
} catch (std::runtime_error &err) {
- throw std::runtime_error(std::string("Failed processing of the output_filename_format template.\n") + err.what());
+ throw std::runtime_error(L("Failed processing of the output_filename_format template.") + "\n" + err.what());
}
}
@@ -1244,4 +1240,11 @@ void Print::set_status(int percent, const std::string &message)
printf("Print::status %d => %s\n", percent, message.c_str());
}
+// Returns extruder this eec should be printed with, according to PrintRegion config
+int Print::get_extruder(const ExtrusionEntityCollection& fill, const PrintRegion &region)
+{
+ return is_infill(fill.role()) ? std::max<int>(0, (is_solid_infill(fill.entities.front()->role()) ? region.config.solid_infill_extruder : region.config.infill_extruder) - 1) :
+ std::max<int>(region.config.perimeter_extruder.value - 1, 0);
+}
+
}
diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp
index 86c15b679..3ef720db3 100644
--- a/xs/src/libslic3r/Print.hpp
+++ b/xs/src/libslic3r/Print.hpp
@@ -24,6 +24,7 @@ class Print;
class PrintObject;
class ModelObject;
+
// Print step IDs for keeping track of the print state.
enum PrintStep {
psSkirt, psBrim, psWipeTower, psCount,
@@ -79,7 +80,10 @@ public:
Print* print() { return this->_print; }
Flow flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, const PrintObject &object) const;
+ // Average diameter of nozzles participating on extruding this region.
coordf_t nozzle_dmr_avg(const PrintConfig &print_config) const;
+ // Average diameter of nozzles participating on extruding this region.
+ coordf_t bridging_height_avg(const PrintConfig &print_config) const;
private:
Print* _print;
@@ -208,6 +212,12 @@ public:
void combine_infill();
void _generate_support_material();
+ bool is_printable() const { return !this->_shifted_copies.empty(); }
+
+ // Helpers to slice support enforcer / blocker meshes by the support generator.
+ std::vector<ExPolygons> slice_support_enforcers() const;
+ std::vector<ExPolygons> slice_support_blockers() const;
+
private:
Print* _print;
ModelObject* _model_object;
@@ -219,6 +229,7 @@ private:
~PrintObject() {}
std::vector<ExPolygons> _slice_region(size_t region_id, const std::vector<float> &z, bool modifier);
+ std::vector<ExPolygons> _slice_volumes(const std::vector<float> &z, const std::vector<const ModelVolume*> &volumes) const;
};
typedef std::vector<PrintObject*> PrintObjectPtrs;
@@ -235,8 +246,9 @@ public:
PrintRegionPtrs regions;
PlaceholderParser placeholder_parser;
// TODO: status_cb
- std::string estimated_print_time;
- double total_used_filament, total_extruded_volume, total_cost, total_weight;
+ std::string estimated_normal_print_time;
+ std::string estimated_silent_print_time;
+ 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;
@@ -255,6 +267,8 @@ public:
void reload_object(size_t idx);
bool reload_model_instances();
+ PrintObjectPtrs get_printable_objects() const;
+
// methods for handling regions
PrintRegion* get_region(size_t idx) { return regions.at(idx); }
const PrintRegion* get_region(size_t idx) const { return regions.at(idx); }
@@ -267,6 +281,7 @@ public:
void add_model_object(ModelObject* model_object, int idx = -1);
bool apply_config(DynamicPrintConfig config);
+ float get_wipe_tower_depth() const { return m_wipe_tower_depth; }
bool has_infinite_skirt() const;
bool has_skirt() const;
// Returns an empty string if valid, otherwise returns an error message.
@@ -285,6 +300,9 @@ public:
bool has_support_material() const;
void auto_assign_extruders(ModelObject* model_object) const;
+ // Returns extruder this eec should be printed with, according to PrintRegion config:
+ static int get_extruder(const ExtrusionEntityCollection& fill, const PrintRegion &region);
+
void _make_skirt();
void _make_brim();
@@ -299,6 +317,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);
@@ -311,15 +331,20 @@ public:
void restart() { m_canceled = false; }
// Has the calculation been canceled?
bool canceled() { return m_canceled; }
-
+
+
private:
bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);
PrintRegionConfig _region_config_from_model_volume(const ModelVolume &volume);
+ // Depth of the wipe tower to pass to GLCanvas3D for exact bounding box:
+ float m_wipe_tower_depth = 0.f;
+
// Has the calculation been canceled?
tbb::atomic<bool> m_canceled;
};
+
#define FOREACH_BASE(type, container, iterator) for (type::const_iterator iterator = (container).begin(); iterator != (container).end(); ++iterator)
#define FOREACH_REGION(print, region) FOREACH_BASE(PrintRegionPtrs, (print)->regions, region)
#define FOREACH_OBJECT(print, object) FOREACH_BASE(PrintObjectPtrs, (print)->objects, object)
diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp
index b77a3a76e..decbc7833 100644
--- a/xs/src/libslic3r/PrintConfig.cpp
+++ b/xs/src/libslic3r/PrintConfig.cpp
@@ -1,7 +1,11 @@
#include "PrintConfig.hpp"
+#include "I18N.hpp"
+#include <algorithm>
#include <set>
#include <boost/algorithm/string/replace.hpp>
+#include <boost/algorithm/string/case_conv.hpp>
+#include <boost/format.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/thread.hpp>
@@ -11,7 +15,7 @@ namespace Slic3r {
//! macro used to mark string used at localization,
//! return same string
-#define L(s) s
+#define L(s) Slic3r::I18N::translate(s)
PrintConfigDef::PrintConfigDef()
{
@@ -120,7 +124,7 @@ PrintConfigDef::PrintConfigDef()
def->tooltip = L("Speed for printing bridges.");
def->sidetext = L("mm/s");
def->cli = "bridge-speed=f";
- def->aliases.push_back("bridge_feed_rate");
+ def->aliases = { "bridge_feed_rate" };
def->min = 0;
def->default_value = new ConfigOptionFloat(60);
@@ -151,6 +155,11 @@ PrintConfigDef::PrintConfigDef()
"with the active printer profile.");
def->default_value = new ConfigOptionString();
+ // The following value is to be stored into the project file (AMF, 3MF, Config ...)
+ // and it contains a sum of "compatible_printers_condition" values over the print and filament profiles.
+ def = this->add("compatible_printers_condition_cummulative", coStrings);
+ def->default_value = new ConfigOptionStrings();
+
def = this->add("complete_objects", coBool);
def->label = L("Complete individual objects");
def->tooltip = L("When printing multiple objects or copies, this feature will complete "
@@ -228,7 +237,7 @@ PrintConfigDef::PrintConfigDef()
def->tooltip = L("Distance used for the auto-arrange feature of the plater.");
def->sidetext = L("mm");
def->cli = "duplicate-distance=f";
- def->aliases.push_back("multiply_distance");
+ def->aliases = { "multiply_distance" };
def->min = 0;
def->default_value = new ConfigOptionFloat(6);
@@ -283,13 +292,13 @@ PrintConfigDef::PrintConfigDef()
def->enum_values.push_back("hilbertcurve");
def->enum_values.push_back("archimedeanchords");
def->enum_values.push_back("octagramspiral");
- def->enum_labels.push_back("Rectilinear");
- def->enum_labels.push_back("Concentric");
- def->enum_labels.push_back("Hilbert Curve");
- def->enum_labels.push_back("Archimedean Chords");
- def->enum_labels.push_back("Octagram Spiral");
+ def->enum_labels.push_back(L("Rectilinear"));
+ def->enum_labels.push_back(L("Concentric"));
+ def->enum_labels.push_back(L("Hilbert Curve"));
+ def->enum_labels.push_back(L("Archimedean Chords"));
+ def->enum_labels.push_back(L("Octagram Spiral"));
// solid_fill_pattern is an obsolete equivalent to external_fill_pattern.
- def->aliases.push_back("solid_fill_pattern");
+ def->aliases = { "solid_fill_pattern" };
def->default_value = new ConfigOptionEnum<InfillPattern>(ipRectilinear);
def = this->add("external_perimeter_extrusion_width", coFloatOrPercent);
@@ -344,6 +353,7 @@ PrintConfigDef::PrintConfigDef()
def->enum_labels.push_back("2");
def->enum_labels.push_back("3");
def->enum_labels.push_back("4");
+ def->enum_labels.push_back("5");
def = this->add("extruder_clearance_height", coFloat);
def->label = L("Height");
@@ -464,6 +474,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 "
@@ -473,6 +491,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. "
@@ -483,6 +509,50 @@ PrintConfigDef::PrintConfigDef()
def->min = 0;
def->default_value = new ConfigOptionFloats { 0. };
+ def = this->add("filament_cooling_moves", coInts);
+ def->label = L("Number of cooling moves");
+ def->tooltip = L("Filament is cooled by being moved back and forth in the "
+ "cooling tubes. Specify desired number of these moves ");
+ def->cli = "filament-cooling-moves=i@";
+ def->max = 0;
+ def->max = 20;
+ def->default_value = new ConfigOptionInts { 4 };
+
+ def = this->add("filament_cooling_initial_speed", coFloats);
+ def->label = L("Speed of the first cooling move");
+ def->tooltip = L("Cooling moves are gradually accelerating beginning at this speed. ");
+ def->cli = "filament-cooling-initial-speed=f@";
+ def->sidetext = L("mm/s");
+ def->min = 0;
+ def->default_value = new ConfigOptionFloats { 2.2f };
+
+ def = this->add("filament_minimal_purge_on_wipe_tower", coFloats);
+ def->label = L("Minimal purge on wipe tower");
+ def->tooltip = L("After a tool change, the exact position of the newly loaded filament inside "
+ "the nozzle may not be known, and the filament pressure is likely not yet stable. "
+ "Before purging the print head into an infill or a sacrificial object, Slic3r will always prime "
+ "this amount of material into the wipe tower to produce successive infill or sacrificial object extrusions reliably.");
+ def->cli = "filament-minimal-purge-on-wipe-tower=f@";
+ def->sidetext = L("mm³");
+ def->min = 0;
+ def->default_value = new ConfigOptionFloats { 15.f };
+
+ def = this->add("filament_cooling_final_speed", coFloats);
+ def->label = L("Speed of the last cooling move");
+ def->tooltip = L("Cooling moves are gradually accelerating towards this speed. ");
+ def->cli = "filament-cooling-final-speed=f@";
+ def->sidetext = L("mm/s");
+ def->min = 0;
+ def->default_value = new ConfigOptionFloats { 3.4f };
+
+ def = this->add("filament_load_time", coFloats);
+ def->label = L("Filament load time");
+ def->tooltip = L("Time for the printer firmware (or the Multi Material Unit 2.0) to load a new filament during a tool change (when executing the T code). This time is added to the total print time by the G-code time estimator.");
+ def->cli = "filament-load-time=i@";
+ def->sidetext = L("s");
+ def->min = 0;
+ def->default_value = new ConfigOptionFloats { 0.0f };
+
def = this->add("filament_ramming_parameters", coStrings);
def->label = L("Ramming parameters");
def->tooltip = L("This string is edited by RammingDialog and contains ramming specific parameters ");
@@ -490,6 +560,14 @@ PrintConfigDef::PrintConfigDef()
def->default_value = new ConfigOptionStrings { "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" };
+ def = this->add("filament_unload_time", coFloats);
+ def->label = L("Filament unload time");
+ def->tooltip = L("Time for the printer firmware (or the Multi Material Unit 2.0) to unload a filament during a tool change (when executing the T code). This time is added to the total print time by the G-code time estimator.");
+ def->cli = "filament-unload-time=i@";
+ def->sidetext = L("s");
+ def->min = 0;
+ def->default_value = new ConfigOptionFloats { 0.0f };
+
def = this->add("filament_diameter", coFloats);
def->label = L("Diameter");
def->tooltip = L("Enter your filament diameter here. Good precision is required, so use a caliper "
@@ -511,10 +589,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";
@@ -617,19 +692,19 @@ PrintConfigDef::PrintConfigDef()
def->enum_values.push_back("hilbertcurve");
def->enum_values.push_back("archimedeanchords");
def->enum_values.push_back("octagramspiral");
- def->enum_labels.push_back("Rectilinear");
- def->enum_labels.push_back("Grid");
- def->enum_labels.push_back("Triangles");
- def->enum_labels.push_back("Stars");
- def->enum_labels.push_back("Cubic");
- def->enum_labels.push_back("Line");
- def->enum_labels.push_back("Concentric");
- def->enum_labels.push_back("Honeycomb");
- def->enum_labels.push_back("3D Honeycomb");
- def->enum_labels.push_back("Gyroid");
- def->enum_labels.push_back("Hilbert Curve");
- def->enum_labels.push_back("Archimedean Chords");
- def->enum_labels.push_back("Octagram Spiral");
+ def->enum_labels.push_back(L("Rectilinear"));
+ def->enum_labels.push_back(L("Grid"));
+ def->enum_labels.push_back(L("Triangles"));
+ def->enum_labels.push_back(L("Stars"));
+ def->enum_labels.push_back(L("Cubic"));
+ def->enum_labels.push_back(L("Line"));
+ def->enum_labels.push_back(L("Concentric"));
+ def->enum_labels.push_back(L("Honeycomb"));
+ def->enum_labels.push_back(L("3D Honeycomb"));
+ def->enum_labels.push_back(L("Gyroid"));
+ def->enum_labels.push_back(L("Hilbert Curve"));
+ def->enum_labels.push_back(L("Archimedean Chords"));
+ def->enum_labels.push_back(L("Octagram Spiral"));
def->default_value = new ConfigOptionEnum<InfillPattern>(ipStars);
def = this->add("first_layer_acceleration", coFloat);
@@ -737,8 +812,8 @@ PrintConfigDef::PrintConfigDef()
def->enum_labels.push_back("Mach3/LinuxCNC");
def->enum_labels.push_back("Machinekit");
def->enum_labels.push_back("Smoothie");
- def->enum_labels.push_back("No extrusion");
- def->default_value = new ConfigOptionEnum<GCodeFlavor>(gcfMarlin);
+ def->enum_labels.push_back(L("No extrusion"));
+ def->default_value = new ConfigOptionEnum<GCodeFlavor>(gcfRepRap);
def = this->add("infill_acceleration", coFloat);
def->label = L("Infill");
@@ -811,8 +886,7 @@ PrintConfigDef::PrintConfigDef()
def->tooltip = L("Speed for printing the internal fill. Set to zero for auto.");
def->sidetext = L("mm/s");
def->cli = "infill-speed=f";
- def->aliases.push_back("print_feed_rate");
- def->aliases.push_back("infill_feed_rate");
+ def->aliases = { "print_feed_rate", "infill_feed_rate" };
def->min = 0;
def->default_value = new ConfigOptionFloat(80);
@@ -821,7 +895,12 @@ PrintConfigDef::PrintConfigDef()
def->tooltip = L("Name of the profile, from which this profile inherits.");
def->full_width = true;
def->height = 50;
- def->default_value = new ConfigOptionString("");
+ def->default_value = new ConfigOptionString();
+
+ // The following value is to be stored into the project file (AMF, 3MF, Config ...)
+ // and it contains a sum of "inherits" values over the print and filament profiles.
+ def = this->add("inherits_cummulative", coStrings);
+ def->default_value = new ConfigOptionStrings();
def = this->add("interface_shells", coBool);
def->label = L("Interface shells");
@@ -853,6 +932,106 @@ PrintConfigDef::PrintConfigDef()
def->min = 0;
def->default_value = new ConfigOptionFloat(0.3);
+ def = this->add("remaining_times", coBool);
+ def->label = L("Supports remaining times");
+ 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.");
+ def->default_value = new ConfigOptionBool(false);
+
+ def = this->add("silent_mode", coBool);
+ def->label = L("Supports silent mode");
+ def->tooltip = L("Set silent mode for the G-code flavor");
+ def->default_value = new ConfigOptionBool(true);
+
+ const int machine_limits_opt_width = 70;
+ {
+ struct AxisDefault {
+ std::string name;
+ std::vector<double> max_feedrate;
+ std::vector<double> max_acceleration;
+ std::vector<double> max_jerk;
+ };
+ std::vector<AxisDefault> axes {
+ // name, max_feedrate, max_acceleration, max_jerk
+ { "x", { 500., 200. }, { 9000., 1000. }, { 10. , 10. } },
+ { "y", { 500., 200. }, { 9000., 1000. }, { 10. , 10. } },
+ { "z", { 12., 12. }, { 500., 200. }, { 0.2, 0.4 } },
+ { "e", { 120., 120. }, { 10000., 5000. }, { 2.5, 2.5 } }
+ };
+ for (const AxisDefault &axis : axes) {
+ std::string axis_upper = boost::to_upper_copy<std::string>(axis.name);
+ // Add the machine feedrate limits for XYZE axes. (M203)
+ def = this->add("machine_max_feedrate_" + axis.name, coFloats);
+ def->full_label = (boost::format(L("Maximum feedrate %1%")) % axis_upper).str();
+ def->category = L("Machine limits");
+ def->tooltip = (boost::format(L("Maximum feedrate of the %1% axis")) % axis_upper).str();
+ def->sidetext = L("mm/s");
+ def->min = 0;
+ def->width = machine_limits_opt_width;
+ def->default_value = new ConfigOptionFloats(axis.max_feedrate);
+ // Add the machine acceleration limits for XYZE axes (M201)
+ def = this->add("machine_max_acceleration_" + axis.name, coFloats);
+ def->full_label = (boost::format(L("Maximum acceleration %1%")) % axis_upper).str();
+ def->category = L("Machine limits");
+ def->tooltip = (boost::format(L("Maximum acceleration of the %1% axis")) % axis_upper).str();
+ def->sidetext = L("mm/s²");
+ def->min = 0;
+ def->width = machine_limits_opt_width;
+ def->default_value = new ConfigOptionFloats(axis.max_acceleration);
+ // Add the machine jerk limits for XYZE axes (M205)
+ def = this->add("machine_max_jerk_" + axis.name, coFloats);
+ def->full_label = (boost::format(L("Maximum jerk %1%")) % axis_upper).str();
+ def->category = L("Machine limits");
+ def->tooltip = (boost::format(L("Maximum jerk of the %1% axis")) % axis_upper).str();
+ def->sidetext = L("mm/s");
+ def->min = 0;
+ def->width = machine_limits_opt_width;
+ def->default_value = new ConfigOptionFloats(axis.max_jerk);
+ }
+ }
+
+ // M205 S... [mm/sec]
+ def = this->add("machine_min_extruding_rate", coFloats);
+ def->full_label = L("Minimum feedrate when extruding");
+ def->category = L("Machine limits");
+ def->tooltip = L("Minimum feedrate when extruding") + " (M205 S)";
+ def->sidetext = L("mm/s");
+ def->min = 0;
+ def->width = machine_limits_opt_width;
+ def->default_value = new ConfigOptionFloats{ 0., 0. };
+
+ // M205 T... [mm/sec]
+ def = this->add("machine_min_travel_rate", coFloats);
+ def->full_label = L("Minimum travel feedrate");
+ def->category = L("Machine limits");
+ def->tooltip = L("Minimum travel feedrate") + " (M205 T)";
+ def->sidetext = L("mm/s");
+ def->min = 0;
+ def->width = machine_limits_opt_width;
+ def->default_value = new ConfigOptionFloats{ 0., 0. };
+
+ // M204 S... [mm/sec^2]
+ def = this->add("machine_max_acceleration_extruding", coFloats);
+ def->full_label = L("Maximum acceleration when extruding");
+ def->category = L("Machine limits");
+ def->tooltip = L("Maximum acceleration when extruding") + " (M204 S)";
+ def->sidetext = L("mm/s²");
+ def->min = 0;
+ def->width = machine_limits_opt_width;
+ def->default_value = new ConfigOptionFloats{ 1500., 1250. };
+
+ // M204 T... [mm/sec^2]
+ def = this->add("machine_max_acceleration_retracting", coFloats);
+ def->full_label = L("Maximum acceleration when retracting");
+ def->category = L("Machine limits");
+ def->tooltip = L("Maximum acceleration when retracting") + " (M204 T)";
+ def->sidetext = L("mm/s²");
+ def->min = 0;
+ def->width = machine_limits_opt_width;
+ def->default_value = new ConfigOptionFloats{ 1500., 1250. };
+
def = this->add("max_fan_speed", coInts);
def->label = L("Max");
def->tooltip = L("This setting represents the maximum speed of your fan.");
@@ -974,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);
@@ -1037,6 +1228,15 @@ PrintConfigDef::PrintConfigDef()
def->min = 0;
def->default_value = new ConfigOptionFloat(92.f);
+ def = this->add("extra_loading_move", coFloat);
+ def->label = L("Extra loading distance");
+ def->tooltip = L("When set to zero, the distance the filament is moved from parking position during load "
+ "is exactly the same as it was moved back during unload. When positive, it is loaded further, "
+ " if negative, the loading move is shorter than unloading. ");
+ def->sidetext = L("mm");
+ def->cli = "extra_loading_move=f";
+ def->default_value = new ConfigOptionFloat(-2.f);
+
def = this->add("perimeter_acceleration", coFloat);
def->label = L("Perimeters");
def->tooltip = L("This is the acceleration your printer will use for perimeters. "
@@ -1051,7 +1251,7 @@ PrintConfigDef::PrintConfigDef()
def->category = L("Extruders");
def->tooltip = L("The extruder to use when printing perimeters and brim. First extruder is 1.");
def->cli = "perimeter-extruder=i";
- def->aliases.push_back("perimeters_extruder");
+ def->aliases = { "perimeters_extruder" };
def->min = 1;
def->default_value = new ConfigOptionInt(1);
@@ -1064,7 +1264,7 @@ PrintConfigDef::PrintConfigDef()
"If expressed as percentage (for example 200%) it will be computed over layer height.");
def->sidetext = L("mm or % (leave 0 for default)");
def->cli = "perimeter-extrusion-width=s";
- def->aliases.push_back("perimeters_extrusion_width");
+ def->aliases = { "perimeters_extrusion_width" };
def->default_value = new ConfigOptionFloatOrPercent(0, false);
def = this->add("perimeter_speed", coFloat);
@@ -1073,7 +1273,7 @@ PrintConfigDef::PrintConfigDef()
def->tooltip = L("Speed for perimeters (contours, aka vertical shells). Set to zero for auto.");
def->sidetext = L("mm/s");
def->cli = "perimeter-speed=f";
- def->aliases.push_back("perimeter_feed_rate");
+ def->aliases = { "perimeter_feed_rate" };
def->min = 0;
def->default_value = new ConfigOptionFloat(60);
@@ -1086,7 +1286,7 @@ PrintConfigDef::PrintConfigDef()
"if the Extra Perimeters option is enabled.");
def->sidetext = L("(minimum)");
def->cli = "perimeters=i";
- def->aliases.push_back("perimeter_offsets");
+ def->aliases = { "perimeter_offsets" };
def->min = 0;
def->default_value = new ConfigOptionInt(3);
@@ -1265,10 +1465,10 @@ PrintConfigDef::PrintConfigDef()
def->enum_values.push_back("nearest");
def->enum_values.push_back("aligned");
def->enum_values.push_back("rear");
- def->enum_labels.push_back("Random");
- def->enum_labels.push_back("Nearest");
- def->enum_labels.push_back("Aligned");
- def->enum_labels.push_back("Rear");
+ def->enum_labels.push_back(L("Random"));
+ def->enum_labels.push_back(L("Nearest"));
+ def->enum_labels.push_back(L("Aligned"));
+ def->enum_labels.push_back(L("Rear"));
def->default_value = new ConfigOptionEnum<SeamPosition>(spAligned);
#if 0
@@ -1414,7 +1614,7 @@ PrintConfigDef::PrintConfigDef()
def->sidetext = L("mm/s or %");
def->cli = "solid-infill-speed=s";
def->ratio_over = "infill_speed";
- def->aliases.push_back("solid_infill_feed_rate");
+ def->aliases = { "solid_infill_feed_rate" };
def->min = 0;
def->default_value = new ConfigOptionFloatOrPercent(20, false);
@@ -1481,7 +1681,13 @@ PrintConfigDef::PrintConfigDef()
def->label = L("Single Extruder Multi Material");
def->tooltip = L("The printer multiplexes filaments into a single hot end.");
def->cli = "single-extruder-multi-material!";
- def->default_value = new ConfigOptionBool(false);
+ def->default_value = new ConfigOptionBool(false);
+
+ def = this->add("single_extruder_multi_material_priming", coBool);
+ def->label = L("Prime all printing extruders");
+ def->tooltip = L("If enabled, all printing extruders will be primed at the front edge of the print bed at the start of the print.");
+ def->cli = "single-extruder-multi-material-priming!";
+ def->default_value = new ConfigOptionBool(true);
def = this->add("support_material", coBool);
def->label = L("Generate support material");
@@ -1490,6 +1696,14 @@ PrintConfigDef::PrintConfigDef()
def->cli = "support-material!";
def->default_value = new ConfigOptionBool(false);
+ def = this->add("support_material_auto", coBool);
+ def->label = L("Auto generated supports");
+ def->category = L("Support material");
+ def->tooltip = L("If checked, supports will be generated automatically based on the overhang threshold value."\
+ " If unchecked, supports will be generated inside the \"Support Enforcer\" volumes only.");
+ def->cli = "support-material-auto!";
+ def->default_value = new ConfigOptionBool(true);
+
def = this->add("support_material_xy_spacing", coFloatOrPercent);
def->label = L("XY separation between an object and its support");
def->category = L("Support material");
@@ -1528,11 +1742,11 @@ PrintConfigDef::PrintConfigDef()
"for the first object layer.");
def->sidetext = L("mm");
def->cli = "support-material-contact-distance=f";
- def->min = 0;
+// def->min = 0;
def->enum_values.push_back("0");
def->enum_values.push_back("0.2");
- def->enum_labels.push_back("0 (soluble)");
- def->enum_labels.push_back("0.2 (detachable)");
+ def->enum_labels.push_back((boost::format("0 (%1%)") % L("soluble")).str());
+ def->enum_labels.push_back((boost::format("0.2 (%1%)") % L("detachable")).str());
def->default_value = new ConfigOptionFloat(0.2);
def = this->add("support_material_enforce_layers", coInt);
@@ -1621,9 +1835,9 @@ PrintConfigDef::PrintConfigDef()
def->enum_values.push_back("rectilinear");
def->enum_values.push_back("rectilinear-grid");
def->enum_values.push_back("honeycomb");
- def->enum_labels.push_back("rectilinear");
- def->enum_labels.push_back("rectilinear grid");
- def->enum_labels.push_back("honeycomb");
+ def->enum_labels.push_back(L("Rectilinear"));
+ def->enum_labels.push_back(L("Rectilinear grid"));
+ def->enum_labels.push_back(L("Honeycomb"));
def->default_value = new ConfigOptionEnum<SupportMaterialPattern>(smpRectilinear);
def = this->add("support_material_spacing", coFloat);
@@ -1754,7 +1968,7 @@ PrintConfigDef::PrintConfigDef()
def->tooltip = L("Speed for travel moves (jumps between distant extrusion points).");
def->sidetext = L("mm/s");
def->cli = "travel-speed=f";
- def->aliases.push_back("travel_feed_rate");
+ def->aliases = { "travel_feed_rate" };
def->min = 1;
def->default_value = new ConfigOptionFloat(130);
@@ -1850,7 +2064,25 @@ PrintConfigDef::PrintConfigDef()
def->sidetext = L("degrees");
def->cli = "wipe-tower-rotation-angle=f";
def->default_value = new ConfigOptionFloat(0.);
-
+
+ def = this->add("wipe_into_infill", coBool);
+ def->category = L("Extruders");
+ 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.");
+ def->cli = "wipe-into-infill!";
+ def->default_value = new ConfigOptionBool(false);
+
+ def = this->add("wipe_into_objects", coBool);
+ def->category = L("Extruders");
+ 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.");
+ def->cli = "wipe-into-objects!";
+ def->default_value = new ConfigOptionBool(false);
+
def = this->add("wipe_tower_bridging", coFloat);
def->label = L("Maximal bridging distance");
def->tooltip = L("Maximal distance between supports on sparse infill sections. ");
@@ -1911,10 +2143,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;
@@ -1925,6 +2153,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:
@@ -1934,9 +2168,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"
};
@@ -1946,7 +2177,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;
}
@@ -2012,6 +2242,24 @@ std::string DynamicPrintConfig::validate()
return fpc.validate();
}
+size_t DynamicPrintConfig::remove_keys_not_in(const DynamicPrintConfig &default_config, std::string &removed_keys_message)
+{
+ size_t n_removed_keys = 0;
+ for (const std::string &key : this->keys()) {
+ if (! default_config.has(key)) {
+ if (removed_keys_message.empty())
+ removed_keys_message = key;
+ else {
+ removed_keys_message += ", ";
+ removed_keys_message += key;
+ }
+ this->erase(key);
+ ++ n_removed_keys;
+ }
+ }
+ return n_removed_keys;
+}
+
double PrintConfig::min_object_distance() const
{
return PrintConfig::min_object_distance(static_cast<const ConfigBase*>(this));
@@ -2198,6 +2446,7 @@ std::string FullPrintConfig::validate()
// Declare the static caches for each StaticPrintConfig derived class.
StaticPrintConfig::StaticCache<class Slic3r::PrintObjectConfig> PrintObjectConfig::s_cache_PrintObjectConfig;
StaticPrintConfig::StaticCache<class Slic3r::PrintRegionConfig> PrintRegionConfig::s_cache_PrintRegionConfig;
+StaticPrintConfig::StaticCache<class Slic3r::MachineEnvelopeConfig> MachineEnvelopeConfig::s_cache_MachineEnvelopeConfig;
StaticPrintConfig::StaticCache<class Slic3r::GCodeConfig> GCodeConfig::s_cache_GCodeConfig;
StaticPrintConfig::StaticCache<class Slic3r::PrintConfig> PrintConfig::s_cache_PrintConfig;
StaticPrintConfig::StaticCache<class Slic3r::HostConfig> HostConfig::s_cache_HostConfig;
diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp
index 2e36ca665..28d5aa8f4 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()) {
@@ -155,6 +168,10 @@ public:
// Validate the PrintConfig. Returns an empty string on success, otherwise an error message is returned.
std::string validate();
+ // Remove all keys not in "valid_keys", return number of removed keys and add the list of keys to "removed_keys_message.
+ // valid_keys has to be sorted lexicographically.
+ size_t remove_keys_not_in(const DynamicPrintConfig &default_config, std::string &removed_keys_message);
+
// Verify whether the opt_key has not been obsoleted or renamed.
// Both opt_key and value may be modified by handle_legacy().
// If the opt_key is no more valid in this version of Slic3r, opt_key is cleared by handle_legacy().
@@ -310,6 +327,7 @@ public:
ConfigOptionFloatOrPercent extrusion_width;
ConfigOptionFloatOrPercent first_layer_height;
ConfigOptionBool infill_only_where_needed;
+ // Force the generation of solid shells between adjacent materials/volumes.
ConfigOptionBool interface_shells;
ConfigOptionFloat layer_height;
ConfigOptionInt raft_layers;
@@ -317,6 +335,9 @@ public:
// ConfigOptionFloat seam_preferred_direction;
// ConfigOptionFloat seam_preferred_direction_jitter;
ConfigOptionBool support_material;
+ // Automatic supports (generated based on support_material_threshold).
+ ConfigOptionBool support_material_auto;
+ // Direction of the support pattern (in XY plane).
ConfigOptionFloat support_material_angle;
ConfigOptionBool support_material_buildplate_only;
ConfigOptionFloat support_material_contact_distance;
@@ -326,17 +347,21 @@ public:
ConfigOptionBool support_material_interface_contact_loops;
ConfigOptionInt support_material_interface_extruder;
ConfigOptionInt support_material_interface_layers;
+ // Spacing between interface lines (the hatching distance). Set zero to get a solid interface.
ConfigOptionFloat support_material_interface_spacing;
ConfigOptionFloatOrPercent support_material_interface_speed;
ConfigOptionEnum<SupportMaterialPattern> support_material_pattern;
+ // Spacing between support material lines (the hatching distance).
ConfigOptionFloat support_material_spacing;
ConfigOptionFloat support_material_speed;
ConfigOptionBool support_material_synchronize_layers;
+ // Overhang angle threshold.
ConfigOptionInt support_material_threshold;
ConfigOptionBool support_material_with_sheath;
ConfigOptionFloatOrPercent support_material_xy_spacing;
ConfigOptionFloat xy_size_compensation;
-
+ ConfigOptionBool wipe_into_objects;
+
protected:
void initialize(StaticCacheBase &cache, const char *base_ptr)
{
@@ -353,6 +378,7 @@ protected:
// OPT_PTR(seam_preferred_direction);
// OPT_PTR(seam_preferred_direction_jitter);
OPT_PTR(support_material);
+ OPT_PTR(support_material_auto);
OPT_PTR(support_material_angle);
OPT_PTR(support_material_buildplate_only);
OPT_PTR(support_material_contact_distance);
@@ -372,6 +398,7 @@ protected:
OPT_PTR(support_material_threshold);
OPT_PTR(support_material_with_sheath);
OPT_PTR(xy_size_compensation);
+ OPT_PTR(wipe_into_objects);
}
};
@@ -399,10 +426,12 @@ public:
ConfigOptionInt infill_every_layers;
ConfigOptionFloatOrPercent infill_overlap;
ConfigOptionFloat infill_speed;
+ // Detect bridging perimeters
ConfigOptionBool overhangs;
ConfigOptionInt perimeter_extruder;
ConfigOptionFloatOrPercent perimeter_extrusion_width;
ConfigOptionFloat perimeter_speed;
+ // Total number of perimeters.
ConfigOptionInt perimeters;
ConfigOptionFloatOrPercent small_perimeter_speed;
ConfigOptionFloat solid_infill_below_area;
@@ -410,11 +439,13 @@ public:
ConfigOptionFloatOrPercent solid_infill_extrusion_width;
ConfigOptionInt solid_infill_every_layers;
ConfigOptionFloatOrPercent solid_infill_speed;
+ // Detect thin walls.
ConfigOptionBool thin_walls;
ConfigOptionFloatOrPercent top_infill_extrusion_width;
ConfigOptionInt top_solid_layers;
ConfigOptionFloatOrPercent top_solid_infill_speed;
-
+ ConfigOptionBool wipe_into_infill;
+
protected:
void initialize(StaticCacheBase &cache, const char *base_ptr)
{
@@ -452,6 +483,57 @@ protected:
OPT_PTR(top_infill_extrusion_width);
OPT_PTR(top_solid_infill_speed);
OPT_PTR(top_solid_layers);
+ OPT_PTR(wipe_into_infill);
+ }
+};
+
+class MachineEnvelopeConfig : public StaticPrintConfig
+{
+ STATIC_PRINT_CONFIG_CACHE(MachineEnvelopeConfig)
+public:
+ // M201 X... Y... Z... E... [mm/sec^2]
+ ConfigOptionFloats machine_max_acceleration_x;
+ ConfigOptionFloats machine_max_acceleration_y;
+ ConfigOptionFloats machine_max_acceleration_z;
+ ConfigOptionFloats machine_max_acceleration_e;
+ // M203 X... Y... Z... E... [mm/sec]
+ ConfigOptionFloats machine_max_feedrate_x;
+ ConfigOptionFloats machine_max_feedrate_y;
+ ConfigOptionFloats machine_max_feedrate_z;
+ ConfigOptionFloats machine_max_feedrate_e;
+ // M204 S... [mm/sec^2]
+ ConfigOptionFloats machine_max_acceleration_extruding;
+ // M204 T... [mm/sec^2]
+ ConfigOptionFloats machine_max_acceleration_retracting;
+ // M205 X... Y... Z... E... [mm/sec]
+ ConfigOptionFloats machine_max_jerk_x;
+ ConfigOptionFloats machine_max_jerk_y;
+ ConfigOptionFloats machine_max_jerk_z;
+ ConfigOptionFloats machine_max_jerk_e;
+ // M205 T... [mm/sec]
+ ConfigOptionFloats machine_min_travel_rate;
+ // M205 S... [mm/sec]
+ ConfigOptionFloats machine_min_extruding_rate;
+
+protected:
+ void initialize(StaticCacheBase &cache, const char *base_ptr)
+ {
+ OPT_PTR(machine_max_acceleration_x);
+ OPT_PTR(machine_max_acceleration_y);
+ OPT_PTR(machine_max_acceleration_z);
+ OPT_PTR(machine_max_acceleration_e);
+ OPT_PTR(machine_max_feedrate_x);
+ OPT_PTR(machine_max_feedrate_y);
+ OPT_PTR(machine_max_feedrate_z);
+ OPT_PTR(machine_max_feedrate_e);
+ OPT_PTR(machine_max_acceleration_extruding);
+ OPT_PTR(machine_max_acceleration_retracting);
+ OPT_PTR(machine_max_jerk_x);
+ OPT_PTR(machine_max_jerk_y);
+ OPT_PTR(machine_max_jerk_z);
+ OPT_PTR(machine_max_jerk_e);
+ OPT_PTR(machine_min_travel_rate);
+ OPT_PTR(machine_min_extruding_rate);
}
};
@@ -474,8 +556,16 @@ 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;
+ ConfigOptionFloats filament_cooling_initial_speed;
+ ConfigOptionFloats filament_minimal_purge_on_wipe_tower;
+ ConfigOptionFloats filament_cooling_final_speed;
ConfigOptionStrings filament_ramming_parameters;
ConfigOptionBool gcode_comments;
ConfigOptionEnum<GCodeFlavor> gcode_flavor;
@@ -496,6 +586,7 @@ public:
ConfigOptionString start_gcode;
ConfigOptionStrings start_filament_gcode;
ConfigOptionBool single_extruder_multi_material;
+ ConfigOptionBool single_extruder_multi_material_priming;
ConfigOptionString toolchange_gcode;
ConfigOptionFloat travel_speed;
ConfigOptionBool use_firmware_retraction;
@@ -505,7 +596,9 @@ public:
ConfigOptionFloat cooling_tube_retraction;
ConfigOptionFloat cooling_tube_length;
ConfigOptionFloat parking_pos_retraction;
-
+ ConfigOptionBool remaining_times;
+ ConfigOptionBool silent_mode;
+ ConfigOptionFloat extra_loading_move;
std::string get_extrusion_axis() const
{
@@ -531,8 +624,16 @@ 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);
+ OPT_PTR(filament_cooling_initial_speed);
+ OPT_PTR(filament_minimal_purge_on_wipe_tower);
+ OPT_PTR(filament_cooling_final_speed);
OPT_PTR(filament_ramming_parameters);
OPT_PTR(gcode_comments);
OPT_PTR(gcode_flavor);
@@ -551,6 +652,7 @@ protected:
OPT_PTR(retract_restart_extra_toolchange);
OPT_PTR(retract_speed);
OPT_PTR(single_extruder_multi_material);
+ OPT_PTR(single_extruder_multi_material_priming);
OPT_PTR(start_gcode);
OPT_PTR(start_filament_gcode);
OPT_PTR(toolchange_gcode);
@@ -562,11 +664,14 @@ protected:
OPT_PTR(cooling_tube_retraction);
OPT_PTR(cooling_tube_length);
OPT_PTR(parking_pos_retraction);
+ OPT_PTR(remaining_times);
+ OPT_PTR(silent_mode);
+ OPT_PTR(extra_loading_move);
}
};
// This object is mapped to Perl as Slic3r::Config::Print.
-class PrintConfig : public GCodeConfig
+class PrintConfig : public MachineEnvelopeConfig, public GCodeConfig
{
STATIC_PRINT_CONFIG_CACHE_DERIVED(PrintConfig)
PrintConfig() : GCodeConfig(0) { initialize_cache(); *this = s_cache_PrintConfig.defaults(); }
@@ -614,6 +719,7 @@ public:
ConfigOptionString output_filename_format;
ConfigOptionFloat perimeter_acceleration;
ConfigOptionStrings post_process;
+ ConfigOptionString printer_model;
ConfigOptionString printer_notes;
ConfigOptionFloat resolution;
ConfigOptionFloats retract_before_travel;
@@ -642,6 +748,7 @@ protected:
PrintConfig(int) : GCodeConfig(1) {}
void initialize(StaticCacheBase &cache, const char *base_ptr)
{
+ this->MachineEnvelopeConfig::initialize(cache, base_ptr);
this->GCodeConfig::initialize(cache, base_ptr);
OPT_PTR(avoid_crossing_perimeters);
OPT_PTR(bed_shape);
@@ -683,6 +790,7 @@ protected:
OPT_PTR(output_filename_format);
OPT_PTR(perimeter_acceleration);
OPT_PTR(post_process);
+ OPT_PTR(printer_model);
OPT_PTR(printer_notes);
OPT_PTR(resolution);
OPT_PTR(retract_before_travel);
@@ -713,18 +821,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/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp
index ba0876a85..1ddcac13d 100644
--- a/xs/src/libslic3r/PrintObject.cpp
+++ b/xs/src/libslic3r/PrintObject.cpp
@@ -75,6 +75,7 @@ bool PrintObject::delete_last_copy()
bool PrintObject::set_copies(const Points &points)
{
+ bool copies_num_changed = this->_copies.size() != points.size();
this->_copies = points;
// order copies with a nearest neighbor search and translate them by _copies_shift
@@ -93,6 +94,8 @@ bool PrintObject::set_copies(const Points &points)
bool invalidated = this->_print->invalidate_step(psSkirt);
invalidated |= this->_print->invalidate_step(psBrim);
+ if (copies_num_changed)
+ invalidated |= this->_print->invalidate_step(psWipeTower);
return invalidated;
}
@@ -101,7 +104,10 @@ bool PrintObject::reload_model_instances()
Points copies;
copies.reserve(this->_model_object->instances.size());
for (const ModelInstance *mi : this->_model_object->instances)
- copies.emplace_back(Point::new_scale(mi->offset.x, mi->offset.y));
+ {
+ if (mi->is_printable())
+ copies.emplace_back(Point::new_scale(mi->offset.x, mi->offset.y));
+ }
return this->set_copies(copies);
}
@@ -166,6 +172,7 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector<t_config_
steps.emplace_back(posSlice);
} else if (
opt_key == "support_material"
+ || opt_key == "support_material_auto"
|| opt_key == "support_material_angle"
|| opt_key == "support_material_buildplate_only"
|| opt_key == "support_material_enforce_layers"
@@ -232,7 +239,10 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector<t_config_
|| opt_key == "perimeter_speed"
|| opt_key == "small_perimeter_speed"
|| opt_key == "solid_infill_speed"
- || opt_key == "top_solid_infill_speed") {
+ || opt_key == "top_solid_infill_speed"
+ || opt_key == "wipe_into_infill" // when these these two are changed, we only need to invalidate the wipe tower,
+ || opt_key == "wipe_into_objects" // which we already did at the very beginning - nothing more to be done
+ ) {
// these options only affect G-code export, so nothing to invalidate
} else {
// for legacy, if we can't handle this option let's invalidate all steps
@@ -272,6 +282,8 @@ bool PrintObject::invalidate_step(PrintObjectStep step)
}
// Wipe tower depends on the ordering of extruders, which in turn depends on everything.
+ // It also decides about what the wipe_into_infill / wipe_into_object features will do,
+ // and that too depends on many of the settings.
invalidated |= this->_print->invalidate_step(psWipeTower);
return invalidated;
}
@@ -285,6 +297,9 @@ bool PrintObject::has_support_material() const
void PrintObject::_prepare_infill()
{
+ if (!this->is_printable())
+ return;
+
// This will assign a type (top/bottom/internal) to $layerm->slices.
// Then the classifcation of $layerm->slices is transfered onto
// the $layerm->fill_surfaces by clipping $layerm->fill_surfaces
@@ -900,7 +915,7 @@ void PrintObject::discover_vertical_shells()
#if 1
// Intentionally inflate a bit more than how much the region has been shrunk,
// so there will be some overlap between this solid infill and the other infill regions (mainly the sparse infill).
- shell = offset2(shell, - 0.5f * min_perimeter_infill_spacing, 0.8f * min_perimeter_infill_spacing, ClipperLib::jtSquare);
+ shell = offset(offset_ex(union_ex(shell), - 0.5f * min_perimeter_infill_spacing), 0.8f * min_perimeter_infill_spacing, ClipperLib::jtSquare);
if (shell.empty())
continue;
#else
@@ -1171,8 +1186,8 @@ void PrintObject::_slice()
this->typed_slices = false;
-#if 0
- // Disable parallelization for debugging purposes.
+#ifdef SLIC3R_PROFILE
+ // Disable parallelization so the Shiny profiler works
static tbb::task_scheduler_init *tbb_init = nullptr;
tbb_init = new tbb::task_scheduler_init(1);
#endif
@@ -1306,29 +1321,62 @@ end:
std::vector<ExPolygons> PrintObject::_slice_region(size_t region_id, const std::vector<float> &z, bool modifier)
{
- std::vector<ExPolygons> layers;
+ std::vector<const ModelVolume*> volumes;
if (region_id < this->region_volumes.size()) {
- std::vector<int> &volumes = this->region_volumes[region_id];
- if (! volumes.empty()) {
- // Compose mesh.
- //FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them.
- TriangleMesh mesh;
- for (int volume_id : volumes) {
- ModelVolume *volume = this->model_object()->volumes[volume_id];
- if (volume->modifier == modifier)
- mesh.merge(volume->mesh);
- }
- if (mesh.stl.stats.number_of_facets > 0) {
- // transform mesh
- // we ignore the per-instance transformations currently and only
- // consider the first one
- this->model_object()->instances.front()->transform_mesh(&mesh, true);
- // align mesh to Z = 0 (it should be already aligned actually) and apply XY shift
- mesh.translate(- float(unscale(this->_copies_shift.x)), - float(unscale(this->_copies_shift.y)), -float(this->model_object()->bounding_box().min.z));
- // perform actual slicing
- TriangleMeshSlicer mslicer(&mesh);
- mslicer.slice(z, &layers);
- }
+ for (int volume_id : this->region_volumes[region_id]) {
+ const ModelVolume *volume = this->model_object()->volumes[volume_id];
+ if (modifier ? volume->is_modifier() : volume->is_model_part())
+ volumes.emplace_back(volume);
+ }
+ }
+ return this->_slice_volumes(z, volumes);
+}
+
+std::vector<ExPolygons> PrintObject::slice_support_enforcers() const
+{
+ std::vector<const ModelVolume*> volumes;
+ for (const ModelVolume *volume : this->model_object()->volumes)
+ if (volume->is_support_enforcer())
+ volumes.emplace_back(volume);
+ std::vector<float> zs;
+ zs.reserve(this->layers.size());
+ for (const Layer *l : this->layers)
+ zs.emplace_back(l->slice_z);
+ return this->_slice_volumes(zs, volumes);
+}
+
+std::vector<ExPolygons> PrintObject::slice_support_blockers() const
+{
+ std::vector<const ModelVolume*> volumes;
+ for (const ModelVolume *volume : this->model_object()->volumes)
+ if (volume->is_support_blocker())
+ volumes.emplace_back(volume);
+ std::vector<float> zs;
+ zs.reserve(this->layers.size());
+ for (const Layer *l : this->layers)
+ zs.emplace_back(l->slice_z);
+ return this->_slice_volumes(zs, volumes);
+}
+
+std::vector<ExPolygons> PrintObject::_slice_volumes(const std::vector<float> &z, const std::vector<const ModelVolume*> &volumes) const
+{
+ std::vector<ExPolygons> layers;
+ if (! volumes.empty()) {
+ // Compose mesh.
+ //FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them.
+ TriangleMesh mesh;
+ for (const ModelVolume *v : volumes)
+ mesh.merge(v->mesh);
+ if (mesh.stl.stats.number_of_facets > 0) {
+ // transform mesh
+ // we ignore the per-instance transformations currently and only
+ // consider the first one
+ this->model_object()->instances.front()->transform_mesh(&mesh, true);
+ // align mesh to Z = 0 (it should be already aligned actually) and apply XY shift
+ mesh.translate(- float(unscale(this->_copies_shift.x)), - float(unscale(this->_copies_shift.y)), -float(this->model_object()->bounding_box().min.z));
+ // perform actual slicing
+ TriangleMeshSlicer mslicer(&mesh);
+ mslicer.slice(z, &layers);
}
}
return layers;
@@ -1436,6 +1484,9 @@ void PrintObject::_simplify_slices(double distance)
void PrintObject::_make_perimeters()
{
+ if (!this->is_printable())
+ return;
+
if (this->state.is_done(posPerimeters)) return;
this->state.set_started(posPerimeters);
@@ -1544,6 +1595,9 @@ void PrintObject::_make_perimeters()
void PrintObject::_infill()
{
+ if (!this->is_printable())
+ return;
+
if (this->state.is_done(posInfill)) return;
this->state.set_started(posInfill);
@@ -1948,6 +2002,9 @@ void PrintObject::combine_infill()
void PrintObject::_generate_support_material()
{
+ if (!this->is_printable())
+ return;
+
PrintObjectSupportMaterial support_material(this, PrintObject::slicing_parameters());
support_material.generate(*this);
}
diff --git a/xs/src/libslic3r/PrintRegion.cpp b/xs/src/libslic3r/PrintRegion.cpp
index 4874c71bc..5bb1fffb3 100644
--- a/xs/src/libslic3r/PrintRegion.cpp
+++ b/xs/src/libslic3r/PrintRegion.cpp
@@ -57,4 +57,9 @@ coordf_t PrintRegion::nozzle_dmr_avg(const PrintConfig &print_config) const
print_config.nozzle_diameter.get_at(this->config.solid_infill_extruder.value - 1)) / 3.;
}
+coordf_t PrintRegion::bridging_height_avg(const PrintConfig &print_config) const
+{
+ return this->nozzle_dmr_avg(print_config) * sqrt(this->config.bridge_flow_ratio.value);
+}
+
}
diff --git a/xs/src/libslic3r/Slicing.cpp b/xs/src/libslic3r/Slicing.cpp
index e9295d1e3..d3fbcc7cb 100644
--- a/xs/src/libslic3r/Slicing.cpp
+++ b/xs/src/libslic3r/Slicing.cpp
@@ -224,9 +224,9 @@ std::vector<coordf_t> layer_height_profile_adaptive(
// 1) Initialize the SlicingAdaptive class with the object meshes.
SlicingAdaptive as;
as.set_slicing_parameters(slicing_params);
- for (ModelVolumePtrs::const_iterator it = volumes.begin(); it != volumes.end(); ++ it)
- if (! (*it)->modifier)
- as.add_mesh(&(*it)->mesh);
+ for (const ModelVolume *volume : volumes)
+ if (volume->is_model_part())
+ as.add_mesh(&volume->mesh);
as.prepare();
// 2) Generate layers using the algorithm of @platsch
diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp
index 0cecf0014..e31b7a201 100644
--- a/xs/src/libslic3r/SupportMaterial.cpp
+++ b/xs/src/libslic3r/SupportMaterial.cpp
@@ -248,10 +248,10 @@ void PrintObjectSupportMaterial::generate(PrintObject &object)
#ifdef SLIC3R_DEBUG
static int iRun = 0;
iRun ++;
- for (MyLayersPtr::const_iterator it = top_contacts.begin(); it != top_contacts.end(); ++ it)
+ for (const MyLayer *layer : top_contacts)
Slic3r::SVG::export_expolygons(
- debug_out_path("support-top-contacts-%d-%lf.svg", iRun, (*it)->print_z),
- union_ex((*it)->polygons, false));
+ debug_out_path("support-top-contacts-%d-%lf.svg", iRun, layer->print_z),
+ union_ex(layer->polygons, false));
#endif /* SLIC3R_DEBUG */
BOOST_LOG_TRIVIAL(info) << "Support generator - Creating bottom contacts";
@@ -282,7 +282,17 @@ void PrintObjectSupportMaterial::generate(PrintObject &object)
MyLayersPtr intermediate_layers = this->raft_and_intermediate_support_layers(
object, bottom_contacts, top_contacts, layer_storage);
- this->trim_support_layers_by_object(object, top_contacts, m_slicing_params.soluble_interface ? 0. : m_support_layer_height_min, 0., m_gap_xy);
+// this->trim_support_layers_by_object(object, top_contacts, m_slicing_params.soluble_interface ? 0. : m_support_layer_height_min, 0., m_gap_xy);
+ this->trim_support_layers_by_object(object, top_contacts,
+ m_slicing_params.soluble_interface ? 0. : m_object_config->support_material_contact_distance.value,
+ m_slicing_params.soluble_interface ? 0. : m_object_config->support_material_contact_distance.value, m_gap_xy);
+
+#ifdef SLIC3R_DEBUG
+ for (const MyLayer *layer : top_contacts)
+ Slic3r::SVG::export_expolygons(
+ debug_out_path("support-top-contacts-trimmed-by-object-%d-%lf.svg", iRun, layer->print_z),
+ union_ex(layer->polygons, false));
+#endif
BOOST_LOG_TRIVIAL(info) << "Support generator - Creating base layers";
@@ -420,29 +430,17 @@ Polygons collect_region_slices_by_type(const Layer &layer, SurfaceType surface_t
{
// 1) Count the new polygons first.
size_t n_polygons_new = 0;
- for (LayerRegionPtrs::const_iterator it_region = layer.regions.begin(); it_region != layer.regions.end(); ++ it_region) {
- const LayerRegion &region = *(*it_region);
- const SurfaceCollection &slices = region.slices;
- for (Surfaces::const_iterator it = slices.surfaces.begin(); it != slices.surfaces.end(); ++ it) {
- const Surface &surface = *it;
+ for (const LayerRegion *region : layer.regions)
+ for (const Surface &surface : region->slices.surfaces)
if (surface.surface_type == surface_type)
n_polygons_new += surface.expolygon.holes.size() + 1;
- }
- }
-
// 2) Collect the new polygons.
Polygons out;
out.reserve(n_polygons_new);
- for (LayerRegionPtrs::const_iterator it_region = layer.regions.begin(); it_region != layer.regions.end(); ++ it_region) {
- const LayerRegion &region = *(*it_region);
- const SurfaceCollection &slices = region.slices;
- for (Surfaces::const_iterator it = slices.surfaces.begin(); it != slices.surfaces.end(); ++ it) {
- const Surface &surface = *it;
+ for (const LayerRegion *region : layer.regions)
+ for (const Surface &surface : region->slices.surfaces)
if (surface.surface_type == surface_type)
polygons_append(out, surface.expolygon);
- }
- }
-
return out;
}
@@ -452,17 +450,22 @@ Polygons collect_slices_outer(const Layer &layer)
{
Polygons out;
out.reserve(out.size() + layer.slices.expolygons.size());
- for (ExPolygons::const_iterator it = layer.slices.expolygons.begin(); it != layer.slices.expolygons.end(); ++ it)
- out.push_back(it->contour);
+ for (const ExPolygon &expoly : layer.slices.expolygons)
+ out.emplace_back(expoly.contour);
return out;
}
class SupportGridPattern
{
public:
+ // Achtung! The support_polygons need to be trimmed by trimming_polygons, otherwise
+ // the selection by island_samples (see the island_samples() method) will not work!
SupportGridPattern(
+ // Support islands, to be stretched into a grid. Already trimmed with min(lower_layer_offset, m_gap_xy)
const Polygons &support_polygons,
- const Polygons &trimming_polygons,
+ // Trimming polygons, to trim the stretched support islands. support_polygons were already trimmed with trimming_polygons.
+ const Polygons &trimming_polygons,
+ // Grid spacing, given by "support_material_spacing" + m_support_material_flow.spacing()
coordf_t support_spacing,
coordf_t support_angle) :
m_support_polygons(&support_polygons), m_trimming_polygons(&trimming_polygons),
@@ -484,8 +487,21 @@ public:
bbox.align_to_grid(grid_resolution);
m_grid.set_bbox(bbox);
m_grid.create(*m_support_polygons, grid_resolution);
+#if 0
+ if (m_grid.has_intersecting_edges()) {
+ // EdgeGrid fails to produce valid signed distance function for self-intersecting polygons.
+ m_support_polygons_rotated = simplify_polygons(*m_support_polygons);
+ m_support_polygons = &m_support_polygons_rotated;
+ m_grid.set_bbox(bbox);
+ m_grid.create(*m_support_polygons, grid_resolution);
+// assert(! m_grid.has_intersecting_edges());
+ printf("SupportGridPattern: fixing polygons with intersection %s\n",
+ m_grid.has_intersecting_edges() ? "FAILED" : "SUCCEEDED");
+ }
+#endif
m_grid.calculate_sdf();
- // Extract a bounding contour from the grid, trim by the object.
+ // Sample a single point per input support polygon, keep it as a reference to maintain corresponding
+ // polygons if ever these polygons get split into parts by the trimming polygons.
m_island_samples = island_samples(*m_support_polygons);
}
@@ -493,22 +509,25 @@ public:
// and trim the extracted polygons by trimming_polygons.
// Trimming by the trimming_polygons may split the extracted polygons into pieces.
// Remove all the pieces, which do not contain any of the island_samples.
- Polygons extract_support(const coord_t offset_in_grid)
+ Polygons extract_support(const coord_t offset_in_grid, bool fill_holes)
{
// Generate islands, so each island may be tested for overlap with m_island_samples.
- ExPolygons islands = diff_ex(
- m_grid.contours_simplified(offset_in_grid),
- *m_trimming_polygons, false);
+ assert(std::abs(2 * offset_in_grid) < m_grid.resolution());
+#ifdef SLIC3R_DEBUG
+ Polygons support_polygons_simplified = m_grid.contours_simplified(offset_in_grid, fill_holes);
+ ExPolygons islands = diff_ex(support_polygons_simplified, *m_trimming_polygons, false);
+#else
+ ExPolygons islands = diff_ex(m_grid.contours_simplified(offset_in_grid, fill_holes), *m_trimming_polygons, false);
+#endif
// Extract polygons, which contain some of the m_island_samples.
Polygons out;
- std::vector<std::pair<Point,bool>> samples_inside;
-
for (ExPolygon &island : islands) {
BoundingBox bbox = get_extents(island.contour);
+ // Samples are sorted lexicographically.
auto it_lower = std::lower_bound(m_island_samples.begin(), m_island_samples.end(), bbox.min - Point(1, 1));
auto it_upper = std::upper_bound(m_island_samples.begin(), m_island_samples.end(), bbox.max + Point(1, 1));
- samples_inside.clear();
+ std::vector<std::pair<Point,bool>> samples_inside;
for (auto it = it_lower; it != it_upper; ++ it)
if (bbox.contains(*it))
samples_inside.push_back(std::make_pair(*it, false));
@@ -549,7 +568,10 @@ public:
bbox.merge(get_extents(islands));
if (!out.empty())
bbox.merge(get_extents(out));
+ if (!support_polygons_simplified.empty())
+ bbox.merge(get_extents(support_polygons_simplified));
SVG svg(debug_out_path("extract_support_from_grid_trimmed-%d.svg", iRun).c_str(), bbox);
+ svg.draw(union_ex(support_polygons_simplified), "gray", 0.25f);
svg.draw(islands, "red", 0.5f);
svg.draw(union_ex(out), "green", 0.5f);
svg.draw(union_ex(*m_support_polygons), "blue", 0.5f);
@@ -566,11 +588,127 @@ public:
return out;
}
+#ifdef SLIC3R_DEBUG
+ void serialize(const std::string &path)
+ {
+ FILE *file = ::fopen(path.c_str(), "wb");
+ ::fwrite(&m_support_spacing, 8, 1, file);
+ ::fwrite(&m_support_angle, 8, 1, file);
+ uint32_t n_polygons = m_support_polygons->size();
+ ::fwrite(&n_polygons, 4, 1, file);
+ for (uint32_t i = 0; i < n_polygons; ++ i) {
+ const Polygon &poly = (*m_support_polygons)[i];
+ uint32_t n_points = poly.size();
+ ::fwrite(&n_points, 4, 1, file);
+ for (uint32_t j = 0; j < n_points; ++ j) {
+ const Point &pt = poly.points[j];
+ ::fwrite(&pt.x, sizeof(coord_t), 1, file);
+ ::fwrite(&pt.y, sizeof(coord_t), 1, file);
+ }
+ }
+ n_polygons = m_trimming_polygons->size();
+ ::fwrite(&n_polygons, 4, 1, file);
+ for (uint32_t i = 0; i < n_polygons; ++ i) {
+ const Polygon &poly = (*m_trimming_polygons)[i];
+ uint32_t n_points = poly.size();
+ ::fwrite(&n_points, 4, 1, file);
+ for (uint32_t j = 0; j < n_points; ++ j) {
+ const Point &pt = poly.points[j];
+ ::fwrite(&pt.x, sizeof(coord_t), 1, file);
+ ::fwrite(&pt.y, sizeof(coord_t), 1, file);
+ }
+ }
+ ::fclose(file);
+ }
+
+ static SupportGridPattern deserialize(const std::string &path, int which = -1)
+ {
+ SupportGridPattern out;
+ out.deserialize_(path, which);
+ return out;
+ }
+
+ // Deserialization constructor
+ bool deserialize_(const std::string &path, int which = -1)
+ {
+ FILE *file = ::fopen(path.c_str(), "rb");
+ if (file == nullptr)
+ return false;
+
+ m_support_polygons = &m_support_polygons_deserialized;
+ m_trimming_polygons = &m_trimming_polygons_deserialized;
+
+ ::fread(&m_support_spacing, 8, 1, file);
+ ::fread(&m_support_angle, 8, 1, file);
+ //FIXME
+ //m_support_spacing *= 0.01 / 2;
+ uint32_t n_polygons;
+ ::fread(&n_polygons, 4, 1, file);
+ m_support_polygons_deserialized.reserve(n_polygons);
+ int32_t scale = 1;
+ for (uint32_t i = 0; i < n_polygons; ++ i) {
+ Polygon poly;
+ uint32_t n_points;
+ ::fread(&n_points, 4, 1, file);
+ poly.points.reserve(n_points);
+ for (uint32_t j = 0; j < n_points; ++ j) {
+ coord_t x, y;
+ ::fread(&x, sizeof(coord_t), 1, file);
+ ::fread(&y, sizeof(coord_t), 1, file);
+ poly.points.emplace_back(Point(x * scale, y * scale));
+ }
+ if (which == -1 || which == i)
+ m_support_polygons_deserialized.emplace_back(std::move(poly));
+ printf("Polygon %d, area: %lf\n", i, area(poly.points));
+ }
+ ::fread(&n_polygons, 4, 1, file);
+ m_trimming_polygons_deserialized.reserve(n_polygons);
+ for (uint32_t i = 0; i < n_polygons; ++ i) {
+ Polygon poly;
+ uint32_t n_points;
+ ::fread(&n_points, 4, 1, file);
+ poly.points.reserve(n_points);
+ for (uint32_t j = 0; j < n_points; ++ j) {
+ coord_t x, y;
+ ::fread(&x, sizeof(coord_t), 1, file);
+ ::fread(&y, sizeof(coord_t), 1, file);
+ poly.points.emplace_back(Point(x * scale, y * scale));
+ }
+ m_trimming_polygons_deserialized.emplace_back(std::move(poly));
+ }
+ ::fclose(file);
+
+ m_support_polygons_deserialized = simplify_polygons(m_support_polygons_deserialized, false);
+ //m_support_polygons_deserialized = to_polygons(union_ex(m_support_polygons_deserialized, false));
+
+ // Create an EdgeGrid, initialize it with projection, initialize signed distance field.
+ coord_t grid_resolution = coord_t(scale_(m_support_spacing));
+ BoundingBox bbox = get_extents(*m_support_polygons);
+ bbox.offset(20);
+ bbox.align_to_grid(grid_resolution);
+ m_grid.set_bbox(bbox);
+ m_grid.create(*m_support_polygons, grid_resolution);
+ m_grid.calculate_sdf();
+ // Sample a single point per input support polygon, keep it as a reference to maintain corresponding
+ // polygons if ever these polygons get split into parts by the trimming polygons.
+ m_island_samples = island_samples(*m_support_polygons);
+ return true;
+ }
+
+ const Polygons& support_polygons() const { return *m_support_polygons; }
+ const Polygons& trimming_polygons() const { return *m_trimming_polygons; }
+ const EdgeGrid::Grid& grid() const { return m_grid; }
+
+#endif /* SLIC3R_DEBUG */
+
private:
+ SupportGridPattern() {}
SupportGridPattern& operator=(const SupportGridPattern &rhs);
+#if 0
// Get some internal point of an expolygon, to be used as a representative
// sample to test, whether this island is inside another island.
+ //FIXME this was quick, but not sufficiently robust.
static Point island_sample(const ExPolygon &expoly)
{
// Find the lowest point lexicographically.
@@ -591,7 +729,10 @@ private:
double coef = 20. / sqrt(l2);
return Point(p2.x + coef * v.x, p2.y + coef * v.y);
}
+#endif
+ // Sample one internal point per expolygon.
+ // FIXME this is quite an overkill to calculate a complete offset just to get a single point, but at least it is robust.
static Points island_samples(const ExPolygons &expolygons)
{
Points pts;
@@ -629,9 +770,193 @@ private:
coordf_t m_support_spacing;
Slic3r::EdgeGrid::Grid m_grid;
+ // Internal sample points of supporting expolygons. These internal points are used to pick regions corresponding
+ // to the initial supporting regions, after these regions werre grown and possibly split to many by the trimming polygons.
Points m_island_samples;
+
+#ifdef SLIC3R_DEBUG
+ // support for deserialization of m_support_polygons, m_trimming_polygons
+ Polygons m_support_polygons_deserialized;
+ Polygons m_trimming_polygons_deserialized;
+#endif /* SLIC3R_DEBUG */
};
+namespace SupportMaterialInternal {
+ static inline bool has_bridging_perimeters(const ExtrusionLoop &loop)
+ {
+ for (const ExtrusionPath &ep : loop.paths)
+ if (ep.role() == erOverhangPerimeter && ! ep.polyline.empty())
+ return ep.size() >= (ep.is_closed() ? 3 : 2);
+ return false;
+ }
+ static bool has_bridging_perimeters(const ExtrusionEntityCollection &perimeters)
+ {
+ for (const ExtrusionEntity *ee : perimeters.entities) {
+ if (ee->is_collection()) {
+ for (const ExtrusionEntity *ee2 : static_cast<const ExtrusionEntityCollection*>(ee)->entities) {
+ assert(! ee2->is_collection());
+ if (ee2->is_loop())
+ if (has_bridging_perimeters(*static_cast<const ExtrusionLoop*>(ee2)))
+ return true;
+ }
+ } else if (ee->is_loop() && has_bridging_perimeters(*static_cast<const ExtrusionLoop*>(ee)))
+ return true;
+ }
+ return false;
+ }
+ static bool has_bridging_fills(const ExtrusionEntityCollection &fills)
+ {
+ for (const ExtrusionEntity *ee : fills.entities) {
+ assert(ee->is_collection());
+ for (const ExtrusionEntity *ee2 : static_cast<const ExtrusionEntityCollection*>(ee)->entities) {
+ assert(! ee2->is_collection());
+ assert(! ee2->is_loop());
+ if (ee2->role() == erBridgeInfill)
+ return true;
+ }
+ }
+ return false;
+ }
+ static bool has_bridging_extrusions(const Layer &layer)
+ {
+ for (const LayerRegion *region : layer.regions) {
+ if (SupportMaterialInternal::has_bridging_perimeters(region->perimeters))
+ return true;
+ if (region->fill_surfaces.has(stBottomBridge) && has_bridging_fills(region->fills))
+ return true;
+ }
+ return false;
+ }
+
+ static inline void collect_bridging_perimeter_areas(const ExtrusionLoop &loop, const float expansion_scaled, Polygons &out)
+ {
+ assert(expansion_scaled >= 0.f);
+ for (const ExtrusionPath &ep : loop.paths)
+ if (ep.role() == erOverhangPerimeter && ! ep.polyline.empty()) {
+ float exp = 0.5f * scale_(ep.width) + expansion_scaled;
+ if (ep.is_closed()) {
+ if (ep.size() >= 3) {
+ // This is a complete loop.
+ // Add the outer contour first.
+ Polygon poly;
+ poly.points = ep.polyline.points;
+ poly.points.pop_back();
+ if (poly.area() < 0)
+ poly.reverse();
+ polygons_append(out, offset(poly, exp, SUPPORT_SURFACES_OFFSET_PARAMETERS));
+ Polygons holes = offset(poly, - exp, SUPPORT_SURFACES_OFFSET_PARAMETERS);
+ polygons_reverse(holes);
+ polygons_append(out, holes);
+ }
+ } else if (ep.size() >= 2) {
+ // Offset the polyline.
+ polygons_append(out, offset(ep.polyline, exp, SUPPORT_SURFACES_OFFSET_PARAMETERS));
+ }
+ }
+ }
+ static void collect_bridging_perimeter_areas(const ExtrusionEntityCollection &perimeters, const float expansion_scaled, Polygons &out)
+ {
+ for (const ExtrusionEntity *ee : perimeters.entities) {
+ if (ee->is_collection()) {
+ for (const ExtrusionEntity *ee2 : static_cast<const ExtrusionEntityCollection*>(ee)->entities) {
+ assert(! ee2->is_collection());
+ if (ee2->is_loop())
+ collect_bridging_perimeter_areas(*static_cast<const ExtrusionLoop*>(ee2), expansion_scaled, out);
+ }
+ } else if (ee->is_loop())
+ collect_bridging_perimeter_areas(*static_cast<const ExtrusionLoop*>(ee), expansion_scaled, out);
+ }
+ }
+
+ static void remove_bridges_from_contacts(
+ const PrintConfig &print_config,
+ const Layer &lower_layer,
+ const Polygons &lower_layer_polygons,
+ LayerRegion *layerm,
+ float fw,
+ Polygons &contact_polygons)
+ {
+ // compute the area of bridging perimeters
+ Polygons bridges;
+ {
+ // Surface supporting this layer, expanded by 0.5 * nozzle_diameter, as we consider this kind of overhang to be sufficiently supported.
+ Polygons lower_grown_slices = offset(lower_layer_polygons,
+ //FIXME to mimic the decision in the perimeter generator, we should use half the external perimeter width.
+ 0.5f * float(scale_(print_config.nozzle_diameter.get_at(layerm->region()->config.perimeter_extruder-1))),
+ SUPPORT_SURFACES_OFFSET_PARAMETERS);
+ // Collect perimeters of this layer.
+ //FIXME split_at_first_point() could split a bridge mid-way
+ #if 0
+ Polylines overhang_perimeters = layerm->perimeters.as_polylines();
+ // workaround for Clipper bug, see Slic3r::Polygon::clip_as_polyline()
+ for (Polyline &polyline : overhang_perimeters)
+ polyline.points[0].x += 1;
+ // Trim the perimeters of this layer by the lower layer to get the unsupported pieces of perimeters.
+ overhang_perimeters = diff_pl(overhang_perimeters, lower_grown_slices);
+ #else
+ Polylines overhang_perimeters = diff_pl(layerm->perimeters.as_polylines(), lower_grown_slices);
+ #endif
+
+ // only consider straight overhangs
+ // only consider overhangs having endpoints inside layer's slices
+ // convert bridging polylines into polygons by inflating them with their thickness
+ // since we're dealing with bridges, we can't assume width is larger than spacing,
+ // so we take the largest value and also apply safety offset to be ensure no gaps
+ // are left in between
+ Flow bridge_flow = layerm->flow(frPerimeter, true);
+ float w = float(std::max(bridge_flow.scaled_width(), bridge_flow.scaled_spacing()));
+ for (Polyline &polyline : overhang_perimeters)
+ if (polyline.is_straight()) {
+ // This is a bridge
+ polyline.extend_start(fw);
+ polyline.extend_end(fw);
+ // Is the straight perimeter segment supported at both sides?
+ if (lower_layer.slices.contains(polyline.first_point()) && lower_layer.slices.contains(polyline.last_point()))
+ // Offset a polyline into a thick line.
+ polygons_append(bridges, offset(polyline, 0.5f * w + 10.f));
+ }
+ bridges = union_(bridges);
+ }
+ // remove the entire bridges and only support the unsupported edges
+ //FIXME the brided regions are already collected as layerm->bridged. Use it?
+ for (const Surface &surface : layerm->fill_surfaces.surfaces)
+ if (surface.surface_type == stBottomBridge && surface.bridge_angle != -1)
+ polygons_append(bridges, surface.expolygon);
+ //FIXME add the gap filled areas. Extrude the gaps with a bridge flow?
+ // Remove the unsupported ends of the bridges from the bridged areas.
+ //FIXME add supports at regular intervals to support long bridges!
+ bridges = diff(bridges,
+ // Offset unsupported edges into polygons.
+ offset(layerm->unsupported_bridge_edges.polylines, scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS));
+ // Remove bridged areas from the supported areas.
+ contact_polygons = diff(contact_polygons, bridges, true);
+ }
+}
+
+#ifdef SLIC3R_DEBUG
+static int Test()
+{
+// for (int i = 0; i < 30; ++ i)
+ {
+ int i = -1;
+// SupportGridPattern grid("d:\\temp\\support-top-contacts-final-run1-layer460-z70.300000-prev.bin", i);
+// SupportGridPattern grid("d:\\temp\\support-top-contacts-final-run1-layer460-z70.300000.bin", i);
+ auto grid = SupportGridPattern::deserialize("d:\\temp\\support-top-contacts-final-run1-layer27-z5.650000.bin", i);
+ std::vector<std::pair<EdgeGrid::Grid::ContourEdge, EdgeGrid::Grid::ContourEdge>> intersections = grid.grid().intersecting_edges();
+ if (! intersections.empty())
+ printf("Intersections between contours!\n");
+ Slic3r::export_intersections_to_svg("d:\\temp\\support_polygon_intersections.svg", grid.support_polygons());
+ Slic3r::SVG::export_expolygons("d:\\temp\\support_polygons.svg", union_ex(grid.support_polygons(), false));
+ Slic3r::SVG::export_expolygons("d:\\temp\\trimming_polygons.svg", union_ex(grid.trimming_polygons(), false));
+ Polygons extracted = grid.extract_support(scale_(0.21 / 2), true);
+ Slic3r::SVG::export_expolygons("d:\\temp\\extracted.svg", union_ex(extracted, false));
+ printf("hu!");
+ }
+ return 0;
+}
+static int run_support_test = Test();
+#endif /* SLIC3R_DEBUG */
+
// Generate top contact layers supporting overhangs.
// For a soluble interface material synchronize the layer heights with the object, otherwise leave the layer height undefined.
// If supports over bed surface only are requested, don't generate contact layers over an object.
@@ -643,9 +968,14 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
++ iRun;
#endif /* SLIC3R_DEBUG */
+ // Slice support enforcers / support blockers.
+ std::vector<ExPolygons> enforcers = object.slice_support_enforcers();
+ std::vector<ExPolygons> blockers = object.slice_support_blockers();
+
// Output layers, sorted by top Z.
MyLayersPtr contact_out;
+ const bool support_auto = m_object_config->support_material_auto.value;
// If user specified a custom angle threshold, convert it to radians.
// Zero means automatic overhang detection.
const double threshold_rad = (m_object_config->support_material_threshold.value > 0) ?
@@ -680,10 +1010,13 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
// Note that layer_id < layer->id when raft_layers > 0 as the layer->id incorporates the raft layers.
// So layer_id == 0 means first object layer and layer->id == 0 means first print layer if there are no explicit raft layers.
size_t num_layers = this->has_support() ? object.layer_count() : 1;
- contact_out.assign(num_layers, nullptr);
+ // For each overhang layer, two supporting layers may be generated: One for the overhangs extruded with a bridging flow,
+ // and the other for the overhangs extruded with a normal flow.
+ contact_out.assign(num_layers * 2, nullptr);
tbb::spin_mutex layer_storage_mutex;
tbb::parallel_for(tbb::blocked_range<size_t>(this->has_raft() ? 0 : 1, num_layers),
- [this, &object, &buildplate_covered, threshold_rad, &layer_storage, &layer_storage_mutex, &contact_out](const tbb::blocked_range<size_t>& range) {
+ [this, &object, &buildplate_covered, &enforcers, &blockers, support_auto, threshold_rad, &layer_storage, &layer_storage_mutex, &contact_out]
+ (const tbb::blocked_range<size_t>& range) {
for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id)
{
const Layer &layer = *object.layers[layer_id];
@@ -694,6 +1027,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
Polygons contact_polygons;
Polygons slices_margin_cached;
float slices_margin_cached_offset = -1.;
+ Polygons lower_layer_polygons = (layer_id == 0) ? Polygons() : to_polygons(object.layers[layer_id-1]->slices.expolygons);
+ // Offset of the lower layer, to trim the support polygons with to calculate dense supports.
+ float no_interface_offset = 0.f;
if (layer_id == 0) {
// This is the first object layer, so the object is being printed on a raft and
// we're here just to get the object footprint for the raft.
@@ -708,6 +1044,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
// Extrusion width accounts for the roundings of the extrudates.
// It is the maximum widh of the extrudate.
float fw = float(layerm->flow(frExternalPerimeter).scaled_width());
+ no_interface_offset = (no_interface_offset == 0.f) ? fw : std::min(no_interface_offset, fw);
float lower_layer_offset =
(layer_id < this->m_object_config->support_material_enforce_layers.value) ?
// Enforce a full possible support, ignore the overhang angle.
@@ -720,7 +1057,6 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
// Overhang polygons for this layer and region.
Polygons diff_polygons;
Polygons layerm_polygons = to_polygons(layerm->slices);
- Polygons lower_layer_polygons = to_polygons(lower_layer.slices.expolygons);
if (lower_layer_offset == 0.f) {
// Support everything.
diff_polygons = diff(layerm_polygons, lower_layer_polygons);
@@ -730,28 +1066,61 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
diff_polygons = diff(diff_polygons, buildplate_covered[layer_id]);
}
} else {
- // Get the regions needing a suport, collapse very tiny spots.
- //FIXME cache the lower layer offset if this layer has multiple regions.
- diff_polygons = offset2(
- diff(layerm_polygons,
- offset(lower_layer_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS)),
- -0.1f*fw, +0.1f*fw);
- if (! buildplate_covered.empty()) {
- // Don't support overhangs above the top surfaces.
- // This step is done before the contact surface is calculated by growing the overhang region.
- diff_polygons = diff(diff_polygons, buildplate_covered[layer_id]);
+ if (support_auto) {
+ // Get the regions needing a suport, collapse very tiny spots.
+ //FIXME cache the lower layer offset if this layer has multiple regions.
+ #if 1
+ diff_polygons = offset2(
+ diff(layerm_polygons,
+ offset2(lower_layer_polygons, - 0.5f * fw, lower_layer_offset + 0.5f * fw, SUPPORT_SURFACES_OFFSET_PARAMETERS)),
+ //FIXME This offset2 is targeted to reduce very thin regions to support, but it may lead to
+ // no support at all for not so steep overhangs.
+ - 0.1f * fw, 0.1f * fw);
+ #else
+ diff_polygons =
+ diff(layerm_polygons,
+ offset(lower_layer_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS));
+ #endif
+ if (! buildplate_covered.empty()) {
+ // Don't support overhangs above the top surfaces.
+ // This step is done before the contact surface is calculated by growing the overhang region.
+ diff_polygons = diff(diff_polygons, buildplate_covered[layer_id]);
+ }
+ if (! diff_polygons.empty()) {
+ // Offset the support regions back to a full overhang, restrict them to the full overhang.
+ // This is done to increase size of the supporting columns below, as they are calculated by
+ // propagating these contact surfaces downwards.
+ diff_polygons = diff(
+ intersection(offset(diff_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS), layerm_polygons),
+ lower_layer_polygons);
+ }
}
- if (diff_polygons.empty())
- continue;
- // Offset the support regions back to a full overhang, restrict them to the full overhang.
- diff_polygons = diff(
- intersection(offset(diff_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS), layerm_polygons),
- lower_layer_polygons);
+ if (! enforcers.empty()) {
+ // Apply the "support enforcers".
+ //FIXME add the "enforcers" to the sparse support regions only.
+ const ExPolygons &enforcer = enforcers[layer_id - 1];
+ if (! enforcer.empty()) {
+ // Enforce supports (as if with 90 degrees of slope) for the regions covered by the enforcer meshes.
+ Polygons new_contacts = diff(intersection(layerm_polygons, to_polygons(enforcer)),
+ offset(lower_layer_polygons, 0.05f * fw, SUPPORT_SURFACES_OFFSET_PARAMETERS));
+ if (! new_contacts.empty()) {
+ if (diff_polygons.empty())
+ diff_polygons = std::move(new_contacts);
+ else
+ diff_polygons = union_(diff_polygons, new_contacts);
+ }
+ }
+ }
+ }
+ // Apply the "support blockers".
+ if (! diff_polygons.empty() && ! blockers.empty() && ! blockers[layer_id].empty()) {
+ // Enforce supports (as if with 90 degrees of slope) for the regions covered by the enforcer meshes.
+ diff_polygons = diff(diff_polygons, to_polygons(blockers[layer_id]));
}
if (diff_polygons.empty())
continue;
- #ifdef SLIC3R_DEBUG
+ #ifdef SLIC3R_DEBUG
{
::Slic3r::SVG svg(debug_out_path("support-top-contacts-raw-run%d-layer%d-region%d.svg",
iRun, layer_id,
@@ -762,73 +1131,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
}
#endif /* SLIC3R_DEBUG */
- if (this->m_object_config->dont_support_bridges) {
- // compute the area of bridging perimeters
- // Note: this is duplicate code from GCode.pm, we need to refactor
- if (true) {
- Polygons bridged_perimeters;
- {
- Flow bridge_flow = layerm->flow(frPerimeter, true);
- coordf_t nozzle_diameter = m_print_config->nozzle_diameter.get_at(layerm->region()->config.perimeter_extruder-1);
- Polygons lower_grown_slices = offset(lower_layer_polygons, 0.5f*float(scale_(nozzle_diameter)), SUPPORT_SURFACES_OFFSET_PARAMETERS);
-
- // Collect perimeters of this layer.
- // TODO: split_at_first_point() could split a bridge mid-way
- Polylines overhang_perimeters;
- for (ExtrusionEntity* extrusion_entity : layerm->perimeters.entities) {
- const ExtrusionEntityCollection *island = dynamic_cast<ExtrusionEntityCollection*>(extrusion_entity);
- assert(island != NULL);
- for (size_t i = 0; i < island->entities.size(); ++ i) {
- ExtrusionEntity *entity = island->entities[i];
- ExtrusionLoop *loop = dynamic_cast<Slic3r::ExtrusionLoop*>(entity);
- overhang_perimeters.push_back(loop ?
- loop->as_polyline() :
- dynamic_cast<const Slic3r::ExtrusionPath*>(entity)->polyline);
- }
- }
-
- // workaround for Clipper bug, see Slic3r::Polygon::clip_as_polyline()
- for (Polyline &polyline : overhang_perimeters)
- polyline.points[0].x += 1;
- // Trim the perimeters of this layer by the lower layer to get the unsupported pieces of perimeters.
- overhang_perimeters = diff_pl(overhang_perimeters, lower_grown_slices);
-
- // only consider straight overhangs
- // only consider overhangs having endpoints inside layer's slices
- // convert bridging polylines into polygons by inflating them with their thickness
- // since we're dealing with bridges, we can't assume width is larger than spacing,
- // so we take the largest value and also apply safety offset to be ensure no gaps
- // are left in between
- float w = float(std::max(bridge_flow.scaled_width(), bridge_flow.scaled_spacing()));
- for (Polyline &polyline : overhang_perimeters)
- if (polyline.is_straight()) {
- // This is a bridge
- polyline.extend_start(fw);
- polyline.extend_end(fw);
- // Is the straight perimeter segment supported at both sides?
- if (layer.slices.contains(polyline.first_point()) && layer.slices.contains(polyline.last_point()))
- // Offset a polyline into a thick line.
- polygons_append(bridged_perimeters, offset(polyline, 0.5f * w + 10.f));
- }
- bridged_perimeters = union_(bridged_perimeters);
- }
- // remove the entire bridges and only support the unsupported edges
- Polygons bridges;
- for (const Surface &surface : layerm->fill_surfaces.surfaces)
- if (surface.surface_type == stBottomBridge && surface.bridge_angle != -1)
- polygons_append(bridges, surface.expolygon);
- diff_polygons = diff(diff_polygons, bridges, true);
- polygons_append(bridges, bridged_perimeters);
- polygons_append(diff_polygons,
- intersection(
- // Offset unsupported edges into polygons.
- offset(layerm->unsupported_bridge_edges.polylines, scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS),
- bridges));
- } else {
- // just remove bridged areas
- diff_polygons = diff(diff_polygons, layerm->bridged, true);
- }
- } // if (m_objconfig->dont_support_bridges)
+ if (this->m_object_config->dont_support_bridges)
+ SupportMaterialInternal::remove_bridges_from_contacts(
+ *m_print_config, lower_layer, lower_layer_polygons, layerm, fw, diff_polygons);
if (diff_polygons.empty())
continue;
@@ -842,7 +1147,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
union_ex(diff_polygons, false));
#endif /* SLIC3R_DEBUG */
- if (this->has_contact_loops())
+ //FIXME the overhang_polygons are used to construct the support towers as well.
+ //if (this->has_contact_loops())
+ // Store the exact contour of the overhang for the contact loops.
polygons_append(overhang_polygons, diff_polygons);
// Let's define the required contact area by using a max gap of half the upper
@@ -851,12 +1158,15 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
// on the other side of the object (if it's very thin).
{
//FIMXE 1) Make the offset configurable, 2) Make the Z span configurable.
+ //FIXME one should trim with the layer span colliding with the support layer, this layer
+ // may be lower than lower_layer, so the support area needed may need to be actually bigger!
+ // For the same reason, the non-bridging support area may be smaller than the bridging support area!
float slices_margin_offset = std::min(lower_layer_offset, float(scale_(m_gap_xy)));
if (slices_margin_cached_offset != slices_margin_offset) {
slices_margin_cached_offset = slices_margin_offset;
slices_margin_cached = (slices_margin_offset == 0.f) ?
- to_polygons(lower_layer.slices.expolygons) :
- offset(lower_layer.slices.expolygons, slices_margin_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS);
+ lower_layer_polygons :
+ offset2(to_polygons(lower_layer.slices.expolygons), - no_interface_offset * 0.5f, slices_margin_offset + no_interface_offset * 0.5f, SUPPORT_SURFACES_OFFSET_PARAMETERS);
if (! buildplate_covered.empty()) {
// Trim the inflated contact surfaces by the top surfaces as well.
polygons_append(slices_margin_cached, buildplate_covered[layer_id]);
@@ -879,88 +1189,220 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
} // for each layer.region
} // end of Generate overhang/contact_polygons for non-raft layers.
- // now apply the contact areas to the layer were they need to be made
+ // Now apply the contact areas to the layer where they need to be made.
if (! contact_polygons.empty()) {
- // get the average nozzle diameter used on this layer
MyLayer &new_layer = layer_allocate(layer_storage, layer_storage_mutex, sltTopContact);
new_layer.idx_object_layer_above = layer_id;
- if (m_slicing_params.soluble_interface) {
+ MyLayer *bridging_layer = nullptr;
+ if (layer_id == 0) {
+ // This is a raft contact layer sitting directly on the print bed.
+ assert(this->has_raft());
+ new_layer.print_z = m_slicing_params.raft_contact_top_z;
+ new_layer.bottom_z = m_slicing_params.raft_interface_top_z;
+ new_layer.height = m_slicing_params.contact_raft_layer_height;
+ } else if (m_slicing_params.soluble_interface) {
// Align the contact surface height with a layer immediately below the supported layer.
- new_layer.print_z = layer.print_z - layer.height;
- if (layer_id == 0) {
- // This is a raft contact layer sitting directly on the print bed.
- new_layer.height = m_slicing_params.contact_raft_layer_height;
- new_layer.bottom_z = m_slicing_params.raft_interface_top_z;
- } else {
- // Interface layer will be synchronized with the object.
- assert(layer_id > 0);
- new_layer.height = object.layers[layer_id - 1]->height;
- new_layer.bottom_z = (layer_id == 1) ? m_slicing_params.object_print_z_min : object.layers[layer_id - 2]->print_z;
- }
+ // Interface layer will be synchronized with the object.
+ new_layer.print_z = layer.print_z - layer.height;
+ new_layer.height = object.layers[layer_id - 1]->height;
+ new_layer.bottom_z = (layer_id == 1) ? m_slicing_params.object_print_z_min : object.layers[layer_id - 2]->print_z;
} else {
- // Contact layer will be printed with a normal flow, but
- // it will support layers printed with a bridging flow.
- //FIXME Probably printing with the bridge flow? How about the unsupported perimeters? Are they printed with the bridging flow?
- // In the future we may switch to a normal extrusion flow for the supported bridges.
- // Get the average nozzle diameter used on this layer.
- coordf_t nozzle_dmr = 0.;
- for (const LayerRegion *region : layer.regions)
- nozzle_dmr += region->region()->nozzle_dmr_avg(*m_print_config);
- nozzle_dmr /= coordf_t(layer.regions.size());
- new_layer.print_z = layer.print_z - nozzle_dmr - m_object_config->support_material_contact_distance;
+ new_layer.print_z = layer.print_z - layer.height - m_object_config->support_material_contact_distance;
new_layer.bottom_z = new_layer.print_z;
new_layer.height = 0.;
- if (layer_id == 0) {
- // This is a raft contact layer sitting directly on the print bed.
- assert(this->has_raft());
- new_layer.bottom_z = m_slicing_params.raft_interface_top_z;
- new_layer.height = m_slicing_params.contact_raft_layer_height;
+ // Ignore this contact area if it's too low.
+ // Don't want to print a layer below the first layer height as it may not stick well.
+ //FIXME there may be a need for a single layer support, then one may decide to print it either as a bottom contact or a top contact
+ // and it may actually make sense to do it with a thinner layer than the first layer height.
+ if (new_layer.print_z < m_slicing_params.first_print_layer_height - EPSILON) {
+ // This contact layer is below the first layer height, therefore not printable. Don't support this surface.
+ continue;
+ } else if (new_layer.print_z < m_slicing_params.first_print_layer_height + EPSILON) {
+ // Align the layer with the 1st layer height.
+ new_layer.print_z = m_slicing_params.first_print_layer_height;
+ new_layer.bottom_z = 0;
+ new_layer.height = m_slicing_params.first_print_layer_height;
} else {
- // Ignore this contact area if it's too low.
- // Don't want to print a layer below the first layer height as it may not stick well.
- //FIXME there may be a need for a single layer support, then one may decide to print it either as a bottom contact or a top contact
- // and it may actually make sense to do it with a thinner layer than the first layer height.
- if (new_layer.print_z < m_slicing_params.first_print_layer_height - EPSILON) {
- // This contact layer is below the first layer height, therefore not printable. Don't support this surface.
- continue;
- } else if (new_layer.print_z < m_slicing_params.first_print_layer_height + EPSILON) {
- // Align the layer with the 1st layer height.
- new_layer.print_z = m_slicing_params.first_print_layer_height;
- new_layer.bottom_z = 0;
- new_layer.height = m_slicing_params.first_print_layer_height;
- } else {
- // Don't know the height of the top contact layer yet. The top contact layer is printed with a normal flow and
- // its height will be set adaptively later on.
+ // Don't know the height of the top contact layer yet. The top contact layer is printed with a normal flow and
+ // its height will be set adaptively later on.
+ }
+
+ // Contact layer will be printed with a normal flow, but
+ // it will support layers printed with a bridging flow.
+ if (SupportMaterialInternal::has_bridging_extrusions(layer)) {
+ coordf_t bridging_height = 0.;
+ for (const LayerRegion *region : layer.regions)
+ bridging_height += region->region()->bridging_height_avg(*m_print_config);
+ bridging_height /= coordf_t(layer.regions.size());
+ coordf_t bridging_print_z = layer.print_z - bridging_height - m_object_config->support_material_contact_distance;
+ if (bridging_print_z >= m_slicing_params.first_print_layer_height - EPSILON) {
+ // Not below the first layer height means this layer is printable.
+ if (new_layer.print_z < m_slicing_params.first_print_layer_height + EPSILON) {
+ // Align the layer with the 1st layer height.
+ bridging_print_z = m_slicing_params.first_print_layer_height;
+ }
+ if (bridging_print_z < new_layer.print_z - EPSILON) {
+ // Allocate the new layer.
+ bridging_layer = &layer_allocate(layer_storage, layer_storage_mutex, sltTopContact);
+ bridging_layer->idx_object_layer_above = layer_id;
+ bridging_layer->print_z = bridging_print_z;
+ if (bridging_print_z == m_slicing_params.first_print_layer_height) {
+ bridging_layer->bottom_z = 0;
+ bridging_layer->height = m_slicing_params.first_print_layer_height;
+ } else {
+ // Don't know the height yet.
+ bridging_layer->bottom_z = bridging_print_z;
+ bridging_layer->height = 0;
+ }
+ }
}
}
}
+ // Achtung! The contact_polygons need to be trimmed by slices_margin_cached, otherwise
+ // the selection by island_samples (see the SupportGridPattern::island_samples() method) will not work!
SupportGridPattern support_grid_pattern(
// Support islands, to be stretched into a grid.
contact_polygons,
// Trimming polygons, to trim the stretched support islands.
slices_margin_cached,
- // How much to offset the extracted contour outside of the grid.
+ // Grid resolution.
m_object_config->support_material_spacing.value + m_support_material_flow.spacing(),
Geometry::deg2rad(m_object_config->support_material_angle.value));
- // 1) infill polygons, expand them by half the extrusion width + a tiny bit of extra.
- new_layer.polygons = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 5);
- // 2) Contact polygons will be projected down. To keep the interface and base layers to grow, return a contour a tiny bit smaller than the grid cells.
- new_layer.contact_polygons = new Polygons(support_grid_pattern.extract_support(-3));
+ // 1) Contact polygons will be projected down. To keep the interface and base layers from growing, return a contour a tiny bit smaller than the grid cells.
+ new_layer.contact_polygons = new Polygons(support_grid_pattern.extract_support(-3, true));
+ // 2) infill polygons, expand them by half the extrusion width + a tiny bit of extra.
+ if (layer_id == 0 || m_slicing_params.soluble_interface) {
+ // if (no_interface_offset == 0.f) {
+ new_layer.polygons = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 5, true);
+ } else {
+ // Reduce the amount of dense interfaces: Do not generate dense interfaces below overhangs with 60% overhang of the extrusions.
+ Polygons dense_interface_polygons = diff(overhang_polygons,
+ offset2(lower_layer_polygons, - no_interface_offset * 0.5f, no_interface_offset * (0.6f + 0.5f), SUPPORT_SURFACES_OFFSET_PARAMETERS));
+ if (! dense_interface_polygons.empty()) {
+ dense_interface_polygons =
+ // Achtung! The dense_interface_polygons need to be trimmed by slices_margin_cached, otherwise
+ // the selection by island_samples (see the SupportGridPattern::island_samples() method) will not work!
+ diff(
+ // Regularize the contour.
+ offset(dense_interface_polygons, no_interface_offset * 0.1f),
+ slices_margin_cached);
+ SupportGridPattern support_grid_pattern(
+ // Support islands, to be stretched into a grid.
+ dense_interface_polygons,
+ // Trimming polygons, to trim the stretched support islands.
+ slices_margin_cached,
+ // Grid resolution.
+ m_object_config->support_material_spacing.value + m_support_material_flow.spacing(),
+ Geometry::deg2rad(m_object_config->support_material_angle.value));
+ new_layer.polygons = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 5, false);
+ #ifdef SLIC3R_DEBUG
+ {
+ support_grid_pattern.serialize(debug_out_path("support-top-contacts-final-run%d-layer%d-z%f.bin", iRun, layer_id, layer.print_z));
+
+ BoundingBox bbox = get_extents(contact_polygons);
+ bbox.merge(get_extents(new_layer.polygons));
+ ::Slic3r::SVG svg(debug_out_path("support-top-contacts-final0-run%d-layer%d-z%f.svg", iRun, layer_id, layer.print_z));
+ svg.draw(union_ex(*new_layer.contact_polygons, false), "gray", 0.5f);
+ svg.draw(union_ex(contact_polygons, false), "blue", 0.5f);
+ svg.draw(union_ex(dense_interface_polygons, false), "green", 0.5f);
+ svg.draw(union_ex(new_layer.polygons, true), "red", 0.5f);
+ svg.draw_outline(union_ex(new_layer.polygons, true), "black", "black", scale_(0.1f));
+ }
+ #endif /* SLIC3R_DEBUG */
+ }
+ }
+ #ifdef SLIC3R_DEBUG
+ {
+ BoundingBox bbox = get_extents(contact_polygons);
+ bbox.merge(get_extents(new_layer.polygons));
+ ::Slic3r::SVG svg(debug_out_path("support-top-contacts-final-run%d-layer%d-z%f.svg", iRun, layer_id, layer.print_z));
+ svg.draw(union_ex(*new_layer.contact_polygons, false), "gray", 0.5f);
+ svg.draw(union_ex(contact_polygons, false), "blue", 0.5f);
+ svg.draw(union_ex(overhang_polygons, false), "green", 0.5f);
+ svg.draw(union_ex(new_layer.polygons, true), "red", 0.5f);
+ svg.draw_outline(union_ex(new_layer.polygons, true), "black", "black", scale_(0.1f));
+ }
+ #endif /* SLIC3R_DEBUG */
// Even after the contact layer was expanded into a grid, some of the contact islands may be too tiny to be extruded.
// Remove those tiny islands from new_layer.polygons and new_layer.contact_polygons.
// Store the overhang polygons.
// The overhang polygons are used in the path generator for planning of the contact loops.
- // if (this->has_contact_loops())
+ // if (this->has_contact_loops()). Compared to "polygons", "overhang_polygons" are snug.
new_layer.overhang_polygons = new Polygons(std::move(overhang_polygons));
- contact_out[layer_id] = &new_layer;
+ contact_out[layer_id * 2] = &new_layer;
+ if (bridging_layer != nullptr) {
+ bridging_layer->polygons = new_layer.polygons;
+ bridging_layer->contact_polygons = new Polygons(*new_layer.contact_polygons);
+ bridging_layer->overhang_polygons = new Polygons(*new_layer.overhang_polygons);
+ contact_out[layer_id * 2 + 1] = bridging_layer;
+ }
}
}
});
+
// Compress contact_out, remove the nullptr items.
remove_nulls(contact_out);
+ // Sort the layers, as one layer may produce bridging and non-bridging contact layers with different print_z.
+ std::sort(contact_out.begin(), contact_out.end(), [](const MyLayer *l1, const MyLayer *l2) { return l1->print_z < l2->print_z; });
+
+ // Merge close contact layers conservatively: If two layers are closer than the minimum allowed print layer height (the min_layer_height parameter),
+ // the top contact layer is merged into the bottom contact layer.
+ {
+ int i = 0;
+ int k = 0;
+ {
+ // Find the span of layers, which are to be printed at the first layer height.
+ int j = 0;
+ for (; j < contact_out.size() && contact_out[j]->print_z < m_slicing_params.first_print_layer_height + this->m_support_layer_height_min - EPSILON; ++ j);
+ if (j > 0) {
+ // Merge the contact_out layers (0) to (j - 1) into the contact_out[0].
+ MyLayer &dst = *contact_out.front();
+ for (int u = 1; u < j; ++ u) {
+ MyLayer &src = *contact_out[u];
+ // The union_() does not support move semantic yet, but maybe one day it will.
+ dst.polygons = union_(dst.polygons, std::move(src.polygons));
+ *dst.contact_polygons = union_(*dst.contact_polygons, std::move(*src.contact_polygons));
+ *dst.overhang_polygons = union_(*dst.overhang_polygons, std::move(*src.overhang_polygons));
+ // Source polygon is no more needed, it will not be refrenced. Release its data.
+ src.reset();
+ }
+ // Snap the first layer to the 1st layer height.
+ dst.print_z = m_slicing_params.first_print_layer_height;
+ dst.height = m_slicing_params.first_print_layer_height;
+ dst.bottom_z = 0;
+ ++ k;
+ }
+ i = j;
+ }
+ for (; i < int(contact_out.size()); ++ k) {
+ // Find the span of layers closer than m_support_layer_height_min.
+ int j = i + 1;
+ coordf_t zmax = contact_out[i]->print_z + m_support_layer_height_min + EPSILON;
+ for (; j < contact_out.size() && contact_out[j]->print_z < zmax; ++ j) ;
+ if (i + 1 < j) {
+ // Merge the contact_out layers (i + 1) to (j - 1) into the contact_out[i].
+ MyLayer &dst = *contact_out[i];
+ for (int u = i + 1; u < j; ++ u) {
+ MyLayer &src = *contact_out[u];
+ // The union_() does not support move semantic yet, but maybe one day it will.
+ dst.polygons = union_(dst.polygons, std::move(src.polygons));
+ *dst.contact_polygons = union_(*dst.contact_polygons, std::move(*src.contact_polygons));
+ *dst.overhang_polygons = union_(*dst.overhang_polygons, std::move(*src.overhang_polygons));
+ // Source polygon is no more needed, it will not be refrenced. Release its data.
+ src.reset();
+ }
+ }
+ if (k < i)
+ contact_out[k] = contact_out[i];
+ i = j;
+ }
+ if (k < contact_out.size())
+ contact_out.erase(contact_out.begin() + k, contact_out.end());
+ }
+
BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::top_contact_layers() in parallel - end";
return contact_out;
@@ -996,7 +1438,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
BOOST_LOG_TRIVIAL(trace) << "Support generator - bottom_contact_layers - layer " << layer_id;
const Layer &layer = *object.get_layer(layer_id);
// Collect projections of all contact areas above or at the same level as this top surface.
- for (; contact_idx >= 0 && top_contacts[contact_idx]->print_z >= layer.print_z; -- contact_idx) {
+ for (; contact_idx >= 0 && top_contacts[contact_idx]->print_z > layer.print_z - EPSILON; -- contact_idx) {
Polygons polygons_new;
// Contact surfaces are expanded away from the object, trimmed by the object.
// Use a slight positive offset to overlap the touching regions.
@@ -1004,7 +1446,8 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
// Merge and collect the contact polygons. The contact polygons are inflated, but not extended into a grid form.
polygons_append(polygons_new, offset(*top_contacts[contact_idx]->contact_polygons, SCALED_EPSILON));
#else
- // Consume the contact_polygons. The contact polygons are already expanded into a grid form.
+ // Consume the contact_polygons. The contact polygons are already expanded into a grid form, and they are a tiny bit smaller
+ // than the grid cells.
polygons_append(polygons_new, std::move(*top_contacts[contact_idx]->contact_polygons));
#endif
// These are the overhang surfaces. They are touching the object and they are not expanded away from the object.
@@ -1016,9 +1459,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
continue;
Polygons projection_raw = union_(projection);
- // Top surfaces of this layer, to be used to stop the surface volume from growing down.
tbb::task_group task_group;
if (! m_object_config->support_material_buildplate_only)
+ // Find the bottom contact layers above the top surfaces of this layer.
task_group.run([this, &object, &top_contacts, contact_idx, &layer, layer_id, &layer_storage, &layer_support_areas, &bottom_contacts, &projection_raw] {
Polygons top = collect_region_slices_by_type(layer, stTop);
#ifdef SLIC3R_DEBUG
@@ -1046,28 +1489,34 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
// Grow top surfaces so that interface and support generation are generated
// with some spacing from object - it looks we don't need the actual
// top shapes so this can be done here
+ //FIXME calculate layer height based on the actual thickness of the layer:
+ // If the layer is extruded with no bridging flow, support just the normal extrusions.
layer_new.height = m_slicing_params.soluble_interface ?
// Align the interface layer with the object's layer height.
object.layers[layer_id + 1]->height :
// Place a bridge flow interface layer over the top surface.
+ //FIXME Check whether the bottom bridging surfaces are extruded correctly (no bridging flow correction applied?)
+ // According to Jindrich the bottom surfaces work well.
+ //FIXME test the bridging flow instead?
m_support_material_interface_flow.nozzle_diameter;
layer_new.print_z = m_slicing_params.soluble_interface ? object.layers[layer_id + 1]->print_z :
layer.print_z + layer_new.height + m_object_config->support_material_contact_distance.value;
layer_new.bottom_z = layer.print_z;
layer_new.idx_object_layer_below = layer_id;
layer_new.bridging = ! m_slicing_params.soluble_interface;
- //FIXME how much to inflate the top surface?
+ //FIXME how much to inflate the bottom surface, as it is being extruded with a bridging flow? The following line uses a normal flow.
+ //FIXME why is the offset positive? It will be trimmed by the object later on anyway, but then it just wastes CPU clocks.
layer_new.polygons = offset(touching, float(m_support_material_flow.scaled_width()), SUPPORT_SURFACES_OFFSET_PARAMETERS);
if (! m_slicing_params.soluble_interface) {
// Walk the top surfaces, snap the top of the new bottom surface to the closest top of the top surface,
// so there will be no support surfaces generated with thickness lower than m_support_layer_height_min.
for (size_t top_idx = size_t(std::max<int>(0, contact_idx));
- top_idx < top_contacts.size() && top_contacts[top_idx]->print_z < layer_new.print_z + this->m_support_layer_height_min;
+ top_idx < top_contacts.size() && top_contacts[top_idx]->print_z < layer_new.print_z + this->m_support_layer_height_min + EPSILON;
++ top_idx) {
- if (top_contacts[top_idx]->print_z > layer_new.print_z - this->m_support_layer_height_min) {
+ if (top_contacts[top_idx]->print_z > layer_new.print_z - this->m_support_layer_height_min - EPSILON) {
// A top layer has been found, which is close to the new bottom layer.
coordf_t diff = layer_new.print_z - top_contacts[top_idx]->print_z;
- assert(std::abs(diff) <= this->m_support_layer_height_min);
+ assert(std::abs(diff) <= this->m_support_layer_height_min + EPSILON);
if (diff > 0.) {
// The top contact layer is below this layer. Make the bridging layer thinner to align with the existing top layer.
assert(diff < layer_new.height + EPSILON);
@@ -1091,10 +1540,11 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
union_ex(layer_new.polygons, false));
#endif /* SLIC3R_DEBUG */
// Trim the already created base layers above the current layer intersecting with the new bottom contacts layer.
+ //FIXME Maybe this is no more needed, as the overlapping base layers are trimmed by the bottom layers at the final stage?
touching = offset(touching, float(SCALED_EPSILON));
for (int layer_id_above = layer_id + 1; layer_id_above < int(object.total_layer_count()); ++ layer_id_above) {
const Layer &layer_above = *object.layers[layer_id_above];
- if (layer_above.print_z > layer_new.print_z + EPSILON)
+ if (layer_above.print_z > layer_new.print_z - EPSILON)
break;
if (! layer_support_areas[layer_id_above].empty()) {
#ifdef SLIC3R_DEBUG
@@ -1147,7 +1597,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
projection,
// Trimming polygons, to trim the stretched support islands.
trimming,
- // How much to offset the extracted contour outside of the grid.
+ // Grid spacing.
m_object_config->support_material_spacing.value + m_support_material_flow.spacing(),
Geometry::deg2rad(m_object_config->support_material_angle.value));
tbb::task_group task_group_inner;
@@ -1158,7 +1608,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
, &layer
#endif /* SLIC3R_DEBUG */
] {
- layer_support_area = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 25);
+ layer_support_area = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 25, true);
#ifdef SLIC3R_DEBUG
Slic3r::SVG::export_expolygons(
debug_out_path("support-layer_support_area-gridded-%d-%lf.svg", iRun, layer.print_z),
@@ -1172,7 +1622,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
, &layer
#endif /* SLIC3R_DEBUG */
] {
- projection_new = support_grid_pattern.extract_support(-5);
+ projection_new = support_grid_pattern.extract_support(-5, true);
#ifdef SLIC3R_DEBUG
Slic3r::SVG::export_expolygons(
debug_out_path("support-projection_new-gridded-%d-%lf.svg", iRun, layer.print_z),
@@ -1185,7 +1635,11 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
task_group.wait();
}
std::reverse(bottom_contacts.begin(), bottom_contacts.end());
- trim_support_layers_by_object(object, bottom_contacts, m_slicing_params.soluble_interface ? 0. : m_support_layer_height_min, 0., m_gap_xy);
+// trim_support_layers_by_object(object, bottom_contacts, 0., 0., m_gap_xy);
+ trim_support_layers_by_object(object, bottom_contacts,
+ m_slicing_params.soluble_interface ? 0. : m_object_config->support_material_contact_distance.value,
+ m_slicing_params.soluble_interface ? 0. : m_object_config->support_material_contact_distance.value, m_gap_xy);
+
} // ! top_contacts.empty()
return bottom_contacts;
@@ -1502,9 +1956,6 @@ void PrintObjectSupportMaterial::generate_base_layers(
assert(idx_intermediate == 0 || layer_intermediate.print_z >= intermediate_layers[idx_intermediate - 1]->print_z);
// Find a top_contact layer touching the layer_intermediate from above, if any, and collect its polygons into polygons_new.
- idx_top_contact_above = idx_lower_or_equal(top_contacts, idx_top_contact_above,
- [&layer_intermediate](const MyLayer *layer){ return layer->bottom_z <= layer_intermediate.print_z - EPSILON; });
-
// New polygons for layer_intermediate.
Polygons polygons_new;
@@ -1523,12 +1974,10 @@ void PrintObjectSupportMaterial::generate_base_layers(
// 3) base.print_z > top.print_z && base.bottom_z >= top.bottom_z -> Overlap, which will be solved inside generate_toolpaths() by reducing the base layer height where it overlaps the top layer. No trimming needed here.
// 4) base.print_z > top.bottom_z && base.bottom_z < top.bottom_z -> Base overlaps with top.bottom_z. This must not happen.
// 5) base.print_z <= top.print_z && base.bottom_z >= top.bottom_z -> Base is fully inside top. Trim base by top.
- int idx_top_contact_overlapping = idx_top_contact_above;
- while (idx_top_contact_overlapping >= 0 &&
- top_contacts[idx_top_contact_overlapping]->bottom_z > layer_intermediate.print_z - EPSILON)
- -- idx_top_contact_overlapping;
+ idx_top_contact_above = idx_lower_or_equal(top_contacts, idx_top_contact_above,
+ [&layer_intermediate](const MyLayer *layer){ return layer->bottom_z <= layer_intermediate.print_z - EPSILON; });
// Collect all the top_contact layer intersecting with this layer.
- for (; idx_top_contact_overlapping >= 0; -- idx_top_contact_overlapping) {
+ for ( int idx_top_contact_overlapping = idx_top_contact_above; idx_top_contact_overlapping >= 0; -- idx_top_contact_overlapping) {
MyLayer &layer_top_overlapping = *top_contacts[idx_top_contact_overlapping];
if (layer_top_overlapping.print_z < layer_intermediate.bottom_z + EPSILON)
break;
@@ -1608,7 +2057,10 @@ void PrintObjectSupportMaterial::generate_base_layers(
++ iRun;
#endif /* SLIC3R_DEBUG */
- trim_support_layers_by_object(object, intermediate_layers, m_slicing_params.soluble_interface ? 0. : m_support_layer_height_min, m_slicing_params.soluble_interface ? 0. : m_support_layer_height_min, m_gap_xy);
+// trim_support_layers_by_object(object, intermediate_layers, 0., 0., m_gap_xy);
+ this->trim_support_layers_by_object(object, intermediate_layers,
+ m_slicing_params.soluble_interface ? 0. : m_object_config->support_material_contact_distance.value,
+ m_slicing_params.soluble_interface ? 0. : m_object_config->support_material_contact_distance.value, m_gap_xy);
}
void PrintObjectSupportMaterial::trim_support_layers_by_object(
@@ -1653,19 +2105,23 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object(
const Layer &object_layer = *object.layers[i];
if (object_layer.print_z - object_layer.height > support_layer.print_z + gap_extra_above - EPSILON)
break;
- polygons_append(polygons_trimming, (Polygons)object_layer.slices);
+ polygons_append(polygons_trimming, offset(object_layer.slices.expolygons, gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS));
}
if (! this->m_slicing_params.soluble_interface) {
// Collect all bottom surfaces, which will be extruded with a bridging flow.
for (; i < object.layers.size(); ++ i) {
const Layer &object_layer = *object.layers[i];
bool some_region_overlaps = false;
- for (LayerRegion* region : object_layer.regions) {
- coordf_t nozzle_dmr = region->region()->nozzle_dmr_avg(*this->m_print_config);
- if (object_layer.print_z - nozzle_dmr > support_layer.print_z + gap_extra_above - EPSILON)
+ for (LayerRegion *region : object_layer.regions) {
+ coordf_t bridging_height = region->region()->bridging_height_avg(*this->m_print_config);
+ if (object_layer.print_z - bridging_height > support_layer.print_z + gap_extra_above - EPSILON)
break;
some_region_overlaps = true;
- polygons_append(polygons_trimming, to_polygons(region->slices.filter_by_type(stBottomBridge)));
+ polygons_append(polygons_trimming,
+ offset(to_expolygons(region->fill_surfaces.filter_by_type(stBottomBridge)),
+ gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS));
+ if (region->region()->config.overhangs.value)
+ SupportMaterialInternal::collect_bridging_perimeter_areas(region->perimeters, gap_xy_scaled, polygons_trimming);
}
if (! some_region_overlaps)
break;
@@ -1675,9 +2131,7 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object(
// perimeter's width. $support contains the full shape of support
// material, thus including the width of its foremost extrusion.
// We leave a gap equal to a full extrusion width.
- support_layer.polygons = diff(
- support_layer.polygons,
- offset(polygons_trimming, gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS));
+ support_layer.polygons = diff(support_layer.polygons, polygons_trimming);
}
});
BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::trim_support_layers_by_object() in parallel - end";
@@ -1800,11 +2254,12 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_int
coordf_t top_z = intermediate_layers[std::min<int>(intermediate_layers.size()-1, idx_intermediate_layer + m_object_config->support_material_interface_layers - 1)]->print_z;
coordf_t bottom_z = intermediate_layers[std::max<int>(0, int(idx_intermediate_layer) - int(m_object_config->support_material_interface_layers) + 1)]->bottom_z;
// Move idx_top_contact_first up until above the current print_z.
- idx_top_contact_first = idx_higher_or_equal(top_contacts, idx_top_contact_first, [&intermediate_layer](const MyLayer *layer){ return layer->print_z >= intermediate_layer.print_z; });
+ idx_top_contact_first = idx_higher_or_equal(top_contacts, idx_top_contact_first, [&intermediate_layer](const MyLayer *layer){ return layer->print_z >= intermediate_layer.print_z; }); // - EPSILON
// Collect the top contact areas above this intermediate layer, below top_z.
Polygons polygons_top_contact_projected;
for (size_t idx_top_contact = idx_top_contact_first; idx_top_contact < top_contacts.size(); ++ idx_top_contact) {
const MyLayer &top_contact_layer = *top_contacts[idx_top_contact];
+ //FIXME maybe this adds one interface layer in excess?
if (top_contact_layer.bottom_z - EPSILON > top_z)
break;
polygons_append(polygons_top_contact_projected, top_contact_layer.polygons);
@@ -1861,8 +2316,8 @@ static inline void fill_expolygons_generate_paths(
fill_params.density = density;
fill_params.complete = true;
fill_params.dont_adjust = true;
- for (ExPolygons::const_iterator it_expolygon = expolygons.begin(); it_expolygon != expolygons.end(); ++ it_expolygon) {
- Surface surface(stInternal, *it_expolygon);
+ for (const ExPolygon &expoly : expolygons) {
+ Surface surface(stInternal, expoly);
extrusion_entities_append_paths(
dst,
filler->fill_surface(&surface, fill_params),
@@ -1883,8 +2338,8 @@ static inline void fill_expolygons_generate_paths(
fill_params.density = density;
fill_params.complete = true;
fill_params.dont_adjust = true;
- for (ExPolygons::iterator it_expolygon = expolygons.begin(); it_expolygon != expolygons.end(); ++ it_expolygon) {
- Surface surface(stInternal, std::move(*it_expolygon));
+ for (ExPolygon &expoly : expolygons) {
+ Surface surface(stInternal, std::move(expoly));
extrusion_entities_append_paths(
dst,
filler->fill_surface(&surface, fill_params),
@@ -2359,7 +2814,7 @@ void modulate_extrusion_by_overlapping_layers(
(fragment_end.is_start ? &polyline.points.front() : &polyline.points.back());
}
private:
- ExtrusionPathFragmentEndPointAccessor& operator=(const ExtrusionPathFragmentEndPointAccessor&);
+ ExtrusionPathFragmentEndPointAccessor& operator=(const ExtrusionPathFragmentEndPointAccessor&) {}
const std::vector<ExtrusionPathFragment> &m_path_fragments;
};
const coord_t search_radius = 7;
@@ -2711,6 +3166,8 @@ void PrintObjectSupportMaterial::generate_toolpaths(
continue;
//FIXME When paralellizing, each thread shall have its own copy of the fillers.
bool interface_as_base = (&layer_ex == &interface_layer) && m_object_config->support_material_interface_layers.value == 0;
+ //FIXME Bottom interfaces are extruded with the briding flow. Some bridging layers have its height slightly reduced, therefore
+ // the bridging flow does not quite apply. Reduce the flow to area of an ellipse? (A = pi * a * b)
Flow interface_flow(
float(layer_ex.layer->bridging ? layer_ex.layer->height : (interface_as_base ? m_support_material_flow.width : m_support_material_interface_flow.width)),
float(layer_ex.layer->height),
diff --git a/xs/src/libslic3r/SupportMaterial.hpp b/xs/src/libslic3r/SupportMaterial.hpp
index 968763446..dcb3bd5b3 100644
--- a/xs/src/libslic3r/SupportMaterial.hpp
+++ b/xs/src/libslic3r/SupportMaterial.hpp
@@ -12,6 +12,7 @@ class PrintConfig;
class PrintObjectConfig;
// how much we extend support around the actual contact area
+//FIXME this should be dependent on the nozzle diameter!
#define SUPPORT_MATERIAL_MARGIN 1.5
// This class manages raft and supports for a single PrintObject.
@@ -71,6 +72,21 @@ public:
overhang_polygons = nullptr;
}
+ void reset() {
+ layer_type = sltUnknown;
+ print_z = 0.;
+ bottom_z = 0.;
+ height = 0.;
+ idx_object_layer_above = size_t(-1);
+ idx_object_layer_below = size_t(-1);
+ bridging = false;
+ polygons.clear();
+ delete contact_polygons;
+ contact_polygons = nullptr;
+ delete overhang_polygons;
+ overhang_polygons = nullptr;
+ }
+
bool operator==(const MyLayer &layer2) const {
return print_z == layer2.print_z && height == layer2.height && bridging == layer2.bridging;
}
diff --git a/xs/src/libslic3r/SurfaceCollection.hpp b/xs/src/libslic3r/SurfaceCollection.hpp
index 29cfeb1db..9544748e9 100644
--- a/xs/src/libslic3r/SurfaceCollection.hpp
+++ b/xs/src/libslic3r/SurfaceCollection.hpp
@@ -37,6 +37,11 @@ public:
void clear() { surfaces.clear(); }
bool empty() const { return surfaces.empty(); }
+ bool has(SurfaceType type) const {
+ for (const Surface &surface : this->surfaces)
+ if (surface.surface_type == type) return true;
+ return false;
+ }
void set(const SurfaceCollection &coll) { surfaces = coll.surfaces; }
void set(SurfaceCollection &&coll) { surfaces = std::move(coll.surfaces); }
diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp
index 45e4b6f5d..544a5d00b 100644
--- a/xs/src/libslic3r/TriangleMesh.cpp
+++ b/xs/src/libslic3r/TriangleMesh.cpp
@@ -1,6 +1,10 @@
#include "TriangleMesh.hpp"
#include "ClipperUtils.hpp"
#include "Geometry.hpp"
+#include "MultiPoint.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,21 +14,28 @@
#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"
+
#if 0
#define DEBUG
#define _DEBUG
#undef NDEBUG
+ #define SLIC3R_DEBUG
+// #define SLIC3R_TRIANGLEMESH_DEBUG
#endif
#include <assert.h>
-#ifdef SLIC3R_DEBUG
-// #define SLIC3R_TRIANGLEMESH_DEBUG
+#if defined(SLIC3R_DEBUG) || defined(SLIC3R_DEBUG_SLICE_PROCESSING)
#include "SVG.hpp"
#endif
@@ -197,10 +208,14 @@ TriangleMesh::repair() {
}
// fill_holes
+#if 0
+ // Don't fill holes, the current algorithm does more harm than good on complex holes.
+ // Rather let the slicing algorithm close gaps in 2D slices.
if (stl.stats.connected_facets_3_edge < stl.stats.number_of_facets) {
stl_fill_holes(&stl);
stl_clear_error(&stl);
}
+#endif
// normal_directions
stl_fix_normal_directions(&stl);
@@ -219,7 +234,6 @@ TriangleMesh::repair() {
BOOST_LOG_TRIVIAL(debug) << "TriangleMesh::repair() finished";
}
-
float TriangleMesh::volume()
{
if (this->stl.stats.volume == -1)
@@ -318,6 +332,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)
@@ -423,7 +448,7 @@ bool TriangleMesh::has_multiple_patches() const
facet_visited[facet_idx] = true;
for (int j = 0; j < 3; ++ j) {
int neighbor_idx = this->stl.neighbors_start[facet_idx].neighbor[j];
- if (! facet_visited[neighbor_idx])
+ if (neighbor_idx != -1 && ! facet_visited[neighbor_idx])
facet_queue[facet_queue_cnt ++] = neighbor_idx;
}
}
@@ -466,7 +491,7 @@ size_t TriangleMesh::number_of_patches() const
facet_visited[facet_idx] = true;
for (int j = 0; j < 3; ++ j) {
int neighbor_idx = this->stl.neighbors_start[facet_idx].neighbor[j];
- if (! facet_visited[neighbor_idx])
+ if (neighbor_idx != -1 && ! facet_visited[neighbor_idx])
facet_queue[facet_queue_cnt ++] = neighbor_idx;
}
}
@@ -475,8 +500,7 @@ size_t TriangleMesh::number_of_patches() const
return num_bodies;
}
-TriangleMeshPtrs
-TriangleMesh::split() const
+TriangleMeshPtrs TriangleMesh::split() const
{
TriangleMeshPtrs meshes;
std::set<int> seen_facets;
@@ -528,8 +552,7 @@ TriangleMesh::split() const
return meshes;
}
-void
-TriangleMesh::merge(const TriangleMesh &mesh)
+void TriangleMesh::merge(const TriangleMesh &mesh)
{
// reset stats and metadata
int number_of_facets = this->stl.stats.number_of_facets;
@@ -583,8 +606,7 @@ Polygon TriangleMesh::convex_hull()
return Slic3r::Geometry::convex_hull(pp);
}
-BoundingBoxf3
-TriangleMesh::bounding_box() const
+BoundingBoxf3 TriangleMesh::bounding_box() const
{
BoundingBoxf3 bb;
bb.defined = true;
@@ -597,8 +619,141 @@ TriangleMesh::bounding_box() const
return bb;
}
-void
-TriangleMesh::require_shared_vertices()
+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";
if (!this->repaired)
@@ -607,10 +762,23 @@ TriangleMesh::require_shared_vertices()
BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - stl_generate_shared_vertices";
stl_generate_shared_vertices(&(this->stl));
}
+#ifdef _DEBUG
+ // Verify validity of neighborship data.
+ for (int facet_idx = 0; facet_idx < stl.stats.number_of_facets; ++facet_idx) {
+ const stl_neighbors &nbr = stl.neighbors_start[facet_idx];
+ const int *vertices = stl.v_indices[facet_idx].vertex;
+ for (int nbr_idx = 0; nbr_idx < 3; ++nbr_idx) {
+ int nbr_face = this->stl.neighbors_start[facet_idx].neighbor[nbr_idx];
+ if (nbr_face != -1) {
+ assert(stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 1) % 3] == vertices[(nbr_idx + 1) % 3]);
+ assert(stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 2) % 3] == vertices[nbr_idx]);
+ }
+ }
+ }
+#endif /* _DEBUG */
BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - end";
}
-
TriangleMeshSlicer::TriangleMeshSlicer(TriangleMesh* _mesh) :
mesh(_mesh)
{
@@ -696,8 +864,7 @@ TriangleMeshSlicer::TriangleMeshSlicer(TriangleMesh* _mesh) :
}
}
-void
-TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons>* layers) const
+void TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons>* layers) const
{
BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::slice";
@@ -759,13 +926,30 @@ TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons>* la
{
static int iRun = 0;
for (size_t i = 0; i < z.size(); ++ i) {
- Polygons &polygons = (*layers)[i];
- SVG::export_expolygons(debug_out_path("slice_%d_%d.svg", iRun, i).c_str(), union_ex(polygons, true));
+ Polygons &polygons = (*layers)[i];
+ ExPolygons expolygons = union_ex(polygons, true);
+ SVG::export_expolygons(debug_out_path("slice_%d_%d.svg", iRun, i).c_str(), expolygons);
+ {
+ BoundingBox bbox;
+ for (const IntersectionLine &l : lines[i]) {
+ bbox.merge(l.a);
+ bbox.merge(l.b);
+ }
+ SVG svg(debug_out_path("slice_loops_%d_%d.svg", iRun, i).c_str(), bbox);
+ svg.draw(expolygons);
+ for (const IntersectionLine &l : lines[i])
+ svg.draw(l, "red", 0);
+ svg.draw_outline(expolygons, "black", "blue", 0);
+ svg.Close();
+ }
+#if 0
+//FIXME slice_facet() may create zero length edges due to rounding of doubles into coord_t.
for (Polygon &poly : polygons) {
for (size_t i = 1; i < poly.points.size(); ++ i)
assert(poly.points[i-1] != poly.points[i]);
assert(poly.points.front() != poly.points.back());
}
+#endif
}
++ iRun;
}
@@ -781,54 +965,36 @@ void TriangleMeshSlicer::_slice_do(size_t facet_idx, std::vector<IntersectionLin
const float min_z = fminf(facet.vertex[0].z, fminf(facet.vertex[1].z, facet.vertex[2].z));
const float max_z = fmaxf(facet.vertex[0].z, fmaxf(facet.vertex[1].z, facet.vertex[2].z));
- #ifdef SLIC3R_DEBUG
+ #ifdef SLIC3R_TRIANGLEMESH_DEBUG
printf("\n==> FACET %d (%f,%f,%f - %f,%f,%f - %f,%f,%f):\n", facet_idx,
facet.vertex[0].x, facet.vertex[0].y, facet.vertex[0].z,
facet.vertex[1].x, facet.vertex[1].y, facet.vertex[1].z,
facet.vertex[2].x, facet.vertex[2].y, facet.vertex[2].z);
printf("z: min = %.2f, max = %.2f\n", min_z, max_z);
- #endif
+ #endif /* SLIC3R_TRIANGLEMESH_DEBUG */
// find layer extents
std::vector<float>::const_iterator min_layer, max_layer;
min_layer = std::lower_bound(z.begin(), z.end(), min_z); // first layer whose slice_z is >= min_z
max_layer = std::upper_bound(z.begin() + (min_layer - z.begin()), z.end(), max_z) - 1; // last layer whose slice_z is <= max_z
- #ifdef SLIC3R_DEBUG
+ #ifdef SLIC3R_TRIANGLEMESH_DEBUG
printf("layers: min = %d, max = %d\n", (int)(min_layer - z.begin()), (int)(max_layer - z.begin()));
- #endif
+ #endif /* SLIC3R_TRIANGLEMESH_DEBUG */
for (std::vector<float>::const_iterator it = min_layer; it != max_layer + 1; ++it) {
std::vector<float>::size_type layer_idx = it - z.begin();
IntersectionLine il;
- if (this->slice_facet(*it / SCALING_FACTOR, facet, facet_idx, min_z, max_z, &il)) {
+ if (this->slice_facet(*it / SCALING_FACTOR, facet, facet_idx, min_z, max_z, &il) == TriangleMeshSlicer::Slicing) {
boost::lock_guard<boost::mutex> l(*lines_mutex);
if (il.edge_type == feHorizontal) {
- // Insert all three edges of the face.
- const int *vertices = this->mesh->stl.v_indices[facet_idx].vertex;
- const bool reverse = this->mesh->stl.facet_start[facet_idx].normal.z < 0;
- for (int j = 0; j < 3; ++ j) {
- int a_id = vertices[j % 3];
- int b_id = vertices[(j+1) % 3];
- if (reverse)
- std::swap(a_id, b_id);
- const stl_vertex *a = &this->v_scaled_shared[a_id];
- const stl_vertex *b = &this->v_scaled_shared[b_id];
- il.a.x = a->x;
- il.a.y = a->y;
- il.b.x = b->x;
- il.b.y = b->y;
- il.a_id = a_id;
- il.b_id = b_id;
- (*lines)[layer_idx].push_back(il);
- }
+ // Ignore horizontal triangles. Any valid horizontal triangle must have a vertical triangle connected, otherwise the part has zero volume.
} else
- (*lines)[layer_idx].push_back(il);
+ (*lines)[layer_idx].emplace_back(il);
}
}
}
-void
-TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<ExPolygons>* layers) const
+void TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<ExPolygons>* layers) const
{
std::vector<Polygons> layers_p;
this->slice(z, &layers_p);
@@ -849,23 +1015,22 @@ TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<ExPolygons>*
}
// Return true, if the facet has been sliced and line_out has been filled.
-bool TriangleMeshSlicer::slice_facet(
+TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet(
float slice_z, const stl_facet &facet, const int facet_idx,
const float min_z, const float max_z,
IntersectionLine *line_out) const
{
IntersectionPoint points[3];
size_t num_points = 0;
- size_t points_on_layer[3];
- size_t num_points_on_layer = 0;
+ size_t point_on_layer = size_t(-1);
// Reorder vertices so that the first one is the one with lowest Z.
// This is needed to get all intersection lines in a consistent order
// (external on the right of the line)
+ const int *vertices = this->mesh->stl.v_indices[facet_idx].vertex;
int i = (facet.vertex[1].z == min_z) ? 1 : ((facet.vertex[2].z == min_z) ? 2 : 0);
- for (int j = i; j - i < 3; ++ j) { // loop through facet edges
+ for (int j = i; j - i < 3; ++j) { // loop through facet edges
int edge_id = this->facets_edges[facet_idx * 3 + (j % 3)];
- const int *vertices = this->mesh->stl.v_indices[facet_idx].vertex;
int a_id = vertices[j % 3];
int b_id = vertices[(j+1) % 3];
const stl_vertex *a = &this->v_scaled_shared[a_id];
@@ -877,22 +1042,36 @@ bool TriangleMeshSlicer::slice_facet(
const stl_vertex &v0 = this->v_scaled_shared[vertices[0]];
const stl_vertex &v1 = this->v_scaled_shared[vertices[1]];
const stl_vertex &v2 = this->v_scaled_shared[vertices[2]];
+ const stl_normal &normal = this->mesh->stl.facet_start[facet_idx].normal;
+ // We may ignore this edge for slicing purposes, but we may still use it for object cutting.
+ FacetSliceType result = Slicing;
+ const stl_neighbors &nbr = this->mesh->stl.neighbors_start[facet_idx];
if (min_z == max_z) {
// All three vertices are aligned with slice_z.
line_out->edge_type = feHorizontal;
- if (this->mesh->stl.facet_start[facet_idx].normal.z < 0) {
+ result = Cutting;
+ if (normal.z < 0) {
// If normal points downwards this is a bottom horizontal facet so we reverse its point order.
std::swap(a, b);
std::swap(a_id, b_id);
}
- } else if (v0.z < slice_z || v1.z < slice_z || v2.z < slice_z) {
- // Two vertices are aligned with the cutting plane, the third vertex is below the cutting plane.
- line_out->edge_type = feTop;
- std::swap(a, b);
- std::swap(a_id, b_id);
} else {
- // Two vertices are aligned with the cutting plane, the third vertex is above the cutting plane.
- line_out->edge_type = feBottom;
+ // Two vertices are aligned with the cutting plane, the third vertex is below or above the cutting plane.
+ int nbr_idx = j % 3;
+ int nbr_face = nbr.neighbor[nbr_idx];
+ // Is the third vertex below the cutting plane?
+ bool third_below = v0.z < slice_z || v1.z < slice_z || v2.z < slice_z;
+ // Two vertices on the cutting plane, the third vertex is below the plane. Consider the edge to be part of the slice
+ // only if it is the upper edge.
+ // (the bottom most edge resp. vertex of a triangle is not owned by the triangle, but the top most edge resp. vertex is part of the triangle
+ // in respect to the cutting plane).
+ result = third_below ? Slicing : Cutting;
+ if (third_below) {
+ line_out->edge_type = feTop;
+ std::swap(a, b);
+ std::swap(a_id, b_id);
+ } else
+ line_out->edge_type = feBottom;
}
line_out->a.x = a->x;
line_out->a.y = a->y;
@@ -900,322 +1079,586 @@ bool TriangleMeshSlicer::slice_facet(
line_out->b.y = b->y;
line_out->a_id = a_id;
line_out->b_id = b_id;
- return true;
+ assert(line_out->a != line_out->b);
+ return result;
}
if (a->z == slice_z) {
// Only point a alings with the cutting plane.
- points_on_layer[num_points_on_layer ++] = num_points;
- IntersectionPoint &point = points[num_points ++];
- point.x = a->x;
- point.y = a->y;
- point.point_id = a_id;
+ if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != a_id) {
+ point_on_layer = num_points;
+ IntersectionPoint &point = points[num_points ++];
+ point.x = a->x;
+ point.y = a->y;
+ point.point_id = a_id;
+ }
} else if (b->z == slice_z) {
// Only point b alings with the cutting plane.
- points_on_layer[num_points_on_layer ++] = num_points;
- IntersectionPoint &point = points[num_points ++];
- point.x = b->x;
- point.y = b->y;
- point.point_id = b_id;
+ if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != b_id) {
+ point_on_layer = num_points;
+ IntersectionPoint &point = points[num_points ++];
+ point.x = b->x;
+ point.y = b->y;
+ point.point_id = b_id;
+ }
} else if ((a->z < slice_z && b->z > slice_z) || (b->z < slice_z && a->z > slice_z)) {
// A general case. The face edge intersects the cutting plane. Calculate the intersection point.
- IntersectionPoint &point = points[num_points ++];
- point.x = b->x + (a->x - b->x) * (slice_z - b->z) / (a->z - b->z);
- point.y = b->y + (a->y - b->y) * (slice_z - b->z) / (a->z - b->z);
- point.edge_id = edge_id;
+ assert(a_id != b_id);
+ // Sort the edge to give a consistent answer.
+ if (a_id > b_id) {
+ std::swap(a_id, b_id);
+ std::swap(a, b);
+ }
+ IntersectionPoint &point = points[num_points];
+ double t = (double(slice_z) - double(b->z)) / (double(a->z) - double(b->z));
+ if (t <= 0.) {
+ if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != a_id) {
+ point.x = a->x;
+ point.y = a->y;
+ point_on_layer = num_points ++;
+ point.point_id = a_id;
+ }
+ } else if (t >= 1.) {
+ if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != b_id) {
+ point.x = b->x;
+ point.y = b->y;
+ point_on_layer = num_points ++;
+ point.point_id = b_id;
+ }
+ } else {
+ point.x = coord_t(floor(double(b->x) + (double(a->x) - double(b->x)) * t + 0.5));
+ point.y = coord_t(floor(double(b->y) + (double(a->y) - double(b->y)) * t + 0.5));
+ point.edge_id = edge_id;
+ ++ num_points;
+ }
}
}
- // We can't have only one point on layer because each vertex gets detected
- // twice (once for each edge), and we can't have three points on layer,
- // because we assume this code is not getting called for horizontal facets.
- assert(num_points_on_layer == 0 || num_points_on_layer == 2);
- if (num_points_on_layer > 0) {
- assert(points[points_on_layer[0]].point_id == points[points_on_layer[1]].point_id);
- assert(num_points == 2 || num_points == 3);
- if (num_points < 3)
- // This triangle touches the cutting plane with a single vertex. Ignore it.
- return false;
- // Erase one of the duplicate points.
- -- num_points;
- for (int i = points_on_layer[1]; i < num_points; ++ i)
- points[i] = points[i + 1];
- }
-
- // Facets must intersect each plane 0 or 2 times.
- assert(num_points == 0 || num_points == 2);
+ // Facets must intersect each plane 0 or 2 times, or it may touch the plane at a single vertex only.
+ assert(num_points < 3);
if (num_points == 2) {
- line_out->edge_type = feNone;
+ line_out->edge_type = feGeneral;
line_out->a = (Point)points[1];
line_out->b = (Point)points[0];
line_out->a_id = points[1].point_id;
line_out->b_id = points[0].point_id;
line_out->edge_a_id = points[1].edge_id;
line_out->edge_b_id = points[0].edge_id;
- return true;
+ // Not a zero lenght edge.
+ //FIXME slice_facet() may create zero length edges due to rounding of doubles into coord_t.
+ //assert(line_out->a != line_out->b);
+ // The plane cuts at least one edge in a general position.
+ assert(line_out->a_id == -1 || line_out->b_id == -1);
+ assert(line_out->edge_a_id != -1 || line_out->edge_b_id != -1);
+ // General slicing position, use the segment for both slicing and object cutting.
+#if 0
+ if (line_out->a_id != -1 && line_out->b_id != -1) {
+ // Solving a degenerate case, where both the intersections snapped to an edge.
+ // Correctly classify the face as below or above based on the position of the 3rd point.
+ int i = vertices[0];
+ if (i == line_out->a_id || i == line_out->b_id)
+ i = vertices[1];
+ if (i == line_out->a_id || i == line_out->b_id)
+ i = vertices[2];
+ assert(i != line_out->a_id && i != line_out->b_id);
+ line_out->edge_type = (this->v_scaled_shared[i].z < slice_z) ? feTop : feBottom;
+ }
+#endif
+ return Slicing;
}
- return false;
+ return NoSlice;
}
-void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygons* loops) const
+//FIXME Should this go away? For valid meshes the function slice_facet() returns Slicing
+// and sets edges of vertical triangles to produce only a single edge per pair of neighbor faces.
+// So the following code makes only sense now to handle degenerate meshes with more than two faces
+// sharing a single edge.
+static inline void remove_tangent_edges(std::vector<IntersectionLine> &lines)
{
- // Remove tangent edges.
- //FIXME This is O(n^2) in rare cases when many faces intersect the cutting plane.
- for (IntersectionLines::iterator line = lines.begin(); line != lines.end(); ++ line)
- if (! line->skip && line->edge_type != feNone) {
- // This line is af facet edge. There may be a duplicate line with the same end vertices.
- // If the line is is an edge connecting two facets, find another facet edge
- // having the same endpoints but in reverse order.
- for (IntersectionLines::iterator line2 = line + 1; line2 != lines.end(); ++ line2)
- if (! line2->skip && line2->edge_type != feNone) {
- // Are these facets adjacent? (sharing a common edge on this layer)
- if (line->a_id == line2->a_id && line->b_id == line2->b_id) {
- line2->skip = true;
- /* if they are both oriented upwards or downwards (like a 'V')
- then we can remove both edges from this layer since it won't
- affect the sliced shape */
- /* if one of them is oriented upwards and the other is oriented
- downwards, let's only keep one of them (it doesn't matter which
- one since all 'top' lines were reversed at slicing) */
- if (line->edge_type == line2->edge_type) {
- line->skip = true;
- break;
- }
- } else if (line->a_id == line2->b_id && line->b_id == line2->a_id) {
- /* if this edge joins two horizontal facets, remove both of them */
- if (line->edge_type == feHorizontal && line2->edge_type == feHorizontal) {
- line->skip = true;
- line2->skip = true;
- break;
- }
+ std::vector<IntersectionLine*> by_vertex_pair;
+ by_vertex_pair.reserve(lines.size());
+ for (IntersectionLine& line : lines)
+ if (line.edge_type != feGeneral && line.a_id != -1)
+ // This is a face edge. Check whether there is its neighbor stored in lines.
+ by_vertex_pair.emplace_back(&line);
+ auto edges_lower_sorted = [](const IntersectionLine *l1, const IntersectionLine *l2) {
+ // Sort vertices of l1, l2 lexicographically
+ int l1a = l1->a_id;
+ int l1b = l1->b_id;
+ int l2a = l2->a_id;
+ int l2b = l2->b_id;
+ if (l1a > l1b)
+ std::swap(l1a, l1b);
+ if (l2a > l2b)
+ std::swap(l2a, l2b);
+ // Lexicographical "lower" operator on lexicographically sorted vertices should bring equal edges together when sored.
+ return l1a < l2a || (l1a == l2a && l1b < l2b);
+ };
+ std::sort(by_vertex_pair.begin(), by_vertex_pair.end(), edges_lower_sorted);
+ for (auto line = by_vertex_pair.begin(); line != by_vertex_pair.end(); ++ line) {
+ IntersectionLine &l1 = **line;
+ if (! l1.skip()) {
+ // Iterate as long as line and line2 edges share the same end points.
+ for (auto line2 = line + 1; line2 != by_vertex_pair.end() && ! edges_lower_sorted(*line, *line2); ++ line2) {
+ // Lines must share the end points.
+ assert(! edges_lower_sorted(*line, *line2));
+ assert(! edges_lower_sorted(*line2, *line));
+ IntersectionLine &l2 = **line2;
+ if (l2.skip())
+ continue;
+ if (l1.a_id == l2.a_id) {
+ assert(l1.b_id == l2.b_id);
+ l2.set_skip();
+ // If they are both oriented upwards or downwards (like a 'V'),
+ // then we can remove both edges from this layer since it won't
+ // affect the sliced shape.
+ // If one of them is oriented upwards and the other is oriented
+ // downwards, let's only keep one of them (it doesn't matter which
+ // one since all 'top' lines were reversed at slicing).
+ if (l1.edge_type == l2.edge_type) {
+ l1.set_skip();
+ break;
+ }
+ } else {
+ assert(l1.a_id == l2.b_id && l1.b_id == l2.a_id);
+ // If this edge joins two horizontal facets, remove both of them.
+ if (l1.edge_type == feHorizontal && l2.edge_type == feHorizontal) {
+ l1.set_skip();
+ l2.set_skip();
+ break;
}
}
+ }
}
+ }
+}
- struct OpenPolyline {
- OpenPolyline() {};
- OpenPolyline(const IntersectionReference &start, const IntersectionReference &end, Points &&points) :
- start(start), end(end), points(std::move(points)), consumed(false) {}
- void reverse() {
- std::swap(start, end);
- std::reverse(points.begin(), points.end());
+struct OpenPolyline {
+ OpenPolyline() {};
+ OpenPolyline(const IntersectionReference &start, const IntersectionReference &end, Points &&points) :
+ start(start), end(end), points(std::move(points)), consumed(false) { this->length = Slic3r::length(this->points); }
+ void reverse() {
+ std::swap(start, end);
+ std::reverse(points.begin(), points.end());
+ }
+ IntersectionReference start;
+ IntersectionReference end;
+ Points points;
+ double length;
+ bool consumed;
+};
+
+// called by TriangleMeshSlicer::make_loops() to connect sliced triangles into closed loops and open polylines by the triangle connectivity.
+// Only connects segments crossing triangles of the same orientation.
+static void chain_lines_by_triangle_connectivity(std::vector<IntersectionLine> &lines, Polygons &loops, std::vector<OpenPolyline> &open_polylines)
+{
+ // Build a map of lines by edge_a_id and a_id.
+ std::vector<IntersectionLine*> by_edge_a_id;
+ std::vector<IntersectionLine*> by_a_id;
+ by_edge_a_id.reserve(lines.size());
+ by_a_id.reserve(lines.size());
+ for (IntersectionLine &line : lines) {
+ if (! line.skip()) {
+ if (line.edge_a_id != -1)
+ by_edge_a_id.emplace_back(&line);
+ if (line.a_id != -1)
+ by_a_id.emplace_back(&line);
}
- IntersectionReference start;
- IntersectionReference end;
- Points points;
- bool consumed;
- };
- std::vector<OpenPolyline> open_polylines;
- {
- // Build a map of lines by edge_a_id and a_id.
- std::vector<IntersectionLine*> by_edge_a_id;
- std::vector<IntersectionLine*> by_a_id;
- by_edge_a_id.reserve(lines.size());
- by_a_id.reserve(lines.size());
- for (IntersectionLine &line : lines) {
- if (! line.skip) {
- if (line.edge_a_id != -1)
- by_edge_a_id.emplace_back(&line);
- if (line.a_id != -1)
- by_a_id.emplace_back(&line);
+ }
+ auto by_edge_lower = [](const IntersectionLine* il1, const IntersectionLine *il2) { return il1->edge_a_id < il2->edge_a_id; };
+ auto by_vertex_lower = [](const IntersectionLine* il1, const IntersectionLine *il2) { return il1->a_id < il2->a_id; };
+ std::sort(by_edge_a_id.begin(), by_edge_a_id.end(), by_edge_lower);
+ std::sort(by_a_id.begin(), by_a_id.end(), by_vertex_lower);
+ // Chain the segments with a greedy algorithm, collect the loops and unclosed polylines.
+ IntersectionLines::iterator it_line_seed = lines.begin();
+ for (;;) {
+ // take first spare line and start a new loop
+ IntersectionLine *first_line = nullptr;
+ for (; it_line_seed != lines.end(); ++ it_line_seed)
+ if (it_line_seed->is_seed_candidate()) {
+ //if (! it_line_seed->skip()) {
+ first_line = &(*it_line_seed ++);
+ break;
}
- }
- auto by_edge_lower = [](const IntersectionLine* il1, const IntersectionLine *il2) { return il1->edge_a_id < il2->edge_a_id; };
- auto by_vertex_lower = [](const IntersectionLine* il1, const IntersectionLine *il2) { return il1->a_id < il2->a_id; };
- std::sort(by_edge_a_id.begin(), by_edge_a_id.end(), by_edge_lower);
- std::sort(by_a_id.begin(), by_a_id.end(), by_vertex_lower);
- // Chain the segments with a greedy algorithm, collect the loops and unclosed polylines.
- IntersectionLines::iterator it_line_seed = lines.begin();
+ if (first_line == nullptr)
+ break;
+ first_line->set_skip();
+ Points loop_pts;
+ loop_pts.emplace_back(first_line->a);
+ IntersectionLine *last_line = first_line;
+
+ /*
+ printf("first_line edge_a_id = %d, edge_b_id = %d, a_id = %d, b_id = %d, a = %d,%d, b = %d,%d\n",
+ first_line->edge_a_id, first_line->edge_b_id, first_line->a_id, first_line->b_id,
+ first_line->a.x, first_line->a.y, first_line->b.x, first_line->b.y);
+ */
+
+ IntersectionLine key;
for (;;) {
- // take first spare line and start a new loop
- IntersectionLine *first_line = nullptr;
- for (; it_line_seed != lines.end(); ++ it_line_seed)
- if (! it_line_seed->skip) {
- first_line = &(*it_line_seed ++);
- break;
- }
- if (first_line == nullptr)
- break;
- first_line->skip = true;
- Points loop_pts;
- loop_pts.emplace_back(first_line->a);
- IntersectionLine *last_line = first_line;
-
- /*
- printf("first_line edge_a_id = %d, edge_b_id = %d, a_id = %d, b_id = %d, a = %d,%d, b = %d,%d\n",
- first_line->edge_a_id, first_line->edge_b_id, first_line->a_id, first_line->b_id,
- first_line->a.x, first_line->a.y, first_line->b.x, first_line->b.y);
- */
-
- IntersectionLine key;
- for (;;) {
- // find a line starting where last one finishes
- IntersectionLine* next_line = nullptr;
- if (last_line->edge_b_id != -1) {
- key.edge_a_id = last_line->edge_b_id;
- auto it_begin = std::lower_bound(by_edge_a_id.begin(), by_edge_a_id.end(), &key, by_edge_lower);
- if (it_begin != by_edge_a_id.end()) {
- auto it_end = std::upper_bound(it_begin, by_edge_a_id.end(), &key, by_edge_lower);
- for (auto it_line = it_begin; it_line != it_end; ++ it_line)
- if (! (*it_line)->skip) {
- next_line = *it_line;
- break;
- }
- }
+ // find a line starting where last one finishes
+ IntersectionLine* next_line = nullptr;
+ if (last_line->edge_b_id != -1) {
+ key.edge_a_id = last_line->edge_b_id;
+ auto it_begin = std::lower_bound(by_edge_a_id.begin(), by_edge_a_id.end(), &key, by_edge_lower);
+ if (it_begin != by_edge_a_id.end()) {
+ auto it_end = std::upper_bound(it_begin, by_edge_a_id.end(), &key, by_edge_lower);
+ for (auto it_line = it_begin; it_line != it_end; ++ it_line)
+ if (! (*it_line)->skip()) {
+ next_line = *it_line;
+ break;
+ }
}
- if (next_line == nullptr && last_line->b_id != -1) {
- key.a_id = last_line->b_id;
- auto it_begin = std::lower_bound(by_a_id.begin(), by_a_id.end(), &key, by_vertex_lower);
- if (it_begin != by_a_id.end()) {
- auto it_end = std::upper_bound(it_begin, by_a_id.end(), &key, by_vertex_lower);
- for (auto it_line = it_begin; it_line != it_end; ++ it_line)
- if (! (*it_line)->skip) {
- next_line = *it_line;
- break;
- }
- }
+ }
+ if (next_line == nullptr && last_line->b_id != -1) {
+ key.a_id = last_line->b_id;
+ auto it_begin = std::lower_bound(by_a_id.begin(), by_a_id.end(), &key, by_vertex_lower);
+ if (it_begin != by_a_id.end()) {
+ auto it_end = std::upper_bound(it_begin, by_a_id.end(), &key, by_vertex_lower);
+ for (auto it_line = it_begin; it_line != it_end; ++ it_line)
+ if (! (*it_line)->skip()) {
+ next_line = *it_line;
+ break;
+ }
}
- if (next_line == nullptr) {
- // Check whether we closed this loop.
- if ((first_line->edge_a_id != -1 && first_line->edge_a_id == last_line->edge_b_id) ||
- (first_line->a_id != -1 && first_line->a_id == last_line->b_id)) {
- // The current loop is complete. Add it to the output.
- loops->emplace_back(std::move(loop_pts));
- #ifdef SLIC3R_TRIANGLEMESH_DEBUG
- printf(" Discovered %s polygon of %d points\n", (p.is_counter_clockwise() ? "ccw" : "cw"), (int)p.points.size());
- #endif
- } else {
- // This is an open polyline. Add it to the list of open polylines. These open polylines will processed later.
- loop_pts.emplace_back(last_line->b);
- open_polylines.emplace_back(OpenPolyline(
- IntersectionReference(first_line->a_id, first_line->edge_a_id),
- IntersectionReference(last_line->b_id, last_line->edge_b_id), std::move(loop_pts)));
- }
- break;
+ }
+ if (next_line == nullptr) {
+ // Check whether we closed this loop.
+ if ((first_line->edge_a_id != -1 && first_line->edge_a_id == last_line->edge_b_id) ||
+ (first_line->a_id != -1 && first_line->a_id == last_line->b_id)) {
+ // The current loop is complete. Add it to the output.
+ loops.emplace_back(std::move(loop_pts));
+ #ifdef SLIC3R_TRIANGLEMESH_DEBUG
+ printf(" Discovered %s polygon of %d points\n", (p.is_counter_clockwise() ? "ccw" : "cw"), (int)p.points.size());
+ #endif
+ } else {
+ // This is an open polyline. Add it to the list of open polylines. These open polylines will processed later.
+ loop_pts.emplace_back(last_line->b);
+ open_polylines.emplace_back(OpenPolyline(
+ IntersectionReference(first_line->a_id, first_line->edge_a_id),
+ IntersectionReference(last_line->b_id, last_line->edge_b_id), std::move(loop_pts)));
}
- /*
- printf("next_line edge_a_id = %d, edge_b_id = %d, a_id = %d, b_id = %d, a = %d,%d, b = %d,%d\n",
- next_line->edge_a_id, next_line->edge_b_id, next_line->a_id, next_line->b_id,
- next_line->a.x, next_line->a.y, next_line->b.x, next_line->b.y);
- */
- loop_pts.emplace_back(next_line->a);
- last_line = next_line;
- next_line->skip = true;
+ break;
}
+ /*
+ printf("next_line edge_a_id = %d, edge_b_id = %d, a_id = %d, b_id = %d, a = %d,%d, b = %d,%d\n",
+ next_line->edge_a_id, next_line->edge_b_id, next_line->a_id, next_line->b_id,
+ next_line->a.x, next_line->a.y, next_line->b.x, next_line->b.y);
+ */
+ loop_pts.emplace_back(next_line->a);
+ last_line = next_line;
+ next_line->set_skip();
}
}
+}
- // Now process the open polylines.
- if (! open_polylines.empty()) {
- // Store the end points of open_polylines into vectors sorted
- struct OpenPolylineEnd {
- OpenPolylineEnd(OpenPolyline *polyline, bool start) : polyline(polyline), start(start) {}
- OpenPolyline *polyline;
- // Is it the start or end point?
- bool start;
- const IntersectionReference& ipref() const { return start ? polyline->start : polyline->end; }
- int point_id() const { return ipref().point_id; }
- int edge_id () const { return ipref().edge_id; }
- };
- auto by_edge_lower = [](const OpenPolylineEnd &ope1, const OpenPolylineEnd &ope2) { return ope1.edge_id() < ope2.edge_id(); };
- auto by_point_lower = [](const OpenPolylineEnd &ope1, const OpenPolylineEnd &ope2) { return ope1.point_id() < ope2.point_id(); };
- std::vector<OpenPolylineEnd> by_edge_id;
- std::vector<OpenPolylineEnd> by_point_id;
- by_edge_id.reserve(2 * open_polylines.size());
- by_point_id.reserve(2 * open_polylines.size());
- for (OpenPolyline &opl : open_polylines) {
- if (opl.start.edge_id != -1)
- by_edge_id .emplace_back(OpenPolylineEnd(&opl, true));
- if (opl.end.edge_id != -1)
- by_edge_id .emplace_back(OpenPolylineEnd(&opl, false));
- if (opl.start.point_id != -1)
- by_point_id.emplace_back(OpenPolylineEnd(&opl, true));
- if (opl.end.point_id != -1)
- by_point_id.emplace_back(OpenPolylineEnd(&opl, false));
+std::vector<OpenPolyline*> open_polylines_sorted(std::vector<OpenPolyline> &open_polylines, bool update_lengths)
+{
+ std::vector<OpenPolyline*> out;
+ out.reserve(open_polylines.size());
+ for (OpenPolyline &opl : open_polylines)
+ if (! opl.consumed) {
+ if (update_lengths)
+ opl.length = Slic3r::length(opl.points);
+ out.emplace_back(&opl);
}
- std::sort(by_edge_id .begin(), by_edge_id .end(), by_edge_lower);
- std::sort(by_point_id.begin(), by_point_id.end(), by_point_lower);
+ std::sort(out.begin(), out.end(), [](const OpenPolyline *lhs, const OpenPolyline *rhs){ return lhs->length > rhs->length; });
+ return out;
+}
- // Try to connect the loops.
- for (OpenPolyline &opl : open_polylines) {
- if (opl.consumed)
- continue;
- opl.consumed = true;
- OpenPolylineEnd end(&opl, false);
- for (;;) {
- // find a line starting where last one finishes
- OpenPolylineEnd* next_start = nullptr;
- if (end.edge_id() != -1) {
- auto it_begin = std::lower_bound(by_edge_id.begin(), by_edge_id.end(), end, by_edge_lower);
- if (it_begin != by_edge_id.end()) {
- auto it_end = std::upper_bound(it_begin, by_edge_id.end(), end, by_edge_lower);
- for (auto it_edge = it_begin; it_edge != it_end; ++ it_edge)
- if (! it_edge->polyline->consumed) {
- next_start = &(*it_edge);
- break;
- }
- }
- }
- if (next_start == nullptr && end.point_id() != -1) {
- auto it_begin = std::lower_bound(by_point_id.begin(), by_point_id.end(), end, by_point_lower);
- if (it_begin != by_point_id.end()) {
- auto it_end = std::upper_bound(it_begin, by_point_id.end(), end, by_point_lower);
- for (auto it_point = it_begin; it_point != it_end; ++ it_point)
- if (! it_point->polyline->consumed) {
- next_start = &(*it_point);
- break;
- }
- }
+// called by TriangleMeshSlicer::make_loops() to connect remaining open polylines across shared triangle edges and vertices.
+// Depending on "try_connect_reversed", it may or may not connect segments crossing triangles of opposite orientation.
+static void chain_open_polylines_exact(std::vector<OpenPolyline> &open_polylines, Polygons &loops, bool try_connect_reversed)
+{
+ // Store the end points of open_polylines into vectors sorted
+ struct OpenPolylineEnd {
+ OpenPolylineEnd(OpenPolyline *polyline, bool start) : polyline(polyline), start(start) {}
+ OpenPolyline *polyline;
+ // Is it the start or end point?
+ bool start;
+ const IntersectionReference& ipref() const { return start ? polyline->start : polyline->end; }
+ // Return a unique ID for the intersection point.
+ // Return a positive id for a point, or a negative id for an edge.
+ int id() const { const IntersectionReference &r = ipref(); return (r.point_id >= 0) ? r.point_id : - r.edge_id; }
+ bool operator==(const OpenPolylineEnd &rhs) const { return this->polyline == rhs.polyline && this->start == rhs.start; }
+ };
+ auto by_id_lower = [](const OpenPolylineEnd &ope1, const OpenPolylineEnd &ope2) { return ope1.id() < ope2.id(); };
+ std::vector<OpenPolylineEnd> by_id;
+ by_id.reserve(2 * open_polylines.size());
+ for (OpenPolyline &opl : open_polylines) {
+ if (opl.start.point_id != -1 || opl.start.edge_id != -1)
+ by_id.emplace_back(OpenPolylineEnd(&opl, true));
+ if (try_connect_reversed && (opl.end.point_id != -1 || opl.end.edge_id != -1))
+ by_id.emplace_back(OpenPolylineEnd(&opl, false));
+ }
+ std::sort(by_id.begin(), by_id.end(), by_id_lower);
+ // Find an iterator to by_id_lower for the particular end of OpenPolyline (by comparing the OpenPolyline pointer and the start attribute).
+ auto find_polyline_end = [&by_id, by_id_lower](const OpenPolylineEnd &end) -> std::vector<OpenPolylineEnd>::iterator {
+ for (auto it = std::lower_bound(by_id.begin(), by_id.end(), end, by_id_lower);
+ it != by_id.end() && it->id() == end.id(); ++ it)
+ if (*it == end)
+ return it;
+ return by_id.end();
+ };
+ // Try to connect the loops.
+ std::vector<OpenPolyline*> sorted_by_length = open_polylines_sorted(open_polylines, false);
+ for (OpenPolyline *opl : sorted_by_length) {
+ if (opl->consumed)
+ continue;
+ opl->consumed = true;
+ OpenPolylineEnd end(opl, false);
+ for (;;) {
+ // find a line starting where last one finishes
+ auto it_next_start = std::lower_bound(by_id.begin(), by_id.end(), end, by_id_lower);
+ for (; it_next_start != by_id.end() && it_next_start->id() == end.id(); ++ it_next_start)
+ if (! it_next_start->polyline->consumed)
+ goto found;
+ // The current loop could not be closed. Unmark the segment.
+ opl->consumed = false;
+ break;
+ found:
+ // Attach this polyline to the end of the initial polyline.
+ if (it_next_start->start) {
+ auto it = it_next_start->polyline->points.begin();
+ std::copy(++ it, it_next_start->polyline->points.end(), back_inserter(opl->points));
+ } else {
+ auto it = it_next_start->polyline->points.rbegin();
+ std::copy(++ it, it_next_start->polyline->points.rend(), back_inserter(opl->points));
+ }
+ opl->length += it_next_start->polyline->length;
+ // Mark the next polyline as consumed.
+ it_next_start->polyline->points.clear();
+ it_next_start->polyline->length = 0.;
+ it_next_start->polyline->consumed = true;
+ if (try_connect_reversed) {
+ // Running in a mode, where the polylines may be connected by mixing their orientations.
+ // Update the end point lookup structure after the end point of the current polyline was extended.
+ auto it_end = find_polyline_end(end);
+ auto it_next_end = find_polyline_end(OpenPolylineEnd(it_next_start->polyline, !it_next_start->start));
+ // Swap the end points of the current and next polyline, but keep the polyline ptr and the start flag.
+ std::swap(opl->end, it_next_end->start ? it_next_end->polyline->start : it_next_end->polyline->end);
+ // Swap the positions of OpenPolylineEnd structures in the sorted array to match their respective end point positions.
+ std::swap(*it_end, *it_next_end);
+ }
+ // Check whether we closed this loop.
+ if ((opl->start.edge_id != -1 && opl->start.edge_id == opl->end.edge_id) ||
+ (opl->start.point_id != -1 && opl->start.point_id == opl->end.point_id)) {
+ // The current loop is complete. Add it to the output.
+ //assert(opl->points.front().point_id == opl->points.back().point_id);
+ //assert(opl->points.front().edge_id == opl->points.back().edge_id);
+ // Remove the duplicate last point.
+ opl->points.pop_back();
+ if (opl->points.size() >= 3) {
+ if (try_connect_reversed && area(opl->points) < 0)
+ // The closed polygon is patched from pieces with messed up orientation, therefore
+ // the orientation of the patched up polygon is not known.
+ // Orient the patched up polygons CCW. This heuristic may close some holes and cavities.
+ std::reverse(opl->points.begin(), opl->points.end());
+ loops.emplace_back(std::move(opl->points));
}
- if (next_start == nullptr) {
- // The current loop could not be closed. Unmark the segment.
- opl.consumed = false;
- break;
- }
- // Attach this polyline to the end of the initial polyline.
- if (next_start->start) {
- auto it = next_start->polyline->points.begin();
- std::copy(++ it, next_start->polyline->points.end(), back_inserter(opl.points));
- //opl.points.insert(opl.points.back(), ++ it, next_start->polyline->points.end());
+ opl->points.clear();
+ break;
+ }
+ // Continue with the current loop.
+ }
+ }
+}
+
+// called by TriangleMeshSlicer::make_loops() to connect remaining open polylines across shared triangle edges and vertices,
+// possibly closing small gaps.
+// Depending on "try_connect_reversed", it may or may not connect segments crossing triangles of opposite orientation.
+static void chain_open_polylines_close_gaps(std::vector<OpenPolyline> &open_polylines, Polygons &loops, double max_gap, bool try_connect_reversed)
+{
+ const coord_t max_gap_scaled = (coord_t)scale_(max_gap);
+
+ // Sort the open polylines by their length, so the new loops will be seeded from longer chains.
+ // Update the polyline lengths, return only not yet consumed polylines.
+ std::vector<OpenPolyline*> sorted_by_length = open_polylines_sorted(open_polylines, true);
+
+ // Store the end points of open_polylines into ClosestPointInRadiusLookup<OpenPolylineEnd>.
+ struct OpenPolylineEnd {
+ OpenPolylineEnd(OpenPolyline *polyline, bool start) : polyline(polyline), start(start) {}
+ OpenPolyline *polyline;
+ // Is it the start or end point?
+ bool start;
+ const Point& point() const { return start ? polyline->points.front() : polyline->points.back(); }
+ bool operator==(const OpenPolylineEnd &rhs) const { return this->polyline == rhs.polyline && this->start == rhs.start; }
+ };
+ struct OpenPolylineEndAccessor {
+ const Point* operator()(const OpenPolylineEnd &pt) const { return pt.polyline->consumed ? nullptr : &pt.point(); }
+ };
+ typedef ClosestPointInRadiusLookup<OpenPolylineEnd, OpenPolylineEndAccessor> ClosestPointLookupType;
+ ClosestPointLookupType closest_end_point_lookup(max_gap_scaled);
+ for (OpenPolyline *opl : sorted_by_length) {
+ closest_end_point_lookup.insert(OpenPolylineEnd(opl, true));
+ if (try_connect_reversed)
+ closest_end_point_lookup.insert(OpenPolylineEnd(opl, false));
+ }
+ // Try to connect the loops.
+ for (OpenPolyline *opl : sorted_by_length) {
+ if (opl->consumed)
+ continue;
+ OpenPolylineEnd end(opl, false);
+ if (try_connect_reversed)
+ // The end point of this polyline will be modified, thus the following entry will become invalid. Remove it.
+ closest_end_point_lookup.erase(end);
+ opl->consumed = true;
+ size_t n_segments_joined = 1;
+ for (;;) {
+ // Find a line starting where last one finishes, only return non-consumed open polylines (OpenPolylineEndAccessor returns null for consumed).
+ std::pair<const OpenPolylineEnd*, double> next_start_and_dist = closest_end_point_lookup.find(end.point());
+ const OpenPolylineEnd *next_start = next_start_and_dist.first;
+ // Check whether we closed this loop.
+ double current_loop_closing_distance2 = opl->points.front().distance_to_sq(opl->points.back());
+ bool loop_closed = current_loop_closing_distance2 < coordf_t(max_gap_scaled) * coordf_t(max_gap_scaled);
+ if (next_start != nullptr && loop_closed && current_loop_closing_distance2 < next_start_and_dist.second) {
+ // Heuristics to decide, whether to close the loop, or connect another polyline.
+ // One should avoid closing loops shorter than max_gap_scaled.
+ loop_closed = sqrt(current_loop_closing_distance2) < 0.3 * length(opl->points);
+ }
+ if (loop_closed) {
+ // Remove the start point of the current polyline from the lookup.
+ // Mark the current segment as not consumed, otherwise the closest_end_point_lookup.erase() would fail.
+ opl->consumed = false;
+ closest_end_point_lookup.erase(OpenPolylineEnd(opl, true));
+ if (current_loop_closing_distance2 == 0.) {
+ // Remove the duplicate last point.
+ opl->points.pop_back();
} else {
- auto it = next_start->polyline->points.rbegin();
- std::copy(++ it, next_start->polyline->points.rend(), back_inserter(opl.points));
- //opl.points.insert(opl.points.back(), ++ it, next_start->polyline->points.rend());
+ // The end points are different, keep both of them.
}
- end = *next_start;
- end.start = !end.start;
- next_start->polyline->points.clear();
- next_start->polyline->consumed = true;
- // Check whether we closed this loop.
- const IntersectionReference &ip1 = opl.start;
- const IntersectionReference &ip2 = end.ipref();
- if ((ip1.edge_id != -1 && ip1.edge_id == ip2.edge_id) ||
- (ip1.point_id != -1 && ip1.point_id == ip2.point_id)) {
- // The current loop is complete. Add it to the output.
- assert(opl.points.front().point_id == opl.points.back().point_id);
- assert(opl.points.front().edge_id == opl.points.back().edge_id);
- // Remove the duplicate last point.
- opl.points.pop_back();
- if (opl.points.size() >= 3) {
+ if (opl->points.size() >= 3) {
+ if (try_connect_reversed && n_segments_joined > 1 && area(opl->points) < 0)
// The closed polygon is patched from pieces with messed up orientation, therefore
// the orientation of the patched up polygon is not known.
// Orient the patched up polygons CCW. This heuristic may close some holes and cavities.
- double area = 0.;
- for (size_t i = 0, j = opl.points.size() - 1; i < opl.points.size(); j = i ++)
- area += double(opl.points[j].x + opl.points[i].x) * double(opl.points[i].y - opl.points[j].y);
- if (area < 0)
- std::reverse(opl.points.begin(), opl.points.end());
- loops->emplace_back(std::move(opl.points));
- }
- opl.points.clear();
- break;
+ std::reverse(opl->points.begin(), opl->points.end());
+ loops.emplace_back(std::move(opl->points));
}
- // Continue with the current loop.
+ opl->points.clear();
+ opl->consumed = true;
+ break;
+ }
+ if (next_start == nullptr) {
+ // The current loop could not be closed. Unmark the segment.
+ opl->consumed = false;
+ if (try_connect_reversed)
+ // Re-insert the end point.
+ closest_end_point_lookup.insert(OpenPolylineEnd(opl, false));
+ break;
+ }
+ // Attach this polyline to the end of the initial polyline.
+ if (next_start->start) {
+ auto it = next_start->polyline->points.begin();
+ if (*it == opl->points.back())
+ ++ it;
+ std::copy(it, next_start->polyline->points.end(), back_inserter(opl->points));
+ } else {
+ auto it = next_start->polyline->points.rbegin();
+ if (*it == opl->points.back())
+ ++ it;
+ std::copy(it, next_start->polyline->points.rend(), back_inserter(opl->points));
}
+ ++ n_segments_joined;
+ // Remove the end points of the consumed polyline segment from the lookup.
+ OpenPolyline *opl2 = next_start->polyline;
+ closest_end_point_lookup.erase(OpenPolylineEnd(opl2, true));
+ if (try_connect_reversed)
+ closest_end_point_lookup.erase(OpenPolylineEnd(opl2, false));
+ opl2->points.clear();
+ opl2->consumed = true;
+ // Continue with the current loop.
}
}
}
+void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygons* loops) const
+{
+#if 0
+//FIXME slice_facet() may create zero length edges due to rounding of doubles into coord_t.
+//#ifdef _DEBUG
+ for (const Line &l : lines)
+ assert(l.a != l.b);
+#endif /* _DEBUG */
+
+ // There should be no tangent edges, as the horizontal triangles are ignored and if two triangles touch at a cutting plane,
+ // only the bottom triangle is considered to be cutting the plane.
+// remove_tangent_edges(lines);
+
+#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
+ BoundingBox bbox_svg;
+ {
+ static int iRun = 0;
+ for (const Line &line : lines) {
+ bbox_svg.merge(line.a);
+ bbox_svg.merge(line.b);
+ }
+ SVG svg(debug_out_path("TriangleMeshSlicer_make_loops-raw_lines-%d.svg", iRun ++).c_str(), bbox_svg);
+ for (const Line &line : lines)
+ svg.draw(line);
+ svg.Close();
+ }
+#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
+
+ std::vector<OpenPolyline> open_polylines;
+ chain_lines_by_triangle_connectivity(lines, *loops, open_polylines);
+
+#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
+ {
+ static int iRun = 0;
+ SVG svg(debug_out_path("TriangleMeshSlicer_make_loops-polylines-%d.svg", iRun ++).c_str(), bbox_svg);
+ svg.draw(union_ex(*loops));
+ for (const OpenPolyline &pl : open_polylines)
+ svg.draw(Polyline(pl.points), "red");
+ svg.Close();
+ }
+#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
+
+ // Now process the open polylines.
+ // Do it in two rounds, first try to connect in the same direction only,
+ // then try to connect the open polylines in reversed order as well.
+ chain_open_polylines_exact(open_polylines, *loops, false);
+ chain_open_polylines_exact(open_polylines, *loops, true);
+
+#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
+ {
+ static int iRun = 0;
+ SVG svg(debug_out_path("TriangleMeshSlicer_make_loops-polylines2-%d.svg", iRun++).c_str(), bbox_svg);
+ svg.draw(union_ex(*loops));
+ for (const OpenPolyline &pl : open_polylines) {
+ if (pl.points.empty())
+ continue;
+ svg.draw(Polyline(pl.points), "red");
+ svg.draw(pl.points.front(), "blue");
+ svg.draw(pl.points.back(), "blue");
+ }
+ svg.Close();
+ }
+#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
+
+ // Try to close gaps.
+ // Do it in two rounds, first try to connect in the same direction only,
+ // then try to connect the open polylines in reversed order as well.
+ const double max_gap = 2.; //mm
+ chain_open_polylines_close_gaps(open_polylines, *loops, max_gap, false);
+ chain_open_polylines_close_gaps(open_polylines, *loops, max_gap, true);
+
+#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
+ {
+ static int iRun = 0;
+ SVG svg(debug_out_path("TriangleMeshSlicer_make_loops-polylines-final-%d.svg", iRun++).c_str(), bbox_svg);
+ svg.draw(union_ex(*loops));
+ for (const OpenPolyline &pl : open_polylines) {
+ if (pl.points.empty())
+ continue;
+ svg.draw(Polyline(pl.points), "red");
+ svg.draw(pl.points.front(), "blue");
+ svg.draw(pl.points.back(), "blue");
+ }
+ svg.Close();
+ }
+#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
+}
+
// Only used to cut the mesh into two halves.
void TriangleMeshSlicer::make_expolygons_simple(std::vector<IntersectionLine> &lines, ExPolygons* slices) const
{
@@ -1393,7 +1836,7 @@ void TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower)
// intersect facet with cutting plane
IntersectionLine line;
- if (this->slice_facet(scaled_z, *facet, facet_idx, min_z, max_z, &line)) {
+ if (this->slice_facet(scaled_z, *facet, facet_idx, min_z, max_z, &line) != TriangleMeshSlicer::NoSlice) {
// Save intersection lines for generating correct triangulations.
if (line.edge_type == feTop) {
lower_lines.push_back(line);
diff --git a/xs/src/libslic3r/TriangleMesh.hpp b/xs/src/libslic3r/TriangleMesh.hpp
index c700784a5..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:
@@ -76,7 +82,7 @@ private:
enum FacetEdgeType {
// A general case, the cutting plane intersect a face at two different edges.
- feNone,
+ feGeneral,
// Two vertices are aligned with the cutting plane, the third vertex is below the cutting plane.
feTop,
// Two vertices are aligned with the cutting plane, the third vertex is above the cutting plane.
@@ -110,6 +116,14 @@ public:
class IntersectionLine : public Line
{
public:
+ IntersectionLine() : a_id(-1), b_id(-1), edge_a_id(-1), edge_b_id(-1), edge_type(feGeneral), flags(0) {}
+
+ bool skip() const { return (this->flags & SKIP) != 0; }
+ void set_skip() { this->flags |= SKIP; }
+
+ bool is_seed_candidate() const { return (this->flags & NO_SEED) == 0 && ! this->skip(); }
+ void set_no_seed(bool set) { if (set) this->flags |= NO_SEED; else this->flags &= ~NO_SEED; }
+
// Inherits Point a, b
// For each line end point, either {a,b}_id or {a,b}edge_a_id is set, the other is left to -1.
// Vertex indices of the line end points.
@@ -118,11 +132,23 @@ public:
// Source mesh edges of the line end points.
int edge_a_id;
int edge_b_id;
- // feNone, feTop, feBottom, feHorizontal
+ // feGeneral, feTop, feBottom, feHorizontal
FacetEdgeType edge_type;
- // Used by TriangleMeshSlicer::make_loops() to skip duplicate edges.
- bool skip;
- IntersectionLine() : a_id(-1), b_id(-1), edge_a_id(-1), edge_b_id(-1), edge_type(feNone), skip(false) {};
+ // Used by TriangleMeshSlicer::slice() to skip duplicate edges.
+ enum {
+ // Triangle edge added, because it has no neighbor.
+ EDGE0_NO_NEIGHBOR = 0x001,
+ EDGE1_NO_NEIGHBOR = 0x002,
+ EDGE2_NO_NEIGHBOR = 0x004,
+ // Triangle edge added, because it makes a fold with another horizontal edge.
+ EDGE0_FOLD = 0x010,
+ EDGE1_FOLD = 0x020,
+ EDGE2_FOLD = 0x040,
+ // The edge cannot be a seed of a greedy loop extraction (folds are not safe to become seeds).
+ NO_SEED = 0x100,
+ SKIP = 0x200,
+ };
+ uint32_t flags;
};
typedef std::vector<IntersectionLine> IntersectionLines;
typedef std::vector<IntersectionLine*> IntersectionLinePtrs;
@@ -133,7 +159,12 @@ public:
TriangleMeshSlicer(TriangleMesh* _mesh);
void slice(const std::vector<float> &z, std::vector<Polygons>* layers) const;
void slice(const std::vector<float> &z, std::vector<ExPolygons>* layers) const;
- bool slice_facet(float slice_z, const stl_facet &facet, const int facet_idx,
+ enum FacetSliceType {
+ NoSlice = 0,
+ Slicing = 1,
+ Cutting = 2
+ };
+ FacetSliceType slice_facet(float slice_z, const stl_facet &facet, const int facet_idx,
const float min_z, const float max_z, IntersectionLine *line_out) const;
void cut(float z, TriangleMesh* upper, TriangleMesh* lower) const;
diff --git a/xs/src/libslic3r/Utils.hpp b/xs/src/libslic3r/Utils.hpp
index 921841a27..9334573e8 100644
--- a/xs/src/libslic3r/Utils.hpp
+++ b/xs/src/libslic3r/Utils.hpp
@@ -9,6 +9,7 @@ namespace Slic3r {
extern void set_logging_level(unsigned int level);
extern void trace(unsigned int level, const char *message);
+extern void disable_multi_threading();
// Set a path with GUI resource files.
void set_var_dir(const std::string &path);
@@ -43,6 +44,11 @@ extern local_encoded_string encode_path(const char *src);
extern std::string decode_path(const char *src);
extern std::string normalize_utf8_nfc(const char *src);
+// Safely rename a file even if the target exists.
+// On Windows, the file explorer (or anti-virus or whatever else) often locks the file
+// for a short while, so the file may not be movable. Retry while we see recoverable errors.
+extern int rename_file(const std::string &from, const std::string &to);
+
// File path / name / extension splitting utilities, working with UTF-8,
// to be published to Perl.
namespace PerlUtils {
@@ -84,6 +90,8 @@ inline T next_highest_power_of_2(T v)
return ++ v;
}
+extern std::string xml_escape(std::string text);
+
class PerlCallback {
public:
PerlCallback(void *sv) : m_callback(nullptr) { this->register_callback(sv); }
@@ -96,7 +104,8 @@ public:
void call(int i, int j) const;
void call(const std::vector<int>& ints) const;
void call(double d) const;
- void call(double x, double y) const;
+ void call(double a, double b) const;
+ void call(double a, double b, double c, double d) const;
void call(bool b) const;
private:
void *m_callback;
diff --git a/xs/src/libslic3r/libslic3r.h b/xs/src/libslic3r/libslic3r.h
index b0c144efe..b8e7e0a4e 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.40.1"
+#define SLIC3R_VERSION "1.41.2-beta"
#define SLIC3R_BUILD "UNKNOWN"
typedef int32_t coord_t;
diff --git a/xs/src/libslic3r/utils.cpp b/xs/src/libslic3r/utils.cpp
index 745d07fcd..45a39bbad 100644
--- a/xs/src/libslic3r/utils.cpp
+++ b/xs/src/libslic3r/utils.cpp
@@ -1,4 +1,5 @@
#include "Utils.hpp"
+#include "I18N.hpp"
#include <locale>
#include <ctime>
@@ -22,6 +23,9 @@
#include <boost/nowide/fstream.hpp>
#include <boost/nowide/integration/filesystem.hpp>
#include <boost/nowide/convert.hpp>
+#include <boost/nowide/cstdio.hpp>
+
+#include <tbb/task_scheduler_init.h>
namespace Slic3r {
@@ -81,6 +85,14 @@ void trace(unsigned int level, const char *message)
(::boost::log::keywords::severity = severity)) << message;
}
+void disable_multi_threading()
+{
+ // Disable parallelization so the Shiny profiler works
+ static tbb::task_scheduler_init *tbb_init = nullptr;
+ if (tbb_init == nullptr)
+ tbb_init = new tbb::task_scheduler_init(1);
+}
+
static std::string g_var_dir;
void set_var_dir(const std::string &dir)
@@ -123,6 +135,9 @@ const std::string& localization_dir()
return g_local_dir;
}
+// Translate function callback, to call wxWidgets translate function to convert non-localized UTF8 string to a localized one.
+Slic3r::I18N::translate_fn_type Slic3r::I18N::translate_fn = nullptr;
+
static std::string g_data_dir;
void set_data_dir(const std::string &dir)
@@ -135,6 +150,68 @@ const std::string& data_dir()
return g_data_dir;
}
+// borrowed from LLVM lib/Support/Windows/Path.inc
+int rename_file(const std::string &from, const std::string &to)
+{
+ int ec = 0;
+
+#ifdef _WIN32
+
+ // Convert to utf-16.
+ std::wstring wide_from = boost::nowide::widen(from);
+ std::wstring wide_to = boost::nowide::widen(to);
+
+ // Retry while we see recoverable errors.
+ // System scanners (eg. indexer) might open the source file when it is written
+ // and closed.
+ bool TryReplace = true;
+
+ // This loop may take more than 2000 x 1ms to finish.
+ for (int i = 0; i < 2000; ++ i) {
+ if (i > 0)
+ // Sleep 1ms
+ ::Sleep(1);
+ if (TryReplace) {
+ // Try ReplaceFile first, as it is able to associate a new data stream
+ // with the destination even if the destination file is currently open.
+ if (::ReplaceFileW(wide_to.data(), wide_from.data(), NULL, 0, NULL, NULL))
+ return 0;
+ DWORD ReplaceError = ::GetLastError();
+ ec = -1; // ReplaceError
+ // If ReplaceFileW returned ERROR_UNABLE_TO_MOVE_REPLACEMENT or
+ // ERROR_UNABLE_TO_MOVE_REPLACEMENT_2, retry but only use MoveFileExW().
+ if (ReplaceError == ERROR_UNABLE_TO_MOVE_REPLACEMENT ||
+ ReplaceError == ERROR_UNABLE_TO_MOVE_REPLACEMENT_2) {
+ TryReplace = false;
+ continue;
+ }
+ // If ReplaceFileW returned ERROR_UNABLE_TO_REMOVE_REPLACED, retry
+ // using ReplaceFileW().
+ if (ReplaceError == ERROR_UNABLE_TO_REMOVE_REPLACED)
+ continue;
+ // We get ERROR_FILE_NOT_FOUND if the destination file is missing.
+ // MoveFileEx can handle this case.
+ if (ReplaceError != ERROR_ACCESS_DENIED && ReplaceError != ERROR_FILE_NOT_FOUND && ReplaceError != ERROR_SHARING_VIOLATION)
+ break;
+ }
+ if (::MoveFileExW(wide_from.c_str(), wide_to.c_str(), MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING))
+ return 0;
+ DWORD MoveError = ::GetLastError();
+ ec = -1; // MoveError
+ if (MoveError != ERROR_ACCESS_DENIED && MoveError != ERROR_SHARING_VIOLATION)
+ break;
+ }
+
+#else
+
+ boost::nowide::remove(to.c_str());
+ ec = boost::nowide::rename(from.c_str(), to.c_str());
+
+#endif
+
+ return ec;
+}
+
} // namespace Slic3r
#include <xsinit.h>
@@ -262,7 +339,7 @@ void PerlCallback::call(double d) const
LEAVE;
}
-void PerlCallback::call(double x, double y) const
+void PerlCallback::call(double a, double b) const
{
if (!m_callback)
return;
@@ -270,8 +347,26 @@ void PerlCallback::call(double x, double y) const
ENTER;
SAVETMPS;
PUSHMARK(SP);
- XPUSHs(sv_2mortal(newSVnv(x)));
- XPUSHs(sv_2mortal(newSVnv(y)));
+ XPUSHs(sv_2mortal(newSVnv(a)));
+ XPUSHs(sv_2mortal(newSVnv(b)));
+ PUTBACK;
+ perl_call_sv(SvRV((SV*)m_callback), G_DISCARD);
+ FREETMPS;
+ LEAVE;
+}
+
+void PerlCallback::call(double a, double b, double c, double d) const
+{
+ if (!m_callback)
+ return;
+ dSP;
+ ENTER;
+ SAVETMPS;
+ PUSHMARK(SP);
+ XPUSHs(sv_2mortal(newSVnv(a)));
+ XPUSHs(sv_2mortal(newSVnv(b)));
+ XPUSHs(sv_2mortal(newSVnv(c)));
+ XPUSHs(sv_2mortal(newSVnv(d)));
PUTBACK;
perl_call_sv(SvRV((SV*)m_callback), G_DISCARD);
FREETMPS;
@@ -365,4 +460,31 @@ unsigned get_current_pid()
#endif
}
+std::string xml_escape(std::string text)
+{
+ std::string::size_type pos = 0;
+ for (;;)
+ {
+ pos = text.find_first_of("\"\'&<>", pos);
+ if (pos == std::string::npos)
+ break;
+
+ std::string replacement;
+ switch (text[pos])
+ {
+ case '\"': replacement = "&quot;"; break;
+ case '\'': replacement = "&apos;"; break;
+ case '&': replacement = "&amp;"; break;
+ case '<': replacement = "&lt;"; break;
+ case '>': replacement = "&gt;"; break;
+ default: break;
+ }
+
+ text.replace(pos, 1, replacement);
+ pos += replacement.size();
+ }
+
+ return text;
+}
+
}; // namespace Slic3r
diff --git a/xs/src/perlglue.cpp b/xs/src/perlglue.cpp
index 205eec218..d6bd0e94c 100644
--- a/xs/src/perlglue.cpp
+++ b/xs/src/perlglue.cpp
@@ -64,7 +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
new file mode 100644
index 000000000..7bd044b25
--- /dev/null
+++ b/xs/src/slic3r/AppController.cpp
@@ -0,0 +1,167 @@
+#include "AppController.hpp"
+
+#include <future>
+#include <chrono>
+#include <sstream>
+#include <cstdarg>
+#include <thread>
+#include <unordered_map>
+
+#include <slic3r/GUI/GUI.hpp>
+#include <ModelArrange.hpp>
+#include <slic3r/GUI/PresetBundle.hpp>
+
+#include <Geometry.hpp>
+#include <PrintConfig.hpp>
+#include <Print.hpp>
+#include <Model.hpp>
+#include <Utils.hpp>
+
+namespace Slic3r {
+
+class AppControllerBoilerplate::PriData {
+public:
+ std::mutex m;
+ std::thread::id ui_thread;
+
+ inline explicit PriData(std::thread::id uit): ui_thread(uit) {}
+};
+
+AppControllerBoilerplate::AppControllerBoilerplate()
+ :pri_data_(new PriData(std::this_thread::get_id())) {}
+
+AppControllerBoilerplate::~AppControllerBoilerplate() {
+ pri_data_.reset();
+}
+
+bool AppControllerBoilerplate::is_main_thread() const
+{
+ return pri_data_->ui_thread == std::this_thread::get_id();
+}
+
+namespace GUI {
+PresetBundle* get_preset_bundle();
+}
+
+AppControllerBoilerplate::ProgresIndicatorPtr
+AppControllerBoilerplate::global_progress_indicator() {
+ ProgresIndicatorPtr ret;
+
+ pri_data_->m.lock();
+ ret = global_progressind_;
+ pri_data_->m.unlock();
+
+ return ret;
+}
+
+void AppControllerBoilerplate::global_progress_indicator(
+ AppControllerBoilerplate::ProgresIndicatorPtr gpri)
+{
+ pri_data_->m.lock();
+ global_progressind_ = gpri;
+ pri_data_->m.unlock();
+}
+
+void ProgressIndicator::message_fmt(
+ const std::string &fmtstr, ...) {
+ std::stringstream ss;
+ va_list args;
+ va_start(args, fmtstr);
+
+ auto fmt = fmtstr.begin();
+
+ while (*fmt != '\0') {
+ if (*fmt == 'd') {
+ int i = va_arg(args, int);
+ ss << i << '\n';
+ } else if (*fmt == 'c') {
+ // note automatic conversion to integral type
+ int c = va_arg(args, int);
+ ss << static_cast<char>(c) << '\n';
+ } else if (*fmt == 'f') {
+ double d = va_arg(args, double);
+ ss << d << '\n';
+ }
+ ++fmt;
+ }
+
+ va_end(args);
+ message(ss.str());
+}
+
+const PrintConfig &PrintController::config() const
+{
+ return print_->config;
+}
+
+void AppController::arrange_model()
+{
+ auto ftr = std::async(
+ supports_asynch()? std::launch::async : std::launch::deferred,
+ [this]()
+ {
+ using Coord = libnest2d::TCoord<libnest2d::PointImpl>;
+
+ unsigned count = 0;
+ for(auto obj : model_->objects) count += obj->instances.size();
+
+ auto pind = global_progress_indicator();
+
+ float pmax = 1.0;
+
+ if(pind) {
+ pmax = pind->max();
+
+ // Set the range of the progress to the object count
+ pind->max(count);
+
+ }
+
+ auto dist = print_ctl()->config().min_object_distance();
+
+ // Create the arranger config
+ auto min_obj_distance = static_cast<Coord>(dist/SCALING_FACTOR);
+
+ auto& bedpoints = print_ctl()->config().bed_shape.values;
+ Polyline bed; bed.points.reserve(bedpoints.size());
+ for(auto& v : bedpoints)
+ bed.append(Point::new_scale(v.x, v.y));
+
+ 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,
+ hint,
+ false, // create many piles not just one pile
+ [pind, count](unsigned rem) {
+ if(pind)
+ 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"));
+ }
+
+ // Restore previous max value
+ if(pind) {
+ pind->max(pmax);
+ pind->update(0, L("Arranging done."));
+ }
+ });
+
+ while( ftr.wait_for(std::chrono::milliseconds(10))
+ != std::future_status::ready) {
+ process_events();
+ }
+}
+
+}
diff --git a/xs/src/slic3r/AppController.hpp b/xs/src/slic3r/AppController.hpp
new file mode 100644
index 000000000..3ef47ffdc
--- /dev/null
+++ b/xs/src/slic3r/AppController.hpp
@@ -0,0 +1,244 @@
+#ifndef APPCONTROLLER_HPP
+#define APPCONTROLLER_HPP
+
+#include <string>
+#include <vector>
+#include <memory>
+#include <atomic>
+#include <iostream>
+
+#include "ProgressIndicator.hpp"
+
+namespace Slic3r {
+
+class Model;
+class Print;
+class PrintObject;
+class PrintConfig;
+class ProgressStatusBar;
+class DynamicPrintConfig;
+
+/**
+ * @brief A boilerplate class for creating application logic. It should provide
+ * features as issue reporting and progress indication, etc...
+ *
+ * The lower lever UI independent classes can be manipulated with a subclass
+ * of this controller class. We can also catch any exceptions that lower level
+ * methods could throw and display appropriate errors and warnings.
+ *
+ * Note that the outer and the inner interface of this class is free from any
+ * UI toolkit dependencies. We can implement it with any UI framework or make it
+ * a cli client.
+ */
+class AppControllerBoilerplate {
+public:
+
+ /// A Progress indicator object smart pointer
+ using ProgresIndicatorPtr = std::shared_ptr<ProgressIndicator>;
+
+private:
+ class PriData; // Some structure to store progress indication data
+
+ // Pimpl data for thread safe progress indication features
+ std::unique_ptr<PriData> pri_data_;
+
+public:
+
+ AppControllerBoilerplate();
+ ~AppControllerBoilerplate();
+
+ using Path = std::string;
+ using PathList = std::vector<Path>;
+
+ /// Common runtime issue types
+ enum class IssueType {
+ INFO,
+ WARN,
+ WARN_Q, // Warning with a question to continue
+ ERR,
+ FATAL
+ };
+
+ /**
+ * @brief Query some paths from the user.
+ *
+ * It should display a file chooser dialog in case of a UI application.
+ * @param title Title of a possible query dialog.
+ * @param extensions Recognized file extensions.
+ * @return Returns a list of paths choosed by the user.
+ */
+ PathList query_destination_paths(
+ 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 std::string& title) const;
+
+ /**
+ * @brief Same as query_destination_paths but returns only one path.
+ */
+ Path query_destination_path(
+ const std::string& title,
+ const std::string& extensions,
+ const std::string& hint = "") const;
+
+ /**
+ * @brief Report an issue to the user be it fatal or recoverable.
+ *
+ * In a UI app this should display some message dialog.
+ *
+ * @param issuetype The type of the runtime issue.
+ * @param description A somewhat longer description of the issue.
+ * @param brief A very brief description. Can be used for message dialog
+ * title.
+ */
+ bool report_issue(IssueType issuetype,
+ const std::string& description,
+ const std::string& brief);
+
+ bool report_issue(IssueType issuetype,
+ const std::string& description);
+
+ /**
+ * @brief Return the global progress indicator for the current controller.
+ * Can be empty as well.
+ *
+ * Only one thread should use the global indicator at a time.
+ */
+ ProgresIndicatorPtr global_progress_indicator();
+
+ void global_progress_indicator(ProgresIndicatorPtr gpri);
+
+ /**
+ * @brief A predicate telling the caller whether it is the thread that
+ * created the AppConroller object itself. This probably means that the
+ * execution is in the UI thread. Otherwise it returns false meaning that
+ * some worker thread called this function.
+ * @return Return true for the same caller thread that created this
+ * object and false for every other.
+ */
+ bool is_main_thread() const;
+
+ /**
+ * @brief The frontend supports asynch execution.
+ *
+ * A Graphic UI will support this, a CLI may not. This can be used in
+ * subclass methods to decide whether to start threads for block free UI.
+ *
+ * Note that even a progress indicator's update called regularly can solve
+ * the blocking UI problem in some cases even when an event loop is present.
+ * This is how wxWidgets gauge work but creating a separate thread will make
+ * the UI even more fluent.
+ *
+ * @return true if a job or method can be executed asynchronously, false
+ * otherwise.
+ */
+ bool supports_asynch() const;
+
+ void process_events();
+
+protected:
+
+ /**
+ * @brief Create a new progress indicator and return a smart pointer to it.
+ * @param statenum The number of states for the given procedure.
+ * @param title The title of the procedure.
+ * @param firstmsg The message for the first subtask to be displayed.
+ * @return Smart pointer to the created object.
+ */
+ ProgresIndicatorPtr create_progress_indicator(
+ unsigned statenum,
+ const std::string& title,
+ const std::string& firstmsg) const;
+
+ ProgresIndicatorPtr create_progress_indicator(
+ unsigned statenum,
+ 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.
+ ProgresIndicatorPtr global_progressind_;
+};
+
+/**
+ * @brief Implementation of the printing logic.
+ */
+class PrintController: public AppControllerBoilerplate {
+ Print *print_ = nullptr;
+public:
+
+ // Must be public for perl to use it
+ explicit inline PrintController(Print *print): print_(print) {}
+
+ PrintController(const PrintController&) = delete;
+ PrintController(PrintController&&) = delete;
+
+ using Ptr = std::unique_ptr<PrintController>;
+
+ inline static Ptr create(Print *print) {
+ return PrintController::Ptr( new PrintController(print) );
+ }
+
+ const PrintConfig& config() const;
+};
+
+/**
+ * @brief Top level controller.
+ */
+class AppController: public AppControllerBoilerplate {
+ Model *model_ = nullptr;
+ PrintController::Ptr printctl;
+public:
+
+ /**
+ * @brief Get the print controller object.
+ *
+ * @return Return a raw pointer instead of a smart one for perl to be able
+ * to use this function and access the print controller.
+ */
+ PrintController * print_ctl() { return printctl.get(); }
+
+ /**
+ * @brief Set a model object.
+ *
+ * @param model A raw pointer to the model object. This can be used from
+ * perl.
+ */
+ void set_model(Model *model) { model_ = model; }
+
+ /**
+ * @brief Set the print object from perl.
+ *
+ * This will create a print controller that will then be accessible from
+ * perl.
+ * @param print A print object which can be a perl-ish extension as well.
+ */
+ void set_print(Print *print) {
+ printctl = PrintController::create(print);
+ }
+
+ /**
+ * @brief Set up a global progress indicator.
+ *
+ * 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
+ * ProgressIndicator interface so we can use this GUI widget from C++.
+ *
+ * This function should be called from perl.
+ *
+ * @param gauge_id The ID of the gague widget of the status bar.
+ * @param statusbar_id The ID of the status bar.
+ */
+ void set_global_progress_indicator(unsigned gauge_id,
+ unsigned statusbar_id);
+
+ void arrange_model();
+};
+
+}
+
+#endif // APPCONTROLLER_HPP
diff --git a/xs/src/slic3r/AppControllerWx.cpp b/xs/src/slic3r/AppControllerWx.cpp
new file mode 100644
index 000000000..36a465919
--- /dev/null
+++ b/xs/src/slic3r/AppControllerWx.cpp
@@ -0,0 +1,307 @@
+#include "AppController.hpp"
+
+#include <thread>
+#include <future>
+
+#include <slic3r/GUI/GUI.hpp>
+
+#include <wx/app.h>
+#include <wx/filedlg.h>
+#include <wx/msgdlg.h>
+#include <wx/progdlg.h>
+#include <wx/gauge.h>
+#include <wx/statusbr.h>
+#include <wx/event.h>
+
+// This source file implements the UI dependent methods of the AppControllers.
+// It will be clear what is needed to be reimplemented in case of a UI framework
+// change or a CLI client creation. In this particular case we use wxWidgets to
+// implement everything.
+
+namespace Slic3r {
+
+bool AppControllerBoilerplate::supports_asynch() const
+{
+ return true;
+}
+
+void AppControllerBoilerplate::process_events()
+{
+ wxSafeYield();
+}
+
+AppControllerBoilerplate::PathList
+AppControllerBoilerplate::query_destination_paths(
+ const std::string &title,
+ const std::string &extensions) const
+{
+
+ wxFileDialog dlg(wxTheApp->GetTopWindow(), _(title) );
+ dlg.SetWildcard(extensions);
+
+ dlg.ShowModal();
+
+ wxArrayString paths;
+ dlg.GetPaths(paths);
+
+ PathList ret(paths.size(), "");
+ for(auto& p : paths) ret.push_back(p.ToStdString());
+
+ return ret;
+}
+
+AppControllerBoilerplate::Path
+AppControllerBoilerplate::query_destination_path(
+ const std::string &title,
+ const std::string &extensions,
+ const std::string& hint) const
+{
+ wxFileDialog dlg(wxTheApp->GetTopWindow(), _(title) );
+ dlg.SetWildcard(extensions);
+
+ dlg.SetFilename(hint);
+
+ Path ret;
+
+ if(dlg.ShowModal() == wxID_OK) {
+ ret = Path(dlg.GetPath());
+ }
+
+ return ret;
+}
+
+bool AppControllerBoilerplate::report_issue(IssueType issuetype,
+ const std::string &description,
+ const std::string &brief)
+{
+ auto icon = wxICON_INFORMATION;
+ auto style = wxOK|wxCENTRE;
+ switch(issuetype) {
+ case IssueType::INFO: break;
+ case IssueType::WARN: icon = wxICON_WARNING; break;
+ case IssueType::WARN_Q: icon = wxICON_WARNING; style |= wxCANCEL; break;
+ case IssueType::ERR:
+ case IssueType::FATAL: icon = wxICON_ERROR;
+ }
+
+ auto ret = wxMessageBox(_(description), _(brief), icon | style);
+ return ret != wxCANCEL;
+}
+
+bool AppControllerBoilerplate::report_issue(
+ AppControllerBoilerplate::IssueType issuetype,
+ const std::string &description)
+{
+ return report_issue(issuetype, description, std::string());
+}
+
+wxDEFINE_EVENT(PROGRESS_STATUS_UPDATE_EVENT, wxCommandEvent);
+
+namespace {
+
+/*
+ * A simple thread safe progress dialog implementation that can be used from
+ * the main thread as well.
+ */
+class GuiProgressIndicator:
+ public ProgressIndicator, public wxEvtHandler {
+
+ wxProgressDialog gauge_;
+ using Base = ProgressIndicator;
+ wxString message_;
+ int range_; wxString title_;
+ bool is_asynch_ = false;
+
+ const int id_ = wxWindow::NewControlId();
+
+ // status update handler
+ void _state( wxCommandEvent& evt) {
+ unsigned st = evt.GetInt();
+ message_ = evt.GetString();
+ _state(st);
+ }
+
+ // Status update implementation
+ void _state( unsigned st) {
+ if(!gauge_.IsShown()) gauge_.ShowModal();
+ Base::state(st);
+ gauge_.Update(static_cast<int>(st), message_);
+ }
+
+public:
+
+ /// Setting whether it will be used from the UI thread or some worker thread
+ inline void asynch(bool is) { is_asynch_ = is; }
+
+ /// Get the mode of parallel operation.
+ inline bool asynch() const { return is_asynch_; }
+
+ inline GuiProgressIndicator(int range, const wxString& title,
+ const wxString& firstmsg) :
+ gauge_(title, firstmsg, range, wxTheApp->GetTopWindow(),
+ wxPD_APP_MODAL | wxPD_AUTO_HIDE),
+ message_(firstmsg),
+ range_(range), title_(title)
+ {
+ Base::max(static_cast<float>(range));
+ Base::states(static_cast<unsigned>(range));
+
+ Bind(PROGRESS_STATUS_UPDATE_EVENT,
+ &GuiProgressIndicator::_state,
+ this, id_);
+ }
+
+ virtual void state(float val) override {
+ state(static_cast<unsigned>(val));
+ }
+
+ void state(unsigned st) {
+ // send status update event
+ if(is_asynch_) {
+ auto evt = new wxCommandEvent(PROGRESS_STATUS_UPDATE_EVENT, id_);
+ evt->SetInt(st);
+ evt->SetString(message_);
+ wxQueueEvent(this, evt);
+ } else _state(st);
+ }
+
+ virtual void message(const std::string & msg) override {
+ message_ = _(msg);
+ }
+
+ virtual void messageFmt(const std::string& fmt, ...) {
+ va_list arglist;
+ va_start(arglist, fmt);
+ message_ = wxString::Format(_(fmt), arglist);
+ va_end(arglist);
+ }
+
+ virtual void title(const std::string & title) override {
+ title_ = _(title);
+ }
+};
+}
+
+AppControllerBoilerplate::ProgresIndicatorPtr
+AppControllerBoilerplate::create_progress_indicator(
+ unsigned statenum,
+ const std::string& title,
+ const std::string& firstmsg) const
+{
+ auto pri =
+ std::make_shared<GuiProgressIndicator>(statenum, title, firstmsg);
+
+ // We set up the mode of operation depending of the creator thread's
+ // identity
+ pri->asynch(!is_main_thread());
+
+ return pri;
+}
+
+AppControllerBoilerplate::ProgresIndicatorPtr
+AppControllerBoilerplate::create_progress_indicator(
+ unsigned statenum, const std::string &title) const
+{
+ return create_progress_indicator(statenum, title, std::string());
+}
+
+namespace {
+
+// A wrapper progress indicator class around the statusbar created in perl.
+class Wrapper: public ProgressIndicator, public wxEvtHandler {
+ wxGauge *gauge_;
+ wxStatusBar *stbar_;
+ using Base = ProgressIndicator;
+ wxString message_;
+ AppControllerBoilerplate& ctl_;
+
+ void showProgress(bool show = true) {
+ gauge_->Show(show);
+ }
+
+ void _state(unsigned st) {
+ if( st <= ProgressIndicator::max() ) {
+ Base::state(st);
+
+ if(!gauge_->IsShown()) showProgress(true);
+
+ stbar_->SetStatusText(message_);
+ if(static_cast<long>(st) == gauge_->GetRange()) {
+ gauge_->SetValue(0);
+ showProgress(false);
+ } else {
+ gauge_->SetValue(static_cast<int>(st));
+ }
+ }
+ }
+
+ // status update handler
+ void _state( wxCommandEvent& evt) {
+ unsigned st = evt.GetInt(); _state(st);
+ }
+
+ const int id_ = wxWindow::NewControlId();
+
+public:
+
+ inline Wrapper(wxGauge *gauge, wxStatusBar *stbar,
+ AppControllerBoilerplate& ctl):
+ gauge_(gauge), stbar_(stbar), ctl_(ctl)
+ {
+ Base::max(static_cast<float>(gauge->GetRange()));
+ Base::states(static_cast<unsigned>(gauge->GetRange()));
+
+ Bind(PROGRESS_STATUS_UPDATE_EVENT,
+ &Wrapper::_state,
+ this, id_);
+ }
+
+ virtual void state(float val) override {
+ state(unsigned(val));
+ }
+
+ virtual void max(float val) override {
+ if(val > 1.0) {
+ gauge_->SetRange(static_cast<int>(val));
+ ProgressIndicator::max(val);
+ }
+ }
+
+ void state(unsigned st) {
+ if(!ctl_.is_main_thread()) {
+ auto evt = new wxCommandEvent(PROGRESS_STATUS_UPDATE_EVENT, id_);
+ evt->SetInt(st);
+ wxQueueEvent(this, evt);
+ } else {
+ _state(st);
+ }
+ }
+
+ virtual void message(const std::string & msg) override {
+ message_ = _(msg);
+ }
+
+ virtual void message_fmt(const std::string& fmt, ...) override {
+ va_list arglist;
+ va_start(arglist, fmt);
+ message_ = wxString::Format(_(fmt), arglist);
+ va_end(arglist);
+ }
+
+ virtual void title(const std::string & /*title*/) override {}
+
+};
+}
+
+void AppController::set_global_progress_indicator(
+ unsigned gid,
+ unsigned sid)
+{
+ wxGauge* gauge = dynamic_cast<wxGauge*>(wxWindow::FindWindowById(gid));
+ wxStatusBar* sb = dynamic_cast<wxStatusBar*>(wxWindow::FindWindowById(sid));
+
+ if(gauge && sb) {
+ global_progressind_ = std::make_shared<Wrapper>(gauge, sb, *this);
+ }
+}
+}
diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp
index 09e10ac28..1901aa3a7 100644
--- a/xs/src/slic3r/GUI/3DScene.cpp
+++ b/xs/src/slic3r/GUI/3DScene.cpp
@@ -2,7 +2,6 @@
#include "3DScene.hpp"
-#include "../../libslic3r/libslic3r.h"
#include "../../libslic3r/ExtrusionEntity.hpp"
#include "../../libslic3r/ExtrusionEntityCollection.hpp"
#include "../../libslic3r/Geometry.hpp"
@@ -23,13 +22,15 @@
#include <tbb/parallel_for.h>
#include <tbb/spin_mutex.h>
-#include <wx/bitmap.h>
-#include <wx/dcmemory.h>
-#include <wx/image.h>
-#include <wx/settings.h>
+#include <Eigen/Dense>
#include "GUI.hpp"
+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 {
void GLIndexedVertexArray::load_mesh_flat_shading(const TriangleMesh &mesh)
@@ -198,6 +199,35 @@ const float GLVolume::HOVER_COLOR[4] = { 0.4f, 0.9f, 0.1f, 1.0f };
const float GLVolume::OUTSIDE_COLOR[4] = { 0.0f, 0.38f, 0.8f, 1.0f };
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_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)
+ , extruder_id(0)
+ , selected(false)
+ , is_active(true)
+ , zoom_to_volumes(true)
+ , shader_outside_printer_detection_enabled(false)
+ , is_outside(false)
+ , hover(false)
+ , is_modifier(false)
+ , is_wipe_tower(false)
+ , is_extrusion_path(false)
+ , tverts_range(0, size_t(-1))
+ , qverts_range(0, size_t(-1))
+{
+ color[0] = r;
+ color[1] = g;
+ color[2] = b;
+ color[3] = a;
+ set_render_color(r, g, b, a);
+}
+
void GLVolume::set_render_color(float r, float g, float b, float a)
{
render_color[0] = r;
@@ -218,20 +248,92 @@ void GLVolume::set_render_color(const float* rgba, unsigned int size)
void GLVolume::set_render_color()
{
if (selected)
- {
- if (is_outside)
- set_render_color(SELECTED_OUTSIDE_COLOR, 4);
- else
- set_render_color(SELECTED_COLOR, 4);
- }
+ set_render_color(is_outside ? SELECTED_OUTSIDE_COLOR : SELECTED_COLOR, 4);
else if (hover)
set_render_color(HOVER_COLOR, 4);
- else if (is_outside)
+ else if (is_outside && shader_outside_printer_detection_enabled)
set_render_color(OUTSIDE_COLOR, 4);
else
set_render_color(color, 4);
}
+const Pointf3& GLVolume::get_origin() const
+{
+ return m_origin;
+}
+
+void GLVolume::set_origin(const Pointf3& origin)
+{
+ 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)
+{
+ 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)
+{
+ 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;
+ }
+}
+
+void GLVolume::set_convex_hull(const TriangleMesh& convex_hull)
+{
+ m_convex_hull = &convex_hull;
+}
+
+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_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;
@@ -272,14 +374,16 @@ void GLVolume::render() const
if (!is_active)
return;
- glCullFace(GL_BACK);
- glPushMatrix();
- glTranslated(this->origin.x, this->origin.y, this->origin.z);
+ ::glCullFace(GL_BACK);
+ ::glPushMatrix();
+ ::glTranslated(m_origin.x, m_origin.y, m_origin.z);
+ ::glRotatef(m_angle_z * 180.0f / PI, 0.0f, 0.0f, 1.0f);
+ ::glScalef(m_scale_factor, m_scale_factor, m_scale_factor);
if (this->indexed_vertex_array.indexed())
this->indexed_vertex_array.render(this->tverts_range, this->qverts_range);
else
this->indexed_vertex_array.render();
- glPopMatrix();
+ ::glPopMatrix();
}
void GLVolume::render_using_layer_height() const
@@ -297,6 +401,7 @@ void GLVolume::render_using_layer_height() const
GLint z_texture_row_to_normalized_id = (layer_height_texture_data.shader_id > 0) ? glGetUniformLocation(layer_height_texture_data.shader_id, "z_texture_row_to_normalized") : -1;
GLint z_cursor_id = (layer_height_texture_data.shader_id > 0) ? glGetUniformLocation(layer_height_texture_data.shader_id, "z_cursor") : -1;
GLint z_cursor_band_width_id = (layer_height_texture_data.shader_id > 0) ? glGetUniformLocation(layer_height_texture_data.shader_id, "z_cursor_band_width") : -1;
+ GLint world_matrix_id = (layer_height_texture_data.shader_id > 0) ? glGetUniformLocation(layer_height_texture_data.shader_id, "volume_world_matrix") : -1;
if (z_to_texture_row_id >= 0)
glUniform1f(z_to_texture_row_id, (GLfloat)layer_height_texture_z_to_row_id());
@@ -310,14 +415,20 @@ void GLVolume::render_using_layer_height() const
if (z_cursor_band_width_id >= 0)
glUniform1f(z_cursor_band_width_id, (GLfloat)layer_height_texture_data.edit_band_width);
- unsigned int w = layer_height_texture_width();
- unsigned int h = layer_height_texture_height();
+ if (world_matrix_id >= 0)
+ ::glUniformMatrix4fv(world_matrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().data());
+
+ GLsizei w = (GLsizei)layer_height_texture_width();
+ GLsizei h = (GLsizei)layer_height_texture_height();
+ GLsizei half_w = w / 2;
+ GLsizei half_h = h / 2;
+ ::glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glBindTexture(GL_TEXTURE_2D, layer_height_texture_data.texture_id);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
- glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, w / 2, h / 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
+ glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, half_w, half_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, layer_height_texture_data_ptr_level0());
- glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, w / 2, h / 2, GL_RGBA, GL_UNSIGNED_BYTE, layer_height_texture_data_ptr_level1());
+ glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, half_w, half_h, GL_RGBA, GL_UNSIGNED_BYTE, layer_height_texture_data_ptr_level1());
render();
@@ -327,6 +438,128 @@ void GLVolume::render_using_layer_height() const
glUseProgram(current_program_id);
}
+void GLVolume::render_VBOs(int color_id, int detection_id, int worldmatrix_id) const
+{
+ if (!is_active)
+ return;
+
+ if (!indexed_vertex_array.vertices_and_normals_interleaved_VBO_id)
+ return;
+
+ if (layer_height_texture_data.can_use())
+ {
+ ::glDisableClientState(GL_VERTEX_ARRAY);
+ ::glDisableClientState(GL_NORMAL_ARRAY);
+ render_using_layer_height();
+ ::glEnableClientState(GL_VERTEX_ARRAY);
+ ::glEnableClientState(GL_NORMAL_ARRAY);
+ return;
+ }
+
+ GLsizei n_triangles = GLsizei(std::min(indexed_vertex_array.triangle_indices_size, tverts_range.second - tverts_range.first));
+ GLsizei n_quads = GLsizei(std::min(indexed_vertex_array.quad_indices_size, qverts_range.second - qverts_range.first));
+ if (n_triangles + n_quads == 0)
+ {
+ ::glDisableClientState(GL_VERTEX_ARRAY);
+ ::glDisableClientState(GL_NORMAL_ARRAY);
+
+ if (color_id >= 0)
+ {
+ float color[4];
+ ::memcpy((void*)color, (const void*)render_color, 4 * sizeof(float));
+ ::glUniform4fv(color_id, 1, (const GLfloat*)color);
+ }
+ else
+ ::glColor4f(render_color[0], render_color[1], render_color[2], render_color[3]);
+
+ if (detection_id != -1)
+ ::glUniform1i(detection_id, shader_outside_printer_detection_enabled ? 1 : 0);
+
+ if (worldmatrix_id != -1)
+ ::glUniformMatrix4fv(worldmatrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().data());
+
+ render();
+
+ ::glEnableClientState(GL_VERTEX_ARRAY);
+ ::glEnableClientState(GL_NORMAL_ARRAY);
+
+ return;
+ }
+
+ if (color_id >= 0)
+ ::glUniform4fv(color_id, 1, (const GLfloat*)render_color);
+ else
+ ::glColor4f(render_color[0], render_color[1], render_color[2], render_color[3]);
+
+ if (detection_id != -1)
+ ::glUniform1i(detection_id, shader_outside_printer_detection_enabled ? 1 : 0);
+
+ if (worldmatrix_id != -1)
+ ::glUniformMatrix4fv(worldmatrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().data());
+
+ ::glBindBuffer(GL_ARRAY_BUFFER, indexed_vertex_array.vertices_and_normals_interleaved_VBO_id);
+ ::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (const void*)(3 * sizeof(float)));
+ ::glNormalPointer(GL_FLOAT, 6 * sizeof(float), nullptr);
+
+ ::glPushMatrix();
+ ::glTranslated(m_origin.x, m_origin.y, m_origin.z);
+ ::glRotatef(m_angle_z * 180.0f / PI, 0.0f, 0.0f, 1.0f);
+ ::glScalef(m_scale_factor, m_scale_factor, m_scale_factor);
+
+ if (n_triangles > 0)
+ {
+ ::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexed_vertex_array.triangle_indices_VBO_id);
+ ::glDrawElements(GL_TRIANGLES, n_triangles, GL_UNSIGNED_INT, (const void*)(tverts_range.first * 4));
+ }
+ if (n_quads > 0)
+ {
+ ::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexed_vertex_array.quad_indices_VBO_id);
+ ::glDrawElements(GL_QUADS, n_quads, GL_UNSIGNED_INT, (const void*)(qverts_range.first * 4));
+ }
+
+ ::glPopMatrix();
+}
+
+void GLVolume::render_legacy() const
+{
+ assert(!indexed_vertex_array.vertices_and_normals_interleaved_VBO_id);
+ if (!is_active)
+ return;
+
+ GLsizei n_triangles = GLsizei(std::min(indexed_vertex_array.triangle_indices_size, tverts_range.second - tverts_range.first));
+ GLsizei n_quads = GLsizei(std::min(indexed_vertex_array.quad_indices_size, qverts_range.second - qverts_range.first));
+ if (n_triangles + n_quads == 0)
+ {
+ ::glDisableClientState(GL_VERTEX_ARRAY);
+ ::glDisableClientState(GL_NORMAL_ARRAY);
+
+ ::glColor4f(render_color[0], render_color[1], render_color[2], render_color[3]);
+ render();
+
+ ::glEnableClientState(GL_VERTEX_ARRAY);
+ ::glEnableClientState(GL_NORMAL_ARRAY);
+
+ return;
+ }
+
+ ::glColor4f(render_color[0], render_color[1], render_color[2], render_color[3]);
+ ::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), indexed_vertex_array.vertices_and_normals_interleaved.data() + 3);
+ ::glNormalPointer(GL_FLOAT, 6 * sizeof(float), indexed_vertex_array.vertices_and_normals_interleaved.data());
+
+ ::glPushMatrix();
+ ::glTranslated(m_origin.x, m_origin.y, m_origin.z);
+ ::glRotatef(m_angle_z * 180.0f / PI, 0.0f, 0.0f, 1.0f);
+ ::glScalef(m_scale_factor, m_scale_factor, m_scale_factor);
+
+ if (n_triangles > 0)
+ ::glDrawElements(GL_TRIANGLES, n_triangles, GL_UNSIGNED_INT, indexed_vertex_array.triangle_indices.data() + tverts_range.first);
+
+ if (n_quads > 0)
+ ::glDrawElements(GL_QUADS, n_quads, GL_UNSIGNED_INT, indexed_vertex_array.quad_indices.data() + qverts_range.first);
+
+ ::glPopMatrix();
+}
+
double GLVolume::layer_height_texture_z_to_row_id() const
{
return (this->layer_height_texture.get() == nullptr) ? 0.0 : double(this->layer_height_texture->cells - 1) / (double(this->layer_height_texture->width) * this->layer_height_texture_data.print_object->model_object()->bounding_box().max.z);
@@ -334,8 +567,8 @@ double GLVolume::layer_height_texture_z_to_row_id() const
void GLVolume::generate_layer_height_texture(PrintObject *print_object, bool force)
{
- GLTexture *tex = this->layer_height_texture.get();
- if (tex == nullptr)
+ LayersTexture *tex = this->layer_height_texture.get();
+ if (tex == nullptr)
// No layer_height_texture is assigned to this GLVolume, therefore the layer height texture cannot be filled.
return;
@@ -382,14 +615,14 @@ std::vector<int> GLVolumeCollection::load_object(
};
// Object will have a single common layer height texture for all volumes.
- std::shared_ptr<GLTexture> layer_height_texture = std::make_shared<GLTexture>();
-
+ std::shared_ptr<LayersTexture> layer_height_texture = std::make_shared<LayersTexture>();
+
std::vector<int> volumes_idx;
for (int volume_idx = 0; volume_idx < int(model_object->volumes.size()); ++ volume_idx) {
const ModelVolume *model_volume = model_object->volumes[volume_idx];
int extruder_id = -1;
- if (!model_volume->modifier)
+ if (model_volume->is_model_part())
{
extruder_id = model_volume->config.has("extruder") ? model_volume->config.option("extruder")->getInt() : 0;
if (extruder_id == 0)
@@ -399,11 +632,19 @@ std::vector<int> GLVolumeCollection::load_object(
for (int instance_idx : instance_idxs) {
const ModelInstance *instance = model_object->instances[instance_idx];
TriangleMesh mesh = model_volume->mesh;
- instance->transform_mesh(&mesh);
volumes_idx.push_back(int(this->volumes.size()));
float color[4];
memcpy(color, colors[((color_by == "volume") ? volume_idx : obj_idx) % 4], sizeof(float) * 3);
- color[3] = model_volume->modifier ? 0.5f : 1.f;
+ if (model_volume->is_support_blocker()) {
+ color[0] = 1.0f;
+ color[1] = 0.2f;
+ color[2] = 0.2f;
+ } else if (model_volume->is_support_enforcer()) {
+ color[0] = 0.2f;
+ color[1] = 0.2f;
+ color[2] = 1.0f;
+ }
+ color[3] = model_volume->is_model_part() ? 1.f : 0.5f;
this->volumes.emplace_back(new GLVolume(color));
GLVolume &v = *this->volumes.back();
if (use_VBOs)
@@ -426,14 +667,18 @@ std::vector<int> GLVolumeCollection::load_object(
else if (drag_by == "instance")
v.drag_group_id = obj_idx * 1000 + instance_idx;
- if (!model_volume->modifier)
+ if (model_volume->is_model_part())
{
+ 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;
}
- v.is_modifier = model_volume->modifier;
- v.outside_printer_detection_enabled = !model_volume->modifier;
+ v.is_modifier = ! model_volume->is_model_part();
+ v.shader_outside_printer_detection_enabled = model_volume->is_model_part();
+ v.set_origin(Pointf3(instance->offset.x, instance->offset.y, 0.0));
+ v.set_angle_z(instance->rotation);
+ v.set_scale_factor(instance->scaling_factor);
}
}
@@ -442,26 +687,68 @@ std::vector<int> GLVolumeCollection::load_object(
int GLVolumeCollection::load_wipe_tower_preview(
- int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool use_VBOs)
+ int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool use_VBOs, bool size_unknown, float brim_width)
{
- float color[4] = { 0.5f, 0.5f, 0.0f, 0.5f };
- this->volumes.emplace_back(new GLVolume(color));
- GLVolume &v = *this->volumes.back();
-
+ if (depth < 0.01f)
+ return int(this->volumes.size() - 1);
if (height == 0.0f)
height = 0.1f;
-
- auto mesh = make_cube(width, depth, height);
- mesh.translate(-width / 2.f, -depth / 2.f, 0.f);
Point origin_of_rotation(0.f, 0.f);
- mesh.rotate(rotation_angle,&origin_of_rotation);
+ TriangleMesh mesh;
+ float color[4] = { 0.5f, 0.5f, 0.0f, 1.f };
+
+ // In case we don't know precise dimensions of the wipe tower yet, we'll draw the box with different color with one side jagged:
+ if (size_unknown) {
+ color[0] = 0.9f;
+ color[1] = 0.6f;
+
+ depth = std::max(depth, 10.f); // Too narrow tower would interfere with the teeth. The estimate is not precise anyway.
+ float min_width = 30.f;
+ // We'll now create the box with jagged edge. y-coordinates of the pre-generated model are shifted so that the front
+ // edge has y=0 and centerline of the back edge has y=depth:
+ Pointf3s points;
+ std::vector<Point3> facets;
+ float out_points_idx[][3] = {{0, -depth, 0}, {0, 0, 0}, {38.453, 0, 0}, {61.547, 0, 0}, {100, 0, 0}, {100, -depth, 0}, {55.7735, -10, 0}, {44.2265, 10, 0},
+ {38.453, 0, 1}, {0, 0, 1}, {0, -depth, 1}, {100, -depth, 1}, {100, 0, 1}, {61.547, 0, 1}, {55.7735, -10, 1}, {44.2265, 10, 1}};
+ int out_facets_idx[][3] = {{0, 1, 2}, {3, 4, 5}, {6, 5, 0}, {3, 5, 6}, {6, 2, 7}, {6, 0, 2}, {8, 9, 10}, {11, 12, 13}, {10, 11, 14}, {14, 11, 13}, {15, 8, 14},
+ {8, 10, 14}, {3, 12, 4}, {3, 13, 12}, {6, 13, 3}, {6, 14, 13}, {7, 14, 6}, {7, 15, 14}, {2, 15, 7}, {2, 8, 15}, {1, 8, 2}, {1, 9, 8},
+ {0, 9, 1}, {0, 10, 9}, {5, 10, 0}, {5, 11, 10}, {4, 11, 5}, {4, 12, 11}};
+ for (int i=0;i<16;++i)
+ points.push_back(Pointf3(out_points_idx[i][0] / (100.f/min_width), out_points_idx[i][1] + depth, out_points_idx[i][2]));
+ for (int i=0;i<28;++i)
+ facets.push_back(Point3(out_facets_idx[i][0], out_facets_idx[i][1], out_facets_idx[i][2]));
+ TriangleMesh tooth_mesh(points, facets);
+
+ // We have the mesh ready. It has one tooth and width of min_width. We will now append several of these together until we are close to
+ // the required width of the block. Than we can scale it precisely.
+ size_t n = std::max(1, int(width/min_width)); // How many shall be merged?
+ for (size_t i=0;i<n;++i) {
+ mesh.merge(tooth_mesh);
+ tooth_mesh.translate(min_width, 0.f, 0.f);
+ }
+
+ mesh.scale(Pointf3(width/(n*min_width), 1.f, height)); // Scaling to proper width
+ }
+ else
+ mesh = make_cube(width, depth, height);
+
+ // We'll make another mesh to show the brim (fixed layer height):
+ TriangleMesh brim_mesh = make_cube(width+2.f*brim_width, depth+2.f*brim_width, 0.2f);
+ brim_mesh.translate(-brim_width, -brim_width, 0.f);
+ mesh.merge(brim_mesh);
+
+ mesh.rotate(rotation_angle, &origin_of_rotation); // rotates the box according to the config rotation setting
+
+ this->volumes.emplace_back(new GLVolume(color));
+ GLVolume &v = *this->volumes.back();
if (use_VBOs)
v.indexed_vertex_array.load_mesh_full_shading(mesh);
else
v.indexed_vertex_array.load_mesh_flat_shading(mesh);
- v.origin = Pointf3(pos_x, pos_y, 0.);
+ v.set_origin(Pointf3(pos_x, pos_y, 0.));
+
// finalize_geometry() clears the vertex arrays, therefore the bounding box has to be computed before finalize_geometry().
v.bounding_box = v.indexed_vertex_array.bounding_box();
v.indexed_vertex_array.finalize_geometry(use_VBOs);
@@ -469,6 +756,7 @@ int GLVolumeCollection::load_wipe_tower_preview(
v.select_group_id = obj_idx * 1000000;
v.drag_group_id = obj_idx * 1000;
v.is_wipe_tower = true;
+ v.shader_outside_printer_detection_enabled = ! size_unknown;
return int(this->volumes.size() - 1);
}
@@ -486,102 +774,23 @@ void GLVolumeCollection::render_VBOs() const
GLint color_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "uniform_color") : -1;
GLint print_box_min_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "print_box.min") : -1;
GLint print_box_max_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "print_box.max") : -1;
- GLint print_box_origin_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "print_box.volume_origin") : -1;
+ GLint print_box_detection_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "print_box.volume_detection") : -1;
+ GLint print_box_worldmatrix_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "print_box.volume_world_matrix") : -1;
- for (GLVolume *volume : this->volumes) {
- if (!volume->is_active)
- continue;
+ if (print_box_min_id != -1)
+ ::glUniform3fv(print_box_min_id, 1, (const GLfloat*)print_box_min);
- if (!volume->indexed_vertex_array.vertices_and_normals_interleaved_VBO_id)
- continue;
+ if (print_box_max_id != -1)
+ ::glUniform3fv(print_box_max_id, 1, (const GLfloat*)print_box_max);
+ for (GLVolume *volume : this->volumes)
+ {
if (volume->layer_height_texture_data.can_use())
- {
- ::glDisableClientState(GL_VERTEX_ARRAY);
- ::glDisableClientState(GL_NORMAL_ARRAY);
volume->generate_layer_height_texture(volume->layer_height_texture_data.print_object, false);
- volume->render_using_layer_height();
- ::glEnableClientState(GL_VERTEX_ARRAY);
- ::glEnableClientState(GL_NORMAL_ARRAY);
- continue;
- }
-
- volume->set_render_color();
-
- GLsizei n_triangles = GLsizei(std::min(volume->indexed_vertex_array.triangle_indices_size, volume->tverts_range.second - volume->tverts_range.first));
- GLsizei n_quads = GLsizei(std::min(volume->indexed_vertex_array.quad_indices_size, volume->qverts_range.second - volume->qverts_range.first));
- if (n_triangles + n_quads == 0)
- {
- ::glDisableClientState(GL_VERTEX_ARRAY);
- ::glDisableClientState(GL_NORMAL_ARRAY);
-
- if (color_id >= 0)
- {
- float color[4];
- ::memcpy((void*)color, (const void*)volume->render_color, 4 * sizeof(float));
- ::glUniform4fv(color_id, 1, (const GLfloat*)color);
- }
- else
- ::glColor4f(volume->render_color[0], volume->render_color[1], volume->render_color[2], volume->render_color[3]);
-
- if (print_box_min_id != -1)
- ::glUniform3fv(print_box_min_id, 1, (const GLfloat*)print_box_min);
-
- if (print_box_max_id != -1)
- ::glUniform3fv(print_box_max_id, 1, (const GLfloat*)print_box_max);
-
- if (print_box_origin_id != -1)
- {
- float origin[4] = { (float)volume->origin.x, (float)volume->origin.y, (float)volume->origin.z, volume->outside_printer_detection_enabled ? 1.0f : 0.0f };
- ::glUniform4fv(print_box_origin_id, 1, (const GLfloat*)origin);
- }
-
- volume->render();
-
- ::glEnableClientState(GL_VERTEX_ARRAY);
- ::glEnableClientState(GL_NORMAL_ARRAY);
-
- continue;
- }
-
- if (color_id >= 0)
- ::glUniform4fv(color_id, 1, (const GLfloat*)volume->render_color);
else
- ::glColor4f(volume->render_color[0], volume->render_color[1], volume->render_color[2], volume->render_color[3]);
-
- if (print_box_min_id != -1)
- ::glUniform3fv(print_box_min_id, 1, (const GLfloat*)print_box_min);
-
- if (print_box_max_id != -1)
- ::glUniform3fv(print_box_max_id, 1, (const GLfloat*)print_box_max);
-
- if (print_box_origin_id != -1)
- {
- float origin[4] = { (float)volume->origin.x, (float)volume->origin.y, (float)volume->origin.z, volume->outside_printer_detection_enabled ? 1.0f : 0.0f };
- ::glUniform4fv(print_box_origin_id, 1, (const GLfloat*)origin);
- }
-
- ::glBindBuffer(GL_ARRAY_BUFFER, volume->indexed_vertex_array.vertices_and_normals_interleaved_VBO_id);
- ::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (const void*)(3 * sizeof(float)));
- ::glNormalPointer(GL_FLOAT, 6 * sizeof(float), nullptr);
+ volume->set_render_color();
- bool has_offset = (volume->origin.x != 0) || (volume->origin.y != 0) || (volume->origin.z != 0);
- if (has_offset) {
- ::glPushMatrix();
- ::glTranslated(volume->origin.x, volume->origin.y, volume->origin.z);
- }
-
- if (n_triangles > 0) {
- ::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, volume->indexed_vertex_array.triangle_indices_VBO_id);
- ::glDrawElements(GL_TRIANGLES, n_triangles, GL_UNSIGNED_INT, (const void*)(volume->tverts_range.first * 4));
- }
- if (n_quads > 0) {
- ::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, volume->indexed_vertex_array.quad_indices_VBO_id);
- ::glDrawElements(GL_QUADS, n_quads, GL_UNSIGNED_INT, (const void*)(volume->qverts_range.first * 4));
- }
-
- if (has_offset)
- ::glPopMatrix();
+ volume->render_VBOs(color_id, print_box_detection_id, print_box_worldmatrix_id);
}
::glBindBuffer(GL_ARRAY_BUFFER, 0);
@@ -602,43 +811,10 @@ void GLVolumeCollection::render_legacy() const
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
- for (GLVolume *volume : this->volumes) {
- assert(! volume->indexed_vertex_array.vertices_and_normals_interleaved_VBO_id);
- if (!volume->is_active)
- continue;
-
+ for (GLVolume *volume : this->volumes)
+ {
volume->set_render_color();
-
- GLsizei n_triangles = GLsizei(std::min(volume->indexed_vertex_array.triangle_indices_size, volume->tverts_range.second - volume->tverts_range.first));
- GLsizei n_quads = GLsizei(std::min(volume->indexed_vertex_array.quad_indices_size, volume->qverts_range.second - volume->qverts_range.first));
- if (n_triangles + n_quads == 0)
- {
- ::glDisableClientState(GL_VERTEX_ARRAY);
- ::glDisableClientState(GL_NORMAL_ARRAY);
-
- ::glColor4f(volume->render_color[0], volume->render_color[1], volume->render_color[2], volume->render_color[3]);
- volume->render();
-
- ::glEnableClientState(GL_VERTEX_ARRAY);
- ::glEnableClientState(GL_NORMAL_ARRAY);
-
- continue;
- }
-
- glColor4f(volume->render_color[0], volume->render_color[1], volume->render_color[2], volume->render_color[3]);
- glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), volume->indexed_vertex_array.vertices_and_normals_interleaved.data() + 3);
- glNormalPointer(GL_FLOAT, 6 * sizeof(float), volume->indexed_vertex_array.vertices_and_normals_interleaved.data());
- bool has_offset = volume->origin.x != 0 || volume->origin.y != 0 || volume->origin.z != 0;
- if (has_offset) {
- glPushMatrix();
- glTranslated(volume->origin.x, volume->origin.y, volume->origin.z);
- }
- if (n_triangles > 0)
- glDrawElements(GL_TRIANGLES, n_triangles, GL_UNSIGNED_INT, volume->indexed_vertex_array.triangle_indices.data() + volume->tverts_range.first);
- if (n_quads > 0)
- glDrawElements(GL_QUADS, n_quads, GL_UNSIGNED_INT, volume->indexed_vertex_array.quad_indices.data() + volume->qverts_range.first);
- if (has_offset)
- glPopMatrix();
+ volume->render_legacy();
}
glDisableClientState(GL_VERTEX_ARRAY);
@@ -647,7 +823,7 @@ void GLVolumeCollection::render_legacy() const
glDisable(GL_BLEND);
}
-bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config)
+bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, ModelInstance::EPrintVolumeState* out_state)
{
if (config == nullptr)
return false;
@@ -661,18 +837,31 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config)
// Allow the objects to protrude below the print bed
print_volume.min.z = -1e10;
- bool contained = true;
+ ModelInstance::EPrintVolumeState state = ModelInstance::PVS_Inside;
+ bool all_contained = true;
+
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)))
{
- bool state = print_volume.contains(volume->transformed_bounding_box());
- contained &= state;
- volume->is_outside = !state;
+ const BoundingBoxf3& bb = volume->transformed_convex_hull_bounding_box();
+ bool contained = print_volume.contains(bb);
+ all_contained &= contained;
+
+ volume->is_outside = !contained;
+
+ if ((state == ModelInstance::PVS_Inside) && volume->is_outside)
+ state = ModelInstance::PVS_Fully_Outside;
+
+ if ((state == ModelInstance::PVS_Fully_Outside) && volume->is_outside && print_volume.intersects(bb))
+ state = ModelInstance::PVS_Partly_Outside;
}
}
- return contained;
+ if (out_state != nullptr)
+ *out_state = state;
+
+ return all_contained;
}
void GLVolumeCollection::reset_outside_state()
@@ -1002,7 +1191,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]);
@@ -1010,10 +1199,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]);
}
@@ -1480,245 +1669,8 @@ void _3DScene::point3_to_verts(const Point3& point, double width, double height,
thick_point_to_verts(point, width, height, volume);
}
-_3DScene::LegendTexture _3DScene::s_legend_texture;
-_3DScene::WarningTexture _3DScene::s_warning_texture;
GUI::GLCanvas3DManager _3DScene::s_canvas_mgr;
-unsigned int _3DScene::TextureBase::finalize()
-{
- if (!m_data.empty()) {
- // sends buffer to gpu
- ::glGenTextures(1, &m_tex_id);
- ::glBindTexture(GL_TEXTURE_2D, m_tex_id);
- ::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, (GLsizei)m_tex_width, (GLsizei)m_tex_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const GLvoid*)m_data.data());
- ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
- ::glBindTexture(GL_TEXTURE_2D, 0);
- m_data.clear();
- }
- return (m_tex_width > 0 && m_tex_height > 0) ? m_tex_id : 0;
-}
-
-void _3DScene::TextureBase::_destroy_texture()
-{
- if (m_tex_id > 0)
- {
- ::glDeleteTextures(1, &m_tex_id);
- m_tex_id = 0;
- m_tex_height = 0;
- m_tex_width = 0;
- }
- m_data.clear();
-}
-
-
-const unsigned char _3DScene::WarningTexture::Background_Color[3] = { 9, 91, 134 };
-const unsigned char _3DScene::WarningTexture::Opacity = 255;
-
-// Generate a texture data, but don't load it into the GPU yet, as the GPU context may not yet be valid.
-bool _3DScene::WarningTexture::generate(const std::string& msg)
-{
- // Mark the texture as released, but don't release the texture from the GPU yet.
- m_tex_width = m_tex_height = 0;
- m_data.clear();
-
- if (msg.empty())
- return false;
-
- wxMemoryDC memDC;
- // select default font
- memDC.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
-
- // calculates texture size
- wxCoord w, h;
- memDC.GetTextExtent(msg, &w, &h);
- m_tex_width = (unsigned int)w;
- m_tex_height = (unsigned int)h;
-
- // generates bitmap
- wxBitmap bitmap(m_tex_width, m_tex_height);
-
-#if defined(__APPLE__) || defined(_MSC_VER)
- bitmap.UseAlpha();
-#endif
-
- memDC.SelectObject(bitmap);
- memDC.SetBackground(wxBrush(wxColour(Background_Color[0], Background_Color[1], Background_Color[2])));
- memDC.Clear();
-
- memDC.SetTextForeground(*wxWHITE);
-
- // draw message
- memDC.DrawText(msg, 0, 0);
-
- memDC.SelectObject(wxNullBitmap);
-
- // Convert the bitmap into a linear data ready to be loaded into the GPU.
- {
- wxImage image = bitmap.ConvertToImage();
- image.SetMaskColour(Background_Color[0], Background_Color[1], Background_Color[2]);
-
- // prepare buffer
- m_data.assign(4 * m_tex_width * m_tex_height, 0);
- for (unsigned int h = 0; h < m_tex_height; ++h)
- {
- unsigned int hh = h * m_tex_width;
- unsigned char* px_ptr = m_data.data() + 4 * hh;
- for (unsigned int w = 0; w < m_tex_width; ++w)
- {
- *px_ptr++ = image.GetRed(w, h);
- *px_ptr++ = image.GetGreen(w, h);
- *px_ptr++ = image.GetBlue(w, h);
- *px_ptr++ = image.IsTransparent(w, h) ? 0 : Opacity;
- }
- }
- }
- return true;
-}
-
-const unsigned char _3DScene::LegendTexture::Squares_Border_Color[3] = { 64, 64, 64 };
-const unsigned char _3DScene::LegendTexture::Background_Color[3] = { 9, 91, 134 };
-const unsigned char _3DScene::LegendTexture::Opacity = 255;
-
-// Generate a texture data, but don't load it into the GPU yet, as the GPU context may not yet be valid.
-bool _3DScene::LegendTexture::generate(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors)
-{
- // Mark the texture as released, but don't release the texture from the GPU yet.
- m_tex_width = m_tex_height = 0;
- m_data.clear();
-
- // collects items to render
- auto title = GUI::L_str(preview_data.get_legend_title());
- const GCodePreviewData::LegendItemsList& items = preview_data.get_legend_items(tool_colors);
-
- unsigned int items_count = (unsigned int)items.size();
- if (items_count == 0)
- // nothing to render, return
- return false;
-
- wxMemoryDC memDC;
- // select default font
- memDC.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
-
- // calculates texture size
- wxCoord w, h;
- memDC.GetTextExtent(title, &w, &h);
- unsigned int title_width = (unsigned int)w;
- unsigned int title_height = (unsigned int)h;
-
- unsigned int max_text_width = 0;
- unsigned int max_text_height = 0;
- for (const GCodePreviewData::LegendItem& item : items)
- {
- memDC.GetTextExtent(GUI::from_u8(item.text), &w, &h);
- max_text_width = std::max(max_text_width, (unsigned int)w);
- max_text_height = std::max(max_text_height, (unsigned int)h);
- }
-
- m_tex_width = std::max(2 * Px_Border + title_width, 2 * (Px_Border + Px_Square_Contour) + Px_Square + Px_Text_Offset + max_text_width);
- m_tex_height = 2 * (Px_Border + Px_Square_Contour) + title_height + Px_Title_Offset + items_count * Px_Square;
- if (items_count > 1)
- m_tex_height += (items_count - 1) * Px_Square_Contour;
-
- // generates bitmap
- wxBitmap bitmap(m_tex_width, m_tex_height);
-
-#if defined(__APPLE__) || defined(_MSC_VER)
- bitmap.UseAlpha();
-#endif
-
- memDC.SelectObject(bitmap);
- memDC.SetBackground(wxBrush(wxColour(Background_Color[0], Background_Color[1], Background_Color[2])));
- memDC.Clear();
-
- memDC.SetTextForeground(*wxWHITE);
-
- // draw title
- unsigned int title_x = Px_Border;
- unsigned int title_y = Px_Border;
- memDC.DrawText(title, title_x, title_y);
-
- // draw icons contours as background
- unsigned int squares_contour_x = Px_Border;
- unsigned int squares_contour_y = Px_Border + title_height + Px_Title_Offset;
- unsigned int squares_contour_width = Px_Square + 2 * Px_Square_Contour;
- unsigned int squares_contour_height = items_count * Px_Square + 2 * Px_Square_Contour;
- if (items_count > 1)
- squares_contour_height += (items_count - 1) * Px_Square_Contour;
-
- wxColour color(Squares_Border_Color[0], Squares_Border_Color[1], Squares_Border_Color[2]);
- wxPen pen(color);
- wxBrush brush(color);
- memDC.SetPen(pen);
- memDC.SetBrush(brush);
- memDC.DrawRectangle(wxRect(squares_contour_x, squares_contour_y, squares_contour_width, squares_contour_height));
-
- // draw items (colored icon + text)
- unsigned int icon_x = squares_contour_x + Px_Square_Contour;
- unsigned int icon_x_inner = icon_x + 1;
- unsigned int icon_y = squares_contour_y + Px_Square_Contour;
- unsigned int icon_y_step = Px_Square + Px_Square_Contour;
-
- unsigned int text_x = icon_x + Px_Square + Px_Text_Offset;
- unsigned int text_y_offset = (Px_Square - max_text_height) / 2;
-
- unsigned int px_inner_square = Px_Square - 2;
-
- for (const GCodePreviewData::LegendItem& item : items)
- {
- // draw darker icon perimeter
- const std::vector<unsigned char>& item_color_bytes = item.color.as_bytes();
- wxImage::HSVValue dark_hsv = wxImage::RGBtoHSV(wxImage::RGBValue(item_color_bytes[0], item_color_bytes[1], item_color_bytes[2]));
- dark_hsv.value *= 0.75;
- wxImage::RGBValue dark_rgb = wxImage::HSVtoRGB(dark_hsv);
- color.Set(dark_rgb.red, dark_rgb.green, dark_rgb.blue, item_color_bytes[3]);
- pen.SetColour(color);
- brush.SetColour(color);
- memDC.SetPen(pen);
- memDC.SetBrush(brush);
- memDC.DrawRectangle(wxRect(icon_x, icon_y, Px_Square, Px_Square));
-
- // draw icon interior
- color.Set(item_color_bytes[0], item_color_bytes[1], item_color_bytes[2], item_color_bytes[3]);
- pen.SetColour(color);
- brush.SetColour(color);
- memDC.SetPen(pen);
- memDC.SetBrush(brush);
- memDC.DrawRectangle(wxRect(icon_x_inner, icon_y + 1, px_inner_square, px_inner_square));
-
- // draw text
- memDC.DrawText(GUI::from_u8(item.text), text_x, icon_y + text_y_offset);
-
- // update y
- icon_y += icon_y_step;
- }
-
- memDC.SelectObject(wxNullBitmap);
-
- // Convert the bitmap into a linear data ready to be loaded into the GPU.
- {
- wxImage image = bitmap.ConvertToImage();
- image.SetMaskColour(Background_Color[0], Background_Color[1], Background_Color[2]);
-
- // prepare buffer
- m_data.assign(4 * m_tex_width * m_tex_height, 0);
- for (unsigned int h = 0; h < m_tex_height; ++h)
- {
- unsigned int hh = h * m_tex_width;
- unsigned char* px_ptr = m_data.data() + 4 * hh;
- for (unsigned int w = 0; w < m_tex_width; ++w)
- {
- *px_ptr++ = image.GetRed(w, h);
- *px_ptr++ = image.GetGreen(w, h);
- *px_ptr++ = image.GetBlue(w, h);
- *px_ptr++ = image.IsTransparent(w, h) ? 0 : Opacity;
- }
- }
- }
- return true;
-}
-
void _3DScene::init_gl()
{
s_canvas_mgr.init_gl();
@@ -1784,7 +1736,7 @@ void _3DScene::update_volumes_selection(wxGLCanvas* canvas, const std::vector<in
s_canvas_mgr.update_volumes_selection(canvas, selections);
}
-bool _3DScene::check_volumes_outside_state(wxGLCanvas* canvas, const DynamicPrintConfig* config)
+int _3DScene::check_volumes_outside_state(wxGLCanvas* canvas, const DynamicPrintConfig* config)
{
return s_canvas_mgr.check_volumes_outside_state(canvas, config);
}
@@ -1919,6 +1871,11 @@ void _3DScene::enable_force_zoom_to_bed(wxGLCanvas* canvas, bool enable)
s_canvas_mgr.enable_force_zoom_to_bed(canvas, enable);
}
+void _3DScene::enable_dynamic_background(wxGLCanvas* canvas, bool enable)
+{
+ s_canvas_mgr.enable_dynamic_background(canvas, enable);
+}
+
void _3DScene::allow_multisample(wxGLCanvas* canvas, bool allow)
{
s_canvas_mgr.allow_multisample(canvas, allow);
@@ -1949,6 +1906,11 @@ void _3DScene::update_volumes_colors_by_extruder(wxGLCanvas* canvas)
s_canvas_mgr.update_volumes_colors_by_extruder(canvas);
}
+void _3DScene::update_gizmos_data(wxGLCanvas* canvas)
+{
+ s_canvas_mgr.update_gizmos_data(canvas);
+}
+
void _3DScene::render(wxGLCanvas* canvas)
{
s_canvas_mgr.render(canvas);
@@ -2044,6 +2006,16 @@ void _3DScene::register_on_gizmo_scale_uniformly_callback(wxGLCanvas* canvas, vo
s_canvas_mgr.register_on_gizmo_scale_uniformly_callback(canvas, callback);
}
+void _3DScene::register_on_gizmo_rotate_callback(wxGLCanvas* canvas, void* callback)
+{
+ s_canvas_mgr.register_on_gizmo_rotate_callback(canvas, callback);
+}
+
+void _3DScene::register_on_update_geometry_info_callback(wxGLCanvas* canvas, void* callback)
+{
+ s_canvas_mgr.register_on_update_geometry_info_callback(canvas, callback);
+}
+
static inline int hex_digit_to_int(const char c)
{
return
@@ -2086,74 +2058,19 @@ void _3DScene::reload_scene(wxGLCanvas* canvas, bool force)
s_canvas_mgr.reload_scene(canvas, force);
}
-void _3DScene::load_print_toolpaths(wxGLCanvas* canvas)
-{
- s_canvas_mgr.load_print_toolpaths(canvas);
-}
-
-void _3DScene::load_print_object_toolpaths(wxGLCanvas* canvas, const PrintObject* print_object, const std::vector<std::string>& str_tool_colors)
-{
- s_canvas_mgr.load_print_object_toolpaths(canvas, print_object, str_tool_colors);
-}
-
-void _3DScene::load_wipe_tower_toolpaths(wxGLCanvas* canvas, const std::vector<std::string>& str_tool_colors)
-{
- s_canvas_mgr.load_wipe_tower_toolpaths(canvas, str_tool_colors);
-}
-
void _3DScene::load_gcode_preview(wxGLCanvas* canvas, const GCodePreviewData* preview_data, const std::vector<std::string>& str_tool_colors)
{
s_canvas_mgr.load_gcode_preview(canvas, preview_data, str_tool_colors);
}
-void _3DScene::generate_legend_texture(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors)
-{
- s_legend_texture.generate(preview_data, tool_colors);
-}
-
-unsigned int _3DScene::get_legend_texture_width()
+void _3DScene::load_preview(wxGLCanvas* canvas, const std::vector<std::string>& str_tool_colors)
{
- return s_legend_texture.get_texture_width();
-}
-
-unsigned int _3DScene::get_legend_texture_height()
-{
- return s_legend_texture.get_texture_height();
+ s_canvas_mgr.load_preview(canvas, str_tool_colors);
}
void _3DScene::reset_legend_texture()
{
- s_legend_texture.reset_texture();
-}
-
-unsigned int _3DScene::finalize_legend_texture()
-{
- return s_legend_texture.finalize();
-}
-
-unsigned int _3DScene::get_warning_texture_width()
-{
- return s_warning_texture.get_texture_width();
-}
-
-unsigned int _3DScene::get_warning_texture_height()
-{
- return s_warning_texture.get_texture_height();
-}
-
-void _3DScene::generate_warning_texture(const std::string& msg)
-{
- s_warning_texture.generate(msg);
-}
-
-void _3DScene::reset_warning_texture()
-{
- s_warning_texture.reset_texture();
-}
-
-unsigned int _3DScene::finalize_warning_texture()
-{
- return s_warning_texture.finalize();
+ s_canvas_mgr.reset_legend_texture();
}
} // namespace Slic3r
diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp
index 9016f984d..5cd144c68 100644
--- a/xs/src/slic3r/GUI/3DScene.hpp
+++ b/xs/src/slic3r/GUI/3DScene.hpp
@@ -6,6 +6,7 @@
#include "../../libslic3r/Line.hpp"
#include "../../libslic3r/TriangleMesh.hpp"
#include "../../libslic3r/Utils.hpp"
+#include "../../libslic3r/Model.hpp"
#include "../../slic3r/GUI/GLCanvas3DManager.hpp"
class wxBitmap;
@@ -199,10 +200,10 @@ private:
}
};
-class GLTexture
+class LayersTexture
{
public:
- GLTexture() : width(0), height(0), levels(0), cells(0) {}
+ LayersTexture() : width(0), height(0), levels(0), cells(0) {}
// Texture data
std::vector<char> data;
@@ -240,7 +241,7 @@ class GLVolume {
edit_band_width = 0.0f;
}
- bool can_use() { return (texture_id > 0) && (shader_id > 0) && (print_object != nullptr); }
+ bool can_use() const { return (texture_id > 0) && (shader_id > 0) && (print_object != nullptr); }
};
public:
@@ -249,44 +250,31 @@ public:
static const float OUTSIDE_COLOR[4];
static const float SELECTED_OUTSIDE_COLOR[4];
- GLVolume(float r = 1.f, float g = 1.f, float b = 1.f, float a = 1.f) :
- composite_id(-1),
- select_group_id(-1),
- drag_group_id(-1),
- extruder_id(0),
- selected(false),
- is_active(true),
- zoom_to_volumes(true),
- outside_printer_detection_enabled(true),
- is_outside(false),
- hover(false),
- is_modifier(false),
- is_wipe_tower(false),
- tverts_range(0, size_t(-1)),
- qverts_range(0, size_t(-1))
- {
- color[0] = r;
- color[1] = g;
- color[2] = b;
- color[3] = a;
- set_render_color(r, g, b, a);
- }
+ GLVolume(float r = 1.f, float g = 1.f, float b = 1.f, float a = 1.f);
GLVolume(const float *rgba) : GLVolume(rgba[0], rgba[1], rgba[2], rgba[3]) {}
- std::vector<int> load_object(
- const ModelObject *model_object,
- const std::vector<int> &instance_idxs,
- const std::string &color_by,
- const std::string &select_by,
- const std::string &drag_by);
+private:
+ // Offset of the volume to be rendered.
+ Pointf3 m_origin;
+ // Rotation around Z axis of the volume to be rendered.
+ float m_angle_z;
+ // Scale factor of the volume to be rendered.
+ float m_scale_factor;
+ // Bounding box of this volume, in unscaled coordinates.
+ mutable BoundingBoxf3 m_transformed_bounding_box;
+ // 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;
- int load_wipe_tower_preview(
- int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool use_VBOs);
+public:
// Bounding box of this volume, in unscaled coordinates.
BoundingBoxf3 bounding_box;
- // Offset of the volume to be rendered.
- Pointf3 origin;
// Color of the triangles / quads held by this volume.
float color[4];
// Color used to render this volume.
@@ -305,8 +293,8 @@ public:
bool is_active;
// Whether or not to use this volume when applying zoom_to_volumes()
bool zoom_to_volumes;
- // Wheter or not this volume is enabled for outside print volume detection.
- bool outside_printer_detection_enabled;
+ // Wheter or not this volume is enabled for outside print volume detection in shader.
+ bool shader_outside_printer_detection_enabled;
// Wheter or not this volume is outside print volume.
bool is_outside;
// Boolean: Is mouse over this object?
@@ -315,6 +303,8 @@ public:
bool is_modifier;
// Wheter or not this volume has been generated from the wipe tower
bool is_wipe_tower;
+ // Wheter or not this volume has been generated from an extrusion path
+ bool is_extrusion_path;
// Interleaved triangles & normals with indexed triangles & quads.
GLIndexedVertexArray indexed_vertex_array;
@@ -333,10 +323,19 @@ public:
// Sets render color in dependence of current state
void set_render_color();
+ const Pointf3& get_origin() const;
+ 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; }
- BoundingBoxf3 transformed_bounding_box() const { BoundingBoxf3 bb = this->bounding_box; bb.translate(this->origin); return bb; }
+
+ 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(); }
@@ -344,11 +343,14 @@ public:
void set_range(coordf_t low, coordf_t high);
void render() const;
void render_using_layer_height() const;
+ void render_VBOs(int color_id, int detection_id, int worldmatrix_id) const;
+ void render_legacy() const;
+
void finalize_geometry(bool use_VBOs) { this->indexed_vertex_array.finalize_geometry(use_VBOs); }
void release_geometry() { this->indexed_vertex_array.release_geometry(); }
/************************************************ Layer height texture ****************************************************/
- std::shared_ptr<GLTexture> layer_height_texture;
+ std::shared_ptr<LayersTexture> layer_height_texture;
// Data to render this volume using the layer height texture
LayerHeightTextureData layer_height_texture_data;
@@ -405,7 +407,7 @@ public:
bool use_VBOs);
int load_wipe_tower_preview(
- int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool use_VBOs);
+ int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool use_VBOs, bool size_unknown, float brim_width);
// Render the volumes by OpenGL.
void render_VBOs() const;
@@ -429,7 +431,9 @@ public:
print_box_max[0] = max_x; print_box_max[1] = max_y; print_box_max[2] = max_z;
}
- bool check_outside_state(const DynamicPrintConfig* config);
+ // returns true if all the volumes are completely contained in the print volume
+ // returns the containment state in the given out_state, if non-null
+ bool check_outside_state(const DynamicPrintConfig* config, ModelInstance::EPrintVolumeState* out_state);
void reset_outside_state();
void update_colors_by_extruder(const DynamicPrintConfig* config);
@@ -444,65 +448,6 @@ private:
class _3DScene
{
- class TextureBase
- {
- protected:
- unsigned int m_tex_id;
- unsigned int m_tex_width;
- unsigned int m_tex_height;
-
- // generate() fills in m_data with the pixels, while finalize() moves the data to the GPU before rendering.
- std::vector<unsigned char> m_data;
-
- public:
- TextureBase() : m_tex_id(0), m_tex_width(0), m_tex_height(0) {}
- virtual ~TextureBase() { _destroy_texture(); }
-
- // If not loaded, load the texture data into the GPU. Return a texture ID or 0 if the texture has zero size.
- unsigned int finalize();
-
- unsigned int get_texture_id() const { return m_tex_id; }
- unsigned int get_texture_width() const { return m_tex_width; }
- unsigned int get_texture_height() const { return m_tex_height; }
-
- void reset_texture() { _destroy_texture(); }
-
- private:
- void _destroy_texture();
- };
-
- class WarningTexture : public TextureBase
- {
- static const unsigned char Background_Color[3];
- static const unsigned char Opacity;
-
- public:
- WarningTexture() : TextureBase() {}
-
- // Generate a texture data, but don't load it into the GPU yet, as the glcontext may not be valid yet.
- bool generate(const std::string& msg);
- };
-
- class LegendTexture : public TextureBase
- {
- static const unsigned int Px_Title_Offset = 5;
- static const unsigned int Px_Text_Offset = 5;
- static const unsigned int Px_Square = 20;
- static const unsigned int Px_Square_Contour = 1;
- static const unsigned int Px_Border = Px_Square / 2;
- static const unsigned char Squares_Border_Color[3];
- static const unsigned char Background_Color[3];
- static const unsigned char Opacity;
-
- public:
- LegendTexture() : TextureBase() {}
-
- // Generate a texture data, but don't load it into the GPU yet, as the glcontext may not be valid yet.
- bool generate(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors);
- };
-
- static LegendTexture s_legend_texture;
- static WarningTexture s_warning_texture;
static GUI::GLCanvas3DManager s_canvas_mgr;
public:
@@ -523,7 +468,7 @@ public:
static void deselect_volumes(wxGLCanvas* canvas);
static void select_volume(wxGLCanvas* canvas, unsigned int id);
static void update_volumes_selection(wxGLCanvas* canvas, const std::vector<int>& selections);
- static bool check_volumes_outside_state(wxGLCanvas* canvas, const DynamicPrintConfig* config);
+ static int check_volumes_outside_state(wxGLCanvas* canvas, const DynamicPrintConfig* config);
static bool move_volume_up(wxGLCanvas* canvas, unsigned int id);
static bool move_volume_down(wxGLCanvas* canvas, unsigned int id);
@@ -560,6 +505,7 @@ public:
static void enable_gizmos(wxGLCanvas* canvas, bool enable);
static void enable_shader(wxGLCanvas* canvas, bool enable);
static void enable_force_zoom_to_bed(wxGLCanvas* canvas, bool enable);
+ static void enable_dynamic_background(wxGLCanvas* canvas, bool enable);
static void allow_multisample(wxGLCanvas* canvas, bool allow);
static void zoom_to_bed(wxGLCanvas* canvas);
@@ -568,6 +514,7 @@ public:
static void set_viewport_from_scene(wxGLCanvas* canvas, wxGLCanvas* other);
static void update_volumes_colors_by_extruder(wxGLCanvas* canvas);
+ static void update_gizmos_data(wxGLCanvas* canvas);
static void render(wxGLCanvas* canvas);
@@ -590,32 +537,18 @@ public:
static void register_on_wipe_tower_moved_callback(wxGLCanvas* canvas, void* callback);
static void register_on_enable_action_buttons_callback(wxGLCanvas* canvas, void* callback);
static void register_on_gizmo_scale_uniformly_callback(wxGLCanvas* canvas, void* callback);
+ static void register_on_gizmo_rotate_callback(wxGLCanvas* canvas, void* callback);
+ static void register_on_update_geometry_info_callback(wxGLCanvas* canvas, void* callback);
static std::vector<int> load_object(wxGLCanvas* canvas, const ModelObject* model_object, int obj_idx, std::vector<int> instance_idxs);
static std::vector<int> load_object(wxGLCanvas* canvas, const Model* model, int obj_idx);
static void reload_scene(wxGLCanvas* canvas, bool force);
- static void load_print_toolpaths(wxGLCanvas* canvas);
- static void load_print_object_toolpaths(wxGLCanvas* canvas, const PrintObject* print_object, const std::vector<std::string>& str_tool_colors);
- static void load_wipe_tower_toolpaths(wxGLCanvas* canvas, const std::vector<std::string>& str_tool_colors);
static void load_gcode_preview(wxGLCanvas* canvas, const GCodePreviewData* preview_data, const std::vector<std::string>& str_tool_colors);
-
- // generates the legend texture in dependence of the current shown view type
- static void generate_legend_texture(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors);
- static unsigned int get_legend_texture_width();
- static unsigned int get_legend_texture_height();
+ static void load_preview(wxGLCanvas* canvas, const std::vector<std::string>& str_tool_colors);
static void reset_legend_texture();
- static unsigned int finalize_legend_texture();
-
- static unsigned int get_warning_texture_width();
- static unsigned int get_warning_texture_height();
-
- // generates a warning texture containing the given message
- static void generate_warning_texture(const std::string& msg);
- static void reset_warning_texture();
- static unsigned int finalize_warning_texture();
static void thick_lines_to_verts(const Lines& lines, const std::vector<double>& widths, const std::vector<double>& heights, bool closed, double top_z, GLVolume& volume);
static void thick_lines_to_verts(const Lines3& lines, const std::vector<double>& widths, const std::vector<double>& heights, bool closed, GLVolume& volume);
diff --git a/xs/src/slic3r/GUI/AppConfig.cpp b/xs/src/slic3r/GUI/AppConfig.cpp
index 2a33cd733..c2ae0bf0b 100644
--- a/xs/src/slic3r/GUI/AppConfig.cpp
+++ b/xs/src/slic3r/GUI/AppConfig.cpp
@@ -16,6 +16,8 @@
#include <boost/property_tree/ini_parser.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/algorithm/string/predicate.hpp>
+#include <boost/format.hpp>
+
namespace Slic3r {
@@ -60,6 +62,14 @@ void AppConfig::set_defaults()
if (get("remember_output_path").empty())
set("remember_output_path", "1");
+
+ // Remove legacy window positions/sizes
+ erase("", "main_frame_maximized");
+ erase("", "main_frame_pos");
+ erase("", "main_frame_size");
+ erase("", "object_settings_maximized");
+ erase("", "object_settings_pos");
+ erase("", "object_settings_size");
}
void AppConfig::load()
@@ -117,8 +127,14 @@ void AppConfig::load()
void AppConfig::save()
{
+ // The config is first written to a file with a PID suffix and then moved
+ // to avoid race conditions with multiple instances of Slic3r
+
+ const auto path = config_path();
+ std::string path_pid = (boost::format("%1%.%2%") % path % get_current_pid()).str();
+
boost::nowide::ofstream c;
- c.open(AppConfig::config_path(), std::ios::out | std::ios::trunc);
+ c.open(path_pid, std::ios::out | std::ios::trunc);
c << "# " << Slic3r::header_slic3r_generated() << std::endl;
// Make sure the "no" category is written first.
for (const std::pair<std::string, std::string> &kvp : m_storage[""])
@@ -147,6 +163,9 @@ void AppConfig::save()
}
}
c.close();
+
+ rename_file(path_pid, path);
+
m_dirty = false;
}
diff --git a/xs/src/slic3r/GUI/AppConfig.hpp b/xs/src/slic3r/GUI/AppConfig.hpp
index b742176ed..5af635a12 100644
--- a/xs/src/slic3r/GUI/AppConfig.hpp
+++ b/xs/src/slic3r/GUI/AppConfig.hpp
@@ -72,6 +72,14 @@ public:
bool has(const std::string &key) const
{ return this->has("", key); }
+ void erase(const std::string &section, const std::string &key)
+ {
+ auto it = m_storage.find(section);
+ if (it != m_storage.end()) {
+ it->second.erase(key);
+ }
+ }
+
void clear_section(const std::string &section)
{ m_storage[section].clear(); }
diff --git a/xs/src/slic3r/GUI/BedShapeDialog.cpp b/xs/src/slic3r/GUI/BedShapeDialog.cpp
index 3dd60ef88..d52535589 100644
--- a/xs/src/slic3r/GUI/BedShapeDialog.cpp
+++ b/xs/src/slic3r/GUI/BedShapeDialog.cpp
@@ -9,6 +9,8 @@
#include "Model.hpp"
#include "boost/nowide/iostream.hpp"
+#include <algorithm>
+
namespace Slic3r {
namespace GUI {
@@ -146,21 +148,18 @@ void BedShapePanel::set_shape(ConfigOptionPoints* points)
if (lines[0].parallel_to(lines[2]) && lines[1].parallel_to(lines[3])) {
// okay, it's a rectangle
// find origin
- // the || 0 hack prevents "-0" which might confuse the user
- int x_min, x_max, y_min, y_max;
- x_max = x_min = points->values[0].x;
+ coordf_t x_min, x_max, y_min, y_max;
+ x_max = x_min = points->values[0].x;
y_max = y_min = points->values[0].y;
- for (auto pt : points->values){
- if (x_min > pt.x) x_min = pt.x;
- if (x_max < pt.x) x_max = pt.x;
- if (y_min > pt.y) y_min = pt.y;
- if (y_max < pt.y) y_max = pt.y;
- }
- if (x_min < 0) x_min = 0;
- if (x_max < 0) x_max = 0;
- if (y_min < 0) y_min = 0;
- if (y_max < 0) y_max = 0;
- auto origin = new ConfigOptionPoints{ Pointf(-x_min, -y_min) };
+ for (auto pt : points->values)
+ {
+ x_min = std::min(x_min, pt.x);
+ x_max = std::max(x_max, pt.x);
+ y_min = std::min(y_min, pt.y);
+ y_max = std::max(y_max, pt.y);
+ }
+
+ auto origin = new ConfigOptionPoints{ Pointf(-x_min, -y_min) };
m_shape_options_book->SetSelection(SHAPE_RECTANGULAR);
auto optgroup = m_optgroups[SHAPE_RECTANGULAR];
diff --git a/xs/src/slic3r/GUI/ConfigWizard.cpp b/xs/src/slic3r/GUI/ConfigWizard.cpp
index 2e315a70b..e784d8525 100644
--- a/xs/src/slic3r/GUI/ConfigWizard.cpp
+++ b/xs/src/slic3r/GUI/ConfigWizard.cpp
@@ -65,22 +65,27 @@ PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, cons
auto namefont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
namefont.SetWeight(wxFONTWEIGHT_BOLD);
- for (const auto &model : models) {
- auto *panel = new wxPanel(this);
- auto *col_sizer = new wxBoxSizer(wxVERTICAL);
- panel->SetSizer(col_sizer);
-
- auto *title = new wxStaticText(panel, wxID_ANY, model.name, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
- title->SetFont(namefont);
- col_sizer->Add(title, 0, wxBOTTOM, 3);
+ // wxGrid appends widgets by rows, but we need to construct them in columns.
+ // These vectors are used to hold the elements so that they can be appended in the right order.
+ std::vector<wxStaticText*> titles;
+ std::vector<wxStaticBitmap*> bitmaps;
+ std::vector<wxPanel*> variants_panels;
+ for (const auto &model : models) {
auto bitmap_file = wxString::Format("printers/%s_%s.png", vendor.id, model.id);
wxBitmap bitmap(GUI::from_u8(Slic3r::var(bitmap_file.ToStdString())), wxBITMAP_TYPE_PNG);
- auto *bitmap_widget = new wxStaticBitmap(panel, wxID_ANY, bitmap);
- col_sizer->Add(bitmap_widget, 0, wxBOTTOM, 3);
- col_sizer->AddSpacer(20);
+ auto *title = new wxStaticText(this, wxID_ANY, model.name, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
+ title->SetFont(namefont);
+ title->Wrap(std::max((int)MODEL_MIN_WRAP, bitmap.GetWidth()));
+ titles.push_back(title);
+
+ auto *bitmap_widget = new wxStaticBitmap(this, wxID_ANY, bitmap);
+ bitmaps.push_back(bitmap_widget);
+ auto *variants_panel = new wxPanel(this);
+ auto *variants_sizer = new wxBoxSizer(wxVERTICAL);
+ variants_panel->SetSizer(variants_sizer);
const auto model_id = model.id;
bool default_variant = true; // Mark the first variant as default in the GUI
@@ -88,22 +93,26 @@ PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, cons
const auto label = wxString::Format("%s %s %s %s", variant.name, _(L("mm")), _(L("nozzle")),
(default_variant ? _(L("(default)")) : wxString()));
default_variant = false;
- auto *cbox = new Checkbox(panel, label, model_id, variant.name);
+ auto *cbox = new Checkbox(variants_panel, label, model_id, variant.name);
const size_t idx = cboxes.size();
cboxes.push_back(cbox);
bool enabled = appconfig_vendors.get_variant("PrusaResearch", model_id, variant.name);
variants_checked += enabled;
cbox->SetValue(enabled);
- col_sizer->Add(cbox, 0, wxBOTTOM, 3);
+ variants_sizer->Add(cbox, 0, wxBOTTOM, 3);
cbox->Bind(wxEVT_CHECKBOX, [this, idx](wxCommandEvent &event) {
if (idx >= this->cboxes.size()) { return; }
this->on_checkbox(this->cboxes[idx], event.IsChecked());
});
}
- printer_grid->Add(panel);
+ variants_panels.push_back(variants_panel);
}
+ for (auto title : titles) { printer_grid->Add(title, 0, wxBOTTOM, 3); }
+ for (auto bitmap : bitmaps) { printer_grid->Add(bitmap, 0, wxBOTTOM, 20); }
+ for (auto vp : variants_panels) { printer_grid->Add(vp); }
+
auto *all_none_sizer = new wxBoxSizer(wxHORIZONTAL);
auto *sel_all = new wxButton(this, wxID_ANY, _(L("Select all")));
auto *sel_none = new wxButton(this, wxID_ANY, _(L("Select none")));
@@ -214,7 +223,7 @@ void ConfigWizardPage::enable_next(bool enable) { parent->p->enable_next(enable)
// Wizard pages
-PageWelcome::PageWelcome(ConfigWizard *parent) :
+PageWelcome::PageWelcome(ConfigWizard *parent, bool check_first_variant) :
ConfigWizardPage(parent, wxString::Format(_(L("Welcome to the Slic3r %s")), ConfigWizard::name()), _(L("Welcome"))),
printer_picker(nullptr),
others_buttons(new wxPanel(parent)),
@@ -238,7 +247,10 @@ PageWelcome::PageWelcome(ConfigWizard *parent) :
AppConfig &appconfig_vendors = this->wizard_p()->appconfig_vendors;
printer_picker = new PrinterPicker(this, vendor_prusa->second, appconfig_vendors);
- printer_picker->select_one(0, true); // Select the default (first) model/variant on the Prusa vendor
+ if (check_first_variant) {
+ // Select the default (first) model/variant on the Prusa vendor
+ printer_picker->select_one(0, true);
+ }
printer_picker->Bind(EVT_PRINTER_PICK, [this, &appconfig_vendors](const PrinterPickerEvent &evt) {
appconfig_vendors.set_variant(evt.vendor_id, evt.model_id, evt.variant_name, evt.enable);
this->on_variant_checked();
@@ -397,11 +409,10 @@ PageFirmware::PageFirmware(ConfigWizard *parent) :
void PageFirmware::apply_custom_config(DynamicPrintConfig &config)
{
- ConfigOptionEnum<GCodeFlavor> opt;
-
auto sel = gcode_picker->GetSelection();
- if (sel != wxNOT_FOUND && opt.deserialize(gcode_picker->GetString(sel).ToStdString())) {
- config.set_key_value("gcode_flavor", &opt);
+ if (sel >= 0 && sel < gcode_opt.enum_labels.size()) {
+ auto *opt = new ConfigOptionEnum<GCodeFlavor>(static_cast<GCodeFlavor>(sel));
+ config.set_key_value("gcode_flavor", opt);
}
}
@@ -779,7 +790,6 @@ void ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
app_config->set("version_check", page_update->version_check ? "1" : "0");
app_config->set("preset_update", page_update->preset_update ? "1" : "0");
app_config->reset_selections();
- // ^ TODO: replace with appropriate printer selection
preset_bundle->load_presets(*app_config);
} else {
for (ConfigWizardPage *page = page_firmware; page != nullptr; page = page->page_next()) {
@@ -831,7 +841,7 @@ ConfigWizard::ConfigWizard(wxWindow *parent, RunReason reason) :
p->btnsizer->Add(p->btn_finish, 0, wxLEFT, BTN_SPACING);
p->btnsizer->Add(p->btn_cancel, 0, wxLEFT, BTN_SPACING);
- p->add_page(p->page_welcome = new PageWelcome(this));
+ p->add_page(p->page_welcome = new PageWelcome(this, reason == RR_DATA_EMPTY || reason == RR_DATA_LEGACY));
p->add_page(p->page_update = new PageUpdate(this));
p->add_page(p->page_vendors = new PageVendors(this));
p->add_page(p->page_firmware = new PageFirmware(this));
@@ -860,10 +870,11 @@ ConfigWizard::ConfigWizard(wxWindow *parent, RunReason reason) :
// If the screen is smaller, resize wizrad to match, which will enable scrollbars.
auto wizard_size = GetSize();
unsigned width, height;
- GUI::get_current_screen_size(width, height);
- wizard_size.SetWidth(std::min(wizard_size.GetWidth(), (int)(width - 2 * DIALOG_MARGIN)));
- wizard_size.SetHeight(std::min(wizard_size.GetHeight(), (int)(height - 2 * DIALOG_MARGIN)));
- SetMinSize(wizard_size);
+ if (GUI::get_current_screen_size(this, width, height)) {
+ wizard_size.SetWidth(std::min(wizard_size.GetWidth(), (int)(width - 2 * DIALOG_MARGIN)));
+ wizard_size.SetHeight(std::min(wizard_size.GetHeight(), (int)(height - 2 * DIALOG_MARGIN)));
+ SetMinSize(wizard_size);
+ }
Fit();
p->btn_prev->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &evt) { this->p->go_prev(); });
diff --git a/xs/src/slic3r/GUI/ConfigWizard_private.hpp b/xs/src/slic3r/GUI/ConfigWizard_private.hpp
index 04319a1b4..2c8f23cd3 100644
--- a/xs/src/slic3r/GUI/ConfigWizard_private.hpp
+++ b/xs/src/slic3r/GUI/ConfigWizard_private.hpp
@@ -27,6 +27,7 @@ namespace GUI {
enum {
WRAP_WIDTH = 500,
+ MODEL_MIN_WRAP = 150,
DIALOG_MARGIN = 15,
INDEX_MARGIN = 40,
@@ -103,7 +104,7 @@ struct PageWelcome: ConfigWizardPage
wxPanel *others_buttons;
wxCheckBox *cbox_reset;
- PageWelcome(ConfigWizard *parent);
+ PageWelcome(ConfigWizard *parent, bool check_first_variant);
virtual wxPanel* extra_buttons() { return others_buttons; }
virtual void on_page_set();
diff --git a/xs/src/slic3r/GUI/Field.cpp b/xs/src/slic3r/GUI/Field.cpp
index 85247b41b..c7f1d48ff 100644
--- a/xs/src/slic3r/GUI/Field.cpp
+++ b/xs/src/slic3r/GUI/Field.cpp
@@ -45,6 +45,22 @@ namespace Slic3r { namespace GUI {
set_undo_bitmap(&bmp);
set_undo_to_sys_bitmap(&bmp);
+ switch (m_opt.type)
+ {
+ case coPercents:
+ case coFloats:
+ case coStrings:
+ case coBools:
+ case coInts: {
+ auto tag_pos = m_opt_id.find("#");
+ if (tag_pos != std::string::npos)
+ m_opt_idx = stoi(m_opt_id.substr(tag_pos + 1, m_opt_id.size()));
+ break;
+ }
+ default:
+ break;
+ }
+
BUILD();
}
@@ -77,11 +93,12 @@ namespace Slic3r { namespace GUI {
wxString Field::get_tooltip_text(const wxString& default_string)
{
wxString tooltip_text("");
- wxString tooltip = L_str(m_opt.tooltip);
+ wxString tooltip = _(m_opt.tooltip);
if (tooltip.length() > 0)
- tooltip_text = tooltip + "(" + _(L("default")) + ": " +
- (boost::iends_with(m_opt_id, "_gcode") ? "\n" : "") +
- default_string + ")";
+ tooltip_text = tooltip + "\n" + _(L("default value")) + "\t: " +
+ (boost::iends_with(m_opt_id, "_gcode") ? "\n" : "") + default_string +
+ (boost::iends_with(m_opt_id, "_gcode") ? "" : "\n") +
+ _(L("parameter name")) + "\t: " + m_opt_id;
return tooltip_text;
}
@@ -161,10 +178,10 @@ namespace Slic3r { namespace GUI {
case coFloat:
{
double val = m_opt.type == coFloats ?
- static_cast<const ConfigOptionFloats*>(m_opt.default_value)->get_at(0) :
+ static_cast<const ConfigOptionFloats*>(m_opt.default_value)->get_at(m_opt_idx) :
m_opt.type == coFloat ?
m_opt.default_value->getFloat() :
- static_cast<const ConfigOptionPercents*>(m_opt.default_value)->get_at(0);
+ static_cast<const ConfigOptionPercents*>(m_opt.default_value)->get_at(m_opt_idx);
text_value = double_to_string(val);
break;
}
@@ -174,10 +191,8 @@ namespace Slic3r { namespace GUI {
case coStrings:
{
const ConfigOptionStrings *vec = static_cast<const ConfigOptionStrings*>(m_opt.default_value);
- if (vec == nullptr || vec->empty()) break;
- if (vec->size() > 1)
- break;
- text_value = vec->values.at(0);
+ if (vec == nullptr || vec->empty()) break; //for the case of empty default value
+ text_value = vec->get_at(m_opt_idx);
break;
}
default:
@@ -209,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
@@ -252,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);
@@ -259,7 +279,7 @@ void CheckBox::BUILD() {
bool check_value = m_opt.type == coBool ?
m_opt.default_value->getBool() : m_opt.type == coBools ?
- static_cast<ConfigOptionBools*>(m_opt.default_value)->values.at(0) :
+ static_cast<ConfigOptionBools*>(m_opt.default_value)->get_at(m_opt_idx) :
false;
auto temp = new wxCheckBox(m_parent, wxID_ANY, wxString(""), wxDefaultPosition, size);
@@ -365,7 +385,7 @@ void Choice::BUILD() {
}
else{
for (auto el : m_opt.enum_labels.empty() ? m_opt.enum_values : m_opt.enum_labels){
- const wxString& str = m_opt_id == "support" ? L_str(el) : el;
+ const wxString& str = _(el);//m_opt_id == "support" ? _(el) : el;
temp->Append(str);
}
set_selection();
@@ -418,7 +438,7 @@ void Choice::set_selection()
break;
}
case coStrings:{
- text_value = static_cast<const ConfigOptionStrings*>(m_opt.default_value)->values.at(0);
+ text_value = static_cast<const ConfigOptionStrings*>(m_opt.default_value)->get_at(m_opt_idx);
size_t idx = 0;
for (auto el : m_opt.enum_values)
@@ -571,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;
@@ -582,7 +604,7 @@ void ColourPicker::BUILD()
if (m_opt.height >= 0) size.SetHeight(m_opt.height);
if (m_opt.width >= 0) size.SetWidth(m_opt.width);
- wxString clr(static_cast<ConfigOptionStrings*>(m_opt.default_value)->values.at(0));
+ wxString clr(static_cast<ConfigOptionStrings*>(m_opt.default_value)->get_at(m_opt_idx));
auto temp = new wxColourPickerCtrl(m_parent, wxID_ANY, clr, wxDefaultPosition, size);
// // recast as a wxWindow to fit the calling convention
@@ -675,6 +697,22 @@ boost::any& PointCtrl::get_value()
return m_value = ret_point;
}
+void StaticText::BUILD()
+{
+ auto size = wxSize(wxDefaultSize);
+ if (m_opt.height >= 0) size.SetHeight(m_opt.height);
+ if (m_opt.width >= 0) size.SetWidth(m_opt.width);
+
+ wxString legend(static_cast<ConfigOptionString*>(m_opt.default_value)->value);
+ auto temp = new wxStaticText(m_parent, wxID_ANY, legend, wxDefaultPosition, size);
+ temp->SetFont(bold_font());
+
+ // // recast as a wxWindow to fit the calling convention
+ window = dynamic_cast<wxWindow*>(temp);
+
+ temp->SetToolTip(get_tooltip_text(legend));
+}
+
} // GUI
} // Slic3r
diff --git a/xs/src/slic3r/GUI/Field.hpp b/xs/src/slic3r/GUI/Field.hpp
index 948178d3e..923f0fd7e 100644
--- a/xs/src/slic3r/GUI/Field.hpp
+++ b/xs/src/slic3r/GUI/Field.hpp
@@ -95,6 +95,7 @@ public:
/// Copy of ConfigOption for deduction purposes
const ConfigOptionDef m_opt {ConfigOptionDef()};
const t_config_option_key m_opt_id;//! {""};
+ int m_opt_idx = 0;
/// Sets a value for this control.
/// subclasses should overload with a specific version
@@ -221,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) {}
@@ -384,6 +386,34 @@ public:
wxSizer* getSizer() override { return sizer; }
};
+class StaticText : public Field {
+ using Field::Field;
+public:
+ StaticText(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id) {}
+ StaticText(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id) {}
+ ~StaticText() {}
+
+ wxWindow* window{ nullptr };
+ void BUILD() override;
+
+ void set_value(const std::string& value, bool change_event = false) {
+ m_disable_change_event = !change_event;
+ dynamic_cast<wxStaticText*>(window)->SetLabel(value);
+ m_disable_change_event = false;
+ }
+ void set_value(const boost::any& value, bool change_event = false) {
+ m_disable_change_event = !change_event;
+ dynamic_cast<wxStaticText*>(window)->SetLabel(boost::any_cast<wxString>(value));
+ m_disable_change_event = false;
+ }
+
+ boost::any& get_value()override { return m_value; }
+
+ void enable() override { dynamic_cast<wxColourPickerCtrl*>(window)->Enable(); };
+ void disable() override{ dynamic_cast<wxColourPickerCtrl*>(window)->Disable(); };
+ wxWindow* getWindow() override { return window; }
+};
+
} // GUI
} // Slic3r
diff --git a/xs/src/slic3r/GUI/FirmwareDialog.cpp b/xs/src/slic3r/GUI/FirmwareDialog.cpp
index d74743055..d5ac64d90 100644
--- a/xs/src/slic3r/GUI/FirmwareDialog.cpp
+++ b/xs/src/slic3r/GUI/FirmwareDialog.cpp
@@ -1,11 +1,24 @@
-#include "FirmwareDialog.hpp"
-
#include <numeric>
#include <algorithm>
+#include <thread>
+#include <condition_variable>
+#include <stdexcept>
#include <boost/format.hpp>
+#include <boost/asio.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/log/trivial.hpp>
+#include <boost/optional.hpp>
+
+#include "libslic3r/Utils.hpp"
+#include "avrdude/avrdude-slic3r.hpp"
+#include "GUI.hpp"
+#include "MsgDialog.hpp"
+#include "../Utils/HexFile.hpp"
+#include "../Utils/Serial.hpp"
+
+// wx includes need to come after asio because of the WinSock.h problem
+#include "FirmwareDialog.hpp"
#include <wx/app.h>
#include <wx/event.h>
@@ -21,17 +34,30 @@
#include <wx/gauge.h>
#include <wx/collpane.h>
#include <wx/msgdlg.h>
+#include <wx/filefn.h>
-#include "libslic3r/Utils.hpp"
-#include "avrdude/avrdude-slic3r.hpp"
-#include "GUI.hpp"
-#include "../Utils/Serial.hpp"
namespace fs = boost::filesystem;
+namespace asio = boost::asio;
+using boost::system::error_code;
+using boost::optional;
namespace Slic3r {
+using Utils::HexFile;
+using Utils::SerialPortInfo;
+using Utils::Serial;
+
+
+// USB IDs used to perform device lookup
+enum {
+ USB_VID_PRUSA = 0x2c99,
+ USB_PID_MK2 = 1,
+ USB_PID_MK3 = 2,
+ USB_PID_MMU_BOOT = 3,
+ USB_PID_MMU_APP = 4,
+};
// This enum discriminates the kind of information in EVT_AVRDUDE,
// it's stored in the ExtraLong field of wxCommandEvent.
@@ -39,12 +65,16 @@ enum AvrdudeEvent
{
AE_MESSAGE,
AE_PROGRESS,
+ AE_STATUS,
AE_EXIT,
};
wxDECLARE_EVENT(EVT_AVRDUDE, wxCommandEvent);
wxDEFINE_EVENT(EVT_AVRDUDE, wxCommandEvent);
+wxDECLARE_EVENT(EVT_ASYNC_DIALOG, wxCommandEvent);
+wxDEFINE_EVENT(EVT_ASYNC_DIALOG, wxCommandEvent);
+
// Private
@@ -55,13 +85,14 @@ struct FirmwareDialog::priv
AC_NONE,
AC_SUCCESS,
AC_FAILURE,
- AC_CANCEL,
+ AC_USER_CANCELLED,
};
FirmwareDialog *q; // PIMPL back pointer ("Q-Pointer")
+ // GUI elements
wxComboBox *port_picker;
- std::vector<Utils::SerialPortInfo> ports;
+ wxStaticText *port_autodetect;
wxFilePickerCtrl *hex_picker;
wxStaticText *txt_status;
wxGauge *progressbar;
@@ -72,33 +103,67 @@ struct FirmwareDialog::priv
wxButton *btn_flash;
wxString btn_flash_label_ready;
wxString btn_flash_label_flashing;
+ wxString label_status_flashing;
wxTimer timer_pulse;
+ // Async modal dialog during flashing
+ std::mutex mutex;
+ int modal_response;
+ std::condition_variable response_cv;
+
+ // Data
+ std::vector<SerialPortInfo> ports;
+ optional<SerialPortInfo> port;
+ HexFile hex_file;
+
// This is a shared pointer holding the background AvrDude task
// also serves as a status indication (it is set _iff_ the background task is running, otherwise it is reset).
AvrDude::Ptr avrdude;
std::string avrdude_config;
unsigned progress_tasks_done;
- bool cancelled;
+ unsigned progress_tasks_bar;
+ bool user_cancelled;
+ const bool extra_verbose; // For debugging
priv(FirmwareDialog *q) :
q(q),
btn_flash_label_ready(_(L("Flash!"))),
btn_flash_label_flashing(_(L("Cancel"))),
+ label_status_flashing(_(L("Flashing in progress. Please do not disconnect the printer!"))),
timer_pulse(q),
avrdude_config((fs::path(::Slic3r::resources_dir()) / "avrdude" / "avrdude.conf").string()),
progress_tasks_done(0),
- cancelled(false)
+ progress_tasks_bar(0),
+ user_cancelled(false),
+ extra_verbose(false)
{}
void find_serial_ports();
- void flashing_start(bool flashing_l10n);
+ void fit_no_shrink();
+ void set_txt_status(const wxString &label);
+ void flashing_start(unsigned tasks);
void flashing_done(AvrDudeComplete complete);
- size_t hex_lang_offset(const wxString &path);
+ void enable_port_picker(bool enable);
+ void load_hex_file(const wxString &path);
+ void queue_status(wxString message);
+ void queue_error(const wxString &message);
+
+ bool ask_model_id_mismatch(const std::string &printer_model);
+ bool check_model_id();
+ void wait_for_mmu_bootloader(unsigned retries);
+ void mmu_reboot(const SerialPortInfo &port);
+ void lookup_port_mmu();
+ void prepare_common();
+ void prepare_mk2();
+ void prepare_mk3();
+ void prepare_mm_control();
void perform_upload();
- void cancel();
+
+ void user_cancel();
void on_avrdude(const wxCommandEvent &evt);
+ void on_async_dialog(const wxCommandEvent &evt);
+ void ensure_joined();
};
void FirmwareDialog::priv::find_serial_ports()
@@ -108,7 +173,7 @@ void FirmwareDialog::priv::find_serial_ports()
this->ports = new_ports;
port_picker->Clear();
for (const auto &port : this->ports)
- port_picker->Append(port.friendly_name);
+ port_picker->Append(wxString::FromUTF8(port.friendly_name.data()));
if (ports.size() > 0) {
int idx = port_picker->GetValue().IsEmpty() ? 0 : -1;
for (int i = 0; i < (int)this->ports.size(); ++ i)
@@ -122,20 +187,43 @@ void FirmwareDialog::priv::find_serial_ports()
}
}
-void FirmwareDialog::priv::flashing_start(bool flashing_l10n)
+void FirmwareDialog::priv::fit_no_shrink()
+{
+ // Ensure content fits into window and window is not shrinked
+ const auto old_size = q->GetSize();
+ q->Layout();
+ q->Fit();
+ const auto new_size = q->GetSize();
+ const auto new_width = std::max(old_size.GetWidth(), new_size.GetWidth());
+ const auto new_height = std::max(old_size.GetHeight(), new_size.GetHeight());
+ q->SetSize(new_width, new_height);
+}
+
+void FirmwareDialog::priv::set_txt_status(const wxString &label)
+{
+ const auto width = txt_status->GetSize().GetWidth();
+ txt_status->SetLabel(label);
+ txt_status->Wrap(width);
+
+ fit_no_shrink();
+}
+
+void FirmwareDialog::priv::flashing_start(unsigned tasks)
{
+ modal_response = wxID_NONE;
txt_stdout->Clear();
- txt_status->SetLabel(_(L("Flashing in progress. Please do not disconnect the printer!")));
+ set_txt_status(label_status_flashing);
txt_status->SetForegroundColour(GUI::get_label_clr_modified());
port_picker->Disable();
btn_rescan->Disable();
hex_picker->Disable();
btn_close->Disable();
btn_flash->SetLabel(btn_flash_label_flashing);
- progressbar->SetRange(flashing_l10n ? 500 : 200); // See progress callback below
+ progressbar->SetRange(200 * tasks); // See progress callback below
progressbar->SetValue(0);
progress_tasks_done = 0;
- cancelled = false;
+ progress_tasks_bar = 0;
+ user_cancelled = false;
timer_pulse.Start(50);
}
@@ -152,81 +240,284 @@ void FirmwareDialog::priv::flashing_done(AvrDudeComplete complete)
progressbar->SetValue(progressbar->GetRange());
switch (complete) {
- case AC_SUCCESS: txt_status->SetLabel(_(L("Flashing succeeded!"))); break;
- case AC_FAILURE: txt_status->SetLabel(_(L("Flashing failed. Please see the avrdude log below."))); break;
- case AC_CANCEL: txt_status->SetLabel(_(L("Flashing cancelled."))); break;
+ case AC_SUCCESS: set_txt_status(_(L("Flashing succeeded!"))); break;
+ case AC_FAILURE: set_txt_status(_(L("Flashing failed. Please see the avrdude log below."))); break;
+ case AC_USER_CANCELLED: set_txt_status(_(L("Flashing cancelled."))); break;
+ default: break;
}
}
-size_t FirmwareDialog::priv::hex_lang_offset(const wxString &path)
+void FirmwareDialog::priv::enable_port_picker(bool enable)
+{
+ port_picker->Show(enable);
+ btn_rescan->Show(enable);
+ port_autodetect->Show(! enable);
+ q->Layout();
+ fit_no_shrink();
+}
+
+void FirmwareDialog::priv::load_hex_file(const wxString &path)
+{
+ hex_file = HexFile(path.wx_str());
+ enable_port_picker(hex_file.device != HexFile::DEV_MM_CONTROL);
+}
+
+void FirmwareDialog::priv::queue_status(wxString message)
{
- fs::ifstream file(fs::path(path.wx_str()));
- if (! file.good()) {
- return 0;
+ auto evt = new wxCommandEvent(EVT_AVRDUDE, this->q->GetId());
+ evt->SetExtraLong(AE_STATUS);
+ evt->SetString(std::move(message));
+ wxQueueEvent(this->q, evt);
+}
+
+void FirmwareDialog::priv::queue_error(const wxString &message)
+{
+ auto evt = new wxCommandEvent(EVT_AVRDUDE, this->q->GetId());
+ evt->SetExtraLong(AE_STATUS);
+ evt->SetString(wxString::Format(_(L("Flashing failed: %s")), message));
+
+ wxQueueEvent(this->q, evt); avrdude->cancel();
+}
+
+bool FirmwareDialog::priv::ask_model_id_mismatch(const std::string &printer_model)
+{
+ // model_id in the hex file doesn't match what the printer repoted.
+ // Ask the user if it should be flashed anyway.
+
+ std::unique_lock<std::mutex> lock(mutex);
+
+ auto evt = new wxCommandEvent(EVT_ASYNC_DIALOG, this->q->GetId());
+ evt->SetString(wxString::Format(_(L(
+ "This firmware hex file does not match the printer model.\n"
+ "The hex file is intended for: %s\n"
+ "Printer reported: %s\n\n"
+ "Do you want to continue and flash this hex file anyway?\n"
+ "Please only continue if you are sure this is the right thing to do.")),
+ hex_file.model_id, printer_model
+ ));
+ wxQueueEvent(this->q, evt);
+
+ response_cv.wait(lock, [this]() { return this->modal_response != wxID_NONE; });
+
+ if (modal_response == wxID_YES) {
+ return true;
+ } else {
+ user_cancel();
+ return false;
}
+}
+
+bool FirmwareDialog::priv::check_model_id()
+{
+ // XXX: The implementation in Serial doesn't currently work reliably enough to be used.
+ // Therefore, regretably, so far the check cannot be used and we just return true here.
+ // TODO: Rewrite Serial using more platform-native code.
+ return true;
+
+ // if (hex_file.model_id.empty()) {
+ // // No data to check against, assume it's ok
+ // return true;
+ // }
+
+ // asio::io_service io;
+ // Serial serial(io, port->port, 115200);
+ // serial.printer_setup();
+
+ // enum {
+ // TIMEOUT = 2000,
+ // RETREIES = 5,
+ // };
+
+ // if (! serial.printer_ready_wait(RETREIES, TIMEOUT)) {
+ // queue_error(wxString::Format(_(L("Could not connect to the printer at %s")), port->port));
+ // return false;
+ // }
+
+ // std::string line;
+ // error_code ec;
+ // serial.printer_write_line("PRUSA Rev");
+ // while (serial.read_line(TIMEOUT, line, ec)) {
+ // if (ec) {
+ // queue_error(wxString::Format(_(L("Could not connect to the printer at %s")), port->port));
+ // return false;
+ // }
+
+ // if (line == "ok") { continue; }
+
+ // if (line == hex_file.model_id) {
+ // return true;
+ // } else {
+ // return ask_model_id_mismatch(line);
+ // }
+
+ // line.clear();
+ // }
+
+ // return false;
+}
+
+void FirmwareDialog::priv::wait_for_mmu_bootloader(unsigned retries)
+{
+ enum {
+ SLEEP_MS = 500,
+ };
- static const char *hex_terminator = ":00000001FF\r";
- size_t res = 0;
- std::string line;
- while (getline(file, line, '\n').good()) {
- // Account for LF vs CRLF
- if (!line.empty() && line.back() != '\r') {
- line.push_back('\r');
+ for (unsigned i = 0; i < retries && !user_cancelled; i++) {
+ std::this_thread::sleep_for(std::chrono::milliseconds(SLEEP_MS));
+
+ auto ports = Utils::scan_serial_ports_extended();
+ ports.erase(std::remove_if(ports.begin(), ports.end(), [=](const SerialPortInfo &port ) {
+ return port.id_vendor != USB_VID_PRUSA || port.id_product != USB_PID_MMU_BOOT;
+ }), ports.end());
+
+ if (ports.size() == 1) {
+ port = ports[0];
+ return;
+ } else if (ports.size() > 1) {
+ BOOST_LOG_TRIVIAL(error) << "Several VID/PID 0x2c99/3 devices found";
+ queue_error(_(L("Multiple Original Prusa i3 MMU 2.0 devices found. Please only connect one at a time for flashing.")));
+ return;
}
+ }
+}
- if (line == hex_terminator) {
- if (res == 0) {
- // This is the first terminator seen, save the position
- res = file.tellg();
- } else {
- // We've found another terminator, return the offset just after the first one
- // which is the start of the second 'section'.
- return res;
+void FirmwareDialog::priv::mmu_reboot(const SerialPortInfo &port)
+{
+ asio::io_service io;
+ Serial serial(io, port.port, 1200);
+ std::this_thread::sleep_for(std::chrono::milliseconds(50));
+}
+
+void FirmwareDialog::priv::lookup_port_mmu()
+{
+ static const auto msg_not_found =
+ "The Multi Material Control device was not found.\n"
+ "If the device is connected, please press the Reset button next to the USB connector ...";
+
+ BOOST_LOG_TRIVIAL(info) << "Flashing MMU 2.0, looking for VID/PID 0x2c99/3 or 0x2c99/4 ...";
+
+ auto ports = Utils::scan_serial_ports_extended();
+ ports.erase(std::remove_if(ports.begin(), ports.end(), [=](const SerialPortInfo &port ) {
+ return port.id_vendor != USB_VID_PRUSA ||
+ port.id_product != USB_PID_MMU_BOOT &&
+ port.id_product != USB_PID_MMU_APP;
+ }), ports.end());
+
+ if (ports.size() == 0) {
+ BOOST_LOG_TRIVIAL(info) << "MMU 2.0 device not found, asking the user to press Reset and waiting for the device to show up ...";
+ queue_status(_(L(msg_not_found)));
+ wait_for_mmu_bootloader(30);
+ } else if (ports.size() > 1) {
+ BOOST_LOG_TRIVIAL(error) << "Several VID/PID 0x2c99/3 devices found";
+ queue_error(_(L("Multiple Original Prusa i3 MMU 2.0 devices found. Please only connect one at a time for flashing.")));
+ } else {
+ if (ports[0].id_product == USB_PID_MMU_APP) {
+ // The device needs to be rebooted into the bootloader mode
+ BOOST_LOG_TRIVIAL(info) << boost::format("Found VID/PID 0x2c99/4 at `%1%`, rebooting the device ...") % ports[0].port;
+ mmu_reboot(ports[0]);
+ wait_for_mmu_bootloader(10);
+
+ if (! port) {
+ // The device in bootloader mode was not found, inform the user and wait some more...
+ BOOST_LOG_TRIVIAL(info) << "MMU 2.0 bootloader device not found after reboot, asking the user to press Reset and waiting for the device to show up ...";
+ queue_status(_(L(msg_not_found)));
+ wait_for_mmu_bootloader(30);
}
+ } else {
+ port = ports[0];
}
}
+}
+
+void FirmwareDialog::priv::prepare_common()
+{
+ std::vector<std::string> args {{
+ extra_verbose ? "-vvvvv" : "-v",
+ "-p", "atmega2560",
+ // Using the "Wiring" mode to program Rambo or Einsy, using the STK500v2 protocol (not the STK500).
+ // The Prusa's avrdude is patched to never send semicolons inside the data packets, as the USB to serial chip
+ // is flashed with a buggy firmware.
+ "-c", "wiring",
+ "-P", port->port,
+ "-b", "115200", // TODO: Allow other rates? Ditto elsewhere.
+ "-D",
+ "-U", (boost::format("flash:w:0:%1%:i") % hex_file.path.string()).str(),
+ }};
- return 0;
+ BOOST_LOG_TRIVIAL(info) << "Invoking avrdude, arguments: "
+ << std::accumulate(std::next(args.begin()), args.end(), args[0], [](std::string a, const std::string &b) {
+ return a + ' ' + b;
+ });
+
+ avrdude->push_args(std::move(args));
}
-void FirmwareDialog::priv::perform_upload()
+void FirmwareDialog::priv::prepare_mk2()
{
- auto filename = hex_picker->GetPath();
- std::string port = port_picker->GetValue().ToStdString();
- int selection = port_picker->GetSelection();
- if (selection != -1) {
- // Verify whether the combo box list selection equals to the combo box edit value.
- if (this->ports[selection].friendly_name == port)
- port = this->ports[selection].port;
+ if (! port) { return; }
+
+ if (! check_model_id()) {
+ avrdude->cancel();
+ return;
}
- if (filename.IsEmpty() || port.empty()) { return; }
- const bool extra_verbose = false; // For debugging
- const auto lang_offset = hex_lang_offset(filename);
- const auto filename_utf8 = filename.utf8_str();
+ prepare_common();
+}
- flashing_start(lang_offset > 0);
+void FirmwareDialog::priv::prepare_mk3()
+{
+ if (! port) { return; }
- // It is ok here to use the q-pointer to the FirmwareDialog
- // because the dialog ensures it doesn't exit before the background thread is done.
- auto q = this->q;
+ if (! check_model_id()) {
+ avrdude->cancel();
+ return;
+ }
- // Init the avrdude object
- AvrDude avrdude(avrdude_config);
+ prepare_common();
- // Build argument list(s)
+ // The hex file also contains another section with l10n data to be flashed into the external flash on MK3 (Einsy)
+ // This is done via another avrdude invocation, here we build arg list for that:
std::vector<std::string> args {{
extra_verbose ? "-vvvvv" : "-v",
"-p", "atmega2560",
- // Using the "Wiring" mode to program Rambo or Einsy, using the STK500v2 protocol (not the STK500).
- // The Prusa's avrdude is patched to never send semicolons inside the data packets, as the USB to serial chip
- // is flashed with a buggy firmware.
- "-c", "wiring",
- "-P", port,
- "-b", "115200", // TODO: Allow other rates? Ditto below.
+ // Using the "Arduino" mode to program Einsy's external flash with languages, using the STK500 protocol (not the STK500v2).
+ // The Prusa's avrdude is patched again to never send semicolons inside the data packets.
+ "-c", "arduino",
+ "-P", port->port,
+ "-b", "115200",
+ "-D",
+ "-u", // disable safe mode
+ "-U", (boost::format("flash:w:1:%1%:i") % hex_file.path.string()).str(),
+ }};
+
+ BOOST_LOG_TRIVIAL(info) << "Invoking avrdude for external flash flashing, arguments: "
+ << std::accumulate(std::next(args.begin()), args.end(), args[0], [](std::string a, const std::string &b) {
+ return a + ' ' + b;
+ });
+
+ avrdude->push_args(std::move(args));
+}
+
+void FirmwareDialog::priv::prepare_mm_control()
+{
+ port = boost::none;
+ lookup_port_mmu();
+ if (! port) {
+ queue_error(_(L("The device could not have been found")));
+ return;
+ }
+
+ BOOST_LOG_TRIVIAL(info) << boost::format("Found VID/PID 0x2c99/3 at `%1%`, flashing ...") % port->port;
+ queue_status(label_status_flashing);
+
+ std::vector<std::string> args {{
+ extra_verbose ? "-vvvvv" : "-v",
+ "-p", "atmega32u4",
+ "-c", "avr109",
+ "-P", port->port,
+ "-b", "57600",
"-D",
- // XXX: Safe mode?
- "-U", (boost::format("flash:w:0:%1%:i") % filename_utf8.data()).str(),
+ "-U", (boost::format("flash:w:0:%1%:i") % hex_file.path.string()).str(),
}};
BOOST_LOG_TRIVIAL(info) << "Invoking avrdude, arguments: "
@@ -234,33 +525,60 @@ void FirmwareDialog::priv::perform_upload()
return a + ' ' + b;
});
- avrdude.push_args(std::move(args));
-
- if (lang_offset > 0) {
- // The hex file also contains another section with l10n data to be flashed into the external flash on MK3 (Einsy)
- // This is done via another avrdude invocation, here we build arg list for that:
- std::vector<std::string> args_l10n {{
- extra_verbose ? "-vvvvv" : "-v",
- "-p", "atmega2560",
- // Using the "Arduino" mode to program Einsy's external flash with languages, using the STK500 protocol (not the STK500v2).
- // The Prusa's avrdude is patched again to never send semicolons inside the data packets.
- "-c", "arduino",
- "-P", port,
- "-b", "115200",
- "-D",
- "-u", // disable safe mode
- "-U", (boost::format("flash:w:%1%:%2%:i") % lang_offset % filename_utf8.data()).str(),
- }};
-
- BOOST_LOG_TRIVIAL(info) << "Invoking avrdude for external flash flashing, arguments: "
- << std::accumulate(std::next(args_l10n.begin()), args_l10n.end(), args_l10n[0], [](std::string a, const std::string &b) {
- return a + ' ' + b;
- });
-
- avrdude.push_args(std::move(args_l10n));
+ avrdude->push_args(std::move(args));
+}
+
+
+void FirmwareDialog::priv::perform_upload()
+{
+ auto filename = hex_picker->GetPath();
+ if (filename.IsEmpty()) { return; }
+
+ load_hex_file(filename); // Might already be loaded, but we want to make sure it's fresh
+
+ int selection = port_picker->GetSelection();
+ if (selection != wxNOT_FOUND) {
+ port = this->ports[selection];
+
+ // Verify whether the combo box list selection equals to the combo box edit value.
+ if (wxString::FromUTF8(port->friendly_name.data()) != port_picker->GetValue()) {
+ return;
+ }
}
-
- this->avrdude = avrdude
+
+ const bool extra_verbose = false; // For debugging
+
+ flashing_start(hex_file.device == HexFile::DEV_MK3 ? 2 : 1);
+
+ // Init the avrdude object
+ AvrDude avrdude(avrdude_config);
+
+ // It is ok here to use the q-pointer to the FirmwareDialog
+ // because the dialog ensures it doesn't exit before the background thread is done.
+ auto q = this->q;
+
+ avrdude
+ .on_run([this](AvrDude::Ptr avrdude) {
+ this->avrdude = std::move(avrdude);
+
+ try {
+ switch (this->hex_file.device) {
+ case HexFile::DEV_MK3:
+ this->prepare_mk3();
+ break;
+
+ case HexFile::DEV_MM_CONTROL:
+ this->prepare_mm_control();
+ break;
+
+ default:
+ this->prepare_mk2();
+ break;
+ }
+ } catch (const std::exception &ex) {
+ queue_error(wxString::Format(_(L("Error accessing port at %s: %s")), port->port, ex.what()));
+ }
+ })
.on_message(std::move([q, extra_verbose](const char *msg, unsigned /* size */) {
if (extra_verbose) {
BOOST_LOG_TRIVIAL(debug) << "avrdude: " << msg;
@@ -278,20 +596,19 @@ void FirmwareDialog::priv::perform_upload()
evt->SetInt(progress);
wxQueueEvent(q, evt);
}))
- .on_complete(std::move([q](int status, size_t /* args_id */) {
- auto evt = new wxCommandEvent(EVT_AVRDUDE, q->GetId());
+ .on_complete(std::move([this]() {
+ auto evt = new wxCommandEvent(EVT_AVRDUDE, this->q->GetId());
evt->SetExtraLong(AE_EXIT);
- evt->SetInt(status);
- wxQueueEvent(q, evt);
+ evt->SetInt(this->avrdude->exit_code());
+ wxQueueEvent(this->q, evt);
}))
.run();
}
-void FirmwareDialog::priv::cancel()
+void FirmwareDialog::priv::user_cancel()
{
if (avrdude) {
- cancelled = true;
- txt_status->SetLabel(_(L("Cancelling...")));
+ user_cancelled = true;
avrdude->cancel();
}
}
@@ -313,12 +630,15 @@ void FirmwareDialog::priv::on_avrdude(const wxCommandEvent &evt)
// and then display overall progress during the latter tasks.
if (progress_tasks_done > 0) {
- progressbar->SetValue(progress_tasks_done - 100 + evt.GetInt());
+ progressbar->SetValue(progress_tasks_bar + evt.GetInt());
}
if (evt.GetInt() == 100) {
timer_pulse.Stop();
- progress_tasks_done += 100;
+ if (progress_tasks_done % 3 != 0) {
+ progress_tasks_bar += 100;
+ }
+ progress_tasks_done++;
}
break;
@@ -326,13 +646,17 @@ void FirmwareDialog::priv::on_avrdude(const wxCommandEvent &evt)
case AE_EXIT:
BOOST_LOG_TRIVIAL(info) << "avrdude exit code: " << evt.GetInt();
- complete_kind = cancelled ? AC_CANCEL : (evt.GetInt() == 0 ? AC_SUCCESS : AC_FAILURE);
- flashing_done(complete_kind);
+ // Figure out the exit state
+ if (user_cancelled) { complete_kind = AC_USER_CANCELLED; }
+ else if (avrdude->cancelled()) { complete_kind = AC_NONE; } // Ie. cancelled programatically
+ else { complete_kind = evt.GetInt() == 0 ? AC_SUCCESS : AC_FAILURE; }
- // Make sure the background thread is collected and the AvrDude object reset
- if (avrdude) { avrdude->join(); }
- avrdude.reset();
+ flashing_done(complete_kind);
+ ensure_joined();
+ break;
+ case AE_STATUS:
+ set_txt_status(evt.GetString());
break;
default:
@@ -340,6 +664,23 @@ void FirmwareDialog::priv::on_avrdude(const wxCommandEvent &evt)
}
}
+void FirmwareDialog::priv::on_async_dialog(const wxCommandEvent &evt)
+{
+ wxMessageDialog dlg(this->q, evt.GetString(), wxMessageBoxCaptionStr, wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION);
+ {
+ std::lock_guard<std::mutex> lock(mutex);
+ modal_response = dlg.ShowModal();
+ }
+ response_cv.notify_all();
+}
+
+void FirmwareDialog::priv::ensure_joined()
+{
+ // Make sure the background thread is collected and the AvrDude object reset
+ if (avrdude) { avrdude->join(); }
+ avrdude.reset();
+}
+
// Public
@@ -360,44 +701,51 @@ FirmwareDialog::FirmwareDialog(wxWindow *parent) :
wxFont mono_font(wxFontInfo().Family(wxFONTFAMILY_TELETYPE));
mono_font.MakeSmaller();
+ // Create GUI components and layout
+
auto *panel = new wxPanel(this);
wxBoxSizer *vsizer = new wxBoxSizer(wxVERTICAL);
panel->SetSizer(vsizer);
+ auto *label_hex_picker = new wxStaticText(panel, wxID_ANY, _(L("Firmware image:")));
+ p->hex_picker = new wxFilePickerCtrl(panel, wxID_ANY, wxEmptyString, wxFileSelectorPromptStr,
+ "Hex files (*.hex)|*.hex|All files|*.*");
+
auto *label_port_picker = new wxStaticText(panel, wxID_ANY, _(L("Serial port:")));
p->port_picker = new wxComboBox(panel, wxID_ANY);
+ p->port_autodetect = new wxStaticText(panel, wxID_ANY, _(L("Autodetected")));
p->btn_rescan = new wxButton(panel, wxID_ANY, _(L("Rescan")));
auto *port_sizer = new wxBoxSizer(wxHORIZONTAL);
port_sizer->Add(p->port_picker, 1, wxEXPAND | wxRIGHT, SPACING);
port_sizer->Add(p->btn_rescan, 0);
+ port_sizer->Add(p->port_autodetect, 1, wxEXPAND);
+ p->enable_port_picker(true);
- auto *label_hex_picker = new wxStaticText(panel, wxID_ANY, _(L("Firmware image:")));
- p->hex_picker = new wxFilePickerCtrl(panel, wxID_ANY);
+ auto *label_progress = new wxStaticText(panel, wxID_ANY, _(L("Progress:")));
+ p->progressbar = new wxGauge(panel, wxID_ANY, 1, wxDefaultPosition, wxDefaultSize, wxGA_HORIZONTAL | wxGA_SMOOTH);
auto *label_status = new wxStaticText(panel, wxID_ANY, _(L("Status:")));
p->txt_status = new wxStaticText(panel, wxID_ANY, _(L("Ready")));
p->txt_status->SetFont(status_font);
- auto *label_progress = new wxStaticText(panel, wxID_ANY, _(L("Progress:")));
- p->progressbar = new wxGauge(panel, wxID_ANY, 1, wxDefaultPosition, wxDefaultSize, wxGA_HORIZONTAL | wxGA_SMOOTH);
-
auto *grid = new wxFlexGridSizer(2, SPACING, SPACING);
grid->AddGrowableCol(1);
- grid->Add(label_port_picker, 0, wxALIGN_CENTER_VERTICAL);
- grid->Add(port_sizer, 0, wxEXPAND);
grid->Add(label_hex_picker, 0, wxALIGN_CENTER_VERTICAL);
grid->Add(p->hex_picker, 0, wxEXPAND);
- grid->Add(label_status, 0, wxALIGN_CENTER_VERTICAL);
- grid->Add(p->txt_status, 0, wxEXPAND);
+ grid->Add(label_port_picker, 0, wxALIGN_CENTER_VERTICAL);
+ grid->Add(port_sizer, 0, wxEXPAND);
grid->Add(label_progress, 0, wxALIGN_CENTER_VERTICAL);
grid->Add(p->progressbar, 1, wxEXPAND | wxALIGN_CENTER_VERTICAL);
+ grid->Add(label_status, 0, wxALIGN_CENTER_VERTICAL);
+ grid->Add(p->txt_status, 0, wxEXPAND);
+
vsizer->Add(grid, 0, wxEXPAND | wxTOP | wxBOTTOM, SPACING);
- p->spoiler = new wxCollapsiblePane(panel, wxID_ANY, _(L("Advanced: avrdude output log")));
+ p->spoiler = new wxCollapsiblePane(panel, wxID_ANY, _(L("Advanced: avrdude output log")), wxDefaultPosition, wxDefaultSize, wxCP_DEFAULT_STYLE | wxCP_NO_TLW_RESIZE);
auto *spoiler_pane = p->spoiler->GetPane();
auto *spoiler_sizer = new wxBoxSizer(wxVERTICAL);
p->txt_stdout = new wxTextCtrl(spoiler_pane, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE | wxTE_READONLY);
@@ -410,6 +758,7 @@ FirmwareDialog::FirmwareDialog(wxWindow *parent) :
p->btn_close = new wxButton(panel, wxID_CLOSE);
p->btn_flash = new wxButton(panel, wxID_ANY, p->btn_flash_label_ready);
+ p->btn_flash->Disable();
auto *bsizer = new wxBoxSizer(wxHORIZONTAL);
bsizer->Add(p->btn_close);
bsizer->AddStretchSpacer();
@@ -424,16 +773,26 @@ FirmwareDialog::FirmwareDialog(wxWindow *parent) :
SetSize(std::max(size.GetWidth(), static_cast<int>(MIN_WIDTH)), std::max(size.GetHeight(), static_cast<int>(MIN_HEIGHT)));
Layout();
+ // Bind events
+
+ p->hex_picker->Bind(wxEVT_FILEPICKER_CHANGED, [this](wxFileDirPickerEvent& evt) {
+ if (wxFileExists(evt.GetPath())) {
+ this->p->load_hex_file(evt.GetPath());
+ this->p->btn_flash->Enable();
+ }
+ });
+
p->spoiler->Bind(wxEVT_COLLAPSIBLEPANE_CHANGED, [this](wxCollapsiblePaneEvent &evt) {
- // Dialog size gets screwed up by wxCollapsiblePane, we need to fix it here
if (evt.GetCollapsed()) {
this->SetMinSize(wxSize(MIN_WIDTH, MIN_HEIGHT));
+ const auto new_height = this->GetSize().GetHeight() - this->p->txt_stdout->GetSize().GetHeight();
+ this->SetSize(this->GetSize().GetWidth(), new_height);
} else {
this->SetMinSize(wxSize(MIN_WIDTH, MIN_HEIGHT_EXPANDED));
}
- this->Fit();
this->Layout();
+ this->p->fit_no_shrink();
});
p->btn_close->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { this->Close(); });
@@ -447,7 +806,8 @@ FirmwareDialog::FirmwareDialog(wxWindow *parent) :
_(L("Confirmation")),
wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION);
if (dlg.ShowModal() == wxID_YES) {
- this->p->cancel();
+ this->p->set_txt_status(_(L("Cancelling...")));
+ this->p->user_cancel();
}
} else {
// Start a flashing task
@@ -458,6 +818,7 @@ FirmwareDialog::FirmwareDialog(wxWindow *parent) :
Bind(wxEVT_TIMER, [this](wxTimerEvent &evt) { this->p->progressbar->Pulse(); });
Bind(EVT_AVRDUDE, [this](wxCommandEvent &evt) { this->p->on_avrdude(evt); });
+ Bind(EVT_ASYNC_DIALOG, [this](wxCommandEvent &evt) { this->p->on_async_dialog(evt); });
Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent &evt) {
if (this->p->avrdude) {
diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp
index adea27fa4..ce1c55264 100644
--- a/xs/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp
@@ -1,5 +1,7 @@
#include "GLCanvas3D.hpp"
+#include "../../admesh/stl.h"
+#include "../../libslic3r/libslic3r.h"
#include "../../slic3r/GUI/3DScene.hpp"
#include "../../slic3r/GUI/GLShader.hpp"
#include "../../slic3r/GUI/GUI.hpp"
@@ -14,6 +16,10 @@
#include <wx/glcanvas.h>
#include <wx/timer.h>
+#include <wx/bitmap.h>
+#include <wx/dcmemory.h>
+#include <wx/image.h>
+#include <wx/settings.h>
#include <tbb/parallel_for.h>
#include <tbb/spin_mutex.h>
@@ -41,6 +47,14 @@ static const float VIEW_REAR[2] = { 180.0f, 90.0f };
static const float VARIABLE_LAYER_THICKNESS_BAR_WIDTH = 70.0f;
static const float VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT = 22.0f;
+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 };
+
+static const float DEFAULT_BG_COLOR[3] = { 10.0f / 255.0f, 98.0f / 255.0f, 144.0f / 255.0f };
+static const float ERROR_BG_COLOR[3] = { 144.0f / 255.0f, 49.0f / 255.0f, 10.0f / 255.0f };
+
namespace Slic3r {
namespace GUI {
@@ -292,10 +306,14 @@ const Pointfs& GLCanvas3D::Bed::get_shape() const
return m_shape;
}
-void GLCanvas3D::Bed::set_shape(const Pointfs& shape)
+bool GLCanvas3D::Bed::set_shape(const Pointfs& shape)
{
+ EType new_type = _detect_type();
+ if (m_shape == shape && m_type == new_type)
+ // No change, no need to update the UI.
+ return false;
m_shape = shape;
- m_type = _detect_type();
+ m_type = new_type;
_calc_bounding_box();
@@ -311,6 +329,8 @@ void GLCanvas3D::Bed::set_shape(const Pointfs& shape)
_calc_gridlines(poly, bed_bbox);
m_polygon = offset_ex(poly.contour, (float)bed_bbox.radius() * 1.7f, jtRound, scale_(0.5))[0].contour;
+ // Let the calee to update the UI.
+ return true;
}
const BoundingBoxf3& GLCanvas3D::Bed::get_bounding_box() const
@@ -493,6 +513,7 @@ void GLCanvas3D::Bed::_render_prusa(float theta) const
::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
::glEnable(GL_TEXTURE_2D);
+ ::glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
::glEnableClientState(GL_VERTEX_ARRAY);
::glEnableClientState(GL_TEXTURE_COORD_ARRAY);
@@ -500,7 +521,6 @@ void GLCanvas3D::Bed::_render_prusa(float theta) const
if (theta > 90.0f)
::glFrontFace(GL_CW);
- ::glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
::glBindTexture(GL_TEXTURE_2D, (theta <= 90.0f) ? (GLuint)m_top_texture.get_id() : (GLuint)m_bottom_texture.get_id());
::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_triangles.get_vertices());
::glTexCoordPointer(2, GL_FLOAT, 0, (GLvoid*)m_triangles.get_tex_coords());
@@ -553,6 +573,7 @@ void GLCanvas3D::Bed::_render_custom() const
::glDisableClientState(GL_VERTEX_ARRAY);
::glDisable(GL_BLEND);
+ ::glDisable(GL_LIGHTING);
}
}
@@ -577,7 +598,6 @@ GLCanvas3D::Axes::Axes()
void GLCanvas3D::Axes::render(bool depth_test) const
{
- ::glDisable(GL_LIGHTING);
if (depth_test)
::glEnable(GL_DEPTH_TEST);
else
@@ -623,7 +643,6 @@ bool GLCanvas3D::CuttingPlane::set(float z, const ExPolygons& polygons)
void GLCanvas3D::CuttingPlane::render(const BoundingBoxf3& bb) const
{
- ::glDisable(GL_LIGHTING);
_render_plane(bb);
_render_contour();
}
@@ -730,6 +749,12 @@ void GLCanvas3D::Shader::set_uniform(const std::string& name, float value) const
m_shader->set_uniform(name.c_str(), value);
}
+void GLCanvas3D::Shader::set_uniform(const std::string& name, const float* matrix) const
+{
+ if (m_shader != nullptr)
+ m_shader->set_uniform(name.c_str(), matrix);
+}
+
const GLShader* GLCanvas3D::Shader::get_shader() const
{
return m_shader;
@@ -963,15 +988,18 @@ void GLCanvas3D::LayersEditing::_render_active_object_annotations(const GLCanvas
m_shader.set_uniform("z_texture_row_to_normalized", 1.0f / (float)volume.layer_height_texture_height());
m_shader.set_uniform("z_cursor", max_z * get_cursor_z_relative(canvas));
m_shader.set_uniform("z_cursor_band_width", band_width);
+ // The shader requires the original model coordinates when rendering to the texture, so we pass it the unit matrix
+ m_shader.set_uniform("volume_world_matrix", UNIT_MATRIX);
GLsizei w = (GLsizei)volume.layer_height_texture_width();
GLsizei h = (GLsizei)volume.layer_height_texture_height();
GLsizei half_w = w / 2;
GLsizei half_h = h / 2;
+ ::glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
::glBindTexture(GL_TEXTURE_2D, m_z_texture_id);
- ::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
- ::glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, half_w, half_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
+ ::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
+ ::glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, half_w, half_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
::glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, volume.layer_height_texture_data_ptr_level0());
::glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, half_w, half_h, GL_RGBA, GL_UNSIGNED_BYTE, volume.layer_height_texture_data_ptr_level1());
@@ -1053,7 +1081,9 @@ const Pointf3 GLCanvas3D::Mouse::Drag::Invalid_3D_Point(DBL_MAX, DBL_MAX, DBL_MA
GLCanvas3D::Mouse::Drag::Drag()
: start_position_2D(Invalid_2D_Point)
, start_position_3D(Invalid_3D_Point)
- , volume_idx(-1)
+ , move_with_shift(false)
+ , move_volume_idx(-1)
+ , gizmo_volume_idx(-1)
{
}
@@ -1083,8 +1113,9 @@ bool GLCanvas3D::Mouse::is_start_position_3D_defined() const
return (drag.start_position_3D != Drag::Invalid_3D_Point);
}
-const float GLCanvas3D::Gizmos::OverlayOffsetX = 10.0f;
-const float GLCanvas3D::Gizmos::OverlayGapY = 10.0f;
+const float GLCanvas3D::Gizmos::OverlayTexturesScale = 0.75f;
+const float GLCanvas3D::Gizmos::OverlayOffsetX = 10.0f * OverlayTexturesScale;
+const float GLCanvas3D::Gizmos::OverlayGapY = 5.0f * OverlayTexturesScale;
GLCanvas3D::Gizmos::Gizmos()
: m_enabled(false)
@@ -1124,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;
}
@@ -1150,7 +1193,7 @@ void GLCanvas3D::Gizmos::update_hover_state(const GLCanvas3D& canvas, const Poin
if (it->second == nullptr)
continue;
- float tex_size = (float)it->second->get_textures_size();
+ float tex_size = (float)it->second->get_textures_size() * OverlayTexturesScale;
float half_tex_size = 0.5f * tex_size;
// we currently use circular icons for gizmo, so we check the radius
@@ -1176,7 +1219,7 @@ void GLCanvas3D::Gizmos::update_on_off_state(const GLCanvas3D& canvas, const Poi
if (it->second == nullptr)
continue;
- float tex_size = (float)it->second->get_textures_size();
+ float tex_size = (float)it->second->get_textures_size() * OverlayTexturesScale;
float half_tex_size = 0.5f * tex_size;
// we currently use circular icons for gizmo, so we check the radius
@@ -1242,7 +1285,7 @@ bool GLCanvas3D::Gizmos::overlay_contains_mouse(const GLCanvas3D& canvas, const
if (it->second == nullptr)
continue;
- float tex_size = (float)it->second->get_textures_size();
+ float tex_size = (float)it->second->get_textures_size() * OverlayTexturesScale;
float half_tex_size = 0.5f * tex_size;
// we currently use circular icons for gizmo, so we check the radius
@@ -1274,14 +1317,19 @@ void GLCanvas3D::Gizmos::update(const Pointf& mouse_pos)
curr->update(mouse_pos);
}
-void GLCanvas3D::Gizmos::update_data(float scale)
+void GLCanvas3D::Gizmos::refresh()
{
if (!m_enabled)
return;
- GizmosMap::const_iterator it = m_gizmos.find(Scale);
- if (it != m_gizmos.end())
- reinterpret_cast<GLGizmoScale*>(it->second)->set_scale(scale);
+ GLGizmoBase* curr = _get_current();
+ if (curr != nullptr)
+ curr->refresh();
+}
+
+GLCanvas3D::Gizmos::EType GLCanvas3D::Gizmos::get_current_type() const
+{
+ return m_current;
}
bool GLCanvas3D::Gizmos::is_running() const
@@ -1309,6 +1357,9 @@ void GLCanvas3D::Gizmos::start_dragging()
void GLCanvas3D::Gizmos::stop_dragging()
{
m_dragging = false;
+ GLGizmoBase* curr = _get_current();
+ if (curr != nullptr)
+ curr->stop_dragging();
}
float GLCanvas3D::Gizmos::get_scale() const
@@ -1320,21 +1371,75 @@ float GLCanvas3D::Gizmos::get_scale() const
return (it != m_gizmos.end()) ? reinterpret_cast<GLGizmoScale*>(it->second)->get_scale() : 1.0f;
}
-void GLCanvas3D::Gizmos::render(const GLCanvas3D& canvas, const BoundingBoxf3& box) const
+void GLCanvas3D::Gizmos::set_scale(float scale)
+{
+ if (!m_enabled)
+ return;
+
+ GizmosMap::const_iterator it = m_gizmos.find(Scale);
+ if (it != m_gizmos.end())
+ reinterpret_cast<GLGizmoScale*>(it->second)->set_scale(scale);
+}
+
+float GLCanvas3D::Gizmos::get_angle_z() const
+{
+ if (!m_enabled)
+ return 0.0f;
+
+ GizmosMap::const_iterator it = m_gizmos.find(Rotate);
+ return (it != m_gizmos.end()) ? reinterpret_cast<GLGizmoRotate*>(it->second)->get_angle_z() : 0.0f;
+}
+
+void GLCanvas3D::Gizmos::set_angle_z(float angle_z)
+{
+ if (!m_enabled)
+ return;
+
+ GizmosMap::const_iterator it = m_gizmos.find(Rotate);
+ if (it != m_gizmos.end())
+ reinterpret_cast<GLGizmoRotate*>(it->second)->set_angle_z(angle_z);
+}
+
+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);
- _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
@@ -1375,8 +1480,8 @@ void GLCanvas3D::Gizmos::_render_overlay(const GLCanvas3D& canvas) const
float scaled_gap_y = OverlayGapY * inv_zoom;
for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
{
- float tex_size = (float)it->second->get_textures_size() * inv_zoom;
- GLTexture::render_texture(it->second->get_textures_id(), top_x, top_x + tex_size, top_y - tex_size, top_y);
+ float tex_size = (float)it->second->get_textures_size() * OverlayTexturesScale * inv_zoom;
+ GLTexture::render_texture(it->second->get_texture_id(), top_x, top_x + tex_size, top_y - tex_size, top_y);
top_y -= (tex_size + scaled_gap_y);
}
}
@@ -1402,6 +1507,224 @@ float GLCanvas3D::Gizmos::_get_total_overlay_height() const
return height;
}
+const unsigned char GLCanvas3D::WarningTexture::Background_Color[3] = { 9, 91, 134 };
+const unsigned char GLCanvas3D::WarningTexture::Opacity = 255;
+
+bool GLCanvas3D::WarningTexture::generate(const std::string& msg)
+{
+ reset();
+
+ if (msg.empty())
+ return false;
+
+ wxMemoryDC memDC;
+ // select default font
+ memDC.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
+
+ // calculates texture size
+ wxCoord w, h;
+ memDC.GetTextExtent(msg, &w, &h);
+ m_width = (int)w;
+ m_height = (int)h;
+
+ // generates bitmap
+ wxBitmap bitmap(m_width, m_height);
+
+#if defined(__APPLE__) || defined(_MSC_VER)
+ bitmap.UseAlpha();
+#endif
+
+ memDC.SelectObject(bitmap);
+ memDC.SetBackground(wxBrush(wxColour(Background_Color[0], Background_Color[1], Background_Color[2])));
+ memDC.Clear();
+
+ memDC.SetTextForeground(*wxWHITE);
+
+ // draw message
+ memDC.DrawText(msg, 0, 0);
+
+ memDC.SelectObject(wxNullBitmap);
+
+ // Convert the bitmap into a linear data ready to be loaded into the GPU.
+ wxImage image = bitmap.ConvertToImage();
+ image.SetMaskColour(Background_Color[0], Background_Color[1], Background_Color[2]);
+
+ // prepare buffer
+ std::vector<unsigned char> data(4 * m_width * m_height, 0);
+ for (int h = 0; h < m_height; ++h)
+ {
+ int hh = h * m_width;
+ unsigned char* px_ptr = data.data() + 4 * hh;
+ for (int w = 0; w < m_width; ++w)
+ {
+ *px_ptr++ = image.GetRed(w, h);
+ *px_ptr++ = image.GetGreen(w, h);
+ *px_ptr++ = image.GetBlue(w, h);
+ *px_ptr++ = image.IsTransparent(w, h) ? 0 : Opacity;
+ }
+ }
+
+ // sends buffer to gpu
+ ::glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ ::glGenTextures(1, &m_id);
+ ::glBindTexture(GL_TEXTURE_2D, (GLuint)m_id);
+ ::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data());
+ ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
+ ::glBindTexture(GL_TEXTURE_2D, 0);
+
+ return true;
+}
+
+const unsigned char GLCanvas3D::LegendTexture::Squares_Border_Color[3] = { 64, 64, 64 };
+const unsigned char GLCanvas3D::LegendTexture::Background_Color[3] = { 9, 91, 134 };
+const unsigned char GLCanvas3D::LegendTexture::Opacity = 255;
+
+bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors)
+{
+ reset();
+
+ // collects items to render
+ auto title = _(preview_data.get_legend_title());
+ const GCodePreviewData::LegendItemsList& items = preview_data.get_legend_items(tool_colors);
+
+ unsigned int items_count = (unsigned int)items.size();
+ if (items_count == 0)
+ // nothing to render, return
+ return false;
+
+ wxMemoryDC memDC;
+ // select default font
+ memDC.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
+
+ // calculates texture size
+ wxCoord w, h;
+ memDC.GetTextExtent(title, &w, &h);
+ int title_width = (int)w;
+ int title_height = (int)h;
+
+ int max_text_width = 0;
+ int max_text_height = 0;
+ for (const GCodePreviewData::LegendItem& item : items)
+ {
+ memDC.GetTextExtent(GUI::from_u8(item.text), &w, &h);
+ max_text_width = std::max(max_text_width, (int)w);
+ max_text_height = std::max(max_text_height, (int)h);
+ }
+
+ m_width = std::max(2 * Px_Border + title_width, 2 * (Px_Border + Px_Square_Contour) + Px_Square + Px_Text_Offset + max_text_width);
+ m_height = 2 * (Px_Border + Px_Square_Contour) + title_height + Px_Title_Offset + items_count * Px_Square;
+ if (items_count > 1)
+ m_height += (items_count - 1) * Px_Square_Contour;
+
+ // generates bitmap
+ wxBitmap bitmap(m_width, m_height);
+
+#if defined(__APPLE__) || defined(_MSC_VER)
+ bitmap.UseAlpha();
+#endif
+
+ memDC.SelectObject(bitmap);
+ memDC.SetBackground(wxBrush(wxColour(Background_Color[0], Background_Color[1], Background_Color[2])));
+ memDC.Clear();
+
+ memDC.SetTextForeground(*wxWHITE);
+
+ // draw title
+ int title_x = Px_Border;
+ int title_y = Px_Border;
+ memDC.DrawText(title, title_x, title_y);
+
+ // draw icons contours as background
+ int squares_contour_x = Px_Border;
+ int squares_contour_y = Px_Border + title_height + Px_Title_Offset;
+ int squares_contour_width = Px_Square + 2 * Px_Square_Contour;
+ int squares_contour_height = items_count * Px_Square + 2 * Px_Square_Contour;
+ if (items_count > 1)
+ squares_contour_height += (items_count - 1) * Px_Square_Contour;
+
+ wxColour color(Squares_Border_Color[0], Squares_Border_Color[1], Squares_Border_Color[2]);
+ wxPen pen(color);
+ wxBrush brush(color);
+ memDC.SetPen(pen);
+ memDC.SetBrush(brush);
+ memDC.DrawRectangle(wxRect(squares_contour_x, squares_contour_y, squares_contour_width, squares_contour_height));
+
+ // draw items (colored icon + text)
+ int icon_x = squares_contour_x + Px_Square_Contour;
+ int icon_x_inner = icon_x + 1;
+ int icon_y = squares_contour_y + Px_Square_Contour;
+ int icon_y_step = Px_Square + Px_Square_Contour;
+
+ int text_x = icon_x + Px_Square + Px_Text_Offset;
+ int text_y_offset = (Px_Square - max_text_height) / 2;
+
+ int px_inner_square = Px_Square - 2;
+
+ for (const GCodePreviewData::LegendItem& item : items)
+ {
+ // draw darker icon perimeter
+ const std::vector<unsigned char>& item_color_bytes = item.color.as_bytes();
+ wxImage::HSVValue dark_hsv = wxImage::RGBtoHSV(wxImage::RGBValue(item_color_bytes[0], item_color_bytes[1], item_color_bytes[2]));
+ dark_hsv.value *= 0.75;
+ wxImage::RGBValue dark_rgb = wxImage::HSVtoRGB(dark_hsv);
+ color.Set(dark_rgb.red, dark_rgb.green, dark_rgb.blue, item_color_bytes[3]);
+ pen.SetColour(color);
+ brush.SetColour(color);
+ memDC.SetPen(pen);
+ memDC.SetBrush(brush);
+ memDC.DrawRectangle(wxRect(icon_x, icon_y, Px_Square, Px_Square));
+
+ // draw icon interior
+ color.Set(item_color_bytes[0], item_color_bytes[1], item_color_bytes[2], item_color_bytes[3]);
+ pen.SetColour(color);
+ brush.SetColour(color);
+ memDC.SetPen(pen);
+ memDC.SetBrush(brush);
+ memDC.DrawRectangle(wxRect(icon_x_inner, icon_y + 1, px_inner_square, px_inner_square));
+
+ // draw text
+ memDC.DrawText(GUI::from_u8(item.text), text_x, icon_y + text_y_offset);
+
+ // update y
+ icon_y += icon_y_step;
+ }
+
+ memDC.SelectObject(wxNullBitmap);
+
+ // Convert the bitmap into a linear data ready to be loaded into the GPU.
+ wxImage image = bitmap.ConvertToImage();
+ image.SetMaskColour(Background_Color[0], Background_Color[1], Background_Color[2]);
+
+ // prepare buffer
+ std::vector<unsigned char> data(4 * m_width * m_height, 0);
+ for (int h = 0; h < m_height; ++h)
+ {
+ int hh = h * m_width;
+ unsigned char* px_ptr = data.data() + 4 * hh;
+ for (int w = 0; w < m_width; ++w)
+ {
+ *px_ptr++ = image.GetRed(w, h);
+ *px_ptr++ = image.GetGreen(w, h);
+ *px_ptr++ = image.GetBlue(w, h);
+ *px_ptr++ = image.IsTransparent(w, h) ? 0 : Opacity;
+ }
+ }
+
+ // sends buffer to gpu
+ ::glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ ::glGenTextures(1, &m_id);
+ ::glBindTexture(GL_TEXTURE_2D, (GLuint)m_id);
+ ::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data());
+ ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
+ ::glBindTexture(GL_TEXTURE_2D, 0);
+
+ return true;
+}
+
GLGizmoBase* GLCanvas3D::Gizmos::_get_current() const
{
GizmosMap::const_iterator it = m_gizmos.find(m_current);
@@ -1426,6 +1749,7 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas)
, m_picking_enabled(false)
, m_moving_enabled(false)
, m_shader_enabled(false)
+ , m_dynamic_background_enabled(false)
, m_multisample_allowed(false)
, m_color_by("volume")
, m_select_by("object")
@@ -1551,7 +1875,6 @@ unsigned int GLCanvas3D::get_volumes_count() const
void GLCanvas3D::reset_volumes()
{
-
if (!m_volumes.empty())
{
// ensures this canvas is current
@@ -1562,6 +1885,9 @@ void GLCanvas3D::reset_volumes()
m_volumes.clear();
m_dirty = true;
}
+
+ enable_warning_texture(false);
+ _reset_warning_texture();
}
void GLCanvas3D::deselect_volumes()
@@ -1601,9 +1927,11 @@ void GLCanvas3D::update_volumes_selection(const std::vector<int>& selections)
}
}
-bool GLCanvas3D::check_volumes_outside_state(const DynamicPrintConfig* config) const
+int GLCanvas3D::check_volumes_outside_state(const DynamicPrintConfig* config) const
{
- return m_volumes.check_outside_state(config);
+ ModelInstance::EPrintVolumeState state;
+ m_volumes.check_outside_state(config, &state);
+ return (int)state;
}
bool GLCanvas3D::move_volume_up(unsigned int id)
@@ -1656,12 +1984,21 @@ void GLCanvas3D::set_model(Model* model)
void GLCanvas3D::set_bed_shape(const Pointfs& shape)
{
- m_bed.set_shape(shape);
+ bool new_shape = m_bed.set_shape(shape);
// Set the origin and size for painting of the coordinate system axes.
m_axes.origin = Pointf3(0.0, 0.0, (coordf_t)GROUND_Z);
set_axes_length(0.3f * (float)m_bed.get_bounding_box().max_size());
+ if (new_shape)
+ {
+ // forces the selection of the proper camera target
+ if (m_volumes.volumes.empty())
+ zoom_to_bed();
+ else
+ zoom_to_volumes();
+ }
+
m_dirty = true;
}
@@ -1786,6 +2123,11 @@ void GLCanvas3D::enable_force_zoom_to_bed(bool enable)
m_force_zoom_to_bed_enabled = enable;
}
+void GLCanvas3D::enable_dynamic_background(bool enable)
+{
+ m_dynamic_background_enabled = enable;
+}
+
void GLCanvas3D::allow_multisample(bool allow)
{
m_multisample_allowed = allow;
@@ -1849,6 +2191,34 @@ void GLCanvas3D::update_volumes_colors_by_extruder()
m_volumes.update_colors_by_extruder(m_config);
}
+void GLCanvas3D::update_gizmos_data()
+{
+ if (!m_gizmos.is_enabled())
+ return;
+
+ int id = _get_first_selected_object_id();
+ if ((id != -1) && (m_model != nullptr))
+ {
+ ModelObject* model_object = m_model->objects[id];
+ if (model_object != nullptr)
+ {
+ ModelInstance* model_instance = model_object->instances[0];
+ if (model_instance != nullptr)
+ {
+ m_gizmos.set_scale(model_instance->scaling_factor);
+ m_gizmos.set_angle_z(model_instance->rotation);
+ m_gizmos.set_flattening_data(model_object);
+ }
+ }
+ }
+ else
+ {
+ m_gizmos.set_scale(1.0f);
+ m_gizmos.set_angle_z(0.0f);
+ m_gizmos.set_flattening_data(nullptr);
+ }
+}
+
void GLCanvas3D::render()
{
if (m_canvas == nullptr)
@@ -1884,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)
{
@@ -1893,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();
@@ -1961,7 +2332,12 @@ void GLCanvas3D::reload_scene(bool force)
m_objects_volumes_idxs.push_back(load_object(*m_model, obj_idx));
}
+ // 1st call to reset if no objects left
+ update_gizmos_data();
update_volumes_selection(m_objects_selections);
+ // 2nd call to restore if something selected
+ if (!m_objects_selections.empty())
+ update_gizmos_data();
if (m_config->has("nozzle_diameter"))
{
@@ -1982,7 +2358,12 @@ void GLCanvas3D::reload_scene(bool force)
float w = dynamic_cast<const ConfigOptionFloat*>(m_config->option("wipe_tower_width"))->value;
float a = dynamic_cast<const ConfigOptionFloat*>(m_config->option("wipe_tower_rotation_angle"))->value;
- m_volumes.load_wipe_tower_preview(1000, x, y, w, 15.0f * (float)(extruders_count - 1), (float)height, a, m_use_VBOs && m_initialized);
+ float depth = m_print->get_wipe_tower_depth();
+ if (!m_print->state.is_done(psWipeTower))
+ depth = (900.f/w) * (float)(extruders_count - 1) ;
+
+ m_volumes.load_wipe_tower_preview(1000, x, y, w, depth, (float)height, a, m_use_VBOs && m_initialized, !m_print->state.is_done(psWipeTower),
+ m_print->config.nozzle_diameter.values[0] * 1.25f * 4.5f);
}
}
@@ -1991,394 +2372,31 @@ void GLCanvas3D::reload_scene(bool force)
// checks for geometry outside the print volume to render it accordingly
if (!m_volumes.empty())
{
- bool contained = m_volumes.check_outside_state(m_config);
+ ModelInstance::EPrintVolumeState state;
+ bool contained = m_volumes.check_outside_state(m_config, &state);
+
if (!contained)
{
enable_warning_texture(true);
- _3DScene::generate_warning_texture(L("Detected object outside print volume"));
- m_on_enable_action_buttons_callback.call(false);
+ _generate_warning_texture(L("Detected object outside print volume"));
+ m_on_enable_action_buttons_callback.call(state == ModelInstance::PVS_Fully_Outside);
}
else
{
enable_warning_texture(false);
m_volumes.reset_outside_state();
- _3DScene::reset_warning_texture();
+ _reset_warning_texture();
m_on_enable_action_buttons_callback.call(!m_model->objects.empty());
}
}
else
{
enable_warning_texture(false);
- _3DScene::reset_warning_texture();
+ _reset_warning_texture();
+ m_on_enable_action_buttons_callback.call(false);
}
}
-void GLCanvas3D::load_print_toolpaths()
-{
- // ensures this canvas is current
- if (!set_current())
- return;
-
- if (m_print == nullptr)
- return;
-
- if (!m_print->state.is_done(psSkirt) || !m_print->state.is_done(psBrim))
- return;
-
- if (!m_print->has_skirt() && (m_print->config.brim_width.value == 0))
- return;
-
- const float color[] = { 0.5f, 1.0f, 0.5f, 1.0f }; // greenish
-
- // number of skirt layers
- size_t total_layer_count = 0;
- for (const PrintObject* print_object : m_print->objects)
- {
- total_layer_count = std::max(total_layer_count, print_object->total_layer_count());
- }
- size_t skirt_height = m_print->has_infinite_skirt() ? total_layer_count : std::min<size_t>(m_print->config.skirt_height.value, total_layer_count);
- if ((skirt_height == 0) && (m_print->config.brim_width.value > 0))
- skirt_height = 1;
-
- // get first skirt_height layers (maybe this should be moved to a PrintObject method?)
- const PrintObject* object0 = m_print->objects.front();
- std::vector<float> print_zs;
- print_zs.reserve(skirt_height * 2);
- for (size_t i = 0; i < std::min(skirt_height, object0->layers.size()); ++i)
- {
- print_zs.push_back(float(object0->layers[i]->print_z));
- }
- //FIXME why there are support layers?
- for (size_t i = 0; i < std::min(skirt_height, object0->support_layers.size()); ++i)
- {
- print_zs.push_back(float(object0->support_layers[i]->print_z));
- }
- sort_remove_duplicates(print_zs);
- if (print_zs.size() > skirt_height)
- print_zs.erase(print_zs.begin() + skirt_height, print_zs.end());
-
- m_volumes.volumes.emplace_back(new GLVolume(color));
- GLVolume& volume = *m_volumes.volumes.back();
- for (size_t i = 0; i < skirt_height; ++i) {
- volume.print_zs.push_back(print_zs[i]);
- volume.offsets.push_back(volume.indexed_vertex_array.quad_indices.size());
- volume.offsets.push_back(volume.indexed_vertex_array.triangle_indices.size());
- if (i == 0)
- _3DScene::extrusionentity_to_verts(m_print->brim, print_zs[i], Point(0, 0), volume);
-
- _3DScene::extrusionentity_to_verts(m_print->skirt, print_zs[i], Point(0, 0), volume);
- }
- volume.bounding_box = volume.indexed_vertex_array.bounding_box();
- volume.indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized);
-}
-
-void GLCanvas3D::load_print_object_toolpaths(const PrintObject& print_object, const std::vector<std::string>& str_tool_colors)
-{
- std::vector<float> tool_colors = _parse_colors(str_tool_colors);
-
- struct Ctxt
- {
- const Points *shifted_copies;
- std::vector<const Layer*> layers;
- bool has_perimeters;
- bool has_infill;
- bool has_support;
- const std::vector<float>* tool_colors;
-
- // Number of vertices (each vertex is 6x4=24 bytes long)
- static const size_t alloc_size_max() { return 131072; } // 3.15MB
- // static const size_t alloc_size_max () { return 65536; } // 1.57MB
- // static const size_t alloc_size_max () { return 32768; } // 786kB
- static const size_t alloc_size_reserve() { return alloc_size_max() * 2; }
-
- static const float* color_perimeters() { static float color[4] = { 1.0f, 1.0f, 0.0f, 1.f }; return color; } // yellow
- static const float* color_infill() { static float color[4] = { 1.0f, 0.5f, 0.5f, 1.f }; return color; } // redish
- static const float* color_support() { static float color[4] = { 0.5f, 1.0f, 0.5f, 1.f }; return color; } // greenish
-
- // For cloring by a tool, return a parsed color.
- bool color_by_tool() const { return tool_colors != nullptr; }
- size_t number_tools() const { return this->color_by_tool() ? tool_colors->size() / 4 : 0; }
- const float* color_tool(size_t tool) const { return tool_colors->data() + tool * 4; }
- int volume_idx(int extruder, int feature) const
- {
- return this->color_by_tool() ? std::min<int>(this->number_tools() - 1, std::max<int>(extruder - 1, 0)) : feature;
- }
- } ctxt;
-
- ctxt.shifted_copies = &print_object._shifted_copies;
-
- // order layers by print_z
- ctxt.layers.reserve(print_object.layers.size() + print_object.support_layers.size());
- for (const Layer *layer : print_object.layers)
- ctxt.layers.push_back(layer);
- for (const Layer *layer : print_object.support_layers)
- ctxt.layers.push_back(layer);
- std::sort(ctxt.layers.begin(), ctxt.layers.end(), [](const Layer *l1, const Layer *l2) { return l1->print_z < l2->print_z; });
-
- // Maximum size of an allocation block: 32MB / sizeof(float)
- ctxt.has_perimeters = print_object.state.is_done(posPerimeters);
- ctxt.has_infill = print_object.state.is_done(posInfill);
- ctxt.has_support = print_object.state.is_done(posSupportMaterial);
- ctxt.tool_colors = tool_colors.empty() ? nullptr : &tool_colors;
-
- BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - start";
-
- //FIXME Improve the heuristics for a grain size.
- size_t grain_size = std::max(ctxt.layers.size() / 16, size_t(1));
- tbb::spin_mutex new_volume_mutex;
- auto new_volume = [this, &new_volume_mutex](const float *color) -> GLVolume* {
- auto *volume = new GLVolume(color);
- new_volume_mutex.lock();
- volume->outside_printer_detection_enabled = false;
- m_volumes.volumes.emplace_back(volume);
- new_volume_mutex.unlock();
- return volume;
- };
- const size_t volumes_cnt_initial = m_volumes.volumes.size();
- std::vector<GLVolumeCollection> volumes_per_thread(ctxt.layers.size());
- tbb::parallel_for(
- tbb::blocked_range<size_t>(0, ctxt.layers.size(), grain_size),
- [&ctxt, &new_volume](const tbb::blocked_range<size_t>& range) {
- std::vector<GLVolume*> vols;
- if (ctxt.color_by_tool()) {
- for (size_t i = 0; i < ctxt.number_tools(); ++i)
- vols.emplace_back(new_volume(ctxt.color_tool(i)));
- }
- else
- vols = { new_volume(ctxt.color_perimeters()), new_volume(ctxt.color_infill()), new_volume(ctxt.color_support()) };
- for (GLVolume *vol : vols)
- vol->indexed_vertex_array.reserve(ctxt.alloc_size_reserve());
- for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++idx_layer) {
- const Layer *layer = ctxt.layers[idx_layer];
- for (size_t i = 0; i < vols.size(); ++i) {
- GLVolume &vol = *vols[i];
- if (vol.print_zs.empty() || vol.print_zs.back() != layer->print_z) {
- vol.print_zs.push_back(layer->print_z);
- vol.offsets.push_back(vol.indexed_vertex_array.quad_indices.size());
- vol.offsets.push_back(vol.indexed_vertex_array.triangle_indices.size());
- }
- }
- for (const Point &copy : *ctxt.shifted_copies) {
- for (const LayerRegion *layerm : layer->regions) {
- if (ctxt.has_perimeters)
- _3DScene::extrusionentity_to_verts(layerm->perimeters, float(layer->print_z), copy,
- *vols[ctxt.volume_idx(layerm->region()->config.perimeter_extruder.value, 0)]);
- if (ctxt.has_infill) {
- for (const ExtrusionEntity *ee : layerm->fills.entities) {
- // fill represents infill extrusions of a single island.
- const auto *fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
- if (!fill->entities.empty())
- _3DScene::extrusionentity_to_verts(*fill, float(layer->print_z), copy,
- *vols[ctxt.volume_idx(
- is_solid_infill(fill->entities.front()->role()) ?
- layerm->region()->config.solid_infill_extruder :
- layerm->region()->config.infill_extruder,
- 1)]);
- }
- }
- }
- if (ctxt.has_support) {
- const SupportLayer *support_layer = dynamic_cast<const SupportLayer*>(layer);
- if (support_layer) {
- for (const ExtrusionEntity *extrusion_entity : support_layer->support_fills.entities)
- _3DScene::extrusionentity_to_verts(extrusion_entity, float(layer->print_z), copy,
- *vols[ctxt.volume_idx(
- (extrusion_entity->role() == erSupportMaterial) ?
- support_layer->object()->config.support_material_extruder :
- support_layer->object()->config.support_material_interface_extruder,
- 2)]);
- }
- }
- }
- for (size_t i = 0; i < vols.size(); ++i) {
- GLVolume &vol = *vols[i];
- if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() / 6 > ctxt.alloc_size_max()) {
- // Store the vertex arrays and restart their containers,
- vols[i] = new_volume(vol.color);
- GLVolume &vol_new = *vols[i];
- // Assign the large pre-allocated buffers to the new GLVolume.
- vol_new.indexed_vertex_array = std::move(vol.indexed_vertex_array);
- // Copy the content back to the old GLVolume.
- vol.indexed_vertex_array = vol_new.indexed_vertex_array;
- // Finalize a bounding box of the old GLVolume.
- vol.bounding_box = vol.indexed_vertex_array.bounding_box();
- // Clear the buffers, but keep them pre-allocated.
- vol_new.indexed_vertex_array.clear();
- // Just make sure that clear did not clear the reserved memory.
- vol_new.indexed_vertex_array.reserve(ctxt.alloc_size_reserve());
- }
- }
- }
- for (GLVolume *vol : vols) {
- vol->bounding_box = vol->indexed_vertex_array.bounding_box();
- vol->indexed_vertex_array.shrink_to_fit();
- }
- });
-
- BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - finalizing results";
- // Remove empty volumes from the newly added volumes.
- m_volumes.volumes.erase(
- std::remove_if(m_volumes.volumes.begin() + volumes_cnt_initial, m_volumes.volumes.end(),
- [](const GLVolume *volume) { return volume->empty(); }),
- m_volumes.volumes.end());
- for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i)
- m_volumes.volumes[i]->indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized);
-
- BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - end";
-}
-
-void GLCanvas3D::load_wipe_tower_toolpaths(const std::vector<std::string>& str_tool_colors)
-{
- if ((m_print == nullptr) || m_print->m_wipe_tower_tool_changes.empty())
- return;
-
- if (!m_print->state.is_done(psWipeTower))
- return;
-
- std::vector<float> tool_colors = _parse_colors(str_tool_colors);
-
- struct Ctxt
- {
- const Print *print;
- const std::vector<float> *tool_colors;
-
- // Number of vertices (each vertex is 6x4=24 bytes long)
- static const size_t alloc_size_max() { return 131072; } // 3.15MB
- static const size_t alloc_size_reserve() { return alloc_size_max() * 2; }
-
- static const float* color_support() { static float color[4] = { 0.5f, 1.0f, 0.5f, 1.f }; return color; } // greenish
-
- // For cloring by a tool, return a parsed color.
- bool color_by_tool() const { return tool_colors != nullptr; }
- size_t number_tools() const { return this->color_by_tool() ? tool_colors->size() / 4 : 0; }
- const float* color_tool(size_t tool) const { return tool_colors->data() + tool * 4; }
- int volume_idx(int tool, int feature) const
- {
- return this->color_by_tool() ? std::min<int>(this->number_tools() - 1, std::max<int>(tool, 0)) : feature;
- }
-
- const std::vector<WipeTower::ToolChangeResult>& tool_change(size_t idx) {
- return priming.empty() ?
- ((idx == print->m_wipe_tower_tool_changes.size()) ? final : print->m_wipe_tower_tool_changes[idx]) :
- ((idx == 0) ? priming : (idx == print->m_wipe_tower_tool_changes.size() + 1) ? final : print->m_wipe_tower_tool_changes[idx - 1]);
- }
- std::vector<WipeTower::ToolChangeResult> priming;
- std::vector<WipeTower::ToolChangeResult> final;
- } ctxt;
-
- ctxt.print = m_print;
- ctxt.tool_colors = tool_colors.empty() ? nullptr : &tool_colors;
- if (m_print->m_wipe_tower_priming)
- ctxt.priming.emplace_back(*m_print->m_wipe_tower_priming.get());
- if (m_print->m_wipe_tower_final_purge)
- ctxt.final.emplace_back(*m_print->m_wipe_tower_final_purge.get());
-
- BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - start";
-
- //FIXME Improve the heuristics for a grain size.
- size_t n_items = m_print->m_wipe_tower_tool_changes.size() + (ctxt.priming.empty() ? 0 : 1);
- size_t grain_size = std::max(n_items / 128, size_t(1));
- tbb::spin_mutex new_volume_mutex;
- auto new_volume = [this, &new_volume_mutex](const float *color) -> GLVolume* {
- auto *volume = new GLVolume(color);
- new_volume_mutex.lock();
- volume->outside_printer_detection_enabled = false;
- m_volumes.volumes.emplace_back(volume);
- new_volume_mutex.unlock();
- return volume;
- };
- const size_t volumes_cnt_initial = m_volumes.volumes.size();
- std::vector<GLVolumeCollection> volumes_per_thread(n_items);
- tbb::parallel_for(
- tbb::blocked_range<size_t>(0, n_items, grain_size),
- [&ctxt, &new_volume](const tbb::blocked_range<size_t>& range) {
- // Bounding box of this slab of a wipe tower.
- std::vector<GLVolume*> vols;
- if (ctxt.color_by_tool()) {
- for (size_t i = 0; i < ctxt.number_tools(); ++i)
- vols.emplace_back(new_volume(ctxt.color_tool(i)));
- }
- else
- vols = { new_volume(ctxt.color_support()) };
- for (GLVolume *volume : vols)
- volume->indexed_vertex_array.reserve(ctxt.alloc_size_reserve());
- for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++idx_layer) {
- const std::vector<WipeTower::ToolChangeResult> &layer = ctxt.tool_change(idx_layer);
- for (size_t i = 0; i < vols.size(); ++i) {
- GLVolume &vol = *vols[i];
- if (vol.print_zs.empty() || vol.print_zs.back() != layer.front().print_z) {
- vol.print_zs.push_back(layer.front().print_z);
- vol.offsets.push_back(vol.indexed_vertex_array.quad_indices.size());
- vol.offsets.push_back(vol.indexed_vertex_array.triangle_indices.size());
- }
- }
- for (const WipeTower::ToolChangeResult &extrusions : layer) {
- for (size_t i = 1; i < extrusions.extrusions.size();) {
- const WipeTower::Extrusion &e = extrusions.extrusions[i];
- if (e.width == 0.) {
- ++i;
- continue;
- }
- size_t j = i + 1;
- if (ctxt.color_by_tool())
- for (; j < extrusions.extrusions.size() && extrusions.extrusions[j].tool == e.tool && extrusions.extrusions[j].width > 0.f; ++j);
- else
- for (; j < extrusions.extrusions.size() && extrusions.extrusions[j].width > 0.f; ++j);
- size_t n_lines = j - i;
- Lines lines;
- std::vector<double> widths;
- std::vector<double> heights;
- lines.reserve(n_lines);
- widths.reserve(n_lines);
- heights.assign(n_lines, extrusions.layer_height);
- for (; i < j; ++i) {
- const WipeTower::Extrusion &e = extrusions.extrusions[i];
- assert(e.width > 0.f);
- const WipeTower::Extrusion &e_prev = *(&e - 1);
- lines.emplace_back(Point::new_scale(e_prev.pos.x, e_prev.pos.y), Point::new_scale(e.pos.x, e.pos.y));
- widths.emplace_back(e.width);
- }
- _3DScene::thick_lines_to_verts(lines, widths, heights, lines.front().a == lines.back().b, extrusions.print_z,
- *vols[ctxt.volume_idx(e.tool, 0)]);
- }
- }
- }
- for (size_t i = 0; i < vols.size(); ++i) {
- GLVolume &vol = *vols[i];
- if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() / 6 > ctxt.alloc_size_max()) {
- // Store the vertex arrays and restart their containers,
- vols[i] = new_volume(vol.color);
- GLVolume &vol_new = *vols[i];
- // Assign the large pre-allocated buffers to the new GLVolume.
- vol_new.indexed_vertex_array = std::move(vol.indexed_vertex_array);
- // Copy the content back to the old GLVolume.
- vol.indexed_vertex_array = vol_new.indexed_vertex_array;
- // Finalize a bounding box of the old GLVolume.
- vol.bounding_box = vol.indexed_vertex_array.bounding_box();
- // Clear the buffers, but keep them pre-allocated.
- vol_new.indexed_vertex_array.clear();
- // Just make sure that clear did not clear the reserved memory.
- vol_new.indexed_vertex_array.reserve(ctxt.alloc_size_reserve());
- }
- }
- for (GLVolume *vol : vols) {
- vol->bounding_box = vol->indexed_vertex_array.bounding_box();
- vol->indexed_vertex_array.shrink_to_fit();
- }
- });
-
- BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - finalizing results";
- // Remove empty volumes from the newly added volumes.
- m_volumes.volumes.erase(
- std::remove_if(m_volumes.volumes.begin() + volumes_cnt_initial, m_volumes.volumes.end(),
- [](const GLVolume *volume) { return volume->empty(); }),
- m_volumes.volumes.end());
- for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i)
- m_volumes.volumes[i]->indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized);
-
- BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - end";
-}
-
void GLCanvas3D::load_gcode_preview(const GCodePreviewData& preview_data, const std::vector<std::string>& str_tool_colors)
{
if ((m_canvas != nullptr) && (m_print != nullptr))
@@ -2399,23 +2417,48 @@ void GLCanvas3D::load_gcode_preview(const GCodePreviewData& preview_data, const
_load_gcode_unretractions(preview_data);
if (m_volumes.empty())
- _3DScene::reset_legend_texture();
+ reset_legend_texture();
else
{
- _3DScene::generate_legend_texture(preview_data, tool_colors);
-
+ _generate_legend_texture(preview_data, tool_colors);
+
// removes empty volumes
m_volumes.volumes.erase(std::remove_if(m_volumes.volumes.begin(), m_volumes.volumes.end(),
[](const GLVolume* volume) { return volume->print_zs.empty(); }), m_volumes.volumes.end());
_load_shells();
}
+ _update_toolpath_volumes_outside_state();
}
_update_gcode_volumes_visibility(preview_data);
+ _show_warning_texture_if_needed();
}
}
+void GLCanvas3D::load_preview(const std::vector<std::string>& str_tool_colors)
+{
+ if (m_print == nullptr)
+ return;
+
+ _load_print_toolpaths();
+ _load_wipe_tower_toolpaths(str_tool_colors);
+ for (const PrintObject* object : m_print->objects)
+ {
+ if (object != nullptr)
+ _load_print_object_toolpaths(*object, str_tool_colors);
+ }
+
+ for (GLVolume* volume : m_volumes.volumes)
+ {
+ volume->is_extrusion_path = true;
+ }
+
+ _update_toolpath_volumes_outside_state();
+ _show_warning_texture_if_needed();
+ reset_legend_texture();
+}
+
void GLCanvas3D::register_on_viewport_changed_callback(void* callback)
{
if (callback != nullptr)
@@ -2512,6 +2555,18 @@ void GLCanvas3D::register_on_gizmo_scale_uniformly_callback(void* callback)
m_on_gizmo_scale_uniformly_callback.register_callback(callback);
}
+void GLCanvas3D::register_on_gizmo_rotate_callback(void* callback)
+{
+ if (callback != nullptr)
+ m_on_gizmo_rotate_callback.register_callback(callback);
+}
+
+void GLCanvas3D::register_on_update_geometry_info_callback(void* callback)
+{
+ if (callback != nullptr)
+ m_on_update_geometry_info_callback.register_callback(callback);
+}
+
void GLCanvas3D::bind_event_handlers()
{
if (m_canvas != nullptr)
@@ -2700,6 +2755,12 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
m_mouse.set_start_position_2D_as_invalid();
#endif
}
+ else if (evt.Leaving())
+ {
+ // to remove hover on objects when the mouse goes out of this canvas
+ m_mouse.position = Pointf(-1.0, -1.0);
+ m_dirty = true;
+ }
else if (evt.LeftDClick() && (m_hover_volume_id != -1))
m_on_double_click_callback.call();
else if (evt.LeftDown() || evt.RightDown())
@@ -2729,15 +2790,26 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
}
else if ((selected_object_idx != -1) && gizmos_overlay_contains_mouse)
{
+ update_gizmos_data();
m_gizmos.update_on_off_state(*this, m_mouse.position);
- _update_gizmos_data();
m_dirty = true;
}
else if ((selected_object_idx != -1) && m_gizmos.grabber_contains_mouse())
- {
- _update_gizmos_data();
+ {
+ update_gizmos_data();
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
{
@@ -2761,9 +2833,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
}
}
- if (m_gizmos.is_running())
- _update_gizmos_data();
-
+ update_gizmos_data();
+ m_gizmos.refresh();
m_dirty = true;
}
}
@@ -2786,7 +2857,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
if (volume_bbox.contains(pos3d))
{
// The dragging operation is initiated.
- m_mouse.drag.volume_idx = volume_idx;
+ m_mouse.drag.move_with_shift = evt.ShiftDown();
+ m_mouse.drag.move_volume_idx = volume_idx;
m_mouse.drag.start_position_3D = pos3d;
// Remember the shift to to the object center.The object center will later be used
// to limit the object placement close to the bed.
@@ -2802,7 +2874,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
}
}
}
- else if (evt.Dragging() && evt.LeftIsDown() && !gizmos_overlay_contains_mouse && (m_layers_editing.state == LayersEditing::Unknown) && (m_mouse.drag.volume_idx != -1))
+ else if (evt.Dragging() && evt.LeftIsDown() && !gizmos_overlay_contains_mouse && (m_layers_editing.state == LayersEditing::Unknown) && (m_mouse.drag.move_volume_idx != -1))
{
m_mouse.dragging = true;
@@ -2825,27 +2897,34 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
// Calculate the translation vector.
Vectorf3 vector = m_mouse.drag.start_position_3D.vector_to(cur_pos);
// Get the volume being dragged.
- GLVolume* volume = m_volumes.volumes[m_mouse.drag.volume_idx];
+ GLVolume* volume = m_volumes.volumes[m_mouse.drag.move_volume_idx];
// Get all volumes belonging to the same group, if any.
std::vector<GLVolume*> volumes;
- if (volume->drag_group_id == -1)
+ int group_id = m_mouse.drag.move_with_shift ? volume->select_group_id : volume->drag_group_id;
+ if (group_id == -1)
volumes.push_back(volume);
else
{
for (GLVolume* v : m_volumes.volumes)
{
- if ((v != nullptr) && (v->drag_group_id == volume->drag_group_id))
- volumes.push_back(v);
+ if (v != nullptr)
+ {
+ if ((m_mouse.drag.move_with_shift && (v->select_group_id == group_id)) || (!m_mouse.drag.move_with_shift && (v->drag_group_id == group_id)))
+ volumes.push_back(v);
+ }
}
}
// Apply new temporary volume origin and ignore Z.
for (GLVolume* v : volumes)
{
- v->origin.translate(vector.x, vector.y, 0.0);
+ Pointf3 origin = v->get_origin();
+ origin.translate(vector.x, vector.y, 0.0);
+ v->set_origin(origin);
}
m_mouse.drag.start_position_3D = cur_pos;
+ m_gizmos.refresh();
m_dirty = true;
}
@@ -2856,7 +2935,63 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
const Pointf3& cur_pos = _mouse_to_bed_3d(pos);
m_gizmos.update(Pointf(cur_pos.x, cur_pos.y));
- m_on_gizmo_scale_uniformly_callback.call((double)m_gizmos.get_scale());
+ std::vector<GLVolume*> volumes;
+ if (m_mouse.drag.gizmo_volume_idx != -1)
+ {
+ GLVolume* volume = m_volumes.volumes[m_mouse.drag.gizmo_volume_idx];
+ // Get all volumes belonging to the same group, if any.
+ if (volume->select_group_id == -1)
+ volumes.push_back(volume);
+ else
+ {
+ for (GLVolume* v : m_volumes.volumes)
+ {
+ if ((v != nullptr) && (v->select_group_id == volume->select_group_id))
+ volumes.push_back(v);
+ }
+ }
+ }
+
+ switch (m_gizmos.get_current_type())
+ {
+ case Gizmos::Scale:
+ {
+ // Apply new temporary scale factor
+ float scale_factor = m_gizmos.get_scale();
+ for (GLVolume* v : volumes)
+ {
+ v->set_scale_factor(scale_factor);
+ }
+ break;
+ }
+ case Gizmos::Rotate:
+ {
+ // Apply new temporary angle_z
+ float angle_z = m_gizmos.get_angle_z();
+ for (GLVolume* v : volumes)
+ {
+ v->set_angle_z(angle_z);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (!volumes.empty())
+ {
+ BoundingBoxf3 bb;
+ for (const GLVolume* volume : volumes)
+ {
+ bb.merge(volume->transformed_bounding_box());
+ }
+ const Pointf3& size = bb.size();
+ m_on_update_geometry_info_callback.call(size.x, size.y, size.z, m_gizmos.get_scale());
+ }
+
+ if ((m_gizmos.get_current_type() != Gizmos::Rotate) && (volumes.size() > 1))
+ m_gizmos.refresh();
+
m_dirty = true;
}
else if (evt.Dragging() && !gizmos_overlay_contains_mouse)
@@ -2914,24 +3049,28 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
if (layer_editing_object_idx != -1)
m_on_model_update_callback.call();
}
- else if ((m_mouse.drag.volume_idx != -1) && m_mouse.dragging)
+ else if ((m_mouse.drag.move_volume_idx != -1) && m_mouse.dragging)
{
// get all volumes belonging to the same group, if any
std::vector<int> volume_idxs;
- int vol_id = m_mouse.drag.volume_idx;
- int group_id = m_volumes.volumes[vol_id]->drag_group_id;
+ int vol_id = m_mouse.drag.move_volume_idx;
+ int group_id = m_mouse.drag.move_with_shift ? m_volumes.volumes[vol_id]->select_group_id : m_volumes.volumes[vol_id]->drag_group_id;
if (group_id == -1)
volume_idxs.push_back(vol_id);
else
{
for (int i = 0; i < (int)m_volumes.volumes.size(); ++i)
{
- if (m_volumes.volumes[i]->drag_group_id == group_id)
+ if ((m_mouse.drag.move_with_shift && (m_volumes.volumes[i]->select_group_id == group_id)) || (m_volumes.volumes[i]->drag_group_id == group_id))
volume_idxs.push_back(i);
}
}
_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())
{
@@ -2940,17 +3079,35 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
{
deselect_volumes();
_on_select(-1);
+ update_gizmos_data();
}
}
else if (evt.LeftUp() && m_gizmos.is_dragging())
{
+ switch (m_gizmos.get_current_type())
+ {
+ case Gizmos::Scale:
+ {
+ m_on_gizmo_scale_uniformly_callback.call((double)m_gizmos.get_scale());
+ break;
+ }
+ case Gizmos::Rotate:
+ {
+ m_on_gizmo_rotate_callback.call((double)m_gizmos.get_angle_z());
+ break;
+ }
+ default:
+ break;
+ }
m_gizmos.stop_dragging();
}
- m_mouse.drag.volume_idx = -1;
+ m_mouse.drag.move_volume_idx = -1;
+ m_mouse.drag.gizmo_volume_idx = -1;
m_mouse.set_start_position_3D_as_invalid();
m_mouse.set_start_position_2D_as_invalid();
m_mouse.dragging = false;
+ m_dirty = true;
}
else if (evt.Moving())
{
@@ -2978,7 +3135,13 @@ void GLCanvas3D::on_key_down(wxKeyEvent& evt)
if (key == WXK_DELETE)
m_on_remove_object_callback.call();
else
- evt.Skip();
+ {
+#ifdef __WXOSX__
+ if (key == WXK_BACK)
+ m_on_remove_object_callback.call();
+#endif
+ evt.Skip();
+ }
}
}
@@ -3002,6 +3165,14 @@ Point GLCanvas3D::get_local_mouse_position() const
return Point(mouse_pos.x, mouse_pos.y);
}
+void GLCanvas3D::reset_legend_texture()
+{
+ if (!set_current())
+ return;
+
+ m_legend_texture.reset();
+}
+
bool GLCanvas3D::_is_shown_on_screen() const
{
return (m_canvas != nullptr) ? m_canvas->IsShownOnScreen() : false;
@@ -3091,11 +3262,45 @@ BoundingBoxf3 GLCanvas3D::_max_bounding_box() const
BoundingBoxf3 GLCanvas3D::_selected_volumes_bounding_box() const
{
BoundingBoxf3 bb;
+
+ std::vector<const GLVolume*> selected_volumes;
for (const GLVolume* volume : m_volumes.volumes)
{
- if ((volume != nullptr) && volume->selected)
+ if ((volume != nullptr) && !volume->is_wipe_tower && volume->selected)
+ selected_volumes.push_back(volume);
+ }
+
+ bool use_drag_group_id = selected_volumes.size() > 1;
+ if (use_drag_group_id)
+ {
+ int drag_group_id = selected_volumes[0]->drag_group_id;
+ for (const GLVolume* volume : selected_volumes)
+ {
+ if (drag_group_id != volume->drag_group_id)
+ {
+ use_drag_group_id = false;
+ break;
+ }
+ }
+ }
+
+ if (use_drag_group_id)
+ {
+ for (const GLVolume* volume : selected_volumes)
+ {
+ bb.merge(volume->bounding_box);
+ }
+
+ bb = bb.transformed(selected_volumes[0]->world_matrix());
+ }
+ else
+ {
+ for (const GLVolume* volume : selected_volumes)
+ {
bb.merge(volume->transformed_bounding_box());
+ }
}
+
return bb;
}
@@ -3200,6 +3405,8 @@ void GLCanvas3D::_deregister_callbacks()
m_on_wipe_tower_moved_callback.deregister_callback();
m_on_enable_action_buttons_callback.deregister_callback();
m_on_gizmo_scale_uniformly_callback.deregister_callback();
+ m_on_gizmo_rotate_callback.deregister_callback();
+ m_on_update_geometry_info_callback.deregister_callback();
}
void GLCanvas3D::_mark_volumes_for_layer_height() const
@@ -3229,8 +3436,14 @@ void GLCanvas3D::_refresh_if_shown_on_screen()
{
const Size& cnv_size = get_canvas_size();
_resize((unsigned int)cnv_size.get_width(), (unsigned int)cnv_size.get_height());
- if (m_canvas != nullptr)
- m_canvas->Refresh();
+
+ // Because of performance problems on macOS, where PaintEvents are not delivered
+ // frequently enough, we call render() here directly when we can.
+ // We can't do that when m_force_zoom_to_bed_enabled == true, because then render()
+ // ends up calling back here via _force_zoom_to_bed(), causing a stack overflow.
+ if (m_canvas != nullptr) {
+ m_force_zoom_to_bed_enabled ? m_canvas->Refresh() : render();
+ }
}
}
@@ -3239,7 +3452,7 @@ void GLCanvas3D::_camera_tranform() const
::glMatrixMode(GL_MODELVIEW);
::glLoadIdentity();
- ::glRotatef(-m_camera.get_theta(), 1.0f, 0.0f, 0.0f); // pitch
+ ::glRotatef(-m_camera.get_theta(), 1.0f, 0.0f, 0.0f); // pitch
::glRotatef(m_camera.phi, 0.0f, 0.0f, 1.0f); // yaw
Pointf3 neg_target = m_camera.target.negative();
@@ -3259,8 +3472,8 @@ void GLCanvas3D::_picking_pass() const
if (m_multisample_allowed)
::glDisable(GL_MULTISAMPLE);
- ::glDisable(GL_LIGHTING);
::glDisable(GL_BLEND);
+ ::glEnable(GL_DEPTH_TEST);
::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
@@ -3270,20 +3483,22 @@ void GLCanvas3D::_picking_pass() const
if (m_multisample_allowed)
::glEnable(GL_MULTISAMPLE);
- const Size& cnv_size = get_canvas_size();
-
- GLubyte color[4];
- ::glReadPixels(pos.x, cnv_size.get_height() - pos.y - 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, (void*)color);
- int volume_id = color[0] + color[1] * 256 + color[2] * 256 * 256;
-
- m_hover_volume_id = -1;
-
+ int volume_id = -1;
for (GLVolume* vol : m_volumes.volumes)
{
vol->hover = false;
}
- if (volume_id < (int)m_volumes.volumes.size())
+ GLubyte color[4] = { 0, 0, 0, 0 };
+ const Size& cnv_size = get_canvas_size();
+ bool inside = (0 <= pos.x) && (pos.x < cnv_size.get_width()) && (0 <= pos.y) && (pos.y < cnv_size.get_height());
+ if (inside)
+ {
+ ::glReadPixels(pos.x, cnv_size.get_height() - pos.y - 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, (void*)color);
+ volume_id = color[0] + color[1] * 256 + color[2] * 256 * 256;
+ }
+
+ if ((0 <= volume_id) && (volume_id < (int)m_volumes.volumes.size()))
{
m_hover_volume_id = volume_id;
m_volumes.volumes[volume_id]->hover = true;
@@ -3299,7 +3514,10 @@ void GLCanvas3D::_picking_pass() const
m_gizmos.set_hover_id(-1);
}
else
- m_gizmos.set_hover_id(254 - (int)color[2]);
+ {
+ m_hover_volume_id = -1;
+ m_gizmos.set_hover_id(inside ? (254 - (int)color[2]) : -1);
+ }
// updates gizmos overlay
if (_get_first_selected_object_id() != -1)
@@ -3313,10 +3531,6 @@ void GLCanvas3D::_render_background() const
{
::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
- static const float COLOR[3] = { 10.0f / 255.0f, 98.0f / 255.0f, 144.0f / 255.0f };
-
- ::glDisable(GL_LIGHTING);
-
::glPushMatrix();
::glLoadIdentity();
::glMatrixMode(GL_PROJECTION);
@@ -3328,11 +3542,16 @@ void GLCanvas3D::_render_background() const
::glBegin(GL_QUADS);
::glColor3f(0.0f, 0.0f, 0.0f);
- ::glVertex3f(-1.0f, -1.0f, 1.0f);
- ::glVertex3f(1.0f, -1.0f, 1.0f);
- ::glColor3f(COLOR[0], COLOR[1], COLOR[2]);
- ::glVertex3f(1.0f, 1.0f, 1.0f);
- ::glVertex3f(-1.0f, 1.0f, 1.0f);
+ ::glVertex2f(-1.0f, -1.0f);
+ ::glVertex2f(1.0f, -1.0f);
+
+ if (m_dynamic_background_enabled && _is_any_volume_outside())
+ ::glColor3f(ERROR_BG_COLOR[0], ERROR_BG_COLOR[1], ERROR_BG_COLOR[2]);
+ else
+ ::glColor3f(DEFAULT_BG_COLOR[0], DEFAULT_BG_COLOR[1], DEFAULT_BG_COLOR[2]);
+
+ ::glVertex2f(1.0f, 1.0f);
+ ::glVertex2f(-1.0f, 1.0f);
::glEnd();
::glEnable(GL_DEPTH_TEST);
@@ -3371,7 +3590,7 @@ void GLCanvas3D::_render_objects() const
{
const BoundingBoxf3& bed_bb = m_bed.get_bounding_box();
m_volumes.set_print_box((float)bed_bb.min.x, (float)bed_bb.min.y, 0.0f, (float)bed_bb.max.x, (float)bed_bb.max.y, (float)m_config->opt_float("max_print_height"));
- m_volumes.check_outside_state(m_config);
+ m_volumes.check_outside_state(m_config, nullptr);
}
// do not cull backfaces to show broken geometry, if any
::glDisable(GL_CULL_FACE);
@@ -3395,6 +3614,8 @@ void GLCanvas3D::_render_objects() const
if (m_picking_enabled)
::glEnable(GL_CULL_FACE);
}
+
+ ::glDisable(GL_LIGHTING);
}
void GLCanvas3D::_render_cutting_plane() const
@@ -3408,11 +3629,11 @@ void GLCanvas3D::_render_warning_texture() const
return;
// If the warning texture has not been loaded into the GPU, do it now.
- unsigned int tex_id = _3DScene::finalize_warning_texture();
+ unsigned int tex_id = m_warning_texture.get_id();
if (tex_id > 0)
{
- unsigned int w = _3DScene::get_warning_texture_width();
- unsigned int h = _3DScene::get_warning_texture_height();
+ int w = m_warning_texture.get_width();
+ int h = m_warning_texture.get_height();
if ((w > 0) && (h > 0))
{
::glDisable(GL_DEPTH_TEST);
@@ -3441,11 +3662,11 @@ void GLCanvas3D::_render_legend_texture() const
return;
// If the legend texture has not been loaded into the GPU, do it now.
- unsigned int tex_id = _3DScene::finalize_legend_texture();
+ unsigned int tex_id = m_legend_texture.get_id();
if (tex_id > 0)
{
- unsigned int w = _3DScene::get_legend_texture_width();
- unsigned int h = _3DScene::get_legend_texture_height();
+ int w = m_legend_texture.get_width();
+ int h = m_legend_texture.get_height();
if ((w > 0) && (h > 0))
{
::glDisable(GL_DEPTH_TEST);
@@ -3459,6 +3680,7 @@ void GLCanvas3D::_render_legend_texture() const
float t = (0.5f * (float)cnv_size.get_height()) * inv_zoom;
float r = l + (float)w * inv_zoom;
float b = t - (float)h * inv_zoom;
+
GLTexture::render_texture(tex_id, l, r, b, t);
::glPopMatrix();
@@ -3503,9 +3725,7 @@ void GLCanvas3D::_render_volumes(bool fake_colors) const
{
static const GLfloat INV_255 = 1.0f / 255.0f;
- if (fake_colors)
- ::glDisable(GL_LIGHTING);
- else
+ if (!fake_colors)
::glEnable(GL_LIGHTING);
// do not cull backfaces to show broken geometry, if any
@@ -3543,11 +3763,14 @@ void GLCanvas3D::_render_volumes(bool fake_colors) const
::glDisable(GL_BLEND);
::glEnable(GL_CULL_FACE);
+
+ if (!fake_colors)
+ ::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
@@ -3668,6 +3891,402 @@ int GLCanvas3D::_get_first_selected_object_id() const
return -1;
}
+int GLCanvas3D::_get_first_selected_volume_id(int object_id) const
+{
+ int volume_id = -1;
+
+ for (const GLVolume* vol : m_volumes.volumes)
+ {
+ ++volume_id;
+ if ((vol != nullptr) && vol->selected && (object_id == vol->select_group_id / 1000000))
+ return volume_id;
+ }
+
+ return -1;
+}
+
+void GLCanvas3D::_load_print_toolpaths()
+{
+ // ensures this canvas is current
+ if (!set_current())
+ return;
+
+ if (m_print == nullptr)
+ return;
+
+ if (!m_print->state.is_done(psSkirt) || !m_print->state.is_done(psBrim))
+ return;
+
+ if (!m_print->has_skirt() && (m_print->config.brim_width.value == 0))
+ return;
+
+ const float color[] = { 0.5f, 1.0f, 0.5f, 1.0f }; // greenish
+
+ // number of skirt layers
+ size_t total_layer_count = 0;
+ for (const PrintObject* print_object : m_print->objects)
+ {
+ total_layer_count = std::max(total_layer_count, print_object->total_layer_count());
+ }
+ size_t skirt_height = m_print->has_infinite_skirt() ? total_layer_count : std::min<size_t>(m_print->config.skirt_height.value, total_layer_count);
+ if ((skirt_height == 0) && (m_print->config.brim_width.value > 0))
+ skirt_height = 1;
+
+ // get first skirt_height layers (maybe this should be moved to a PrintObject method?)
+ const PrintObject* object0 = m_print->objects.front();
+ std::vector<float> print_zs;
+ print_zs.reserve(skirt_height * 2);
+ for (size_t i = 0; i < std::min(skirt_height, object0->layers.size()); ++i)
+ {
+ print_zs.push_back(float(object0->layers[i]->print_z));
+ }
+ //FIXME why there are support layers?
+ for (size_t i = 0; i < std::min(skirt_height, object0->support_layers.size()); ++i)
+ {
+ print_zs.push_back(float(object0->support_layers[i]->print_z));
+ }
+ sort_remove_duplicates(print_zs);
+ if (print_zs.size() > skirt_height)
+ print_zs.erase(print_zs.begin() + skirt_height, print_zs.end());
+
+ m_volumes.volumes.emplace_back(new GLVolume(color));
+ GLVolume& volume = *m_volumes.volumes.back();
+ for (size_t i = 0; i < skirt_height; ++i) {
+ volume.print_zs.push_back(print_zs[i]);
+ volume.offsets.push_back(volume.indexed_vertex_array.quad_indices.size());
+ volume.offsets.push_back(volume.indexed_vertex_array.triangle_indices.size());
+ if (i == 0)
+ _3DScene::extrusionentity_to_verts(m_print->brim, print_zs[i], Point(0, 0), volume);
+
+ _3DScene::extrusionentity_to_verts(m_print->skirt, print_zs[i], Point(0, 0), volume);
+ }
+ volume.bounding_box = volume.indexed_vertex_array.bounding_box();
+ volume.indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized);
+}
+
+void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, const std::vector<std::string>& str_tool_colors)
+{
+ std::vector<float> tool_colors = _parse_colors(str_tool_colors);
+
+ struct Ctxt
+ {
+ const Points *shifted_copies;
+ std::vector<const Layer*> layers;
+ bool has_perimeters;
+ bool has_infill;
+ bool has_support;
+ const std::vector<float>* tool_colors;
+
+ // Number of vertices (each vertex is 6x4=24 bytes long)
+ static const size_t alloc_size_max() { return 131072; } // 3.15MB
+ // static const size_t alloc_size_max () { return 65536; } // 1.57MB
+ // static const size_t alloc_size_max () { return 32768; } // 786kB
+ static const size_t alloc_size_reserve() { return alloc_size_max() * 2; }
+
+ static const float* color_perimeters() { static float color[4] = { 1.0f, 1.0f, 0.0f, 1.f }; return color; } // yellow
+ static const float* color_infill() { static float color[4] = { 1.0f, 0.5f, 0.5f, 1.f }; return color; } // redish
+ static const float* color_support() { static float color[4] = { 0.5f, 1.0f, 0.5f, 1.f }; return color; } // greenish
+
+ // For cloring by a tool, return a parsed color.
+ bool color_by_tool() const { return tool_colors != nullptr; }
+ size_t number_tools() const { return this->color_by_tool() ? tool_colors->size() / 4 : 0; }
+ const float* color_tool(size_t tool) const { return tool_colors->data() + tool * 4; }
+ int volume_idx(int extruder, int feature) const
+ {
+ return this->color_by_tool() ? std::min<int>(this->number_tools() - 1, std::max<int>(extruder - 1, 0)) : feature;
+ }
+ } ctxt;
+
+ ctxt.shifted_copies = &print_object._shifted_copies;
+
+ // order layers by print_z
+ ctxt.layers.reserve(print_object.layers.size() + print_object.support_layers.size());
+ for (const Layer *layer : print_object.layers)
+ ctxt.layers.push_back(layer);
+ for (const Layer *layer : print_object.support_layers)
+ ctxt.layers.push_back(layer);
+ std::sort(ctxt.layers.begin(), ctxt.layers.end(), [](const Layer *l1, const Layer *l2) { return l1->print_z < l2->print_z; });
+
+ // Maximum size of an allocation block: 32MB / sizeof(float)
+ ctxt.has_perimeters = print_object.state.is_done(posPerimeters);
+ ctxt.has_infill = print_object.state.is_done(posInfill);
+ ctxt.has_support = print_object.state.is_done(posSupportMaterial);
+ ctxt.tool_colors = tool_colors.empty() ? nullptr : &tool_colors;
+
+ BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - start";
+
+ //FIXME Improve the heuristics for a grain size.
+ size_t grain_size = std::max(ctxt.layers.size() / 16, size_t(1));
+ tbb::spin_mutex new_volume_mutex;
+ auto new_volume = [this, &new_volume_mutex](const float *color) -> GLVolume* {
+ auto *volume = new GLVolume(color);
+ new_volume_mutex.lock();
+ m_volumes.volumes.emplace_back(volume);
+ new_volume_mutex.unlock();
+ return volume;
+ };
+ const size_t volumes_cnt_initial = m_volumes.volumes.size();
+ std::vector<GLVolumeCollection> volumes_per_thread(ctxt.layers.size());
+ tbb::parallel_for(
+ tbb::blocked_range<size_t>(0, ctxt.layers.size(), grain_size),
+ [&ctxt, &new_volume](const tbb::blocked_range<size_t>& range) {
+ std::vector<GLVolume*> vols;
+ if (ctxt.color_by_tool()) {
+ for (size_t i = 0; i < ctxt.number_tools(); ++i)
+ vols.emplace_back(new_volume(ctxt.color_tool(i)));
+ }
+ else
+ vols = { new_volume(ctxt.color_perimeters()), new_volume(ctxt.color_infill()), new_volume(ctxt.color_support()) };
+ for (GLVolume *vol : vols)
+ vol->indexed_vertex_array.reserve(ctxt.alloc_size_reserve());
+ for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++idx_layer) {
+ const Layer *layer = ctxt.layers[idx_layer];
+ for (size_t i = 0; i < vols.size(); ++i) {
+ GLVolume &vol = *vols[i];
+ if (vol.print_zs.empty() || vol.print_zs.back() != layer->print_z) {
+ vol.print_zs.push_back(layer->print_z);
+ vol.offsets.push_back(vol.indexed_vertex_array.quad_indices.size());
+ vol.offsets.push_back(vol.indexed_vertex_array.triangle_indices.size());
+ }
+ }
+ for (const Point &copy : *ctxt.shifted_copies) {
+ for (const LayerRegion *layerm : layer->regions) {
+ if (ctxt.has_perimeters)
+ _3DScene::extrusionentity_to_verts(layerm->perimeters, float(layer->print_z), copy,
+ *vols[ctxt.volume_idx(layerm->region()->config.perimeter_extruder.value, 0)]);
+ if (ctxt.has_infill) {
+ for (const ExtrusionEntity *ee : layerm->fills.entities) {
+ // fill represents infill extrusions of a single island.
+ const auto *fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
+ if (!fill->entities.empty())
+ _3DScene::extrusionentity_to_verts(*fill, float(layer->print_z), copy,
+ *vols[ctxt.volume_idx(
+ is_solid_infill(fill->entities.front()->role()) ?
+ layerm->region()->config.solid_infill_extruder :
+ layerm->region()->config.infill_extruder,
+ 1)]);
+ }
+ }
+ }
+ if (ctxt.has_support) {
+ const SupportLayer *support_layer = dynamic_cast<const SupportLayer*>(layer);
+ if (support_layer) {
+ for (const ExtrusionEntity *extrusion_entity : support_layer->support_fills.entities)
+ _3DScene::extrusionentity_to_verts(extrusion_entity, float(layer->print_z), copy,
+ *vols[ctxt.volume_idx(
+ (extrusion_entity->role() == erSupportMaterial) ?
+ support_layer->object()->config.support_material_extruder :
+ support_layer->object()->config.support_material_interface_extruder,
+ 2)]);
+ }
+ }
+ }
+ for (size_t i = 0; i < vols.size(); ++i) {
+ GLVolume &vol = *vols[i];
+ if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() / 6 > ctxt.alloc_size_max()) {
+ // Store the vertex arrays and restart their containers,
+ vols[i] = new_volume(vol.color);
+ GLVolume &vol_new = *vols[i];
+ // Assign the large pre-allocated buffers to the new GLVolume.
+ vol_new.indexed_vertex_array = std::move(vol.indexed_vertex_array);
+ // Copy the content back to the old GLVolume.
+ vol.indexed_vertex_array = vol_new.indexed_vertex_array;
+ // Finalize a bounding box of the old GLVolume.
+ vol.bounding_box = vol.indexed_vertex_array.bounding_box();
+ // Clear the buffers, but keep them pre-allocated.
+ vol_new.indexed_vertex_array.clear();
+ // Just make sure that clear did not clear the reserved memory.
+ vol_new.indexed_vertex_array.reserve(ctxt.alloc_size_reserve());
+ }
+ }
+ }
+ for (GLVolume *vol : vols) {
+ vol->bounding_box = vol->indexed_vertex_array.bounding_box();
+ vol->indexed_vertex_array.shrink_to_fit();
+ }
+ });
+
+ BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - finalizing results";
+ // Remove empty volumes from the newly added volumes.
+ m_volumes.volumes.erase(
+ std::remove_if(m_volumes.volumes.begin() + volumes_cnt_initial, m_volumes.volumes.end(),
+ [](const GLVolume *volume) { return volume->empty(); }),
+ m_volumes.volumes.end());
+ for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i)
+ m_volumes.volumes[i]->indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized);
+
+ BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - end";
+}
+
+void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector<std::string>& str_tool_colors)
+{
+ if ((m_print == nullptr) || m_print->m_wipe_tower_tool_changes.empty())
+ return;
+
+ if (!m_print->state.is_done(psWipeTower))
+ return;
+
+ std::vector<float> tool_colors = _parse_colors(str_tool_colors);
+
+ struct Ctxt
+ {
+ const Print *print;
+ const std::vector<float> *tool_colors;
+ WipeTower::xy wipe_tower_pos;
+ float wipe_tower_angle;
+
+ // Number of vertices (each vertex is 6x4=24 bytes long)
+ static const size_t alloc_size_max() { return 131072; } // 3.15MB
+ static const size_t alloc_size_reserve() { return alloc_size_max() * 2; }
+
+ static const float* color_support() { static float color[4] = { 0.5f, 1.0f, 0.5f, 1.f }; return color; } // greenish
+
+ // For cloring by a tool, return a parsed color.
+ bool color_by_tool() const { return tool_colors != nullptr; }
+ size_t number_tools() const { return this->color_by_tool() ? tool_colors->size() / 4 : 0; }
+ const float* color_tool(size_t tool) const { return tool_colors->data() + tool * 4; }
+ int volume_idx(int tool, int feature) const
+ {
+ return this->color_by_tool() ? std::min<int>(this->number_tools() - 1, std::max<int>(tool, 0)) : feature;
+ }
+
+ const std::vector<WipeTower::ToolChangeResult>& tool_change(size_t idx) {
+ return priming.empty() ?
+ ((idx == print->m_wipe_tower_tool_changes.size()) ? final : print->m_wipe_tower_tool_changes[idx]) :
+ ((idx == 0) ? priming : (idx == print->m_wipe_tower_tool_changes.size() + 1) ? final : print->m_wipe_tower_tool_changes[idx - 1]);
+ }
+ std::vector<WipeTower::ToolChangeResult> priming;
+ std::vector<WipeTower::ToolChangeResult> final;
+ } ctxt;
+
+ ctxt.print = m_print;
+ ctxt.tool_colors = tool_colors.empty() ? nullptr : &tool_colors;
+ if (m_print->m_wipe_tower_priming && m_print->config.single_extruder_multi_material_priming)
+ ctxt.priming.emplace_back(*m_print->m_wipe_tower_priming.get());
+ if (m_print->m_wipe_tower_final_purge)
+ ctxt.final.emplace_back(*m_print->m_wipe_tower_final_purge.get());
+
+ ctxt.wipe_tower_angle = ctxt.print->config.wipe_tower_rotation_angle.value/180.f * M_PI;
+ ctxt.wipe_tower_pos = WipeTower::xy(ctxt.print->config.wipe_tower_x.value, ctxt.print->config.wipe_tower_y.value);
+
+ BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - start";
+
+ //FIXME Improve the heuristics for a grain size.
+ size_t n_items = m_print->m_wipe_tower_tool_changes.size() + (ctxt.priming.empty() ? 0 : 1);
+ size_t grain_size = std::max(n_items / 128, size_t(1));
+ tbb::spin_mutex new_volume_mutex;
+ auto new_volume = [this, &new_volume_mutex](const float *color) -> GLVolume* {
+ auto *volume = new GLVolume(color);
+ new_volume_mutex.lock();
+ m_volumes.volumes.emplace_back(volume);
+ new_volume_mutex.unlock();
+ return volume;
+ };
+ const size_t volumes_cnt_initial = m_volumes.volumes.size();
+ std::vector<GLVolumeCollection> volumes_per_thread(n_items);
+ tbb::parallel_for(
+ tbb::blocked_range<size_t>(0, n_items, grain_size),
+ [&ctxt, &new_volume](const tbb::blocked_range<size_t>& range) {
+ // Bounding box of this slab of a wipe tower.
+ std::vector<GLVolume*> vols;
+ if (ctxt.color_by_tool()) {
+ for (size_t i = 0; i < ctxt.number_tools(); ++i)
+ vols.emplace_back(new_volume(ctxt.color_tool(i)));
+ }
+ else
+ vols = { new_volume(ctxt.color_support()) };
+ for (GLVolume *volume : vols)
+ volume->indexed_vertex_array.reserve(ctxt.alloc_size_reserve());
+ for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++idx_layer) {
+ const std::vector<WipeTower::ToolChangeResult> &layer = ctxt.tool_change(idx_layer);
+ for (size_t i = 0; i < vols.size(); ++i) {
+ GLVolume &vol = *vols[i];
+ if (vol.print_zs.empty() || vol.print_zs.back() != layer.front().print_z) {
+ vol.print_zs.push_back(layer.front().print_z);
+ vol.offsets.push_back(vol.indexed_vertex_array.quad_indices.size());
+ vol.offsets.push_back(vol.indexed_vertex_array.triangle_indices.size());
+ }
+ }
+ for (const WipeTower::ToolChangeResult &extrusions : layer) {
+ for (size_t i = 1; i < extrusions.extrusions.size();) {
+ const WipeTower::Extrusion &e = extrusions.extrusions[i];
+ if (e.width == 0.) {
+ ++i;
+ continue;
+ }
+ size_t j = i + 1;
+ if (ctxt.color_by_tool())
+ for (; j < extrusions.extrusions.size() && extrusions.extrusions[j].tool == e.tool && extrusions.extrusions[j].width > 0.f; ++j);
+ else
+ for (; j < extrusions.extrusions.size() && extrusions.extrusions[j].width > 0.f; ++j);
+ size_t n_lines = j - i;
+ Lines lines;
+ std::vector<double> widths;
+ std::vector<double> heights;
+ lines.reserve(n_lines);
+ widths.reserve(n_lines);
+ heights.assign(n_lines, extrusions.layer_height);
+ WipeTower::Extrusion e_prev = extrusions.extrusions[i-1];
+
+ if (!extrusions.priming) { // wipe tower extrusions describe the wipe tower at the origin with no rotation
+ e_prev.pos.rotate(ctxt.wipe_tower_angle);
+ e_prev.pos.translate(ctxt.wipe_tower_pos);
+ }
+
+ for (; i < j; ++i) {
+ WipeTower::Extrusion e = extrusions.extrusions[i];
+ assert(e.width > 0.f);
+ if (!extrusions.priming) {
+ e.pos.rotate(ctxt.wipe_tower_angle);
+ e.pos.translate(ctxt.wipe_tower_pos);
+ }
+
+ lines.emplace_back(Point::new_scale(e_prev.pos.x, e_prev.pos.y), Point::new_scale(e.pos.x, e.pos.y));
+ widths.emplace_back(e.width);
+
+ e_prev = e;
+ }
+ _3DScene::thick_lines_to_verts(lines, widths, heights, lines.front().a == lines.back().b, extrusions.print_z,
+ *vols[ctxt.volume_idx(e.tool, 0)]);
+ }
+ }
+ }
+ for (size_t i = 0; i < vols.size(); ++i) {
+ GLVolume &vol = *vols[i];
+ if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() / 6 > ctxt.alloc_size_max()) {
+ // Store the vertex arrays and restart their containers,
+ vols[i] = new_volume(vol.color);
+ GLVolume &vol_new = *vols[i];
+ // Assign the large pre-allocated buffers to the new GLVolume.
+ vol_new.indexed_vertex_array = std::move(vol.indexed_vertex_array);
+ // Copy the content back to the old GLVolume.
+ vol.indexed_vertex_array = vol_new.indexed_vertex_array;
+ // Finalize a bounding box of the old GLVolume.
+ vol.bounding_box = vol.indexed_vertex_array.bounding_box();
+ // Clear the buffers, but keep them pre-allocated.
+ vol_new.indexed_vertex_array.clear();
+ // Just make sure that clear did not clear the reserved memory.
+ vol_new.indexed_vertex_array.reserve(ctxt.alloc_size_reserve());
+ }
+ }
+ for (GLVolume *vol : vols) {
+ vol->bounding_box = vol->indexed_vertex_array.bounding_box();
+ vol->indexed_vertex_array.shrink_to_fit();
+ }
+ });
+
+ BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - finalizing results";
+ // Remove empty volumes from the newly added volumes.
+ m_volumes.volumes.erase(
+ std::remove_if(m_volumes.volumes.begin() + volumes_cnt_initial, m_volumes.volumes.end(),
+ [](const GLVolume *volume) { return volume->empty(); }),
+ m_volumes.volumes.end());
+ for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i)
+ m_volumes.volumes[i]->indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized);
+
+ BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - end";
+}
+
static inline int hex_digit_to_int(const char c)
{
return
@@ -3786,6 +4405,7 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat
if (volume != nullptr)
{
filter.volume = volume;
+ volume->is_extrusion_path = true;
m_volumes.volumes.emplace_back(volume);
}
else
@@ -4183,8 +4803,11 @@ void GLCanvas3D::_load_shells()
const PrintConfig& config = m_print->config;
unsigned int extruders_count = config.nozzle_diameter.size();
if ((extruders_count > 1) && config.single_extruder_multi_material && config.wipe_tower && !config.complete_objects) {
- const float width_per_extruder = 15.0f; // a simple workaround after wipe_tower_per_color_wipe got obsolete
- m_volumes.load_wipe_tower_preview(1000, config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, width_per_extruder * (extruders_count - 1), max_z, config.wipe_tower_rotation_angle, m_use_VBOs && m_initialized);
+ float depth = m_print->get_wipe_tower_depth();
+ if (!m_print->state.is_done(psWipeTower))
+ depth = (900.f/config.wipe_tower_width) * (float)(extruders_count - 1) ;
+ m_volumes.load_wipe_tower_preview(1000, config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, depth, max_z, config.wipe_tower_rotation_angle,
+ m_use_VBOs && m_initialized, !m_print->state.is_done(psWipeTower), m_print->config.nozzle_diameter.values[0] * 1.25f * 4.5f);
}
}
@@ -4199,7 +4822,6 @@ void GLCanvas3D::_update_gcode_volumes_visibility(const GCodePreviewData& previe
for (std::vector<GLVolume*>::iterator it = begin; it != end; ++it)
{
GLVolume* volume = *it;
- volume->outside_printer_detection_enabled = false;
switch (m_gcode_preview_volume_index.first_volumes[i].type)
{
@@ -4247,12 +4869,51 @@ void GLCanvas3D::_update_gcode_volumes_visibility(const GCodePreviewData& previe
}
}
+void GLCanvas3D::_update_toolpath_volumes_outside_state()
+{
+ // tolerance to avoid false detection at bed edges
+ static const coordf_t tolerance_x = 0.05;
+ static const coordf_t tolerance_y = 0.05;
+
+ BoundingBoxf3 print_volume;
+ if (m_config != nullptr)
+ {
+ const ConfigOptionPoints* opt = dynamic_cast<const ConfigOptionPoints*>(m_config->option("bed_shape"));
+ if (opt != nullptr)
+ {
+ BoundingBox bed_box_2D = get_extents(Polygon::new_scale(opt->values));
+ print_volume = BoundingBoxf3(Pointf3(unscale(bed_box_2D.min.x) - tolerance_x, unscale(bed_box_2D.min.y) - tolerance_y, 0.0), Pointf3(unscale(bed_box_2D.max.x) + tolerance_x, unscale(bed_box_2D.max.y) + tolerance_y, m_config->opt_float("max_print_height")));
+ // Allow the objects to protrude below the print bed
+ print_volume.min.z = -1e10;
+ }
+ }
+
+ for (GLVolume* volume : m_volumes.volumes)
+ {
+ volume->is_outside = ((print_volume.radius() > 0.0) && volume->is_extrusion_path) ? !print_volume.contains(volume->bounding_box) : false;
+ }
+}
+
+void GLCanvas3D::_show_warning_texture_if_needed()
+{
+ if (_is_any_volume_outside())
+ {
+ enable_warning_texture(true);
+ _generate_warning_texture(L("Detected toolpath outside print volume"));
+ }
+ else
+ {
+ enable_warning_texture(false);
+ _reset_warning_texture();
+ }
+}
+
void GLCanvas3D::_on_move(const std::vector<int>& volume_idxs)
{
if (m_model == nullptr)
return;
- std::set<std::string> done; // prevent moving instances twice
+ std::set<std::string> done; // prevent moving instances twice
bool object_moved = false;
Pointf3 wipe_tower_origin(0.0, 0.0, 0.0);
for (int volume_idx : volume_idxs)
@@ -4261,7 +4922,7 @@ void GLCanvas3D::_on_move(const std::vector<int>& volume_idxs)
int obj_idx = volume->object_idx();
int instance_idx = volume->instance_idx();
- // prevent moving instances twice
+ // prevent moving instances twice
char done_id[64];
::sprintf(done_id, "%d_%d", obj_idx, instance_idx);
if (done.find(done_id) != done.end())
@@ -4273,13 +4934,14 @@ void GLCanvas3D::_on_move(const std::vector<int>& volume_idxs)
{
// Move a regular object.
ModelObject* model_object = m_model->objects[obj_idx];
- model_object->instances[instance_idx]->offset.translate(volume->origin.x, volume->origin.y);
+ const Pointf3& origin = volume->get_origin();
+ model_object->instances[instance_idx]->offset = Pointf(origin.x, origin.y);
model_object->invalidate_bounding_box();
object_moved = true;
}
else if (obj_idx == 1000)
// Move a wipe tower proxy.
- wipe_tower_origin = volume->origin;
+ wipe_tower_origin = volume->get_origin();
}
if (object_moved)
@@ -4302,21 +4964,6 @@ void GLCanvas3D::_on_select(int volume_idx)
m_on_select_object_callback.call(id);
}
-void GLCanvas3D::_update_gizmos_data()
-{
- int id = _get_first_selected_object_id();
- if ((id != -1) && (m_model != nullptr))
- {
- ModelObject* model_object = m_model->objects[id];
- if (model_object != nullptr)
- {
- ModelInstance* model_instance = model_object->instances[0];
- if (model_instance != nullptr)
- m_gizmos.update_data(model_instance->scaling_factor);
- }
- }
-}
-
std::vector<float> GLCanvas3D::_parse_colors(const std::vector<std::string>& colors)
{
static const float INV_255 = 1.0f / 255.0f;
@@ -4342,5 +4989,40 @@ std::vector<float> GLCanvas3D::_parse_colors(const std::vector<std::string>& col
return output;
}
+void GLCanvas3D::_generate_legend_texture(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors)
+{
+ if (!set_current())
+ return;
+
+ m_legend_texture.generate(preview_data, tool_colors);
+}
+
+void GLCanvas3D::_generate_warning_texture(const std::string& msg)
+{
+ if (!set_current())
+ return;
+
+ m_warning_texture.generate(msg);
+}
+
+void GLCanvas3D::_reset_warning_texture()
+{
+ if (!set_current())
+ return;
+
+ m_warning_texture.reset();
+}
+
+bool GLCanvas3D::_is_any_volume_outside() const
+{
+ for (const GLVolume* volume : m_volumes.volumes)
+ {
+ if ((volume != nullptr) && volume->is_outside)
+ return true;
+ }
+
+ return false;
+}
+
} // namespace GUI
} // namespace Slic3r
diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp
index b0706a05d..f09bd4b20 100644
--- a/xs/src/slic3r/GUI/GLCanvas3D.hpp
+++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp
@@ -162,7 +162,8 @@ public:
bool is_custom() const;
const Pointfs& get_shape() const;
- void set_shape(const Pointfs& shape);
+ // Return true if the bed shape changed, so the calee will update the UI.
+ bool set_shape(const Pointfs& shape);
const BoundingBoxf3& get_bounding_box() const;
bool contains(const Point& point) const;
@@ -225,6 +226,7 @@ public:
void stop_using() const;
void set_uniform(const std::string& name, float value) const;
+ void set_uniform(const std::string& name, const float* matrix) const;
const GLShader* get_shader() const;
@@ -302,7 +304,10 @@ public:
Point start_position_2D;
Pointf3 start_position_3D;
Vectorf3 volume_center_offset;
- int volume_idx;
+
+ bool move_with_shift;
+ int move_volume_idx;
+ int gizmo_volume_idx;
public:
Drag();
@@ -323,6 +328,7 @@ public:
class Gizmos
{
+ static const float OverlayTexturesScale;
static const float OverlayOffsetX;
static const float OverlayGapY;
@@ -332,8 +338,13 @@ public:
Undefined,
Scale,
Rotate,
+ Flatten,
Num_Types
};
+ enum RenderOrder : unsigned char {
+ BeforeBed,
+ AfterBed
+ };
private:
bool m_enabled;
@@ -360,16 +371,26 @@ public:
bool overlay_contains_mouse(const GLCanvas3D& canvas, const Pointf& mouse_pos) const;
bool grabber_contains_mouse() const;
void update(const Pointf& mouse_pos);
- void update_data(float scale);
+ void refresh();
+
+ EType get_current_type() const;
bool is_running() const;
+
bool is_dragging() const;
void start_dragging();
void stop_dragging();
float get_scale() const;
+ void set_scale(float scale);
- void render(const GLCanvas3D& canvas, const BoundingBoxf3& box) const;
+ float get_angle_z() const;
+ void set_angle_z(float angle_z);
+
+ 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:
@@ -382,9 +403,35 @@ public:
GLGizmoBase* _get_current() const;
};
+ class WarningTexture : public GUI::GLTexture
+ {
+ static const unsigned char Background_Color[3];
+ static const unsigned char Opacity;
+
+ public:
+ bool generate(const std::string& msg);
+ };
+
+ class LegendTexture : public GUI::GLTexture
+ {
+ static const int Px_Title_Offset = 5;
+ static const int Px_Text_Offset = 5;
+ static const int Px_Square = 20;
+ static const int Px_Square_Contour = 1;
+ static const int Px_Border = Px_Square / 2;
+ static const unsigned char Squares_Border_Color[3];
+ static const unsigned char Background_Color[3];
+ static const unsigned char Opacity;
+
+ public:
+ bool generate(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors);
+ };
+
private:
wxGLCanvas* m_canvas;
wxGLContext* m_context;
+ LegendTexture m_legend_texture;
+ WarningTexture m_warning_texture;
wxTimer* m_timer;
Camera m_camera;
Bed m_bed;
@@ -411,6 +458,7 @@ private:
bool m_picking_enabled;
bool m_moving_enabled;
bool m_shader_enabled;
+ bool m_dynamic_background_enabled;
bool m_multisample_allowed;
std::string m_color_by;
@@ -439,6 +487,8 @@ private:
PerlCallback m_on_wipe_tower_moved_callback;
PerlCallback m_on_enable_action_buttons_callback;
PerlCallback m_on_gizmo_scale_uniformly_callback;
+ PerlCallback m_on_gizmo_rotate_callback;
+ PerlCallback m_on_update_geometry_info_callback;
public:
GLCanvas3D(wxGLCanvas* canvas);
@@ -455,7 +505,7 @@ public:
void deselect_volumes();
void select_volume(unsigned int id);
void update_volumes_selection(const std::vector<int>& selections);
- bool check_volumes_outside_state(const DynamicPrintConfig* config) const;
+ int check_volumes_outside_state(const DynamicPrintConfig* config) const;
bool move_volume_up(unsigned int id);
bool move_volume_down(unsigned int id);
@@ -499,6 +549,7 @@ public:
void enable_gizmos(bool enable);
void enable_shader(bool enable);
void enable_force_zoom_to_bed(bool enable);
+ void enable_dynamic_background(bool enable);
void allow_multisample(bool allow);
void zoom_to_bed();
@@ -507,6 +558,7 @@ public:
void set_viewport_from_scene(const GLCanvas3D& other);
void update_volumes_colors_by_extruder();
+ void update_gizmos_data();
void render();
@@ -518,16 +570,8 @@ public:
void reload_scene(bool force);
- // Create 3D thick extrusion lines for a skirt and brim.
- // Adds a new Slic3r::GUI::3DScene::Volume to volumes.
- void load_print_toolpaths();
- // Create 3D thick extrusion lines for object forming extrusions.
- // Adds a new Slic3r::GUI::3DScene::Volume to $self->volumes,
- // one for perimeters, one for infill and one for supports.
- void load_print_object_toolpaths(const PrintObject& print_object, const std::vector<std::string>& str_tool_colors);
- // Create 3D thick extrusion lines for wipe tower extrusions
- void load_wipe_tower_toolpaths(const std::vector<std::string>& str_tool_colors);
void load_gcode_preview(const GCodePreviewData& preview_data, const std::vector<std::string>& str_tool_colors);
+ void load_preview(const std::vector<std::string>& str_tool_colors);
void register_on_viewport_changed_callback(void* callback);
void register_on_double_click_callback(void* callback);
@@ -545,6 +589,8 @@ public:
void register_on_wipe_tower_moved_callback(void* callback);
void register_on_enable_action_buttons_callback(void* callback);
void register_on_gizmo_scale_uniformly_callback(void* callback);
+ void register_on_gizmo_rotate_callback(void* callback);
+ void register_on_update_geometry_info_callback(void* callback);
void bind_event_handlers();
void unbind_event_handlers();
@@ -561,6 +607,8 @@ public:
Size get_canvas_size() const;
Point get_local_mouse_position() const;
+ void reset_legend_texture();
+
private:
bool _is_shown_on_screen() const;
void _force_zoom_to_bed();
@@ -589,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);
@@ -605,6 +653,17 @@ private:
void _stop_timer();
int _get_first_selected_object_id() const;
+ int _get_first_selected_volume_id(int object_id) const;
+
+ // Create 3D thick extrusion lines for a skirt and brim.
+ // Adds a new Slic3r::GUI::3DScene::Volume to volumes.
+ void _load_print_toolpaths();
+ // Create 3D thick extrusion lines for object forming extrusions.
+ // Adds a new Slic3r::GUI::3DScene::Volume to $self->volumes,
+ // one for perimeters, one for infill and one for supports.
+ void _load_print_object_toolpaths(const PrintObject& print_object, const std::vector<std::string>& str_tool_colors);
+ // Create 3D thick extrusion lines for wipe tower extrusions
+ void _load_wipe_tower_toolpaths(const std::vector<std::string>& str_tool_colors);
// generates gcode extrusion paths geometry
void _load_gcode_extrusion_paths(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors);
@@ -621,11 +680,20 @@ private:
void _load_shells();
// sets gcode geometry visibility according to user selection
void _update_gcode_volumes_visibility(const GCodePreviewData& preview_data);
+ void _update_toolpath_volumes_outside_state();
+ void _show_warning_texture_if_needed();
void _on_move(const std::vector<int>& volume_idxs);
void _on_select(int volume_idx);
- void _update_gizmos_data();
+ // generates the legend texture in dependence of the current shown view type
+ void _generate_legend_texture(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors);
+
+ // generates a warning texture containing the given message
+ void _generate_warning_texture(const std::string& msg);
+ void _reset_warning_texture();
+
+ bool _is_any_volume_outside() const;
static std::vector<float> _parse_colors(const std::vector<std::string>& colors);
};
diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp
index ec4ac1606..5249f6dc4 100644
--- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp
+++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp
@@ -237,7 +237,7 @@ void GLCanvas3DManager::update_volumes_selection(wxGLCanvas* canvas, const std::
it->second->update_volumes_selection(selections);
}
-bool GLCanvas3DManager::check_volumes_outside_state(wxGLCanvas* canvas, const DynamicPrintConfig* config) const
+int GLCanvas3DManager::check_volumes_outside_state(wxGLCanvas* canvas, const DynamicPrintConfig* config) const
{
CanvasesMap::const_iterator it = _get_canvas(canvas);
return (it != m_canvases.end()) ? it->second->check_volumes_outside_state(config) : false;
@@ -418,6 +418,13 @@ void GLCanvas3DManager::enable_force_zoom_to_bed(wxGLCanvas* canvas, bool enable
it->second->enable_force_zoom_to_bed(enable);
}
+void GLCanvas3DManager::enable_dynamic_background(wxGLCanvas* canvas, bool enable)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->enable_dynamic_background(enable);
+}
+
void GLCanvas3DManager::allow_multisample(wxGLCanvas* canvas, bool allow)
{
CanvasesMap::iterator it = _get_canvas(canvas);
@@ -464,6 +471,13 @@ void GLCanvas3DManager::update_volumes_colors_by_extruder(wxGLCanvas* canvas)
it->second->update_volumes_colors_by_extruder();
}
+void GLCanvas3DManager::update_gizmos_data(wxGLCanvas* canvas)
+{
+ CanvasesMap::const_iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->update_gizmos_data();
+}
+
void GLCanvas3DManager::render(wxGLCanvas* canvas) const
{
CanvasesMap::const_iterator it = _get_canvas(canvas);
@@ -509,38 +523,30 @@ void GLCanvas3DManager::reload_scene(wxGLCanvas* canvas, bool force)
it->second->reload_scene(force);
}
-void GLCanvas3DManager::load_print_toolpaths(wxGLCanvas* canvas)
-{
- CanvasesMap::iterator it = _get_canvas(canvas);
- if (it != m_canvases.end())
- it->second->load_print_toolpaths();
-}
-
-void GLCanvas3DManager::load_print_object_toolpaths(wxGLCanvas* canvas, const PrintObject* print_object, const std::vector<std::string>& tool_colors)
+void GLCanvas3DManager::load_gcode_preview(wxGLCanvas* canvas, const GCodePreviewData* preview_data, const std::vector<std::string>& str_tool_colors)
{
- if (print_object == nullptr)
+ if (preview_data == nullptr)
return;
CanvasesMap::iterator it = _get_canvas(canvas);
if (it != m_canvases.end())
- it->second->load_print_object_toolpaths(*print_object, tool_colors);
+ it->second->load_gcode_preview(*preview_data, str_tool_colors);
}
-void GLCanvas3DManager::load_wipe_tower_toolpaths(wxGLCanvas* canvas, const std::vector<std::string>& str_tool_colors)
+void GLCanvas3DManager::load_preview(wxGLCanvas* canvas, const std::vector<std::string>& str_tool_colors)
{
CanvasesMap::iterator it = _get_canvas(canvas);
if (it != m_canvases.end())
- it->second->load_wipe_tower_toolpaths(str_tool_colors);
+ it->second->load_preview(str_tool_colors);
}
-void GLCanvas3DManager::load_gcode_preview(wxGLCanvas* canvas, const GCodePreviewData* preview_data, const std::vector<std::string>& str_tool_colors)
+void GLCanvas3DManager::reset_legend_texture()
{
- if (preview_data == nullptr)
- return;
-
- CanvasesMap::iterator it = _get_canvas(canvas);
- if (it != m_canvases.end())
- it->second->load_gcode_preview(*preview_data, str_tool_colors);
+ for (CanvasesMap::value_type& canvas : m_canvases)
+ {
+ if (canvas.second != nullptr)
+ canvas.second->reset_legend_texture();
+ }
}
void GLCanvas3DManager::register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback)
@@ -655,6 +661,20 @@ void GLCanvas3DManager::register_on_gizmo_scale_uniformly_callback(wxGLCanvas* c
it->second->register_on_gizmo_scale_uniformly_callback(callback);
}
+void GLCanvas3DManager::register_on_gizmo_rotate_callback(wxGLCanvas* canvas, void* callback)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->register_on_gizmo_rotate_callback(callback);
+}
+
+void GLCanvas3DManager::register_on_update_geometry_info_callback(wxGLCanvas* canvas, void* callback)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->register_on_update_geometry_info_callback(callback);
+}
+
GLCanvas3DManager::CanvasesMap::iterator GLCanvas3DManager::_get_canvas(wxGLCanvas* canvas)
{
return (canvas == nullptr) ? m_canvases.end() : m_canvases.find(canvas);
diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp
index 9d9285601..55c42fda6 100644
--- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp
+++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp
@@ -75,7 +75,7 @@ public:
void deselect_volumes(wxGLCanvas* canvas);
void select_volume(wxGLCanvas* canvas, unsigned int id);
void update_volumes_selection(wxGLCanvas* canvas, const std::vector<int>& selections);
- bool check_volumes_outside_state(wxGLCanvas* canvas, const DynamicPrintConfig* config) const;
+ int check_volumes_outside_state(wxGLCanvas* canvas, const DynamicPrintConfig* config) const;
bool move_volume_up(wxGLCanvas* canvas, unsigned int id);
bool move_volume_down(wxGLCanvas* canvas, unsigned int id);
@@ -112,6 +112,7 @@ public:
void enable_gizmos(wxGLCanvas* canvas, bool enable);
void enable_shader(wxGLCanvas* canvas, bool enable);
void enable_force_zoom_to_bed(wxGLCanvas* canvas, bool enable);
+ void enable_dynamic_background(wxGLCanvas* canvas, bool enable);
void allow_multisample(wxGLCanvas* canvas, bool allow);
void zoom_to_bed(wxGLCanvas* canvas);
@@ -120,6 +121,7 @@ public:
void set_viewport_from_scene(wxGLCanvas* canvas, wxGLCanvas* other);
void update_volumes_colors_by_extruder(wxGLCanvas* canvas);
+ void update_gizmos_data(wxGLCanvas* canvas);
void render(wxGLCanvas* canvas) const;
@@ -131,10 +133,10 @@ public:
void reload_scene(wxGLCanvas* canvas, bool force);
- void load_print_toolpaths(wxGLCanvas* canvas);
- void load_print_object_toolpaths(wxGLCanvas* canvas, const PrintObject* print_object, const std::vector<std::string>& tool_colors);
- void load_wipe_tower_toolpaths(wxGLCanvas* canvas, const std::vector<std::string>& str_tool_colors);
void load_gcode_preview(wxGLCanvas* canvas, const GCodePreviewData* preview_data, const std::vector<std::string>& str_tool_colors);
+ void load_preview(wxGLCanvas* canvas, const std::vector<std::string>& str_tool_colors);
+
+ void reset_legend_texture();
void register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback);
void register_on_double_click_callback(wxGLCanvas* canvas, void* callback);
@@ -152,6 +154,8 @@ public:
void register_on_wipe_tower_moved_callback(wxGLCanvas* canvas, void* callback);
void register_on_enable_action_buttons_callback(wxGLCanvas* canvas, void* callback);
void register_on_gizmo_scale_uniformly_callback(wxGLCanvas* canvas, void* callback);
+ void register_on_gizmo_rotate_callback(wxGLCanvas* canvas, void* callback);
+ void register_on_update_geometry_info_callback(wxGLCanvas* canvas, void* callback);
private:
CanvasesMap::iterator _get_canvas(wxGLCanvas* canvas);
diff --git a/xs/src/slic3r/GUI/GLGizmo.cpp b/xs/src/slic3r/GUI/GLGizmo.cpp
index d3aae33e8..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 {
@@ -90,9 +93,10 @@ GLGizmoBase::EState GLGizmoBase::get_state() const
void GLGizmoBase::set_state(GLGizmoBase::EState state)
{
m_state = state;
+ on_set_state();
}
-unsigned int GLGizmoBase::get_textures_id() const
+unsigned int GLGizmoBase::get_texture_id() const
{
return m_textures[m_state].get_id();
}
@@ -109,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;
}
@@ -118,12 +122,22 @@ void GLGizmoBase::start_dragging()
on_start_dragging();
}
+void GLGizmoBase::stop_dragging()
+{
+ on_stop_dragging();
+}
+
void GLGizmoBase::update(const Pointf& mouse_pos)
{
if (m_hover_id != -1)
on_update(mouse_pos);
}
+void GLGizmoBase::refresh()
+{
+ on_refresh();
+}
+
void GLGizmoBase::render(const BoundingBoxf3& box) const
{
on_render(box);
@@ -134,13 +148,29 @@ void GLGizmoBase::render_for_picking(const BoundingBoxf3& box) const
on_render_for_picking(box);
}
+void GLGizmoBase::on_set_state()
+{
+ // do nothing
+}
+
void GLGizmoBase::on_start_dragging()
{
+ // do nothing
+}
+
+void GLGizmoBase::on_stop_dragging()
+{
+ // do nothing
+}
+
+void GLGizmoBase::on_refresh()
+{
+ // do nothing
}
void GLGizmoBase::render_grabbers() const
{
- for (unsigned int i = 0; i < (unsigned int)m_grabbers.size(); ++i)
+ for (int i = 0; i < (int)m_grabbers.size(); ++i)
{
m_grabbers[i].render(m_hover_id == i);
}
@@ -162,7 +192,21 @@ GLGizmoRotate::GLGizmoRotate()
, m_angle_z(0.0f)
, m_center(Pointf(0.0, 0.0))
, m_radius(0.0f)
+ , m_keep_initial_values(false)
+{
+}
+
+float GLGizmoRotate::get_angle_z() const
{
+ return m_angle_z;
+}
+
+void GLGizmoRotate::set_angle_z(float angle_z)
+{
+ if (std::abs(angle_z - 2.0f * PI) < EPSILON)
+ angle_z = 0.0f;
+
+ m_angle_z = angle_z;
}
bool GLGizmoRotate::on_init()
@@ -186,6 +230,11 @@ bool GLGizmoRotate::on_init()
return true;
}
+void GLGizmoRotate::on_set_state()
+{
+ m_keep_initial_values = (m_state == On) ? false : true;
+}
+
void GLGizmoRotate::on_update(const Pointf& mouse_pos)
{
Vectorf orig_dir(1.0, 0.0);
@@ -194,6 +243,7 @@ void GLGizmoRotate::on_update(const Pointf& mouse_pos)
if (cross(orig_dir, new_dir) < 0.0)
theta = 2.0 * (coordf_t)PI - theta;
+ // snap
if (length(m_center.vector_to(mouse_pos)) < 2.0 * (double)m_radius / 3.0)
{
coordf_t step = 2.0 * (coordf_t)PI / (coordf_t)SnapRegionsCount;
@@ -202,18 +252,26 @@ void GLGizmoRotate::on_update(const Pointf& mouse_pos)
if (theta == 2.0 * (coordf_t)PI)
theta = 0.0;
-
+
m_angle_z = (float)theta;
}
+void GLGizmoRotate::on_refresh()
+{
+ m_keep_initial_values = false;
+}
+
void GLGizmoRotate::on_render(const BoundingBoxf3& box) const
{
- ::glDisable(GL_LIGHTING);
::glDisable(GL_DEPTH_TEST);
- const Pointf3& size = box.size();
- m_center = box.center();
- m_radius = Offset + ::sqrt(sqr(0.5f * size.x) + sqr(0.5f * size.y));
+ 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_initial_values = true;
+ }
::glLineWidth(2.0f);
::glColor3fv(BaseColor);
@@ -230,7 +288,6 @@ void GLGizmoRotate::on_render(const BoundingBoxf3& box) const
void GLGizmoRotate::on_render_for_picking(const BoundingBoxf3& box) const
{
- ::glDisable(GL_LIGHTING);
::glDisable(GL_DEPTH_TEST);
m_grabbers[0].color[0] = 1.0f;
@@ -399,7 +456,6 @@ void GLGizmoScale::on_update(const Pointf& mouse_pos)
void GLGizmoScale::on_render(const BoundingBoxf3& box) const
{
- ::glDisable(GL_LIGHTING);
::glDisable(GL_DEPTH_TEST);
coordf_t min_x = box.min.x - (coordf_t)Offset;
@@ -438,7 +494,6 @@ void GLGizmoScale::on_render_for_picking(const BoundingBoxf3& box) const
{
static const GLfloat INV_255 = 1.0f / 255.0f;
- ::glDisable(GL_LIGHTING);
::glDisable(GL_DEPTH_TEST);
for (unsigned int i = 0; i < 4; ++i)
@@ -450,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 2baec8f9b..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 {
@@ -57,22 +58,27 @@ public:
EState get_state() const;
void set_state(EState state);
- unsigned int get_textures_id() const;
+ unsigned int get_texture_id() const;
int get_textures_size() const;
int get_hover_id() const;
void set_hover_id(int id);
void start_dragging();
+ void stop_dragging();
void update(const Pointf& mouse_pos);
+ void refresh();
void render(const BoundingBoxf3& box) const;
void render_for_picking(const BoundingBoxf3& box) const;
protected:
virtual bool on_init() = 0;
+ virtual void on_set_state();
virtual void on_start_dragging();
+ virtual void on_stop_dragging();
virtual void on_update(const Pointf& mouse_pos) = 0;
+ virtual void on_refresh();
virtual void on_render(const BoundingBoxf3& box) const = 0;
virtual void on_render_for_picking(const BoundingBoxf3& box) const = 0;
@@ -96,13 +102,19 @@ class GLGizmoRotate : public GLGizmoBase
mutable Pointf m_center;
mutable float m_radius;
+ mutable bool m_keep_initial_values;
public:
GLGizmoRotate();
+ float get_angle_z() const;
+ void set_angle_z(float angle_z);
+
protected:
virtual bool on_init();
+ virtual void on_set_state();
virtual void on_update(const Pointf& mouse_pos);
+ virtual void on_refresh();
virtual void on_render(const BoundingBoxf3& box) const;
virtual void on_render_for_picking(const BoundingBoxf3& box) const;
@@ -120,9 +132,9 @@ class GLGizmoScale : public GLGizmoBase
static const float Offset;
float m_scale;
+ float m_starting_scale;
Pointf m_starting_drag_position;
- float m_starting_scale;
public:
GLGizmoScale();
@@ -138,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/GLShader.cpp b/xs/src/slic3r/GUI/GLShader.cpp
index 903f6c347..e2995f7c3 100644
--- a/xs/src/slic3r/GUI/GLShader.cpp
+++ b/xs/src/slic3r/GUI/GLShader.cpp
@@ -214,6 +214,17 @@ bool GLShader::set_uniform(const char *name, float value) const
return false;
}
+bool GLShader::set_uniform(const char* name, const float* matrix) const
+{
+ int id = get_uniform_location(name);
+ if (id >= 0)
+ {
+ ::glUniformMatrix4fv(id, 1, GL_FALSE, (const GLfloat*)matrix);
+ return true;
+ }
+ return false;
+}
+
/*
# Set shader vector
sub SetVector
diff --git a/xs/src/slic3r/GUI/GLShader.hpp b/xs/src/slic3r/GUI/GLShader.hpp
index 032640d8d..803b2f154 100644
--- a/xs/src/slic3r/GUI/GLShader.hpp
+++ b/xs/src/slic3r/GUI/GLShader.hpp
@@ -25,6 +25,7 @@ public:
int get_uniform_location(const char *name) const;
bool set_uniform(const char *name, float value) const;
+ bool set_uniform(const char* name, const float* matrix) const;
void enable() const;
void disable() const;
diff --git a/xs/src/slic3r/GUI/GLTexture.cpp b/xs/src/slic3r/GUI/GLTexture.cpp
index 924920bd8..18c9f5dea 100644
--- a/xs/src/slic3r/GUI/GLTexture.cpp
+++ b/xs/src/slic3r/GUI/GLTexture.cpp
@@ -72,13 +72,15 @@ bool GLTexture::load_from_file(const std::string& filename, bool generate_mipmap
}
// sends data to gpu
+ ::glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
::glGenTextures(1, &m_id);
::glBindTexture(GL_TEXTURE_2D, m_id);
- ::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data());
+ ::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data());
if (generate_mipmaps)
{
// we manually generate mipmaps because glGenerateMipmap() function is not reliable on all graphics cards
- _generate_mipmaps(image);
+ unsigned int levels_count = _generate_mipmaps(image);
+ ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1 + levels_count);
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
}
else
@@ -127,37 +129,35 @@ const std::string& GLTexture::get_source() const
void GLTexture::render_texture(unsigned int tex_id, float left, float right, float bottom, float top)
{
- ::glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
-
- ::glDisable(GL_LIGHTING);
::glEnable(GL_BLEND);
::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
::glEnable(GL_TEXTURE_2D);
+ ::glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
::glBindTexture(GL_TEXTURE_2D, (GLuint)tex_id);
::glBegin(GL_QUADS);
- ::glTexCoord2d(0.0f, 1.0f); ::glVertex3f(left, bottom, 0.0f);
- ::glTexCoord2d(1.0f, 1.0f); ::glVertex3f(right, bottom, 0.0f);
- ::glTexCoord2d(1.0f, 0.0f); ::glVertex3f(right, top, 0.0f);
- ::glTexCoord2d(0.0f, 0.0f); ::glVertex3f(left, top, 0.0f);
+ ::glTexCoord2f(0.0f, 1.0f); ::glVertex2f(left, bottom);
+ ::glTexCoord2f(1.0f, 1.0f); ::glVertex2f(right, bottom);
+ ::glTexCoord2f(1.0f, 0.0f); ::glVertex2f(right, top);
+ ::glTexCoord2f(0.0f, 0.0f); ::glVertex2f(left, top);
::glEnd();
::glBindTexture(GL_TEXTURE_2D, 0);
::glDisable(GL_TEXTURE_2D);
::glDisable(GL_BLEND);
- ::glEnable(GL_LIGHTING);
}
-void GLTexture::_generate_mipmaps(wxImage& image)
+unsigned int GLTexture::_generate_mipmaps(wxImage& image)
{
int w = image.GetWidth();
int h = image.GetHeight();
GLint level = 0;
std::vector<unsigned char> data(w * h * 4, 0);
- while ((w > 1) && (h > 1))
+ while ((w > 1) || (h > 1))
{
++level;
@@ -182,8 +182,10 @@ void GLTexture::_generate_mipmaps(wxImage& image)
data[data_id + 3] = (img_alpha != nullptr) ? img_alpha[i] : 255;
}
- ::glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA8, (GLsizei)w, (GLsizei)h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data());
+ ::glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, (GLsizei)w, (GLsizei)h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data());
}
+
+ return (unsigned int)level;
}
} // namespace GUI
diff --git a/xs/src/slic3r/GUI/GLTexture.hpp b/xs/src/slic3r/GUI/GLTexture.hpp
index 70480c605..3113fcab2 100644
--- a/xs/src/slic3r/GUI/GLTexture.hpp
+++ b/xs/src/slic3r/GUI/GLTexture.hpp
@@ -10,7 +10,7 @@ namespace GUI {
class GLTexture
{
- private:
+ protected:
unsigned int m_id;
int m_width;
int m_height;
@@ -18,7 +18,7 @@ namespace GUI {
public:
GLTexture();
- ~GLTexture();
+ virtual ~GLTexture();
bool load_from_file(const std::string& filename, bool generate_mipmaps);
void reset();
@@ -26,12 +26,13 @@ namespace GUI {
unsigned int get_id() const;
int get_width() const;
int get_height() const;
+
const std::string& get_source() const;
static void render_texture(unsigned int tex_id, float left, float right, float bottom, float top);
- private:
- void _generate_mipmaps(wxImage& image);
+ protected:
+ unsigned int _generate_mipmaps(wxImage& image);
};
} // namespace GUI
diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp
index 12af36d19..9d49ff496 100644
--- a/xs/src/slic3r/GUI/GUI.cpp
+++ b/xs/src/slic3r/GUI/GUI.cpp
@@ -7,6 +7,7 @@
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/format.hpp>
+#include <boost/lexical_cast.hpp>
#if __APPLE__
#import <IOKit/pwr_mgt/IOPMLib.h>
@@ -56,8 +57,9 @@
#include "../Utils/PresetUpdater.hpp"
#include "../Config/Snapshot.hpp"
-#include "3DScene.hpp"
+#include "3DScene.hpp"
+#include "libslic3r/I18N.hpp"
namespace Slic3r { namespace GUI {
@@ -110,6 +112,7 @@ wxNotebook *g_wxTabPanel = nullptr;
AppConfig *g_AppConfig = nullptr;
PresetBundle *g_PresetBundle= nullptr;
PresetUpdater *g_PresetUpdater = nullptr;
+_3DScene *g_3DScene = nullptr;
wxColour g_color_label_modified;
wxColour g_color_label_sys;
wxColour g_color_label_default;
@@ -118,6 +121,9 @@ std::vector<Tab *> g_tabs_list;
wxLocale* g_wxLocale;
+wxFont g_small_font;
+wxFont g_bold_font;
+
std::shared_ptr<ConfigOptionsGroup> m_optgroup;
double m_brim_width = 0.0;
wxButton* g_wiping_dialog_button = nullptr;
@@ -150,10 +156,25 @@ void update_label_colours_from_appconfig()
}
}
+static void init_fonts()
+{
+ g_small_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
+ g_bold_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold();
+#ifdef __WXMAC__
+ g_small_font.SetPointSize(11);
+ g_bold_font.SetPointSize(13);
+#endif /*__WXMAC__*/
+}
+
+static std::string libslic3r_translate_callback(const char *s) { return wxGetTranslation(wxString(s, wxConvUTF8)).utf8_str().data(); }
+
void set_wxapp(wxApp *app)
{
g_wxApp = app;
+ // Let the libslic3r know the callback, which will translate messages on demand.
+ Slic3r::I18N::set_translate_callback(libslic3r_translate_callback);
init_label_colours();
+ init_fonts();
}
void set_main_frame(wxFrame *main_frame)
@@ -181,6 +202,11 @@ void set_preset_updater(PresetUpdater *updater)
g_PresetUpdater = updater;
}
+void set_3DScene(_3DScene *scene)
+{
+ g_3DScene = scene;
+}
+
std::vector<Tab *>& get_tabs_list()
{
return g_tabs_list;
@@ -225,6 +251,7 @@ bool select_language(wxArrayString & names,
g_wxLocale->AddCatalogLookupPathPrefix(wxPathOnly(localization_dir()));
g_wxLocale->AddCatalog(g_wxApp->GetAppName());
wxSetlocale(LC_NUMERIC, "C");
+ Preset::update_suffix_modified();
return true;
}
return false;
@@ -250,6 +277,7 @@ bool load_language()
g_wxLocale->AddCatalogLookupPathPrefix(wxPathOnly(localization_dir()));
g_wxLocale->AddCatalog(g_wxApp->GetAppName());
wxSetlocale(LC_NUMERIC, "C");
+ Preset::update_suffix_modified();
return true;
}
}
@@ -317,7 +345,7 @@ void add_config_menu(wxMenuBar *menu, int event_preferences_changed, int event_l
auto local_menu = new wxMenu();
wxWindowID config_id_base = wxWindow::NewControlId((int)ConfigMenuCnt);
- auto config_wizard_name = _(ConfigWizard::name().wx_str());
+ const auto config_wizard_name = _(ConfigWizard::name().wx_str());
const auto config_wizard_tooltip = wxString::Format(_(L("Run %s")), config_wizard_name);
// Cmd+, is standard on OS X - what about other operating systems?
local_menu->Append(config_id_base + ConfigMenuWizard, config_wizard_name + dots, config_wizard_tooltip);
@@ -577,6 +605,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:{
@@ -671,6 +701,14 @@ void set_label_clr_sys(const wxColour& clr) {
g_AppConfig->save();
}
+const wxFont& small_font(){
+ return g_small_font;
+}
+
+const wxFont& bold_font(){
+ return g_bold_font;
+}
+
const wxColour& get_label_clr_default() {
return g_color_label_default;
}
@@ -789,7 +827,7 @@ void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFl
double brim_width = config->opt_float("brim_width");
if (boost::any_cast<bool>(value) == true)
{
- new_val = m_brim_width == 0.0 ? 10 :
+ new_val = m_brim_width == 0.0 ? 5 :
m_brim_width < 0.0 ? m_brim_width * (-1) :
m_brim_width;
}
@@ -868,6 +906,7 @@ void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFl
std::vector<float> extruders = dlg.get_extruders();
(config.option<ConfigOptionFloats>("wiping_volumes_matrix"))->values = std::vector<double>(matrix.begin(),matrix.end());
(config.option<ConfigOptionFloats>("wiping_volumes_extruders"))->values = std::vector<double>(extruders.begin(),extruders.end());
+ g_on_request_update_callback.call();
}
}));
return sizer;
@@ -884,7 +923,6 @@ ConfigOptionsGroup* get_optgroup()
return m_optgroup.get();
}
-
wxButton* get_wiping_dialog_button()
{
return g_wiping_dialog_button;
@@ -937,14 +975,66 @@ int get_export_option(wxFileDialog* dlg)
}
-void get_current_screen_size(unsigned &width, unsigned &height)
+bool get_current_screen_size(wxWindow *window, unsigned &width, unsigned &height)
{
- wxDisplay display(wxDisplay::GetFromWindow(g_wxMainFrame));
+ const auto idx = wxDisplay::GetFromWindow(window);
+ if (idx == wxNOT_FOUND) {
+ return false;
+ }
+
+ wxDisplay display(idx);
const auto disp_size = display.GetClientArea();
width = disp_size.GetWidth();
height = disp_size.GetHeight();
+
+ return true;
+}
+
+void save_window_size(wxTopLevelWindow *window, const std::string &name)
+{
+ const wxSize size = window->GetSize();
+ const wxPoint pos = window->GetPosition();
+ const auto maximized = window->IsMaximized() ? "1" : "0";
+
+ g_AppConfig->set((boost::format("window_%1%_size") % name).str(), (boost::format("%1%;%2%") % size.GetWidth() % size.GetHeight()).str());
+ g_AppConfig->set((boost::format("window_%1%_maximized") % name).str(), maximized);
+}
+
+void restore_window_size(wxTopLevelWindow *window, const std::string &name)
+{
+ // XXX: This still doesn't behave nicely in some situations (mostly on Linux).
+ // The problem is that it's hard to obtain window position with respect to screen geometry reliably
+ // from wxWidgets. Sometimes wxWidgets claim a window is located on a different screen than on which
+ // it's actually visible. I suspect this has something to do with window initialization (maybe we
+ // restore window geometry too early), but haven't yet found a workaround.
+
+ const auto display_idx = wxDisplay::GetFromWindow(window);
+ if (display_idx == wxNOT_FOUND) { return; }
+
+ const auto display = wxDisplay(display_idx).GetClientArea();
+ std::vector<std::string> pair;
+
+ try {
+ const auto key_size = (boost::format("window_%1%_size") % name).str();
+ if (g_AppConfig->has(key_size)) {
+ if (unescape_strings_cstyle(g_AppConfig->get(key_size), pair) && pair.size() == 2) {
+ auto width = boost::lexical_cast<int>(pair[0]);
+ auto height = boost::lexical_cast<int>(pair[1]);
+
+ window->SetSize(width, height);
+ }
+ }
+ } catch(const boost::bad_lexical_cast &) {}
+
+ // Maximizing should be the last thing to do.
+ // This ensure the size and position are sane when the user un-maximizes the window.
+ const auto key_maximized = (boost::format("window_%1%_maximized") % name).str();
+ if (g_AppConfig->get(key_maximized) == "1") {
+ window->Maximize(true);
+ }
}
+
void about()
{
AboutDialog dlg;
diff --git a/xs/src/slic3r/GUI/GUI.hpp b/xs/src/slic3r/GUI/GUI.hpp
index 285354446..68dbdfe84 100644
--- a/xs/src/slic3r/GUI/GUI.hpp
+++ b/xs/src/slic3r/GUI/GUI.hpp
@@ -4,6 +4,7 @@
#include <string>
#include <vector>
#include "Config.hpp"
+#include "../../libslic3r/Utils.hpp"
#include <wx/intl.h>
#include <wx/string.h>
@@ -11,7 +12,7 @@
class wxApp;
class wxWindow;
class wxFrame;
-class wxWindow;
+class wxFont;
class wxMenuBar;
class wxNotebook;
class wxComboCtrl;
@@ -23,6 +24,7 @@ class wxBoxSizer;
class wxFlexGridSizer;
class wxButton;
class wxFileDialog;
+class wxTopLevelWindow;
namespace Slic3r {
@@ -32,12 +34,16 @@ class AppConfig;
class PresetUpdater;
class DynamicPrintConfig;
class TabIface;
+class _3DScene;
+
+#define _(s) Slic3r::GUI::I18N::translate((s))
-#define _(s) Slic3r::translate((s))
-inline wxString translate(const char *s) { return wxGetTranslation(wxString(s, wxConvUTF8)); }
-inline wxString translate(const wchar_t *s) { return wxGetTranslation(s); }
-inline wxString translate(const std::string &s) { return wxGetTranslation(wxString(s.c_str(), wxConvUTF8)); }
-inline wxString translate(const std::wstring &s) { return wxGetTranslation(s.c_str()); }
+namespace GUI { namespace I18N {
+ inline wxString translate(const char *s) { return wxGetTranslation(wxString(s, wxConvUTF8)); }
+ inline wxString translate(const wchar_t *s) { return wxGetTranslation(s); }
+ inline wxString translate(const std::string &s) { return wxGetTranslation(wxString(s.c_str(), wxConvUTF8)); }
+ inline wxString translate(const std::wstring &s) { return wxGetTranslation(s.c_str()); }
+} }
// !!! If you needed to translate some wxString,
// !!! please use _(L(string))
@@ -87,6 +93,7 @@ void set_tab_panel(wxNotebook *tab_panel);
void set_app_config(AppConfig *app_config);
void set_preset_bundle(PresetBundle *preset_bundle);
void set_preset_updater(PresetUpdater *updater);
+void set_3DScene(_3DScene *scene);
AppConfig* get_app_config();
wxApp* get_app();
@@ -99,6 +106,9 @@ unsigned get_colour_approx_luma(const wxColour &colour);
void set_label_clr_modified(const wxColour& clr);
void set_label_clr_sys(const wxColour& clr);
+const wxFont& small_font();
+const wxFont& bold_font();
+
extern void add_menus(wxMenuBar *menu, int event_preferences_changed, int event_language_change);
// This is called when closing the application, when loading a config file or when starting the config wizard
@@ -163,6 +173,9 @@ wxString from_u8(const std::string &str);
void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFlexGridSizer* preset_sizer);
+// Callback to trigger a configuration update timer on the Plater.
+static PerlCallback g_on_request_update_callback;
+
ConfigOptionsGroup* get_optgroup();
wxButton* get_wiping_dialog_button();
@@ -170,7 +183,12 @@ void add_export_option(wxFileDialog* dlg, const std::string& format);
int get_export_option(wxFileDialog* dlg);
// Returns the dimensions of the screen on which the main frame is displayed
-void get_current_screen_size(unsigned &width, unsigned &height);
+bool get_current_screen_size(wxWindow *window, unsigned &width, unsigned &height);
+
+// Save window size and maximized status into AppConfig
+void save_window_size(wxTopLevelWindow *window, const std::string &name);
+// Restore the above
+void restore_window_size(wxTopLevelWindow *window, const std::string &name);
// Display an About dialog
extern void about();
diff --git a/xs/src/slic3r/GUI/OptionsGroup.cpp b/xs/src/slic3r/GUI/OptionsGroup.cpp
index 629a9f3a0..a2d6559a9 100644
--- a/xs/src/slic3r/GUI/OptionsGroup.cpp
+++ b/xs/src/slic3r/GUI/OptionsGroup.cpp
@@ -31,6 +31,8 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co
m_fields.emplace(id, STDMOVE(Choice::Create<Choice>(parent(), opt, id)));
} else if (opt.gui_type.compare("slider") == 0) {
} else if (opt.gui_type.compare("i_spin") == 0) { // Spinctrl
+ } else if (opt.gui_type.compare("legend") == 0) { // StaticText
+ m_fields.emplace(id, STDMOVE(StaticText::Create<StaticText>(parent(), opt, id)));
} else {
switch (opt.type) {
case coFloatOrPercent:
@@ -86,7 +88,7 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co
if (!this->m_disabled)
this->back_to_sys_value(opt_id);
};
- if (!m_is_tab_opt) {
+ if (!m_show_modified_btns) {
field->m_Undo_btn->Hide();
field->m_Undo_to_sys_btn->Hide();
}
@@ -199,7 +201,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** colored_Label/*
ConfigOptionDef option = opt.opt;
// add label if any
if (option.label != "") {
- wxString str_label = L_str(option.label);
+ wxString str_label = _(option.label);
//! To correct translation by context have to use wxGETTEXT_IN_CONTEXT macro from wxWidget 3.1.1
// wxString str_label = (option.label == "Top" || option.label == "Bottom") ?
// wxGETTEXT_IN_CONTEXT("Layers", wxString(option.label.c_str()):
@@ -220,7 +222,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** colored_Label/*
// add sidetext if any
if (option.sidetext != "") {
- auto sidetext = new wxStaticText(parent(), wxID_ANY, L_str(option.sidetext), wxDefaultPosition, wxDefaultSize);
+ auto sidetext = new wxStaticText(parent(), wxID_ANY, _(option.sidetext), wxDefaultPosition, wxDefaultSize);
sidetext->SetFont(sidetext_font);
sizer->Add(sidetext, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 4);
}
@@ -242,7 +244,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** colored_Label/*
}
Line OptionsGroup::create_single_option_line(const Option& option) const {
- Line retval{ L_str(option.opt.label), L_str(option.opt.tooltip) };
+ Line retval{ _(option.opt.label), _(option.opt.tooltip) };
Option tmp(option);
tmp.opt.label = std::string("");
retval.append_option(tmp);
@@ -457,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/OptionsGroup.hpp b/xs/src/slic3r/GUI/OptionsGroup.hpp
index 83b5b1233..422e1afd9 100644
--- a/xs/src/slic3r/GUI/OptionsGroup.hpp
+++ b/xs/src/slic3r/GUI/OptionsGroup.hpp
@@ -127,9 +127,15 @@ public:
inline void enable() { for (auto& field : m_fields) field.second->enable(); }
inline void disable() { for (auto& field : m_fields) field.second->disable(); }
+ void set_show_modified_btns_val(bool show) {
+ m_show_modified_btns = show;
+ }
+
OptionsGroup(wxWindow* _parent, const wxString& title, bool is_tab_opt=false) :
- m_parent(_parent), title(title), m_is_tab_opt(is_tab_opt), staticbox(title!="") {
- sizer = (staticbox ? new wxStaticBoxSizer(new wxStaticBox(_parent, wxID_ANY, title), wxVERTICAL) : new wxBoxSizer(wxVERTICAL));
+ m_parent(_parent), title(title), m_show_modified_btns(is_tab_opt), staticbox(title!="") {
+ auto stb = new wxStaticBox(_parent, wxID_ANY, title);
+ stb->SetFont(bold_font());
+ sizer = (staticbox ? new wxStaticBoxSizer(stb/*new wxStaticBox(_parent, wxID_ANY, title)*/, wxVERTICAL) : new wxBoxSizer(wxVERTICAL));
auto num_columns = 1U;
if (label_width != 0) num_columns++;
if (extra_column != nullptr) num_columns++;
@@ -156,7 +162,7 @@ protected:
bool m_disabled {false};
wxGridSizer* m_grid_sizer {nullptr};
// "true" if option is created in preset tabs
- bool m_is_tab_opt{ false };
+ bool m_show_modified_btns{ false };
// This panel is needed for correct showing of the ToolTips for Button, StaticText and CheckBox
// Tooltips on GTK doesn't work inside wxStaticBoxSizer unless you insert a panel
diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp
index 68982185b..8af10d293 100644
--- a/xs/src/slic3r/GUI/Preset.cpp
+++ b/xs/src/slic3r/GUI/Preset.cpp
@@ -146,6 +146,11 @@ const std::string& Preset::suffix_modified()
{
return g_suffix_modified;
}
+
+void Preset::update_suffix_modified()
+{
+ g_suffix_modified = (" (" + _(L("modified")) + ")").ToUTF8().data();
+}
// Remove an optional "(modified)" suffix from a name.
// This converts a UI name to a unique preset identifier.
std::string Preset::remove_suffix_modified(const std::string &name)
@@ -234,12 +239,12 @@ std::string Preset::label() const
bool Preset::is_compatible_with_printer(const Preset &active_printer, const DynamicPrintConfig *extra_config) const
{
- auto *condition = dynamic_cast<const ConfigOptionString*>(this->config.option("compatible_printers_condition"));
+ auto &condition = this->compatible_printers_condition();
auto *compatible_printers = dynamic_cast<const ConfigOptionStrings*>(this->config.option("compatible_printers"));
bool has_compatible_printers = compatible_printers != nullptr && ! compatible_printers->values.empty();
- if (! has_compatible_printers && condition != nullptr && ! condition->value.empty()) {
+ if (! has_compatible_printers && ! condition.empty()) {
try {
- return PlaceholderParser::evaluate_boolean_expression(condition->value, active_printer.config, extra_config);
+ return PlaceholderParser::evaluate_boolean_expression(condition, active_printer.config, extra_config);
} catch (const std::runtime_error &err) {
//FIXME in case of an error, return "compatible with everything".
printf("Preset::is_compatible_with_printer - parsing error of compatible_printers_condition %s:\n%s\n", active_printer.name.c_str(), err.what());
@@ -287,7 +292,7 @@ const std::vector<std::string>& Preset::print_options()
"top_solid_infill_speed", "support_material_speed", "support_material_xy_spacing", "support_material_interface_speed",
"bridge_speed", "gap_fill_speed", "travel_speed", "first_layer_speed", "perimeter_acceleration", "infill_acceleration",
"bridge_acceleration", "first_layer_acceleration", "default_acceleration", "skirts", "skirt_distance", "skirt_height",
- "min_skirt_length", "brim_width", "support_material", "support_material_threshold", "support_material_enforce_layers",
+ "min_skirt_length", "brim_width", "support_material", "support_material_auto", "support_material_threshold", "support_material_enforce_layers",
"raft_layers", "support_material_pattern", "support_material_with_sheath", "support_material_spacing",
"support_material_synchronize_layers", "support_material_angle", "support_material_interface_layers",
"support_material_interface_spacing", "support_material_interface_contact_loops", "support_material_contact_distance",
@@ -298,7 +303,8 @@ const std::vector<std::string>& Preset::print_options()
"perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width",
"top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "bridge_flow_ratio", "clip_multipart_objects",
"elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "wipe_tower", "wipe_tower_x", "wipe_tower_y",
- "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_bridging", "compatible_printers", "compatible_printers_condition","inherits"
+ "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_bridging", "single_extruder_multi_material_priming",
+ "compatible_printers", "compatible_printers_condition","inherits"
};
return s_opts;
}
@@ -307,11 +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_unloading_speed", "filament_toolchange_delay",
- "filament_ramming_parameters", "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;
}
@@ -321,11 +328,16 @@ 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", "max_print_height", "default_print_profile", "inherits",
+ "cooling_tube_length", "parking_pos_retraction", "extra_loading_move", "max_print_height", "default_print_profile", "inherits",
+ "remaining_times", "silent_mode", "machine_max_acceleration_extruding", "machine_max_acceleration_retracting",
+ "machine_max_acceleration_x", "machine_max_acceleration_y", "machine_max_acceleration_z", "machine_max_acceleration_e",
+ "machine_max_feedrate_x", "machine_max_feedrate_y", "machine_max_feedrate_z", "machine_max_feedrate_e",
+ "machine_min_extruding_rate", "machine_min_travel_rate",
+ "machine_max_jerk_x", "machine_max_jerk_y", "machine_max_jerk_z", "machine_max_jerk_e"
};
s_opts.insert(s_opts.end(), Preset::nozzle_options().begin(), Preset::nozzle_options().end());
}
@@ -405,7 +417,14 @@ void PresetCollection::load_presets(const std::string &dir_path, const std::stri
try {
Preset preset(m_type, name, false);
preset.file = dir_entry.path().string();
- preset.load(keys);
+ DynamicPrintConfig &config = preset.load(keys);
+ // Report configuration fields, which are misplaced into a wrong group.
+ std::string incorrect_keys;
+ if (config.remove_keys_not_in(this->default_preset().config, incorrect_keys) > 0)
+ BOOST_LOG_TRIVIAL(error) << "Error in \"" << dir_entry.path().string() << "\": The preset contains the following incorrect keys: " <<
+ incorrect_keys << ", which were ignored";
+ // Normalize once again to set the length of the filament specific vectors to 1.
+ Preset::normalize(config);
m_presets.emplace_back(preset);
} catch (const std::runtime_error &err) {
errors_cummulative += err.what();
@@ -424,7 +443,90 @@ Preset& PresetCollection::load_preset(const std::string &path, const std::string
{
DynamicPrintConfig cfg(this->default_preset().config);
cfg.apply_only(config, cfg.keys(), true);
- return this->load_preset(path, name, std::move(cfg));
+ return this->load_preset(path, name, std::move(cfg), select);
+}
+
+static bool profile_print_params_same(const DynamicPrintConfig &cfg1, const DynamicPrintConfig &cfg2)
+{
+ t_config_option_keys diff = cfg1.diff(cfg2);
+ // Following keys are used by the UI, not by the slicing core, therefore they are not important
+ // when comparing profiles for equality. Ignore them.
+ for (const char *key : { "compatible_printers", "compatible_printers_condition", "inherits",
+ "print_settings_id", "filament_settings_id", "printer_settings_id",
+ "printer_model", "printer_variant", "default_print_profile", "default_filament_profile" })
+ diff.erase(std::remove(diff.begin(), diff.end(), key), diff.end());
+ // Preset with the same name as stored inside the config exists.
+ return diff.empty();
+}
+
+// Load a preset from an already parsed config file, insert it into the sorted sequence of presets
+// and select it, losing previous modifications.
+// In case
+Preset& PresetCollection::load_external_preset(
+ // Path to the profile source file (a G-code, an AMF or 3MF file, a config file)
+ const std::string &path,
+ // Name of the profile, derived from the source file name.
+ const std::string &name,
+ // Original name of the profile, extracted from the loaded config. Empty, if the name has not been stored.
+ const std::string &original_name,
+ // Config to initialize the preset from.
+ const DynamicPrintConfig &config,
+ // Select the preset after loading?
+ bool select)
+{
+ // Load the preset over a default preset, so that the missing fields are filled in from the default preset.
+ DynamicPrintConfig cfg(this->default_preset().config);
+ cfg.apply_only(config, cfg.keys(), true);
+ // Is there a preset already loaded with the name stored inside the config?
+ std::deque<Preset>::iterator it = this->find_preset_internal(original_name);
+ if (it != m_presets.end() && it->name == original_name && profile_print_params_same(it->config, cfg)) {
+ // The preset exists and it matches the values stored inside config.
+ if (select)
+ this->select_preset(it - m_presets.begin());
+ return *it;
+ }
+ // Update the "inherits" field.
+ std::string &inherits = Preset::inherits(cfg);
+ if (it != m_presets.end() && inherits.empty()) {
+ // There is a profile with the same name already loaded. Should we update the "inherits" field?
+ if (it->vendor == nullptr)
+ inherits = it->inherits();
+ else
+ inherits = it->name;
+ }
+ // The external preset does not match an internal preset, load the external preset.
+ std::string new_name;
+ for (size_t idx = 0;; ++ idx) {
+ std::string suffix;
+ if (original_name.empty()) {
+ if (idx > 0)
+ suffix = " (" + std::to_string(idx) + ")";
+ } else {
+ if (idx == 0)
+ suffix = " (" + original_name + ")";
+ else
+ suffix = " (" + original_name + "-" + std::to_string(idx) + ")";
+ }
+ new_name = name + suffix;
+ it = this->find_preset_internal(new_name);
+ if (it == m_presets.end() || it->name != new_name)
+ // Unique profile name. Insert a new profile.
+ break;
+ if (profile_print_params_same(it->config, cfg)) {
+ // The preset exists and it matches the values stored inside config.
+ if (select)
+ this->select_preset(it - m_presets.begin());
+ return *it;
+ }
+ // Form another profile name.
+ }
+ // Insert a new profile.
+ Preset &preset = this->load_preset(path, new_name, std::move(cfg), select);
+ preset.is_external = true;
+ if (&this->get_selected_preset() == &preset)
+ this->get_edited_preset().is_external = true;
+
+ return preset;
}
Preset& PresetCollection::load_preset(const std::string &path, const std::string &name, DynamicPrintConfig &&config, bool select)
@@ -460,7 +562,7 @@ void PresetCollection::save_current_preset(const std::string &new_name)
} else {
// Creating a new preset.
Preset &preset = *m_presets.insert(it, m_edited_preset);
- std::string &inherits = preset.config.opt_string("inherits", true);
+ std::string &inherits = preset.inherits();
std::string old_name = preset.name;
preset.name = new_name;
preset.file = this->path_from_name(new_name);
@@ -475,7 +577,6 @@ void PresetCollection::save_current_preset(const std::string &new_name)
// Inherited from a user preset. Just maintain the "inherited" flag,
// meaning it will inherit from either the system preset, or the inherited user preset.
}
- preset.inherits = inherits;
preset.is_default = false;
preset.is_system = false;
preset.is_external = false;
@@ -513,20 +614,20 @@ bool PresetCollection::load_bitmap_default(const std::string &file_name)
const Preset* PresetCollection::get_selected_preset_parent() const
{
- auto *inherits = dynamic_cast<const ConfigOptionString*>(this->get_edited_preset().config.option("inherits"));
- if (inherits == nullptr || inherits->value.empty())
- return this->get_selected_preset().is_system ? &this->get_selected_preset() : nullptr; // nullptr;
- const Preset* preset = this->find_preset(inherits->value, false);
+ const std::string &inherits = this->get_edited_preset().inherits();
+ if (inherits.empty())
+ return this->get_selected_preset().is_system ? &this->get_selected_preset() : nullptr;
+ const Preset* preset = this->find_preset(inherits, false);
return (preset == nullptr || preset->is_default || preset->is_external) ? nullptr : preset;
}
const Preset* PresetCollection::get_preset_parent(const Preset& child) const
{
- auto *inherits = dynamic_cast<const ConfigOptionString*>(child.config.option("inherits"));
- if (inherits == nullptr || inherits->value.empty())
+ const std::string &inherits = child.inherits();
+ if (inherits.empty())
// return this->get_selected_preset().is_system ? &this->get_selected_preset() : nullptr;
return nullptr;
- const Preset* preset = this->find_preset(inherits->value, false);
+ const Preset* preset = this->find_preset(inherits, false);
return (preset == nullptr/* || preset->is_default */|| preset->is_external) ? nullptr : preset;
}
@@ -601,6 +702,7 @@ void PresetCollection::update_platter_ui(wxBitmapComboBox *ui)
// Otherwise fill in the list from scratch.
ui->Freeze();
ui->Clear();
+ size_t selected_preset_item = 0;
const Preset &selected_preset = this->get_selected_preset();
// Show wide icons if the currently selected preset is not compatible with the current printer,
@@ -641,7 +743,7 @@ void PresetCollection::update_platter_ui(wxBitmapComboBox *ui)
ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()),
(bmp == 0) ? (m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap) : *bmp);
if (i == m_idx_selected)
- ui->SetSelection(ui->GetCount() - 1);
+ selected_preset_item = ui->GetCount() - 1;
}
else
{
@@ -658,10 +760,13 @@ void PresetCollection::update_platter_ui(wxBitmapComboBox *ui)
for (std::map<wxString, wxBitmap*>::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) {
ui->Append(it->first, *it->second);
if (it->first == selected)
- ui->SetSelection(ui->GetCount() - 1);
+ selected_preset_item = ui->GetCount() - 1;
}
}
- ui->Thaw();
+
+ ui->SetSelection(selected_preset_item);
+ ui->SetToolTip(ui->GetString(selected_preset_item));
+ ui->Thaw();
}
size_t PresetCollection::update_tab_ui(wxBitmapComboBox *ui, bool show_incompatible)
@@ -719,6 +824,7 @@ size_t PresetCollection::update_tab_ui(wxBitmapComboBox *ui, bool show_incompati
}
}
ui->SetSelection(selected_preset_item);
+ ui->SetToolTip(ui->GetString(selected_preset_item));
ui->Thaw();
return selected_preset_item;
}
@@ -763,7 +869,7 @@ std::vector<std::string> PresetCollection::dirty_options(const Preset *edited, c
// The "compatible_printers" option key is handled differently from the others:
// It is not mandatory. If the key is missing, it means it is compatible with any printer.
// If the key exists and it is empty, it means it is compatible with no printer.
- std::initializer_list<const char*> optional_keys { "compatible_printers", "compatible_printers_condition" };
+ std::initializer_list<const char*> optional_keys { "compatible_printers" };
for (auto &opt_key : optional_keys) {
if (reference->config.has(opt_key) != edited->config.has(opt_key))
changed.emplace_back(opt_key);
@@ -772,17 +878,6 @@ std::vector<std::string> PresetCollection::dirty_options(const Preset *edited, c
return changed;
}
-std::vector<std::string> PresetCollection::system_equal_options() const
-{
- const Preset *edited = &this->get_edited_preset();
- const Preset *reference = this->get_selected_preset_parent();
- std::vector<std::string> equal;
- if (edited != nullptr && reference != nullptr) {
- equal = reference->config.equal(edited->config);
- }
- return equal;
-}
-
// Select a new preset. This resets all the edits done to the currently selected preset.
// If the preset with index idx does not exist, a first visible preset is selected.
Preset& PresetCollection::select_preset(size_t idx)
diff --git a/xs/src/slic3r/GUI/Preset.hpp b/xs/src/slic3r/GUI/Preset.hpp
index 31fb69aa8..0d00cae48 100644
--- a/xs/src/slic3r/GUI/Preset.hpp
+++ b/xs/src/slic3r/GUI/Preset.hpp
@@ -113,9 +113,6 @@ public:
// or a Configuration file bundling the Print + Filament + Printer presets (in that case is_external and possibly is_system will be true),
// or it could be a G-code (again, is_external will be true).
std::string file;
- // A user profile may inherit its settings either from a system profile, or from a user profile.
- // A system profile shall never derive from any other profile, as the system profile hierarchy is being flattened during loading.
- std::string inherits;
// If this is a system profile, then there should be a vendor data available to display at the UI.
const VendorProfile *vendor = nullptr;
@@ -142,6 +139,16 @@ public:
bool is_compatible_with_printer(const Preset &active_printer, const DynamicPrintConfig *extra_config) const;
bool is_compatible_with_printer(const Preset &active_printer) const;
+ // Returns the name of the preset, from which this preset inherits.
+ static std::string& inherits(DynamicPrintConfig &cfg) { return cfg.option<ConfigOptionString>("inherits", true)->value; }
+ std::string& inherits() { return Preset::inherits(this->config); }
+ const std::string& inherits() const { return Preset::inherits(const_cast<Preset*>(this)->config); }
+
+ // Returns the "compatible_printers_condition".
+ static std::string& compatible_printers_condition(DynamicPrintConfig &cfg) { return cfg.option<ConfigOptionString>("compatible_printers_condition", true)->value; }
+ std::string& compatible_printers_condition() { return Preset::compatible_printers_condition(this->config); }
+ const std::string& compatible_printers_condition() const { return Preset::compatible_printers_condition(const_cast<Preset*>(this)->config); }
+
// Mark this preset as compatible if it is compatible with active_printer.
bool update_compatible_with_printer(const Preset &active_printer, const DynamicPrintConfig *extra_config);
@@ -160,6 +167,7 @@ public:
static const std::vector<std::string>& printer_options();
// Nozzle options of the printer options.
static const std::vector<std::string>& nozzle_options();
+ static void update_suffix_modified();
protected:
friend class PresetCollection;
@@ -200,6 +208,18 @@ public:
Preset& load_preset(const std::string &path, const std::string &name, const DynamicPrintConfig &config, bool select = true);
Preset& load_preset(const std::string &path, const std::string &name, DynamicPrintConfig &&config, bool select = true);
+ Preset& load_external_preset(
+ // Path to the profile source file (a G-code, an AMF or 3MF file, a config file)
+ const std::string &path,
+ // Name of the profile, derived from the source file name.
+ const std::string &name,
+ // Original name of the profile, extracted from the loaded config. Empty, if the name has not been stored.
+ const std::string &original_name,
+ // Config to initialize the preset from.
+ const DynamicPrintConfig &config,
+ // Select the preset after loading?
+ bool select = true);
+
// Save the preset under a new name. If the name is different from the old one,
// a new preset is stored into the list of presets.
// All presets are marked as not modified and the new preset is activated.
@@ -241,7 +261,7 @@ public:
// used to update preset_choice from Tab
const std::deque<Preset>& get_presets() { return m_presets; }
int get_idx_selected() { return m_idx_selected; }
- const std::string& get_suffix_modified();
+ static const std::string& get_suffix_modified();
// Return a preset possibly with modifications.
Preset& default_preset() { return m_presets.front(); }
@@ -312,8 +332,6 @@ public:
// Compare the content of get_selected_preset() with get_edited_preset() configs, return the list of keys where they differ.
std::vector<std::string> current_different_from_parent_options(const bool is_printer_type = false) const
{ return dirty_options(&this->get_edited_preset(), this->get_selected_preset_parent(), is_printer_type); }
- // Compare the content of get_selected_preset() with get_selected_preset_parent() configs, return the list of keys where they equal.
- std::vector<std::string> system_equal_options() const;
// Update the choice UI from the list of presets.
// If show_incompatible, all presets are shown, otherwise only the compatible presets are shown.
@@ -349,9 +367,10 @@ private:
PresetCollection(const PresetCollection &other);
PresetCollection& operator=(const PresetCollection &other);
- // Find a preset in the sorted list of presets.
+ // Find a preset position in the sorted list of presets.
// The "-- default -- " preset is always the first, so it needs
// to be handled differently.
+ // If a preset does not exist, an iterator is returned indicating where to insert a preset with the same name.
std::deque<Preset>::iterator find_preset_internal(const std::string &name)
{
Preset key(m_type, name);
diff --git a/xs/src/slic3r/GUI/PresetBundle.cpp b/xs/src/slic3r/GUI/PresetBundle.cpp
index 0a280eee1..e7ae4ebd9 100644
--- a/xs/src/slic3r/GUI/PresetBundle.cpp
+++ b/xs/src/slic3r/GUI/PresetBundle.cpp
@@ -52,26 +52,37 @@ PresetBundle::PresetBundle() :
if (wxImage::FindHandler(wxBITMAP_TYPE_PNG) == nullptr)
wxImage::AddHandler(new wxPNGHandler);
+ // The following keys are handled by the UI, they do not have a counterpart in any StaticPrintConfig derived classes,
+ // therefore they need to be handled differently. As they have no counterpart in StaticPrintConfig, they are not being
+ // initialized based on PrintConfigDef(), but to empty values (zeros, empty vectors, empty strings).
+ //
+ // "compatible_printers", "compatible_printers_condition", "inherits",
+ // "print_settings_id", "filament_settings_id", "printer_settings_id",
+ // "printer_vendor", "printer_model", "printer_variant", "default_print_profile", "default_filament_profile"
+
// Create the ID config keys, as they are not part of the Static print config classes.
- this->prints.default_preset().config.opt_string("print_settings_id", true);
- this->filaments.default_preset().config.option<ConfigOptionStrings>("filament_settings_id", true)->values.assign(1, std::string());
- this->printers.default_preset().config.opt_string("printer_settings_id", true);
- // "compatible printers" are not mandatory yet.
- //FIXME Rename "compatible_printers" and "compatible_printers_condition", as they are defined in both print and filament profiles,
- // therefore they are clashing when generating a a config file, G-code or AMF/3MF.
-// this->filaments.default_preset().config.optptr("compatible_printers", true);
-// this->filaments.default_preset().config.optptr("compatible_printers_condition", true);
-// this->prints.default_preset().config.optptr("compatible_printers", true);
-// this->prints.default_preset().config.optptr("compatible_printers_condition", true);
- // Create the "printer_vendor", "printer_model" and "printer_variant" keys.
+ this->prints.default_preset().config.optptr("print_settings_id", true);
+ this->prints.default_preset().compatible_printers_condition();
+ this->prints.default_preset().inherits();
+
+ this->filaments.default_preset().config.option<ConfigOptionStrings>("filament_settings_id", true)->values = { "" };
+ this->filaments.default_preset().compatible_printers_condition();
+ this->filaments.default_preset().inherits();
+
+ this->printers.default_preset().config.optptr("printer_settings_id", true);
this->printers.default_preset().config.optptr("printer_vendor", true);
this->printers.default_preset().config.optptr("printer_model", true);
this->printers.default_preset().config.optptr("printer_variant", true);
- // Load the default preset bitmaps.
+ this->printers.default_preset().config.optptr("default_print_profile", true);
+ this->printers.default_preset().config.option<ConfigOptionStrings>("default_filament_profile", true)->values = { "" };
+ this->printers.default_preset().inherits();
+
+ // Load the default preset bitmaps.
this->prints .load_bitmap_default("cog.png");
this->filaments.load_bitmap_default("spool.png");
this->printers .load_bitmap_default("printer_empty.png");
this->load_compatible_bitmaps();
+
// Re-activate the default presets, so their "edited" preset copies will be updated with the additional configuration values above.
this->prints .select_preset(0);
this->filaments.select_preset(0);
@@ -257,7 +268,7 @@ void PresetBundle::load_installed_printers(const AppConfig &config)
}
// Load selections (current print, current filaments, current printer) from config.ini
-// This is done just once on application start up.
+// This is done on application start up or after updates are applied.
void PresetBundle::load_selections(const AppConfig &config)
{
// Update visibility of presets based on application vendor / model / variant configuration.
@@ -372,9 +383,16 @@ DynamicPrintConfig PresetBundle::full_config() const
auto *nozzle_diameter = dynamic_cast<const ConfigOptionFloats*>(out.option("nozzle_diameter"));
size_t num_extruders = nozzle_diameter->values.size();
+ // Collect the "compatible_printers_condition" and "inherits" values over all presets (print, filaments, printers) into a single vector.
+ std::vector<std::string> compatible_printers_condition;
+ std::vector<std::string> inherits;
+ compatible_printers_condition.emplace_back(this->prints.get_edited_preset().compatible_printers_condition());
+ inherits .emplace_back(this->prints.get_edited_preset().inherits());
if (num_extruders <= 1) {
out.apply(this->filaments.get_edited_preset().config);
+ compatible_printers_condition.emplace_back(this->filaments.get_edited_preset().compatible_printers_condition());
+ inherits .emplace_back(this->filaments.get_edited_preset().inherits());
} else {
// Retrieve filament presets and build a single config object for them.
// First collect the filament configurations based on the user selection of this->filament_presets.
@@ -384,11 +402,15 @@ DynamicPrintConfig PresetBundle::full_config() const
filament_configs.emplace_back(&this->filaments.find_preset(filament_preset_name, true)->config);
while (filament_configs.size() < num_extruders)
filament_configs.emplace_back(&this->filaments.first_visible().config);
+ for (const DynamicPrintConfig *cfg : filament_configs) {
+ compatible_printers_condition.emplace_back(Preset::compatible_printers_condition(*const_cast<DynamicPrintConfig*>(cfg)));
+ inherits .emplace_back(Preset::inherits(*const_cast<DynamicPrintConfig*>(cfg)));
+ }
// Option values to set a ConfigOptionVector from.
std::vector<const ConfigOption*> filament_opts(num_extruders, nullptr);
// loop through options and apply them to the resulting config.
for (const t_config_option_key &key : this->filaments.default_preset().config.keys()) {
- if (key == "compatible_printers" || key == "compatible_printers_condition")
+ if (key == "compatible_printers")
continue;
// Get a destination option.
ConfigOption *opt_dst = out.option(key, false);
@@ -406,9 +428,13 @@ DynamicPrintConfig PresetBundle::full_config() const
}
}
- //FIXME These two value types clash between the print and filament profiles. They should be renamed.
+ // Don't store the "compatible_printers_condition" for the printer profile, there is none.
+ inherits.emplace_back(this->printers.get_edited_preset().inherits());
+
+ // These two value types clash between the print and filament profiles. They should be renamed.
out.erase("compatible_printers");
out.erase("compatible_printers_condition");
+ out.erase("inherits");
static const char *keys[] = { "perimeter", "infill", "solid_infill", "support_material", "support_material_interface" };
for (size_t i = 0; i < sizeof(keys) / sizeof(keys[0]); ++ i) {
@@ -418,6 +444,25 @@ DynamicPrintConfig PresetBundle::full_config() const
opt->value = boost::algorithm::clamp<int>(opt->value, 0, int(num_extruders));
}
+ out.option<ConfigOptionString >("print_settings_id", true)->value = this->prints.get_selected_preset().name;
+ out.option<ConfigOptionStrings>("filament_settings_id", true)->values = this->filament_presets;
+ out.option<ConfigOptionString >("printer_settings_id", true)->value = this->printers.get_selected_preset().name;
+
+ // Serialize the collected "compatible_printers_condition" and "inherits" fields.
+ // There will be 1 + num_exturders fields for "inherits" and 2 + num_extruders for "compatible_printers_condition" stored.
+ // The vector will not be stored if all fields are empty strings.
+ auto add_if_some_non_empty = [&out](std::vector<std::string> &&values, const std::string &key) {
+ bool nonempty = false;
+ for (const std::string &v : values)
+ if (! v.empty()) {
+ nonempty = true;
+ break;
+ }
+ if (nonempty)
+ out.set_key_value(key, new ConfigOptionStrings(std::move(values)));
+ };
+ add_if_some_non_empty(std::move(compatible_printers_condition), "compatible_printers_condition_cummulative");
+ add_if_some_non_empty(std::move(inherits), "inherits_cummulative");
return out;
}
@@ -496,6 +541,20 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
}
}
+ size_t num_extruders = std::min(config.option<ConfigOptionFloats>("nozzle_diameter" )->values.size(),
+ config.option<ConfigOptionFloats>("filament_diameter")->values.size());
+ // Make a copy of the "compatible_printers_condition_cummulative" and "inherits_cummulative" vectors, which
+ // accumulate values over all presets (print, filaments, printers).
+ // These values will be distributed into their particular presets when loading.
+ std::vector<std::string> compatible_printers_condition_values = std::move(config.option<ConfigOptionStrings>("compatible_printers_condition_cummulative", true)->values);
+ std::vector<std::string> inherits_values = std::move(config.option<ConfigOptionStrings>("inherits_cummulative", true)->values);
+ std::string &compatible_printers_condition = Preset::compatible_printers_condition(config);
+ std::string &inherits = Preset::inherits(config);
+ compatible_printers_condition_values.resize(num_extruders + 2, std::string());
+ inherits_values.resize(num_extruders + 2, std::string());
+ // The "default_filament_profile" will be later extracted into the printer profile.
+ config.option<ConfigOptionStrings>("default_filament_profile", true)->values.resize(num_extruders, std::string());
+
// 1) Create a name from the file name.
// Keep the suffix (.ini, .gcode, .amf, .3mf etc) to differentiate it from the normal profiles.
std::string name = is_external ? boost::filesystem::path(name_or_path).filename().string() : name_or_path;
@@ -504,24 +563,30 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
// First load the print and printer presets.
for (size_t i_group = 0; i_group < 2; ++ i_group) {
PresetCollection &presets = (i_group == 0) ? this->prints : this->printers;
- Preset &preset = presets.load_preset(is_external ? name_or_path : presets.path_from_name(name), name, config);
- if (is_external)
- preset.is_external = true;
+ // Split the "compatible_printers_condition" and "inherits" values one by one from a single vector to the print & printer profiles.
+ size_t idx = (i_group == 0) ? 0 : num_extruders + 1;
+ inherits = inherits_values[idx];
+ compatible_printers_condition = compatible_printers_condition_values[idx];
+ if (is_external)
+ presets.load_external_preset(name_or_path, name,
+ config.opt_string((i_group == 0) ? "print_settings_id" : "printer_settings_id", true),
+ config);
else
- preset.save();
+ presets.load_preset(presets.path_from_name(name), name, config).save();
}
// 3) Now load the filaments. If there are multiple filament presets, split them and load them.
- auto *nozzle_diameter = dynamic_cast<const ConfigOptionFloats*>(config.option("nozzle_diameter"));
- auto *filament_diameter = dynamic_cast<const ConfigOptionFloats*>(config.option("filament_diameter"));
- size_t num_extruders = std::min(nozzle_diameter->values.size(), filament_diameter->values.size());
+ auto old_filament_profile_names = config.option<ConfigOptionStrings>("filament_settings_id", true);
+ old_filament_profile_names->values.resize(num_extruders, std::string());
+
if (num_extruders <= 1) {
- Preset &preset = this->filaments.load_preset(
- is_external ? name_or_path : this->filaments.path_from_name(name), name, config);
+ // Split the "compatible_printers_condition" and "inherits" from the cummulative vectors to separate filament presets.
+ inherits = inherits_values[1];
+ compatible_printers_condition = compatible_printers_condition_values[1];
if (is_external)
- preset.is_external = true;
+ this->filaments.load_external_preset(name_or_path, name, old_filament_profile_names->values.front(), config);
else
- preset.save();
+ this->filaments.load_preset(this->filaments.path_from_name(name), name, config).save();
this->filament_presets.clear();
this->filament_presets.emplace_back(name);
} else {
@@ -543,21 +608,30 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
// Load the configs into this->filaments and make them active.
this->filament_presets.clear();
for (size_t i = 0; i < configs.size(); ++ i) {
- char suffix[64];
- if (i == 0)
- suffix[0] = 0;
- else
- sprintf(suffix, " (%d)", i);
- std::string new_name = name + suffix;
+ DynamicPrintConfig &cfg = configs[i];
+ // Split the "compatible_printers_condition" and "inherits" from the cummulative vectors to separate filament presets.
+ cfg.opt_string("compatible_printers_condition", true) = compatible_printers_condition_values[i + 1];
+ cfg.opt_string("inherits", true) = inherits_values[i + 1];
// Load all filament presets, but only select the first one in the preset dialog.
- Preset &preset = this->filaments.load_preset(
- is_external ? name_or_path : this->filaments.path_from_name(new_name),
- new_name, std::move(configs[i]), i == 0);
+ Preset *loaded = nullptr;
if (is_external)
- preset.is_external = true;
- else
- preset.save();
- this->filament_presets.emplace_back(new_name);
+ loaded = &this->filaments.load_external_preset(name_or_path, name,
+ (i < old_filament_profile_names->values.size()) ? old_filament_profile_names->values[i] : "",
+ std::move(cfg), i == 0);
+ else {
+ // Used by the config wizard when creating a custom setup.
+ // Therefore this block should only be called for a single extruder.
+ char suffix[64];
+ if (i == 0)
+ suffix[0] = 0;
+ else
+ sprintf(suffix, "%d", i);
+ std::string new_name = name + suffix;
+ loaded = &this->filaments.load_preset(this->filaments.path_from_name(new_name),
+ new_name, std::move(cfg), i == 0);
+ loaded->save();
+ }
+ this->filament_presets.emplace_back(loaded->name);
}
}
@@ -845,24 +919,12 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla
DynamicPrintConfig config(default_config);
for (auto &kvp : section.second)
config.set_deserialize(kvp.first, kvp.second.data());
- Preset::normalize(config);
// Report configuration fields, which are misplaced into a wrong group.
std::string incorrect_keys;
- size_t n_incorrect_keys = 0;
- for (const std::string &key : config.keys())
- if (! default_config.has(key)) {
- if (incorrect_keys.empty())
- incorrect_keys = key;
- else {
- incorrect_keys += ", ";
- incorrect_keys += key;
- }
- config.erase(key);
- ++ n_incorrect_keys;
- }
- if (! incorrect_keys.empty())
+ if (config.remove_keys_not_in(default_config, incorrect_keys) > 0)
BOOST_LOG_TRIVIAL(error) << "Error in a Vendor Config Bundle \"" << path << "\": The printer preset \"" <<
- section.first << "\" contains the following incorrect keys: " << incorrect_keys << ", which were removed";
+ section.first << "\" contains the following incorrect keys: " << incorrect_keys << ", which were removed";
+ Preset::normalize(config);
if ((flags & LOAD_CFGBNDLE_SYSTEM) && presets == &printers) {
// Filter out printer presets, which are not mentioned in the vendor profile.
// These presets are considered not installed.
@@ -1108,6 +1170,7 @@ void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, wxBitma
// Fill in the list from scratch.
ui->Freeze();
ui->Clear();
+ size_t selected_preset_item = 0;
const Preset *selected_preset = this->filaments.find_preset(this->filament_presets[idx_extruder]);
// Show wide icons if the currently selected preset is not compatible with the current printer,
// and draw a red flag in front of the selected preset.
@@ -1159,7 +1222,7 @@ void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, wxBitma
ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()),
(bitmap == 0) ? wxNullBitmap : *bitmap);
if (selected)
- ui->SetSelection(ui->GetCount() - 1);
+ selected_preset_item = ui->GetCount() - 1;
}
else
{
@@ -1178,9 +1241,11 @@ void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, wxBitma
for (std::map<wxString, wxBitmap*>::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) {
ui->Append(it->first, *it->second);
if (it->first == selected_str)
- ui->SetSelection(ui->GetCount() - 1);
+ selected_preset_item = ui->GetCount() - 1;
}
}
+ ui->SetSelection(selected_preset_item);
+ ui->SetToolTip(ui->GetString(selected_preset_item));
ui->Thaw();
}
diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
index 9e0e4fc27..9d265cfc4 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"
@@ -847,6 +847,7 @@ void TabPrint::build()
page = add_options_page(_(L("Support material")), "building.png");
optgroup = page->new_optgroup(_(L("Support material")));
optgroup->append_single_option_line("support_material");
+ optgroup->append_single_option_line("support_material_auto");
optgroup->append_single_option_line("support_material_threshold");
optgroup->append_single_option_line("support_material_enforce_layers");
@@ -919,6 +920,7 @@ void TabPrint::build()
optgroup->append_single_option_line("wipe_tower_width");
optgroup->append_single_option_line("wipe_tower_rotation_angle");
optgroup->append_single_option_line("wipe_tower_bridging");
+ optgroup->append_single_option_line("single_extruder_multi_material_priming");
optgroup = page->new_optgroup(_(L("Advanced")));
optgroup->append_single_option_line("interface_shells");
@@ -1182,13 +1184,15 @@ void TabPrint::update()
bool have_raft = m_config->opt_int("raft_layers") > 0;
bool have_support_material = m_config->opt_bool("support_material") || have_raft;
+ bool have_support_material_auto = have_support_material && m_config->opt_bool("support_material_auto");
bool have_support_interface = m_config->opt_int("support_material_interface_layers") > 0;
bool have_support_soluble = have_support_material && m_config->opt_float("support_material_contact_distance") == 0;
- for (auto el : {"support_material_threshold", "support_material_pattern", "support_material_with_sheath",
+ for (auto el : {"support_material_pattern", "support_material_with_sheath",
"support_material_spacing", "support_material_angle", "support_material_interface_layers",
"dont_support_bridges", "support_material_extrusion_width", "support_material_contact_distance",
"support_material_xy_spacing" })
get_field(el)->toggle(have_support_material);
+ get_field("support_material_threshold")->toggle(have_support_material_auto);
for (auto el : {"support_material_interface_spacing", "support_material_interface_extruder",
"support_material_interface_speed", "support_material_interface_contact_loops" })
@@ -1289,9 +1293,18 @@ 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");
optgroup->append_single_option_line("filament_toolchange_delay");
+ optgroup->append_single_option_line("filament_cooling_moves");
+ optgroup->append_single_option_line("filament_cooling_initial_speed");
+ optgroup->append_single_option_line("filament_cooling_final_speed");
+ optgroup->append_single_option_line("filament_minimal_purge_on_wipe_tower");
+
line = { _(L("Ramming")), "" };
line.widget = [this](wxWindow* parent){
auto ramming_dialog_btn = new wxButton(parent, wxID_ANY, _(L("Ramming settings"))+dots, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT);
@@ -1513,10 +1526,12 @@ void TabPrinter::build()
optgroup->append_line(line);
}
- optgroup = page->new_optgroup(_(L("OctoPrint upload")));
+ optgroup = page->new_optgroup(_(L("Printer Host upload")));
+
+ optgroup->append_single_option_line("host_type");
- auto octoprint_host_browse = [this, optgroup] (wxWindow* parent) {
- auto btn = new wxButton(parent, wxID_ANY, _(L(" Browse "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
+ 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);
@@ -1524,47 +1539,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);
@@ -1574,17 +1592,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);
@@ -1594,13 +1612,30 @@ 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);
}
optgroup = page->new_optgroup(_(L("Firmware")));
optgroup->append_single_option_line("gcode_flavor");
+ optgroup->append_single_option_line("silent_mode");
+ optgroup->append_single_option_line("remaining_times");
+
+ optgroup->m_on_change = [this, optgroup](t_config_option_key opt_key, boost::any value){
+ wxTheApp->CallAfter([this, opt_key, value](){
+ if (opt_key.compare("silent_mode") == 0) {
+ bool val = boost::any_cast<bool>(value);
+ if (m_use_silent_mode != val) {
+ m_rebuild_kinematics_page = true;
+ m_use_silent_mode = val;
+ }
+ }
+ build_extruder_pages();
+ update_dirty();
+ on_value_change(opt_key, value);
+ });
+ };
optgroup = page->new_optgroup(_(L("Advanced")));
optgroup->append_single_option_line("use_relative_e_distances");
@@ -1682,8 +1717,94 @@ void TabPrinter::extruders_count_changed(size_t extruders_count){
on_value_change("extruders_count", extruders_count);
}
-void TabPrinter::build_extruder_pages(){
+void TabPrinter::append_option_line(ConfigOptionsGroupShp optgroup, const std::string opt_key)
+{
+ auto option = optgroup->get_option(opt_key, 0);
+ auto line = Line{ option.opt.full_label, "" };
+ line.append_option(option);
+ if (m_use_silent_mode)
+ line.append_option(optgroup->get_option(opt_key, 1));
+ optgroup->append_line(line);
+}
+
+PageShp TabPrinter::build_kinematics_page()
+{
+ auto page = add_options_page(_(L("Machine limits")), "cog.png", true);
+
+ if (m_use_silent_mode) {
+ // Legend for OptionsGroups
+ auto optgroup = page->new_optgroup(_(L("")));
+ optgroup->set_show_modified_btns_val(false);
+ optgroup->label_width = 230;
+ auto line = Line{ "", "" };
+
+ ConfigOptionDef def;
+ def.type = coString;
+ def.width = 150;
+ def.gui_type = "legend";
+ def.tooltip = L("Values in this column are for Full Power mode");
+ def.default_value = new ConfigOptionString{ L("Full Power") };
+
+ auto option = Option(def, "full_power_legend");
+ line.append_option(option);
+
+ def.tooltip = L("Values in this column are for Silent mode");
+ def.default_value = new ConfigOptionString{ L("Silent") };
+ option = Option(def, "silent_legend");
+ line.append_option(option);
+
+ optgroup->append_line(line);
+ }
+
+ std::vector<std::string> axes{ "x", "y", "z", "e" };
+ auto optgroup = page->new_optgroup(_(L("Maximum feedrates")));
+ for (const std::string &axis : axes) {
+ append_option_line(optgroup, "machine_max_feedrate_" + axis);
+ }
+
+ optgroup = page->new_optgroup(_(L("Maximum accelerations")));
+ for (const std::string &axis : axes) {
+ append_option_line(optgroup, "machine_max_acceleration_" + axis);
+ }
+ append_option_line(optgroup, "machine_max_acceleration_extruding");
+ append_option_line(optgroup, "machine_max_acceleration_retracting");
+
+ optgroup = page->new_optgroup(_(L("Jerk limits")));
+ for (const std::string &axis : axes) {
+ append_option_line(optgroup, "machine_max_jerk_" + axis);
+ }
+
+ optgroup = page->new_optgroup(_(L("Minimum feedrates")));
+ append_option_line(optgroup, "machine_min_extruding_rate");
+ append_option_line(optgroup, "machine_min_travel_rate");
+
+ return page;
+}
+
+
+void TabPrinter::build_extruder_pages()
+{
size_t n_before_extruders = 2; // Count of pages before Extruder pages
+ bool is_marlin_flavor = m_config->option<ConfigOptionEnum<GCodeFlavor>>("gcode_flavor")->value == gcfMarlin;
+
+ // Add/delete Kinematics page according to is_marlin_flavor
+ size_t existed_page = 0;
+ for (int i = n_before_extruders; i < m_pages.size(); ++i) // first make sure it's not there already
+ if (m_pages[i]->title().find(_(L("Machine limits"))) != std::string::npos) {
+ if (!is_marlin_flavor || m_rebuild_kinematics_page)
+ m_pages.erase(m_pages.begin() + i);
+ else
+ existed_page = i;
+ break;
+ }
+
+ if (existed_page < n_before_extruders && is_marlin_flavor){
+ auto page = build_kinematics_page();
+ m_pages.insert(m_pages.begin() + n_before_extruders, page);
+ }
+
+ if (is_marlin_flavor)
+ n_before_extruders++;
size_t n_after_single_extruder_MM = 2; // Count of pages after single_extruder_multi_material page
if (m_extruders_count_old == m_extruders_count ||
@@ -1704,6 +1825,7 @@ void TabPrinter::build_extruder_pages(){
optgroup->append_single_option_line("cooling_tube_retraction");
optgroup->append_single_option_line("cooling_tube_length");
optgroup->append_single_option_line("parking_pos_retraction");
+ optgroup->append_single_option_line("extra_loading_move");
m_pages.insert(m_pages.end() - n_after_single_extruder_MM, page);
m_has_single_extruder_MM_page = true;
}
@@ -1757,7 +1879,6 @@ void TabPrinter::build_extruder_pages(){
m_pages.begin() + n_before_extruders + m_extruders_count_old);
m_extruders_count_old = m_extruders_count;
-
rebuild_page_tree();
}
@@ -1786,12 +1907,34 @@ 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);
+ bool is_marlin_flavor = m_config->option<ConfigOptionEnum<GCodeFlavor>>("gcode_flavor")->value == gcfMarlin;
+
+ {
+ Field *sm = get_field("silent_mode");
+ if (! is_marlin_flavor)
+ // Disable silent mode for non-marlin firmwares.
+ get_field("silent_mode")->toggle(false);
+ if (is_marlin_flavor)
+ sm->enable();
+ else
+ sm->disable();
+ }
+
+ if (m_use_silent_mode != m_config->opt_bool("silent_mode")) {
+ m_rebuild_kinematics_page = true;
+ m_use_silent_mode = m_config->opt_bool("silent_mode");
+ }
+
for (size_t i = 0; i < m_extruders_count; ++i) {
bool have_retract_length = m_config->opt_float("retract_length", i) > 0;
@@ -1910,7 +2053,8 @@ void Tab::rebuild_page_tree()
auto itemId = m_treectrl->AppendItem(rootItem, p->title(), p->iconID());
m_treectrl->SetItemTextColour(itemId, p->get_item_colour());
if (p->title() == selected) {
- m_disable_tree_sel_changed_event = 1;
+ if (!(p->title() == _(L("Machine limits")) || p->title() == _(L("Single extruder MM setup")))) // These Pages have to be updated inside OnTreeSelChange
+ m_disable_tree_sel_changed_event = 1;
m_treectrl->SelectItem(itemId);
m_disable_tree_sel_changed_event = 0;
have_selection = 1;
@@ -2603,7 +2747,7 @@ void SavePresetWindow::accept()
const char* unusable_symbols = "<>:/\\|?*\"";
bool is_unusable_symbol = false;
bool is_unusable_postfix = false;
- const std::string unusable_postfix = "(modified)";
+ const std::string unusable_postfix = PresetCollection::get_suffix_modified();//"(modified)";
for (size_t i = 0; i < std::strlen(unusable_symbols); i++){
if (m_chosen_name.find_first_of(unusable_symbols[i]) != std::string::npos){
is_unusable_symbol = true;
@@ -2618,8 +2762,9 @@ void SavePresetWindow::accept()
_(L("the following characters are not allowed:")) + " <>:/\\|?*\"");
}
else if (is_unusable_postfix){
- show_error(this, _(L("The supplied name is not valid;")) + "\n" +
- _(L("the following postfix are not allowed:")) + "\n\t" + unusable_postfix);
+ show_error(this,_(L("The supplied name is not valid;")) + "\n" +
+ _(L("the following postfix are not allowed:")) + "\n\t" + //unusable_postfix);
+ wxString::FromUTF8(unusable_postfix.c_str()));
}
else if (m_chosen_name.compare("- default -") == 0) {
show_error(this, _(L("The supplied name is not available.")));
diff --git a/xs/src/slic3r/GUI/Tab.hpp b/xs/src/slic3r/GUI/Tab.hpp
index c755f91f1..230fe659e 100644
--- a/xs/src/slic3r/GUI/Tab.hpp
+++ b/xs/src/slic3r/GUI/Tab.hpp
@@ -175,7 +175,7 @@ protected:
std::vector<std::string> m_reload_dependent_tabs = {};
enum OptStatus { osSystemValue = 1, osInitValue = 2 };
std::map<std::string, int> m_options_list;
- int m_opt_status_value;
+ int m_opt_status_value = 0;
t_icon_descriptions m_icon_descriptions = {};
@@ -316,9 +316,13 @@ public:
class TabPrinter : public Tab
{
bool m_has_single_extruder_MM_page = false;
+ bool m_use_silent_mode = false;
+ void append_option_line(ConfigOptionsGroupShp optgroup, const std::string opt_key);
+ 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;
@@ -333,6 +337,7 @@ public:
void update() override;
void update_serial_ports();
void extruders_count_changed(size_t extruders_count);
+ PageShp build_kinematics_page();
void build_extruder_pages();
void on_preset_loaded() override;
void init_options_list() override;
diff --git a/xs/src/slic3r/ProgressIndicator.hpp b/xs/src/slic3r/ProgressIndicator.hpp
new file mode 100644
index 000000000..1b064077e
--- /dev/null
+++ b/xs/src/slic3r/ProgressIndicator.hpp
@@ -0,0 +1,64 @@
+#ifndef IPROGRESSINDICATOR_HPP
+#define IPROGRESSINDICATOR_HPP
+
+#include <string>
+#include <functional>
+
+namespace Slic3r {
+
+/**
+ * @brief Generic progress indication interface.
+ */
+class ProgressIndicator {
+public:
+ using CancelFn = std::function<void(void)>; // Cancel function signature.
+
+private:
+ float state_ = .0f, max_ = 1.f, step_;
+ CancelFn cancelfunc_ = [](){};
+
+public:
+
+ inline virtual ~ProgressIndicator() {}
+
+ /// Get the maximum of the progress range.
+ float max() const { return max_; }
+
+ /// Get the current progress state
+ float state() const { return state_; }
+
+ /// 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 instead of giving a
+ * maximum value.
+ */
+ virtual void states(unsigned statenum) {
+ step_ = max_ / statenum;
+ }
+
+ /// Message shown on the next status update.
+ virtual void message(const std::string&) = 0;
+
+ /// Title of the operation.
+ virtual void title(const std::string&) = 0;
+
+ /// 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.
+ virtual void on_cancel(CancelFn func = CancelFn()) { cancelfunc_ = func; }
+
+ /// Convenience function to call message and status update in one function.
+ void update(float st, const std::string& msg) {
+ message(msg); state(st);
+ }
+};
+
+}
+
+#endif // IPROGRESSINDICATOR_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/HexFile.cpp b/xs/src/slic3r/Utils/HexFile.cpp
new file mode 100644
index 000000000..282c647bd
--- /dev/null
+++ b/xs/src/slic3r/Utils/HexFile.cpp
@@ -0,0 +1,106 @@
+#include "HexFile.hpp"
+
+#include <sstream>
+#include <boost/filesystem/fstream.hpp>
+#include <boost/property_tree/ptree.hpp>
+#include <boost/property_tree/ini_parser.hpp>
+
+namespace fs = boost::filesystem;
+namespace pt = boost::property_tree;
+
+
+namespace Slic3r {
+namespace Utils {
+
+
+static HexFile::DeviceKind parse_device_kind(const std::string &str)
+{
+ if (str == "mk2") { return HexFile::DEV_MK2; }
+ else if (str == "mk3") { return HexFile::DEV_MK3; }
+ else if (str == "mm-control") { return HexFile::DEV_MM_CONTROL; }
+ else { return HexFile::DEV_GENERIC; }
+}
+
+static size_t hex_num_sections(fs::ifstream &file)
+{
+ file.seekg(0);
+ if (! file.good()) {
+ return 0;
+ }
+
+ static const char *hex_terminator = ":00000001FF\r";
+ size_t res = 0;
+ std::string line;
+ while (getline(file, line, '\n').good()) {
+ // Account for LF vs CRLF
+ if (!line.empty() && line.back() != '\r') {
+ line.push_back('\r');
+ }
+
+ if (line == hex_terminator) {
+ res++;
+ }
+ }
+
+ return res;
+}
+
+HexFile::HexFile(fs::path path) :
+ path(std::move(path))
+{
+ fs::ifstream file(this->path);
+ if (! file.good()) {
+ return;
+ }
+
+ std::string line;
+ std::stringstream header_ini;
+ while (std::getline(file, line, '\n').good()) {
+ if (line.empty()) {
+ continue;
+ }
+
+ // Account for LF vs CRLF
+ if (!line.empty() && line.back() == '\r') {
+ line.pop_back();
+ }
+
+ if (line.front() == ';') {
+ line.front() = ' ';
+ header_ini << line << std::endl;
+ } else if (line.front() == ':') {
+ break;
+ }
+ }
+
+ pt::ptree ptree;
+ try {
+ pt::read_ini(header_ini, ptree);
+ } catch (std::exception &e) {
+ return;
+ }
+
+ bool has_device_meta = false;
+ const auto device = ptree.find("device");
+ if (device != ptree.not_found()) {
+ this->device = parse_device_kind(device->second.data());
+ has_device_meta = true;
+ }
+
+ const auto model_id = ptree.find("model_id");
+ if (model_id != ptree.not_found()) {
+ this->model_id = model_id->second.data();
+ }
+
+ if (! has_device_meta) {
+ // No device metadata, look at the number of 'sections'
+ if (hex_num_sections(file) == 2) {
+ // Looks like a pre-metadata l10n firmware for the MK3, assume that's the case
+ this->device = DEV_MK3;
+ }
+ }
+}
+
+
+}
+}
diff --git a/xs/src/slic3r/Utils/HexFile.hpp b/xs/src/slic3r/Utils/HexFile.hpp
new file mode 100644
index 000000000..1201d23a4
--- /dev/null
+++ b/xs/src/slic3r/Utils/HexFile.hpp
@@ -0,0 +1,33 @@
+#ifndef slic3r_Hex_hpp_
+#define slic3r_Hex_hpp_
+
+#include <string>
+#include <boost/filesystem/path.hpp>
+
+
+namespace Slic3r {
+namespace Utils {
+
+
+struct HexFile
+{
+ enum DeviceKind {
+ DEV_GENERIC,
+ DEV_MK2,
+ DEV_MK3,
+ DEV_MM_CONTROL,
+ };
+
+ boost::filesystem::path path;
+ DeviceKind device = DEV_GENERIC;
+ std::string model_id;
+
+ HexFile() {}
+ HexFile(boost::filesystem::path path);
+};
+
+
+}
+}
+
+#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/PresetUpdater.cpp b/xs/src/slic3r/Utils/PresetUpdater.cpp
index 1ce814b89..6e23ab421 100644
--- a/xs/src/slic3r/Utils/PresetUpdater.cpp
+++ b/xs/src/slic3r/Utils/PresetUpdater.cpp
@@ -322,10 +322,13 @@ Updates PresetUpdater::priv::get_config_updates() const
const auto ver_current = idx.find(vp.config_version);
if (ver_current == idx.end()) {
- BOOST_LOG_TRIVIAL(error) << boost::format("Preset bundle (`%1%`) version not found in index: %2%") % idx.vendor() % vp.config_version.to_string();
- continue;
+ auto message = (boost::format("Preset bundle `%1%` version not found in index: %2%") % idx.vendor() % vp.config_version.to_string()).str();
+ BOOST_LOG_TRIVIAL(error) << message;
+ throw std::runtime_error(message);
}
+ // Getting a recommended version from the latest index, wich may have been downloaded
+ // from the internet, or installed / updated from the installation resources.
const auto recommended = idx.recommended();
if (recommended == idx.end()) {
BOOST_LOG_TRIVIAL(error) << boost::format("No recommended version for vendor: %1%, invalid index?") % idx.vendor();
@@ -353,25 +356,34 @@ Updates PresetUpdater::priv::get_config_updates() const
}
auto path_src = cache_path / (idx.vendor() + ".ini");
+ auto path_in_rsrc = rsrc_path / (idx.vendor() + ".ini");
if (! fs::exists(path_src)) {
- auto path_in_rsrc = rsrc_path / (idx.vendor() + ".ini");
if (! fs::exists(path_in_rsrc)) {
BOOST_LOG_TRIVIAL(warning) << boost::format("Index for vendor %1% indicates update, but bundle found in neither cache nor resources")
- % idx.vendor();;
+ % idx.vendor();
continue;
} else {
path_src = std::move(path_in_rsrc);
+ path_in_rsrc.clear();
}
}
- const auto new_vp = VendorProfile::from_ini(path_src, false);
+ auto new_vp = VendorProfile::from_ini(path_src, false);
+ bool found = false;
if (new_vp.config_version == recommended->config_version) {
updates.updates.emplace_back(std::move(path_src), std::move(bundle_path), *recommended);
- } else {
+ found = true;
+ } else if (! path_in_rsrc.empty() && fs::exists(path_in_rsrc)) {
+ new_vp = VendorProfile::from_ini(path_in_rsrc, false);
+ if (new_vp.config_version == recommended->config_version) {
+ updates.updates.emplace_back(std::move(path_in_rsrc), std::move(bundle_path), *recommended);
+ found = true;
+ }
+ }
+ if (! found)
BOOST_LOG_TRIVIAL(warning) << boost::format("Index for vendor %1% indicates update (%2%) but the new bundle was found neither in cache nor resources")
% idx.vendor()
% recommended->config_version.to_string();
- }
}
}
@@ -530,10 +542,21 @@ bool PresetUpdater::config_update() const
std::unordered_map<std::string, wxString> incompats_map;
for (const auto &incompat : updates.incompats) {
auto vendor = incompat.name();
- auto restrictions = wxString::Format(_(L("requires min. %s and max. %s")),
- incompat.version.min_slic3r_version.to_string(),
- incompat.version.max_slic3r_version.to_string()
- );
+
+ const auto min_slic3r = incompat.version.min_slic3r_version;
+ const auto max_slic3r = incompat.version.max_slic3r_version;
+ wxString restrictions;
+ if (min_slic3r != Semver::zero() && max_slic3r != Semver::inf()) {
+ restrictions = wxString::Format(_(L("requires min. %s and max. %s")),
+ min_slic3r.to_string(),
+ max_slic3r.to_string()
+ );
+ } else if (min_slic3r != Semver::zero()) {
+ restrictions = wxString::Format(_(L("requires min. %s")), min_slic3r.to_string());
+ } else {
+ restrictions = wxString::Format(_(L("requires max. %s")), max_slic3r.to_string());
+ }
+
incompats_map.emplace(std::make_pair(std::move(vendor), std::move(restrictions)));
}
@@ -545,9 +568,10 @@ bool PresetUpdater::config_update() const
BOOST_LOG_TRIVIAL(info) << "User wants to re-configure...";
p->perform_updates(std::move(updates));
GUI::ConfigWizard wizard(nullptr, GUI::ConfigWizard::RR_DATA_INCOMPAT);
- if (! wizard.run(GUI::get_preset_bundle(), this)) {
+ if (! wizard.run(GUI::get_preset_bundle(), this)) {
return false;
}
+ GUI::load_current_presets();
} else {
BOOST_LOG_TRIVIAL(info) << "User wants to exit Slic3r, bye...";
return false;
@@ -577,7 +601,6 @@ bool PresetUpdater::config_update() const
// Reload global configuration
auto *app_config = GUI::get_app_config();
- app_config->reset_selections();
GUI::get_preset_bundle()->load_presets(*app_config);
GUI::load_current_presets();
} else {
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/src/slic3r/Utils/Serial.cpp b/xs/src/slic3r/Utils/Serial.cpp
index 32b6bb160..601719b50 100644
--- a/xs/src/slic3r/Utils/Serial.cpp
+++ b/xs/src/slic3r/Utils/Serial.cpp
@@ -3,17 +3,22 @@
#include <algorithm>
#include <string>
#include <vector>
+#include <chrono>
+#include <thread>
#include <fstream>
+#include <stdexcept>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/filesystem.hpp>
#include <boost/format.hpp>
+#include <boost/optional.hpp>
#if _WIN32
#include <Windows.h>
#include <Setupapi.h>
#include <initguid.h>
#include <devguid.h>
+ #include <regex>
// Undefine min/max macros incompatible with the standard library
// For example, std::numeric_limits<std::streamsize>::max()
// produces some weird errors
@@ -34,6 +39,23 @@
#include <sys/syslimits.h>
#endif
+#ifndef _WIN32
+ #include <sys/ioctl.h>
+ #include <sys/time.h>
+ #include <sys/unistd.h>
+ #include <sys/select.h>
+#endif
+
+#if defined(__APPLE__) || defined(__OpenBSD__)
+ #include <termios.h>
+#elif defined __linux__
+ #include <fcntl.h>
+ #include <asm-generic/ioctls.h>
+#endif
+
+using boost::optional;
+
+
namespace Slic3r {
namespace Utils {
@@ -42,15 +64,43 @@ static bool looks_like_printer(const std::string &friendly_name)
return friendly_name.find("Original Prusa") != std::string::npos;
}
+#if _WIN32
+void parse_hardware_id(const std::string &hardware_id, SerialPortInfo &spi)
+{
+ unsigned vid, pid;
+ std::regex pattern("USB\\\\.*VID_([[:xdigit:]]+)&PID_([[:xdigit:]]+).*");
+ std::smatch matches;
+ if (std::regex_match(hardware_id, matches, pattern)) {
+ try {
+ vid = std::stoul(matches[1].str(), 0, 16);
+ pid = std::stoul(matches[2].str(), 0, 16);
+ spi.id_vendor = vid;
+ spi.id_product = pid;
+ }
+ catch (...) {}
+ }
+}
+#endif
+
#ifdef __linux__
-static std::string get_tty_friendly_name(const std::string &path, const std::string &name)
+optional<std::string> sysfs_tty_prop(const std::string &tty_dev, const std::string &name)
{
- const auto sysfs_product = (boost::format("/sys/class/tty/%1%/device/../product") % name).str();
- std::ifstream file(sysfs_product);
- std::string product;
+ const auto prop_path = (boost::format("/sys/class/tty/%1%/device/../%2%") % tty_dev % name).str();
+ std::ifstream file(prop_path);
+ std::string res;
- std::getline(file, product);
- return file.good() ? (boost::format("%1% (%2%)") % product % path).str() : path;
+ std::getline(file, res);
+ if (file.good()) { return res; }
+ else { return boost::none; }
+}
+
+optional<unsigned long> sysfs_tty_prop_hex(const std::string &tty_dev, const std::string &name)
+{
+ auto prop = sysfs_tty_prop(tty_dev, name);
+ if (!prop) { return boost::none; }
+
+ try { return std::stoul(*prop, 0, 16); }
+ catch (...) { return boost::none; }
}
#endif
@@ -80,6 +130,7 @@ std::vector<SerialPortInfo> scan_serial_ports_extended()
if (port_info.port.empty())
continue;
}
+
// Find the size required to hold the device info.
DWORD regDataType;
DWORD reqSize = 0;
@@ -88,7 +139,8 @@ std::vector<SerialPortInfo> scan_serial_ports_extended()
// Now store it in a buffer.
if (! SetupDiGetDeviceRegistryProperty(hDeviceInfo, &devInfoData, SPDRP_HARDWAREID, &regDataType, (BYTE*)hardware_id.data(), reqSize, nullptr))
continue;
- port_info.hardware_id = boost::nowide::narrow(hardware_id.data());
+ parse_hardware_id(boost::nowide::narrow(hardware_id.data()), port_info);
+
// Find the size required to hold the friendly name.
reqSize = 0;
SetupDiGetDeviceRegistryProperty(hDeviceInfo, &devInfoData, SPDRP_FRIENDLYNAME, nullptr, nullptr, 0, &reqSize);
@@ -120,6 +172,8 @@ std::vector<SerialPortInfo> scan_serial_ports_extended()
if (result) {
SerialPortInfo port_info;
port_info.port = path;
+
+ // Attempt to read out the device friendly name
if ((cf_property = IORegistryEntrySearchCFProperty(port, kIOServicePlane,
CFSTR("USB Interface Name"), kCFAllocatorDefault,
kIORegistryIterateRecursively | kIORegistryIterateParents)) ||
@@ -141,6 +195,23 @@ std::vector<SerialPortInfo> scan_serial_ports_extended()
}
if (port_info.friendly_name.empty())
port_info.friendly_name = port_info.port;
+
+ // Attempt to read out the VID & PID
+ int vid, pid;
+ auto cf_vendor = IORegistryEntrySearchCFProperty(port, kIOServicePlane, CFSTR("idVendor"),
+ kCFAllocatorDefault, kIORegistryIterateRecursively | kIORegistryIterateParents);
+ auto cf_product = IORegistryEntrySearchCFProperty(port, kIOServicePlane, CFSTR("idProduct"),
+ kCFAllocatorDefault, kIORegistryIterateRecursively | kIORegistryIterateParents);
+ if (cf_vendor && cf_product) {
+ if (CFNumberGetValue((CFNumberRef)cf_vendor, kCFNumberIntType, &vid) &&
+ CFNumberGetValue((CFNumberRef)cf_product, kCFNumberIntType, &pid)) {
+ port_info.id_vendor = vid;
+ port_info.id_product = pid;
+ }
+ }
+ if (cf_vendor) { CFRelease(cf_vendor); }
+ if (cf_product) { CFRelease(cf_product); }
+
output.emplace_back(std::move(port_info));
}
}
@@ -158,9 +229,20 @@ std::vector<SerialPortInfo> scan_serial_ports_extended()
const auto path = dir_entry.path().string();
SerialPortInfo spi;
spi.port = path;
- spi.hardware_id = path;
#ifdef __linux__
- spi.friendly_name = get_tty_friendly_name(path, name);
+ auto friendly_name = sysfs_tty_prop(name, "product");
+ if (friendly_name) {
+ spi.is_printer = looks_like_printer(*friendly_name);
+ spi.friendly_name = (boost::format("%1% (%2%)") % *friendly_name % path).str();
+ } else {
+ spi.friendly_name = path;
+ }
+ auto vid = sysfs_tty_prop_hex(name, "idVendor");
+ auto pid = sysfs_tty_prop_hex(name, "idProduct");
+ if (vid && pid) {
+ spi.id_vendor = *vid;
+ spi.id_product = *pid;
+ }
#else
spi.friendly_name = path;
#endif
@@ -189,5 +271,225 @@ std::vector<std::string> scan_serial_ports()
return output;
}
+
+
+// Class Serial
+
+namespace asio = boost::asio;
+using boost::system::error_code;
+
+Serial::Serial(asio::io_service& io_service) :
+ asio::serial_port(io_service)
+{}
+
+Serial::Serial(asio::io_service& io_service, const std::string &name, unsigned baud_rate) :
+ asio::serial_port(io_service, name)
+{
+ set_baud_rate(baud_rate);
+}
+
+Serial::~Serial() {}
+
+void Serial::set_baud_rate(unsigned baud_rate)
+{
+ try {
+ // This does not support speeds > 115200
+ set_option(boost::asio::serial_port_base::baud_rate(baud_rate));
+ } catch (boost::system::system_error &) {
+ auto handle = native_handle();
+
+ auto handle_errno = [](int retval) {
+ if (retval != 0) {
+ throw std::runtime_error(
+ (boost::format("Could not set baud rate: %1%") % strerror(errno)).str()
+ );
+ }
+ };
+
+#if __APPLE__
+ termios ios;
+ handle_errno(::tcgetattr(handle, &ios));
+ handle_errno(::cfsetspeed(&ios, baud_rate));
+ speed_t newSpeed = baud_rate;
+ handle_errno(::ioctl(handle, IOSSIOSPEED, &newSpeed));
+ handle_errno(::tcsetattr(handle, TCSANOW, &ios));
+#elif __linux
+
+ /* The following definitions are kindly borrowed from:
+ /usr/include/asm-generic/termbits.h
+ Unfortunately we cannot just include that one because
+ it would redefine the "struct termios" already defined
+ the <termios.h> already included by Boost.ASIO. */
+#define K_NCCS 19
+ struct termios2 {
+ tcflag_t c_iflag;
+ tcflag_t c_oflag;
+ tcflag_t c_cflag;
+ tcflag_t c_lflag;
+ cc_t c_line;
+ cc_t c_cc[K_NCCS];
+ speed_t c_ispeed;
+ speed_t c_ospeed;
+ };
+#define BOTHER CBAUDEX
+
+ termios2 ios;
+ handle_errno(::ioctl(handle, TCGETS2, &ios));
+ ios.c_ispeed = ios.c_ospeed = baud_rate;
+ ios.c_cflag &= ~CBAUD;
+ ios.c_cflag |= BOTHER | CLOCAL | CREAD;
+ ios.c_cc[VMIN] = 1; // Minimum of characters to read, prevents eof errors when 0 bytes are read
+ ios.c_cc[VTIME] = 1;
+ handle_errno(::ioctl(handle, TCSETS2, &ios));
+
+#elif __OpenBSD__
+ struct termios ios;
+ handle_errno(::tcgetattr(handle, &ios));
+ handle_errno(::cfsetspeed(&ios, baud_rate));
+ handle_errno(::tcsetattr(handle, TCSAFLUSH, &ios));
+#else
+ throw std::runtime_error("Custom baud rates are not currently supported on this OS");
+#endif
+ }
+}
+
+void Serial::set_DTR(bool on)
+{
+ auto handle = native_handle();
+#if defined(_WIN32) && !defined(__SYMBIAN32__)
+ if (! EscapeCommFunction(handle, on ? SETDTR : CLRDTR)) {
+ throw std::runtime_error("Could not set serial port DTR");
+ }
+#else
+ int status;
+ if (::ioctl(handle, TIOCMGET, &status) == 0) {
+ on ? status |= TIOCM_DTR : status &= ~TIOCM_DTR;
+ if (::ioctl(handle, TIOCMSET, &status) == 0) {
+ return;
+ }
+ }
+
+ throw std::runtime_error(
+ (boost::format("Could not set serial port DTR: %1%") % strerror(errno)).str()
+ );
+#endif
+}
+
+void Serial::reset_line_num()
+{
+ // See https://github.com/MarlinFirmware/Marlin/wiki/M110
+ write_string("M110 N0\n");
+ m_line_num = 0;
+}
+
+bool Serial::read_line(unsigned timeout, std::string &line, error_code &ec)
+{
+ auto &io_service = get_io_service();
+ asio::deadline_timer timer(io_service);
+ char c = 0;
+ bool fail = false;
+
+ while (true) {
+ io_service.reset();
+
+ asio::async_read(*this, boost::asio::buffer(&c, 1), [&](const error_code &read_ec, size_t size) {
+ if (ec || size == 0) {
+ fail = true;
+ ec = read_ec; // FIXME: only if operation not aborted
+ }
+ timer.cancel(); // FIXME: ditto
+ });
+
+ if (timeout > 0) {
+ timer.expires_from_now(boost::posix_time::milliseconds(timeout));
+ timer.async_wait([&](const error_code &ec) {
+ // Ignore timer aborts
+ if (!ec) {
+ fail = true;
+ this->cancel();
+ }
+ });
+ }
+
+ io_service.run();
+
+ if (fail) {
+ return false;
+ } else if (c != '\n') {
+ line += c;
+ } else {
+ return true;
+ }
+ }
+}
+
+void Serial::printer_setup()
+{
+ printer_reset();
+ write_string("\r\r\r\r\r\r\r\r\r\r"); // Gets rid of line noise, if any
+}
+
+size_t Serial::write_string(const std::string &str)
+{
+ // TODO: might be wise to timeout here as well
+ return asio::write(*this, asio::buffer(str));
+}
+
+bool Serial::printer_ready_wait(unsigned retries, unsigned timeout)
+{
+ std::string line;
+ error_code ec;
+
+ for (; retries > 0; retries--) {
+ reset_line_num();
+
+ while (read_line(timeout, line, ec)) {
+ if (line == "ok") {
+ return true;
+ }
+ line.clear();
+ }
+
+ line.clear();
+ }
+
+ return false;
+}
+
+size_t Serial::printer_write_line(const std::string &line, unsigned line_num)
+{
+ const auto formatted_line = Utils::Serial::printer_format_line(line, line_num);
+ return write_string(formatted_line);
+}
+
+size_t Serial::printer_write_line(const std::string &line)
+{
+ m_line_num++;
+ return printer_write_line(line, m_line_num);
+}
+
+void Serial::printer_reset()
+{
+ this->set_DTR(false);
+ std::this_thread::sleep_for(std::chrono::milliseconds(200));
+ this->set_DTR(true);
+ std::this_thread::sleep_for(std::chrono::milliseconds(200));
+ this->set_DTR(false);
+ std::this_thread::sleep_for(std::chrono::milliseconds(1000));
+}
+
+std::string Serial::printer_format_line(const std::string &line, unsigned line_num)
+{
+ const auto line_num_str = std::to_string(line_num);
+
+ unsigned checksum = 'N';
+ for (auto c : line_num_str) { checksum ^= c; }
+ checksum ^= ' ';
+ for (auto c : line) { checksum ^= c; }
+
+ return (boost::format("N%1% %2%*%3%\n") % line_num_str % line % checksum).str();
+}
+
+
} // namespace Utils
} // namespace Slic3r
diff --git a/xs/src/slic3r/Utils/Serial.hpp b/xs/src/slic3r/Utils/Serial.hpp
index 9381ab398..e4a28de09 100644
--- a/xs/src/slic3r/Utils/Serial.hpp
+++ b/xs/src/slic3r/Utils/Serial.hpp
@@ -1,31 +1,81 @@
#ifndef slic3r_GUI_Utils_Serial_hpp_
#define slic3r_GUI_Utils_Serial_hpp_
-#include <memory>
#include <vector>
#include <string>
-#include <vector>
+#include <boost/system/error_code.hpp>
+#include <boost/asio.hpp>
+
namespace Slic3r {
namespace Utils {
struct SerialPortInfo {
std::string port;
- std::string hardware_id;
+ unsigned id_vendor = -1;
+ unsigned id_product = -1;
std::string friendly_name;
- bool is_printer = false;
+ bool is_printer = false;
+
+ bool id_match(unsigned id_vendor, unsigned id_product) const { return id_vendor == this->id_vendor && id_product == this->id_product; }
};
-inline bool operator==(const SerialPortInfo &sp1, const SerialPortInfo &sp2)
+inline bool operator==(const SerialPortInfo &sp1, const SerialPortInfo &sp2)
{
- return sp1.port == sp2.port &&
- sp1.hardware_id == sp2.hardware_id &&
- sp1.is_printer == sp2.is_printer;
+ return
+ sp1.port == sp2.port &&
+ sp1.id_vendor == sp2.id_vendor &&
+ sp1.id_product == sp2.id_product &&
+ sp1.is_printer == sp2.is_printer;
}
extern std::vector<std::string> scan_serial_ports();
extern std::vector<SerialPortInfo> scan_serial_ports_extended();
+
+class Serial : public boost::asio::serial_port
+{
+public:
+ Serial(boost::asio::io_service &io_service);
+ Serial(boost::asio::io_service &io_service, const std::string &name, unsigned baud_rate);
+ Serial(const Serial &) = delete;
+ Serial &operator=(const Serial &) = delete;
+ ~Serial();
+
+ void set_baud_rate(unsigned baud_rate);
+ void set_DTR(bool on);
+
+ // Resets the line number both internally as well as with the firmware using M110
+ void reset_line_num();
+
+ // Reads a line or times out, the timeout is in milliseconds
+ bool read_line(unsigned timeout, std::string &line, boost::system::error_code &ec);
+
+ // Perform an initial setup for communicating with a printer
+ void printer_setup();
+
+ // Write data from a string
+ size_t write_string(const std::string &str);
+
+ // Attempts to reset the line numer and waits until the printer says "ok"
+ bool printer_ready_wait(unsigned retries, unsigned timeout);
+
+ // Write Marlin-formatted line, with a line number and a checksum
+ size_t printer_write_line(const std::string &line, unsigned line_num);
+
+ // Same as above, but with internally-managed line number
+ size_t printer_write_line(const std::string &line);
+
+ // Toggles DTR to reset the printer
+ void printer_reset();
+
+ // Formats a line Marlin-style, ie. with a sequential number and a checksum
+ static std::string printer_format_line(const std::string &line, unsigned line_num);
+private:
+ unsigned m_line_num = 0;
+};
+
+
} // Utils
} // Slic3r
diff --git a/xs/src/xsinit.h b/xs/src/xsinit.h
index 01c1293ac..9365f1979 100644
--- a/xs/src/xsinit.h
+++ b/xs/src/xsinit.h
@@ -80,6 +80,7 @@ extern "C" {
#include <Polygon.hpp>
#include <Polyline.hpp>
#include <TriangleMesh.hpp>
+#include <slic3r/AppController.hpp>
namespace Slic3r {
diff --git a/xs/t/01_trianglemesh.t b/xs/t/01_trianglemesh.t
index fd57cf805..4013a1f83 100644
--- a/xs/t/01_trianglemesh.t
+++ b/xs/t/01_trianglemesh.t
@@ -79,7 +79,9 @@ my $cube = {
my $m = Slic3r::TriangleMesh->new;
$m->ReadFromPerl($cube->{vertices}, $cube->{facets});
$m->repair;
- my @z = (0,2,4,8,6,8,10,12,14,16,18,20);
+ # The slice at zero height does not belong to the mesh, the slicing considers the vertical structures to be
+ # open intervals at the bottom end, closed at the top end.
+ my @z = (0.0001,2,4,8,6,8,10,12,14,16,18,20);
my $result = $m->slice(\@z);
my $SCALING_FACTOR = 0.000001;
for my $i (0..$#z) {
@@ -105,7 +107,9 @@ my $cube = {
# this second test also checks that performing a second slice on a mesh after
# a transformation works properly (shared_vertices is correctly invalidated);
# at Z = -10 we have a bottom horizontal surface
- my $slices = $m->slice([ -5, -10 ]);
+ # (The slice at zero height does not belong to the mesh, the slicing considers the vertical structures to be
+ # open intervals at the bottom end, closed at the top end, so the Z = -10 is shifted a bit up to get a valid slice).
+ my $slices = $m->slice([ -5, -10+0.00001 ]);
is $slices->[0][0]->area, $slices->[1][0]->area, 'slicing a bottom tangent plane includes its area';
}
}
diff --git a/xs/xsp/AppController.xsp b/xs/xsp/AppController.xsp
new file mode 100644
index 000000000..a578fe0b1
--- /dev/null
+++ b/xs/xsp/AppController.xsp
@@ -0,0 +1,24 @@
+%module{Slic3r::XS};
+
+%{
+#include <xsinit.h>
+#include "slic3r/AppController.hpp"
+#include "libslic3r/Model.hpp"
+#include "libslic3r/Print.hpp"
+%}
+
+%name{Slic3r::PrintController} class PrintController {
+ PrintController(Print *print);
+};
+
+%name{Slic3r::AppController} class AppController {
+
+ AppController();
+
+ PrintController *print_ctl();
+ void set_model(Model *model);
+ void set_print(Print *print);
+ void set_global_progress_indicator(unsigned gauge_id, unsigned statusbar_id);
+
+ void arrange_model();
+}; \ No newline at end of file
diff --git a/xs/xsp/Config.xsp b/xs/xsp/Config.xsp
index 6adfc49a2..b8ad84ba4 100644
--- a/xs/xsp/Config.xsp
+++ b/xs/xsp/Config.xsp
@@ -74,13 +74,13 @@
static StaticPrintConfig* new_GCodeConfig()
%code{% RETVAL = new GCodeConfig(); %};
static StaticPrintConfig* new_PrintConfig()
- %code{% RETVAL = new PrintConfig(); %};
+ %code{% RETVAL = static_cast<GCodeConfig*>(new PrintConfig()); %};
static StaticPrintConfig* new_PrintObjectConfig()
%code{% RETVAL = new PrintObjectConfig(); %};
static StaticPrintConfig* new_PrintRegionConfig()
%code{% RETVAL = new PrintRegionConfig(); %};
static StaticPrintConfig* new_FullPrintConfig()
- %code{% RETVAL = static_cast<PrintObjectConfig*>(new FullPrintConfig()); %};
+ %code{% RETVAL = static_cast<GCodeConfig*>(new FullPrintConfig()); %};
~StaticPrintConfig();
bool has(t_config_option_key opt_key);
SV* as_hash()
@@ -119,7 +119,7 @@
auto config = new FullPrintConfig();
try {
config->load(path);
- RETVAL = static_cast<PrintObjectConfig*>(config);
+ RETVAL = static_cast<GCodeConfig*>(config);
} catch (std::exception& e) {
delete config;
croak("Error extracting configuration from %s:\n%s\n", path, e.what());
diff --git a/xs/xsp/GUI.xsp b/xs/xsp/GUI.xsp
index af0612f19..8d2efb858 100644
--- a/xs/xsp/GUI.xsp
+++ b/xs/xsp/GUI.xsp
@@ -101,3 +101,19 @@ void desktop_open_datadir_folder()
void fix_model_by_win10_sdk_gui(ModelObject *model_object_src, Print *print, Model *model_dst)
%code%{ Slic3r::fix_model_by_win10_sdk_gui(*model_object_src, *print, *model_dst); %};
+
+void set_3DScene(SV *scene)
+ %code%{ Slic3r::GUI::set_3DScene((_3DScene *)wxPli_sv_2_object(aTHX_ scene, "Slic3r::Model::3DScene") ); %};
+
+void register_on_request_update_callback(SV* callback)
+ %code%{ Slic3r::GUI::g_on_request_update_callback.register_callback(callback); %};
+
+void deregister_on_request_update_callback()
+ %code%{ Slic3r::GUI::g_on_request_update_callback.deregister_callback(); %};
+
+void save_window_size(SV *window, std::string name)
+ %code%{ Slic3r::GUI::save_window_size((wxTopLevelWindow*)wxPli_sv_2_object(aTHX_ window, "Wx::TopLevelWindow"), name); %};
+
+void restore_window_size(SV *window, std::string name)
+ %code%{ Slic3r::GUI::restore_window_size((wxTopLevelWindow*)wxPli_sv_2_object(aTHX_ window, "Wx::TopLevelWindow"), name); %};
+
diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp
index 48bd30cd1..1a0fc9b9f 100644
--- a/xs/xsp/GUI_3DScene.xsp
+++ b/xs/xsp/GUI_3DScene.xsp
@@ -56,12 +56,15 @@
int volume_idx() const;
int instance_idx() const;
Clone<Pointf3> origin() const
- %code%{ RETVAL = THIS->origin; %};
+ %code%{ RETVAL = THIS->get_origin(); %};
void translate(double x, double y, double z)
- %code%{ THIS->origin.translate(x, y, z); %};
+ %code%{
+ Pointf3 o = THIS->get_origin();
+ o.translate(x, y, z);
+ THIS->set_origin(o);
+ %};
Clone<BoundingBoxf3> bounding_box() const
%code%{ RETVAL = THIS->bounding_box; %};
- Clone<BoundingBoxf3> transformed_bounding_box() const;
bool empty() const;
bool indexed() const;
@@ -85,7 +88,7 @@
std::vector<int> load_object(ModelObject *object, int obj_idx, std::vector<int> instance_idxs, std::string color_by, std::string select_by, std::string drag_by, bool use_VBOs);
- int load_wipe_tower_preview(int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool use_VBOs);
+ int load_wipe_tower_preview(int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool use_VBOs, bool size_unknown, float brim_width);
void erase()
%code{% THIS->clear(); %};
@@ -101,10 +104,6 @@
void release_geometry();
void set_print_box(float min_x, float min_y, float min_z, float max_x, float max_y, float max_z);
- bool check_outside_state(DynamicPrintConfig* config)
- %code%{
- RETVAL = THIS->check_outside_state(config);
- %};
void reset_outside_state();
void update_colors_by_extruder(DynamicPrintConfig* config);
@@ -230,7 +229,7 @@ update_volumes_selection(canvas, selections)
CODE:
_3DScene::update_volumes_selection((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), selections);
-bool
+int
check_volumes_outside_state(canvas, config)
SV *canvas;
DynamicPrintConfig *config;
@@ -431,6 +430,13 @@ enable_force_zoom_to_bed(canvas, enable)
_3DScene::enable_force_zoom_to_bed((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), enable);
void
+enable_dynamic_background(canvas, enable)
+ SV *canvas;
+ bool enable;
+ CODE:
+ _3DScene::enable_dynamic_background((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), enable);
+
+void
allow_multisample(canvas, allow)
SV *canvas;
bool allow;
@@ -470,6 +476,12 @@ update_volumes_colors_by_extruder(canvas)
_3DScene::update_volumes_colors_by_extruder((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"));
void
+update_gizmos_data(canvas)
+ SV *canvas;
+ CODE:
+ _3DScene::update_gizmos_data((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"));
+
+void
render(canvas)
SV *canvas;
CODE:
@@ -604,63 +616,25 @@ register_on_gizmo_scale_uniformly_callback(canvas, callback)
CODE:
_3DScene::register_on_gizmo_scale_uniformly_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback);
-unsigned int
-finalize_legend_texture()
- CODE:
- RETVAL = _3DScene::finalize_legend_texture();
- OUTPUT:
- RETVAL
-
-unsigned int
-get_legend_texture_width()
+void
+register_on_gizmo_rotate_callback(canvas, callback)
+ SV *canvas;
+ SV *callback;
CODE:
- RETVAL = _3DScene::get_legend_texture_width();
- OUTPUT:
- RETVAL
+ _3DScene::register_on_gizmo_rotate_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback);
-unsigned int
-get_legend_texture_height()
+void
+register_on_update_geometry_info_callback(canvas, callback)
+ SV *canvas;
+ SV *callback;
CODE:
- RETVAL = _3DScene::get_legend_texture_height();
- OUTPUT:
- RETVAL
+ _3DScene::register_on_update_geometry_info_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback);
void
reset_legend_texture()
CODE:
_3DScene::reset_legend_texture();
-void
-generate_warning_texture(std::string msg)
- CODE:
- _3DScene::generate_warning_texture(msg);
-
-unsigned int
-finalize_warning_texture()
- CODE:
- RETVAL = _3DScene::finalize_warning_texture();
- OUTPUT:
- RETVAL
-
-unsigned int
-get_warning_texture_width()
- CODE:
- RETVAL = _3DScene::get_warning_texture_width();
- OUTPUT:
- RETVAL
-
-unsigned int
-get_warning_texture_height()
- CODE:
- RETVAL = _3DScene::get_warning_texture_height();
- OUTPUT:
- RETVAL
-
-void
-reset_warning_texture()
- CODE:
- _3DScene::reset_warning_texture();
-
std::vector<int>
load_model_object(canvas, model_object, obj_idx, instance_idxs)
SV *canvas;
@@ -689,33 +663,19 @@ reload_scene(canvas, force)
CODE:
_3DScene::reload_scene((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), force);
-void
-load_print_toolpaths(canvas)
- SV *canvas;
- CODE:
- _3DScene::load_print_toolpaths((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"));
-
void
-load_print_object_toolpaths(canvas, print_object, tool_colors)
+load_gcode_preview(canvas, preview_data, str_tool_colors)
SV *canvas;
- PrintObject *print_object;
- std::vector<std::string> tool_colors;
+ GCodePreviewData *preview_data;
+ std::vector<std::string> str_tool_colors;
CODE:
- _3DScene::load_print_object_toolpaths((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), print_object, tool_colors);
+ _3DScene::load_gcode_preview((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), preview_data, str_tool_colors);
void
-load_wipe_tower_toolpaths(canvas, tool_colors)
- SV *canvas;
- std::vector<std::string> tool_colors;
- CODE:
- _3DScene::load_wipe_tower_toolpaths((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), tool_colors);
-
-void
-load_gcode_preview(canvas, preview_data, str_tool_colors)
+load_preview(canvas, str_tool_colors)
SV *canvas;
- GCodePreviewData *preview_data;
std::vector<std::string> str_tool_colors;
CODE:
- _3DScene::load_gcode_preview((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), preview_data, str_tool_colors);
-
+ _3DScene::load_preview((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), str_tool_colors);
+
%}
diff --git a/xs/xsp/Layer.xsp b/xs/xsp/Layer.xsp
index 4f09fb521..efd6c9ae6 100644
--- a/xs/xsp/Layer.xsp
+++ b/xs/xsp/Layer.xsp
@@ -17,8 +17,6 @@
%code%{ RETVAL = &THIS->thin_fills; %};
Ref<SurfaceCollection> fill_surfaces()
%code%{ RETVAL = &THIS->fill_surfaces; %};
- Ref<SurfaceCollection> perimeter_surfaces()
- %code%{ RETVAL = &THIS->perimeter_surfaces; %};
Polygons bridged()
%code%{ RETVAL = THIS->bridged; %};
Ref<PolylineCollection> unsupported_bridge_edges()
diff --git a/xs/xsp/Model.xsp b/xs/xsp/Model.xsp
index 25c26c380..0b59b3126 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)
@@ -337,11 +338,23 @@ ModelMaterial::attributes()
%code%{ RETVAL = &THIS->config; %};
Ref<TriangleMesh> mesh()
%code%{ RETVAL = &THIS->mesh; %};
+ Ref<TriangleMesh> convex_hull()
+ %code%{ RETVAL = &THIS->get_convex_hull(); %};
bool modifier()
- %code%{ RETVAL = THIS->modifier; %};
+ %code%{ RETVAL = THIS->is_modifier(); %};
void set_modifier(bool modifier)
- %code%{ THIS->modifier = modifier; %};
+ %code%{ THIS->set_type(modifier ? ModelVolume::PARAMETER_MODIFIER : ModelVolume::MODEL_PART); %};
+ bool model_part()
+ %code%{ RETVAL = THIS->is_model_part(); %};
+ bool support_enforcer()
+ %code%{ RETVAL = THIS->is_support_enforcer(); %};
+ void set_support_enforcer()
+ %code%{ THIS->set_type(ModelVolume::SUPPORT_ENFORCER); %};
+ bool support_blocker()
+ %code%{ RETVAL = THIS->is_support_blocker(); %};
+ void set_support_blocker()
+ %code%{ THIS->set_type(ModelVolume::SUPPORT_BLOCKER); %};
size_t split(unsigned int max_extruders);
diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp
index b53b5e82d..1dee8a4c4 100644
--- a/xs/xsp/Print.xsp
+++ b/xs/xsp/Print.xsp
@@ -133,7 +133,7 @@ _constant()
~Print();
Ref<StaticPrintConfig> config()
- %code%{ RETVAL = &THIS->config; %};
+ %code%{ RETVAL = static_cast<GCodeConfig*>(&THIS->config); %};
Ref<StaticPrintConfig> default_object_config()
%code%{ RETVAL = &THIS->default_object_config; %};
Ref<StaticPrintConfig> default_region_config()
@@ -145,8 +145,10 @@ _constant()
%code%{ RETVAL = &THIS->skirt; %};
Ref<ExtrusionEntityCollection> brim()
%code%{ RETVAL = &THIS->brim; %};
- std::string estimated_print_time()
- %code%{ RETVAL = THIS->estimated_print_time; %};
+ std::string estimated_normal_print_time()
+ %code%{ RETVAL = THIS->estimated_normal_print_time; %};
+ std::string estimated_silent_print_time()
+ %code%{ RETVAL = THIS->estimated_silent_print_time; %};
PrintObjectPtrs* objects()
%code%{ RETVAL = &THIS->objects; %};
@@ -275,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/XS.xsp b/xs/xsp/XS.xsp
index e900532aa..04969a7f9 100644
--- a/xs/xsp/XS.xsp
+++ b/xs/xsp/XS.xsp
@@ -49,6 +49,11 @@ trace(level, message)
Slic3r::trace(level, message);
void
+disable_multi_threading()
+ CODE:
+ Slic3r::disable_multi_threading();
+
+void
set_var_dir(dir)
char *dir;
CODE:
diff --git a/xs/xsp/my.map b/xs/xsp/my.map
index cc902acae..ba20ee236 100644
--- a/xs/xsp/my.map
+++ b/xs/xsp/my.map
@@ -216,6 +216,8 @@ Ref<PrintObjectSupportMaterial> O_OBJECT_SLIC3R_T
Clone<PrintObjectSupportMaterial> O_OBJECT_SLIC3R_T
AppConfig* O_OBJECT_SLIC3R
+AppController* O_OBJECT_SLIC3R
+PrintController* O_OBJECT_SLIC3R
Ref<AppConfig> O_OBJECT_SLIC3R_T
GLShader* O_OBJECT_SLIC3R
@@ -237,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 57eae1598..cee75fe26 100644
--- a/xs/xsp/typemap.xspt
+++ b/xs/xsp/typemap.xspt
@@ -268,3 +268,6 @@
$CVar = (PrintObjectStep)SvUV($PerlVar);
%};
};
+%typemap{AppController*};
+%typemap{PrintController*};
+%typemap{PrintHost*};