Преглед изворни кода

BRL.Polygon. Initial Import

Added indices argument to DrawPoly.
Brucey пре 2 година
родитељ
комит
446ff75c04
88 измењених фајлова са 8399 додато и 6 уклоњено
  1. 1 1
      d3d7max2d.mod/d3d7max2d.bmx
  2. 1 1
      d3d9max2d.mod/d3d9max2d.bmx
  3. 1 1
      glmax2d.mod/glmax2d.bmx
  4. 1 1
      max2d.mod/driver.bmx
  5. 2 2
      max2d.mod/max2d.bmx
  6. 30 0
      polygon.mod/common.bmx
  7. 27 0
      polygon.mod/earcut/CHANGELOG.md
  8. 151 0
      polygon.mod/earcut/CMakeLists.txt
  9. 15 0
      polygon.mod/earcut/LICENSE
  10. 131 0
      polygon.mod/earcut/README.md
  11. 33 0
      polygon.mod/earcut/appveyor.yml
  12. 816 0
      polygon.mod/earcut/include/mapbox/earcut.hpp
  13. 82 0
      polygon.mod/earcut/test/bench.cpp
  14. 43 0
      polygon.mod/earcut/test/comparison/earcut.hpp
  15. 105 0
      polygon.mod/earcut/test/comparison/libtess2.hpp
  16. 25 0
      polygon.mod/earcut/test/comparison/libtess2/LICENSE.txt
  17. 191 0
      polygon.mod/earcut/test/comparison/libtess2/bucketalloc.c
  18. 51 0
      polygon.mod/earcut/test/comparison/libtess2/bucketalloc.h
  19. 109 0
      polygon.mod/earcut/test/comparison/libtess2/dict.c
  20. 74 0
      polygon.mod/earcut/test/comparison/libtess2/dict.h
  21. 261 0
      polygon.mod/earcut/test/comparison/libtess2/geom.c
  22. 76 0
      polygon.mod/earcut/test/comparison/libtess2/geom.h
  23. 843 0
      polygon.mod/earcut/test/comparison/libtess2/mesh.c
  24. 267 0
      polygon.mod/earcut/test/comparison/libtess2/mesh.h
  25. 514 0
      polygon.mod/earcut/test/comparison/libtess2/priorityq.c
  26. 104 0
      polygon.mod/earcut/test/comparison/libtess2/priorityq.h
  27. 1326 0
      polygon.mod/earcut/test/comparison/libtess2/sweep.c
  28. 74 0
      polygon.mod/earcut/test/comparison/libtess2/sweep.h
  29. 982 0
      polygon.mod/earcut/test/comparison/libtess2/tess.c
  30. 90 0
      polygon.mod/earcut/test/comparison/libtess2/tess.h
  31. 221 0
      polygon.mod/earcut/test/comparison/libtess2/tesselator.h
  32. 79 0
      polygon.mod/earcut/test/convert_tests.js
  33. 13 0
      polygon.mod/earcut/test/fixtures/bad_diagonals.cpp
  34. 16 0
      polygon.mod/earcut/test/fixtures/bad_hole.cpp
  35. 17 0
      polygon.mod/earcut/test/fixtures/boxy.cpp
  36. 13 0
      polygon.mod/earcut/test/fixtures/building.cpp
  37. 13 0
      polygon.mod/earcut/test/fixtures/collinear_diagonal.cpp
  38. 13 0
      polygon.mod/earcut/test/fixtures/degenerate.cpp
  39. 8 0
      polygon.mod/earcut/test/fixtures/dude.cpp
  40. 15 0
      polygon.mod/earcut/test/fixtures/eberly_3.cpp
  41. 8 0
      polygon.mod/earcut/test/fixtures/eberly_6.cpp
  42. 14 0
      polygon.mod/earcut/test/fixtures/empty_square.cpp
  43. 17 0
      polygon.mod/earcut/test/fixtures/filtered_bridge_jhl.cpp
  44. 106 0
      polygon.mod/earcut/test/fixtures/geometries.hpp
  45. 8 0
      polygon.mod/earcut/test/fixtures/hilbert.cpp
  46. 14 0
      polygon.mod/earcut/test/fixtures/hole_touching_outer.cpp
  47. 13 0
      polygon.mod/earcut/test/fixtures/hourglass.cpp
  48. 14 0
      polygon.mod/earcut/test/fixtures/infinite_loop_jhl.cpp
  49. 14 0
      polygon.mod/earcut/test/fixtures/issue107.cpp
  50. 16 0
      polygon.mod/earcut/test/fixtures/issue111.cpp
  51. 17 0
      polygon.mod/earcut/test/fixtures/issue119.cpp
  52. 15 0
      polygon.mod/earcut/test/fixtures/issue131.cpp
  53. 14 0
      polygon.mod/earcut/test/fixtures/issue135.cpp
  54. 14 0
      polygon.mod/earcut/test/fixtures/issue142.cpp
  55. 14 0
      polygon.mod/earcut/test/fixtures/issue149.cpp
  56. 14 0
      polygon.mod/earcut/test/fixtures/issue16.cpp
  57. 14 0
      polygon.mod/earcut/test/fixtures/issue17.cpp
  58. 14 0
      polygon.mod/earcut/test/fixtures/issue29.cpp
  59. 20 0
      polygon.mod/earcut/test/fixtures/issue34.cpp
  60. 8 0
      polygon.mod/earcut/test/fixtures/issue35.cpp
  61. 15 0
      polygon.mod/earcut/test/fixtures/issue45.cpp
  62. 18 0
      polygon.mod/earcut/test/fixtures/issue52.cpp
  63. 15 0
      polygon.mod/earcut/test/fixtures/issue83.cpp
  64. 15 0
      polygon.mod/earcut/test/fixtures/outside_ring.cpp
  65. 8 0
      polygon.mod/earcut/test/fixtures/rain.cpp
  66. 8 0
      polygon.mod/earcut/test/fixtures/self_touching.cpp
  67. 13 0
      polygon.mod/earcut/test/fixtures/shared_points.cpp
  68. 14 0
      polygon.mod/earcut/test/fixtures/simplified_us_border.cpp
  69. 17 0
      polygon.mod/earcut/test/fixtures/steiner.cpp
  70. 14 0
      polygon.mod/earcut/test/fixtures/touching2.cpp
  71. 15 0
      polygon.mod/earcut/test/fixtures/touching3.cpp
  72. 17 0
      polygon.mod/earcut/test/fixtures/touching4.cpp
  73. 20 0
      polygon.mod/earcut/test/fixtures/touching_holes.cpp
  74. 8 0
      polygon.mod/earcut/test/fixtures/water.cpp
  75. 8 0
      polygon.mod/earcut/test/fixtures/water2.cpp
  76. 18 0
      polygon.mod/earcut/test/fixtures/water3.cpp
  77. 15 0
      polygon.mod/earcut/test/fixtures/water3b.cpp
  78. 8 0
      polygon.mod/earcut/test/fixtures/water4.cpp
  79. 8 0
      polygon.mod/earcut/test/fixtures/water_huge.cpp
  80. 8 0
      polygon.mod/earcut/test/fixtures/water_huge2.cpp
  81. 70 0
      polygon.mod/earcut/test/tap.cpp
  82. 27 0
      polygon.mod/earcut/test/tap.hpp
  83. 117 0
      polygon.mod/earcut/test/test.cpp
  84. 510 0
      polygon.mod/earcut/test/viz.cpp
  85. 15 0
      polygon.mod/examples/example_01.bmx
  86. 33 0
      polygon.mod/examples/example_02.bmx
  87. 102 0
      polygon.mod/glue.cpp
  88. 61 0
      polygon.mod/polygon.bmx

+ 1 - 1
d3d7max2d.mod/d3d7max2d.bmx

@@ -391,7 +391,7 @@ Type TD3D7Max2DDriver Extends TMax2DDriver
 		device.DrawPrimitive(D3DPT_TRIANGLEFAN,D3DFVF_XYZ|D3DFVF_DIFFUSE,vrts,segs,0)
 	End Method
 
-	Method DrawPoly( xy#[],handlex#,handley#,tx#,ty# )
+	Method DrawPoly( xy#[],handlex#,handley#,tx#,ty#, indices:Int[] )
 		If Not IsValid() Return
 
 		If xy.length<6 Or (xy.length&1) Return

+ 1 - 1
d3d9max2d.mod/d3d9max2d.bmx

@@ -1047,7 +1047,7 @@ Type TD3D9Max2DDriver Extends TMax2dDriver
 		_d3dDev.DrawPrimitiveUP D3DPT_TRIANGLEFAN,segs-2,fverts,24
 	End Method
 	
-	Method DrawPoly( verts#[],handlex#,handley#,tx#,ty# ) Override
+	Method DrawPoly( verts#[],handlex#,handley#,tx#,ty#, indices:Int[] ) Override
 		If verts.length<6 Or (verts.length&1) Return
 		Local segs:Int=verts.length/2
 		Local fverts#[segs*6]

+ 1 - 1
glmax2d.mod/glmax2d.bmx

@@ -786,7 +786,7 @@ Type TGLMax2DDriver Extends TMax2DDriver
 		
 	End Method
 	
-	Method DrawPoly( xy#[],handle_x#,handle_y#,origin_x#,origin_y# ) Override
+	Method DrawPoly( xy#[],handle_x#,handle_y#,origin_x#,origin_y#, indices:Int[] ) Override
 		If xy.length<6 Or (xy.length&1) Return
 		
 		DisableTex

+ 1 - 1
max2d.mod/driver.bmx

@@ -57,7 +57,7 @@ Type TMax2DDriver Extends TGraphicsDriver
 	Method DrawLine( x0#,y0#,x1#,y1#,tx#,ty# ) Abstract
 	Method DrawRect( x0#,y0#,x1#,y1#,tx#,ty# ) Abstract
 	Method DrawOval( x0#,y0#,x1#,y1#,tx#,ty# ) Abstract
-	Method DrawPoly( xy#[],handlex#,handley#,originx#,originy# ) Abstract
+	Method DrawPoly( xy#[],handlex#,handley#,originx#,originy#, indices:Int[] ) Abstract
 		
 	Method DrawPixmap( pixmap:TPixmap,x:Int,y:Int ) Abstract
 	Method GrabPixmap:TPixmap( x:Int,y:Int,width:Int,height:Int ) Abstract

+ 2 - 2
max2d.mod/max2d.bmx

@@ -441,10 +441,10 @@ about:
 BlitzMax commands that affect the drawing of polygons include #SetColor, #SetHandle, 
 #SetScale, #SetRotation, #SetOrigin, #SetViewPort, #SetBlend and #SetAlpha.
 End Rem
-Function DrawPoly( xy#[] )
+Function DrawPoly( xy:Float[], indices:Int[] = Null )
 	_max2dDriver.DrawPoly xy,..
 	gc.handle_x,gc.handle_y,..
-	gc.origin_x,gc.origin_y
+	gc.origin_x,gc.origin_y, indices
 End Function
 
 Rem

+ 30 - 0
polygon.mod/common.bmx

@@ -0,0 +1,30 @@
+' ISC License
+' 
+' Copyright (c) 2023, Bruce A Henderson
+' 
+' Permission to use, copy, modify, and/or distribute this software for any purpose
+' with or without fee is hereby granted, provided that the above copyright notice
+' and this permission notice appear in all copies.
+' 
+' THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+' REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+' FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+' INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+' OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+' TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+' THIS SOFTWARE.
+'
+SuperStrict
+
+Import BRL.Vector
+
+Import "earcut/include/*.h"
+
+Import "glue.cpp"
+
+Extern
+
+	Function bmx_polygon_tri_svec2i:Int[](poly:SVec2I Ptr, size:Int)
+	Function bmx_polygon_tri_svec2f:Int[](poly:SVec2F Ptr, size:Int)
+
+End Extern

+ 27 - 0
polygon.mod/earcut/CHANGELOG.md

@@ -0,0 +1,27 @@
+## Earcut.hpp changelog
+
+### master
+
+ - Fixed a bunch of rare edge cases that led to bad triangulation (parity with Earcut v2.2.2)
+ - Removed use of deprecated `std::allocator::construct`
+ - Fixed a minor z-order hashing bug
+ - Improved visualization app, better docs
+
+### v0.12.4
+
+ - Fixed a crash in Crash in Earcut::findHoleBridge
+ - Added coverage checks
+ - Added macOS, MinGW builds
+
+### v0.12.3
+
+ - Fixed -Wunused-lambda-capture
+
+### v0.12.2
+
+ - Fixed potential division by zero
+ - Fixed -fsanitize=integer warning
+
+### v0.12.1
+
+ - Fixed cast precision warning

+ 151 - 0
polygon.mod/earcut/CMakeLists.txt

@@ -0,0 +1,151 @@
+cmake_minimum_required(VERSION 3.2)
+project(earcut_hpp LANGUAGES CXX C)
+
+option(EARCUT_BUILD_TESTS "Build the earcut test program" ON)
+option(EARCUT_BUILD_BENCH "Build the earcut benchmark program" ON)
+option(EARCUT_BUILD_VIZ "Build the earcut visualizer program" ON)
+option(EARCUT_WARNING_IS_ERROR "Treat warnings as errors" OFF)
+
+if (NOT CMAKE_BUILD_TYPE AND NOT GENERATOR_IS_MULTI_CONFIG)
+    message(STATUS "No build type specified. Setting to 'Release'")
+    set(CMAKE_BUILD_TYPE "Release" CACHE STRING "The type of build." FORCE)
+endif()
+
+
+include(GNUInstallDirs)
+
+add_library(earcut_hpp INTERFACE)
+add_library(earcut_hpp::earcut_hpp ALIAS earcut_hpp)
+
+target_include_directories(earcut_hpp INTERFACE
+  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
+  $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
+)
+
+set(CMAKE_CXX_STANDARD 11)
+
+if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" GREATER 3.7)
+    # Allow C++11 requirements to propagate when using recent CMake versions
+    target_compile_features(earcut_hpp INTERFACE cxx_std_11)
+endif()
+
+file(GLOB FIXTURE_SOURCE_FILES test/fixtures/*.cpp test/fixtures/*.hpp)
+source_group(fixtures FILES ${FIXTURE_SOURCE_FILES})
+add_library(fixtures OBJECT ${FIXTURE_SOURCE_FILES})
+target_compile_options(fixtures PRIVATE $<$<CXX_COMPILER_ID:MSVC>:/Od>)
+
+# In CMake 3.12, use target_link_libraries(fixtures PUBLIC earcut_hpp libtess2).
+# Since we support down to CMake 3.2, we need to manually propagate usage requirements of earcut_hpp
+target_include_directories(fixtures PRIVATE "$<TARGET_PROPERTY:earcut_hpp,INTERFACE_INCLUDE_DIRECTORIES>")
+target_compile_features(fixtures PRIVATE "$<TARGET_PROPERTY:earcut_hpp,INTERFACE_COMPILE_FEATURES>")
+
+
+file(GLOB COMPARISON_SOURCE_FILES test/comparison/*.cpp test/comparison/*.hpp)
+source_group(comparison FILES ${COMPARISON_SOURCE_FILES})
+# this is interface since there is no cpp files in the comparison directory
+add_library(comparison INTERFACE)
+
+
+file(GLOB LIBTESS2_SOURCE_FILES test/comparison/libtess2/*.c test/comparison/libtess2/*.h)
+source_group(comparison/libtess2 FILES ${LIBTESS2_SOURCE_FILES})
+add_library(libtess2 ${LIBTESS2_SOURCE_FILES})
+target_compile_options(libtess2 PRIVATE
+    $<$<CXX_COMPILER_ID:MSVC>:/wd4244 /wd4267>
+    $<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-w>
+)
+
+add_library(common INTERFACE)
+target_link_libraries(common INTERFACE libtess2 comparison)
+
+# optional: -march=native (builds with the optimizations available on the build machine (only for local use!))
+target_compile_options(common INTERFACE
+    $<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-pipe -Wall -Wextra -Wconversion -Wpedantic>
+)
+
+if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang$" OR CMAKE_COMPILER_IS_GNUCXX)
+    if ("${CMAKE_CXX_FLAGS}" MATCHES "--coverage")
+        # We disable debug code for the coverage so it won't see assertion and other things only enabled for debugging
+        target_compile_definitions(common INTERFACE NDEBUG)
+    else()
+        # Here we enable the undefined behavior sanitizer for the tests, benchmarks and the viz
+        include(CheckCXXCompilerFlag)
+        check_cxx_compiler_flag("-fsanitize=undefined" HAVE_FLAG_SANITIZE_UNDEFINED)
+        if(HAVE_FLAG_SANITIZE_UNDEFINED)
+            target_compile_options(common INTERFACE $<$<CONFIG:Debug>:-fsanitize=undefined>)
+            # TODO: Replace with target link option once we support CMake 3.13 
+            target_link_libraries(common INTERFACE $<$<CONFIG:Debug>:-fsanitize=undefined>)
+        endif()
+    endif()
+endif()
+
+if (EARCUT_WARNING_IS_ERROR)
+    target_compile_options(common INTERFACE
+        $<$<CXX_COMPILER_ID:MSVC>:/WX>
+        $<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Werror>
+    )
+endif()
+
+if (EARCUT_BUILD_TESTS)
+    enable_testing()
+    add_executable(tests test/tap.cpp test/tap.hpp test/test.cpp $<TARGET_OBJECTS:fixtures>)
+    target_link_libraries(tests PRIVATE earcut_hpp common)
+    add_test(NAME earcut_tests COMMAND tests)
+endif()
+if (EARCUT_BUILD_BENCH)
+    add_executable(bench test/bench.cpp $<TARGET_OBJECTS:fixtures>)
+    target_link_libraries(bench PRIVATE earcut_hpp common)
+endif()
+if (EARCUT_BUILD_VIZ)
+    add_executable(viz test/viz.cpp $<TARGET_OBJECTS:fixtures>)
+
+    # Setup viz target
+    # OpenGL
+    # linux: xorg-dev libgl1-mesa-glx libgl1-mesa-dev
+    # windows: in the windows sdk
+    find_package(OpenGL REQUIRED)
+
+    # GLFW3
+    find_package(glfw3 QUIET) # try to use the system default
+    if (NOT glfw3_FOUND)
+        if(EXISTS "${PROJECT_SOURCE_DIR}/.gitmodules")
+            find_package(Git REQUIRED)
+            execute_process(
+                    COMMAND             ${GIT_EXECUTABLE} submodule update --init --recursive
+                    WORKING_DIRECTORY   ${PROJECT_SOURCE_DIR}
+                    OUTPUT_QUIET
+                    ERROR_QUIET
+            )
+        endif()
+
+        set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "Build the GLFW example programs" FORCE)
+        set(GLFW_BUILD_TESTS OFF CACHE BOOL "Build the GLFW test programs" FORCE)
+        set(GLFW_BUILD_DOCS OFF CACHE BOOL "Build the GLFW documentation" FORCE)
+        set(GLFW_INSTALL OFF CACHE BOOL "Generate installation target" FORCE)
+        add_subdirectory(glfw)
+    endif()
+    
+    target_compile_definitions(viz PRIVATE GL_SILENCE_DEPRECATION)
+    
+    # TODO: Using old variables for OpenGL package since they were added in CMake 3.8
+    target_link_libraries(viz PRIVATE earcut_hpp common glfw ${OPENGL_LIBRARIES})
+    target_include_directories(viz PRIVATE ${OPENGL_INCLUDE_DIR})
+endif()
+
+install(
+  DIRECTORY include/mapbox
+  DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} FILES_MATCHING PATTERN "*.hpp"
+)
+
+install(TARGETS earcut_hpp EXPORT earcut_hpp-config)
+
+# Since there is two projects, we need to export into the parent directory
+export(
+  TARGETS earcut_hpp
+  NAMESPACE earcut_hpp::
+  FILE "${PROJECT_BINARY_DIR}/earcut_hpp-config.cmake"
+)
+
+install(EXPORT earcut_hpp-config
+  DESTINATION "${CMAKE_INSTALL_DATADIR}/cmake/earcut_hpp"
+  NAMESPACE earcut_hpp::
+)

+ 15 - 0
polygon.mod/earcut/LICENSE

@@ -0,0 +1,15 @@
+ISC License
+
+Copyright (c) 2015, Mapbox
+
+Permission to use, copy, modify, and/or distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright notice
+and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.

+ 131 - 0
polygon.mod/earcut/README.md

@@ -0,0 +1,131 @@
+## Earcut
+
+A C++ port of [earcut.js](https://github.com/mapbox/earcut), a fast, [header-only](https://github.com/mapbox/earcut.hpp/blob/master/include/mapbox/earcut.hpp) polygon triangulation library.
+
+[![Travis](https://img.shields.io/travis/com/mapbox/earcut.hpp.svg)](https://travis-ci.com/github/mapbox/earcut.hpp)
+[![AppVeyor](https://ci.appveyor.com/api/projects/status/a1ysrqd69mqn7coo/branch/master?svg=true)](https://ci.appveyor.com/project/Mapbox/earcut-hpp-8wm4o/branch/master)
+[![Coverage](https://img.shields.io/coveralls/github/mapbox/earcut.hpp.svg)](https://coveralls.io/github/mapbox/earcut.hpp)
+[![Coverity Scan](https://img.shields.io/coverity/scan/14000.svg)](https://scan.coverity.com/projects/14000)
+[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/mapbox/earcut.hpp.svg)](http://isitmaintained.com/project/mapbox/earcut.hpp "Average time to resolve an issue")
+[![Percentage of issues still open](http://isitmaintained.com/badge/open/mapbox/earcut.hpp.svg)](http://isitmaintained.com/project/mapbox/earcut.hpp "Percentage of issues still open")
+[![Mourner](https://img.shields.io/badge/simply-awesome-brightgreen.svg)](https://github.com/mourner/projects)
+
+The library implements a modified ear slicing algorithm, optimized by [z-order curve](http://en.wikipedia.org/wiki/Z-order_curve) hashing and extended to handle holes, twisted polygons, degeneracies and self-intersections in a way that doesn't _guarantee_ correctness of triangulation, but attempts to always produce acceptable results for practical data like geographical shapes.
+
+It's based on ideas from [FIST: Fast Industrial-Strength Triangulation of Polygons](http://www.cosy.sbg.ac.at/~held/projects/triang/triang.html) by Martin Held and [Triangulation by Ear Clipping](http://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf) by David Eberly.
+
+## Usage
+
+```cpp
+#include <earcut.hpp>
+```
+```cpp
+// The number type to use for tessellation
+using Coord = double;
+
+// The index type. Defaults to uint32_t, but you can also pass uint16_t if you know that your
+// data won't have more than 65536 vertices.
+using N = uint32_t;
+
+// Create array
+using Point = std::array<Coord, 2>;
+std::vector<std::vector<Point>> polygon;
+
+// Fill polygon structure with actual data. Any winding order works.
+// The first polyline defines the main polygon.
+polygon.push_back({{100, 0}, {100, 100}, {0, 100}, {0, 0}});
+// Following polylines define holes.
+polygon.push_back({{75, 25}, {75, 75}, {25, 75}, {25, 25}});
+
+// Run tessellation
+// Returns array of indices that refer to the vertices of the input polygon.
+// e.g: the index 6 would refer to {25, 75} in this example.
+// Three subsequent indices form a triangle. Output triangles are clockwise.
+std::vector<N> indices = mapbox::earcut<N>(polygon);
+```
+
+Earcut can triangulate a simple, planar polygon of any winding order including holes. It will even return a robust, acceptable solution for non-simple poygons. Earcut works on a 2D plane. If you have three or more dimensions, you can project them onto a 2D surface before triangulation, or use a more suitable library for the task (e.g [CGAL](https://doc.cgal.org/latest/Triangulation_3/index.html)).
+
+
+It is also possible to use your custom point type as input. There are default accessors defined for `std::tuple`, `std::pair`, and `std::array`. For a custom type (like Clipper's `IntPoint` type), do this:
+
+```cpp
+// struct IntPoint {
+//     int64_t X, Y;
+// };
+
+namespace mapbox {
+namespace util {
+
+template <>
+struct nth<0, IntPoint> {
+    inline static auto get(const IntPoint &t) {
+        return t.X;
+    };
+};
+template <>
+struct nth<1, IntPoint> {
+    inline static auto get(const IntPoint &t) {
+        return t.Y;
+    };
+};
+
+} // namespace util
+} // namespace mapbox
+```
+
+You can also use a custom container type for your polygon. Similar to std::vector<T>, it has to meet the requirements of [Container](https://en.cppreference.com/w/cpp/named_req/Container), in particular `size()`, `empty()` and `operator[]`.
+
+<p align="center">
+  <img src="https://camo.githubusercontent.com/01836f8ba21af844c93d8d3145f4e9976025a696/68747470733a2f2f692e696d6775722e636f6d2f67314e704c54712e706e67" alt="example triangulation"/>
+</p>
+
+## Additional build instructions
+In case you just want to use the earcut triangulation library; copy and include the header file [`<earcut.hpp>`](https://github.com/mapbox/earcut.hpp/blob/master/include/mapbox/earcut.hpp) in your project and follow the steps documented in the section [Usage](#usage).
+
+If you want to build the test, benchmark and visualization programs instead, follow these instructions:
+
+### Dependencies
+
+Before you continue, make sure to have the following tools and libraries installed:
+ * git ([Ubuntu](https://help.ubuntu.com/lts/serverguide/git.html)/[Windows/macOS](http://git-scm.com/downloads))
+ * cmake 3.2+ ([Ubuntu](https://launchpad.net/~george-edison55/+archive/ubuntu/cmake-3.x)/[Windows/macOS](https://cmake.org/download/))
+ * OpenGL SDK ([Ubuntu](http://packages.ubuntu.com/de/trusty/libgl1-mesa-dev)/[Windows](https://dev.windows.com/en-us/downloads/windows-10-sdk)/[macOS](https://developer.apple.com/opengl/))
+ * Compiler such as [GCC 4.9+, Clang 3.4+](https://launchpad.net/~ubuntu-toolchain-r/+archive/ubuntu/test), [MSVC12+](https://www.visualstudio.com/)
+
+Note: On some operating systems such as Windows, manual steps are required to add cmake and [git](http://blog.countableset.ch/2012/06/07/adding-git-to-windows-7-path/) to your PATH environment variable.
+
+### Manual compilation
+
+```bash
+git clone --recursive https://github.com/mapbox/earcut.hpp.git
+cd earcut.hpp
+mkdir build
+cd build
+cmake ..
+make
+# ./tests
+# ./bench
+# ./viz
+```
+
+### [Visual Studio](https://www.visualstudio.com/), [Eclipse](https://eclipse.org/), [XCode](https://developer.apple.com/xcode/), ...
+
+```batch
+git clone --recursive https://github.com/mapbox/earcut.hpp.git
+cd earcut.hpp
+mkdir project
+cd project
+cmake .. -G "Visual Studio 14 2015"
+::you can also generate projects for "Visual Studio 12 2013", "XCode", "Eclipse CDT4 - Unix Makefiles"
+```
+After completion, open the generated project with your IDE.
+
+
+### [CLion](https://www.jetbrains.com/clion/), [Visual Studio 2017+](https://www.visualstudio.com/)
+
+Import the project from https://github.com/mapbox/earcut.hpp.git and you should be good to go!
+
+## Status
+
+This is currently based on [earcut 2.2.4](https://github.com/mapbox/earcut#224-jul-5-2022).

+ 33 - 0
polygon.mod/earcut/appveyor.yml

@@ -0,0 +1,33 @@
+os: Visual Studio 2017
+
+configuration:
+  #- Debug
+  - Release
+
+environment:
+  matrix:
+    - GENERATOR: "MinGW Makefiles"
+      CXX_PATH: 'C:\mingw-w64\i686-5.3.0-posix-dwarf-rt_v4-rev0\mingw32\bin'
+      APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
+    - GENERATOR: "Visual Studio 12 2013 Win64"
+    - GENERATOR: "Visual Studio 14 2015 Win64"
+    - GENERATOR: "Visual Studio 15 2017 Win64"
+    - GENERATOR: "Visual Studio 15 2017"
+
+matrix:
+  fast_finish: true
+
+install:
+  - git submodule update --init
+  - if "%GENERATOR%"=="MinGW Makefiles" (set "PATH=%PATH:C:\Program Files\Git\usr\bin;=%")
+  - if not "%CXX_PATH%"=="" (set "PATH=%PATH%;%CXX_PATH%")
+
+build_script:
+  - cmake -H. -Bbuild -G"%GENERATOR%" -DEARCUT_WARNING_IS_ERROR=ON
+  - cmake --build build --config %configuration%
+
+test_script:
+  - cd build
+  - if exist %configuration% (cd "%configuration%") 
+  - call "tests.exe"
+  - call "bench.exe"

+ 816 - 0
polygon.mod/earcut/include/mapbox/earcut.hpp

@@ -0,0 +1,816 @@
+#pragma once
+
+#include <algorithm>
+#include <cassert>
+#include <cmath>
+#include <cstddef>
+#include <limits>
+#include <memory>
+#include <utility>
+#include <vector>
+
+namespace mapbox {
+
+namespace util {
+
+template <std::size_t I, typename T> struct nth {
+    inline static typename std::tuple_element<I, T>::type
+    get(const T& t) { return std::get<I>(t); };
+};
+
+}
+
+namespace detail {
+
+template <typename N = uint32_t>
+class Earcut {
+public:
+    std::vector<N> indices;
+    std::size_t vertices = 0;
+
+    template <typename Polygon>
+    void operator()(const Polygon& points);
+
+private:
+    struct Node {
+        Node(N index, double x_, double y_) : i(index), x(x_), y(y_) {}
+        Node(const Node&) = delete;
+        Node& operator=(const Node&) = delete;
+        Node(Node&&) = delete;
+        Node& operator=(Node&&) = delete;
+
+        const N i;
+        const double x;
+        const double y;
+
+        // previous and next vertice nodes in a polygon ring
+        Node* prev = nullptr;
+        Node* next = nullptr;
+
+        // z-order curve value
+        int32_t z = 0;
+
+        // previous and next nodes in z-order
+        Node* prevZ = nullptr;
+        Node* nextZ = nullptr;
+
+        // indicates whether this is a steiner point
+        bool steiner = false;
+    };
+
+    template <typename Ring> Node* linkedList(const Ring& points, const bool clockwise);
+    Node* filterPoints(Node* start, Node* end = nullptr);
+    void earcutLinked(Node* ear, int pass = 0);
+    bool isEar(Node* ear);
+    bool isEarHashed(Node* ear);
+    Node* cureLocalIntersections(Node* start);
+    void splitEarcut(Node* start);
+    template <typename Polygon> Node* eliminateHoles(const Polygon& points, Node* outerNode);
+    Node* eliminateHole(Node* hole, Node* outerNode);
+    Node* findHoleBridge(Node* hole, Node* outerNode);
+    bool sectorContainsSector(const Node* m, const Node* p);
+    void indexCurve(Node* start);
+    Node* sortLinked(Node* list);
+    int32_t zOrder(const double x_, const double y_);
+    Node* getLeftmost(Node* start);
+    bool pointInTriangle(double ax, double ay, double bx, double by, double cx, double cy, double px, double py) const;
+    bool isValidDiagonal(Node* a, Node* b);
+    double area(const Node* p, const Node* q, const Node* r) const;
+    bool equals(const Node* p1, const Node* p2);
+    bool intersects(const Node* p1, const Node* q1, const Node* p2, const Node* q2);
+    bool onSegment(const Node* p, const Node* q, const Node* r);
+    int sign(double val);
+    bool intersectsPolygon(const Node* a, const Node* b);
+    bool locallyInside(const Node* a, const Node* b);
+    bool middleInside(const Node* a, const Node* b);
+    Node* splitPolygon(Node* a, Node* b);
+    template <typename Point> Node* insertNode(std::size_t i, const Point& p, Node* last);
+    void removeNode(Node* p);
+
+    bool hashing;
+    double minX, maxX;
+    double minY, maxY;
+    double inv_size = 0;
+
+    template <typename T, typename Alloc = std::allocator<T>>
+    class ObjectPool {
+    public:
+        ObjectPool() { }
+        ObjectPool(std::size_t blockSize_) {
+            reset(blockSize_);
+        }
+        ~ObjectPool() {
+            clear();
+        }
+        template <typename... Args>
+        T* construct(Args&&... args) {
+            if (currentIndex >= blockSize) {
+                currentBlock = alloc_traits::allocate(alloc, blockSize);
+                allocations.emplace_back(currentBlock);
+                currentIndex = 0;
+            }
+            T* object = &currentBlock[currentIndex++];
+            alloc_traits::construct(alloc, object, std::forward<Args>(args)...);
+            return object;
+        }
+        void reset(std::size_t newBlockSize) {
+            for (auto allocation : allocations) {
+                alloc_traits::deallocate(alloc, allocation, blockSize);
+            }
+            allocations.clear();
+            blockSize = std::max<std::size_t>(1, newBlockSize);
+            currentBlock = nullptr;
+            currentIndex = blockSize;
+        }
+        void clear() { reset(blockSize); }
+    private:
+        T* currentBlock = nullptr;
+        std::size_t currentIndex = 1;
+        std::size_t blockSize = 1;
+        std::vector<T*> allocations;
+        Alloc alloc;
+        typedef typename std::allocator_traits<Alloc> alloc_traits;
+    };
+    ObjectPool<Node> nodes;
+};
+
+template <typename N> template <typename Polygon>
+void Earcut<N>::operator()(const Polygon& points) {
+    // reset
+    indices.clear();
+    vertices = 0;
+
+    if (points.empty()) return;
+
+    double x;
+    double y;
+    int threshold = 80;
+    std::size_t len = 0;
+
+    for (size_t i = 0; threshold >= 0 && i < points.size(); i++) {
+        threshold -= static_cast<int>(points[i].size());
+        len += points[i].size();
+    }
+
+    //estimate size of nodes and indices
+    nodes.reset(len * 3 / 2);
+    indices.reserve(len + points[0].size());
+
+    Node* outerNode = linkedList(points[0], true);
+    if (!outerNode || outerNode->prev == outerNode->next) return;
+
+    if (points.size() > 1) outerNode = eliminateHoles(points, outerNode);
+
+    // if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox
+    hashing = threshold < 0;
+    if (hashing) {
+        Node* p = outerNode->next;
+        minX = maxX = outerNode->x;
+        minY = maxY = outerNode->y;
+        do {
+            x = p->x;
+            y = p->y;
+            minX = std::min<double>(minX, x);
+            minY = std::min<double>(minY, y);
+            maxX = std::max<double>(maxX, x);
+            maxY = std::max<double>(maxY, y);
+            p = p->next;
+        } while (p != outerNode);
+
+        // minX, minY and inv_size are later used to transform coords into integers for z-order calculation
+        inv_size = std::max<double>(maxX - minX, maxY - minY);
+        inv_size = inv_size != .0 ? (32767. / inv_size) : .0;
+    }
+
+    earcutLinked(outerNode);
+
+    nodes.clear();
+}
+
+// create a circular doubly linked list from polygon points in the specified winding order
+template <typename N> template <typename Ring>
+typename Earcut<N>::Node*
+Earcut<N>::linkedList(const Ring& points, const bool clockwise) {
+    using Point = typename Ring::value_type;
+    double sum = 0;
+    const std::size_t len = points.size();
+    std::size_t i, j;
+    Node* last = nullptr;
+
+    // calculate original winding order of a polygon ring
+    for (i = 0, j = len > 0 ? len - 1 : 0; i < len; j = i++) {
+        const auto& p1 = points[i];
+        const auto& p2 = points[j];
+        const double p20 = util::nth<0, Point>::get(p2);
+        const double p10 = util::nth<0, Point>::get(p1);
+        const double p11 = util::nth<1, Point>::get(p1);
+        const double p21 = util::nth<1, Point>::get(p2);
+        sum += (p20 - p10) * (p11 + p21);
+    }
+
+    // link points into circular doubly-linked list in the specified winding order
+    if (clockwise == (sum > 0)) {
+        for (i = 0; i < len; i++) last = insertNode(vertices + i, points[i], last);
+    } else {
+        for (i = len; i-- > 0;) last = insertNode(vertices + i, points[i], last);
+    }
+
+    if (last && equals(last, last->next)) {
+        removeNode(last);
+        last = last->next;
+    }
+
+    vertices += len;
+
+    return last;
+}
+
+// eliminate colinear or duplicate points
+template <typename N>
+typename Earcut<N>::Node*
+Earcut<N>::filterPoints(Node* start, Node* end) {
+    if (!end) end = start;
+
+    Node* p = start;
+    bool again;
+    do {
+        again = false;
+
+        if (!p->steiner && (equals(p, p->next) || area(p->prev, p, p->next) == 0)) {
+            removeNode(p);
+            p = end = p->prev;
+
+            if (p == p->next) break;
+            again = true;
+
+        } else {
+            p = p->next;
+        }
+    } while (again || p != end);
+
+    return end;
+}
+
+// main ear slicing loop which triangulates a polygon (given as a linked list)
+template <typename N>
+void Earcut<N>::earcutLinked(Node* ear, int pass) {
+    if (!ear) return;
+
+    // interlink polygon nodes in z-order
+    if (!pass && hashing) indexCurve(ear);
+
+    Node* stop = ear;
+    Node* prev;
+    Node* next;
+
+    int iterations = 0;
+
+    // iterate through ears, slicing them one by one
+    while (ear->prev != ear->next) {
+        iterations++;
+        prev = ear->prev;
+        next = ear->next;
+
+        if (hashing ? isEarHashed(ear) : isEar(ear)) {
+            // cut off the triangle
+            indices.emplace_back(prev->i);
+            indices.emplace_back(ear->i);
+            indices.emplace_back(next->i);
+
+            removeNode(ear);
+
+            // skipping the next vertice leads to less sliver triangles
+            ear = next->next;
+            stop = next->next;
+
+            continue;
+        }
+
+        ear = next;
+
+        // if we looped through the whole remaining polygon and can't find any more ears
+        if (ear == stop) {
+            // try filtering points and slicing again
+            if (!pass) earcutLinked(filterPoints(ear), 1);
+
+            // if this didn't work, try curing all small self-intersections locally
+            else if (pass == 1) {
+                ear = cureLocalIntersections(filterPoints(ear));
+                earcutLinked(ear, 2);
+
+            // as a last resort, try splitting the remaining polygon into two
+            } else if (pass == 2) splitEarcut(ear);
+
+            break;
+        }
+    }
+}
+
+// check whether a polygon node forms a valid ear with adjacent nodes
+template <typename N>
+bool Earcut<N>::isEar(Node* ear) {
+    const Node* a = ear->prev;
+    const Node* b = ear;
+    const Node* c = ear->next;
+
+    if (area(a, b, c) >= 0) return false; // reflex, can't be an ear
+
+    // now make sure we don't have other points inside the potential ear
+    Node* p = ear->next->next;
+
+    while (p != ear->prev) {
+        if (pointInTriangle(a->x, a->y, b->x, b->y, c->x, c->y, p->x, p->y) &&
+            area(p->prev, p, p->next) >= 0) return false;
+        p = p->next;
+    }
+
+    return true;
+}
+
+template <typename N>
+bool Earcut<N>::isEarHashed(Node* ear) {
+    const Node* a = ear->prev;
+    const Node* b = ear;
+    const Node* c = ear->next;
+
+    if (area(a, b, c) >= 0) return false; // reflex, can't be an ear
+
+    // triangle bbox; min & max are calculated like this for speed
+    const double minTX = std::min<double>(a->x, std::min<double>(b->x, c->x));
+    const double minTY = std::min<double>(a->y, std::min<double>(b->y, c->y));
+    const double maxTX = std::max<double>(a->x, std::max<double>(b->x, c->x));
+    const double maxTY = std::max<double>(a->y, std::max<double>(b->y, c->y));
+
+    // z-order range for the current triangle bbox;
+    const int32_t minZ = zOrder(minTX, minTY);
+    const int32_t maxZ = zOrder(maxTX, maxTY);
+
+    // first look for points inside the triangle in increasing z-order
+    Node* p = ear->nextZ;
+
+    while (p && p->z <= maxZ) {
+        if (p != ear->prev && p != ear->next &&
+            pointInTriangle(a->x, a->y, b->x, b->y, c->x, c->y, p->x, p->y) &&
+            area(p->prev, p, p->next) >= 0) return false;
+        p = p->nextZ;
+    }
+
+    // then look for points in decreasing z-order
+    p = ear->prevZ;
+
+    while (p && p->z >= minZ) {
+        if (p != ear->prev && p != ear->next &&
+            pointInTriangle(a->x, a->y, b->x, b->y, c->x, c->y, p->x, p->y) &&
+            area(p->prev, p, p->next) >= 0) return false;
+        p = p->prevZ;
+    }
+
+    return true;
+}
+
+// go through all polygon nodes and cure small local self-intersections
+template <typename N>
+typename Earcut<N>::Node*
+Earcut<N>::cureLocalIntersections(Node* start) {
+    Node* p = start;
+    do {
+        Node* a = p->prev;
+        Node* b = p->next->next;
+
+        // a self-intersection where edge (v[i-1],v[i]) intersects (v[i+1],v[i+2])
+        if (!equals(a, b) && intersects(a, p, p->next, b) && locallyInside(a, b) && locallyInside(b, a)) {
+            indices.emplace_back(a->i);
+            indices.emplace_back(p->i);
+            indices.emplace_back(b->i);
+
+            // remove two nodes involved
+            removeNode(p);
+            removeNode(p->next);
+
+            p = start = b;
+        }
+        p = p->next;
+    } while (p != start);
+
+    return filterPoints(p);
+}
+
+// try splitting polygon into two and triangulate them independently
+template <typename N>
+void Earcut<N>::splitEarcut(Node* start) {
+    // look for a valid diagonal that divides the polygon into two
+    Node* a = start;
+    do {
+        Node* b = a->next->next;
+        while (b != a->prev) {
+            if (a->i != b->i && isValidDiagonal(a, b)) {
+                // split the polygon in two by the diagonal
+                Node* c = splitPolygon(a, b);
+
+                // filter colinear points around the cuts
+                a = filterPoints(a, a->next);
+                c = filterPoints(c, c->next);
+
+                // run earcut on each half
+                earcutLinked(a);
+                earcutLinked(c);
+                return;
+            }
+            b = b->next;
+        }
+        a = a->next;
+    } while (a != start);
+}
+
+// link every hole into the outer loop, producing a single-ring polygon without holes
+template <typename N> template <typename Polygon>
+typename Earcut<N>::Node*
+Earcut<N>::eliminateHoles(const Polygon& points, Node* outerNode) {
+    const size_t len = points.size();
+
+    std::vector<Node*> queue;
+    for (size_t i = 1; i < len; i++) {
+        Node* list = linkedList(points[i], false);
+        if (list) {
+            if (list == list->next) list->steiner = true;
+            queue.push_back(getLeftmost(list));
+        }
+    }
+    std::sort(queue.begin(), queue.end(), [](const Node* a, const Node* b) {
+        return a->x < b->x;
+    });
+
+    // process holes from left to right
+    for (size_t i = 0; i < queue.size(); i++) {
+        outerNode = eliminateHole(queue[i], outerNode);
+    }
+
+    return outerNode;
+}
+
+// find a bridge between vertices that connects hole with an outer ring and and link it
+template <typename N>
+typename Earcut<N>::Node*
+Earcut<N>::eliminateHole(Node* hole, Node* outerNode) {
+    Node* bridge = findHoleBridge(hole, outerNode);
+    if (!bridge) {
+        return outerNode;
+    }
+
+    Node* bridgeReverse = splitPolygon(bridge, hole);
+
+    // filter collinear points around the cuts
+    filterPoints(bridgeReverse, bridgeReverse->next);
+
+    // Check if input node was removed by the filtering
+    return filterPoints(bridge, bridge->next);
+}
+
+// David Eberly's algorithm for finding a bridge between hole and outer polygon
+template <typename N>
+typename Earcut<N>::Node*
+Earcut<N>::findHoleBridge(Node* hole, Node* outerNode) {
+    Node* p = outerNode;
+    double hx = hole->x;
+    double hy = hole->y;
+    double qx = -std::numeric_limits<double>::infinity();
+    Node* m = nullptr;
+
+    // find a segment intersected by a ray from the hole's leftmost Vertex to the left;
+    // segment's endpoint with lesser x will be potential connection Vertex
+    do {
+        if (hy <= p->y && hy >= p->next->y && p->next->y != p->y) {
+          double x = p->x + (hy - p->y) * (p->next->x - p->x) / (p->next->y - p->y);
+          if (x <= hx && x > qx) {
+            qx = x;
+            m = p->x < p->next->x ? p : p->next;
+            if (x == hx) return m; // hole touches outer segment; pick leftmost endpoint
+          }
+        }
+        p = p->next;
+    } while (p != outerNode);
+
+    if (!m) return 0;
+
+    // look for points inside the triangle of hole Vertex, segment intersection and endpoint;
+    // if there are no points found, we have a valid connection;
+    // otherwise choose the Vertex of the minimum angle with the ray as connection Vertex
+
+    const Node* stop = m;
+    double tanMin = std::numeric_limits<double>::infinity();
+    double tanCur = 0;
+
+    p = m;
+    double mx = m->x;
+    double my = m->y;
+
+    do {
+        if (hx >= p->x && p->x >= mx && hx != p->x &&
+            pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p->x, p->y)) {
+
+            tanCur = std::abs(hy - p->y) / (hx - p->x); // tangential
+
+            if (locallyInside(p, hole) &&
+                (tanCur < tanMin || (tanCur == tanMin && (p->x > m->x || sectorContainsSector(m, p))))) {
+                m = p;
+                tanMin = tanCur;
+            }
+        }
+
+        p = p->next;
+    } while (p != stop);
+
+    return m;
+}
+
+// whether sector in vertex m contains sector in vertex p in the same coordinates
+template <typename N>
+bool Earcut<N>::sectorContainsSector(const Node* m, const Node* p) {
+    return area(m->prev, m, p->prev) < 0 && area(p->next, m, m->next) < 0;
+}
+
+// interlink polygon nodes in z-order
+template <typename N>
+void Earcut<N>::indexCurve(Node* start) {
+    assert(start);
+    Node* p = start;
+
+    do {
+        p->z = p->z ? p->z : zOrder(p->x, p->y);
+        p->prevZ = p->prev;
+        p->nextZ = p->next;
+        p = p->next;
+    } while (p != start);
+
+    p->prevZ->nextZ = nullptr;
+    p->prevZ = nullptr;
+
+    sortLinked(p);
+}
+
+// Simon Tatham's linked list merge sort algorithm
+// http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html
+template <typename N>
+typename Earcut<N>::Node*
+Earcut<N>::sortLinked(Node* list) {
+    assert(list);
+    Node* p;
+    Node* q;
+    Node* e;
+    Node* tail;
+    int i, numMerges, pSize, qSize;
+    int inSize = 1;
+
+    for (;;) {
+        p = list;
+        list = nullptr;
+        tail = nullptr;
+        numMerges = 0;
+
+        while (p) {
+            numMerges++;
+            q = p;
+            pSize = 0;
+            for (i = 0; i < inSize; i++) {
+                pSize++;
+                q = q->nextZ;
+                if (!q) break;
+            }
+
+            qSize = inSize;
+
+            while (pSize > 0 || (qSize > 0 && q)) {
+
+                if (pSize == 0) {
+                    e = q;
+                    q = q->nextZ;
+                    qSize--;
+                } else if (qSize == 0 || !q) {
+                    e = p;
+                    p = p->nextZ;
+                    pSize--;
+                } else if (p->z <= q->z) {
+                    e = p;
+                    p = p->nextZ;
+                    pSize--;
+                } else {
+                    e = q;
+                    q = q->nextZ;
+                    qSize--;
+                }
+
+                if (tail) tail->nextZ = e;
+                else list = e;
+
+                e->prevZ = tail;
+                tail = e;
+            }
+
+            p = q;
+        }
+
+        tail->nextZ = nullptr;
+
+        if (numMerges <= 1) return list;
+
+        inSize *= 2;
+    }
+}
+
+// z-order of a Vertex given coords and size of the data bounding box
+template <typename N>
+int32_t Earcut<N>::zOrder(const double x_, const double y_) {
+    // coords are transformed into non-negative 15-bit integer range
+    int32_t x = static_cast<int32_t>((x_ - minX) * inv_size);
+    int32_t y = static_cast<int32_t>((y_ - minY) * inv_size);
+
+    x = (x | (x << 8)) & 0x00FF00FF;
+    x = (x | (x << 4)) & 0x0F0F0F0F;
+    x = (x | (x << 2)) & 0x33333333;
+    x = (x | (x << 1)) & 0x55555555;
+
+    y = (y | (y << 8)) & 0x00FF00FF;
+    y = (y | (y << 4)) & 0x0F0F0F0F;
+    y = (y | (y << 2)) & 0x33333333;
+    y = (y | (y << 1)) & 0x55555555;
+
+    return x | (y << 1);
+}
+
+// find the leftmost node of a polygon ring
+template <typename N>
+typename Earcut<N>::Node*
+Earcut<N>::getLeftmost(Node* start) {
+    Node* p = start;
+    Node* leftmost = start;
+    do {
+        if (p->x < leftmost->x || (p->x == leftmost->x && p->y < leftmost->y))
+            leftmost = p;
+        p = p->next;
+    } while (p != start);
+
+    return leftmost;
+}
+
+// check if a point lies within a convex triangle
+template <typename N>
+bool Earcut<N>::pointInTriangle(double ax, double ay, double bx, double by, double cx, double cy, double px, double py) const {
+    return (cx - px) * (ay - py) >= (ax - px) * (cy - py) &&
+           (ax - px) * (by - py) >= (bx - px) * (ay - py) &&
+           (bx - px) * (cy - py) >= (cx - px) * (by - py);
+}
+
+// check if a diagonal between two polygon nodes is valid (lies in polygon interior)
+template <typename N>
+bool Earcut<N>::isValidDiagonal(Node* a, Node* b) {
+    return a->next->i != b->i && a->prev->i != b->i && !intersectsPolygon(a, b) && // dones't intersect other edges
+           ((locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b) && // locally visible
+            (area(a->prev, a, b->prev) != 0.0 || area(a, b->prev, b) != 0.0)) || // does not create opposite-facing sectors
+            (equals(a, b) && area(a->prev, a, a->next) > 0 && area(b->prev, b, b->next) > 0)); // special zero-length case
+}
+
+// signed area of a triangle
+template <typename N>
+double Earcut<N>::area(const Node* p, const Node* q, const Node* r) const {
+    return (q->y - p->y) * (r->x - q->x) - (q->x - p->x) * (r->y - q->y);
+}
+
+// check if two points are equal
+template <typename N>
+bool Earcut<N>::equals(const Node* p1, const Node* p2) {
+    return p1->x == p2->x && p1->y == p2->y;
+}
+
+// check if two segments intersect
+template <typename N>
+bool Earcut<N>::intersects(const Node* p1, const Node* q1, const Node* p2, const Node* q2) {
+    int o1 = sign(area(p1, q1, p2));
+    int o2 = sign(area(p1, q1, q2));
+    int o3 = sign(area(p2, q2, p1));
+    int o4 = sign(area(p2, q2, q1));
+
+    if (o1 != o2 && o3 != o4) return true; // general case
+
+    if (o1 == 0 && onSegment(p1, p2, q1)) return true; // p1, q1 and p2 are collinear and p2 lies on p1q1
+    if (o2 == 0 && onSegment(p1, q2, q1)) return true; // p1, q1 and q2 are collinear and q2 lies on p1q1
+    if (o3 == 0 && onSegment(p2, p1, q2)) return true; // p2, q2 and p1 are collinear and p1 lies on p2q2
+    if (o4 == 0 && onSegment(p2, q1, q2)) return true; // p2, q2 and q1 are collinear and q1 lies on p2q2
+
+    return false;
+}
+
+// for collinear points p, q, r, check if point q lies on segment pr
+template <typename N>
+bool Earcut<N>::onSegment(const Node* p, const Node* q, const Node* r) {
+    return q->x <= std::max<double>(p->x, r->x) &&
+        q->x >= std::min<double>(p->x, r->x) &&
+        q->y <= std::max<double>(p->y, r->y) &&
+        q->y >= std::min<double>(p->y, r->y);
+}
+
+template <typename N>
+int Earcut<N>::sign(double val) {
+    return (0.0 < val) - (val < 0.0);
+}
+
+// check if a polygon diagonal intersects any polygon segments
+template <typename N>
+bool Earcut<N>::intersectsPolygon(const Node* a, const Node* b) {
+    const Node* p = a;
+    do {
+        if (p->i != a->i && p->next->i != a->i && p->i != b->i && p->next->i != b->i &&
+                intersects(p, p->next, a, b)) return true;
+        p = p->next;
+    } while (p != a);
+
+    return false;
+}
+
+// check if a polygon diagonal is locally inside the polygon
+template <typename N>
+bool Earcut<N>::locallyInside(const Node* a, const Node* b) {
+    return area(a->prev, a, a->next) < 0 ?
+        area(a, b, a->next) >= 0 && area(a, a->prev, b) >= 0 :
+        area(a, b, a->prev) < 0 || area(a, a->next, b) < 0;
+}
+
+// check if the middle Vertex of a polygon diagonal is inside the polygon
+template <typename N>
+bool Earcut<N>::middleInside(const Node* a, const Node* b) {
+    const Node* p = a;
+    bool inside = false;
+    double px = (a->x + b->x) / 2;
+    double py = (a->y + b->y) / 2;
+    do {
+        if (((p->y > py) != (p->next->y > py)) && p->next->y != p->y &&
+                (px < (p->next->x - p->x) * (py - p->y) / (p->next->y - p->y) + p->x))
+            inside = !inside;
+        p = p->next;
+    } while (p != a);
+
+    return inside;
+}
+
+// link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits
+// polygon into two; if one belongs to the outer ring and another to a hole, it merges it into a
+// single ring
+template <typename N>
+typename Earcut<N>::Node*
+Earcut<N>::splitPolygon(Node* a, Node* b) {
+    Node* a2 = nodes.construct(a->i, a->x, a->y);
+    Node* b2 = nodes.construct(b->i, b->x, b->y);
+    Node* an = a->next;
+    Node* bp = b->prev;
+
+    a->next = b;
+    b->prev = a;
+
+    a2->next = an;
+    an->prev = a2;
+
+    b2->next = a2;
+    a2->prev = b2;
+
+    bp->next = b2;
+    b2->prev = bp;
+
+    return b2;
+}
+
+// create a node and util::optionally link it with previous one (in a circular doubly linked list)
+template <typename N> template <typename Point>
+typename Earcut<N>::Node*
+Earcut<N>::insertNode(std::size_t i, const Point& pt, Node* last) {
+    Node* p = nodes.construct(static_cast<N>(i), util::nth<0, Point>::get(pt), util::nth<1, Point>::get(pt));
+
+    if (!last) {
+        p->prev = p;
+        p->next = p;
+
+    } else {
+        assert(last);
+        p->next = last->next;
+        p->prev = last;
+        last->next->prev = p;
+        last->next = p;
+    }
+    return p;
+}
+
+template <typename N>
+void Earcut<N>::removeNode(Node* p) {
+    p->next->prev = p->prev;
+    p->prev->next = p->next;
+
+    if (p->prevZ) p->prevZ->nextZ = p->nextZ;
+    if (p->nextZ) p->nextZ->prevZ = p->prevZ;
+}
+}
+
+template <typename N = uint32_t, typename Polygon>
+std::vector<N> earcut(const Polygon& poly) {
+    mapbox::detail::Earcut<N> earcut;
+    earcut(poly);
+    return std::move(earcut.indices);
+}
+}

+ 82 - 0
polygon.mod/earcut/test/bench.cpp

@@ -0,0 +1,82 @@
+#include "fixtures/geometries.hpp"
+
+#include <iostream>
+#include <iomanip>
+#include <vector>
+#include <chrono>
+#include <set>
+
+template<typename Proc>
+double bench(Proc&& procedure) {
+    int64_t runs = -10;
+    int64_t total = 0;
+
+    while (total < 2000000000ll || runs < 100) {
+        const auto started = std::chrono::high_resolution_clock::now();
+        procedure();
+        const auto finished = std::chrono::high_resolution_clock::now();
+
+        // Don't count the first couple of iterations.
+        if (++runs > 0) {
+            total += std::chrono::duration_cast<std::chrono::nanoseconds>(finished - started).count();
+        }
+    }
+
+    return double(runs) / (double(total) / 1e9);
+}
+
+void report(mapbox::fixtures::FixtureTester* fixture, const int cols[]) {
+    std::ios::fmtflags flags(std::cerr.flags());
+    const char filling = std::cerr.fill();
+    std::cerr << std::setfill(' ');
+    std::cerr << "| " << std::left << std::setw(cols[0]) << fixture->name << " | ";
+    auto earcut = bench([&]{ fixture->earcut(); });
+    std::cerr << std::right << std::setw(cols[1] - 6) << std::fixed << std::setprecision(0) << earcut << " ops/s | ";
+    auto libtess2 = bench([&]{ fixture->libtess(); });
+    std::cerr << std::setw(cols[2] - 6) << std::setprecision(0) << libtess2 << " ops/s |" << std::endl;
+    std::cerr << std::setfill(filling);
+    std::cerr.flags(flags);
+}
+
+void separator(const int cols[]) {
+    std::ios::fmtflags flags(std::cerr.flags());
+    const char filling = std::cerr.fill();
+    std::cerr << std::setfill('-');
+    for (int i = 0; cols[i]; i++) {
+        std::cerr << "+" << std::setw(cols[i]+2) << std::cerr.fill();
+    }
+    std::cerr << std::setfill(filling);
+    std::cerr << "+" << std::endl;
+    std::cerr.flags(flags);
+}
+
+int main() {
+    std::cerr.imbue(std::locale(""));
+    const int cols[] = { 14, 18, 18, 0 };
+
+    separator(cols);
+
+    std::ios::fmtflags flags(std::cerr.flags());
+    std::cerr << "|" << std::left
+        << std::setw(cols[0]+1) << " Polygon" << " |"
+        << std::setw(cols[1]+1) << " earcut" << " |"
+        << std::setw(cols[2]+1) << " libtess2" << " |"
+        << std::endl;
+    std::cerr.flags(flags);
+
+    separator(cols);
+
+    auto& fixtures = mapbox::fixtures::FixtureTester::collection();
+    std::set<std::string> bench_whitelist = {
+        "bad_hole", "building", "degenerate", "dude", "empty_square", "water_huge",
+        "water_huge2", "water", "water2", "water3", "water3b", "water4"
+    };
+    for (auto fixture : fixtures) {
+        if (bench_whitelist.find(fixture->name) != bench_whitelist.end()) {
+            report(fixture, cols);
+        }
+    }
+
+    separator(cols);
+    return 0;
+}

+ 43 - 0
polygon.mod/earcut/test/comparison/earcut.hpp

@@ -0,0 +1,43 @@
+#pragma once
+#include <mapbox/earcut.hpp>
+
+#include <array>
+#include <memory>
+#include <vector>
+
+template <typename Coord, typename Polygon>
+class EarcutTesselator {
+public:
+    using Vertex = std::array<Coord, 2>;
+    using Vertices = std::vector<Vertex>;
+
+    EarcutTesselator(const Polygon &polygon_)
+        : polygon(polygon_)
+    {
+        for (const auto& ring : polygon_) {
+            for (const auto& vertex : ring) {
+                vertices_.emplace_back(Vertex {{ Coord(std::get<0>(vertex)),
+                                                 Coord(std::get<1>(vertex)) }});
+            }
+        }
+    }
+
+    EarcutTesselator & operator=(const EarcutTesselator&) = delete;
+
+    void run() {
+        indices_ = mapbox::earcut(polygon);
+    }
+
+    std::vector<uint32_t> const& indices() const {
+        return indices_;
+    }
+
+    Vertices const& vertices() const {
+        return vertices_;
+    }
+
+private:
+    const Polygon &polygon;
+    Vertices vertices_;
+    std::vector<uint32_t> indices_;
+};

+ 105 - 0
polygon.mod/earcut/test/comparison/libtess2.hpp

@@ -0,0 +1,105 @@
+#pragma once
+#ifdef __GNUC__
+#pragma GCC diagnostic push 
+#pragma GCC diagnostic ignored "-Wpedantic"
+#endif
+#include "libtess2/tesselator.h"
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
+#include <memory>
+#include <vector>
+#include <array>
+#include <stdexcept>
+
+template <typename Coord, typename Polygon>
+class Libtess2Tesselator {
+    using Vertex = std::array<Coord, 2>;
+    using Triangles = std::vector<Vertex>;
+    using Vertices = std::vector<Vertex>;
+    using Indices = std::vector<uint32_t>;
+
+public:
+    Libtess2Tesselator(const Polygon &polygon)
+        : tess(std::unique_ptr<TESStesselator, tessDeleter>(tessNewTess(nullptr)))
+    {
+        // Convert the polygon to Libtess2 format.
+        for (const auto &ring : polygon) {
+            std::vector<TESSreal> tessRing;
+            for (const auto &pt : ring) {
+                tessRing.push_back(static_cast<TESSreal>(pt.first));
+                tessRing.push_back(static_cast<TESSreal>(pt.second));
+            }
+            tessPolygon.push_back(tessRing);
+        }
+    }
+
+    void run() {
+        dirty = true;
+
+        // Add polygon data
+        for (const auto &tessRing : tessPolygon) {
+            tessAddContour(tess.get(), vertexSize, tessRing.data(), stride, (int)tessRing.size() / vertexSize);
+        }
+
+        int status = tessTesselate(tess.get(), TESS_WINDING_POSITIVE, TESS_POLYGONS, verticesPerTriangle, vertexSize, 0);
+        if (!status) {
+#if defined(__cpp_exceptions) || defined(__EXCEPTIONS)
+            throw std::runtime_error("tesselation failed");
+#else
+            assert(false && "tesselation failed");
+#endif
+        }
+    }
+
+    auto indices() -> const Indices & {
+        if (dirty) {
+            indexData.clear();
+            const auto elements = tessGetElements(tess.get());
+            const auto elementCount = tessGetElementCount(tess.get());
+
+             for (int i = 0; i < elementCount; i++) {
+                const TESSindex *group = &elements[i * verticesPerTriangle];
+                if (group[0] != TESS_UNDEF && group[1] != TESS_UNDEF && group[2] != TESS_UNDEF) {
+                    indexData.push_back(static_cast<uint32_t>(group[0]));
+                    indexData.push_back(static_cast<uint32_t>(group[1]));
+                    indexData.push_back(static_cast<uint32_t>(group[2]));
+                }
+            }
+        }
+
+        return indexData;
+    }
+
+    auto vertices() -> const Vertices & {
+        if (dirty) {
+            vertexData.clear();
+
+            const auto vertices = tessGetVertices(tess.get());
+            const auto vertexCount = tessGetVertexCount(tess.get());
+            for (int i = 0; i < vertexCount; i++) {
+                vertexData.emplace_back(Vertex{{ Coord(vertices[i * vertexSize]),
+                                                 Coord(vertices[i * vertexSize + 1]) }});
+            }
+        }
+
+        return vertexData;
+    }
+
+private:
+    static const int vertexSize = 2;
+    static const int stride = sizeof(TESSreal) * vertexSize;
+    static const int verticesPerTriangle = 3;
+
+    struct tessDeleter {
+        void operator()(TESStesselator *t) const { tessDeleteTess(t); }
+    };
+
+    std::vector<std::vector<TESSreal>> tessPolygon;
+    const std::unique_ptr<TESStesselator, tessDeleter> tess;
+
+    bool dirty = true;
+    Vertices vertexData;
+    Indices indexData;
+};

+ 25 - 0
polygon.mod/earcut/test/comparison/libtess2/LICENSE.txt

@@ -0,0 +1,25 @@
+** SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) 
+** Copyright (C) [dates of first publication] Silicon Graphics, Inc.
+** All Rights Reserved.
+**
+** 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 including the dates of first publication and either this
+** permission notice or a reference to http://oss.sgi.com/projects/FreeB/ 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 SILICON GRAPHICS, INC.
+** 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.
+** 
+** Except as contained in this notice, the name of Silicon Graphics, Inc. shall not
+** be used in advertising or otherwise to promote the sale, use or other dealings in
+** this Software without prior written authorization from Silicon Graphics, Inc.

+ 191 - 0
polygon.mod/earcut/test/comparison/libtess2/bucketalloc.c

@@ -0,0 +1,191 @@
+/*
+** SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
+** Copyright (C) [dates of first publication] Silicon Graphics, Inc.
+** All Rights Reserved.
+**
+** 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 including the dates of first publication and either this
+** permission notice or a reference to http://oss.sgi.com/projects/FreeB/ 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 SILICON GRAPHICS, INC.
+** 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.
+**
+** Except as contained in this notice, the name of Silicon Graphics, Inc. shall not
+** be used in advertising or otherwise to promote the sale, use or other dealings in
+** this Software without prior written authorization from Silicon Graphics, Inc.
+*/
+/*
+** Author: Mikko Mononen, July 2009.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "tesselator.h"
+
+//#define CHECK_BOUNDS
+
+typedef struct BucketAlloc BucketAlloc;
+typedef struct Bucket Bucket;
+
+struct Bucket
+{
+	Bucket *next;
+};
+
+struct BucketAlloc
+{
+	void *freelist;
+	Bucket *buckets;
+	unsigned int itemSize;
+	unsigned int bucketSize;
+	const char *name;
+	TESSalloc* alloc;
+};
+
+static int CreateBucket( struct BucketAlloc* ba )
+{
+	size_t size;
+	Bucket* bucket;
+	void* freelist;
+	unsigned char* head;
+	unsigned char* it;
+
+	// Allocate memory for the bucket
+	size = sizeof(Bucket) + ba->itemSize * ba->bucketSize;
+	bucket = (Bucket*)ba->alloc->memalloc( ba->alloc->userData, size );
+	if ( !bucket )
+		return 0;
+	bucket->next = 0;
+
+	// Add the bucket into the list of buckets.
+	bucket->next = ba->buckets;
+	ba->buckets = bucket;
+
+	// Add new items to the free list.
+	freelist = ba->freelist;
+	head = (unsigned char*)bucket + sizeof(Bucket);
+	it = head + ba->itemSize * ba->bucketSize;
+	do
+	{
+		it -= ba->itemSize;
+		// Store pointer to next free item.
+		*((void**)it) = freelist;
+		// Pointer to next location containing a free item.
+		freelist = (void*)it;
+	}
+	while ( it != head );
+	// Update pointer to next location containing a free item.
+	ba->freelist = (void*)it;
+
+	return 1;
+}
+
+static void *NextFreeItem( struct BucketAlloc *ba )
+{
+	return *(void**)ba->freelist;
+}
+
+struct BucketAlloc* createBucketAlloc( TESSalloc* alloc, const char* name,
+									  unsigned int itemSize, unsigned int bucketSize )
+{
+	BucketAlloc* ba = (BucketAlloc*)alloc->memalloc( alloc->userData, sizeof(BucketAlloc) );
+
+	ba->alloc = alloc;
+	ba->name = name;
+	ba->itemSize = itemSize;
+	if ( ba->itemSize < sizeof(void*) )
+		ba->itemSize = sizeof(void*);
+	ba->bucketSize = bucketSize;
+	ba->freelist = 0;
+	ba->buckets = 0;
+
+	if ( !CreateBucket( ba ) )
+	{
+		alloc->memfree( alloc->userData, ba );
+		return 0;
+	}
+
+	return ba;
+}
+
+void* bucketAlloc( struct BucketAlloc *ba )
+{
+	void *it;
+
+	// If running out of memory, allocate new bucket and update the freelist.
+	if ( !ba->freelist || !NextFreeItem( ba ) )
+	{
+		if ( !CreateBucket( ba ) )
+			return 0;
+	}
+
+	// Pop item from in front of the free list.
+	it = ba->freelist;
+	ba->freelist = NextFreeItem( ba );
+
+	return it;
+}
+
+void bucketFree( struct BucketAlloc *ba, void *ptr )
+{
+#ifdef CHECK_BOUNDS
+	int inBounds = 0;
+	Bucket *bucket;
+
+	// Check that the pointer is allocated with this allocator.
+	bucket = ba->buckets;
+	while ( bucket )
+	{
+		void *bucketMin = (void*)((unsigned char*)bucket + sizeof(Bucket));
+		void *bucketMax = (void*)((unsigned char*)bucket + sizeof(Bucket) + ba->itemSize * ba->bucketSize);
+		if ( ptr >= bucketMin && ptr < bucketMax )
+		{
+			inBounds = 1;
+			break;
+		}
+		bucket = bucket->next;
+	}
+
+	if ( inBounds )
+	{
+		// Add the node in front of the free list.
+		*(void**)ptr = ba->freelist;
+		ba->freelist = ptr;
+	}
+	else
+	{
+		printf("ERROR! pointer 0x%p does not belong to allocator '%s'\n", ba->name);
+	}
+#else
+	// Add the node in front of the free list.
+	*(void**)ptr = ba->freelist;
+	ba->freelist = ptr;
+#endif
+}
+
+void deleteBucketAlloc( struct BucketAlloc *ba )
+{
+	TESSalloc* alloc = ba->alloc;
+	Bucket *bucket = ba->buckets;
+	Bucket *next;
+	while ( bucket )
+	{
+		next = bucket->next;
+		alloc->memfree( alloc->userData, bucket );
+		bucket = next;
+	}
+	ba->freelist = 0;
+	ba->buckets = 0;
+	alloc->memfree( alloc->userData, ba );
+}

+ 51 - 0
polygon.mod/earcut/test/comparison/libtess2/bucketalloc.h

@@ -0,0 +1,51 @@
+/*
+** SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
+** Copyright (C) [dates of first publication] Silicon Graphics, Inc.
+** All Rights Reserved.
+**
+** 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 including the dates of first publication and either this
+** permission notice or a reference to http://oss.sgi.com/projects/FreeB/ 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 SILICON GRAPHICS, INC.
+** 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.
+**
+** Except as contained in this notice, the name of Silicon Graphics, Inc. shall not
+** be used in advertising or otherwise to promote the sale, use or other dealings in
+** this Software without prior written authorization from Silicon Graphics, Inc.
+*/
+/*
+** Author: Mikko Mononen, July 2009.
+*/
+
+#ifndef MEMALLOC_H
+#define MEMALLOC_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "tesselator.h"
+
+struct BucketAlloc *createBucketAlloc( TESSalloc* alloc, const char *name,
+									  unsigned int itemSize, unsigned int bucketSize );
+void *bucketAlloc( struct BucketAlloc *ba);
+void bucketFree( struct BucketAlloc *ba, void *ptr );
+void deleteBucketAlloc( struct BucketAlloc *ba );
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif

+ 109 - 0
polygon.mod/earcut/test/comparison/libtess2/dict.c

@@ -0,0 +1,109 @@
+/*
+** SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
+** Copyright (C) [dates of first publication] Silicon Graphics, Inc.
+** All Rights Reserved.
+**
+** 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 including the dates of first publication and either this
+** permission notice or a reference to http://oss.sgi.com/projects/FreeB/ 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 SILICON GRAPHICS, INC.
+** 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.
+**
+** Except as contained in this notice, the name of Silicon Graphics, Inc. shall not
+** be used in advertising or otherwise to promote the sale, use or other dealings in
+** this Software without prior written authorization from Silicon Graphics, Inc.
+*/
+/*
+** Author: Eric Veach, July 1994.
+*/
+
+#include <stddef.h>
+#include "tesselator.h"
+#include "bucketalloc.h"
+#include "dict.h"
+
+/* really tessDictListNewDict */
+Dict *dictNewDict( TESSalloc* alloc, void *frame, int (*leq)(void *frame, DictKey key1, DictKey key2) )
+{
+	Dict *dict = (Dict *)alloc->memalloc( alloc->userData, sizeof( Dict ));
+	DictNode *head;
+
+	if (dict == NULL) return NULL;
+
+	head = &dict->head;
+
+	head->key = NULL;
+	head->next = head;
+	head->prev = head;
+
+	dict->frame = frame;
+	dict->leq = leq;
+
+	if (alloc->dictNodeBucketSize < 16)
+		alloc->dictNodeBucketSize = 16;
+	if (alloc->dictNodeBucketSize > 4096)
+		alloc->dictNodeBucketSize = 4096;
+	dict->nodePool = createBucketAlloc( alloc, "Dict", sizeof(DictNode), alloc->dictNodeBucketSize );
+
+	return dict;
+}
+
+/* really tessDictListDeleteDict */
+void dictDeleteDict( TESSalloc* alloc, Dict *dict )
+{
+	deleteBucketAlloc( dict->nodePool );
+	alloc->memfree( alloc->userData, dict );
+}
+
+/* really tessDictListInsertBefore */
+DictNode *dictInsertBefore( Dict *dict, DictNode *node, DictKey key )
+{
+	DictNode *newNode;
+
+	do {
+		node = node->prev;
+	} while( node->key != NULL && ! (*dict->leq)(dict->frame, node->key, key));
+
+	newNode = (DictNode *)bucketAlloc( dict->nodePool );
+	if (newNode == NULL) return NULL;
+
+	newNode->key = key;
+	newNode->next = node->next;
+	node->next->prev = newNode;
+	newNode->prev = node;
+	node->next = newNode;
+
+	return newNode;
+}
+
+/* really tessDictListDelete */
+void dictDelete( Dict *dict, DictNode *node ) /*ARGSUSED*/
+{
+	node->next->prev = node->prev;
+	node->prev->next = node->next;
+	bucketFree( dict->nodePool, node );
+}
+
+/* really tessDictListSearch */
+DictNode *dictSearch( Dict *dict, DictKey key )
+{
+	DictNode *node = &dict->head;
+
+	do {
+		node = node->next;
+	} while( node->key != NULL && ! (*dict->leq)(dict->frame, key, node->key));
+
+	return node;
+}

+ 74 - 0
polygon.mod/earcut/test/comparison/libtess2/dict.h

@@ -0,0 +1,74 @@
+/*
+** SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
+** Copyright (C) [dates of first publication] Silicon Graphics, Inc.
+** All Rights Reserved.
+**
+** 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 including the dates of first publication and either this
+** permission notice or a reference to http://oss.sgi.com/projects/FreeB/ 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 SILICON GRAPHICS, INC.
+** 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.
+**
+** Except as contained in this notice, the name of Silicon Graphics, Inc. shall not
+** be used in advertising or otherwise to promote the sale, use or other dealings in
+** this Software without prior written authorization from Silicon Graphics, Inc.
+*/
+/*
+** Author: Eric Veach, July 1994.
+*/
+
+#ifndef DICT_LIST_H
+#define DICT_LIST_H
+
+typedef void *DictKey;
+typedef struct Dict Dict;
+typedef struct DictNode DictNode;
+
+Dict *dictNewDict( TESSalloc* alloc, void *frame, int (*leq)(void *frame, DictKey key1, DictKey key2) );
+
+void dictDeleteDict( TESSalloc* alloc, Dict *dict );
+
+/* Search returns the node with the smallest key greater than or equal
+* to the given key.  If there is no such key, returns a node whose
+* key is NULL.  Similarly, Succ(Max(d)) has a NULL key, etc.
+*/
+DictNode *dictSearch( Dict *dict, DictKey key );
+DictNode *dictInsertBefore( Dict *dict, DictNode *node, DictKey key );
+void dictDelete( Dict *dict, DictNode *node );
+
+#define dictKey(n)	((n)->key)
+#define dictSucc(n)	((n)->next)
+#define dictPred(n)	((n)->prev)
+#define dictMin(d)	((d)->head.next)
+#define dictMax(d)	((d)->head.prev)
+#define dictInsert(d,k) (dictInsertBefore((d),&(d)->head,(k)))
+
+
+/*** Private data structures ***/
+
+struct DictNode {
+	DictKey	key;
+	DictNode *next;
+	DictNode *prev;
+};
+
+struct Dict {
+	DictNode head;
+	void *frame;
+	struct BucketAlloc *nodePool;
+	int (*leq)(void *frame, DictKey key1, DictKey key2);
+};
+
+#endif

+ 261 - 0
polygon.mod/earcut/test/comparison/libtess2/geom.c

@@ -0,0 +1,261 @@
+/*
+** SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
+** Copyright (C) [dates of first publication] Silicon Graphics, Inc.
+** All Rights Reserved.
+**
+** 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 including the dates of first publication and either this
+** permission notice or a reference to http://oss.sgi.com/projects/FreeB/ 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 SILICON GRAPHICS, INC.
+** 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.
+**
+** Except as contained in this notice, the name of Silicon Graphics, Inc. shall not
+** be used in advertising or otherwise to promote the sale, use or other dealings in
+** this Software without prior written authorization from Silicon Graphics, Inc.
+*/
+/*
+** Author: Eric Veach, July 1994.
+*/
+
+//#include "tesos.h"
+#include <assert.h>
+#include "mesh.h"
+#include "geom.h"
+
+int tesvertLeq( TESSvertex *u, TESSvertex *v )
+{
+	/* Returns TRUE if u is lexicographically <= v. */
+
+	return VertLeq( u, v );
+}
+
+TESSreal tesedgeEval( TESSvertex *u, TESSvertex *v, TESSvertex *w )
+{
+	/* Given three vertices u,v,w such that VertLeq(u,v) && VertLeq(v,w),
+	* evaluates the t-coord of the edge uw at the s-coord of the vertex v.
+	* Returns v->t - (uw)(v->s), ie. the signed distance from uw to v.
+	* If uw is vertical (and thus passes thru v), the result is zero.
+	*
+	* The calculation is extremely accurate and stable, even when v
+	* is very close to u or w.  In particular if we set v->t = 0 and
+	* let r be the negated result (this evaluates (uw)(v->s)), then
+	* r is guaranteed to satisfy MIN(u->t,w->t) <= r <= MAX(u->t,w->t).
+	*/
+	TESSreal gapL, gapR;
+
+	assert( VertLeq( u, v ) && VertLeq( v, w ));
+
+	gapL = v->s - u->s;
+	gapR = w->s - v->s;
+
+	if( gapL + gapR > 0 ) {
+		if( gapL < gapR ) {
+			return (v->t - u->t) + (u->t - w->t) * (gapL / (gapL + gapR));
+		} else {
+			return (v->t - w->t) + (w->t - u->t) * (gapR / (gapL + gapR));
+		}
+	}
+	/* vertical line */
+	return 0;
+}
+
+TESSreal tesedgeSign( TESSvertex *u, TESSvertex *v, TESSvertex *w )
+{
+	/* Returns a number whose sign matches EdgeEval(u,v,w) but which
+	* is cheaper to evaluate.  Returns > 0, == 0 , or < 0
+	* as v is above, on, or below the edge uw.
+	*/
+	TESSreal gapL, gapR;
+
+	assert( VertLeq( u, v ) && VertLeq( v, w ));
+
+	gapL = v->s - u->s;
+	gapR = w->s - v->s;
+
+	if( gapL + gapR > 0 ) {
+		return (v->t - w->t) * gapL + (v->t - u->t) * gapR;
+	}
+	/* vertical line */
+	return 0;
+}
+
+
+/***********************************************************************
+* Define versions of EdgeSign, EdgeEval with s and t transposed.
+*/
+
+TESSreal testransEval( TESSvertex *u, TESSvertex *v, TESSvertex *w )
+{
+	/* Given three vertices u,v,w such that TransLeq(u,v) && TransLeq(v,w),
+	* evaluates the t-coord of the edge uw at the s-coord of the vertex v.
+	* Returns v->s - (uw)(v->t), ie. the signed distance from uw to v.
+	* If uw is vertical (and thus passes thru v), the result is zero.
+	*
+	* The calculation is extremely accurate and stable, even when v
+	* is very close to u or w.  In particular if we set v->s = 0 and
+	* let r be the negated result (this evaluates (uw)(v->t)), then
+	* r is guaranteed to satisfy MIN(u->s,w->s) <= r <= MAX(u->s,w->s).
+	*/
+	TESSreal gapL, gapR;
+
+	assert( TransLeq( u, v ) && TransLeq( v, w ));
+
+	gapL = v->t - u->t;
+	gapR = w->t - v->t;
+
+	if( gapL + gapR > 0 ) {
+		if( gapL < gapR ) {
+			return (v->s - u->s) + (u->s - w->s) * (gapL / (gapL + gapR));
+		} else {
+			return (v->s - w->s) + (w->s - u->s) * (gapR / (gapL + gapR));
+		}
+	}
+	/* vertical line */
+	return 0;
+}
+
+TESSreal testransSign( TESSvertex *u, TESSvertex *v, TESSvertex *w )
+{
+	/* Returns a number whose sign matches TransEval(u,v,w) but which
+	* is cheaper to evaluate.  Returns > 0, == 0 , or < 0
+	* as v is above, on, or below the edge uw.
+	*/
+	TESSreal gapL, gapR;
+
+	assert( TransLeq( u, v ) && TransLeq( v, w ));
+
+	gapL = v->t - u->t;
+	gapR = w->t - v->t;
+
+	if( gapL + gapR > 0 ) {
+		return (v->s - w->s) * gapL + (v->s - u->s) * gapR;
+	}
+	/* vertical line */
+	return 0;
+}
+
+
+int tesvertCCW( TESSvertex *u, TESSvertex *v, TESSvertex *w )
+{
+	/* For almost-degenerate situations, the results are not reliable.
+	* Unless the floating-point arithmetic can be performed without
+	* rounding errors, *any* implementation will give incorrect results
+	* on some degenerate inputs, so the client must have some way to
+	* handle this situation.
+	*/
+	return (u->s*(v->t - w->t) + v->s*(w->t - u->t) + w->s*(u->t - v->t)) >= 0;
+}
+
+/* Given parameters a,x,b,y returns the value (b*x+a*y)/(a+b),
+* or (x+y)/2 if a==b==0.  It requires that a,b >= 0, and enforces
+* this in the rare case that one argument is slightly negative.
+* The implementation is extremely stable numerically.
+* In particular it guarantees that the result r satisfies
+* MIN(x,y) <= r <= MAX(x,y), and the results are very accurate
+* even when a and b differ greatly in magnitude.
+*/
+#define RealInterpolate(a,x,b,y)			\
+	(a = (a < 0) ? 0 : a, b = (b < 0) ? 0 : b,		\
+	((a <= b) ? ((b == 0) ? ((x+y) / 2)			\
+	: (x + (y-x) * (a/(a+b))))	\
+	: (y + (x-y) * (b/(a+b)))))
+
+#ifndef FOR_TRITE_TEST_PROGRAM
+#define Interpolate(a,x,b,y)	RealInterpolate(a,x,b,y)
+#else
+
+/* Claim: the ONLY property the sweep algorithm relies on is that
+* MIN(x,y) <= r <= MAX(x,y).  This is a nasty way to test that.
+*/
+#include <stdlib.h>
+extern int RandomInterpolate;
+
+double Interpolate( double a, double x, double b, double y)
+{
+	printf("*********************%d\n",RandomInterpolate);
+	if( RandomInterpolate ) {
+		a = 1.2 * drand48() - 0.1;
+		a = (a < 0) ? 0 : ((a > 1) ? 1 : a);
+		b = 1.0 - a;
+	}
+	return RealInterpolate(a,x,b,y);
+}
+
+#endif
+
+#define Swap(a,b)	if (1) { TESSvertex *t = a; a = b; b = t; } else
+
+void tesedgeIntersect( TESSvertex *o1, TESSvertex *d1,
+					  TESSvertex *o2, TESSvertex *d2,
+					  TESSvertex *v )
+					  /* Given edges (o1,d1) and (o2,d2), compute their point of intersection.
+					  * The computed point is guaranteed to lie in the intersection of the
+					  * bounding rectangles defined by each edge.
+					  */
+{
+	TESSreal z1, z2;
+
+	/* This is certainly not the most efficient way to find the intersection
+	* of two line segments, but it is very numerically stable.
+	*
+	* Strategy: find the two middle vertices in the VertLeq ordering,
+	* and interpolate the intersection s-value from these.  Then repeat
+	* using the TransLeq ordering to find the intersection t-value.
+	*/
+
+	if( ! VertLeq( o1, d1 )) { Swap( o1, d1 ); }
+	if( ! VertLeq( o2, d2 )) { Swap( o2, d2 ); }
+	if( ! VertLeq( o1, o2 )) { Swap( o1, o2 ); Swap( d1, d2 ); }
+
+	if( ! VertLeq( o2, d1 )) {
+		/* Technically, no intersection -- do our best */
+		v->s = (o2->s + d1->s) / 2;
+	} else if( VertLeq( d1, d2 )) {
+		/* Interpolate between o2 and d1 */
+		z1 = EdgeEval( o1, o2, d1 );
+		z2 = EdgeEval( o2, d1, d2 );
+		if( z1+z2 < 0 ) { z1 = -z1; z2 = -z2; }
+		v->s = Interpolate( z1, o2->s, z2, d1->s );
+	} else {
+		/* Interpolate between o2 and d2 */
+		z1 = EdgeSign( o1, o2, d1 );
+		z2 = -EdgeSign( o1, d2, d1 );
+		if( z1+z2 < 0 ) { z1 = -z1; z2 = -z2; }
+		v->s = Interpolate( z1, o2->s, z2, d2->s );
+	}
+
+	/* Now repeat the process for t */
+
+	if( ! TransLeq( o1, d1 )) { Swap( o1, d1 ); }
+	if( ! TransLeq( o2, d2 )) { Swap( o2, d2 ); }
+	if( ! TransLeq( o1, o2 )) { Swap( o1, o2 ); Swap( d1, d2 ); }
+
+	if( ! TransLeq( o2, d1 )) {
+		/* Technically, no intersection -- do our best */
+		v->t = (o2->t + d1->t) / 2;
+	} else if( TransLeq( d1, d2 )) {
+		/* Interpolate between o2 and d1 */
+		z1 = TransEval( o1, o2, d1 );
+		z2 = TransEval( o2, d1, d2 );
+		if( z1+z2 < 0 ) { z1 = -z1; z2 = -z2; }
+		v->t = Interpolate( z1, o2->t, z2, d1->t );
+	} else {
+		/* Interpolate between o2 and d2 */
+		z1 = TransSign( o1, o2, d1 );
+		z2 = -TransSign( o1, d2, d1 );
+		if( z1+z2 < 0 ) { z1 = -z1; z2 = -z2; }
+		v->t = Interpolate( z1, o2->t, z2, d2->t );
+	}
+}

+ 76 - 0
polygon.mod/earcut/test/comparison/libtess2/geom.h

@@ -0,0 +1,76 @@
+/*
+** SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
+** Copyright (C) [dates of first publication] Silicon Graphics, Inc.
+** All Rights Reserved.
+**
+** 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 including the dates of first publication and either this
+** permission notice or a reference to http://oss.sgi.com/projects/FreeB/ 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 SILICON GRAPHICS, INC.
+** 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.
+**
+** Except as contained in this notice, the name of Silicon Graphics, Inc. shall not
+** be used in advertising or otherwise to promote the sale, use or other dealings in
+** this Software without prior written authorization from Silicon Graphics, Inc.
+*/
+/*
+** Author: Eric Veach, July 1994.
+*/
+
+#ifndef GEOM_H
+#define GEOM_H
+
+#include "mesh.h"
+
+#ifdef NO_BRANCH_CONDITIONS
+/* MIPS architecture has special instructions to evaluate boolean
+* conditions -- more efficient than branching, IF you can get the
+* compiler to generate the right instructions (SGI compiler doesn't)
+*/
+#define VertEq(u,v)	(((u)->s == (v)->s) & ((u)->t == (v)->t))
+#define VertLeq(u,v)	(((u)->s < (v)->s) | \
+	((u)->s == (v)->s & (u)->t <= (v)->t))
+#else
+#define VertEq(u,v) ((u)->s == (v)->s && (u)->t == (v)->t)
+#define VertLeq(u,v) (((u)->s < (v)->s) || ((u)->s == (v)->s && (u)->t <= (v)->t))
+#endif
+
+#define EdgeEval(u,v,w)	tesedgeEval(u,v,w)
+#define EdgeSign(u,v,w)	tesedgeSign(u,v,w)
+
+/* Versions of VertLeq, EdgeSign, EdgeEval with s and t transposed. */
+
+#define TransLeq(u,v) (((u)->t < (v)->t) || ((u)->t == (v)->t && (u)->s <= (v)->s))
+#define TransEval(u,v,w) testransEval(u,v,w)
+#define TransSign(u,v,w) testransSign(u,v,w)
+
+
+#define EdgeGoesLeft(e) VertLeq( (e)->Dst, (e)->Org )
+#define EdgeGoesRight(e) VertLeq( (e)->Org, (e)->Dst )
+
+#define ABS(x) ((x) < 0 ? -(x) : (x))
+#define VertL1dist(u,v) (ABS(u->s - v->s) + ABS(u->t - v->t))
+
+#define VertCCW(u,v,w) tesvertCCW(u,v,w)
+
+int tesvertLeq( TESSvertex *u, TESSvertex *v );
+TESSreal	tesedgeEval( TESSvertex *u, TESSvertex *v, TESSvertex *w );
+TESSreal	tesedgeSign( TESSvertex *u, TESSvertex *v, TESSvertex *w );
+TESSreal	testransEval( TESSvertex *u, TESSvertex *v, TESSvertex *w );
+TESSreal	testransSign( TESSvertex *u, TESSvertex *v, TESSvertex *w );
+int tesvertCCW( TESSvertex *u, TESSvertex *v, TESSvertex *w );
+void tesedgeIntersect( TESSvertex *o1, TESSvertex *d1, TESSvertex *o2, TESSvertex *d2, TESSvertex *v );
+
+#endif

+ 843 - 0
polygon.mod/earcut/test/comparison/libtess2/mesh.c

@@ -0,0 +1,843 @@
+/*
+** SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
+** Copyright (C) [dates of first publication] Silicon Graphics, Inc.
+** All Rights Reserved.
+**
+** 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 including the dates of first publication and either this
+** permission notice or a reference to http://oss.sgi.com/projects/FreeB/ 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 SILICON GRAPHICS, INC.
+** 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.
+**
+** Except as contained in this notice, the name of Silicon Graphics, Inc. shall not
+** be used in advertising or otherwise to promote the sale, use or other dealings in
+** this Software without prior written authorization from Silicon Graphics, Inc.
+*/
+/*
+** Author: Eric Veach, July 1994.
+*/
+
+//#include "tesos.h"
+#include <stddef.h>
+#include <assert.h>
+#include "mesh.h"
+#include "geom.h"
+#include "bucketalloc.h"
+
+#define TRUE 1
+#define FALSE 0
+
+/************************ Utility Routines ************************/
+
+/* Allocate and free half-edges in pairs for efficiency.
+* The *only* place that should use this fact is allocation/free.
+*/
+typedef struct { TESShalfEdge e, eSym; } EdgePair;
+
+/* MakeEdge creates a new pair of half-edges which form their own loop.
+* No vertex or face structures are allocated, but these must be assigned
+* before the current edge operation is completed.
+*/
+static TESShalfEdge *MakeEdge( TESSmesh* mesh, TESShalfEdge *eNext )
+{
+	TESShalfEdge *e;
+	TESShalfEdge *eSym;
+	TESShalfEdge *ePrev;
+	EdgePair *pair = (EdgePair *)bucketAlloc( mesh->edgeBucket );
+	if (pair == NULL) return NULL;
+
+	e = &pair->e;
+	eSym = &pair->eSym;
+
+	/* Make sure eNext points to the first edge of the edge pair */
+	if( eNext->Sym < eNext ) { eNext = eNext->Sym; }
+
+	/* Insert in circular doubly-linked list before eNext.
+	* Note that the prev pointer is stored in Sym->next.
+	*/
+	ePrev = eNext->Sym->next;
+	eSym->next = ePrev;
+	ePrev->Sym->next = e;
+	e->next = eNext;
+	eNext->Sym->next = eSym;
+
+	e->Sym = eSym;
+	e->Onext = e;
+	e->Lnext = eSym;
+	e->Org = NULL;
+	e->Lface = NULL;
+	e->winding = 0;
+	e->activeRegion = NULL;
+
+	eSym->Sym = e;
+	eSym->Onext = eSym;
+	eSym->Lnext = e;
+	eSym->Org = NULL;
+	eSym->Lface = NULL;
+	eSym->winding = 0;
+	eSym->activeRegion = NULL;
+
+	return e;
+}
+
+/* Splice( a, b ) is best described by the Guibas/Stolfi paper or the
+* CS348a notes (see mesh.h).  Basically it modifies the mesh so that
+* a->Onext and b->Onext are exchanged.  This can have various effects
+* depending on whether a and b belong to different face or vertex rings.
+* For more explanation see tessMeshSplice() below.
+*/
+static void Splice( TESShalfEdge *a, TESShalfEdge *b )
+{
+	TESShalfEdge *aOnext = a->Onext;
+	TESShalfEdge *bOnext = b->Onext;
+
+	aOnext->Sym->Lnext = b;
+	bOnext->Sym->Lnext = a;
+	a->Onext = bOnext;
+	b->Onext = aOnext;
+}
+
+/* MakeVertex( newVertex, eOrig, vNext ) attaches a new vertex and makes it the
+* origin of all edges in the vertex loop to which eOrig belongs. "vNext" gives
+* a place to insert the new vertex in the global vertex list.  We insert
+* the new vertex *before* vNext so that algorithms which walk the vertex
+* list will not see the newly created vertices.
+*/
+static void MakeVertex( TESSvertex *newVertex,
+					   TESShalfEdge *eOrig, TESSvertex *vNext )
+{
+	TESShalfEdge *e;
+	TESSvertex *vPrev;
+	TESSvertex *vNew = newVertex;
+
+	assert(vNew != NULL);
+
+	/* insert in circular doubly-linked list before vNext */
+	vPrev = vNext->prev;
+	vNew->prev = vPrev;
+	vPrev->next = vNew;
+	vNew->next = vNext;
+	vNext->prev = vNew;
+
+	vNew->anEdge = eOrig;
+	/* leave coords, s, t undefined */
+
+	/* fix other edges on this vertex loop */
+	e = eOrig;
+	do {
+		e->Org = vNew;
+		e = e->Onext;
+	} while( e != eOrig );
+}
+
+/* MakeFace( newFace, eOrig, fNext ) attaches a new face and makes it the left
+* face of all edges in the face loop to which eOrig belongs.  "fNext" gives
+* a place to insert the new face in the global face list.  We insert
+* the new face *before* fNext so that algorithms which walk the face
+* list will not see the newly created faces.
+*/
+static void MakeFace( TESSface *newFace, TESShalfEdge *eOrig, TESSface *fNext )
+{
+	TESShalfEdge *e;
+	TESSface *fPrev;
+	TESSface *fNew = newFace;
+
+	assert(fNew != NULL);
+
+	/* insert in circular doubly-linked list before fNext */
+	fPrev = fNext->prev;
+	fNew->prev = fPrev;
+	fPrev->next = fNew;
+	fNew->next = fNext;
+	fNext->prev = fNew;
+
+	fNew->anEdge = eOrig;
+	fNew->trail = NULL;
+	fNew->marked = FALSE;
+
+	/* The new face is marked "inside" if the old one was.  This is a
+	* convenience for the common case where a face has been split in two.
+	*/
+	fNew->inside = fNext->inside;
+
+	/* fix other edges on this face loop */
+	e = eOrig;
+	do {
+		e->Lface = fNew;
+		e = e->Lnext;
+	} while( e != eOrig );
+}
+
+/* KillEdge( eDel ) destroys an edge (the half-edges eDel and eDel->Sym),
+* and removes from the global edge list.
+*/
+static void KillEdge( TESSmesh *mesh, TESShalfEdge *eDel )
+{
+	TESShalfEdge *ePrev, *eNext;
+
+	/* Half-edges are allocated in pairs, see EdgePair above */
+	if( eDel->Sym < eDel ) { eDel = eDel->Sym; }
+
+	/* delete from circular doubly-linked list */
+	eNext = eDel->next;
+	ePrev = eDel->Sym->next;
+	eNext->Sym->next = ePrev;
+	ePrev->Sym->next = eNext;
+
+	bucketFree( mesh->edgeBucket, eDel );
+}
+
+
+/* KillVertex( vDel ) destroys a vertex and removes it from the global
+* vertex list.  It updates the vertex loop to point to a given new vertex.
+*/
+static void KillVertex( TESSmesh *mesh, TESSvertex *vDel, TESSvertex *newOrg )
+{
+	TESShalfEdge *e, *eStart = vDel->anEdge;
+	TESSvertex *vPrev, *vNext;
+
+	/* change the origin of all affected edges */
+	e = eStart;
+	do {
+		e->Org = newOrg;
+		e = e->Onext;
+	} while( e != eStart );
+
+	/* delete from circular doubly-linked list */
+	vPrev = vDel->prev;
+	vNext = vDel->next;
+	vNext->prev = vPrev;
+	vPrev->next = vNext;
+
+	bucketFree( mesh->vertexBucket, vDel );
+}
+
+/* KillFace( fDel ) destroys a face and removes it from the global face
+* list.  It updates the face loop to point to a given new face.
+*/
+static void KillFace( TESSmesh *mesh, TESSface *fDel, TESSface *newLface )
+{
+	TESShalfEdge *e, *eStart = fDel->anEdge;
+	TESSface *fPrev, *fNext;
+
+	/* change the left face of all affected edges */
+	e = eStart;
+	do {
+		e->Lface = newLface;
+		e = e->Lnext;
+	} while( e != eStart );
+
+	/* delete from circular doubly-linked list */
+	fPrev = fDel->prev;
+	fNext = fDel->next;
+	fNext->prev = fPrev;
+	fPrev->next = fNext;
+
+	bucketFree( mesh->faceBucket, fDel );
+}
+
+
+/****************** Basic Edge Operations **********************/
+
+/* tessMeshMakeEdge creates one edge, two vertices, and a loop (face).
+* The loop consists of the two new half-edges.
+*/
+TESShalfEdge *tessMeshMakeEdge( TESSmesh *mesh )
+{
+	TESSvertex *newVertex1 = (TESSvertex*)bucketAlloc(mesh->vertexBucket);
+	TESSvertex *newVertex2 = (TESSvertex*)bucketAlloc(mesh->vertexBucket);
+	TESSface *newFace = (TESSface*)bucketAlloc(mesh->faceBucket);
+	TESShalfEdge *e;
+
+	/* if any one is null then all get freed */
+	if (newVertex1 == NULL || newVertex2 == NULL || newFace == NULL) {
+		if (newVertex1 != NULL) bucketFree( mesh->vertexBucket, newVertex1 );
+		if (newVertex2 != NULL) bucketFree( mesh->vertexBucket, newVertex2 );
+		if (newFace != NULL) bucketFree( mesh->faceBucket, newFace );
+		return NULL;
+	}
+
+	e = MakeEdge( mesh, &mesh->eHead );
+	if (e == NULL) return NULL;
+
+	MakeVertex( newVertex1, e, &mesh->vHead );
+	MakeVertex( newVertex2, e->Sym, &mesh->vHead );
+	MakeFace( newFace, e, &mesh->fHead );
+	return e;
+}
+
+
+/* tessMeshSplice( eOrg, eDst ) is the basic operation for changing the
+* mesh connectivity and topology.  It changes the mesh so that
+*	eOrg->Onext <- OLD( eDst->Onext )
+*	eDst->Onext <- OLD( eOrg->Onext )
+* where OLD(...) means the value before the meshSplice operation.
+*
+* This can have two effects on the vertex structure:
+*  - if eOrg->Org != eDst->Org, the two vertices are merged together
+*  - if eOrg->Org == eDst->Org, the origin is split into two vertices
+* In both cases, eDst->Org is changed and eOrg->Org is untouched.
+*
+* Similarly (and independently) for the face structure,
+*  - if eOrg->Lface == eDst->Lface, one loop is split into two
+*  - if eOrg->Lface != eDst->Lface, two distinct loops are joined into one
+* In both cases, eDst->Lface is changed and eOrg->Lface is unaffected.
+*
+* Some special cases:
+* If eDst == eOrg, the operation has no effect.
+* If eDst == eOrg->Lnext, the new face will have a single edge.
+* If eDst == eOrg->Lprev, the old face will have a single edge.
+* If eDst == eOrg->Onext, the new vertex will have a single edge.
+* If eDst == eOrg->Oprev, the old vertex will have a single edge.
+*/
+int tessMeshSplice( TESSmesh* mesh, TESShalfEdge *eOrg, TESShalfEdge *eDst )
+{
+	int joiningLoops = FALSE;
+	int joiningVertices = FALSE;
+
+	if( eOrg == eDst ) return 1;
+
+	if( eDst->Org != eOrg->Org ) {
+		/* We are merging two disjoint vertices -- destroy eDst->Org */
+		joiningVertices = TRUE;
+		KillVertex( mesh, eDst->Org, eOrg->Org );
+	}
+	if( eDst->Lface != eOrg->Lface ) {
+		/* We are connecting two disjoint loops -- destroy eDst->Lface */
+		joiningLoops = TRUE;
+		KillFace( mesh, eDst->Lface, eOrg->Lface );
+	}
+
+	/* Change the edge structure */
+	Splice( eDst, eOrg );
+
+	if( ! joiningVertices ) {
+		TESSvertex *newVertex = (TESSvertex*)bucketAlloc( mesh->vertexBucket );
+		if (newVertex == NULL) return 0;
+
+		/* We split one vertex into two -- the new vertex is eDst->Org.
+		* Make sure the old vertex points to a valid half-edge.
+		*/
+		MakeVertex( newVertex, eDst, eOrg->Org );
+		eOrg->Org->anEdge = eOrg;
+	}
+	if( ! joiningLoops ) {
+		TESSface *newFace = (TESSface*)bucketAlloc( mesh->faceBucket );
+		if (newFace == NULL) return 0;
+
+		/* We split one loop into two -- the new loop is eDst->Lface.
+		* Make sure the old face points to a valid half-edge.
+		*/
+		MakeFace( newFace, eDst, eOrg->Lface );
+		eOrg->Lface->anEdge = eOrg;
+	}
+
+	return 1;
+}
+
+
+/* tessMeshDelete( eDel ) removes the edge eDel.  There are several cases:
+* if (eDel->Lface != eDel->Rface), we join two loops into one; the loop
+* eDel->Lface is deleted.  Otherwise, we are splitting one loop into two;
+* the newly created loop will contain eDel->Dst.  If the deletion of eDel
+* would create isolated vertices, those are deleted as well.
+*
+* This function could be implemented as two calls to tessMeshSplice
+* plus a few calls to memFree, but this would allocate and delete
+* unnecessary vertices and faces.
+*/
+int tessMeshDelete( TESSmesh *mesh, TESShalfEdge *eDel )
+{
+	TESShalfEdge *eDelSym = eDel->Sym;
+	int joiningLoops = FALSE;
+
+	/* First step: disconnect the origin vertex eDel->Org.  We make all
+	* changes to get a consistent mesh in this "intermediate" state.
+	*/
+	if( eDel->Lface != eDel->Rface ) {
+		/* We are joining two loops into one -- remove the left face */
+		joiningLoops = TRUE;
+		KillFace( mesh, eDel->Lface, eDel->Rface );
+	}
+
+	if( eDel->Onext == eDel ) {
+		KillVertex( mesh, eDel->Org, NULL );
+	} else {
+		/* Make sure that eDel->Org and eDel->Rface point to valid half-edges */
+		eDel->Rface->anEdge = eDel->Oprev;
+		eDel->Org->anEdge = eDel->Onext;
+
+		Splice( eDel, eDel->Oprev );
+		if( ! joiningLoops ) {
+			TESSface *newFace= (TESSface*)bucketAlloc( mesh->faceBucket );
+			if (newFace == NULL) return 0;
+
+			/* We are splitting one loop into two -- create a new loop for eDel. */
+			MakeFace( newFace, eDel, eDel->Lface );
+		}
+	}
+
+	/* Claim: the mesh is now in a consistent state, except that eDel->Org
+	* may have been deleted.  Now we disconnect eDel->Dst.
+	*/
+	if( eDelSym->Onext == eDelSym ) {
+		KillVertex( mesh, eDelSym->Org, NULL );
+		KillFace( mesh, eDelSym->Lface, NULL );
+	} else {
+		/* Make sure that eDel->Dst and eDel->Lface point to valid half-edges */
+		eDel->Lface->anEdge = eDelSym->Oprev;
+		eDelSym->Org->anEdge = eDelSym->Onext;
+		Splice( eDelSym, eDelSym->Oprev );
+	}
+
+	/* Any isolated vertices or faces have already been freed. */
+	KillEdge( mesh, eDel );
+
+	return 1;
+}
+
+
+/******************** Other Edge Operations **********************/
+
+/* All these routines can be implemented with the basic edge
+* operations above.  They are provided for convenience and efficiency.
+*/
+
+
+/* tessMeshAddEdgeVertex( eOrg ) creates a new edge eNew such that
+* eNew == eOrg->Lnext, and eNew->Dst is a newly created vertex.
+* eOrg and eNew will have the same left face.
+*/
+TESShalfEdge *tessMeshAddEdgeVertex( TESSmesh *mesh, TESShalfEdge *eOrg )
+{
+	TESShalfEdge *eNewSym;
+	TESShalfEdge *eNew = MakeEdge( mesh, eOrg );
+	if (eNew == NULL) return NULL;
+
+	eNewSym = eNew->Sym;
+
+	/* Connect the new edge appropriately */
+	Splice( eNew, eOrg->Lnext );
+
+	/* Set the vertex and face information */
+	eNew->Org = eOrg->Dst;
+	{
+		TESSvertex *newVertex= (TESSvertex*)bucketAlloc( mesh->vertexBucket );
+		if (newVertex == NULL) return NULL;
+
+		MakeVertex( newVertex, eNewSym, eNew->Org );
+	}
+	eNew->Lface = eNewSym->Lface = eOrg->Lface;
+
+	return eNew;
+}
+
+
+/* tessMeshSplitEdge( eOrg ) splits eOrg into two edges eOrg and eNew,
+* such that eNew == eOrg->Lnext.  The new vertex is eOrg->Dst == eNew->Org.
+* eOrg and eNew will have the same left face.
+*/
+TESShalfEdge *tessMeshSplitEdge( TESSmesh *mesh, TESShalfEdge *eOrg )
+{
+	TESShalfEdge *eNew;
+	TESShalfEdge *tempHalfEdge= tessMeshAddEdgeVertex( mesh, eOrg );
+	if (tempHalfEdge == NULL) return NULL;
+
+	eNew = tempHalfEdge->Sym;
+
+	/* Disconnect eOrg from eOrg->Dst and connect it to eNew->Org */
+	Splice( eOrg->Sym, eOrg->Sym->Oprev );
+	Splice( eOrg->Sym, eNew );
+
+	/* Set the vertex and face information */
+	eOrg->Dst = eNew->Org;
+	eNew->Dst->anEdge = eNew->Sym;	/* may have pointed to eOrg->Sym */
+	eNew->Rface = eOrg->Rface;
+	eNew->winding = eOrg->winding;	/* copy old winding information */
+	eNew->Sym->winding = eOrg->Sym->winding;
+
+	return eNew;
+}
+
+
+/* tessMeshConnect( eOrg, eDst ) creates a new edge from eOrg->Dst
+* to eDst->Org, and returns the corresponding half-edge eNew.
+* If eOrg->Lface == eDst->Lface, this splits one loop into two,
+* and the newly created loop is eNew->Lface.  Otherwise, two disjoint
+* loops are merged into one, and the loop eDst->Lface is destroyed.
+*
+* If (eOrg == eDst), the new face will have only two edges.
+* If (eOrg->Lnext == eDst), the old face is reduced to a single edge.
+* If (eOrg->Lnext->Lnext == eDst), the old face is reduced to two edges.
+*/
+TESShalfEdge *tessMeshConnect( TESSmesh *mesh, TESShalfEdge *eOrg, TESShalfEdge *eDst )
+{
+	TESShalfEdge *eNewSym;
+	int joiningLoops = FALSE;
+	TESShalfEdge *eNew = MakeEdge( mesh, eOrg );
+	if (eNew == NULL) return NULL;
+
+	eNewSym = eNew->Sym;
+
+	if( eDst->Lface != eOrg->Lface ) {
+		/* We are connecting two disjoint loops -- destroy eDst->Lface */
+		joiningLoops = TRUE;
+		KillFace( mesh, eDst->Lface, eOrg->Lface );
+	}
+
+	/* Connect the new edge appropriately */
+	Splice( eNew, eOrg->Lnext );
+	Splice( eNewSym, eDst );
+
+	/* Set the vertex and face information */
+	eNew->Org = eOrg->Dst;
+	eNewSym->Org = eDst->Org;
+	eNew->Lface = eNewSym->Lface = eOrg->Lface;
+
+	/* Make sure the old face points to a valid half-edge */
+	eOrg->Lface->anEdge = eNewSym;
+
+	if( ! joiningLoops ) {
+		TESSface *newFace= (TESSface*)bucketAlloc( mesh->faceBucket );
+		if (newFace == NULL) return NULL;
+
+		/* We split one loop into two -- the new loop is eNew->Lface */
+		MakeFace( newFace, eNew, eOrg->Lface );
+	}
+	return eNew;
+}
+
+
+/******************** Other Operations **********************/
+
+/* tessMeshZapFace( fZap ) destroys a face and removes it from the
+* global face list.  All edges of fZap will have a NULL pointer as their
+* left face.  Any edges which also have a NULL pointer as their right face
+* are deleted entirely (along with any isolated vertices this produces).
+* An entire mesh can be deleted by zapping its faces, one at a time,
+* in any order.  Zapped faces cannot be used in further mesh operations!
+*/
+void tessMeshZapFace( TESSmesh *mesh, TESSface *fZap )
+{
+	TESShalfEdge *eStart = fZap->anEdge;
+	TESShalfEdge *e, *eNext, *eSym;
+	TESSface *fPrev, *fNext;
+
+	/* walk around face, deleting edges whose right face is also NULL */
+	eNext = eStart->Lnext;
+	do {
+		e = eNext;
+		eNext = e->Lnext;
+
+		e->Lface = NULL;
+		if( e->Rface == NULL ) {
+			/* delete the edge -- see TESSmeshDelete above */
+
+			if( e->Onext == e ) {
+				KillVertex( mesh, e->Org, NULL );
+			} else {
+				/* Make sure that e->Org points to a valid half-edge */
+				e->Org->anEdge = e->Onext;
+				Splice( e, e->Oprev );
+			}
+			eSym = e->Sym;
+			if( eSym->Onext == eSym ) {
+				KillVertex( mesh, eSym->Org, NULL );
+			} else {
+				/* Make sure that eSym->Org points to a valid half-edge */
+				eSym->Org->anEdge = eSym->Onext;
+				Splice( eSym, eSym->Oprev );
+			}
+			KillEdge( mesh, e );
+		}
+	} while( e != eStart );
+
+	/* delete from circular doubly-linked list */
+	fPrev = fZap->prev;
+	fNext = fZap->next;
+	fNext->prev = fPrev;
+	fPrev->next = fNext;
+
+	bucketFree( mesh->faceBucket, fZap );
+}
+
+
+/* tessMeshNewMesh() creates a new mesh with no edges, no vertices,
+* and no loops (what we usually call a "face").
+*/
+TESSmesh *tessMeshNewMesh( TESSalloc* alloc )
+{
+	TESSvertex *v;
+	TESSface *f;
+	TESShalfEdge *e;
+	TESShalfEdge *eSym;
+	TESSmesh *mesh = (TESSmesh *)alloc->memalloc( alloc->userData, sizeof( TESSmesh ));
+	if (mesh == NULL) {
+		return NULL;
+	}
+
+	if (alloc->meshEdgeBucketSize < 16)
+		alloc->meshEdgeBucketSize = 16;
+	if (alloc->meshEdgeBucketSize > 4096)
+		alloc->meshEdgeBucketSize = 4096;
+
+	if (alloc->meshVertexBucketSize < 16)
+		alloc->meshVertexBucketSize = 16;
+	if (alloc->meshVertexBucketSize > 4096)
+		alloc->meshVertexBucketSize = 4096;
+
+	if (alloc->meshFaceBucketSize < 16)
+		alloc->meshFaceBucketSize = 16;
+	if (alloc->meshFaceBucketSize > 4096)
+		alloc->meshFaceBucketSize = 4096;
+
+	mesh->edgeBucket = createBucketAlloc( alloc, "Mesh Edges", sizeof(EdgePair), alloc->meshEdgeBucketSize );
+	mesh->vertexBucket = createBucketAlloc( alloc, "Mesh Vertices", sizeof(TESSvertex), alloc->meshVertexBucketSize );
+	mesh->faceBucket = createBucketAlloc( alloc, "Mesh Faces", sizeof(TESSface), alloc->meshFaceBucketSize );
+
+	v = &mesh->vHead;
+	f = &mesh->fHead;
+	e = &mesh->eHead;
+	eSym = &mesh->eHeadSym;
+
+	v->next = v->prev = v;
+	v->anEdge = NULL;
+
+	f->next = f->prev = f;
+	f->anEdge = NULL;
+	f->trail = NULL;
+	f->marked = FALSE;
+	f->inside = FALSE;
+
+	e->next = e;
+	e->Sym = eSym;
+	e->Onext = NULL;
+	e->Lnext = NULL;
+	e->Org = NULL;
+	e->Lface = NULL;
+	e->winding = 0;
+	e->activeRegion = NULL;
+
+	eSym->next = eSym;
+	eSym->Sym = e;
+	eSym->Onext = NULL;
+	eSym->Lnext = NULL;
+	eSym->Org = NULL;
+	eSym->Lface = NULL;
+	eSym->winding = 0;
+	eSym->activeRegion = NULL;
+
+	return mesh;
+}
+
+
+/* tessMeshUnion( mesh1, mesh2 ) forms the union of all structures in
+* both meshes, and returns the new mesh (the old meshes are destroyed).
+*/
+TESSmesh *tessMeshUnion( TESSalloc* alloc, TESSmesh *mesh1, TESSmesh *mesh2 )
+{
+	TESSface *f1 = &mesh1->fHead;
+	TESSvertex *v1 = &mesh1->vHead;
+	TESShalfEdge *e1 = &mesh1->eHead;
+	TESSface *f2 = &mesh2->fHead;
+	TESSvertex *v2 = &mesh2->vHead;
+	TESShalfEdge *e2 = &mesh2->eHead;
+
+	/* Add the faces, vertices, and edges of mesh2 to those of mesh1 */
+	if( f2->next != f2 ) {
+		f1->prev->next = f2->next;
+		f2->next->prev = f1->prev;
+		f2->prev->next = f1;
+		f1->prev = f2->prev;
+	}
+
+	if( v2->next != v2 ) {
+		v1->prev->next = v2->next;
+		v2->next->prev = v1->prev;
+		v2->prev->next = v1;
+		v1->prev = v2->prev;
+	}
+
+	if( e2->next != e2 ) {
+		e1->Sym->next->Sym->next = e2->next;
+		e2->next->Sym->next = e1->Sym->next;
+		e2->Sym->next->Sym->next = e1;
+		e1->Sym->next = e2->Sym->next;
+	}
+
+	alloc->memfree( alloc->userData, mesh2 );
+	return mesh1;
+}
+
+
+static int CountFaceVerts( TESSface *f )
+{
+	TESShalfEdge *eCur = f->anEdge;
+	int n = 0;
+	do
+	{
+		n++;
+		eCur = eCur->Lnext;
+	}
+	while (eCur != f->anEdge);
+	return n;
+}
+
+int tessMeshMergeConvexFaces( TESSmesh *mesh, int maxVertsPerFace )
+{
+	TESSface *f;
+	TESShalfEdge *eCur, *eNext, *eSym;
+	TESSvertex *vStart;
+	int curNv, symNv;
+
+	for( f = mesh->fHead.next; f != &mesh->fHead; f = f->next )
+	{
+		// Skip faces which are outside the result.
+		if( !f->inside )
+			continue;
+
+		eCur = f->anEdge;
+		vStart = eCur->Org;
+
+		while (1)
+		{
+			eNext = eCur->Lnext;
+			eSym = eCur->Sym;
+
+			// Try to merge if the neighbour face is valid.
+			if( eSym && eSym->Lface && eSym->Lface->inside )
+			{
+				// Try to merge the neighbour faces if the resulting polygons
+				// does not exceed maximum number of vertices.
+				curNv = CountFaceVerts( f );
+				symNv = CountFaceVerts( eSym->Lface );
+				if( (curNv+symNv-2) <= maxVertsPerFace )
+				{
+					// Merge if the resulting poly is convex.
+					if( VertCCW( eCur->Lprev->Org, eCur->Org, eSym->Lnext->Lnext->Org ) &&
+						VertCCW( eSym->Lprev->Org, eSym->Org, eCur->Lnext->Lnext->Org ) )
+					{
+						eNext = eSym->Lnext;
+						if( !tessMeshDelete( mesh, eSym ) )
+							return 0;
+						eCur = 0;
+					}
+				}
+			}
+
+			if( eCur && eCur->Lnext->Org == vStart )
+				break;
+
+			// Continue to next edge.
+			eCur = eNext;
+		}
+	}
+
+	return 1;
+}
+
+
+#ifdef DELETE_BY_ZAPPING
+
+/* tessMeshDeleteMesh( mesh ) will free all storage for any valid mesh.
+*/
+void tessMeshDeleteMesh( TESSalloc* alloc, TESSmesh *mesh )
+{
+	TESSface *fHead = &mesh->fHead;
+
+	while( fHead->next != fHead ) {
+		tessMeshZapFace( fHead->next );
+	}
+	assert( mesh->vHead.next == &mesh->vHead );
+
+	alloc->memfree( alloc->userData, mesh );
+}
+
+#else
+
+/* tessMeshDeleteMesh( mesh ) will free all storage for any valid mesh.
+*/
+void tessMeshDeleteMesh( TESSalloc* alloc, TESSmesh *mesh )
+{
+	deleteBucketAlloc(mesh->edgeBucket);
+	deleteBucketAlloc(mesh->vertexBucket);
+	deleteBucketAlloc(mesh->faceBucket);
+
+	alloc->memfree( alloc->userData, mesh );
+}
+
+#endif
+
+#ifndef NDEBUG
+
+/* tessMeshCheckMesh( mesh ) checks a mesh for self-consistency.
+*/
+void tessMeshCheckMesh( TESSmesh *mesh )
+{
+	TESSface *fHead = &mesh->fHead;
+	TESSvertex *vHead = &mesh->vHead;
+	TESShalfEdge *eHead = &mesh->eHead;
+	TESSface *f, *fPrev;
+	TESSvertex *v, *vPrev;
+	TESShalfEdge *e, *ePrev;
+
+	fPrev = fHead;
+	for( fPrev = fHead ; (f = fPrev->next) != fHead; fPrev = f) {
+		assert( f->prev == fPrev );
+		e = f->anEdge;
+		do {
+			assert( e->Sym != e );
+			assert( e->Sym->Sym == e );
+			assert( e->Lnext->Onext->Sym == e );
+			assert( e->Onext->Sym->Lnext == e );
+			assert( e->Lface == f );
+			e = e->Lnext;
+		} while( e != f->anEdge );
+	}
+	assert( f->prev == fPrev && f->anEdge == NULL );
+
+	vPrev = vHead;
+	for( vPrev = vHead ; (v = vPrev->next) != vHead; vPrev = v) {
+		assert( v->prev == vPrev );
+		e = v->anEdge;
+		do {
+			assert( e->Sym != e );
+			assert( e->Sym->Sym == e );
+			assert( e->Lnext->Onext->Sym == e );
+			assert( e->Onext->Sym->Lnext == e );
+			assert( e->Org == v );
+			e = e->Onext;
+		} while( e != v->anEdge );
+	}
+	assert( v->prev == vPrev && v->anEdge == NULL );
+
+	ePrev = eHead;
+	for( ePrev = eHead ; (e = ePrev->next) != eHead; ePrev = e) {
+		assert( e->Sym->next == ePrev->Sym );
+		assert( e->Sym != e );
+		assert( e->Sym->Sym == e );
+		assert( e->Org != NULL );
+		assert( e->Dst != NULL );
+		assert( e->Lnext->Onext->Sym == e );
+		assert( e->Onext->Sym->Lnext == e );
+	}
+	assert( e->Sym->next == ePrev->Sym
+		&& e->Sym == &mesh->eHeadSym
+		&& e->Sym->Sym == e
+		&& e->Org == NULL && e->Dst == NULL
+		&& e->Lface == NULL && e->Rface == NULL );
+}
+
+#endif

+ 267 - 0
polygon.mod/earcut/test/comparison/libtess2/mesh.h

@@ -0,0 +1,267 @@
+/*
+** SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
+** Copyright (C) [dates of first publication] Silicon Graphics, Inc.
+** All Rights Reserved.
+**
+** 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 including the dates of first publication and either this
+** permission notice or a reference to http://oss.sgi.com/projects/FreeB/ 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 SILICON GRAPHICS, INC.
+** 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.
+**
+** Except as contained in this notice, the name of Silicon Graphics, Inc. shall not
+** be used in advertising or otherwise to promote the sale, use or other dealings in
+** this Software without prior written authorization from Silicon Graphics, Inc.
+*/
+/*
+** Author: Eric Veach, July 1994.
+*/
+
+#ifndef MESH_H
+#define MESH_H
+
+#include "tesselator.h"
+
+typedef struct TESSmesh TESSmesh;
+typedef struct TESSvertex TESSvertex;
+typedef struct TESSface TESSface;
+typedef struct TESShalfEdge TESShalfEdge;
+typedef struct ActiveRegion ActiveRegion;
+
+/* The mesh structure is similar in spirit, notation, and operations
+* to the "quad-edge" structure (see L. Guibas and J. Stolfi, Primitives
+* for the manipulation of general subdivisions and the computation of
+* Voronoi diagrams, ACM Transactions on Graphics, 4(2):74-123, April 1985).
+* For a simplified description, see the course notes for CS348a,
+* "Mathematical Foundations of Computer Graphics", available at the
+* Stanford bookstore (and taught during the fall quarter).
+* The implementation also borrows a tiny subset of the graph-based approach
+* use in Mantyla's Geometric Work Bench (see M. Mantyla, An Introduction
+* to Sold Modeling, Computer Science Press, Rockville, Maryland, 1988).
+*
+* The fundamental data structure is the "half-edge".  Two half-edges
+* go together to make an edge, but they point in opposite directions.
+* Each half-edge has a pointer to its mate (the "symmetric" half-edge Sym),
+* its origin vertex (Org), the face on its left side (Lface), and the
+* adjacent half-edges in the CCW direction around the origin vertex
+* (Onext) and around the left face (Lnext).  There is also a "next"
+* pointer for the global edge list (see below).
+*
+* The notation used for mesh navigation:
+*  Sym   = the mate of a half-edge (same edge, but opposite direction)
+*  Onext = edge CCW around origin vertex (keep same origin)
+*  Dnext = edge CCW around destination vertex (keep same dest)
+*  Lnext = edge CCW around left face (dest becomes new origin)
+*  Rnext = edge CCW around right face (origin becomes new dest)
+*
+* "prev" means to substitute CW for CCW in the definitions above.
+*
+* The mesh keeps global lists of all vertices, faces, and edges,
+* stored as doubly-linked circular lists with a dummy header node.
+* The mesh stores pointers to these dummy headers (vHead, fHead, eHead).
+*
+* The circular edge list is special; since half-edges always occur
+* in pairs (e and e->Sym), each half-edge stores a pointer in only
+* one direction.  Starting at eHead and following the e->next pointers
+* will visit each *edge* once (ie. e or e->Sym, but not both).
+* e->Sym stores a pointer in the opposite direction, thus it is
+* always true that e->Sym->next->Sym->next == e.
+*
+* Each vertex has a pointer to next and previous vertices in the
+* circular list, and a pointer to a half-edge with this vertex as
+* the origin (NULL if this is the dummy header).  There is also a
+* field "data" for client data.
+*
+* Each face has a pointer to the next and previous faces in the
+* circular list, and a pointer to a half-edge with this face as
+* the left face (NULL if this is the dummy header).  There is also
+* a field "data" for client data.
+*
+* Note that what we call a "face" is really a loop; faces may consist
+* of more than one loop (ie. not simply connected), but there is no
+* record of this in the data structure.  The mesh may consist of
+* several disconnected regions, so it may not be possible to visit
+* the entire mesh by starting at a half-edge and traversing the edge
+* structure.
+*
+* The mesh does NOT support isolated vertices; a vertex is deleted along
+* with its last edge.  Similarly when two faces are merged, one of the
+* faces is deleted (see tessMeshDelete below).  For mesh operations,
+* all face (loop) and vertex pointers must not be NULL.  However, once
+* mesh manipulation is finished, TESSmeshZapFace can be used to delete
+* faces of the mesh, one at a time.  All external faces can be "zapped"
+* before the mesh is returned to the client; then a NULL face indicates
+* a region which is not part of the output polygon.
+*/
+
+struct TESSvertex {
+	TESSvertex *next;      /* next vertex (never NULL) */
+	TESSvertex *prev;      /* previous vertex (never NULL) */
+	TESShalfEdge *anEdge;    /* a half-edge with this origin */
+
+	/* Internal data (keep hidden) */
+	TESSreal coords[3];  /* vertex location in 3D */
+	TESSreal s, t;       /* projection onto the sweep plane */
+	int pqHandle;   /* to allow deletion from priority queue */
+	TESSindex n;			/* to allow identify unique vertices */
+	TESSindex idx;			/* to allow map result to original verts */
+};
+
+struct TESSface {
+	TESSface *next;      /* next face (never NULL) */
+	TESSface *prev;      /* previous face (never NULL) */
+	TESShalfEdge *anEdge;    /* a half edge with this left face */
+
+	/* Internal data (keep hidden) */
+	TESSface *trail;     /* "stack" for conversion to strips */
+	TESSindex n;		/* to allow identiy unique faces */
+	char marked;     /* flag for conversion to strips */
+	char inside;     /* this face is in the polygon interior */
+};
+
+struct TESShalfEdge {
+	TESShalfEdge *next;      /* doubly-linked list (prev==Sym->next) */
+	TESShalfEdge *Sym;       /* same edge, opposite direction */
+	TESShalfEdge *Onext;     /* next edge CCW around origin */
+	TESShalfEdge *Lnext;     /* next edge CCW around left face */
+	TESSvertex *Org;       /* origin vertex (Overtex too long) */
+	TESSface *Lface;     /* left face */
+
+	/* Internal data (keep hidden) */
+	ActiveRegion *activeRegion;  /* a region with this upper edge (sweep.c) */
+	int winding;    /* change in winding number when crossing
+						  from the right face to the left face */
+};
+
+#define Rface   Sym->Lface
+#define Dst Sym->Org
+
+#define Oprev   Sym->Lnext
+#define Lprev   Onext->Sym
+#define Dprev   Lnext->Sym
+#define Rprev   Sym->Onext
+#define Dnext   Rprev->Sym  /* 3 pointers */
+#define Rnext   Oprev->Sym  /* 3 pointers */
+
+
+struct TESSmesh {
+	TESSvertex vHead;      /* dummy header for vertex list */
+	TESSface fHead;      /* dummy header for face list */
+	TESShalfEdge eHead;      /* dummy header for edge list */
+	TESShalfEdge eHeadSym;   /* and its symmetric counterpart */
+
+	struct BucketAlloc* edgeBucket;
+	struct BucketAlloc* vertexBucket;
+	struct BucketAlloc* faceBucket;
+};
+
+/* The mesh operations below have three motivations: completeness,
+* convenience, and efficiency.  The basic mesh operations are MakeEdge,
+* Splice, and Delete.  All the other edge operations can be implemented
+* in terms of these.  The other operations are provided for convenience
+* and/or efficiency.
+*
+* When a face is split or a vertex is added, they are inserted into the
+* global list *before* the existing vertex or face (ie. e->Org or e->Lface).
+* This makes it easier to process all vertices or faces in the global lists
+* without worrying about processing the same data twice.  As a convenience,
+* when a face is split, the "inside" flag is copied from the old face.
+* Other internal data (v->data, v->activeRegion, f->data, f->marked,
+* f->trail, e->winding) is set to zero.
+*
+* ********************** Basic Edge Operations **************************
+*
+* tessMeshMakeEdge( mesh ) creates one edge, two vertices, and a loop.
+* The loop (face) consists of the two new half-edges.
+*
+* tessMeshSplice( eOrg, eDst ) is the basic operation for changing the
+* mesh connectivity and topology.  It changes the mesh so that
+*  eOrg->Onext <- OLD( eDst->Onext )
+*  eDst->Onext <- OLD( eOrg->Onext )
+* where OLD(...) means the value before the meshSplice operation.
+*
+* This can have two effects on the vertex structure:
+*  - if eOrg->Org != eDst->Org, the two vertices are merged together
+*  - if eOrg->Org == eDst->Org, the origin is split into two vertices
+* In both cases, eDst->Org is changed and eOrg->Org is untouched.
+*
+* Similarly (and independently) for the face structure,
+*  - if eOrg->Lface == eDst->Lface, one loop is split into two
+*  - if eOrg->Lface != eDst->Lface, two distinct loops are joined into one
+* In both cases, eDst->Lface is changed and eOrg->Lface is unaffected.
+*
+* tessMeshDelete( eDel ) removes the edge eDel.  There are several cases:
+* if (eDel->Lface != eDel->Rface), we join two loops into one; the loop
+* eDel->Lface is deleted.  Otherwise, we are splitting one loop into two;
+* the newly created loop will contain eDel->Dst.  If the deletion of eDel
+* would create isolated vertices, those are deleted as well.
+*
+* ********************** Other Edge Operations **************************
+*
+* tessMeshAddEdgeVertex( eOrg ) creates a new edge eNew such that
+* eNew == eOrg->Lnext, and eNew->Dst is a newly created vertex.
+* eOrg and eNew will have the same left face.
+*
+* tessMeshSplitEdge( eOrg ) splits eOrg into two edges eOrg and eNew,
+* such that eNew == eOrg->Lnext.  The new vertex is eOrg->Dst == eNew->Org.
+* eOrg and eNew will have the same left face.
+*
+* tessMeshConnect( eOrg, eDst ) creates a new edge from eOrg->Dst
+* to eDst->Org, and returns the corresponding half-edge eNew.
+* If eOrg->Lface == eDst->Lface, this splits one loop into two,
+* and the newly created loop is eNew->Lface.  Otherwise, two disjoint
+* loops are merged into one, and the loop eDst->Lface is destroyed.
+*
+* ************************ Other Operations *****************************
+*
+* tessMeshNewMesh() creates a new mesh with no edges, no vertices,
+* and no loops (what we usually call a "face").
+*
+* tessMeshUnion( mesh1, mesh2 ) forms the union of all structures in
+* both meshes, and returns the new mesh (the old meshes are destroyed).
+*
+* tessMeshDeleteMesh( mesh ) will free all storage for any valid mesh.
+*
+* tessMeshZapFace( fZap ) destroys a face and removes it from the
+* global face list.  All edges of fZap will have a NULL pointer as their
+* left face.  Any edges which also have a NULL pointer as their right face
+* are deleted entirely (along with any isolated vertices this produces).
+* An entire mesh can be deleted by zapping its faces, one at a time,
+* in any order.  Zapped faces cannot be used in further mesh operations!
+*
+* tessMeshCheckMesh( mesh ) checks a mesh for self-consistency.
+*/
+
+TESShalfEdge *tessMeshMakeEdge( TESSmesh *mesh );
+int tessMeshSplice( TESSmesh *mesh, TESShalfEdge *eOrg, TESShalfEdge *eDst );
+int tessMeshDelete( TESSmesh *mesh, TESShalfEdge *eDel );
+
+TESShalfEdge *tessMeshAddEdgeVertex( TESSmesh *mesh, TESShalfEdge *eOrg );
+TESShalfEdge *tessMeshSplitEdge( TESSmesh *mesh, TESShalfEdge *eOrg );
+TESShalfEdge *tessMeshConnect( TESSmesh *mesh, TESShalfEdge *eOrg, TESShalfEdge *eDst );
+
+TESSmesh *tessMeshNewMesh( TESSalloc* alloc );
+TESSmesh *tessMeshUnion( TESSalloc* alloc, TESSmesh *mesh1, TESSmesh *mesh2 );
+int tessMeshMergeConvexFaces( TESSmesh *mesh, int maxVertsPerFace );
+void tessMeshDeleteMesh( TESSalloc* alloc, TESSmesh *mesh );
+void tessMeshZapFace( TESSmesh *mesh, TESSface *fZap );
+
+#ifdef NDEBUG
+#define tessMeshCheckMesh( mesh )
+#else
+void tessMeshCheckMesh( TESSmesh *mesh );
+#endif
+
+#endif

+ 514 - 0
polygon.mod/earcut/test/comparison/libtess2/priorityq.c

@@ -0,0 +1,514 @@
+/*
+** SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
+** Copyright (C) [dates of first publication] Silicon Graphics, Inc.
+** All Rights Reserved.
+**
+** 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 including the dates of first publication and either this
+** permission notice or a reference to http://oss.sgi.com/projects/FreeB/ 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 SILICON GRAPHICS, INC.
+** 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.
+**
+** Except as contained in this notice, the name of Silicon Graphics, Inc. shall not
+** be used in advertising or otherwise to promote the sale, use or other dealings in
+** this Software without prior written authorization from Silicon Graphics, Inc.
+*/
+/*
+** Author: Eric Veach, July 1994.
+*/
+
+//#include "tesos.h"
+#include <stddef.h>
+#include <assert.h>
+#include "tesselator.h"
+#include "priorityq.h"
+
+
+#define INIT_SIZE	32
+
+#define TRUE 1
+#define FALSE 0
+
+#ifdef FOR_TRITE_TEST_PROGRAM
+#define LEQ(x,y)	(*pq->leq)(x,y)
+#else
+/* Violates modularity, but a little faster */
+#include "geom.h"
+#define LEQ(x,y)	VertLeq((TESSvertex *)x, (TESSvertex *)y)
+#endif
+
+
+/* Include all the code for the regular heap-based queue here. */
+
+/* The basic operations are insertion of a new key (pqInsert),
+* and examination/extraction of a key whose value is minimum
+* (pqMinimum/pqExtractMin).  Deletion is also allowed (pqDelete);
+* for this purpose pqInsert returns a "handle" which is supplied
+* as the argument.
+*
+* An initial heap may be created efficiently by calling pqInsert
+* repeatedly, then calling pqInit.  In any case pqInit must be called
+* before any operations other than pqInsert are used.
+*
+* If the heap is empty, pqMinimum/pqExtractMin will return a NULL key.
+* This may also be tested with pqIsEmpty.
+*/
+
+
+/* Since we support deletion the data structure is a little more
+* complicated than an ordinary heap.  "nodes" is the heap itself;
+* active nodes are stored in the range 1..pq->size.  When the
+* heap exceeds its allocated size (pq->max), its size doubles.
+* The children of node i are nodes 2i and 2i+1.
+*
+* Each node stores an index into an array "handles".  Each handle
+* stores a key, plus a pointer back to the node which currently
+* represents that key (ie. nodes[handles[i].node].handle == i).
+*/
+
+
+#define pqHeapMinimum(pq)	((pq)->handles[(pq)->nodes[1].handle].key)
+#define pqHeapIsEmpty(pq)	((pq)->size == 0)
+
+
+
+/* really pqHeapNewPriorityQHeap */
+PriorityQHeap *pqHeapNewPriorityQ( TESSalloc* alloc, int size, int (*leq)(PQkey key1, PQkey key2) )
+{
+	PriorityQHeap *pq = (PriorityQHeap *)alloc->memalloc( alloc->userData, sizeof( PriorityQHeap ));
+	if (pq == NULL) return NULL;
+
+	pq->size = 0;
+	pq->max = size;
+	pq->nodes = (PQnode *)alloc->memalloc( alloc->userData, (size + 1) * sizeof(pq->nodes[0]) );
+	if (pq->nodes == NULL) {
+		alloc->memfree( alloc->userData, pq );
+		return NULL;
+	}
+
+	pq->handles = (PQhandleElem *)alloc->memalloc( alloc->userData, (size + 1) * sizeof(pq->handles[0]) );
+	if (pq->handles == NULL) {
+		alloc->memfree( alloc->userData, pq->nodes );
+		alloc->memfree( alloc->userData, pq );
+		return NULL;
+	}
+
+	pq->initialized = FALSE;
+	pq->freeList = 0;
+	pq->leq = leq;
+
+	pq->nodes[1].handle = 1;	/* so that Minimum() returns NULL */
+	pq->handles[1].key = NULL;
+	return pq;
+}
+
+/* really pqHeapDeletePriorityQHeap */
+void pqHeapDeletePriorityQ( TESSalloc* alloc, PriorityQHeap *pq )
+{
+	alloc->memfree( alloc->userData, pq->handles );
+	alloc->memfree( alloc->userData, pq->nodes );
+	alloc->memfree( alloc->userData, pq );
+}
+
+
+static void FloatDown( PriorityQHeap *pq, int curr )
+{
+	PQnode *n = pq->nodes;
+	PQhandleElem *h = pq->handles;
+	PQhandle hCurr, hChild;
+	int child;
+
+	hCurr = n[curr].handle;
+	for( ;; ) {
+		child = curr << 1;
+		if( child < pq->size && LEQ( h[n[child+1].handle].key,
+			h[n[child].handle].key )) {
+				++child;
+		}
+
+		assert(child <= pq->max);
+
+		hChild = n[child].handle;
+		if( child > pq->size || LEQ( h[hCurr].key, h[hChild].key )) {
+			n[curr].handle = hCurr;
+			h[hCurr].node = curr;
+			break;
+		}
+		n[curr].handle = hChild;
+		h[hChild].node = curr;
+		curr = child;
+	}
+}
+
+
+static void FloatUp( PriorityQHeap *pq, int curr )
+{
+	PQnode *n = pq->nodes;
+	PQhandleElem *h = pq->handles;
+	PQhandle hCurr, hParent;
+	int parent;
+
+	hCurr = n[curr].handle;
+	for( ;; ) {
+		parent = curr >> 1;
+		hParent = n[parent].handle;
+		if( parent == 0 || LEQ( h[hParent].key, h[hCurr].key )) {
+			n[curr].handle = hCurr;
+			h[hCurr].node = curr;
+			break;
+		}
+		n[curr].handle = hParent;
+		h[hParent].node = curr;
+		curr = parent;
+	}
+}
+
+/* really pqHeapInit */
+void pqHeapInit( PriorityQHeap *pq )
+{
+	int i;
+
+	/* This method of building a heap is O(n), rather than O(n lg n). */
+
+	for( i = pq->size; i >= 1; --i ) {
+		FloatDown( pq, i );
+	}
+	pq->initialized = TRUE;
+}
+
+/* really pqHeapInsert */
+/* returns INV_HANDLE iff out of memory */
+PQhandle pqHeapInsert( TESSalloc* alloc, PriorityQHeap *pq, PQkey keyNew )
+{
+	int curr;
+	PQhandle free;
+
+	curr = ++ pq->size;
+	if( (curr*2) > pq->max ) {
+		if (!alloc->memrealloc)
+		{
+			return INV_HANDLE;
+		}
+		else
+		{
+			PQnode *saveNodes= pq->nodes;
+			PQhandleElem *saveHandles= pq->handles;
+
+			// If the heap overflows, double its size.
+			pq->max <<= 1;
+			pq->nodes = (PQnode *)alloc->memrealloc( alloc->userData, pq->nodes,
+				(size_t)((pq->max + 1) * sizeof( pq->nodes[0] )));
+			if (pq->nodes == NULL) {
+				pq->nodes = saveNodes;	// restore ptr to free upon return
+				return INV_HANDLE;
+			}
+			pq->handles = (PQhandleElem *)alloc->memrealloc( alloc->userData, pq->handles,
+				(size_t) ((pq->max + 1) * sizeof( pq->handles[0] )));
+			if (pq->handles == NULL) {
+				pq->handles = saveHandles; // restore ptr to free upon return
+				return INV_HANDLE;
+			}
+		}
+	}
+
+	if( pq->freeList == 0 ) {
+		free = curr;
+	} else {
+		free = pq->freeList;
+		pq->freeList = pq->handles[free].node;
+	}
+
+	pq->nodes[curr].handle = free;
+	pq->handles[free].node = curr;
+	pq->handles[free].key = keyNew;
+
+	if( pq->initialized ) {
+		FloatUp( pq, curr );
+	}
+	assert(free != INV_HANDLE);
+	return free;
+}
+
+/* really pqHeapExtractMin */
+PQkey pqHeapExtractMin( PriorityQHeap *pq )
+{
+	PQnode *n = pq->nodes;
+	PQhandleElem *h = pq->handles;
+	PQhandle hMin = n[1].handle;
+	PQkey min = h[hMin].key;
+
+	if( pq->size > 0 ) {
+		n[1].handle = n[pq->size].handle;
+		h[n[1].handle].node = 1;
+
+		h[hMin].key = NULL;
+		h[hMin].node = pq->freeList;
+		pq->freeList = hMin;
+
+		if( -- pq->size > 0 ) {
+			FloatDown( pq, 1 );
+		}
+	}
+	return min;
+}
+
+/* really pqHeapDelete */
+void pqHeapDelete( PriorityQHeap *pq, PQhandle hCurr )
+{
+	PQnode *n = pq->nodes;
+	PQhandleElem *h = pq->handles;
+	int curr;
+
+	assert( hCurr >= 1 && hCurr <= pq->max && h[hCurr].key != NULL );
+
+	curr = h[hCurr].node;
+	n[curr].handle = n[pq->size].handle;
+	h[n[curr].handle].node = curr;
+
+	if( curr <= -- pq->size ) {
+		if( curr <= 1 || LEQ( h[n[curr>>1].handle].key, h[n[curr].handle].key )) {
+			FloatDown( pq, curr );
+		} else {
+			FloatUp( pq, curr );
+		}
+	}
+	h[hCurr].key = NULL;
+	h[hCurr].node = pq->freeList;
+	pq->freeList = hCurr;
+}
+
+
+
+/* Now redefine all the function names to map to their "Sort" versions. */
+
+/* really tessPqSortNewPriorityQ */
+PriorityQ *pqNewPriorityQ( TESSalloc* alloc, int size, int (*leq)(PQkey key1, PQkey key2) )
+{
+	PriorityQ *pq = (PriorityQ *)alloc->memalloc( alloc->userData, sizeof( PriorityQ ));
+	if (pq == NULL) return NULL;
+
+	pq->heap = pqHeapNewPriorityQ( alloc, size, leq );
+	if (pq->heap == NULL) {
+		alloc->memfree( alloc->userData, pq );
+		return NULL;
+	}
+
+//	pq->keys = (PQkey *)memAlloc( INIT_SIZE * sizeof(pq->keys[0]) );
+	pq->keys = (PQkey *)alloc->memalloc( alloc->userData, size * sizeof(pq->keys[0]) );
+	if (pq->keys == NULL) {
+		pqHeapDeletePriorityQ( alloc, pq->heap );
+		alloc->memfree( alloc->userData, pq );
+		return NULL;
+	}
+
+	pq->size = 0;
+	pq->max = size; //INIT_SIZE;
+	pq->initialized = FALSE;
+	pq->leq = leq;
+
+	return pq;
+}
+
+/* really tessPqSortDeletePriorityQ */
+void pqDeletePriorityQ( TESSalloc* alloc, PriorityQ *pq )
+{
+	assert(pq != NULL);
+	if (pq->heap != NULL) pqHeapDeletePriorityQ( alloc, pq->heap );
+	if (pq->order != NULL) alloc->memfree( alloc->userData, pq->order );
+	if (pq->keys != NULL) alloc->memfree( alloc->userData, pq->keys );
+	alloc->memfree( alloc->userData, pq );
+}
+
+
+#define LT(x,y)     (! LEQ(y,x))
+#define GT(x,y)     (! LEQ(x,y))
+#define Swap(a,b)   if(1){PQkey *tmp = *a; *a = *b; *b = tmp;}else
+
+/* really tessPqSortInit */
+int pqInit( TESSalloc* alloc, PriorityQ *pq )
+{
+	PQkey **p, **r, **i, **j, *piv;
+	struct { PQkey **p, **r; } Stack[50], *top = Stack;
+	unsigned int seed = 2016473283;
+
+	/* Create an array of indirect pointers to the keys, so that we
+	* the handles we have returned are still valid.
+	*/
+	/*
+	pq->order = (PQkey **)memAlloc( (size_t)
+	(pq->size * sizeof(pq->order[0])) );
+	*/
+	pq->order = (PQkey **)alloc->memalloc( alloc->userData,
+										  (size_t)((pq->size+1) * sizeof(pq->order[0])) );
+	/* the previous line is a patch to compensate for the fact that IBM */
+	/* machines return a null on a malloc of zero bytes (unlike SGI),   */
+	/* so we have to put in this defense to guard against a memory      */
+	/* fault four lines down. from [email protected].               */
+	if (pq->order == NULL) return 0;
+
+	p = pq->order;
+	r = p + pq->size - 1;
+	for( piv = pq->keys, i = p; i <= r; ++piv, ++i ) {
+		*i = piv;
+	}
+
+	/* Sort the indirect pointers in descending order,
+	* using randomized Quicksort
+	*/
+	top->p = p; top->r = r; ++top;
+	while( --top >= Stack ) {
+		p = top->p;
+		r = top->r;
+		while( r > p + 10 ) {
+			seed = seed * 1539415821 + 1;
+			i = p + seed % (r - p + 1);
+			piv = *i;
+			*i = *p;
+			*p = piv;
+			i = p - 1;
+			j = r + 1;
+			do {
+				do { ++i; } while( GT( **i, *piv ));
+				do { --j; } while( LT( **j, *piv ));
+				Swap( i, j );
+			} while( i < j );
+			Swap( i, j ); /* Undo last swap */
+			if( i - p < r - j ) {
+				top->p = j+1; top->r = r; ++top;
+				r = i-1;
+			} else {
+				top->p = p; top->r = i-1; ++top;
+				p = j+1;
+			}
+		}
+		/* Insertion sort small lists */
+		for( i = p+1; i <= r; ++i ) {
+			piv = *i;
+			for( j = i; j > p && LT( **(j-1), *piv ); --j ) {
+				*j = *(j-1);
+			}
+			*j = piv;
+		}
+	}
+	pq->max = pq->size;
+	pq->initialized = TRUE;
+	pqHeapInit( pq->heap );  /* always succeeds */
+
+#ifndef NDEBUG
+	p = pq->order;
+	r = p + pq->size - 1;
+	for( i = p; i < r; ++i ) {
+		assert( LEQ( **(i+1), **i ));
+	}
+#endif
+
+	return 1;
+}
+
+/* really tessPqSortInsert */
+/* returns INV_HANDLE iff out of memory */
+PQhandle pqInsert( TESSalloc* alloc, PriorityQ *pq, PQkey keyNew )
+{
+	int curr;
+
+	if( pq->initialized ) {
+		return pqHeapInsert( alloc, pq->heap, keyNew );
+	}
+	curr = pq->size;
+	if( ++ pq->size >= pq->max ) {
+		if (!alloc->memrealloc)
+		{
+			return INV_HANDLE;
+		}
+		else
+		{
+			PQkey *saveKey= pq->keys;
+			// If the heap overflows, double its size.
+			pq->max <<= 1;
+			pq->keys = (PQkey *)alloc->memrealloc( alloc->userData, pq->keys,
+				(size_t)(pq->max * sizeof( pq->keys[0] )));
+			if (pq->keys == NULL) {
+				pq->keys = saveKey;  // restore ptr to free upon return
+				return INV_HANDLE;
+			}
+		}
+	}
+	assert(curr != INV_HANDLE);
+	pq->keys[curr] = keyNew;
+
+	/* Negative handles index the sorted array. */
+	return -(curr+1);
+}
+
+/* really tessPqSortExtractMin */
+PQkey pqExtractMin( PriorityQ *pq )
+{
+	PQkey sortMin, heapMin;
+
+	if( pq->size == 0 ) {
+		return pqHeapExtractMin( pq->heap );
+	}
+	sortMin = *(pq->order[pq->size-1]);
+	if( ! pqHeapIsEmpty( pq->heap )) {
+		heapMin = pqHeapMinimum( pq->heap );
+		if( LEQ( heapMin, sortMin )) {
+			return pqHeapExtractMin( pq->heap );
+		}
+	}
+	do {
+		-- pq->size;
+	} while( pq->size > 0 && *(pq->order[pq->size-1]) == NULL );
+	return sortMin;
+}
+
+/* really tessPqSortMinimum */
+PQkey pqMinimum( PriorityQ *pq )
+{
+	PQkey sortMin, heapMin;
+
+	if( pq->size == 0 ) {
+		return pqHeapMinimum( pq->heap );
+	}
+	sortMin = *(pq->order[pq->size-1]);
+	if( ! pqHeapIsEmpty( pq->heap )) {
+		heapMin = pqHeapMinimum( pq->heap );
+		if( LEQ( heapMin, sortMin )) {
+			return heapMin;
+		}
+	}
+	return sortMin;
+}
+
+/* really tessPqSortIsEmpty */
+int pqIsEmpty( PriorityQ *pq )
+{
+	return (pq->size == 0) && pqHeapIsEmpty( pq->heap );
+}
+
+/* really tessPqSortDelete */
+void pqDelete( PriorityQ *pq, PQhandle curr )
+{
+	if( curr >= 0 ) {
+		pqHeapDelete( pq->heap, curr );
+		return;
+	}
+	curr = -(curr+1);
+	assert( curr < pq->max && pq->keys[curr] != NULL );
+
+	pq->keys[curr] = NULL;
+	while( pq->size > 0 && *(pq->order[pq->size-1]) == NULL ) {
+		-- pq->size;
+	}
+}

+ 104 - 0
polygon.mod/earcut/test/comparison/libtess2/priorityq.h

@@ -0,0 +1,104 @@
+/*
+** SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
+** Copyright (C) [dates of first publication] Silicon Graphics, Inc.
+** All Rights Reserved.
+**
+** 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 including the dates of first publication and either this
+** permission notice or a reference to http://oss.sgi.com/projects/FreeB/ 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 SILICON GRAPHICS, INC.
+** 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.
+**
+** Except as contained in this notice, the name of Silicon Graphics, Inc. shall not
+** be used in advertising or otherwise to promote the sale, use or other dealings in
+** this Software without prior written authorization from Silicon Graphics, Inc.
+*/
+/*
+** Author: Eric Veach, July 1994.
+*/
+
+#ifndef PRIORITYQ_H
+#define PRIORITYQ_H
+
+/* The basic operations are insertion of a new key (pqInsert),
+* and examination/extraction of a key whose value is minimum
+* (pqMinimum/pqExtractMin).  Deletion is also allowed (pqDelete);
+* for this purpose pqInsert returns a "handle" which is supplied
+* as the argument.
+*
+* An initial heap may be created efficiently by calling pqInsert
+* repeatedly, then calling pqInit.  In any case pqInit must be called
+* before any operations other than pqInsert are used.
+*
+* If the heap is empty, pqMinimum/pqExtractMin will return a NULL key.
+* This may also be tested with pqIsEmpty.
+*/
+
+/* Since we support deletion the data structure is a little more
+* complicated than an ordinary heap.  "nodes" is the heap itself;
+* active nodes are stored in the range 1..pq->size.  When the
+* heap exceeds its allocated size (pq->max), its size doubles.
+* The children of node i are nodes 2i and 2i+1.
+*
+* Each node stores an index into an array "handles".  Each handle
+* stores a key, plus a pointer back to the node which currently
+* represents that key (ie. nodes[handles[i].node].handle == i).
+*/
+
+typedef void *PQkey;
+typedef int PQhandle;
+typedef struct PriorityQHeap PriorityQHeap;
+
+#define INV_HANDLE 0x0fffffff
+
+typedef struct { PQhandle handle; } PQnode;
+typedef struct { PQkey key; PQhandle node; } PQhandleElem;
+
+struct PriorityQHeap {
+
+	PQnode *nodes;
+	PQhandleElem *handles;
+	int size, max;
+	PQhandle freeList;
+	int initialized;
+
+	int (*leq)(PQkey key1, PQkey key2);
+};
+
+typedef struct PriorityQ PriorityQ;
+
+struct PriorityQ {
+	PriorityQHeap *heap;
+
+	PQkey *keys;
+	PQkey **order;
+	PQhandle size, max;
+	int initialized;
+
+	int (*leq)(PQkey key1, PQkey key2);
+};
+
+PriorityQ *pqNewPriorityQ( TESSalloc* alloc, int size, int (*leq)(PQkey key1, PQkey key2) );
+void pqDeletePriorityQ( TESSalloc* alloc, PriorityQ *pq );
+
+int pqInit( TESSalloc* alloc, PriorityQ *pq );
+PQhandle pqInsert( TESSalloc* alloc, PriorityQ *pq, PQkey key );
+PQkey pqExtractMin( PriorityQ *pq );
+void pqDelete( PriorityQ *pq, PQhandle handle );
+
+PQkey pqMinimum( PriorityQ *pq );
+int pqIsEmpty( PriorityQ *pq );
+
+#endif

+ 1326 - 0
polygon.mod/earcut/test/comparison/libtess2/sweep.c

@@ -0,0 +1,1326 @@
+/*
+** SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
+** Copyright (C) [dates of first publication] Silicon Graphics, Inc.
+** All Rights Reserved.
+**
+** 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 including the dates of first publication and either this
+** permission notice or a reference to http://oss.sgi.com/projects/FreeB/ 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 SILICON GRAPHICS, INC.
+** 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.
+**
+** Except as contained in this notice, the name of Silicon Graphics, Inc. shall not
+** be used in advertising or otherwise to promote the sale, use or other dealings in
+** this Software without prior written authorization from Silicon Graphics, Inc.
+*/
+/*
+** Author: Eric Veach, July 1994.
+*/
+
+#include <assert.h>
+#include <stddef.h>
+#include <setjmp.h>		/* longjmp */
+
+#include "mesh.h"
+#include "geom.h"
+#include "tess.h"
+#include "dict.h"
+#include "priorityq.h"
+#include "bucketalloc.h"
+#include "sweep.h"
+
+#define TRUE 1
+#define FALSE 0
+
+#ifdef FOR_TRITE_TEST_PROGRAM
+extern void DebugEvent( TESStesselator *tess );
+#else
+#define DebugEvent( tess )
+#endif
+
+/*
+* Invariants for the Edge Dictionary.
+* - each pair of adjacent edges e2=Succ(e1) satisfies EdgeLeq(e1,e2)
+*   at any valid location of the sweep event
+* - if EdgeLeq(e2,e1) as well (at any valid sweep event), then e1 and e2
+*   share a common endpoint
+* - for each e, e->Dst has been processed, but not e->Org
+* - each edge e satisfies VertLeq(e->Dst,event) && VertLeq(event,e->Org)
+*   where "event" is the current sweep line event.
+* - no edge e has zero length
+*
+* Invariants for the Mesh (the processed portion).
+* - the portion of the mesh left of the sweep line is a planar graph,
+*   ie. there is *some* way to embed it in the plane
+* - no processed edge has zero length
+* - no two processed vertices have identical coordinates
+* - each "inside" region is monotone, ie. can be broken into two chains
+*   of monotonically increasing vertices according to VertLeq(v1,v2)
+*   - a non-invariant: these chains may intersect (very slightly)
+*
+* Invariants for the Sweep.
+* - if none of the edges incident to the event vertex have an activeRegion
+*   (ie. none of these edges are in the edge dictionary), then the vertex
+*   has only right-going edges.
+* - if an edge is marked "fixUpperEdge" (it is a temporary edge introduced
+*   by ConnectRightVertex), then it is the only right-going edge from
+*   its associated vertex.  (This says that these edges exist only
+*   when it is necessary.)
+*/
+
+#define MAX(x,y)	((x) >= (y) ? (x) : (y))
+#define MIN(x,y)	((x) <= (y) ? (x) : (y))
+
+/* When we merge two edges into one, we need to compute the combined
+* winding of the new edge.
+*/
+#define AddWinding(eDst,eSrc)	(eDst->winding += eSrc->winding, \
+	eDst->Sym->winding += eSrc->Sym->winding)
+
+static void SweepEvent( TESStesselator *tess, TESSvertex *vEvent );
+static void WalkDirtyRegions( TESStesselator *tess, ActiveRegion *regUp );
+static int CheckForRightSplice( TESStesselator *tess, ActiveRegion *regUp );
+
+static int EdgeLeq( TESStesselator *tess, ActiveRegion *reg1, ActiveRegion *reg2 )
+/*
+* Both edges must be directed from right to left (this is the canonical
+* direction for the upper edge of each region).
+*
+* The strategy is to evaluate a "t" value for each edge at the
+* current sweep line position, given by tess->event.  The calculations
+* are designed to be very stable, but of course they are not perfect.
+*
+* Special case: if both edge destinations are at the sweep event,
+* we sort the edges by slope (they would otherwise compare equally).
+*/
+{
+	TESSvertex *event = tess->event;
+	TESShalfEdge *e1, *e2;
+	TESSreal t1, t2;
+
+	e1 = reg1->eUp;
+	e2 = reg2->eUp;
+
+	if( e1->Dst == event ) {
+		if( e2->Dst == event ) {
+			/* Two edges right of the sweep line which meet at the sweep event.
+			* Sort them by slope.
+			*/
+			if( VertLeq( e1->Org, e2->Org )) {
+				return EdgeSign( e2->Dst, e1->Org, e2->Org ) <= 0;
+			}
+			return EdgeSign( e1->Dst, e2->Org, e1->Org ) >= 0;
+		}
+		return EdgeSign( e2->Dst, event, e2->Org ) <= 0;
+	}
+	if( e2->Dst == event ) {
+		return EdgeSign( e1->Dst, event, e1->Org ) >= 0;
+	}
+
+	/* General case - compute signed distance *from* e1, e2 to event */
+	t1 = EdgeEval( e1->Dst, event, e1->Org );
+	t2 = EdgeEval( e2->Dst, event, e2->Org );
+	return (t1 >= t2);
+}
+
+
+static void DeleteRegion( TESStesselator *tess, ActiveRegion *reg )
+{
+	if( reg->fixUpperEdge ) {
+		/* It was created with zero winding number, so it better be
+		* deleted with zero winding number (ie. it better not get merged
+		* with a real edge).
+		*/
+		assert( reg->eUp->winding == 0 );
+	}
+	reg->eUp->activeRegion = NULL;
+	dictDelete( tess->dict, reg->nodeUp );
+	bucketFree( tess->regionPool, reg );
+}
+
+
+static int FixUpperEdge( TESStesselator *tess, ActiveRegion *reg, TESShalfEdge *newEdge )
+/*
+* Replace an upper edge which needs fixing (see ConnectRightVertex).
+*/
+{
+	assert( reg->fixUpperEdge );
+	if ( !tessMeshDelete( tess->mesh, reg->eUp ) ) return 0;
+	reg->fixUpperEdge = FALSE;
+	reg->eUp = newEdge;
+	newEdge->activeRegion = reg;
+
+	return 1;
+}
+
+static ActiveRegion *TopLeftRegion( TESStesselator *tess, ActiveRegion *reg )
+{
+	TESSvertex *org = reg->eUp->Org;
+	TESShalfEdge *e;
+
+	/* Find the region above the uppermost edge with the same origin */
+	do {
+		reg = RegionAbove( reg );
+	} while( reg->eUp->Org == org );
+
+	/* If the edge above was a temporary edge introduced by ConnectRightVertex,
+	* now is the time to fix it.
+	*/
+	if( reg->fixUpperEdge ) {
+		e = tessMeshConnect( tess->mesh, RegionBelow(reg)->eUp->Sym, reg->eUp->Lnext );
+		if (e == NULL) return NULL;
+		if ( !FixUpperEdge( tess, reg, e ) ) return NULL;
+		reg = RegionAbove( reg );
+	}
+	return reg;
+}
+
+static ActiveRegion *TopRightRegion( ActiveRegion *reg )
+{
+	TESSvertex *dst = reg->eUp->Dst;
+
+	/* Find the region above the uppermost edge with the same destination */
+	do {
+		reg = RegionAbove( reg );
+	} while( reg->eUp->Dst == dst );
+	return reg;
+}
+
+static ActiveRegion *AddRegionBelow( TESStesselator *tess,
+									ActiveRegion *regAbove,
+									TESShalfEdge *eNewUp )
+/*
+* Add a new active region to the sweep line, *somewhere* below "regAbove"
+* (according to where the new edge belongs in the sweep-line dictionary).
+* The upper edge of the new region will be "eNewUp".
+* Winding number and "inside" flag are not updated.
+*/
+{
+	ActiveRegion *regNew = (ActiveRegion *)bucketAlloc( tess->regionPool );
+	if (regNew == NULL) longjmp(tess->env,1);
+
+	regNew->eUp = eNewUp;
+	regNew->nodeUp = dictInsertBefore( tess->dict, regAbove->nodeUp, regNew );
+	if (regNew->nodeUp == NULL) longjmp(tess->env,1);
+	regNew->fixUpperEdge = FALSE;
+	regNew->sentinel = FALSE;
+	regNew->dirty = FALSE;
+
+	eNewUp->activeRegion = regNew;
+	return regNew;
+}
+
+static int IsWindingInside( TESStesselator *tess, int n )
+{
+	switch( tess->windingRule ) {
+		case TESS_WINDING_ODD:
+			return (n & 1);
+		case TESS_WINDING_NONZERO:
+			return (n != 0);
+		case TESS_WINDING_POSITIVE:
+			return (n > 0);
+		case TESS_WINDING_NEGATIVE:
+			return (n < 0);
+		case TESS_WINDING_ABS_GEQ_TWO:
+			return (n >= 2) || (n <= -2);
+	}
+	/*LINTED*/
+	assert( FALSE );
+	/*NOTREACHED*/
+
+	return( FALSE );
+}
+
+
+static void ComputeWinding( TESStesselator *tess, ActiveRegion *reg )
+{
+	reg->windingNumber = RegionAbove(reg)->windingNumber + reg->eUp->winding;
+	reg->inside = IsWindingInside( tess, reg->windingNumber );
+}
+
+
+static void FinishRegion( TESStesselator *tess, ActiveRegion *reg )
+/*
+* Delete a region from the sweep line.  This happens when the upper
+* and lower chains of a region meet (at a vertex on the sweep line).
+* The "inside" flag is copied to the appropriate mesh face (we could
+* not do this before -- since the structure of the mesh is always
+* changing, this face may not have even existed until now).
+*/
+{
+	TESShalfEdge *e = reg->eUp;
+	TESSface *f = e->Lface;
+
+	f->inside = reg->inside;
+	f->anEdge = e;   /* optimization for tessMeshTessellateMonoRegion() */
+	DeleteRegion( tess, reg );
+}
+
+
+static TESShalfEdge *FinishLeftRegions( TESStesselator *tess,
+									  ActiveRegion *regFirst, ActiveRegion *regLast )
+/*
+* We are given a vertex with one or more left-going edges.  All affected
+* edges should be in the edge dictionary.  Starting at regFirst->eUp,
+* we walk down deleting all regions where both edges have the same
+* origin vOrg.  At the same time we copy the "inside" flag from the
+* active region to the face, since at this point each face will belong
+* to at most one region (this was not necessarily true until this point
+* in the sweep).  The walk stops at the region above regLast; if regLast
+* is NULL we walk as far as possible.  At the same time we relink the
+* mesh if necessary, so that the ordering of edges around vOrg is the
+* same as in the dictionary.
+*/
+{
+	ActiveRegion *reg, *regPrev;
+	TESShalfEdge *e, *ePrev;
+
+	regPrev = regFirst;
+	ePrev = regFirst->eUp;
+	while( regPrev != regLast ) {
+		regPrev->fixUpperEdge = FALSE;	/* placement was OK */
+		reg = RegionBelow( regPrev );
+		e = reg->eUp;
+		if( e->Org != ePrev->Org ) {
+			if( ! reg->fixUpperEdge ) {
+				/* Remove the last left-going edge.  Even though there are no further
+				* edges in the dictionary with this origin, there may be further
+				* such edges in the mesh (if we are adding left edges to a vertex
+				* that has already been processed).  Thus it is important to call
+				* FinishRegion rather than just DeleteRegion.
+				*/
+				FinishRegion( tess, regPrev );
+				break;
+			}
+			/* If the edge below was a temporary edge introduced by
+			* ConnectRightVertex, now is the time to fix it.
+			*/
+			e = tessMeshConnect( tess->mesh, ePrev->Lprev, e->Sym );
+			if (e == NULL) longjmp(tess->env,1);
+			if ( !FixUpperEdge( tess, reg, e ) ) longjmp(tess->env,1);
+		}
+
+		/* Relink edges so that ePrev->Onext == e */
+		if( ePrev->Onext != e ) {
+			if ( !tessMeshSplice( tess->mesh, e->Oprev, e ) ) longjmp(tess->env,1);
+			if ( !tessMeshSplice( tess->mesh, ePrev, e ) ) longjmp(tess->env,1);
+		}
+		FinishRegion( tess, regPrev );	/* may change reg->eUp */
+		ePrev = reg->eUp;
+		regPrev = reg;
+	}
+	return ePrev;
+}
+
+
+static void AddRightEdges( TESStesselator *tess, ActiveRegion *regUp,
+						  TESShalfEdge *eFirst, TESShalfEdge *eLast, TESShalfEdge *eTopLeft,
+						  int cleanUp )
+/*
+* Purpose: insert right-going edges into the edge dictionary, and update
+* winding numbers and mesh connectivity appropriately.  All right-going
+* edges share a common origin vOrg.  Edges are inserted CCW starting at
+* eFirst; the last edge inserted is eLast->Oprev.  If vOrg has any
+* left-going edges already processed, then eTopLeft must be the edge
+* such that an imaginary upward vertical segment from vOrg would be
+* contained between eTopLeft->Oprev and eTopLeft; otherwise eTopLeft
+* should be NULL.
+*/
+{
+	ActiveRegion *reg, *regPrev;
+	TESShalfEdge *e, *ePrev;
+	int firstTime = TRUE;
+
+	/* Insert the new right-going edges in the dictionary */
+	e = eFirst;
+	do {
+		assert( VertLeq( e->Org, e->Dst ));
+		AddRegionBelow( tess, regUp, e->Sym );
+		e = e->Onext;
+	} while ( e != eLast );
+
+	/* Walk *all* right-going edges from e->Org, in the dictionary order,
+	* updating the winding numbers of each region, and re-linking the mesh
+	* edges to match the dictionary ordering (if necessary).
+	*/
+	if( eTopLeft == NULL ) {
+		eTopLeft = RegionBelow( regUp )->eUp->Rprev;
+	}
+	regPrev = regUp;
+	ePrev = eTopLeft;
+	for( ;; ) {
+		reg = RegionBelow( regPrev );
+		e = reg->eUp->Sym;
+		if( e->Org != ePrev->Org ) break;
+
+		if( e->Onext != ePrev ) {
+			/* Unlink e from its current position, and relink below ePrev */
+			if ( !tessMeshSplice( tess->mesh, e->Oprev, e ) ) longjmp(tess->env,1);
+			if ( !tessMeshSplice( tess->mesh, ePrev->Oprev, e ) ) longjmp(tess->env,1);
+		}
+		/* Compute the winding number and "inside" flag for the new regions */
+		reg->windingNumber = regPrev->windingNumber - e->winding;
+		reg->inside = IsWindingInside( tess, reg->windingNumber );
+
+		/* Check for two outgoing edges with same slope -- process these
+		* before any intersection tests (see example in tessComputeInterior).
+		*/
+		regPrev->dirty = TRUE;
+		if( ! firstTime && CheckForRightSplice( tess, regPrev )) {
+			AddWinding( e, ePrev );
+			DeleteRegion( tess, regPrev );
+			if ( !tessMeshDelete( tess->mesh, ePrev ) ) longjmp(tess->env,1);
+		}
+		firstTime = FALSE;
+		regPrev = reg;
+		ePrev = e;
+	}
+	regPrev->dirty = TRUE;
+	assert( regPrev->windingNumber - e->winding == reg->windingNumber );
+
+	if( cleanUp ) {
+		/* Check for intersections between newly adjacent edges. */
+		WalkDirtyRegions( tess, regPrev );
+	}
+}
+
+
+static void SpliceMergeVertices( TESStesselator *tess, TESShalfEdge *e1,
+								TESShalfEdge *e2 )
+/*
+* Two vertices with idential coordinates are combined into one.
+* e1->Org is kept, while e2->Org is discarded.
+*/
+{
+	if ( !tessMeshSplice( tess->mesh, e1, e2 ) ) longjmp(tess->env,1);
+}
+
+static void VertexWeights( TESSvertex *isect, TESSvertex *org, TESSvertex *dst,
+						  TESSreal *weights )
+/*
+* Find some weights which describe how the intersection vertex is
+* a linear combination of "org" and "dest".  Each of the two edges
+* which generated "isect" is allocated 50% of the weight; each edge
+* splits the weight between its org and dst according to the
+* relative distance to "isect".
+*/
+{
+	TESSreal t1 = VertL1dist( org, isect );
+	TESSreal t2 = VertL1dist( dst, isect );
+
+	weights[0] = (TESSreal)0.5 * t2 / (t1 + t2);
+	weights[1] = (TESSreal)0.5 * t1 / (t1 + t2);
+	isect->coords[0] += weights[0]*org->coords[0] + weights[1]*dst->coords[0];
+	isect->coords[1] += weights[0]*org->coords[1] + weights[1]*dst->coords[1];
+	isect->coords[2] += weights[0]*org->coords[2] + weights[1]*dst->coords[2];
+}
+
+
+static void GetIntersectData( TESStesselator *tess, TESSvertex *isect,
+							 TESSvertex *orgUp, TESSvertex *dstUp,
+							 TESSvertex *orgLo, TESSvertex *dstLo )
+ /*
+ * We've computed a new intersection point, now we need a "data" pointer
+ * from the user so that we can refer to this new vertex in the
+ * rendering callbacks.
+ */
+{
+	TESSreal weights[4];
+	TESS_NOTUSED( tess );
+
+	isect->coords[0] = isect->coords[1] = isect->coords[2] = 0;
+	isect->idx = TESS_UNDEF;
+	VertexWeights( isect, orgUp, dstUp, &weights[0] );
+	VertexWeights( isect, orgLo, dstLo, &weights[2] );
+}
+
+static int CheckForRightSplice( TESStesselator *tess, ActiveRegion *regUp )
+/*
+* Check the upper and lower edge of "regUp", to make sure that the
+* eUp->Org is above eLo, or eLo->Org is below eUp (depending on which
+* origin is leftmost).
+*
+* The main purpose is to splice right-going edges with the same
+* dest vertex and nearly identical slopes (ie. we can't distinguish
+* the slopes numerically).  However the splicing can also help us
+* to recover from numerical errors.  For example, suppose at one
+* point we checked eUp and eLo, and decided that eUp->Org is barely
+* above eLo.  Then later, we split eLo into two edges (eg. from
+* a splice operation like this one).  This can change the result of
+* our test so that now eUp->Org is incident to eLo, or barely below it.
+* We must correct this condition to maintain the dictionary invariants.
+*
+* One possibility is to check these edges for intersection again
+* (ie. CheckForIntersect).  This is what we do if possible.  However
+* CheckForIntersect requires that tess->event lies between eUp and eLo,
+* so that it has something to fall back on when the intersection
+* calculation gives us an unusable answer.  So, for those cases where
+* we can't check for intersection, this routine fixes the problem
+* by just splicing the offending vertex into the other edge.
+* This is a guaranteed solution, no matter how degenerate things get.
+* Basically this is a combinatorial solution to a numerical problem.
+*/
+{
+	ActiveRegion *regLo = RegionBelow(regUp);
+	TESShalfEdge *eUp = regUp->eUp;
+	TESShalfEdge *eLo = regLo->eUp;
+
+	if( VertLeq( eUp->Org, eLo->Org )) {
+		if( EdgeSign( eLo->Dst, eUp->Org, eLo->Org ) > 0 ) return FALSE;
+
+		/* eUp->Org appears to be below eLo */
+		if( ! VertEq( eUp->Org, eLo->Org )) {
+			/* Splice eUp->Org into eLo */
+			if ( tessMeshSplitEdge( tess->mesh, eLo->Sym ) == NULL) longjmp(tess->env,1);
+			if ( !tessMeshSplice( tess->mesh, eUp, eLo->Oprev ) ) longjmp(tess->env,1);
+			regUp->dirty = regLo->dirty = TRUE;
+
+		} else if( eUp->Org != eLo->Org ) {
+			/* merge the two vertices, discarding eUp->Org */
+			pqDelete( tess->pq, eUp->Org->pqHandle );
+			SpliceMergeVertices( tess, eLo->Oprev, eUp );
+		}
+	} else {
+		if( EdgeSign( eUp->Dst, eLo->Org, eUp->Org ) < 0 ) return FALSE;
+
+		/* eLo->Org appears to be above eUp, so splice eLo->Org into eUp */
+		RegionAbove(regUp)->dirty = regUp->dirty = TRUE;
+		if (tessMeshSplitEdge( tess->mesh, eUp->Sym ) == NULL) longjmp(tess->env,1);
+		if ( !tessMeshSplice( tess->mesh, eLo->Oprev, eUp ) ) longjmp(tess->env,1);
+	}
+	return TRUE;
+}
+
+static int CheckForLeftSplice( TESStesselator *tess, ActiveRegion *regUp )
+/*
+* Check the upper and lower edge of "regUp", to make sure that the
+* eUp->Dst is above eLo, or eLo->Dst is below eUp (depending on which
+* destination is rightmost).
+*
+* Theoretically, this should always be true.  However, splitting an edge
+* into two pieces can change the results of previous tests.  For example,
+* suppose at one point we checked eUp and eLo, and decided that eUp->Dst
+* is barely above eLo.  Then later, we split eLo into two edges (eg. from
+* a splice operation like this one).  This can change the result of
+* the test so that now eUp->Dst is incident to eLo, or barely below it.
+* We must correct this condition to maintain the dictionary invariants
+* (otherwise new edges might get inserted in the wrong place in the
+* dictionary, and bad stuff will happen).
+*
+* We fix the problem by just splicing the offending vertex into the
+* other edge.
+*/
+{
+	ActiveRegion *regLo = RegionBelow(regUp);
+	TESShalfEdge *eUp = regUp->eUp;
+	TESShalfEdge *eLo = regLo->eUp;
+	TESShalfEdge *e;
+
+	assert( ! VertEq( eUp->Dst, eLo->Dst ));
+
+	if( VertLeq( eUp->Dst, eLo->Dst )) {
+		if( EdgeSign( eUp->Dst, eLo->Dst, eUp->Org ) < 0 ) return FALSE;
+
+		/* eLo->Dst is above eUp, so splice eLo->Dst into eUp */
+		RegionAbove(regUp)->dirty = regUp->dirty = TRUE;
+		e = tessMeshSplitEdge( tess->mesh, eUp );
+		if (e == NULL) longjmp(tess->env,1);
+		if ( !tessMeshSplice( tess->mesh, eLo->Sym, e ) ) longjmp(tess->env,1);
+		e->Lface->inside = regUp->inside;
+	} else {
+		if( EdgeSign( eLo->Dst, eUp->Dst, eLo->Org ) > 0 ) return FALSE;
+
+		/* eUp->Dst is below eLo, so splice eUp->Dst into eLo */
+		regUp->dirty = regLo->dirty = TRUE;
+		e = tessMeshSplitEdge( tess->mesh, eLo );
+		if (e == NULL) longjmp(tess->env,1);
+		if ( !tessMeshSplice( tess->mesh, eUp->Lnext, eLo->Sym ) ) longjmp(tess->env,1);
+		e->Rface->inside = regUp->inside;
+	}
+	return TRUE;
+}
+
+
+static int CheckForIntersect( TESStesselator *tess, ActiveRegion *regUp )
+/*
+* Check the upper and lower edges of the given region to see if
+* they intersect.  If so, create the intersection and add it
+* to the data structures.
+*
+* Returns TRUE if adding the new intersection resulted in a recursive
+* call to AddRightEdges(); in this case all "dirty" regions have been
+* checked for intersections, and possibly regUp has been deleted.
+*/
+{
+	ActiveRegion *regLo = RegionBelow(regUp);
+	TESShalfEdge *eUp = regUp->eUp;
+	TESShalfEdge *eLo = regLo->eUp;
+	TESSvertex *orgUp = eUp->Org;
+	TESSvertex *orgLo = eLo->Org;
+	TESSvertex *dstUp = eUp->Dst;
+	TESSvertex *dstLo = eLo->Dst;
+	TESSreal tMinUp, tMaxLo;
+	TESSvertex isect, *orgMin;
+	TESShalfEdge *e;
+
+	assert( ! VertEq( dstLo, dstUp ));
+	assert( EdgeSign( dstUp, tess->event, orgUp ) <= 0 );
+	assert( EdgeSign( dstLo, tess->event, orgLo ) >= 0 );
+	assert( orgUp != tess->event && orgLo != tess->event );
+	assert( ! regUp->fixUpperEdge && ! regLo->fixUpperEdge );
+
+	if( orgUp == orgLo ) return FALSE;	/* right endpoints are the same */
+
+	tMinUp = MIN( orgUp->t, dstUp->t );
+	tMaxLo = MAX( orgLo->t, dstLo->t );
+	if( tMinUp > tMaxLo ) return FALSE;	/* t ranges do not overlap */
+
+	if( VertLeq( orgUp, orgLo )) {
+		if( EdgeSign( dstLo, orgUp, orgLo ) > 0 ) return FALSE;
+	} else {
+		if( EdgeSign( dstUp, orgLo, orgUp ) < 0 ) return FALSE;
+	}
+
+	/* At this point the edges intersect, at least marginally */
+	DebugEvent( tess );
+
+	tesedgeIntersect( dstUp, orgUp, dstLo, orgLo, &isect );
+	/* The following properties are guaranteed: */
+	assert( MIN( orgUp->t, dstUp->t ) <= isect.t );
+	assert( isect.t <= MAX( orgLo->t, dstLo->t ));
+	assert( MIN( dstLo->s, dstUp->s ) <= isect.s );
+	assert( isect.s <= MAX( orgLo->s, orgUp->s ));
+
+	if( VertLeq( &isect, tess->event )) {
+		/* The intersection point lies slightly to the left of the sweep line,
+		* so move it until it''s slightly to the right of the sweep line.
+		* (If we had perfect numerical precision, this would never happen
+		* in the first place).  The easiest and safest thing to do is
+		* replace the intersection by tess->event.
+		*/
+		isect.s = tess->event->s;
+		isect.t = tess->event->t;
+	}
+	/* Similarly, if the computed intersection lies to the right of the
+	* rightmost origin (which should rarely happen), it can cause
+	* unbelievable inefficiency on sufficiently degenerate inputs.
+	* (If you have the test program, try running test54.d with the
+	* "X zoom" option turned on).
+	*/
+	orgMin = VertLeq( orgUp, orgLo ) ? orgUp : orgLo;
+	if( VertLeq( orgMin, &isect )) {
+		isect.s = orgMin->s;
+		isect.t = orgMin->t;
+	}
+
+	if( VertEq( &isect, orgUp ) || VertEq( &isect, orgLo )) {
+		/* Easy case -- intersection at one of the right endpoints */
+		(void) CheckForRightSplice( tess, regUp );
+		return FALSE;
+	}
+
+	if(    (! VertEq( dstUp, tess->event )
+		&& EdgeSign( dstUp, tess->event, &isect ) >= 0)
+		|| (! VertEq( dstLo, tess->event )
+		&& EdgeSign( dstLo, tess->event, &isect ) <= 0 ))
+	{
+		/* Very unusual -- the new upper or lower edge would pass on the
+		* wrong side of the sweep event, or through it.  This can happen
+		* due to very small numerical errors in the intersection calculation.
+		*/
+		if( dstLo == tess->event ) {
+			/* Splice dstLo into eUp, and process the new region(s) */
+			if (tessMeshSplitEdge( tess->mesh, eUp->Sym ) == NULL) longjmp(tess->env,1);
+			if ( !tessMeshSplice( tess->mesh, eLo->Sym, eUp ) ) longjmp(tess->env,1);
+			regUp = TopLeftRegion( tess, regUp );
+			if (regUp == NULL) longjmp(tess->env,1);
+			eUp = RegionBelow(regUp)->eUp;
+			FinishLeftRegions( tess, RegionBelow(regUp), regLo );
+			AddRightEdges( tess, regUp, eUp->Oprev, eUp, eUp, TRUE );
+			return TRUE;
+		}
+		if( dstUp == tess->event ) {
+			/* Splice dstUp into eLo, and process the new region(s) */
+			if (tessMeshSplitEdge( tess->mesh, eLo->Sym ) == NULL) longjmp(tess->env,1);
+			if ( !tessMeshSplice( tess->mesh, eUp->Lnext, eLo->Oprev ) ) longjmp(tess->env,1);
+			regLo = regUp;
+			regUp = TopRightRegion( regUp );
+			e = RegionBelow(regUp)->eUp->Rprev;
+			regLo->eUp = eLo->Oprev;
+			eLo = FinishLeftRegions( tess, regLo, NULL );
+			AddRightEdges( tess, regUp, eLo->Onext, eUp->Rprev, e, TRUE );
+			return TRUE;
+		}
+		/* Special case: called from ConnectRightVertex.  If either
+		* edge passes on the wrong side of tess->event, split it
+		* (and wait for ConnectRightVertex to splice it appropriately).
+		*/
+		if( EdgeSign( dstUp, tess->event, &isect ) >= 0 ) {
+			RegionAbove(regUp)->dirty = regUp->dirty = TRUE;
+			if (tessMeshSplitEdge( tess->mesh, eUp->Sym ) == NULL) longjmp(tess->env,1);
+			eUp->Org->s = tess->event->s;
+			eUp->Org->t = tess->event->t;
+		}
+		if( EdgeSign( dstLo, tess->event, &isect ) <= 0 ) {
+			regUp->dirty = regLo->dirty = TRUE;
+			if (tessMeshSplitEdge( tess->mesh, eLo->Sym ) == NULL) longjmp(tess->env,1);
+			eLo->Org->s = tess->event->s;
+			eLo->Org->t = tess->event->t;
+		}
+		/* leave the rest for ConnectRightVertex */
+		return FALSE;
+	}
+
+	/* General case -- split both edges, splice into new vertex.
+	* When we do the splice operation, the order of the arguments is
+	* arbitrary as far as correctness goes.  However, when the operation
+	* creates a new face, the work done is proportional to the size of
+	* the new face.  We expect the faces in the processed part of
+	* the mesh (ie. eUp->Lface) to be smaller than the faces in the
+	* unprocessed original contours (which will be eLo->Oprev->Lface).
+	*/
+	if (tessMeshSplitEdge( tess->mesh, eUp->Sym ) == NULL) longjmp(tess->env,1);
+	if (tessMeshSplitEdge( tess->mesh, eLo->Sym ) == NULL) longjmp(tess->env,1);
+	if ( !tessMeshSplice( tess->mesh, eLo->Oprev, eUp ) ) longjmp(tess->env,1);
+	eUp->Org->s = isect.s;
+	eUp->Org->t = isect.t;
+	eUp->Org->pqHandle = pqInsert( &tess->alloc, tess->pq, eUp->Org );
+	if (eUp->Org->pqHandle == INV_HANDLE) {
+		pqDeletePriorityQ( &tess->alloc, tess->pq );
+		tess->pq = NULL;
+		longjmp(tess->env,1);
+	}
+	GetIntersectData( tess, eUp->Org, orgUp, dstUp, orgLo, dstLo );
+	RegionAbove(regUp)->dirty = regUp->dirty = regLo->dirty = TRUE;
+	return FALSE;
+}
+
+static void WalkDirtyRegions( TESStesselator *tess, ActiveRegion *regUp )
+/*
+* When the upper or lower edge of any region changes, the region is
+* marked "dirty".  This routine walks through all the dirty regions
+* and makes sure that the dictionary invariants are satisfied
+* (see the comments at the beginning of this file).  Of course
+* new dirty regions can be created as we make changes to restore
+* the invariants.
+*/
+{
+	ActiveRegion *regLo = RegionBelow(regUp);
+	TESShalfEdge *eUp, *eLo;
+
+	for( ;; ) {
+		/* Find the lowest dirty region (we walk from the bottom up). */
+		while( regLo->dirty ) {
+			regUp = regLo;
+			regLo = RegionBelow(regLo);
+		}
+		if( ! regUp->dirty ) {
+			regLo = regUp;
+			regUp = RegionAbove( regUp );
+			if( regUp == NULL || ! regUp->dirty ) {
+				/* We've walked all the dirty regions */
+				return;
+			}
+		}
+		regUp->dirty = FALSE;
+		eUp = regUp->eUp;
+		eLo = regLo->eUp;
+
+		if( eUp->Dst != eLo->Dst ) {
+			/* Check that the edge ordering is obeyed at the Dst vertices. */
+			if( CheckForLeftSplice( tess, regUp )) {
+
+				/* If the upper or lower edge was marked fixUpperEdge, then
+				* we no longer need it (since these edges are needed only for
+				* vertices which otherwise have no right-going edges).
+				*/
+				if( regLo->fixUpperEdge ) {
+					DeleteRegion( tess, regLo );
+					if ( !tessMeshDelete( tess->mesh, eLo ) ) longjmp(tess->env,1);
+					regLo = RegionBelow( regUp );
+					eLo = regLo->eUp;
+				} else if( regUp->fixUpperEdge ) {
+					DeleteRegion( tess, regUp );
+					if ( !tessMeshDelete( tess->mesh, eUp ) ) longjmp(tess->env,1);
+					regUp = RegionAbove( regLo );
+					eUp = regUp->eUp;
+				}
+			}
+		}
+		if( eUp->Org != eLo->Org ) {
+			if(    eUp->Dst != eLo->Dst
+				&& ! regUp->fixUpperEdge && ! regLo->fixUpperEdge
+				&& (eUp->Dst == tess->event || eLo->Dst == tess->event) )
+			{
+				/* When all else fails in CheckForIntersect(), it uses tess->event
+				* as the intersection location.  To make this possible, it requires
+				* that tess->event lie between the upper and lower edges, and also
+				* that neither of these is marked fixUpperEdge (since in the worst
+				* case it might splice one of these edges into tess->event, and
+				* violate the invariant that fixable edges are the only right-going
+				* edge from their associated vertex).
+				*/
+				if( CheckForIntersect( tess, regUp )) {
+					/* WalkDirtyRegions() was called recursively; we're done */
+					return;
+				}
+			} else {
+				/* Even though we can't use CheckForIntersect(), the Org vertices
+				* may violate the dictionary edge ordering.  Check and correct this.
+				*/
+				(void) CheckForRightSplice( tess, regUp );
+			}
+		}
+		if( eUp->Org == eLo->Org && eUp->Dst == eLo->Dst ) {
+			/* A degenerate loop consisting of only two edges -- delete it. */
+			AddWinding( eLo, eUp );
+			DeleteRegion( tess, regUp );
+			if ( !tessMeshDelete( tess->mesh, eUp ) ) longjmp(tess->env,1);
+			regUp = RegionAbove( regLo );
+		}
+	}
+}
+
+
+static void ConnectRightVertex( TESStesselator *tess, ActiveRegion *regUp,
+							   TESShalfEdge *eBottomLeft )
+/*
+* Purpose: connect a "right" vertex vEvent (one where all edges go left)
+* to the unprocessed portion of the mesh.  Since there are no right-going
+* edges, two regions (one above vEvent and one below) are being merged
+* into one.  "regUp" is the upper of these two regions.
+*
+* There are two reasons for doing this (adding a right-going edge):
+*  - if the two regions being merged are "inside", we must add an edge
+*    to keep them separated (the combined region would not be monotone).
+*  - in any case, we must leave some record of vEvent in the dictionary,
+*    so that we can merge vEvent with features that we have not seen yet.
+*    For example, maybe there is a vertical edge which passes just to
+*    the right of vEvent; we would like to splice vEvent into this edge.
+*
+* However, we don't want to connect vEvent to just any vertex.  We don''t
+* want the new edge to cross any other edges; otherwise we will create
+* intersection vertices even when the input data had no self-intersections.
+* (This is a bad thing; if the user's input data has no intersections,
+* we don't want to generate any false intersections ourselves.)
+*
+* Our eventual goal is to connect vEvent to the leftmost unprocessed
+* vertex of the combined region (the union of regUp and regLo).
+* But because of unseen vertices with all right-going edges, and also
+* new vertices which may be created by edge intersections, we don''t
+* know where that leftmost unprocessed vertex is.  In the meantime, we
+* connect vEvent to the closest vertex of either chain, and mark the region
+* as "fixUpperEdge".  This flag says to delete and reconnect this edge
+* to the next processed vertex on the boundary of the combined region.
+* Quite possibly the vertex we connected to will turn out to be the
+* closest one, in which case we won''t need to make any changes.
+*/
+{
+	TESShalfEdge *eNew;
+	TESShalfEdge *eTopLeft = eBottomLeft->Onext;
+	ActiveRegion *regLo = RegionBelow(regUp);
+	TESShalfEdge *eUp = regUp->eUp;
+	TESShalfEdge *eLo = regLo->eUp;
+	int degenerate = FALSE;
+
+	if( eUp->Dst != eLo->Dst ) {
+		(void) CheckForIntersect( tess, regUp );
+	}
+
+	/* Possible new degeneracies: upper or lower edge of regUp may pass
+	* through vEvent, or may coincide with new intersection vertex
+	*/
+	if( VertEq( eUp->Org, tess->event )) {
+		if ( !tessMeshSplice( tess->mesh, eTopLeft->Oprev, eUp ) ) longjmp(tess->env,1);
+		regUp = TopLeftRegion( tess, regUp );
+		if (regUp == NULL) longjmp(tess->env,1);
+		eTopLeft = RegionBelow( regUp )->eUp;
+		FinishLeftRegions( tess, RegionBelow(regUp), regLo );
+		degenerate = TRUE;
+	}
+	if( VertEq( eLo->Org, tess->event )) {
+		if ( !tessMeshSplice( tess->mesh, eBottomLeft, eLo->Oprev ) ) longjmp(tess->env,1);
+		eBottomLeft = FinishLeftRegions( tess, regLo, NULL );
+		degenerate = TRUE;
+	}
+	if( degenerate ) {
+		AddRightEdges( tess, regUp, eBottomLeft->Onext, eTopLeft, eTopLeft, TRUE );
+		return;
+	}
+
+	/* Non-degenerate situation -- need to add a temporary, fixable edge.
+	* Connect to the closer of eLo->Org, eUp->Org.
+	*/
+	if( VertLeq( eLo->Org, eUp->Org )) {
+		eNew = eLo->Oprev;
+	} else {
+		eNew = eUp;
+	}
+	eNew = tessMeshConnect( tess->mesh, eBottomLeft->Lprev, eNew );
+	if (eNew == NULL) longjmp(tess->env,1);
+
+	/* Prevent cleanup, otherwise eNew might disappear before we've even
+	* had a chance to mark it as a temporary edge.
+	*/
+	AddRightEdges( tess, regUp, eNew, eNew->Onext, eNew->Onext, FALSE );
+	eNew->Sym->activeRegion->fixUpperEdge = TRUE;
+	WalkDirtyRegions( tess, regUp );
+}
+
+/* Because vertices at exactly the same location are merged together
+* before we process the sweep event, some degenerate cases can't occur.
+* However if someone eventually makes the modifications required to
+* merge features which are close together, the cases below marked
+* TOLERANCE_NONZERO will be useful.  They were debugged before the
+* code to merge identical vertices in the main loop was added.
+*/
+#define TOLERANCE_NONZERO	FALSE
+
+static void ConnectLeftDegenerate( TESStesselator *tess,
+								  ActiveRegion *regUp, TESSvertex *vEvent )
+/*
+* The event vertex lies exacty on an already-processed edge or vertex.
+* Adding the new vertex involves splicing it into the already-processed
+* part of the mesh.
+*/
+{
+	TESShalfEdge *e, *eTopLeft, *eTopRight, *eLast;
+	ActiveRegion *reg;
+
+	e = regUp->eUp;
+	if( VertEq( e->Org, vEvent )) {
+		/* e->Org is an unprocessed vertex - just combine them, and wait
+		* for e->Org to be pulled from the queue
+		*/
+		assert( TOLERANCE_NONZERO );
+		SpliceMergeVertices( tess, e, vEvent->anEdge );
+		return;
+	}
+
+	if( ! VertEq( e->Dst, vEvent )) {
+		/* General case -- splice vEvent into edge e which passes through it */
+		if (tessMeshSplitEdge( tess->mesh, e->Sym ) == NULL) longjmp(tess->env,1);
+		if( regUp->fixUpperEdge ) {
+			/* This edge was fixable -- delete unused portion of original edge */
+			if ( !tessMeshDelete( tess->mesh, e->Onext ) ) longjmp(tess->env,1);
+			regUp->fixUpperEdge = FALSE;
+		}
+		if ( !tessMeshSplice( tess->mesh, vEvent->anEdge, e ) ) longjmp(tess->env,1);
+		SweepEvent( tess, vEvent );	/* recurse */
+		return;
+	}
+
+	/* vEvent coincides with e->Dst, which has already been processed.
+	* Splice in the additional right-going edges.
+	*/
+	assert( TOLERANCE_NONZERO );
+	regUp = TopRightRegion( regUp );
+	reg = RegionBelow( regUp );
+	eTopRight = reg->eUp->Sym;
+	eTopLeft = eLast = eTopRight->Onext;
+	if( reg->fixUpperEdge ) {
+		/* Here e->Dst has only a single fixable edge going right.
+		* We can delete it since now we have some real right-going edges.
+		*/
+		assert( eTopLeft != eTopRight );   /* there are some left edges too */
+		DeleteRegion( tess, reg );
+		if ( !tessMeshDelete( tess->mesh, eTopRight ) ) longjmp(tess->env,1);
+		eTopRight = eTopLeft->Oprev;
+	}
+	if ( !tessMeshSplice( tess->mesh, vEvent->anEdge, eTopRight ) ) longjmp(tess->env,1);
+	if( ! EdgeGoesLeft( eTopLeft )) {
+		/* e->Dst had no left-going edges -- indicate this to AddRightEdges() */
+		eTopLeft = NULL;
+	}
+	AddRightEdges( tess, regUp, eTopRight->Onext, eLast, eTopLeft, TRUE );
+}
+
+
+static void ConnectLeftVertex( TESStesselator *tess, TESSvertex *vEvent )
+/*
+* Purpose: connect a "left" vertex (one where both edges go right)
+* to the processed portion of the mesh.  Let R be the active region
+* containing vEvent, and let U and L be the upper and lower edge
+* chains of R.  There are two possibilities:
+*
+* - the normal case: split R into two regions, by connecting vEvent to
+*   the rightmost vertex of U or L lying to the left of the sweep line
+*
+* - the degenerate case: if vEvent is close enough to U or L, we
+*   merge vEvent into that edge chain.  The subcases are:
+*	- merging with the rightmost vertex of U or L
+*	- merging with the active edge of U or L
+*	- merging with an already-processed portion of U or L
+*/
+{
+	ActiveRegion *regUp, *regLo, *reg;
+	TESShalfEdge *eUp, *eLo, *eNew;
+	ActiveRegion tmp;
+
+	/* assert( vEvent->anEdge->Onext->Onext == vEvent->anEdge ); */
+
+	/* Get a pointer to the active region containing vEvent */
+	tmp.eUp = vEvent->anEdge->Sym;
+	/* __GL_DICTLISTKEY */ /* tessDictListSearch */
+	regUp = (ActiveRegion *)dictKey( dictSearch( tess->dict, &tmp ));
+	regLo = RegionBelow( regUp );
+	if( !regLo ) {
+		// This may happen if the input polygon is coplanar.
+		return;
+	}
+	eUp = regUp->eUp;
+	eLo = regLo->eUp;
+
+	/* Try merging with U or L first */
+	if( EdgeSign( eUp->Dst, vEvent, eUp->Org ) == 0 ) {
+		ConnectLeftDegenerate( tess, regUp, vEvent );
+		return;
+	}
+
+	/* Connect vEvent to rightmost processed vertex of either chain.
+	* e->Dst is the vertex that we will connect to vEvent.
+	*/
+	reg = VertLeq( eLo->Dst, eUp->Dst ) ? regUp : regLo;
+
+	if( regUp->inside || reg->fixUpperEdge) {
+		if( reg == regUp ) {
+			eNew = tessMeshConnect( tess->mesh, vEvent->anEdge->Sym, eUp->Lnext );
+			if (eNew == NULL) longjmp(tess->env,1);
+		} else {
+			TESShalfEdge *tempHalfEdge= tessMeshConnect( tess->mesh, eLo->Dnext, vEvent->anEdge);
+			if (tempHalfEdge == NULL) longjmp(tess->env,1);
+
+			eNew = tempHalfEdge->Sym;
+		}
+		if( reg->fixUpperEdge ) {
+			if ( !FixUpperEdge( tess, reg, eNew ) ) longjmp(tess->env,1);
+		} else {
+			ComputeWinding( tess, AddRegionBelow( tess, regUp, eNew ));
+		}
+		SweepEvent( tess, vEvent );
+	} else {
+		/* The new vertex is in a region which does not belong to the polygon.
+		* We don''t need to connect this vertex to the rest of the mesh.
+		*/
+		AddRightEdges( tess, regUp, vEvent->anEdge, vEvent->anEdge, NULL, TRUE );
+	}
+}
+
+
+static void SweepEvent( TESStesselator *tess, TESSvertex *vEvent )
+/*
+* Does everything necessary when the sweep line crosses a vertex.
+* Updates the mesh and the edge dictionary.
+*/
+{
+	ActiveRegion *regUp, *reg;
+	TESShalfEdge *e, *eTopLeft, *eBottomLeft;
+
+	tess->event = vEvent;		/* for access in EdgeLeq() */
+	DebugEvent( tess );
+
+	/* Check if this vertex is the right endpoint of an edge that is
+	* already in the dictionary.  In this case we don't need to waste
+	* time searching for the location to insert new edges.
+	*/
+	e = vEvent->anEdge;
+	while( e->activeRegion == NULL ) {
+		e = e->Onext;
+		if( e == vEvent->anEdge ) {
+			/* All edges go right -- not incident to any processed edges */
+			ConnectLeftVertex( tess, vEvent );
+			return;
+		}
+	}
+
+	/* Processing consists of two phases: first we "finish" all the
+	* active regions where both the upper and lower edges terminate
+	* at vEvent (ie. vEvent is closing off these regions).
+	* We mark these faces "inside" or "outside" the polygon according
+	* to their winding number, and delete the edges from the dictionary.
+	* This takes care of all the left-going edges from vEvent.
+	*/
+	regUp = TopLeftRegion( tess, e->activeRegion );
+	if (regUp == NULL) longjmp(tess->env,1);
+	reg = RegionBelow( regUp );
+	eTopLeft = reg->eUp;
+	eBottomLeft = FinishLeftRegions( tess, reg, NULL );
+
+	/* Next we process all the right-going edges from vEvent.  This
+	* involves adding the edges to the dictionary, and creating the
+	* associated "active regions" which record information about the
+	* regions between adjacent dictionary edges.
+	*/
+	if( eBottomLeft->Onext == eTopLeft ) {
+		/* No right-going edges -- add a temporary "fixable" edge */
+		ConnectRightVertex( tess, regUp, eBottomLeft );
+	} else {
+		AddRightEdges( tess, regUp, eBottomLeft->Onext, eTopLeft, eTopLeft, TRUE );
+	}
+}
+
+
+/* Make the sentinel coordinates big enough that they will never be
+* merged with real input features.
+*/
+
+static void AddSentinel( TESStesselator *tess, TESSreal smin, TESSreal smax, TESSreal t )
+/*
+* We add two sentinel edges above and below all other edges,
+* to avoid special cases at the top and bottom.
+*/
+{
+	TESShalfEdge *e;
+	ActiveRegion *reg = (ActiveRegion *)bucketAlloc( tess->regionPool );
+	if (reg == NULL) longjmp(tess->env,1);
+
+	e = tessMeshMakeEdge( tess->mesh );
+	if (e == NULL) longjmp(tess->env,1);
+
+	e->Org->s = smax;
+	e->Org->t = t;
+	e->Dst->s = smin;
+	e->Dst->t = t;
+	tess->event = e->Dst;		/* initialize it */
+
+	reg->eUp = e;
+	reg->windingNumber = 0;
+	reg->inside = FALSE;
+	reg->fixUpperEdge = FALSE;
+	reg->sentinel = TRUE;
+	reg->dirty = FALSE;
+	reg->nodeUp = dictInsert( tess->dict, reg );
+	if (reg->nodeUp == NULL) longjmp(tess->env,1);
+}
+
+
+static void InitEdgeDict( TESStesselator *tess )
+/*
+* We maintain an ordering of edge intersections with the sweep line.
+* This order is maintained in a dynamic dictionary.
+*/
+{
+	TESSreal w, h;
+	TESSreal smin, smax, tmin, tmax;
+
+	tess->dict = dictNewDict( &tess->alloc, tess, (int (*)(void *, DictKey, DictKey)) EdgeLeq );
+	if (tess->dict == NULL) longjmp(tess->env,1);
+
+	w = (tess->bmax[0] - tess->bmin[0]);
+	h = (tess->bmax[1] - tess->bmin[1]);
+
+        /* If the bbox is empty, ensure that sentinels are not coincident by
+           slightly enlarging it. */
+	smin = tess->bmin[0] - (w > 0 ? w : 0.01);
+        smax = tess->bmax[0] + (w > 0 ? w : 0.01);
+        tmin = tess->bmin[1] - (h > 0 ? h : 0.01);
+        tmax = tess->bmax[1] + (h > 0 ? h : 0.01);
+
+	AddSentinel( tess, smin, smax, tmin );
+	AddSentinel( tess, smin, smax, tmax );
+}
+
+
+static void DoneEdgeDict( TESStesselator *tess )
+{
+	ActiveRegion *reg;
+	int fixedEdges = 0;
+	(void)fixedEdges;
+
+	while( (reg = (ActiveRegion *)dictKey( dictMin( tess->dict ))) != NULL ) {
+		/*
+		* At the end of all processing, the dictionary should contain
+		* only the two sentinel edges, plus at most one "fixable" edge
+		* created by ConnectRightVertex().
+		*/
+		if( ! reg->sentinel ) {
+			assert( reg->fixUpperEdge );
+			assert( ++fixedEdges == 1 );
+		}
+		assert( reg->windingNumber == 0 );
+		DeleteRegion( tess, reg );
+		/*    tessMeshDelete( reg->eUp );*/
+	}
+	dictDeleteDict( &tess->alloc, tess->dict );
+}
+
+
+static void RemoveDegenerateEdges( TESStesselator *tess )
+/*
+* Remove zero-length edges, and contours with fewer than 3 vertices.
+*/
+{
+	TESShalfEdge *e, *eNext, *eLnext;
+	TESShalfEdge *eHead = &tess->mesh->eHead;
+
+	/*LINTED*/
+	for( e = eHead->next; e != eHead; e = eNext ) {
+		eNext = e->next;
+		eLnext = e->Lnext;
+
+		if( VertEq( e->Org, e->Dst ) && e->Lnext->Lnext != e ) {
+			/* Zero-length edge, contour has at least 3 edges */
+
+			SpliceMergeVertices( tess, eLnext, e );	/* deletes e->Org */
+			if ( !tessMeshDelete( tess->mesh, e ) ) longjmp(tess->env,1); /* e is a self-loop */
+			e = eLnext;
+			eLnext = e->Lnext;
+		}
+		if( eLnext->Lnext == e ) {
+			/* Degenerate contour (one or two edges) */
+
+			if( eLnext != e ) {
+				if( eLnext == eNext || eLnext == eNext->Sym ) { eNext = eNext->next; }
+				if ( !tessMeshDelete( tess->mesh, eLnext ) ) longjmp(tess->env,1);
+			}
+			if( e == eNext || e == eNext->Sym ) { eNext = eNext->next; }
+			if ( !tessMeshDelete( tess->mesh, e ) ) longjmp(tess->env,1);
+		}
+	}
+}
+
+static int InitPriorityQ( TESStesselator *tess )
+/*
+* Insert all vertices into the priority queue which determines the
+* order in which vertices cross the sweep line.
+*/
+{
+	PriorityQ *pq;
+	TESSvertex *v, *vHead;
+	int vertexCount = 0;
+
+	vHead = &tess->mesh->vHead;
+	for( v = vHead->next; v != vHead; v = v->next ) {
+		vertexCount++;
+	}
+	/* Make sure there is enough space for sentinels. */
+	vertexCount += MAX( 8, tess->alloc.extraVertices );
+
+	pq = tess->pq = pqNewPriorityQ( &tess->alloc, vertexCount, (int (*)(PQkey, PQkey)) tesvertLeq );
+	if (pq == NULL) return 0;
+
+	vHead = &tess->mesh->vHead;
+	for( v = vHead->next; v != vHead; v = v->next ) {
+		v->pqHandle = pqInsert( &tess->alloc, pq, v );
+		if (v->pqHandle == INV_HANDLE)
+			break;
+	}
+	if (v != vHead || !pqInit( &tess->alloc, pq ) ) {
+		pqDeletePriorityQ( &tess->alloc, tess->pq );
+		tess->pq = NULL;
+		return 0;
+	}
+
+	return 1;
+}
+
+
+static void DonePriorityQ( TESStesselator *tess )
+{
+	pqDeletePriorityQ( &tess->alloc, tess->pq );
+}
+
+
+static int RemoveDegenerateFaces( TESStesselator *tess, TESSmesh *mesh )
+/*
+* Delete any degenerate faces with only two edges.  WalkDirtyRegions()
+* will catch almost all of these, but it won't catch degenerate faces
+* produced by splice operations on already-processed edges.
+* The two places this can happen are in FinishLeftRegions(), when
+* we splice in a "temporary" edge produced by ConnectRightVertex(),
+* and in CheckForLeftSplice(), where we splice already-processed
+* edges to ensure that our dictionary invariants are not violated
+* by numerical errors.
+*
+* In both these cases it is *very* dangerous to delete the offending
+* edge at the time, since one of the routines further up the stack
+* will sometimes be keeping a pointer to that edge.
+*/
+{
+	TESSface *f, *fNext;
+	TESShalfEdge *e;
+
+	/*LINTED*/
+	for( f = mesh->fHead.next; f != &mesh->fHead; f = fNext ) {
+		fNext = f->next;
+		e = f->anEdge;
+		assert( e->Lnext != e );
+
+		if( e->Lnext->Lnext == e ) {
+			/* A face with only two edges */
+			AddWinding( e->Onext, e );
+			if ( !tessMeshDelete( tess->mesh, e ) ) return 0;
+		}
+	}
+	return 1;
+}
+
+int tessComputeInterior( TESStesselator *tess )
+/*
+* tessComputeInterior( tess ) computes the planar arrangement specified
+* by the given contours, and further subdivides this arrangement
+* into regions.  Each region is marked "inside" if it belongs
+* to the polygon, according to the rule given by tess->windingRule.
+* Each interior region is guaranteed be monotone.
+*/
+{
+	TESSvertex *v, *vNext;
+
+	/* Each vertex defines an event for our sweep line.  Start by inserting
+	* all the vertices in a priority queue.  Events are processed in
+	* lexicographic order, ie.
+	*
+	*	e1 < e2  iff  e1.x < e2.x || (e1.x == e2.x && e1.y < e2.y)
+	*/
+	RemoveDegenerateEdges( tess );
+	if ( !InitPriorityQ( tess ) ) return 0; /* if error */
+	InitEdgeDict( tess );
+
+	while( (v = (TESSvertex *)pqExtractMin( tess->pq )) != NULL ) {
+		for( ;; ) {
+			vNext = (TESSvertex *)pqMinimum( tess->pq );
+			if( vNext == NULL || ! VertEq( vNext, v )) break;
+
+			/* Merge together all vertices at exactly the same location.
+			* This is more efficient than processing them one at a time,
+			* simplifies the code (see ConnectLeftDegenerate), and is also
+			* important for correct handling of certain degenerate cases.
+			* For example, suppose there are two identical edges A and B
+			* that belong to different contours (so without this code they would
+			* be processed by separate sweep events).  Suppose another edge C
+			* crosses A and B from above.  When A is processed, we split it
+			* at its intersection point with C.  However this also splits C,
+			* so when we insert B we may compute a slightly different
+			* intersection point.  This might leave two edges with a small
+			* gap between them.  This kind of error is especially obvious
+			* when using boundary extraction (TESS_BOUNDARY_ONLY).
+			*/
+			vNext = (TESSvertex *)pqExtractMin( tess->pq );
+			SpliceMergeVertices( tess, v->anEdge, vNext->anEdge );
+		}
+		SweepEvent( tess, v );
+	}
+
+	/* Set tess->event for debugging purposes */
+	tess->event = ((ActiveRegion *) dictKey( dictMin( tess->dict )))->eUp->Org;
+	DebugEvent( tess );
+	DoneEdgeDict( tess );
+	DonePriorityQ( tess );
+
+	if ( !RemoveDegenerateFaces( tess, tess->mesh ) ) return 0;
+	tessMeshCheckMesh( tess->mesh );
+
+	return 1;
+}

+ 74 - 0
polygon.mod/earcut/test/comparison/libtess2/sweep.h

@@ -0,0 +1,74 @@
+/*
+** SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
+** Copyright (C) [dates of first publication] Silicon Graphics, Inc.
+** All Rights Reserved.
+**
+** 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 including the dates of first publication and either this
+** permission notice or a reference to http://oss.sgi.com/projects/FreeB/ 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 SILICON GRAPHICS, INC.
+** 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.
+**
+** Except as contained in this notice, the name of Silicon Graphics, Inc. shall not
+** be used in advertising or otherwise to promote the sale, use or other dealings in
+** this Software without prior written authorization from Silicon Graphics, Inc.
+*/
+/*
+** Author: Eric Veach, July 1994.
+*/
+
+#ifndef SWEEP_H
+#define SWEEP_H
+
+#include "mesh.h"
+
+/* tessComputeInterior( tess ) computes the planar arrangement specified
+* by the given contours, and further subdivides this arrangement
+* into regions.  Each region is marked "inside" if it belongs
+* to the polygon, according to the rule given by tess->windingRule.
+* Each interior region is guaranteed be monotone.
+*/
+int tessComputeInterior( TESStesselator *tess );
+
+
+/* The following is here *only* for access by debugging routines */
+
+#include "dict.h"
+
+/* For each pair of adjacent edges crossing the sweep line, there is
+* an ActiveRegion to represent the region between them.  The active
+* regions are kept in sorted order in a dynamic dictionary.  As the
+* sweep line crosses each vertex, we update the affected regions.
+*/
+
+struct ActiveRegion {
+	TESShalfEdge *eUp;		/* upper edge, directed right to left */
+	DictNode *nodeUp;	/* dictionary node corresponding to eUp */
+	int windingNumber;	/* used to determine which regions are
+							* inside the polygon */
+	int inside;		/* is this region inside the polygon? */
+	int sentinel;	/* marks fake edges at t = +/-infinity */
+	int dirty;		/* marks regions where the upper or lower
+					* edge has changed, but we haven't checked
+					* whether they intersect yet */
+	int fixUpperEdge;	/* marks temporary edges introduced when
+						* we process a "right vertex" (one without
+						* any edges leaving to the right) */
+};
+
+#define RegionBelow(r) ((ActiveRegion *) dictKey(dictPred((r)->nodeUp)))
+#define RegionAbove(r) ((ActiveRegion *) dictKey(dictSucc((r)->nodeUp)))
+
+#endif

+ 982 - 0
polygon.mod/earcut/test/comparison/libtess2/tess.c

@@ -0,0 +1,982 @@
+/*
+** SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
+** Copyright (C) [dates of first publication] Silicon Graphics, Inc.
+** All Rights Reserved.
+**
+** 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 including the dates of first publication and either this
+** permission notice or a reference to http://oss.sgi.com/projects/FreeB/ 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 SILICON GRAPHICS, INC.
+** 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.
+**
+** Except as contained in this notice, the name of Silicon Graphics, Inc. shall not
+** be used in advertising or otherwise to promote the sale, use or other dealings in
+** this Software without prior written authorization from Silicon Graphics, Inc.
+*/
+/*
+** Author: Eric Veach, July 1994.
+*/
+
+#include <stddef.h>
+#include <assert.h>
+#include <setjmp.h>
+#include "bucketalloc.h"
+#include "tess.h"
+#include "mesh.h"
+#include "sweep.h"
+#include "geom.h"
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#define TRUE 1
+#define FALSE 0
+
+#define Dot(u,v)	(u[0]*v[0] + u[1]*v[1] + u[2]*v[2])
+
+#if defined(FOR_TRITE_TEST_PROGRAM) || defined(TRUE_PROJECT)
+static void Normalize( TESSreal v[3] )
+{
+	TESSreal len = v[0]*v[0] + v[1]*v[1] + v[2]*v[2];
+
+	assert( len > 0 );
+	len = sqrtf( len );
+	v[0] /= len;
+	v[1] /= len;
+	v[2] /= len;
+}
+#endif
+
+#define ABS(x)	((x) < 0 ? -(x) : (x))
+
+static int LongAxis( TESSreal v[3] )
+{
+	int i = 0;
+
+	if( ABS(v[1]) > ABS(v[0]) ) { i = 1; }
+	if( ABS(v[2]) > ABS(v[i]) ) { i = 2; }
+	return i;
+}
+
+static int ShortAxis( TESSreal v[3] )
+{
+	int i = 0;
+
+	if( ABS(v[1]) < ABS(v[0]) ) { i = 1; }
+	if( ABS(v[2]) < ABS(v[i]) ) { i = 2; }
+	return i;
+}
+
+static void ComputeNormal( TESStesselator *tess, TESSreal norm[3] )
+{
+	TESSvertex *v, *v1, *v2;
+	TESSreal c, tLen2, maxLen2;
+	TESSreal maxVal[3], minVal[3], d1[3], d2[3], tNorm[3];
+	TESSvertex *maxVert[3], *minVert[3];
+	TESSvertex *vHead = &tess->mesh->vHead;
+	int i;
+
+	v = vHead->next;
+	for( i = 0; i < 3; ++i ) {
+		c = v->coords[i];
+		minVal[i] = c;
+		minVert[i] = v;
+		maxVal[i] = c;
+		maxVert[i] = v;
+	}
+
+	for( v = vHead->next; v != vHead; v = v->next ) {
+		for( i = 0; i < 3; ++i ) {
+			c = v->coords[i];
+			if( c < minVal[i] ) { minVal[i] = c; minVert[i] = v; }
+			if( c > maxVal[i] ) { maxVal[i] = c; maxVert[i] = v; }
+		}
+	}
+
+	/* Find two vertices separated by at least 1/sqrt(3) of the maximum
+	* distance between any two vertices
+	*/
+	i = 0;
+	if( maxVal[1] - minVal[1] > maxVal[0] - minVal[0] ) { i = 1; }
+	if( maxVal[2] - minVal[2] > maxVal[i] - minVal[i] ) { i = 2; }
+	if( minVal[i] >= maxVal[i] ) {
+		/* All vertices are the same -- normal doesn't matter */
+		norm[0] = 0; norm[1] = 0; norm[2] = 1;
+		return;
+	}
+
+	/* Look for a third vertex which forms the triangle with maximum area
+	* (Length of normal == twice the triangle area)
+	*/
+	maxLen2 = 0;
+	v1 = minVert[i];
+	v2 = maxVert[i];
+	d1[0] = v1->coords[0] - v2->coords[0];
+	d1[1] = v1->coords[1] - v2->coords[1];
+	d1[2] = v1->coords[2] - v2->coords[2];
+	for( v = vHead->next; v != vHead; v = v->next ) {
+		d2[0] = v->coords[0] - v2->coords[0];
+		d2[1] = v->coords[1] - v2->coords[1];
+		d2[2] = v->coords[2] - v2->coords[2];
+		tNorm[0] = d1[1]*d2[2] - d1[2]*d2[1];
+		tNorm[1] = d1[2]*d2[0] - d1[0]*d2[2];
+		tNorm[2] = d1[0]*d2[1] - d1[1]*d2[0];
+		tLen2 = tNorm[0]*tNorm[0] + tNorm[1]*tNorm[1] + tNorm[2]*tNorm[2];
+		if( tLen2 > maxLen2 ) {
+			maxLen2 = tLen2;
+			norm[0] = tNorm[0];
+			norm[1] = tNorm[1];
+			norm[2] = tNorm[2];
+		}
+	}
+
+	if( maxLen2 <= 0 ) {
+		/* All points lie on a single line -- any decent normal will do */
+		norm[0] = norm[1] = norm[2] = 0;
+		norm[ShortAxis(d1)] = 1;
+	}
+}
+
+
+static void CheckOrientation( TESStesselator *tess )
+{
+	TESSreal area;
+	TESSface *f, *fHead = &tess->mesh->fHead;
+	TESSvertex *v, *vHead = &tess->mesh->vHead;
+	TESShalfEdge *e;
+
+	/* When we compute the normal automatically, we choose the orientation
+	* so that the the sum of the signed areas of all contours is non-negative.
+	*/
+	area = 0;
+	for( f = fHead->next; f != fHead; f = f->next ) {
+		e = f->anEdge;
+		if( e->winding <= 0 ) continue;
+		do {
+			area += (e->Org->s - e->Dst->s) * (e->Org->t + e->Dst->t);
+			e = e->Lnext;
+		} while( e != f->anEdge );
+	}
+	if( area < 0 ) {
+		/* Reverse the orientation by flipping all the t-coordinates */
+		for( v = vHead->next; v != vHead; v = v->next ) {
+			v->t = - v->t;
+		}
+		tess->tUnit[0] = - tess->tUnit[0];
+		tess->tUnit[1] = - tess->tUnit[1];
+		tess->tUnit[2] = - tess->tUnit[2];
+	}
+}
+
+#ifdef FOR_TRITE_TEST_PROGRAM
+#include <stdlib.h>
+extern int RandomSweep;
+#define S_UNIT_X	(RandomSweep ? (2*drand48()-1) : 1.0)
+#define S_UNIT_Y	(RandomSweep ? (2*drand48()-1) : 0.0)
+#else
+#if defined(SLANTED_SWEEP)
+/* The "feature merging" is not intended to be complete.  There are
+* special cases where edges are nearly parallel to the sweep line
+* which are not implemented.  The algorithm should still behave
+* robustly (ie. produce a reasonable tesselation) in the presence
+* of such edges, however it may miss features which could have been
+* merged.  We could minimize this effect by choosing the sweep line
+* direction to be something unusual (ie. not parallel to one of the
+* coordinate axes).
+*/
+#define S_UNIT_X	(TESSreal)0.50941539564955385	/* Pre-normalized */
+#define S_UNIT_Y	(TESSreal)0.86052074622010633
+#else
+#define S_UNIT_X	(TESSreal)1.0
+#define S_UNIT_Y	(TESSreal)0.0
+#endif
+#endif
+
+/* Determine the polygon normal and project vertices onto the plane
+* of the polygon.
+*/
+void tessProjectPolygon( TESStesselator *tess )
+{
+	TESSvertex *v, *vHead = &tess->mesh->vHead;
+	TESSreal norm[3];
+	TESSreal *sUnit, *tUnit;
+	int i, first, computedNormal = FALSE;
+
+	norm[0] = tess->normal[0];
+	norm[1] = tess->normal[1];
+	norm[2] = tess->normal[2];
+	if( norm[0] == 0 && norm[1] == 0 && norm[2] == 0 ) {
+		ComputeNormal( tess, norm );
+		computedNormal = TRUE;
+	}
+	sUnit = tess->sUnit;
+	tUnit = tess->tUnit;
+	i = LongAxis( norm );
+
+#if defined(FOR_TRITE_TEST_PROGRAM) || defined(TRUE_PROJECT)
+	/* Choose the initial sUnit vector to be approximately perpendicular
+	* to the normal.
+	*/
+	Normalize( norm );
+
+	sUnit[i] = 0;
+	sUnit[(i+1)%3] = S_UNIT_X;
+	sUnit[(i+2)%3] = S_UNIT_Y;
+
+	/* Now make it exactly perpendicular */
+	w = Dot( sUnit, norm );
+	sUnit[0] -= w * norm[0];
+	sUnit[1] -= w * norm[1];
+	sUnit[2] -= w * norm[2];
+	Normalize( sUnit );
+
+	/* Choose tUnit so that (sUnit,tUnit,norm) form a right-handed frame */
+	tUnit[0] = norm[1]*sUnit[2] - norm[2]*sUnit[1];
+	tUnit[1] = norm[2]*sUnit[0] - norm[0]*sUnit[2];
+	tUnit[2] = norm[0]*sUnit[1] - norm[1]*sUnit[0];
+	Normalize( tUnit );
+#else
+	/* Project perpendicular to a coordinate axis -- better numerically */
+	sUnit[i] = 0;
+	sUnit[(i+1)%3] = S_UNIT_X;
+	sUnit[(i+2)%3] = S_UNIT_Y;
+
+	tUnit[i] = 0;
+	tUnit[(i+1)%3] = (norm[i] > 0) ? -S_UNIT_Y : S_UNIT_Y;
+	tUnit[(i+2)%3] = (norm[i] > 0) ? S_UNIT_X : -S_UNIT_X;
+#endif
+
+	/* Project the vertices onto the sweep plane */
+	for( v = vHead->next; v != vHead; v = v->next )
+	{
+		v->s = Dot( v->coords, sUnit );
+		v->t = Dot( v->coords, tUnit );
+	}
+	if( computedNormal ) {
+		CheckOrientation( tess );
+	}
+
+	/* Compute ST bounds. */
+	first = 1;
+	for( v = vHead->next; v != vHead; v = v->next )
+	{
+		if (first)
+		{
+			tess->bmin[0] = tess->bmax[0] = v->s;
+			tess->bmin[1] = tess->bmax[1] = v->t;
+			first = 0;
+		}
+		else
+		{
+			if (v->s < tess->bmin[0]) tess->bmin[0] = v->s;
+			if (v->s > tess->bmax[0]) tess->bmax[0] = v->s;
+			if (v->t < tess->bmin[1]) tess->bmin[1] = v->t;
+			if (v->t > tess->bmax[1]) tess->bmax[1] = v->t;
+		}
+	}
+}
+
+#define AddWinding(eDst,eSrc)	(eDst->winding += eSrc->winding, \
+	eDst->Sym->winding += eSrc->Sym->winding)
+
+/* tessMeshTessellateMonoRegion( face ) tessellates a monotone region
+* (what else would it do??)  The region must consist of a single
+* loop of half-edges (see mesh.h) oriented CCW.  "Monotone" in this
+* case means that any vertical line intersects the interior of the
+* region in a single interval.
+*
+* Tessellation consists of adding interior edges (actually pairs of
+* half-edges), to split the region into non-overlapping triangles.
+*
+* The basic idea is explained in Preparata and Shamos (which I don''t
+* have handy right now), although their implementation is more
+* complicated than this one.  The are two edge chains, an upper chain
+* and a lower chain.  We process all vertices from both chains in order,
+* from right to left.
+*
+* The algorithm ensures that the following invariant holds after each
+* vertex is processed: the untessellated region consists of two
+* chains, where one chain (say the upper) is a single edge, and
+* the other chain is concave.  The left vertex of the single edge
+* is always to the left of all vertices in the concave chain.
+*
+* Each step consists of adding the rightmost unprocessed vertex to one
+* of the two chains, and forming a fan of triangles from the rightmost
+* of two chain endpoints.  Determining whether we can add each triangle
+* to the fan is a simple orientation test.  By making the fan as large
+* as possible, we restore the invariant (check it yourself).
+*/
+int tessMeshTessellateMonoRegion( TESSmesh *mesh, TESSface *face )
+{
+	TESShalfEdge *up, *lo;
+
+	/* All edges are oriented CCW around the boundary of the region.
+	* First, find the half-edge whose origin vertex is rightmost.
+	* Since the sweep goes from left to right, face->anEdge should
+	* be close to the edge we want.
+	*/
+	up = face->anEdge;
+	assert( up->Lnext != up && up->Lnext->Lnext != up );
+
+	for( ; VertLeq( up->Dst, up->Org ); up = up->Lprev )
+		;
+	for( ; VertLeq( up->Org, up->Dst ); up = up->Lnext )
+		;
+	lo = up->Lprev;
+
+	while( up->Lnext != lo ) {
+		if( VertLeq( up->Dst, lo->Org )) {
+			/* up->Dst is on the left.  It is safe to form triangles from lo->Org.
+			* The EdgeGoesLeft test guarantees progress even when some triangles
+			* are CW, given that the upper and lower chains are truly monotone.
+			*/
+			while( lo->Lnext != up && (EdgeGoesLeft( lo->Lnext )
+				|| EdgeSign( lo->Org, lo->Dst, lo->Lnext->Dst ) <= 0 )) {
+					TESShalfEdge *tempHalfEdge= tessMeshConnect( mesh, lo->Lnext, lo );
+					if (tempHalfEdge == NULL) return 0;
+					lo = tempHalfEdge->Sym;
+			}
+			lo = lo->Lprev;
+		} else {
+			/* lo->Org is on the left.  We can make CCW triangles from up->Dst. */
+			while( lo->Lnext != up && (EdgeGoesRight( up->Lprev )
+				|| EdgeSign( up->Dst, up->Org, up->Lprev->Org ) >= 0 )) {
+					TESShalfEdge *tempHalfEdge= tessMeshConnect( mesh, up, up->Lprev );
+					if (tempHalfEdge == NULL) return 0;
+					up = tempHalfEdge->Sym;
+			}
+			up = up->Lnext;
+		}
+	}
+
+	/* Now lo->Org == up->Dst == the leftmost vertex.  The remaining region
+	* can be tessellated in a fan from this leftmost vertex.
+	*/
+	assert( lo->Lnext != up );
+	while( lo->Lnext->Lnext != up ) {
+		TESShalfEdge *tempHalfEdge= tessMeshConnect( mesh, lo->Lnext, lo );
+		if (tempHalfEdge == NULL) return 0;
+		lo = tempHalfEdge->Sym;
+	}
+
+	return 1;
+}
+
+
+/* tessMeshTessellateInterior( mesh ) tessellates each region of
+* the mesh which is marked "inside" the polygon.  Each such region
+* must be monotone.
+*/
+int tessMeshTessellateInterior( TESSmesh *mesh )
+{
+	TESSface *f, *next;
+
+	/*LINTED*/
+	for( f = mesh->fHead.next; f != &mesh->fHead; f = next ) {
+		/* Make sure we don''t try to tessellate the new triangles. */
+		next = f->next;
+		if( f->inside ) {
+			if ( !tessMeshTessellateMonoRegion( mesh, f ) ) return 0;
+		}
+	}
+
+	return 1;
+}
+
+
+/* tessMeshDiscardExterior( mesh ) zaps (ie. sets to NULL) all faces
+* which are not marked "inside" the polygon.  Since further mesh operations
+* on NULL faces are not allowed, the main purpose is to clean up the
+* mesh so that exterior loops are not represented in the data structure.
+*/
+void tessMeshDiscardExterior( TESSmesh *mesh )
+{
+	TESSface *f, *next;
+
+	/*LINTED*/
+	for( f = mesh->fHead.next; f != &mesh->fHead; f = next ) {
+		/* Since f will be destroyed, save its next pointer. */
+		next = f->next;
+		if( ! f->inside ) {
+			tessMeshZapFace( mesh, f );
+		}
+	}
+}
+
+/* tessMeshSetWindingNumber( mesh, value, keepOnlyBoundary ) resets the
+* winding numbers on all edges so that regions marked "inside" the
+* polygon have a winding number of "value", and regions outside
+* have a winding number of 0.
+*
+* If keepOnlyBoundary is TRUE, it also deletes all edges which do not
+* separate an interior region from an exterior one.
+*/
+int tessMeshSetWindingNumber( TESSmesh *mesh, int value,
+							 int keepOnlyBoundary )
+{
+	TESShalfEdge *e, *eNext;
+
+	for( e = mesh->eHead.next; e != &mesh->eHead; e = eNext ) {
+		eNext = e->next;
+		if( e->Rface->inside != e->Lface->inside ) {
+
+			/* This is a boundary edge (one side is interior, one is exterior). */
+			e->winding = (e->Lface->inside) ? value : -value;
+		} else {
+
+			/* Both regions are interior, or both are exterior. */
+			if( ! keepOnlyBoundary ) {
+				e->winding = 0;
+			} else {
+				if ( !tessMeshDelete( mesh, e ) ) return 0;
+			}
+		}
+	}
+	return 1;
+}
+
+void* heapAlloc( void* userData, unsigned int size )
+{
+	TESS_NOTUSED( userData );
+	return malloc( size );
+}
+
+void* heapRealloc( void *userData, void* ptr, unsigned int size )
+{
+	TESS_NOTUSED( userData );
+	return realloc( ptr, size );
+}
+
+void heapFree( void* userData, void* ptr )
+{
+	TESS_NOTUSED( userData );
+	free( ptr );
+}
+
+static TESSalloc defaulAlloc =
+{
+	heapAlloc,
+	heapRealloc,
+	heapFree,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+};
+
+TESStesselator* tessNewTess( TESSalloc* alloc )
+{
+	TESStesselator* tess;
+
+	if (alloc == NULL)
+		alloc = &defaulAlloc;
+
+	/* Only initialize fields which can be changed by the api.  Other fields
+	* are initialized where they are used.
+	*/
+
+	tess = (TESStesselator *)alloc->memalloc( alloc->userData, sizeof( TESStesselator ));
+	if ( tess == NULL ) {
+		return 0;          /* out of memory */
+	}
+	tess->alloc = *alloc;
+	/* Check and set defaults. */
+	if (tess->alloc.meshEdgeBucketSize == 0)
+		tess->alloc.meshEdgeBucketSize = 512;
+	if (tess->alloc.meshVertexBucketSize == 0)
+		tess->alloc.meshVertexBucketSize = 512;
+	if (tess->alloc.meshFaceBucketSize == 0)
+		tess->alloc.meshFaceBucketSize = 256;
+	if (tess->alloc.dictNodeBucketSize == 0)
+		tess->alloc.dictNodeBucketSize = 512;
+	if (tess->alloc.regionBucketSize == 0)
+		tess->alloc.regionBucketSize = 256;
+
+	tess->normal[0] = 0;
+	tess->normal[1] = 0;
+	tess->normal[2] = 0;
+
+	tess->bmin[0] = 0;
+	tess->bmin[1] = 0;
+	tess->bmax[0] = 0;
+	tess->bmax[1] = 0;
+
+	tess->windingRule = TESS_WINDING_ODD;
+
+	if (tess->alloc.regionBucketSize < 16)
+		tess->alloc.regionBucketSize = 16;
+	if (tess->alloc.regionBucketSize > 4096)
+		tess->alloc.regionBucketSize = 4096;
+	tess->regionPool = createBucketAlloc( &tess->alloc, "Regions",
+										 sizeof(ActiveRegion), tess->alloc.regionBucketSize );
+
+	// Initialize to begin polygon.
+	tess->mesh = NULL;
+
+	tess->outOfMemory = 0;
+	tess->vertexIndexCounter = 0;
+
+	tess->vertices = 0;
+	tess->vertexIndices = 0;
+	tess->vertexCount = 0;
+	tess->elements = 0;
+	tess->elementCount = 0;
+
+	return tess;
+}
+
+void tessDeleteTess( TESStesselator *tess )
+{
+
+	struct TESSalloc alloc = tess->alloc;
+
+	deleteBucketAlloc( tess->regionPool );
+
+	if( tess->mesh != NULL ) {
+		tessMeshDeleteMesh( &alloc, tess->mesh );
+		tess->mesh = NULL;
+	}
+	if (tess->vertices != NULL) {
+		alloc.memfree( alloc.userData, tess->vertices );
+		tess->vertices = 0;
+	}
+	if (tess->vertexIndices != NULL) {
+		alloc.memfree( alloc.userData, tess->vertexIndices );
+		tess->vertexIndices = 0;
+	}
+	if (tess->elements != NULL) {
+		alloc.memfree( alloc.userData, tess->elements );
+		tess->elements = 0;
+	}
+
+	alloc.memfree( alloc.userData, tess );
+}
+
+
+static TESSindex GetNeighbourFace(TESShalfEdge* edge)
+{
+	if (!edge->Rface)
+		return TESS_UNDEF;
+	if (!edge->Rface->inside)
+		return TESS_UNDEF;
+	return edge->Rface->n;
+}
+
+void OutputPolymesh( TESStesselator *tess, TESSmesh *mesh, int elementType, int polySize, int vertexSize )
+{
+	TESSvertex* v = 0;
+	TESSface* f = 0;
+	TESShalfEdge* edge = 0;
+	int maxFaceCount = 0;
+	int maxVertexCount = 0;
+	int faceVerts, i;
+	TESSindex *elements = 0;
+	TESSreal *vert;
+
+	// Assume that the input data is triangles now.
+	// Try to merge as many polygons as possible
+	if (polySize > 3)
+	{
+		if (!tessMeshMergeConvexFaces( mesh, polySize ))
+		{
+			tess->outOfMemory = 1;
+			return;
+		}
+	}
+
+	// Mark unused
+	for ( v = mesh->vHead.next; v != &mesh->vHead; v = v->next )
+		v->n = TESS_UNDEF;
+
+	// Create unique IDs for all vertices and faces.
+	for ( f = mesh->fHead.next; f != &mesh->fHead; f = f->next )
+	{
+		f->n = TESS_UNDEF;
+		if( !f->inside ) continue;
+
+		edge = f->anEdge;
+		faceVerts = 0;
+		do
+		{
+			v = edge->Org;
+			if ( v->n == TESS_UNDEF )
+			{
+				v->n = maxVertexCount;
+				maxVertexCount++;
+			}
+			faceVerts++;
+			edge = edge->Lnext;
+		}
+		while (edge != f->anEdge);
+
+		assert( faceVerts <= polySize );
+
+		f->n = maxFaceCount;
+		++maxFaceCount;
+	}
+
+	tess->elementCount = maxFaceCount;
+	if (elementType == TESS_CONNECTED_POLYGONS)
+		maxFaceCount *= 2;
+	tess->elements = (TESSindex*)tess->alloc.memalloc( tess->alloc.userData,
+													  sizeof(TESSindex) * maxFaceCount * polySize );
+	if (!tess->elements)
+	{
+		tess->outOfMemory = 1;
+		return;
+	}
+
+	tess->vertexCount = maxVertexCount;
+	tess->vertices = (TESSreal*)tess->alloc.memalloc( tess->alloc.userData,
+													 sizeof(TESSreal) * tess->vertexCount * vertexSize );
+	if (!tess->vertices)
+	{
+		tess->outOfMemory = 1;
+		return;
+	}
+
+	tess->vertexIndices = (TESSindex*)tess->alloc.memalloc( tess->alloc.userData,
+														    sizeof(TESSindex) * tess->vertexCount );
+	if (!tess->vertexIndices)
+	{
+		tess->outOfMemory = 1;
+		return;
+	}
+
+	// Output vertices.
+	for ( v = mesh->vHead.next; v != &mesh->vHead; v = v->next )
+	{
+		if ( v->n != TESS_UNDEF )
+		{
+			// Store coordinate
+			vert = &tess->vertices[v->n*vertexSize];
+			vert[0] = v->coords[0];
+			vert[1] = v->coords[1];
+			if ( vertexSize > 2 )
+				vert[2] = v->coords[2];
+			// Store vertex index.
+			tess->vertexIndices[v->n] = v->idx;
+		}
+	}
+
+	// Output indices.
+	elements = tess->elements;
+	for ( f = mesh->fHead.next; f != &mesh->fHead; f = f->next )
+	{
+		if ( !f->inside ) continue;
+
+		// Store polygon
+		edge = f->anEdge;
+		faceVerts = 0;
+		do
+		{
+			v = edge->Org;
+			*elements++ = v->n;
+			faceVerts++;
+			edge = edge->Lnext;
+		}
+		while (edge != f->anEdge);
+		// Fill unused.
+		for (i = faceVerts; i < polySize; ++i)
+			*elements++ = TESS_UNDEF;
+
+		// Store polygon connectivity
+		if ( elementType == TESS_CONNECTED_POLYGONS )
+		{
+			edge = f->anEdge;
+			do
+			{
+				*elements++ = GetNeighbourFace( edge );
+				edge = edge->Lnext;
+			}
+			while (edge != f->anEdge);
+			// Fill unused.
+			for (i = faceVerts; i < polySize; ++i)
+				*elements++ = TESS_UNDEF;
+		}
+	}
+}
+
+void OutputContours( TESStesselator *tess, TESSmesh *mesh, int vertexSize )
+{
+	TESSface *f = 0;
+	TESShalfEdge *edge = 0;
+	TESShalfEdge *start = 0;
+	TESSreal *verts = 0;
+	TESSindex *elements = 0;
+	TESSindex *vertInds = 0;
+	int startVert = 0;
+	int vertCount = 0;
+
+	tess->vertexCount = 0;
+	tess->elementCount = 0;
+
+	for ( f = mesh->fHead.next; f != &mesh->fHead; f = f->next )
+	{
+		if ( !f->inside ) continue;
+
+		start = edge = f->anEdge;
+		do
+		{
+			++tess->vertexCount;
+			edge = edge->Lnext;
+		}
+		while ( edge != start );
+
+		++tess->elementCount;
+	}
+
+	tess->elements = (TESSindex*)tess->alloc.memalloc( tess->alloc.userData,
+													  sizeof(TESSindex) * tess->elementCount * 2 );
+	if (!tess->elements)
+	{
+		tess->outOfMemory = 1;
+		return;
+	}
+
+	tess->vertices = (TESSreal*)tess->alloc.memalloc( tess->alloc.userData,
+													  sizeof(TESSreal) * tess->vertexCount * vertexSize );
+	if (!tess->vertices)
+	{
+		tess->outOfMemory = 1;
+		return;
+	}
+
+	tess->vertexIndices = (TESSindex*)tess->alloc.memalloc( tess->alloc.userData,
+														    sizeof(TESSindex) * tess->vertexCount );
+	if (!tess->vertexIndices)
+	{
+		tess->outOfMemory = 1;
+		return;
+	}
+
+	verts = tess->vertices;
+	elements = tess->elements;
+	vertInds = tess->vertexIndices;
+
+	startVert = 0;
+
+	for ( f = mesh->fHead.next; f != &mesh->fHead; f = f->next )
+	{
+		if ( !f->inside ) continue;
+
+		vertCount = 0;
+		start = edge = f->anEdge;
+		do
+		{
+			*verts++ = edge->Org->coords[0];
+			*verts++ = edge->Org->coords[1];
+			if ( vertexSize > 2 )
+				*verts++ = edge->Org->coords[2];
+			*vertInds++ = edge->Org->idx;
+			++vertCount;
+			edge = edge->Lnext;
+		}
+		while ( edge != start );
+
+		elements[0] = startVert;
+		elements[1] = vertCount;
+		elements += 2;
+
+		startVert += vertCount;
+	}
+}
+
+void tessAddContour( TESStesselator *tess, int size, const void* vertices,
+					int stride, int numVertices )
+{
+	const unsigned char *src = (const unsigned char*)vertices;
+	TESShalfEdge *e;
+	int i;
+
+	if ( tess->mesh == NULL )
+	  	tess->mesh = tessMeshNewMesh( &tess->alloc );
+ 	if ( tess->mesh == NULL ) {
+		tess->outOfMemory = 1;
+		return;
+	}
+
+	if ( size < 2 )
+		size = 2;
+	if ( size > 3 )
+		size = 3;
+
+	e = NULL;
+
+	for( i = 0; i < numVertices; ++i )
+	{
+		const TESSreal* coords = (const TESSreal*)src;
+		src += stride;
+
+		if( e == NULL ) {
+			/* Make a self-loop (one vertex, one edge). */
+			e = tessMeshMakeEdge( tess->mesh );
+			if ( e == NULL ) {
+				tess->outOfMemory = 1;
+				return;
+			}
+			if ( !tessMeshSplice( tess->mesh, e, e->Sym ) ) {
+				tess->outOfMemory = 1;
+				return;
+			}
+		} else {
+			/* Create a new vertex and edge which immediately follow e
+			* in the ordering around the left face.
+			*/
+			if ( tessMeshSplitEdge( tess->mesh, e ) == NULL ) {
+				tess->outOfMemory = 1;
+				return;
+			}
+			e = e->Lnext;
+		}
+
+		/* The new vertex is now e->Org. */
+		e->Org->coords[0] = coords[0];
+		e->Org->coords[1] = coords[1];
+		if ( size > 2 )
+			e->Org->coords[2] = coords[2];
+		else
+			e->Org->coords[2] = 0;
+		/* Store the insertion number so that the vertex can be later recognized. */
+		e->Org->idx = tess->vertexIndexCounter++;
+
+		/* The winding of an edge says how the winding number changes as we
+		* cross from the edge''s right face to its left face.  We add the
+		* vertices in such an order that a CCW contour will add +1 to
+		* the winding number of the region inside the contour.
+		*/
+		e->winding = 1;
+		e->Sym->winding = -1;
+	}
+}
+
+int tessTesselate( TESStesselator *tess, int windingRule, int elementType,
+				  int polySize, int vertexSize, const TESSreal* normal )
+{
+	TESSmesh *mesh;
+	int rc = 1;
+
+	if (tess->vertices != NULL) {
+		tess->alloc.memfree( tess->alloc.userData, tess->vertices );
+		tess->vertices = 0;
+	}
+	if (tess->elements != NULL) {
+		tess->alloc.memfree( tess->alloc.userData, tess->elements );
+		tess->elements = 0;
+	}
+	if (tess->vertexIndices != NULL) {
+		tess->alloc.memfree( tess->alloc.userData, tess->vertexIndices );
+		tess->vertexIndices = 0;
+	}
+
+	tess->vertexIndexCounter = 0;
+
+	if (normal)
+	{
+		tess->normal[0] = normal[0];
+		tess->normal[1] = normal[1];
+		tess->normal[2] = normal[2];
+	}
+
+	tess->windingRule = windingRule;
+
+	if (vertexSize < 2)
+		vertexSize = 2;
+	if (vertexSize > 3)
+		vertexSize = 3;
+
+	if (setjmp(tess->env) != 0) {
+		/* come back here if out of memory */
+		return 0;
+	}
+
+	if (!tess->mesh)
+	{
+		return 0;
+	}
+
+	/* Determine the polygon normal and project vertices onto the plane
+	* of the polygon.
+	*/
+	tessProjectPolygon( tess );
+
+	/* tessComputeInterior( tess ) computes the planar arrangement specified
+	* by the given contours, and further subdivides this arrangement
+	* into regions.  Each region is marked "inside" if it belongs
+	* to the polygon, according to the rule given by tess->windingRule.
+	* Each interior region is guaranteed be monotone.
+	*/
+	if ( !tessComputeInterior( tess ) ) {
+		longjmp(tess->env,1);  /* could've used a label */
+	}
+
+	mesh = tess->mesh;
+
+	/* If the user wants only the boundary contours, we throw away all edges
+	* except those which separate the interior from the exterior.
+	* Otherwise we tessellate all the regions marked "inside".
+	*/
+	if (elementType == TESS_BOUNDARY_CONTOURS) {
+		rc = tessMeshSetWindingNumber( mesh, 1, TRUE );
+	} else {
+		rc = tessMeshTessellateInterior( mesh );
+	}
+	if (rc == 0) longjmp(tess->env,1);  /* could've used a label */
+
+	tessMeshCheckMesh( mesh );
+
+	if (elementType == TESS_BOUNDARY_CONTOURS) {
+		OutputContours( tess, mesh, vertexSize );     /* output contours */
+	}
+	else
+	{
+		OutputPolymesh( tess, mesh, elementType, polySize, vertexSize );     /* output polygons */
+	}
+
+	tessMeshDeleteMesh( &tess->alloc, mesh );
+	tess->mesh = NULL;
+
+	if (tess->outOfMemory)
+		return 0;
+	return 1;
+}
+
+int tessGetVertexCount( TESStesselator *tess )
+{
+	return tess->vertexCount;
+}
+
+const TESSreal* tessGetVertices( TESStesselator *tess )
+{
+	return tess->vertices;
+}
+
+const TESSindex* tessGetVertexIndices( TESStesselator *tess )
+{
+	return tess->vertexIndices;
+}
+
+int tessGetElementCount( TESStesselator *tess )
+{
+	return tess->elementCount;
+}
+
+const int* tessGetElements( TESStesselator *tess )
+{
+	return tess->elements;
+}

+ 90 - 0
polygon.mod/earcut/test/comparison/libtess2/tess.h

@@ -0,0 +1,90 @@
+/*
+** SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
+** Copyright (C) [dates of first publication] Silicon Graphics, Inc.
+** All Rights Reserved.
+**
+** 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 including the dates of first publication and either this
+** permission notice or a reference to http://oss.sgi.com/projects/FreeB/ 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 SILICON GRAPHICS, INC.
+** 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.
+**
+** Except as contained in this notice, the name of Silicon Graphics, Inc. shall not
+** be used in advertising or otherwise to promote the sale, use or other dealings in
+** this Software without prior written authorization from Silicon Graphics, Inc.
+*/
+/*
+** Author: Eric Veach, July 1994.
+*/
+
+#ifndef TESS_H
+#define TESS_H
+
+#include <setjmp.h>
+#include "bucketalloc.h"
+#include "mesh.h"
+#include "dict.h"
+#include "priorityq.h"
+#include "tesselator.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//typedef struct TESStesselator TESStesselator;
+
+struct TESStesselator {
+
+	/*** state needed for collecting the input data ***/
+	TESSmesh	*mesh;		/* stores the input contours, and eventually
+						the tessellation itself */
+	int outOfMemory;
+
+	/*** state needed for projecting onto the sweep plane ***/
+
+	TESSreal normal[3];	/* user-specified normal (if provided) */
+	TESSreal sUnit[3];	/* unit vector in s-direction (debugging) */
+	TESSreal tUnit[3];	/* unit vector in t-direction (debugging) */
+
+	TESSreal bmin[2];
+	TESSreal bmax[2];
+
+	/*** state needed for the line sweep ***/
+	int	windingRule;	/* rule for determining polygon interior */
+
+	Dict *dict;		/* edge dictionary for sweep line */
+	PriorityQ *pq;		/* priority queue of vertex events */
+	TESSvertex *event;		/* current sweep event being processed */
+
+	struct BucketAlloc* regionPool;
+
+	TESSindex vertexIndexCounter;
+
+	TESSreal *vertices;
+	TESSindex *vertexIndices;
+	int vertexCount;
+	TESSindex *elements;
+	int elementCount;
+
+	TESSalloc alloc;
+
+	jmp_buf env;			/* place to jump to when memAllocs fail */
+};
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif

+ 221 - 0
polygon.mod/earcut/test/comparison/libtess2/tesselator.h

@@ -0,0 +1,221 @@
+/*
+** SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
+** Copyright (C) [dates of first publication] Silicon Graphics, Inc.
+** All Rights Reserved.
+**
+** 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 including the dates of first publication and either this
+** permission notice or a reference to http://oss.sgi.com/projects/FreeB/ 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 SILICON GRAPHICS, INC.
+** 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.
+**
+** Except as contained in this notice, the name of Silicon Graphics, Inc. shall not
+** be used in advertising or otherwise to promote the sale, use or other dealings in
+** this Software without prior written authorization from Silicon Graphics, Inc.
+*/
+/*
+** Author: Mikko Mononen, July 2009.
+*/
+
+#ifndef TESSELATOR_H
+#define TESSELATOR_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// See OpenGL Red Book for description of the winding rules
+// http://www.glprogramming.com/red/chapter11.html
+enum TessWindingRule
+{
+	TESS_WINDING_ODD,
+	TESS_WINDING_NONZERO,
+	TESS_WINDING_POSITIVE,
+	TESS_WINDING_NEGATIVE,
+	TESS_WINDING_ABS_GEQ_TWO,
+};
+
+// The contents of the tessGetElements() depends on element type being passed to tessTesselate().
+// Tesselation result element types:
+// TESS_POLYGONS
+//   Each element in the element array is polygon defined as 'polySize' number of vertex indices.
+//   If a polygon has than 'polySize' vertices, the remaining indices are stored as TESS_UNDEF.
+//   Example, drawing a polygon:
+//     const int nelems = tessGetElementCount(tess);
+//     const TESSindex* elems = tessGetElements(tess);
+//     for (int i = 0; i < nelems; i++) {
+//         const TESSindex* poly = &elems[i * polySize];
+//         glBegin(GL_POLYGON);
+//         for (int j = 0; j < polySize; j++) {
+//             if (poly[j] == TESS_UNDEF) break;
+//             glVertex2fv(&verts[poly[j]*vertexSize]);
+//         }
+//         glEnd();
+//     }
+//
+// TESS_CONNECTED_POLYGONS
+//   Each element in the element array is polygon defined as 'polySize' number of vertex indices,
+//   followed by 'polySize' indices to neighour polygons, that is each element is 'polySize' * 2 indices.
+//   If a polygon has than 'polySize' vertices, the remaining indices are stored as TESS_UNDEF.
+//   If a polygon edge is a boundary, that is, not connected to another polygon, the neighbour index is TESS_UNDEF.
+//   Example, flood fill based on seed polygon:
+//     const int nelems = tessGetElementCount(tess);
+//     const TESSindex* elems = tessGetElements(tess);
+//     unsigned char* visited = (unsigned char*)calloc(nelems);
+//     TESSindex stack[50];
+//     int nstack = 0;
+//     stack[nstack++] = seedPoly;
+//     visited[startPoly] = 1;
+//     while (nstack > 0) {
+//         TESSindex idx = stack[--nstack];
+//			const TESSindex* poly = &elems[idx * polySize * 2];
+//			const TESSindex* nei = &poly[polySize];
+//          for (int i = 0; i < polySize; i++) {
+//              if (poly[i] == TESS_UNDEF) break;
+//              if (nei[i] != TESS_UNDEF && !visited[nei[i]])
+//	                stack[nstack++] = nei[i];
+//                  visited[nei[i]] = 1;
+//              }
+//          }
+//     }
+//
+// TESS_BOUNDARY_CONTOURS
+//   Each element in the element array is [base index, count] pair defining a range of vertices for a contour.
+//   The first value is index to first vertex in contour and the second value is number of vertices in the contour.
+//   Example, drawing contours:
+//     const int nelems = tessGetElementCount(tess);
+//     const TESSindex* elems = tessGetElements(tess);
+//     for (int i = 0; i < nelems; i++) {
+//         const TESSindex base = elems[i * 2];
+//         const TESSindex count = elems[i * 2 + 1];
+//         glBegin(GL_LINE_LOOP);
+//         for (int j = 0; j < count; j++) {
+//             glVertex2fv(&verts[(base+j) * vertexSize]);
+//         }
+//         glEnd();
+//     }
+//
+enum TessElementType
+{
+	TESS_POLYGONS,
+	TESS_CONNECTED_POLYGONS,
+	TESS_BOUNDARY_CONTOURS,
+};
+
+typedef float TESSreal;
+typedef int TESSindex;
+typedef struct TESStesselator TESStesselator;
+typedef struct TESSalloc TESSalloc;
+
+#define TESS_UNDEF (~(TESSindex)0)
+
+#define TESS_NOTUSED(v) do { (void)(1 ? (void)0 : ( (void)(v) ) ); } while(0)
+
+// Custom memory allocator interface.
+// The internal memory allocator allocates mesh edges, vertices and faces
+// as well as dictionary nodes and active regions in buckets and uses simple
+// freelist to speed up the allocation. The bucket size should roughly match your
+// expected input data. For example if you process only hundreds of vertices,
+// a bucket size of 128 might be ok, where as when processing thousands of vertices
+// bucket size of 1024 might be approproate. The bucket size is a compromise between
+// how often to allocate memory from the system versus how much extra space the system
+// should allocate. Reasonable defaults are show in commects below, they will be used if
+// the bucket sizes are zero.
+//
+// The use may left the memrealloc to be null. In that case, the tesselator will not try to
+// dynamically grow int's internal arrays. The tesselator only needs the reallocation when it
+// has found intersecting segments and needs to add new vertex. This defency can be cured by
+// allocating some extra vertices beforehand. The 'extraVertices' variable allows to specify
+// number of expected extra vertices.
+struct TESSalloc
+{
+	void *(*memalloc)( void *userData, unsigned int size );
+	void *(*memrealloc)( void *userData, void* ptr, unsigned int size );
+	void (*memfree)( void *userData, void *ptr );
+	void* userData;				// User data passed to the allocator functions.
+	int meshEdgeBucketSize;		// 512
+	int meshVertexBucketSize;	// 512
+	int meshFaceBucketSize;		// 256
+	int dictNodeBucketSize;		// 512
+	int regionBucketSize;		// 256
+	int extraVertices;			// Number of extra vertices allocated for the priority queue.
+};
+
+
+//
+// Example use:
+//
+//
+//
+//
+
+// tessNewTess() - Creates a new tesselator.
+// Use tessDeleteTess() to delete the tesselator.
+// Parameters:
+//   alloc - pointer to a filled TESSalloc struct or NULL to use default malloc based allocator.
+// Returns:
+//   new tesselator object.
+TESStesselator* tessNewTess( TESSalloc* alloc );
+
+// tessDeleteTess() - Deletes a tesselator.
+// Parameters:
+//   tess - pointer to tesselator object to be deleted.
+void tessDeleteTess( TESStesselator *tess );
+
+// tessAddContour() - Adds a contour to be tesselated.
+// The type of the vertex coordinates is assumed to be TESSreal.
+// Parameters:
+//   tess - pointer to tesselator object.
+//   size - number of coordinates per vertex. Must be 2 or 3.
+//   pointer - pointer to the first coordinate of the first vertex in the array.
+//   stride - defines offset in bytes between consecutive vertices.
+//   count - number of vertices in contour.
+void tessAddContour( TESStesselator *tess, int size, const void* pointer, int stride, int count );
+
+// tessTesselate() - tesselate contours.
+// Parameters:
+//   tess - pointer to tesselator object.
+//   windingRule - winding rules used for tesselation, must be one of TessWindingRule.
+//   elementType - defines the tesselation result element type, must be one of TessElementType.
+//   polySize - defines maximum vertices per polygons if output is polygons.
+//   vertexSize - defines the number of coordinates in tesselation result vertex, must be 2 or 3.
+//   normal - defines the normal of the input contours, of null the normal is calculated automatically.
+// Returns:
+//   1 if succeed, 0 if failed.
+int tessTesselate( TESStesselator *tess, int windingRule, int elementType, int polySize, int vertexSize, const TESSreal* normal );
+
+// tessGetVertexCount() - Returns number of vertices in the tesselated output.
+int tessGetVertexCount( TESStesselator *tess );
+
+// tessGetVertices() - Returns pointer to first coordinate of first vertex.
+const TESSreal* tessGetVertices( TESStesselator *tess );
+
+// tessGetVertexIndices() - Returns pointer to first vertex index.
+// Vertex indices can be used to map the generated vertices to the original vertices.
+// Every point added using tessAddContour() will get a new index starting at 0.
+// New vertices generated at the intersections of segments are assigned value TESS_UNDEF.
+const TESSindex* tessGetVertexIndices( TESStesselator *tess );
+
+// tessGetElementCount() - Returns number of elements in the the tesselated output.
+int tessGetElementCount( TESStesselator *tess );
+
+// tessGetElements() - Returns pointer to the first element.
+const TESSindex* tessGetElements( TESStesselator *tess );
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif // TESSELATOR_H

+ 79 - 0
polygon.mod/earcut/test/convert_tests.js

@@ -0,0 +1,79 @@
+'use strict';
+/* jshint node: true */
+
+var fs = require('fs');
+var path = require('path');
+var earcut = require('../../earcut/src/earcut.js');
+
+var integerPolygons = '';
+var doublePolygons = '';
+
+var base = '../earcut/test/fixtures';
+fs.readdirSync(base).filter(function (name) {
+    return path.extname(name) === '.json';
+}).forEach(function (name) {
+    var json = JSON.parse(fs.readFileSync(path.join(base, name), 'utf-8'));
+    var data = earcut.flatten(json),
+        indices = earcut(data.vertices, data.holes, data.dimensions),
+        deviation = earcut.deviation(data.vertices, data.holes, data.dimensions, indices);
+
+    var id = path.basename(name, path.extname(name)).replace(/[^a-z0-9]+/g, '_');
+
+    var integer = true;
+    var short_integer = true;
+
+    function processPoint(p) {
+        if (integer && (p[0] % 1 !== 0 || p[1] % 1 !== 0)) {
+            integer = false;
+            short_integer = false;
+        }
+        if (short_integer && (p[0] < -32767 || p[0] > 32767 || p[1] < -32767 || p[1] > 32767)) {
+            short_integer = false;
+        }
+        return p.join(',');
+    }
+
+    var geometry = '';
+    for (var i = 0; i < json.length; i++) {
+        geometry += '    {{' + (json[i].map(processPoint).join('},{')) + '}},\n';
+    }
+
+    var className = "Fixture<double>"
+    if (short_integer) {
+        className = "Fixture<short>"
+    } else if (integer) {
+        className = "Fixture<int>"
+    }
+
+    var expectedTriangles = indices.length / 3;
+    var expectedDeviation = deviation;
+    expectedDeviation += 1e-14;
+    var libtessDeviationMap = {
+        "water": 0.00002,
+        "water_huge": 0.0002,
+        "water_huge2": 0.00015,
+        "bad_hole": 0.0022,
+        "issue16": 0.0255,
+        "self_touching": 0.002,
+        "simplified_us_border": 0.001,
+        "issue45": 0.094,
+        "empty_square": Infinity,
+        "issue83": Infinity,
+        "issue107": Infinity,
+        "issue119": 0.04,
+        "touching4": 0.06
+    };
+    var expectedLibtessDeviation = libtessDeviationMap[id];
+    if (!expectedLibtessDeviation) expectedLibtessDeviation = 0.000001;
+    var cpp = '// This file is auto-generated, manual changes will be lost if the code is regenerated.\n\n';
+    cpp += '#include "geometries.hpp"\n\n';
+    cpp += 'namespace mapbox {\n';
+    cpp += 'namespace fixtures {\n\n';
+    cpp += 'static const ' + className + ' ' + id + '("' + id + '", ' + expectedTriangles + ', ' + expectedDeviation + ', ' + expectedLibtessDeviation +', {\n';
+    cpp += geometry;
+    cpp += '});\n\n';
+    cpp += '}\n';
+    cpp += '}\n';
+
+    fs.writeFileSync('test/fixtures/' + id + '.cpp', cpp);
+});

+ 13 - 0
polygon.mod/earcut/test/fixtures/bad_diagonals.cpp

@@ -0,0 +1,13 @@
+// This file is auto-generated, manual changes will be lost if the code is regenerated.
+
+#include "geometries.hpp"
+
+namespace mapbox {
+namespace fixtures {
+
+static const Fixture<short> bad_diagonals("bad_diagonals", 7, 1e-14, 0.000001, {
+    {{440,4152},{440,4208},{296,4192},{368,4192},{400,4200},{400,4176},{368,4192},{296,4192},{264,4200},{288,4160},{296,4192}},
+});
+
+}
+}

+ 16 - 0
polygon.mod/earcut/test/fixtures/bad_hole.cpp

@@ -0,0 +1,16 @@
+// This file is auto-generated, manual changes will be lost if the code is regenerated.
+
+#include "geometries.hpp"
+
+namespace mapbox {
+namespace fixtures {
+
+static const Fixture<short> bad_hole("bad_hole", 42, 0.018674136321205143, 0.0022, {
+    {{810,2828},{818,2828},{832,2818},{844,2806},{855,2808},{866,2816},{867,2824},{876,2827},{883,2834},{875,2834},{867,2840},{878,2838},{889,2844},{880,2847},{870,2847},{860,2864},{852,2879},{847,2867},{810,2828},{810,2828}},
+    {{818,2834},{823,2833},{831,2828},{839,2829},{839,2837},{851,2845},{847,2835},{846,2827},{847,2827},{837,2827},{840,2815},{835,2823},{818,2834},{818,2834}},
+    {{857,2846},{864,2850},{866,2839},{857,2846},{857,2846}},
+    {{848,2863},{848,2866},{854,2852},{846,2854},{847,2862},{838,2851},{838,2859},{848,2863},{848,2863}},
+});
+
+}
+}

+ 17 - 0
polygon.mod/earcut/test/fixtures/boxy.cpp

@@ -0,0 +1,17 @@
+// This file is auto-generated, manual changes will be lost if the code is regenerated.
+
+#include "geometries.hpp"
+
+namespace mapbox {
+namespace fixtures {
+
+static const Fixture<short> boxy("boxy", 57, 1e-14, 0.000001, {
+    {{3432,2779},{3432,2794},{3450,2794},{3450,2825},{3413,2825},{3413,2856},{3395,2856},{3395,2871},{3377,2871},{3377,2856},{3359,2856},{3359,2840},{3341,2840},{3341,2871},{3322,2871},{3322,2887},{3249,2887},{3249,2871},{3268,2871},{3268,2840},{3304,2840},{3304,2825},{3322,2825},{3322,2810},{3304,2810},{3304,2794},{3322,2794},{3322,2779},{3341,2779},{3341,2733},{3359,2733},{3359,2687},{3395,2687},{3395,2702},{3432,2702},{3432,2717},{3450,2717},{3450,2733},{3486,2733},{3486,2748},{3468,2748},{3468,2763},{3450,2763},{3450,2779},{3432,2779}},
+    {{3359,2794},{3341,2794},{3341,2810},{3395,2810},{3395,2794},{3377,2794},{3377,2779},{3359,2779},{3359,2794}},
+    {{3432,2779},{3432,2748},{3413,2748},{3413,2779},{3432,2779}},
+    {{3377,2779},{3395,2779},{3395,2748},{3377,2748},{3377,2779}},
+    {{3377,2717},{3395,2717},{3395,2702},{3377,2702},{3377,2717}},
+});
+
+}
+}

+ 13 - 0
polygon.mod/earcut/test/fixtures/building.cpp

@@ -0,0 +1,13 @@
+// This file is auto-generated, manual changes will be lost if the code is regenerated.
+
+#include "geometries.hpp"
+
+namespace mapbox {
+namespace fixtures {
+
+static const Fixture<short> building("building", 13, 1e-14, 0.000001, {
+    {{661,112},{661,96},{666,96},{666,87},{743,87},{771,87},{771,114},{750,114},{750,113},{742,113},{742,106},{710,106},{710,113},{666,113},{666,112}},
+});
+
+}
+}

+ 13 - 0
polygon.mod/earcut/test/fixtures/collinear_diagonal.cpp

@@ -0,0 +1,13 @@
+// This file is auto-generated, manual changes will be lost if the code is regenerated.
+
+#include "geometries.hpp"
+
+namespace mapbox {
+namespace fixtures {
+
+static const Fixture<short> collinear_diagonal("collinear_diagonal", 14, 1e-14, 0.000001, {
+    {{3468,1913},{3486,1884},{3413,1869},{3322,1869},{3413,1854},{3413,1869},{3486,1869},{3486,1884},{3504,1884},{3504,1869},{3432,1869},{3432,1854},{3395,1854},{3432,1839},{3432,1854},{3450,1839},{3341,1839},{3341,1825},{3195,1825},{3341,1810},{3341,1825},{3450,1825},{3523,1854},{3523,1913}},
+});
+
+}
+}

+ 13 - 0
polygon.mod/earcut/test/fixtures/degenerate.cpp

@@ -0,0 +1,13 @@
+// This file is auto-generated, manual changes will be lost if the code is regenerated.
+
+#include "geometries.hpp"
+
+namespace mapbox {
+namespace fixtures {
+
+static const Fixture<short> degenerate("degenerate", 0, 1e-14, 0.000001, {
+    {{100,100},{100,100},{200,100},{200,200},{200,100},{0,100}},
+});
+
+}
+}

Разлика између датотеке није приказан због своје велике величине
+ 8 - 0
polygon.mod/earcut/test/fixtures/dude.cpp


+ 15 - 0
polygon.mod/earcut/test/fixtures/eberly_3.cpp

@@ -0,0 +1,15 @@
+// This file is auto-generated, manual changes will be lost if the code is regenerated.
+
+#include "geometries.hpp"
+
+namespace mapbox {
+namespace fixtures {
+
+static const Fixture<short> eberly_3("eberly_3", 73, 1e-14, 0.000001, {
+    {{2328,2408},{2328,2472},{2344,2472},{2344,2432},{2384,2448},{2384,2536},{2408,2552},{2448,2544},{2456,2560},{2496,2544},{2480,2624},{2456,2664},{2424,2680},{2400,2768},{2376,2768},{2368,2704},{2336,2704},{2264,2784},{2216,2784},{2200,2760},{2168,2760},{2152,2744},{2128,2744},{2128,2784},{2072,2768},{2032,2720},{2000,2720},{2000,2688},{1936,2696},{1920,2736},{1888,2728},{1896,2696},{1928,2688},{1928,2664},{1896,2664},{1896,2640},{1912,2632},{1872,2608},{1888,2576},{2056,2576},{2088,2600},{2184,2608},{2216,2632},{2256,2624},{2248,2600},{2216,2592},{2192,2560},{2120,2576},{2072,2544},{2096,2544},{2080,2520},{2080,2488},{2096,2480},{2080,2448},{2096,2432},{2176,2496},{2200,2488},{2224,2528},{2248,2528},{2240,2488},{2256,2472},{2280,2480},{2264,2416},{2272,2392},{2328,2408}},
+    {{2320,2608},{2304,2640},{2312,2664},{2360,2632},{2352,2608},{2320,2608}},
+    {{1912,2632},{1936,2632},{1936,2616},{1912,2608},{1912,2632}},
+});
+
+}
+}

Разлика између датотеке није приказан због своје велике величине
+ 8 - 0
polygon.mod/earcut/test/fixtures/eberly_6.cpp


+ 14 - 0
polygon.mod/earcut/test/fixtures/empty_square.cpp

@@ -0,0 +1,14 @@
+// This file is auto-generated, manual changes will be lost if the code is regenerated.
+
+#include "geometries.hpp"
+
+namespace mapbox {
+namespace fixtures {
+
+static const Fixture<short> empty_square("empty_square", 0, 1e-14, Infinity, {
+    {{0,0},{4000,0},{4000,4000},{0,4000}},
+    {{0,0},{4000,0},{4000,4000},{0,4000}},
+});
+
+}
+}

+ 17 - 0
polygon.mod/earcut/test/fixtures/filtered_bridge_jhl.cpp

@@ -0,0 +1,17 @@
+// This file is auto-generated, manual changes will be lost if the code is regenerated.
+
+#include "geometries.hpp"
+
+namespace mapbox {
+namespace fixtures {
+
+static const Fixture<short> filtered_bridge_jhl("filtered_bridge_jhl", 25, 1e-14, 0.000001, {
+    {{22,14},{17,12},{5,12},{0,12},{0,0},{22,0}},
+    {{9,4},{10,4},{10,3},{9,3}},
+    {{6,9},{7,9},{7,8},{6,8}},
+    {{7,10},{17,10},{17,5},{8,5}},
+    {{13,4},{14,4},{14,3},{13,3}},
+});
+
+}
+}

+ 106 - 0
polygon.mod/earcut/test/fixtures/geometries.hpp

@@ -0,0 +1,106 @@
+#pragma once
+
+#include <utility>
+#include <vector>
+#include <map>
+#include <string>
+#include <utility>
+#include <string>
+#include <iostream>
+#include <algorithm>
+#include <type_traits>
+
+#include "../comparison/earcut.hpp"
+#include "../comparison/libtess2.hpp"
+
+namespace mapbox {
+namespace fixtures {
+template <typename T> using Polygon = std::vector<std::vector<T>>;
+template <typename T> using Triangles = std::vector<T>;
+using DoublePoint = std::pair<double, double>;
+using DoubleTriangles = Triangles<DoublePoint>;
+using DoublePolygon = Polygon<DoublePoint>;
+const double Infinity = std::numeric_limits<double>::infinity();
+
+template<class T>
+class Collector {
+    std::vector<T> objects;
+    Collector<T>() = default;
+public:
+    static std::vector<T>& collection() {
+        static Collector singleton;
+        return singleton.objects;
+    }
+    static void add(T const& object) {
+        collection().push_back(object);
+    }
+    static void remove(T const& object) {
+        auto& objects = collection();
+        objects.erase(std::remove(objects.begin(), objects.end(), object), objects.end());
+    }
+};
+
+class FixtureTester {
+public:
+    struct TesselatorResult {
+        std::vector<std::array<double, 2>> const& vertices;
+        std::vector<uint32_t> const& indices;
+    };
+    const std::string name;
+    const std::size_t expectedTriangles;
+    const double expectedEarcutDeviation;
+    const double expectedLibtessDeviation;
+    FixtureTester(std::string testname, std::size_t triangles, double deviation, double libtessdeviation)
+    : name(std::move(testname)), expectedTriangles(triangles), expectedEarcutDeviation(deviation), expectedLibtessDeviation(libtessdeviation) {
+        Collector<FixtureTester*>::add(this);
+    }
+    virtual ~FixtureTester() {
+        Collector<FixtureTester*>::remove(this);
+    }
+    virtual TesselatorResult earcut() = 0;
+    virtual TesselatorResult libtess() = 0;
+    virtual DoublePolygon const& polygon() = 0;
+    static std::vector<FixtureTester*>& collection() {
+        auto& objects = Collector<FixtureTester*>::collection();
+        std::sort(objects.begin(), objects.end(), [](FixtureTester* a, FixtureTester* b) { return a->name < b->name; });
+        return objects;
+    }
+};
+
+template <class T>
+class Fixture : public FixtureTester {
+private:
+    Polygon<std::pair<T, T>> inputPolygon;
+    DoublePolygon doublePolygon;
+    EarcutTesselator<double, Polygon<std::pair<T, T>>> earcutTesselator;
+    Libtess2Tesselator<double, Polygon<std::pair<T, T>>> libtessTesselator;
+public:
+    Fixture<T>(std::string const& name, std::size_t expectedTriangles,
+        double expectedDeviation, double expectedLibtessDeviation, Polygon<std::pair<T, T>> const& p)
+        : FixtureTester(name, expectedTriangles, expectedDeviation, expectedLibtessDeviation),
+          inputPolygon(p), earcutTesselator(inputPolygon), libtessTesselator(inputPolygon) {
+        doublePolygon.reserve(inputPolygon.size());
+        for (auto& ring : inputPolygon) {
+            std::vector<std::pair<double, double>> r;
+            r.reserve(ring.size());
+            for (auto& point : ring) {
+                r.emplace_back(static_cast<double>(std::get<0>(point)), static_cast<double>(std::get<1>(point)));
+            }
+            doublePolygon.push_back(r);
+        }
+    }
+    TesselatorResult earcut() override {
+        earcutTesselator.run();
+        return { earcutTesselator.vertices(), earcutTesselator.indices() };
+    }
+    TesselatorResult libtess() override {
+        libtessTesselator.run();
+        return { libtessTesselator.vertices(), libtessTesselator.indices() };
+    }
+    DoublePolygon const& polygon() override {
+        return doublePolygon;
+    }
+};
+
+}
+}

Разлика између датотеке није приказан због своје велике величине
+ 8 - 0
polygon.mod/earcut/test/fixtures/hilbert.cpp


+ 14 - 0
polygon.mod/earcut/test/fixtures/hole_touching_outer.cpp

@@ -0,0 +1,14 @@
+// This file is auto-generated, manual changes will be lost if the code is regenerated.
+
+#include "geometries.hpp"
+
+namespace mapbox {
+namespace fixtures {
+
+static const Fixture<short> hole_touching_outer("hole_touching_outer", 77, 1e-14, 0.000001, {
+    {{-64,-64},{253,-64},{491,358},{697,298},{928,197},{929,505},{1346,507},{1347,303},{1771,306},{1770,512},{2191,509},{2198,933},{2621,932},{2623,1115},{2577,1120},{2494,1183},{2390,1329},{2326,1590},{2287,1678},{2286,1407},{2229,1407},{2182,1493},{2106,1494},{2068,1460},{2019,1460},{2016,1775},{1889,1923},{1953,1989},{2097,1866},{2198,1925},{2203,1973},{2311,1976},{2320,1831},{2352,1824},{2358,1797},{2378,1780},{3350,1782},{3307,2086},{3139,2088},{3143,2203},{3493,2205},{3543,2187},{3540,2260},{3661,2264},{3665,1906},{3630,1902},{3626,1784},{4160,1786},{4160,2631},{4076,2631},{4021,2683},{3930,2701},{3915,2693},{3898,2639},{2630,2630},{2635,3476},{2287,3478},{2118,3203},{2180,3145},{2327,3087},{2610,2643},{2613,2536},{2658,2495},{2650,2203},{1829,2189},{1732,2241},{1551,2245},{933,1183},{890,1152},{455,401},{398,412},{89,547},{-64,606},{-64,-64}},
+    {{1762,928},{1770,512},{1343,513},{1345,715},{931,719},{932,930},{1762,928}},
+});
+
+}
+}

+ 13 - 0
polygon.mod/earcut/test/fixtures/hourglass.cpp

@@ -0,0 +1,13 @@
+// This file is auto-generated, manual changes will be lost if the code is regenerated.
+
+#include "geometries.hpp"
+
+namespace mapbox {
+namespace fixtures {
+
+static const Fixture<short> hourglass("hourglass", 2, 1e-14, 0.000001, {
+    {{7,18},{7,15},{5,15},{7,13},{7,15},{17,17}},
+});
+
+}
+}

+ 14 - 0
polygon.mod/earcut/test/fixtures/infinite_loop_jhl.cpp

@@ -0,0 +1,14 @@
+// This file is auto-generated, manual changes will be lost if the code is regenerated.
+
+#include "geometries.hpp"
+
+namespace mapbox {
+namespace fixtures {
+
+static const Fixture<double> infinite_loop_jhl("infinite_loop_jhl", 0, 1e-14, Infinity, {
+    {{-1,2},{0,0},{2,-1}},
+    {{2,-1},{0,1e-28},{-1,2}},
+});
+
+}
+}

+ 14 - 0
polygon.mod/earcut/test/fixtures/issue107.cpp

@@ -0,0 +1,14 @@
+// This file is auto-generated, manual changes will be lost if the code is regenerated.
+
+#include "geometries.hpp"
+
+namespace mapbox {
+namespace fixtures {
+
+static const Fixture<double> issue107("issue107", 0, 1.00000000000001, Infinity, {
+    {{7.943741826741378,46.46223436733343},{7.943741826741378,46.43293749233343},{7.943741826741378,46.46223436733343}},
+    {{7.973038701741378,46.46223436733343},{8.002335576741377,46.46223436733343},{8.002335576741377,46.43293749233343},{8.031632451741377,46.43293749233343},{8.002335576741377,46.43293749233343},{8.002335576741377,46.46223436733343},{8.031632451741377,46.46223436733343},{8.031632451741377,46.49153124233343},{8.002335576741377,46.49153124233343},{8.002335576741377,46.46223436733343},{7.973038701741378,46.46223436733343}},
+});
+
+}
+}

+ 16 - 0
polygon.mod/earcut/test/fixtures/issue111.cpp

@@ -0,0 +1,16 @@
+// This file is auto-generated, manual changes will be lost if the code is regenerated.
+
+#include "geometries.hpp"
+
+namespace mapbox {
+namespace fixtures {
+
+static const Fixture<short> issue111("issue111", 19, 1e-14, 0.000001, {
+    {{800,4520},{800,4700},{796,4702},{800,4692},{734,4644},{734,4628},{730,4632},{726,4630},{718,4640},{690,4623},{722,4598},{690,4608},{690,4520},{800,4520}},
+    {{718,4640},{716,4630},{710,4628},{718,4640}},
+    {{734,4610},{734,4628},{740,4622},{734,4610}},
+    {{734,4610},{745,4600},{734,4602},{734,4610}},
+});
+
+}
+}

+ 17 - 0
polygon.mod/earcut/test/fixtures/issue119.cpp

@@ -0,0 +1,17 @@
+// This file is auto-generated, manual changes will be lost if the code is regenerated.
+
+#include "geometries.hpp"
+
+namespace mapbox {
+namespace fixtures {
+
+static const Fixture<short> issue119("issue119", 18, 1e-14, 0.04, {
+    {{2,12},{2,20},{25,20},{25,12}},
+    {{7,18},{7,15},{5,15}},
+    {{19,18},{19,17},{17,17}},
+    {{19,17},{21,17},{19,16}},
+    {{7,15},{9,15},{7,13}},
+});
+
+}
+}

+ 15 - 0
polygon.mod/earcut/test/fixtures/issue131.cpp

@@ -0,0 +1,15 @@
+// This file is auto-generated, manual changes will be lost if the code is regenerated.
+
+#include "geometries.hpp"
+
+namespace mapbox {
+namespace fixtures {
+
+static const Fixture<short> issue131("issue131", 12, 1e-14, 0.000001, {
+    {{3506,-2048},{7464,402},{-2048,2685},{-2048,-2048},{3506,-2048}},
+    {{-2048,-37},{1235,747},{338,-1464},{-116,-1188},{-2048,-381},{-2048,-37}},
+    {{-1491,-1981},{-1300,-1800},{-1155,-1981},{-1491,-1981}},
+});
+
+}
+}

+ 14 - 0
polygon.mod/earcut/test/fixtures/issue135.cpp

@@ -0,0 +1,14 @@
+// This file is auto-generated, manual changes will be lost if the code is regenerated.
+
+#include "geometries.hpp"
+
+namespace mapbox {
+namespace fixtures {
+
+static const Fixture<short> issue135("issue135", 1, Infinity, Infinity, {
+    {{1,2},{2,2},{1,2},{1,1}},
+    {{4,1},{5,1},{3,2},{4,2}},
+});
+
+}
+}

+ 14 - 0
polygon.mod/earcut/test/fixtures/issue142.cpp

@@ -0,0 +1,14 @@
+// This file is auto-generated, manual changes will be lost if the code is regenerated.
+
+#include "geometries.hpp"
+
+namespace mapbox {
+namespace fixtures {
+
+static const Fixture<double> issue142("issue142", 4, 0.12754968037146489, 8.53067013, {
+    {{5.62675358811389,31.94879819160804},{-16.369709114391867,28.341954255099814},{-10.786562672455382,-1.2779295357476745},{10.819423740334923,2.069348113719755}},
+    {{3.220439475288522,4.197526331591453},{5.024815373142793,1.1716264034331543},{10.819423740334923,2.069348113719755},{5.62675358811389,31.94879819160804},{-16.369709114391867,28.341954255099814},{-10.786562672455382,-1.2779295357476745},{-6.833718161055838,-0.6655405509524673},{-8.602352370111433,2.142874784407777},{-5.34630560403934,6.768689248602321},{-1.4053749889060216,7.453573097663546}},
+});
+
+}
+}

+ 14 - 0
polygon.mod/earcut/test/fixtures/issue149.cpp

@@ -0,0 +1,14 @@
+// This file is auto-generated, manual changes will be lost if the code is regenerated.
+
+#include "geometries.hpp"
+
+namespace mapbox {
+namespace fixtures {
+
+static const Fixture<short> issue149("issue149", 2, 1e-14, 0.000001, {
+    {{1888,5504},{1872,5504},{1872,5536},{1856,5536},{1856,5520},{1840,5520},{1840,5504},{1856,5504},{1856,5520},{1872,5520},{1872,5504},{1888,5504}},
+    {{1856,5520},{1856,5536},{1872,5536},{1872,5520}},
+});
+
+}
+}

+ 14 - 0
polygon.mod/earcut/test/fixtures/issue16.cpp

@@ -0,0 +1,14 @@
+// This file is auto-generated, manual changes will be lost if the code is regenerated.
+
+#include "geometries.hpp"
+
+namespace mapbox {
+namespace fixtures {
+
+static const Fixture<double> issue16("issue16", 12, 1e-14, 0.0255, {
+    {{143.12952728374512,61.24016082659364},{147.39952728376375,74.78016082663089},{154.04952728375793,90.26016082707793},{174.42952728376258,81.71016082633287},{168.0395272837486,67.04016082640737},{159.09952728374628,53.590160826221116}},
+    {{156.8595272837556,67.43016082700342},{157.48952728376025,67.16016082651913},{159.96952728374163,68.35016082692891},{161.33952728376607,67.64016082696617},{159.64952728376375,63.31016082689166},{155.75952728374978,64.88016082625836}},
+});
+
+}
+}

+ 14 - 0
polygon.mod/earcut/test/fixtures/issue17.cpp

@@ -0,0 +1,14 @@
+// This file is auto-generated, manual changes will be lost if the code is regenerated.
+
+#include "geometries.hpp"
+
+namespace mapbox {
+namespace fixtures {
+
+static const Fixture<double> issue17("issue17", 11, 1.0156177186728038e-14, 0.000001, {
+    {{-20037508.34,19971868.877628453},{-20037508.34,-19971868.877628453},{20037508.34,-19971868.877628453},{20037508.34,19971868.877628453}},
+    {{537637.6007702783,5907542.234420554},{539500.1483225027,5905165.501947839},{538610.3146341922,5905217.430281373},{538040.6306361248,5906132.0755739985},{538068.958329954,5906571.138846622},{537711.0379352621,5906645.06648362},{537629.886026485,5907533.69114742}},
+});
+
+}
+}

+ 14 - 0
polygon.mod/earcut/test/fixtures/issue29.cpp

@@ -0,0 +1,14 @@
+// This file is auto-generated, manual changes will be lost if the code is regenerated.
+
+#include "geometries.hpp"
+
+namespace mapbox {
+namespace fixtures {
+
+static const Fixture<double> issue29("issue29", 40, 1.0364030632684926e-14, 0.000001, {
+    {{200.95000055654114,69.90565782485673},{201.73186418809928,76.50881049432792},{204.05247248713854,82.73234962527638},{207.79442497901618,88.23839184455574},{212.7323883100471,92.70045673093409},{218.58296075442922,95.86217257360113},{225.00460918453172,97.53918036725955},{231.66534446463922,97.66082593216561},{238.15796607054654,96.21973398409901},{244.1176358256489,93.27806596420706},{249.2188404462824,88.99822680730722},{253.15628113771672,83.64108043884043},{255.70631344406866,77.51111824424007},{256.73126424155197,70.93641692795792},{256.19351709797047,64.30797780468129},{254.1057433114911,57.996416078653425},{250.56431880965246,52.346517799043795},{245.8112865351897,47.719993951247304},{240.07834375849924,44.33761266223155},{233.71343464441597,42.419284673407674},{227.06488359675492,42.055728640102465},{220.51757991796475,43.257153422775446},{214.45449861431845,45.97523169373744},{209.20995664413203,50.053084840223896},{205.06721924245355,55.271000209450726},{202.29122001552022,61.30178454495035},{201.02451470680535,67.8368895214051}},
+    {{242.34999892718187,69.90549289577612},{240.7584948063828,76.30057721128688},{236.31611852571368,81.17358751371503},{230.07699953842675,83.34595728587593},{223.55761859836056,82.33733346881347},{218.2910646148026,78.34856240227819},{215.5668820463121,72.34290095195175},{215.9904494531453,65.75019118711353},{219.47497291108593,60.1536534355022},{225.2189893186092,56.88651757836341},{231.8100271829404,56.72041164720431},{237.70269737243652,59.67713584899902},{241.47838292121884,65.0856644153595}},
+});
+
+}
+}

+ 20 - 0
polygon.mod/earcut/test/fixtures/issue34.cpp

@@ -0,0 +1,20 @@
+// This file is auto-generated, manual changes will be lost if the code is regenerated.
+
+#include "geometries.hpp"
+
+namespace mapbox {
+namespace fixtures {
+
+static const Fixture<short> issue34("issue34", 139, 1e-14, 0.000001, {
+    {{1500,0},{0,0},{0,1000},{1500,1000},{1500,0}},
+    {{804,642},{814,644},{818,676},{850,690},{838,728},{806,728},{772,752},{748,746},{764,724},{728,726},{710,708},{738,656},{764,668},{784,700},{806,702},{792,666},{804,642}},
+    {{1176,214},{1254,216},{1292,242},{1324,242},{1332,268},{1352,278},{1352,298},{1290,348},{1290,358},{1312,350},{1314,362},{1266,416},{1240,474},{1182,500},{1200,510},{1200,520},{1186,520},{1200,544},{1186,580},{1160,584},{1162,606},{1146,620},{1162,650},{1136,672},{1124,658},{1076,668},{1022,658},{1036,698},{1066,706},{1118,688},{1144,708},{1132,746},{1064,748},{1004,740},{990,668},{966,670},{946,648},{948,632},{962,628},{992,650},{1016,648},{1054,622},{1044,592},{1054,584},{1078,606},{1076,576},{1052,570},{1056,540},{1038,568},{1004,570},{976,526},{996,502},{958,496},{948,454},{962,454},{952,436},{964,390},{986,382},{974,368},{1004,376},{1018,420},{1052,434},{1060,482},{1078,490},{1062,472},{1062,442},{1104,450},{1104,436},{1142,422},{1154,402},{1110,424},{1046,416},{1022,388},{1022,344},{1002,344},{1018,318},{1060,308},{1076,272},{1104,288},{1122,246},{1140,230},{1168,234},{1176,214}},
+    {{974,698},{986,738},{964,740},{952,714},{974,698}},
+    {{842,596},{860,626},{848,622},{842,596}},
+    {{798,572},{792,606},{768,614},{740,580},{758,586},{798,572}},
+    {{892,584},{894,594},{882,588},{892,584}},
+    {{870,500},{912,538},{922,586},{908,590},{894,568},{864,564},{854,550},{868,538},{846,520},{854,500},{870,500}},
+});
+
+}
+}

Разлика између датотеке није приказан због своје велике величине
+ 8 - 0
polygon.mod/earcut/test/fixtures/issue35.cpp


+ 15 - 0
polygon.mod/earcut/test/fixtures/issue45.cpp

@@ -0,0 +1,15 @@
+// This file is auto-generated, manual changes will be lost if the code is regenerated.
+
+#include "geometries.hpp"
+
+namespace mapbox {
+namespace fixtures {
+
+static const Fixture<short> issue45("issue45", 10, 1e-14, 0.094, {
+    {{10,10},{25,10},{25,40},{10,40}},
+    {{15,30},{20,35},{10,40}},
+    {{15,15},{15,20},{20,15}},
+});
+
+}
+}

+ 18 - 0
polygon.mod/earcut/test/fixtures/issue52.cpp

@@ -0,0 +1,18 @@
+// This file is auto-generated, manual changes will be lost if the code is regenerated.
+
+#include "geometries.hpp"
+
+namespace mapbox {
+namespace fixtures {
+
+static const Fixture<short> issue52("issue52", 109, 1e-14, 0.000001, {
+    {{1920,552},{1904,616},{1912,664},{1984,672},{2008,712},{1944,720},{1904,760},{1896,800},{1856,760},{1824,768},{1824,832},{1864,864},{1888,864},{1904,936},{1936,944},{1936,1064},{1936,1112},{1872,1136},{1856,1160},{1840,1144},{1792,1152},{1784,1112},{1752,1096},{1608,1096},{1600,1064},{1640,1040},{1664,992},{1640,968},{1568,1024},{1560,1056},{1480,1048},{1440,1072},{1440,1032},{1400,1032},{1400,1088},{1336,1136},{1320,1136},{1264,1072},{1232,1080},{1240,1104},{1200,1096},{1232,1048},{1272,1032},{1272,1000},{1232,1024},{1176,1024},{1176,1000},{1248,952},{1344,944},{1352,904},{1424,880},{1448,848},{1496,840},{1512,800},{1568,760},{1616,752},{1640,640},{1680,600},{1736,592},{1776,560},{1776,536},{1840,464},{1848,400},{1888,328},{1952,264},{2000,240},{2040,240},{2040,264},{1968,376},{1912,424},{1936,512},{1920,528},{1880,528},{1872,552},{1920,552}},
+    {{1608,800},{1576,848},{1520,840},{1512,872},{1456,904},{1440,952},{1528,936},{1552,912},{1584,912},{1608,880},{1664,864},{1680,816},{1656,776},{1608,800}},
+    {{1720,792},{1736,792},{1720,780},{1720,792}},
+    {{1656,728},{1670,752},{1672,728},{1656,728}},
+    {{1712,680},{1696,720},{1720,728},{1736,704},{1736,680},{1712,680}},
+    {{1968,712},{2000,712},{1968,688},{1968,712}},
+});
+
+}
+}

+ 15 - 0
polygon.mod/earcut/test/fixtures/issue83.cpp

@@ -0,0 +1,15 @@
+// This file is auto-generated, manual changes will be lost if the code is regenerated.
+
+#include "geometries.hpp"
+
+namespace mapbox {
+namespace fixtures {
+
+static const Fixture<short> issue83("issue83", 0, 1e-14, Infinity, {
+    {{0,0},{4000,0},{4000,4000},{0,4000}},
+    {{0,0},{4000,0},{4000,4000},{0,4000}},
+    {{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1}},
+});
+
+}
+}

+ 15 - 0
polygon.mod/earcut/test/fixtures/outside_ring.cpp

@@ -0,0 +1,15 @@
+// This file is auto-generated, manual changes will be lost if the code is regenerated.
+
+#include "geometries.hpp"
+
+namespace mapbox {
+namespace fixtures {
+
+static const Fixture<short> outside_ring("outside_ring", 64, 1e-14, 0.000001, {
+    {{2181,1228},{2182,1231},{2178,1231},{2180,1228},{2175,1225},{2174,1212},{2182,1210},{2182,1193},{2190,1187},{2187,1166},{2194,1158},{2186,1149},{2186,1103},{2195,1091},{2207,1092},{2209,1080},{2203,1077},{2213,1057},{2213,1035},{2224,1031},{2238,983},{2251,982},{2254,965},{2275,970},{2277,948},{2317,982},{2317,1030},{2323,1044},{2306,1041},{2303,1051},{2290,1057},{2294,1062},{2287,1071},{2294,1081},{2255,1123},{2249,1118},{2253,1128},{2245,1131},{2249,1137},{2243,1168},{2265,1195},{2253,1203},{2260,1204},{2252,1215},{2249,1208},{2245,1217},{2232,1220},{2241,1223},{2235,1223},{2238,1245},{2229,1274},{2215,1272},{2209,1288},{2196,1288},{2190,1269},{2194,1271},{2195,1262},{2181,1240},{2182,1233},{2183,1229},{2181,1228}},
+    {{2181,1228},{2181,1227},{2180,1228},{2181,1228}},
+    {{2246,1197},{2230,1201},{2251,1203},{2246,1197}},
+});
+
+}
+}

Разлика између датотеке није приказан због своје велике величине
+ 8 - 0
polygon.mod/earcut/test/fixtures/rain.cpp


Разлика између датотеке није приказан због своје велике величине
+ 8 - 0
polygon.mod/earcut/test/fixtures/self_touching.cpp


+ 13 - 0
polygon.mod/earcut/test/fixtures/shared_points.cpp

@@ -0,0 +1,13 @@
+// This file is auto-generated, manual changes will be lost if the code is regenerated.
+
+#include "geometries.hpp"
+
+namespace mapbox {
+namespace fixtures {
+
+static const Fixture<short> shared_points("shared_points", 4, 1e-14, 0.000001, {
+    {{4136,1016},{4112,1016},{4104,976},{4136,1016},{4144,984},{4104,976},{4144,968},{4144,984},{4168,992},{4152,1064}},
+});
+
+}
+}

+ 14 - 0
polygon.mod/earcut/test/fixtures/simplified_us_border.cpp

@@ -0,0 +1,14 @@
+// This file is auto-generated, manual changes will be lost if the code is regenerated.
+
+#include "geometries.hpp"
+
+namespace mapbox {
+namespace fixtures {
+
+static const Fixture<short> simplified_us_border("simplified_us_border", 120, 1e-14, 0.001, {
+    {{1130,1713},{1131,1710},{1137,1731},{1133,1752},{1125,1753},{1118,1742},{1110,1717},{1105,1718},{1108,1704},{1096,1691},{1077,1694},{1067,1683},{1019,1687},{1031,1689},{1031,1704},{1022,1696},{1022,1702},{1010,1700},{1003,1692},{998,1696},{980,1690},{970,1698},{966,1694},{966,1702},{938,1718},{943,1742},{920,1736},{916,1721},{894,1693},{884,1691},{872,1703},{837,1667},{785,1672},{743,1654},{715,1656},{699,1636},{676,1628},{654,1587},{656,1583},{660,1588},{657,1579},{649,1580},{633,1547},{637,1529},{631,1507},{638,1454},{647,1454},{637,1452},{639,1441},{635,1442},{629,1417},{651,1421},{647,1434},{655,1428},{650,1440},{656,1434},{654,1423},{651,1420},{653,1419},{651,1407},{965,1407},{966,1400},{972,1411},{1008,1423},{1043,1419},{1083,1442},{1086,1450},{1091,1448},{1109,1468},{1114,1496},{1102,1520},{1107,1525},{1149,1508},{1147,1498},{1152,1495},{1174,1495},{1195,1474},{1242,1470},{1260,1433},{1277,1440},{1277,1462},{1286,1476},{1274,1484},{1265,1480},{1243,1503},{1240,1516},{1252,1526},{1238,1529},{1236,1523},{1234,1530},{1218,1531},{1206,1540},{1205,1554},{1195,1567},{1188,1556},{1194,1574},{1185,1590},{1187,1581},{1179,1567},{1185,1557},{1176,1562},{1180,1579},{1179,1585},{1170,1577},{1180,1593},{1169,1590},{1183,1596},{1186,1607},{1175,1605},{1183,1613},{1182,1618},{1171,1615},{1179,1624},{1167,1626},{1145,1650},{1132,1659},{1128,1656},{1121,1675},{1131,1708},{1129,1710},{1130,1713}},
+    {{654,1419},{653,1419},{654,1423},{656,1425},{654,1419}},
+});
+
+}
+}

+ 17 - 0
polygon.mod/earcut/test/fixtures/steiner.cpp

@@ -0,0 +1,17 @@
+// This file is auto-generated, manual changes will be lost if the code is regenerated.
+
+#include "geometries.hpp"
+
+namespace mapbox {
+namespace fixtures {
+
+static const Fixture<short> steiner("steiner", 9, 1e-14, 0.000001, {
+    {{0,0},{100,0},{100,100},{0,100}},
+    {{50,50}},
+    {{30,40}},
+    {{70,60}},
+    {{20,70}},
+});
+
+}
+}

+ 14 - 0
polygon.mod/earcut/test/fixtures/touching2.cpp

@@ -0,0 +1,14 @@
+// This file is auto-generated, manual changes will be lost if the code is regenerated.
+
+#include "geometries.hpp"
+
+namespace mapbox {
+namespace fixtures {
+
+static const Fixture<short> touching2("touching2", 8, 1e-14, 0.000001, {
+    {{120,2031},{92,2368},{94,2200},{33,2119},{42,2112},{53,2068}},
+    {{44,2104},{79,2132},{88,2115},{44,2104}},
+});
+
+}
+}

+ 15 - 0
polygon.mod/earcut/test/fixtures/touching3.cpp

@@ -0,0 +1,15 @@
+// This file is auto-generated, manual changes will be lost if the code is regenerated.
+
+#include "geometries.hpp"
+
+namespace mapbox {
+namespace fixtures {
+
+static const Fixture<short> touching3("touching3", 15, 1e-14, 0.000001, {
+    {{1241,887},{1257,891},{1248,904},{1232,911},{1212,911},{1207,911},{1209,900},{1219,898},{1225,907},{1241,887}},
+    {{1212,902},{1212,911},{1219,909},{1212,902}},
+    {{1248,891},{1239,896},{1246,898},{1248,891}},
+});
+
+}
+}

+ 17 - 0
polygon.mod/earcut/test/fixtures/touching4.cpp

@@ -0,0 +1,17 @@
+// This file is auto-generated, manual changes will be lost if the code is regenerated.
+
+#include "geometries.hpp"
+
+namespace mapbox {
+namespace fixtures {
+
+static const Fixture<short> touching4("touching4", 20, 1e-14, 0.06, {
+    {{11,10},{0,10},{0,0},{11,0}},
+    {{7,6},{7,9},{10,9}},
+    {{7,5},{10,2},{10,5}},
+    {{6,9},{1,4},{1,9}},
+    {{1,1},{1,4},{4,1}},
+});
+
+}
+}

+ 20 - 0
polygon.mod/earcut/test/fixtures/touching_holes.cpp

@@ -0,0 +1,20 @@
+// This file is auto-generated, manual changes will be lost if the code is regenerated.
+
+#include "geometries.hpp"
+
+namespace mapbox {
+namespace fixtures {
+
+static const Fixture<short> touching_holes("touching_holes", 57, 1e-14, 0.000001, {
+    {{3694,2061},{3794,2035},{3812,2123},{3784,2123},{3708,2139},{3694,2061}},
+    {{3752,2109},{3740,2102},{3712,2109},{3715,2125},{3723,2128},{3740,2124},{3742,2112},{3752,2109}},
+    {{3797,2101},{3787,2096},{3780,2106},{3788,2114},{3797,2101}},
+    {{3734,2099},{3732,2091},{3719,2094},{3721,2102},{3734,2099}},
+    {{3777,2082},{3774,2071},{3772,2086},{3765,2091},{3748,2088},{3749,2062},{3738,2081},{3745,2095},{3761,2099},{3777,2082}},
+    {{3719,2079},{3712,2079},{3706,2091},{3712,2097},{3721,2080},{3719,2079}},
+    {{3773,2067},{3761,2053},{3753,2061},{3753,2071},{3756,2075},{3773,2067}},
+    {{3708,2079},{3712,2079},{3714,2076},{3719,2079},{3722,2079},{3718,2088},{3723,2089},{3734,2075},{3730,2068},{3717,2065},{3708,2079}},
+});
+
+}
+}

Разлика између датотеке није приказан због своје велике величине
+ 8 - 0
polygon.mod/earcut/test/fixtures/water.cpp


Разлика између датотеке није приказан због своје велике величине
+ 8 - 0
polygon.mod/earcut/test/fixtures/water2.cpp


+ 18 - 0
polygon.mod/earcut/test/fixtures/water3.cpp

@@ -0,0 +1,18 @@
+// This file is auto-generated, manual changes will be lost if the code is regenerated.
+
+#include "geometries.hpp"
+
+namespace mapbox {
+namespace fixtures {
+
+static const Fixture<short> water3("water3", 197, 1e-14, 0.000001, {
+    {{-128,4224},{-128,-128},{4224,-128},{4224,4224},{-128,4224}},
+    {{3030,-21},{3019,7},{3025,21},{3045,65},{3054,114},{3041,189},{3000,219},{3017,257},{2966,338},{2938,340},{2934,541},{2973,618},{2979,752},{3026,803},{3052,938},{3083,1030},{3034,1175},{3041,1264},{3083,1311},{3088,1348},{3067,1399},{3139,1435},{3209,1412},{3220,1378},{3242,1316},{3276,1335},{3314,1367},{3369,1529},{3436,1563},{3464,1681},{3512,1732},{3521,1811},{3508,1883},{3591,1939},{3724,2088},{3828,2171},{3867,2238},{3905,2344},{3939,2443},{3892,2498},{3889,2521},{3884,2560},{3942,2624},{3986,2681},{3999,2831},{4089,3008},{4117,3130},{4104,3172},{4023,3205},{3969,3283},{3991,3347},{4091,3365},{4146,3411},{4136,3456},{4068,3467},{3999,3412},{3978,3373},{3935,3350},{3937,3401},{4001,3465},{4036,3503},{3941,3459},{3907,3533},{3939,3655},{3867,3574},{3867,3663},{3796,3614},{3773,3730},{3880,4118},{3854,4159},{3891,4224},{4084,4224},{4106,4103},{4213,4062},{4224,4060},{4224,2439},{4155,2244},{4020,2282},{3978,2233},{3976,2146},{3927,2103},{3937,2060},{3910,1930},{3933,1862},{3893,1781},{3850,1774},{3803,1712},{3824,1651},{3794,1572},{3820,1535},{3835,1493},{3920,1550},{3957,1520},{3948,1444},{3973,1459},{3982,1508},{4016,1520},{4012,1471},{4042,1426},{4068,1422},{4198,1390},{4224,1368},{4224,969},{4214,1019},{4177,1095},{4142,1070},{4176,978},{4202,843},{4213,653},{4181,656},{4181,804},{4155,796},{4138,673},{4114,600},{4050,622},{4050,573},{4100,551},{4050,379},{4015,274},{3986,208},{3972,175},{3965,158},{3953,131},{3921,99},{3885,60},{3853,29},{3807,-4},{3755,-27},{3692,-53},{3639,-58},{3595,-34},{3573,-11},{3519,-4},{3488,17},{3467,6},{3426,4},{3384,25},{3308,28},{3263,-93},{3126,-83},{3041,-46},{3030,-21},{3030,-21}},
+    {{3832,-21},{3840,-17},{3877,21},{3895,39},{3961,-21},{3893,-98},{3855,-128},{3688,-128},{3742,-81},{3793,-41},{3832,-21},{3832,-21}},
+    {{4205,596},{4224,572},{4224,248},{4166,163},{4119,50},{4020,36},{4004,21},{3969,21},{3936,62},{3982,117},{4088,293},{4152,419},{4185,544},{4205,596},{4205,596}},
+    {{3228,2459},{3243,2459},{3248,2434},{3273,2429},{3218,2186},{3255,2102},{3285,2094},{3314,1972},{3198,1969},{3192,1943},{3223,1943},{3222,1913},{3177,1928},{3187,1979},{3180,2171},{3134,2187},{3212,2399},{3243,2398},{3228,2459},{3228,2459}},
+    {{4224,1574},{4212,1572},{4175,1600},{4152,1647},{4131,1689},{4106,1736},{4101,1785},{4115,1851},{4149,1885},{4169,1920},{4204,1908},{4224,1875},{4214,1844},{4199,1798},{4215,1763},{4224,1767},{4224,1574}},
+});
+
+}
+}

+ 15 - 0
polygon.mod/earcut/test/fixtures/water3b.cpp

@@ -0,0 +1,15 @@
+// This file is auto-generated, manual changes will be lost if the code is regenerated.
+
+#include "geometries.hpp"
+
+namespace mapbox {
+namespace fixtures {
+
+static const Fixture<short> water3b("water3b", 25, 1e-14, 0.000001, {
+    {{-128,4224},{-128,-128},{4224,-128},{4224,4224},{-128,4224}},
+    {{3832,-21},{3840,-17},{3877,21},{3895,39},{3961,-21},{3893,-98},{3855,-128},{3688,-128},{3742,-81},{3793,-41},{3832,-21},{3832,-21}},
+    {{4205,596},{4224,572},{4224,248},{4166,163},{4119,50},{4020,36},{4004,21},{3969,21},{3936,62},{3982,117},{4088,293},{4152,419},{4185,544},{4205,596},{4205,596}},
+});
+
+}
+}

Разлика између датотеке није приказан због своје велике величине
+ 8 - 0
polygon.mod/earcut/test/fixtures/water4.cpp


Разлика између датотеке није приказан због своје велике величине
+ 8 - 0
polygon.mod/earcut/test/fixtures/water_huge.cpp


Разлика између датотеке није приказан због своје велике величине
+ 8 - 0
polygon.mod/earcut/test/fixtures/water_huge2.cpp


+ 70 - 0
polygon.mod/earcut/test/tap.cpp

@@ -0,0 +1,70 @@
+#include "tap.hpp"
+
+#include <iostream>
+#include <stdexcept>
+#include <cassert>
+
+int Tap::total = 0;
+int Tap::errored = 0;
+bool Tap::started = false;
+
+Tap::Tap() {
+    if (started) {
+#if defined(__cpp_exceptions) || defined(__EXCEPTIONS)
+        throw std::runtime_error("Tap cannot be initialized more than once");
+#else
+        assert(false && "Tap cannot be initialized more than once");
+        exit(1);
+#endif
+    }
+
+    std::cout << "TAP version 13" << std::endl;
+
+    atexit([]() {
+
+    });
+}
+
+Tap::~Tap() {
+    std::cout << std::endl;
+    std::cout << "1.." << total << std::endl;
+    std::cout << "# tests " << total << std::endl;
+    std::cout << "# pass  " << (total - errored) << std::endl;
+    std::cout << std::endl;
+    if (!errored) {
+        std::cout << "# ok" << std::endl << std::endl;
+    } else {
+        std::cout << "# not ok" << std::endl << std::endl;
+        exit(1);
+    }
+}
+
+Tap::Test::Test(const std::string &name) {
+    std::cout << "# " << name << std::endl;
+}
+
+Tap::Test::~Test() {
+    if (!finished) {
+        fail("test exited without ending");
+    }
+    if (failed) {
+        errored++;
+    }
+}
+
+void Tap::Test::ok(bool status, const std::string &message) {
+    if (!status) {
+        fail(message);
+    } else {
+        std::cout << "ok " << ++total << " " << message << std::endl;
+    }
+}
+
+void Tap::Test::fail(const std::string &message) {
+    failed = true;
+    std::cout << "not ok " << ++total << " " << message << std::endl;
+}
+
+void Tap::Test::end() {
+    finished = true;
+}

+ 27 - 0
polygon.mod/earcut/test/tap.hpp

@@ -0,0 +1,27 @@
+#include <string>
+
+class Tap {
+public:
+    class Test;
+    Tap();
+    ~Tap();
+
+private:
+    static int total;
+    static int errored;
+    static bool started;
+};
+
+class Tap::Test {
+public:
+    Test(const std::string &name);
+    ~Test();
+
+    void ok(bool status, const std::string &message);
+    void fail(const std::string &message);
+    void end();
+
+private:
+    bool failed = false;
+    bool finished = false;
+};

+ 117 - 0
polygon.mod/earcut/test/test.cpp

@@ -0,0 +1,117 @@
+#include "tap.hpp"
+#include "fixtures/geometries.hpp"
+
+#include <iomanip>
+#include <locale>
+#include <sstream>
+
+template <typename Point>
+double triangleArea(const Point &a, const Point &b, const Point &c) {
+    using namespace mapbox::util;
+    return double(std::abs((nth<0, Point>::get(a) - nth<0, Point>::get(c)) * (nth<1, Point>::get(b) - nth<1, Point>::get(a)) -
+                           (nth<0, Point>::get(a) - nth<0, Point>::get(b)) * (nth<1, Point>::get(c) - nth<1, Point>::get(a)))) / 2;
+}
+
+template <typename Vertices, typename Indices>
+double trianglesArea(const Vertices &vertices, const Indices &indices) {
+    double area = 0;
+    for (size_t i = 0; i < indices.size(); i += 3) {
+        area += triangleArea(
+            vertices[indices[i]],
+            vertices[indices[i + 1]],
+            vertices[indices[i + 2]]
+        );
+    }
+    return area;
+}
+
+template <typename Ring>
+double ringArea(const Ring &points) {
+    using namespace mapbox::util;
+    using Point = typename Ring::value_type;
+    double sum = 0;
+    for (size_t i = 0, len = points.size(), j = len - 1; i < len; j = i++) {
+        sum += (nth<0, Point>::get(points[i]) - nth<0, Point>::get(points[j])) *
+               (nth<1, Point>::get(points[i]) + nth<1, Point>::get(points[j]));
+    }
+    return std::abs(sum) / 2;
+}
+
+template <typename Polygon>
+double polygonArea(const Polygon &rings) {
+    if (rings.empty()) return .0;
+    double sum = ringArea(rings[0]);
+    for (size_t i = 1; i < rings.size(); i++) {
+        sum -= ringArea(rings[i]);
+    }
+    return std::max(sum, .0);
+}
+
+std::string formatPercent(double num) {
+    std::stringstream ss;
+    ss.imbue(std::locale(""));
+    ss << std::fixed << std::setprecision(6) << num * 100 << "%";
+    return ss.str();
+}
+
+void areaTest(mapbox::fixtures::FixtureTester* fixture) {
+    Tap::Test t(fixture->name);
+
+    const auto expectedArea = polygonArea(fixture->polygon());
+    const auto expectedTriangles = fixture->expectedTriangles;
+
+    { // Earcut
+        const auto earcut = fixture->earcut();
+
+        const auto earcutTriangles = earcut.indices.size() / 3;
+        t.ok(earcutTriangles == expectedTriangles, std::to_string(earcutTriangles) + " triangles when expected " +
+            std::to_string(expectedTriangles));
+
+        if (expectedTriangles > 0) {
+            const auto area = trianglesArea(earcut.vertices, earcut.indices);
+            const double deviation = (expectedArea == area) ? 0 :
+                    expectedArea == 0 ? std::numeric_limits<double>::infinity() :
+                    std::abs(area - expectedArea) / expectedArea;
+
+            bool deviationOk = deviation <= fixture->expectedEarcutDeviation;
+            t.ok(deviationOk, std::string{ "earcut deviation " } + formatPercent(deviation) +
+                                                    " is " + (deviationOk ? "" : "not ") + "less than " +
+                                                    formatPercent(fixture->expectedEarcutDeviation));
+        }
+    }
+
+    { // Libtess2
+        const auto libtess = fixture->libtess();
+        const auto area = trianglesArea(libtess.vertices, libtess.indices);
+        const double deviation = (expectedArea == area) ? 0 :
+                expectedArea == 0 ? std::numeric_limits<double>::infinity() :
+                std::abs(area - expectedArea) / expectedArea;
+
+        bool deviationOk = deviation <= fixture->expectedLibtessDeviation;
+        t.ok(deviationOk, std::string{ "libtess2 deviation " } + formatPercent(deviation) +
+                                             " is " + (deviationOk ? "" : "not ") + "less than " +
+                                             formatPercent(fixture->expectedLibtessDeviation));
+    }
+
+    t.end();
+}
+
+int main() {
+    Tap tap;
+
+    {
+        Tap::Test t("empty");
+        auto polygon = mapbox::fixtures::Polygon<std::pair<int, int>> {};
+        EarcutTesselator<int, decltype(polygon)> tesselator(polygon);
+        tesselator.run();
+        t.ok(tesselator.indices().empty(), "empty input produces empty result");
+        t.end();
+    }
+
+    auto& fixtures = mapbox::fixtures::FixtureTester::collection();
+    for (auto fixture : fixtures) {
+        areaTest(fixture);
+    }
+
+    return 0;
+}

+ 510 - 0
polygon.mod/earcut/test/viz.cpp

@@ -0,0 +1,510 @@
+#include "comparison/earcut.hpp"
+#include "comparison/libtess2.hpp"
+
+#include "fixtures/geometries.hpp"
+
+#if _MSC_VER >= 1900
+#pragma comment(lib, "legacy_stdio_definitions.lib")
+#endif
+
+#include <GLFW/glfw3.h>
+
+#include <cstdlib>
+#include <cmath>
+#include <vector>
+#include <memory>
+
+static GLFWwindow *window = nullptr;
+static const int width = 1024;
+static const int height = 1024;
+static bool drawFill = true, drawMesh = true, drawOutline = true;
+static bool dirtyViewport = true, dirtyShape = true, dirtyTessellator = true;
+static float colorBackground[4] = {1.f, 1.f, 1.f, 1.f};
+static float colorMesh[4] = {1.f, 0.f, 0.f, 0.2f}, colorFill[4] = {1.f, 1.f, 0.f, 0.2f};
+static float colorOutline[4] = {0.2f, 0.2f, 0.2f, 0.9f}, colorInline[4] = {0.7f, 0.6f, .2f, 0.9f};
+
+static bool mouseDrag = false;
+
+static std::size_t shapeIndex = 0;
+static std::size_t tessellator = 0;
+
+struct Camera2D {
+    double left = .0, right = .0, bottom = .0, top = .0;
+    double translateX = .0, translateY = .0;
+    int viewWidth = 0, viewHeight = 0;
+    double zoom = -1.;
+    double mx = .0, cx = .0, my = .0, cy = .0;
+    inline float dpi() { return float(viewHeight) / height; }
+    inline double scaling() { return std::pow(1.1, zoom); }
+    void setView(int width, int height) {
+        viewWidth = width;
+        viewHeight = height;
+    }
+    void limits(double l, double r, double b, double t) {
+        left = l;
+        right = r;
+        bottom = b;
+        top = t;
+    }
+    bool scale(double z) {
+        if (z == 0) return false;
+        zoom += z;
+        return true;
+    }
+    bool move(double x, double y) {
+        const double s = scaling();
+        const double dx = x / double(viewWidth) * (right - left) / s;
+        const double dy = y / double(viewHeight) * (bottom - top) / s;
+        if (dx == 0 && dy == 0) {
+            return false;
+        }
+        translateX += dx;
+        translateY += dy;
+        return true;
+    }
+    /* apply current transform to opengl context */
+    void apply() {
+        glViewport(0, 0, viewWidth, viewHeight);
+
+        glMatrixMode(GL_PROJECTION);
+        glLoadIdentity();
+
+        glMatrixMode(GL_MODELVIEW);
+        glLoadIdentity();
+
+        const double s = scaling();
+        const double w2 = .5 * (right - left);
+        const double h2 = .5 * (top - bottom);
+        mx = s / w2;
+        cx = (-left - w2 + translateX) * mx;
+        my = s / h2;
+        cy = (-bottom - h2 + translateY) * my;
+    }
+    void setDefaults() {
+        zoom = -1.;
+        translateX = .0;
+        translateY = .0;
+    }
+    /* transforms world coordinate to screen range [-1,1] with double precision */
+    inline void toScreen(double *x, double *y) {
+        *x = *x * mx + cx;
+        *y = *y * my + cy;
+    }
+    /* transforms screen coordinates in range [-1,1] to world coordinates with double precision */
+    inline void toWorld(double *x, double *y) {
+        *x = (*x - cx) / mx;
+        *y = (*y - cy) / my;
+    }
+    inline void vec2(double x, double y) {
+        toScreen(&x, &y);
+        glVertex2d(x, y);
+    }
+};
+
+static Camera2D cam;
+
+class DrawablePolygon {
+public:
+    static std::unique_ptr<DrawablePolygon> makeDrawable(std::size_t index, mapbox::fixtures::FixtureTester* fixture);
+    DrawablePolygon() = default;
+    virtual ~DrawablePolygon() = default;
+    virtual const char* name() = 0;
+    virtual void drawMesh() = 0;
+    virtual void drawOutline() = 0;
+    virtual void drawFill() = 0;
+};
+
+class DrawableTesselator : public DrawablePolygon {
+    mapbox::fixtures::FixtureTester::TesselatorResult shape;
+    mapbox::fixtures::DoublePolygon const& polygon;
+public:
+    explicit DrawableTesselator(mapbox::fixtures::FixtureTester::TesselatorResult tessellation,
+                                mapbox::fixtures::DoublePolygon const& poly) : shape(tessellation), polygon(poly) { }
+    void drawMesh() override {
+        const auto &v = shape.vertices;
+        const auto &x = shape.indices;
+        glBegin(GL_LINES);
+        glColor4fv(colorMesh);
+        for (size_t i = 0; i < x.size(); i += 3) {
+            cam.vec2(v[x[i]][0], v[x[i]][1]);
+            cam.vec2(v[x[i + 1]][0], v[x[i + 1]][1]);
+            cam.vec2(v[x[i + 1]][0], v[x[i + 1]][1]);
+            cam.vec2(v[x[i + 2]][0], v[x[i + 2]][1]);
+            cam.vec2(v[x[i + 2]][0], v[x[i + 2]][1]);
+            cam.vec2(v[x[i]][0], v[x[i]][1]);
+        }
+        glEnd();
+    }
+    void drawOutline() override {
+        glBegin(GL_LINES);
+        for (std::size_t i = 0; i < polygon.size(); i++) {
+            auto& ring = polygon[i];
+            glColor4fv(i == 0 ? colorOutline : colorInline);
+            for (std::size_t j = 0; j < ring.size(); j++) {
+                auto& p0 = ring[j];
+                auto& p1 = ring[(j+1) % ring.size()];
+                cam.vec2(std::get<0>(p0), std::get<1>(p0));
+                cam.vec2(std::get<0>(p1), std::get<1>(p1));
+            }
+        }
+        glEnd();
+    }
+    void drawFill() override {
+        const auto &v = shape.vertices;
+        const auto &x = shape.indices;
+        glBegin(GL_TRIANGLES);
+        glColor4fv(colorFill);
+        for (const auto pt : x) {
+            cam.vec2(v[pt][0], v[pt][1]);
+        }
+        glEnd();
+    }
+};
+
+class DrawableEarcut : public DrawableTesselator {
+public:
+    explicit DrawableEarcut(mapbox::fixtures::FixtureTester* fixture)
+            : DrawableTesselator(fixture->earcut(), fixture->polygon()) { }
+    const char *name() override { return "earcut"; };
+};
+
+class DrawableLibtess : public DrawableTesselator {
+public:
+    explicit DrawableLibtess(mapbox::fixtures::FixtureTester* fixture)
+            : DrawableTesselator(fixture->libtess(), fixture->polygon()) { }
+    const char *name() override { return "libtess2"; };
+};
+
+class DrawableScanLineFill : public DrawablePolygon {
+    struct Edge {
+        float yMin;
+        float yMax;
+        float scale;
+        float offset;
+        Edge* next = nullptr;
+        Edge(double x1, double y1, double x2, double y2)
+                : yMin((float)std::min<double>(y1, y2)),
+                  yMax((float)std::max<double>(y1, y2))
+        {
+            const double dx = x1 - x2;
+            const double dy = y1 - y2;
+            scale = (float)(dx / dy);
+            offset = (float)((y1 * x2 - x1 * y2) / dy);
+        }
+        inline double intersection(double scanline) const {
+            // horizontal scan-line intersection from the left
+            // double precision for the calculation is required to avoid artifacts
+            return scanline * scale + offset;
+        }
+    };
+    mapbox::fixtures::FixtureTester* shape;
+    std::vector<Edge*> activeList; /* contains current sorted intersections */
+    std::vector<std::vector<Edge>> edgeTables; /* contains all edges sorted by yMin */
+public:
+    explicit DrawableScanLineFill(mapbox::fixtures::FixtureTester* fixture) : shape(fixture) {
+        auto& polygon = shape->polygon();
+        edgeTables.reserve(polygon.size());
+        for (const auto &ring : polygon) {
+            std::vector<Edge> edgeTable;
+            edgeTable.reserve(ring.size());
+            for (std::size_t i = 1; i <= ring.size(); i++) {
+                const auto &p0 = ring[i - 1], p1 = i == ring.size() ? ring[0] : ring[i];
+                const double x1 = std::get<0>(p0), y1 = std::get<1>(p0), x2 = std::get<0>(p1), y2 = std::get<1>(p1);
+                if (y1 != y2) { edgeTable.emplace_back(x1, y1, x2, y2); }
+            }
+            std::sort(edgeTable.begin(), edgeTable.end(), [&](Edge const &a, Edge const &b) {
+                return a.yMin < b.yMin;
+            });
+            edgeTables.push_back(std::move(edgeTable));
+        }
+    }
+    void scanLineFill(std::vector<Edge>& edgeTable, double top, double bottom, double lineWidth) {
+        assert(top < bottom);
+        assert(lineWidth > 0);
+
+        // create intrusive sorted edge list
+        for (std::size_t i = 1; i < edgeTable.size(); i++) {
+            edgeTable[i-1].next = &edgeTable[i];
+        }
+        Edge* edgeList = edgeTable.empty() ? nullptr : &edgeTable[0];
+
+        const double halfWidth = lineWidth * 0.5;
+        top -= lineWidth;
+        bottom += lineWidth;
+
+        double y0 = top;
+        while(y0 < bottom && edgeList) {
+            double y1 = y0 + lineWidth;
+            if (y1 == y0) { y1 = std::nextafter(y1, bottom); }
+            const double y = y0 + halfWidth;
+
+            activeList.clear();
+            Edge* prevEdge = nullptr;
+            for (auto edge = edgeList; edge != nullptr; edge = edge->next) {
+                const auto min = edge->yMin, max = edge->yMax;
+                if (min <= y && y < max) {
+                    activeList.push_back(edge);
+                }
+                if (min > y)  {
+                    break;
+                } else if (y >= max) {
+                    // unlink edge
+                    Edge** next = prevEdge ? &prevEdge->next : &edgeList;
+                    *next = edge->next;
+                } else {
+                    prevEdge = edge;
+                }
+            }
+            std::sort(activeList.begin(), activeList.end(), [&](Edge *a, Edge *b) {
+                return a->intersection(y) < b->intersection(y);
+            });
+
+            double x1y0 = 0, x1y1 = 0;
+            for (std::size_t i = 0; i < activeList.size(); i++) {
+                Edge *edge = activeList[i];
+
+                // use slope to make MSAA possible
+                const double x2y0 = edge->intersection(std::max<double>(edge->yMin, y0));
+                const double x2y1 = edge->intersection(std::min<double>(edge->yMax, y1));
+                if ((i % 2) != 0) {
+                    cam.vec2(x1y0, y0);
+                    cam.vec2(x1y1, y1);
+                    cam.vec2(x2y1, y1);
+                    cam.vec2(x2y0, y0);
+                }
+                x1y0 = x2y0;
+                x1y1 = x2y1;
+            }
+
+            y0 = y1;
+        }
+    }
+    void drawFill() override {
+        for (std::size_t i = 0; i < edgeTables.size(); i++) {
+            auto& edgeTable = edgeTables[i];
+            glBegin(GL_QUADS);
+            glColor4fv(i == 0 ? colorFill : colorBackground);
+            double x0 = 1;
+            double y0 = 1;
+            cam.toWorld(&x0, &y0);
+            double x1 = -1;
+            double y1 = -1;
+            cam.toWorld(&x1, &y1);
+            scanLineFill(edgeTable, y0, y1, (y1 - y0) / cam.viewHeight);
+            glEnd();
+        }
+    }
+    void drawOutline() override {
+        auto& polygon = shape->polygon();
+        glBegin(GL_LINES);
+        for (std::size_t i = 0; i < polygon.size(); i++) {
+            auto& ring = polygon[i];
+            glColor4fv(i == 0 ? colorOutline : colorInline);
+            for (std::size_t j = 0; j < ring.size(); j++) {
+                auto& p0 = ring[j];
+                auto& p1 = ring[(j+1) % ring.size()];
+                cam.vec2(std::get<0>(p0), std::get<1>(p0));
+                cam.vec2(std::get<0>(p1), std::get<1>(p1));
+            }
+        }
+        glEnd();
+    }
+    void drawMesh() override { }
+    const char *name() override { return "scanline-fill"; }
+};
+
+std::unique_ptr<DrawablePolygon> DrawablePolygon::makeDrawable(std::size_t index, mapbox::fixtures::FixtureTester* fixture) {
+    if (index == 0) {
+        return std::unique_ptr<DrawablePolygon>(new DrawableEarcut(fixture));
+    } else if (index == 1) {
+        return std::unique_ptr<DrawablePolygon>(new DrawableLibtess(fixture));
+    } else {
+        return std::unique_ptr<DrawablePolygon>(new DrawableScanLineFill(fixture));
+    }
+}
+
+static std::array<std::unique_ptr<DrawablePolygon>, 3> tessellators;
+
+
+mapbox::fixtures::FixtureTester *getFixture(std::size_t i) {
+    auto& fixtures = mapbox::fixtures::FixtureTester::collection();
+    if (fixtures.empty()) {
+        assert(false);
+        exit(1);
+    }
+    return fixtures[i % fixtures.size()];
+}
+
+int main() {
+    if (!glfwInit()) {
+        return 1;
+    }
+
+    glfwWindowHint(GLFW_RESIZABLE, 0);
+    glfwWindowHint(GLFW_SAMPLES, 4);
+    window = glfwCreateWindow(width, height, "Tessellation", nullptr, nullptr);
+    if (!window) {
+        glfwTerminate();
+        return 1;
+    }
+
+    glfwSetKeyCallback(window,
+                       [](GLFWwindow *win, int key, int /*scancode*/, int action, int /*mods*/) {
+        if (action != GLFW_PRESS && action != GLFW_REPEAT) {
+            return;
+        }
+
+        if (key == GLFW_KEY_ESCAPE || key == GLFW_KEY_Q) {
+            glfwSetWindowShouldClose(win, 1);
+        } else if (key == GLFW_KEY_F) {
+            drawFill = !drawFill;
+            dirtyViewport = true;
+        } else if (key == GLFW_KEY_M) {
+            drawMesh = !drawMesh;
+            dirtyViewport = true;
+        } else if (key == GLFW_KEY_O) {
+            drawOutline = !drawOutline;
+            dirtyViewport = true;
+        } else if (key == GLFW_KEY_RIGHT) {
+            if (shapeIndex + 1 < mapbox::fixtures::FixtureTester::collection().size()) {
+                shapeIndex++;
+                dirtyShape = true;
+            }
+        } else if (key == GLFW_KEY_LEFT) {
+            if (shapeIndex >= 1) {
+                shapeIndex--;
+                dirtyShape = true;
+            }
+        } else if (key == GLFW_KEY_T || key == GLFW_KEY_UP) {
+            tessellator = (tessellator + 1) % tessellators.size();
+            dirtyTessellator = true;
+        } else if (key == GLFW_KEY_DOWN) {
+            tessellator = (tessellator + tessellators.size() - 1) % tessellators.size();
+            dirtyTessellator = true;
+        } else if (key == GLFW_KEY_KP_ADD) {
+            dirtyViewport |= cam.scale(1.);
+        } else if (key == GLFW_KEY_KP_SUBTRACT) {
+            dirtyViewport |= cam.scale(-1.);
+        } else if (key == GLFW_KEY_R) {
+            dirtyTessellator = dirtyViewport = dirtyShape = true;
+        } else if (key == GLFW_KEY_W) {
+            dirtyViewport |= cam.move(.0, cam.viewHeight / 50.);
+        } else if (key == GLFW_KEY_A) {
+            dirtyViewport |= cam.move(cam.viewWidth / 50., .0);
+        } else if (key == GLFW_KEY_S) {
+            dirtyViewport |= cam.move(.0, -cam.viewHeight / 50.);
+        } else if (key == GLFW_KEY_D) {
+            dirtyViewport |= cam.move(-cam.viewWidth / 50., .0);
+        }
+    });
+
+    glfwSetScrollCallback(window, [](GLFWwindow* /* window */, double /* xoffset */, double yoffset) {
+        dirtyViewport |= cam.scale(yoffset);
+    });
+
+    glfwSetMouseButtonCallback(window, [](GLFWwindow* /* window */, int button, int action, int /* mods */){
+        if (button == GLFW_MOUSE_BUTTON_LEFT) {
+            mouseDrag = action != GLFW_RELEASE;
+        }
+    });
+
+    glfwSetFramebufferSizeCallback(window, [](GLFWwindow * /*win*/, int w, int h) {
+        cam.setView(w, h);
+    });
+
+    int fbWidth, fbHeight;
+    glfwGetFramebufferSize(window, &fbWidth, &fbHeight);
+    cam.setView(fbWidth, fbHeight);
+
+    glfwMakeContextCurrent(window);
+
+    glfwSwapInterval(1);
+
+    glClearColor(colorBackground[0], colorBackground[1], colorBackground[2], colorBackground[3]);
+
+    glEnable(GL_BLEND);
+    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+    glEnable(GL_LINE_SMOOTH);
+    glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
+
+    while (!glfwWindowShouldClose(window)) {
+        static double mouseX = 0, mouseY = 0;
+        double mousePrevX = mouseX, mousePrevY = mouseY;
+        glfwGetCursorPos(window, &mouseX, &mouseY);
+        double mouseDeltaX = mouseX - mousePrevX, mouseDeltaY = mouseY - mousePrevY;
+        if (mouseDrag) {
+            dirtyViewport |= cam.move(mouseDeltaX, mouseDeltaY);
+        }
+
+        if (dirtyShape) {
+            for (auto &tessellator : tessellators) {
+                tessellator.reset(nullptr);
+            }
+
+            const auto& polygon = getFixture(shapeIndex)->polygon();
+            auto minX = std::numeric_limits<double>::max();
+            auto maxX = std::numeric_limits<double>::min();
+            auto minY = std::numeric_limits<double>::max();
+            auto maxY = std::numeric_limits<double>::min();
+            if (!polygon.empty()) {
+                for (const auto &pt : polygon[0]) {
+                    minX = std::min<double>(minX, std::get<0>(pt));
+                    minY = std::min<double>(minY, std::get<1>(pt));
+                    maxX = std::max<double>(maxX, std::get<0>(pt));
+                    maxY = std::max<double>(maxY, std::get<1>(pt));
+                }
+            }
+            const auto dimX = minX < maxX ? maxX - minX : 0;
+            const auto dimY = minY < maxY ? maxY - minY : 0;
+
+            auto midX = minX + dimX / 2;
+            auto midY = minY + dimY / 2;
+            auto ext = std::max<double>(dimX, dimY) / 2;
+
+            cam.setDefaults();
+            cam.limits(midX - ext, midX + ext, midY + ext, midY - ext);
+        }
+
+        if (dirtyViewport || dirtyShape || dirtyTessellator) {
+            glClear(GL_COLOR_BUFFER_BIT);
+
+            cam.apply();
+            glLineWidth(cam.dpi() * std::sqrt(2.f));
+
+            auto& drawable = tessellators[tessellator];
+
+            if (!drawable) {
+                drawable = DrawablePolygon::makeDrawable(tessellator, getFixture(shapeIndex));
+            }
+
+            if (dirtyTessellator || dirtyShape) {
+                glfwSetWindowTitle(window, (std::string(drawable->name()) + ": "
+                                            + getFixture(shapeIndex)->name).c_str());
+            }
+
+            if (!drawMesh && !drawFill && !drawOutline) {
+                drawMesh = drawFill = drawOutline = true;
+            }
+
+            if (drawFill) {
+                drawable->drawFill();
+            }
+            if (drawMesh) {
+                drawable->drawMesh();
+            }
+            if (drawOutline) {
+                drawable->drawOutline();
+            }
+
+            glFlush(); /* required for Mesa 3D driver */
+            glfwSwapBuffers(window);
+        }
+
+        dirtyTessellator = dirtyShape = dirtyViewport = false;
+        glfwWaitEvents();
+    }
+
+    glfwTerminate();
+    return 0;
+}

+ 15 - 0
polygon.mod/examples/example_01.bmx

@@ -0,0 +1,15 @@
+SuperStrict
+
+Framework BRL.StandardIO
+Import BRL.Polygon
+
+
+Local points:SVec2I[] = [new SVec2I(1, 1), new SVec2I(10, 1), new SVec2I(10, 10), new SVec2I(1, 10)]
+
+Local indices:Int[] = TriangulatePoly(points)
+
+Print indices.length
+
+For Local i:Int = 0 Until indices.length
+	Print indices[i]
+next

+ 33 - 0
polygon.mod/examples/example_02.bmx

@@ -0,0 +1,33 @@
+SuperStrict
+
+Framework SDL.SDLRenderMax2D
+'Framework brl.glmax2d
+import brl.polygon
+Import BRL.StandardIO
+
+Local poly:Float[] = [100, 100, 200, 100, 200, 200, 100, 200]
+
+Local indices:Int[] = TriangulatePoly(poly)
+
+Print "count = " + indices.length
+
+For Local i:Int = 0 Until indices.length
+	Print indices[i]
+next
+Graphics 800, 600, 0
+
+SetHandle( 150, 150 )
+SetOrigin( 200, 200 )
+
+Local angle:Float = 0
+While Not keydown(KEY_ESCAPE)
+	
+		Cls
+
+		DrawPoly(poly, indices)
+
+		SetRotation( angle )
+		angle :+ 0.5
+
+		Flip
+Wend

+ 102 - 0
polygon.mod/glue.cpp

@@ -0,0 +1,102 @@
+/*
+ISC License
+
+Copyright (c) 2023, Bruce A Henderson
+
+Permission to use, copy, modify, and/or distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright notice
+and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+*/
+#include "mapbox/earcut.hpp"
+
+extern "C" {
+
+#include "brl.mod/blitz.mod/blitz.h"
+
+    struct SVec2I {
+        int x;
+        int y;
+    };
+
+    struct SVec2F {
+        float x;
+        float y;
+    };
+
+    BBArray * bmx_polygon_tri_svec2i(struct SVec2I * p, int size);
+    BBArray * bmx_polygon_tri_svec2f(struct SVec2F * p, int size);
+}
+
+namespace mapbox {
+namespace util {
+
+template <>
+struct nth<0, struct SVec2I> {
+    inline static int get(const struct SVec2I &v) {
+        return v.x;
+    };
+};
+template <>
+struct nth<1, struct SVec2I> {
+    inline static int get(const struct SVec2I &v) {
+        return v.y;
+    };
+};
+
+template <>
+struct nth<0, struct SVec2F> {
+    inline static float get(const struct SVec2F &v) {
+        return v.x;
+    };
+};
+template <>
+struct nth<1, struct SVec2F> {
+    inline static float get(const struct SVec2F &v) {
+        return v.y;
+    };
+};
+
+} // namespace util
+} // namespace mapbox
+
+BBArray * bmx_polygon_tri_svec2i(struct SVec2I * p, int size) {
+
+    std::vector<struct SVec2I> points(p, p + size);
+    std::vector<std::vector<struct SVec2I>> polygon;
+    polygon.push_back(points);
+
+    std::vector<int> indices = mapbox::earcut<int>(polygon);
+
+    BBArray *arr = bbArrayNew1DNoInit("i", indices.size());
+
+	int *s = (int*)BBARRAYDATA(arr,arr->dims);
+
+    std::copy(indices.begin(), indices.end(), s);
+
+    return arr;
+}
+
+BBArray * bmx_polygon_tri_svec2f(struct SVec2F * p, int size) {
+
+    std::vector<struct SVec2F> points(p, p + size);
+    std::vector<std::vector<struct SVec2F>> polygon;
+    polygon.push_back(points);
+
+    std::vector<int> indices = mapbox::earcut<int>(polygon);
+
+    BBArray *arr = bbArrayNew1DNoInit("f", indices.size());
+
+	int *s = (int*)BBARRAYDATA(arr,arr->dims);
+
+    std::copy(indices.begin(), indices.end(), s);
+
+    return arr;
+}

+ 61 - 0
polygon.mod/polygon.bmx

@@ -0,0 +1,61 @@
+' ISC License
+' 
+' Copyright (c) 2023, Bruce A Henderson
+' 
+' Permission to use, copy, modify, and/or distribute this software for any purpose
+' with or without fee is hereby granted, provided that the above copyright notice
+' and this permission notice appear in all copies.
+' 
+' THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+' REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+' FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+' INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+' OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+' TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+' THIS SOFTWARE.
+'
+SuperStrict
+
+Rem
+bbdoc: Polygons
+End Rem
+Module BRL.Polygon
+
+ModuleInfo "Version: 1.00"
+ModuleInfo "Author: Bruce A Henderson"
+ModuleInfo "License: ISC"
+ModuleInfo "earcut - Copyright: 2015 mapbox"
+ModuleInfo "Copyright: 2023 Bruce A Henderson"
+
+ModuleInfo "History: 1.00"
+ModuleInfo "History: Initial Release"
+
+ModuleInfo "CPP_OPTS: -std=c++11"
+
+Import "common.bmx"
+
+
+Rem
+bbdoc: Runs a tesselation against a polygon #SVec2I array, returning a list of triangle indices.
+returns: An array of indices that refer to the vertices of the input polygon. Three subsequent indices form a triangle.
+End Rem
+Function TriangulatePoly:Int[](poly:SVec2I[])
+	Return bmx_polygon_tri_svec2i(poly, poly.Length)
+End Function
+
+Rem
+bbdoc: Runs a tesselation against a polygon #SVec2F array, returning a list of triangle indices.
+returns: An array of indices that refer to the vertices of the input polygon. Three subsequent indices form a triangle.
+End Rem
+Function TriangulatePoly:Int[](poly:SVec2F[])
+	Return bmx_polygon_tri_svec2f(poly, poly.Length)
+End Function
+
+Rem
+bbdoc: Runs a tesselation against a polygon #Float array, returning a list of triangle indices.
+returns: An array of indices that refer to the vertices of the input polygon. Three subsequent indices form a triangle.
+about: The array consists of pairs of x, y vertices.  Output triangles are clockwise.
+End Rem
+Function TriangulatePoly:Int[](poly:Float[])
+	Return bmx_polygon_tri_svec2f(poly, poly.Length / 2)
+End Function

Неке датотеке нису приказане због велике количине промена